Spaces:
Sleeping
Sleeping
Update training configuration and dependencies
Browse files- Modified `requirements.txt` to use flexible versions of PyTorch and related libraries.
- Adjusted training parameters in `train.yaml`, reducing epochs from 20 to 8 and batch sizes from 32 and 64 to 8.
- Enhanced `train_gnn.py` with gradient accumulation and mixed precision training support.
- Fixed data type handling in `graph_models.py` for message passing layer.
- configs/train.yaml +3 -3
- requirements.txt +3 -3
- scripts/train_gnn.py +39 -9
- src/train/graph_models.py +2 -1
configs/train.yaml
CHANGED
|
@@ -12,7 +12,7 @@ run:
|
|
| 12 |
training:
|
| 13 |
stage1:
|
| 14 |
enabled: true
|
| 15 |
-
epochs:
|
| 16 |
freeze_encoders: true
|
| 17 |
lr_head: 0.001
|
| 18 |
lr_encoder: 0.0
|
|
@@ -40,8 +40,8 @@ scheduler:
|
|
| 40 |
min_lr: 0.0000001
|
| 41 |
|
| 42 |
batching:
|
| 43 |
-
batch_size:
|
| 44 |
-
eval_batch_size:
|
| 45 |
|
| 46 |
loss:
|
| 47 |
bce:
|
|
|
|
| 12 |
training:
|
| 13 |
stage1:
|
| 14 |
enabled: true
|
| 15 |
+
epochs: 8
|
| 16 |
freeze_encoders: true
|
| 17 |
lr_head: 0.001
|
| 18 |
lr_encoder: 0.0
|
|
|
|
| 40 |
min_lr: 0.0000001
|
| 41 |
|
| 42 |
batching:
|
| 43 |
+
batch_size: 8
|
| 44 |
+
eval_batch_size: 8
|
| 45 |
|
| 46 |
loss:
|
| 47 |
bce:
|
requirements.txt
CHANGED
|
@@ -3,9 +3,9 @@
|
|
| 3 |
PyYAML
|
| 4 |
pandas
|
| 5 |
pyarrow
|
| 6 |
-
torch
|
| 7 |
-
torchvision
|
| 8 |
-
torchaudio
|
| 9 |
timm
|
| 10 |
scikit-learn
|
| 11 |
Pillow
|
|
|
|
| 3 |
PyYAML
|
| 4 |
pandas
|
| 5 |
pyarrow
|
| 6 |
+
torch
|
| 7 |
+
torchvision
|
| 8 |
+
torchaudio
|
| 9 |
timm
|
| 10 |
scikit-learn
|
| 11 |
Pillow
|
scripts/train_gnn.py
CHANGED
|
@@ -53,7 +53,8 @@ def parse_args() -> argparse.Namespace:
|
|
| 53 |
parser.add_argument("--run-name", type=str, required=True)
|
| 54 |
parser.add_argument("--overrides-json", type=str, default="")
|
| 55 |
parser.add_argument("--mode", type=str, default="full", choices=["smoke", "full"])
|
| 56 |
-
parser.add_argument("--device", type=str, default="auto",
|
|
|
|
| 57 |
return parser.parse_args()
|
| 58 |
|
| 59 |
|
|
@@ -272,6 +273,10 @@ def train() -> None:
|
|
| 272 |
weight_decay=weight_decay,
|
| 273 |
)
|
| 274 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 275 |
output_root = REPO_ROOT / run_cfg.get("output_dir", "outputs")
|
| 276 |
run_dir = output_root / args.run_name
|
| 277 |
run_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -279,22 +284,47 @@ def train() -> None:
|
|
| 279 |
history: List[Dict[str, Any]] = []
|
| 280 |
best_map = -1.0
|
| 281 |
best_path = run_dir / "best.pt"
|
| 282 |
-
log(
|
|
|
|
|
|
|
|
|
|
| 283 |
for epoch in range(1, train_epochs + 1):
|
| 284 |
backbone.eval()
|
| 285 |
gnn_model.train()
|
| 286 |
train_losses: List[float] = []
|
| 287 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 288 |
targets = batch["targets"].to(device)
|
| 289 |
images = batch["images"]
|
| 290 |
feats = backbone.encode(images=images, device=device).unsqueeze(1)
|
| 291 |
graph = build_bipartite_batch(feats=feats, targets=targets)
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 298 |
|
| 299 |
val_metrics = run_eval(
|
| 300 |
backbone=backbone,
|
|
|
|
| 53 |
parser.add_argument("--run-name", type=str, required=True)
|
| 54 |
parser.add_argument("--overrides-json", type=str, default="")
|
| 55 |
parser.add_argument("--mode", type=str, default="full", choices=["smoke", "full"])
|
| 56 |
+
parser.add_argument("--device", type=str, default="auto",
|
| 57 |
+
help="Device: auto, cuda, cpu, or cuda:N (e.g. cuda:1)")
|
| 58 |
return parser.parse_args()
|
| 59 |
|
| 60 |
|
|
|
|
| 273 |
weight_decay=weight_decay,
|
| 274 |
)
|
| 275 |
|
| 276 |
+
grad_accum_steps = int(cfg["optimization"].get("gradient_accumulation_steps", 1))
|
| 277 |
+
use_amp = bool(cfg["optimization"].get("mixed_precision", False)) and device.type == "cuda"
|
| 278 |
+
scaler = torch.cuda.amp.GradScaler(enabled=use_amp)
|
| 279 |
+
|
| 280 |
output_root = REPO_ROOT / run_cfg.get("output_dir", "outputs")
|
| 281 |
run_dir = output_root / args.run_name
|
| 282 |
run_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
| 284 |
history: List[Dict[str, Any]] = []
|
| 285 |
best_map = -1.0
|
| 286 |
best_path = run_dir / "best.pt"
|
| 287 |
+
log(
|
| 288 |
+
f"Training for epochs={train_epochs}, batch_size={batch_size}, "
|
| 289 |
+
f"eval_batch_size={eval_batch_size}, grad_accum_steps={grad_accum_steps}, amp={use_amp}"
|
| 290 |
+
)
|
| 291 |
for epoch in range(1, train_epochs + 1):
|
| 292 |
backbone.eval()
|
| 293 |
gnn_model.train()
|
| 294 |
train_losses: List[float] = []
|
| 295 |
+
step_times: List[float] = []
|
| 296 |
+
start_epoch = time.time()
|
| 297 |
+
for step_idx, batch in enumerate(
|
| 298 |
+
tqdm(train_loader, desc=f"{args.run_name} epoch {epoch}/{train_epochs}")
|
| 299 |
+
):
|
| 300 |
+
batch_start = time.time()
|
| 301 |
targets = batch["targets"].to(device)
|
| 302 |
images = batch["images"]
|
| 303 |
feats = backbone.encode(images=images, device=device).unsqueeze(1)
|
| 304 |
graph = build_bipartite_batch(feats=feats, targets=targets)
|
| 305 |
+
|
| 306 |
+
with torch.cuda.amp.autocast(enabled=use_amp):
|
| 307 |
+
logits = gnn_model(graph)
|
| 308 |
+
loss = bce_logits_loss(logits, targets, pos_weight=pos_weight)
|
| 309 |
+
loss = loss / grad_accum_steps
|
| 310 |
+
|
| 311 |
+
scaler.scale(loss).backward()
|
| 312 |
+
if (step_idx + 1) % grad_accum_steps == 0 or (step_idx + 1) == len(train_loader):
|
| 313 |
+
scaler.unscale_(optimizer)
|
| 314 |
+
torch.nn.utils.clip_grad_norm_(gnn_model.parameters(), cfg["optimization"].get("grad_clip_norm", 1.0))
|
| 315 |
+
scaler.step(optimizer)
|
| 316 |
+
scaler.update()
|
| 317 |
+
optimizer.zero_grad(set_to_none=True)
|
| 318 |
+
|
| 319 |
+
train_losses.append(float(loss.item()) * grad_accum_steps)
|
| 320 |
+
step_times.append(time.time() - batch_start)
|
| 321 |
+
|
| 322 |
+
epoch_time = time.time() - start_epoch
|
| 323 |
+
avg_step = float(np.mean(step_times)) if step_times else 0.0
|
| 324 |
+
log(
|
| 325 |
+
f"Epoch {epoch} finished in {epoch_time:.1f}s, "
|
| 326 |
+
f"avg_step_time={avg_step:.3f}s, steps={len(step_times)}"
|
| 327 |
+
)
|
| 328 |
|
| 329 |
val_metrics = run_eval(
|
| 330 |
backbone=backbone,
|
src/train/graph_models.py
CHANGED
|
@@ -68,7 +68,8 @@ class BipartiteMessagePassingLayer(nn.Module):
|
|
| 68 |
# Messages from attributes to objects.
|
| 69 |
attr_msgs = flat_attrs.index_select(0, src_attr) # [E, Din_attr]
|
| 70 |
attr_msgs = self.attr_to_obj(attr_msgs) # [E, Din]
|
| 71 |
-
|
|
|
|
| 72 |
weighted_msgs = attr_msgs * w
|
| 73 |
|
| 74 |
# Aggregate messages per object index.
|
|
|
|
| 68 |
# Messages from attributes to objects.
|
| 69 |
attr_msgs = flat_attrs.index_select(0, src_attr) # [E, Din_attr]
|
| 70 |
attr_msgs = self.attr_to_obj(attr_msgs) # [E, Din]
|
| 71 |
+
attr_msgs = attr_msgs.to(flat_objects.dtype)
|
| 72 |
+
w = edge_weight.view(-1, 1).to(flat_objects.dtype)
|
| 73 |
weighted_msgs = attr_msgs * w
|
| 74 |
|
| 75 |
# Aggregate messages per object index.
|