trainloop 0.4.0__tar.gz → 0.5.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: trainloop
3
- Version: 0.4.0
3
+ Version: 0.5.1
4
4
  Summary: Minimal PyTorch training loop with hooks and checkpointing.
5
- Author: Karim Abou Zeid
6
- Author-email: Karim Abou Zeid <contact@ka.codes>
5
+ Author: Karim Knaebel
6
+ Author-email: Karim Knaebel <contact@knaebel.dev>
7
7
  Requires-Dist: pillow>=11.3.0
8
8
  Requires-Dist: torch>=2.0.0
9
9
  Requires-Python: >=3.10
@@ -1,10 +1,10 @@
1
1
  [project]
2
2
  name = "trainloop"
3
- version = "0.4.0"
3
+ version = "0.5.1"
4
4
  description = "Minimal PyTorch training loop with hooks and checkpointing."
5
5
  readme = "README.md"
6
6
  authors = [
7
- { name = "Karim Abou Zeid", email = "contact@ka.codes" }
7
+ { name = "Karim Knaebel", email = "contact@knaebel.dev" }
8
8
  ]
9
9
  requires-python = ">=3.10"
10
10
  dependencies = [
@@ -269,7 +269,7 @@ class ProgressHook(_StatsHook):
269
269
  ):
270
270
  eta = self.eta_tracker.get_eta(trainer.max_steps - trainer.step)
271
271
  trainer.logger.info(
272
- f"Step {trainer.step:>{len(str(trainer.max_steps))}}/{trainer.max_steps}:"
272
+ f"Step {trainer.step}/{trainer.max_steps}:"
273
273
  + f" step {step_time:.4f}{'s' if self.show_units else ''} data {data_time:.4f}{'s' if self.show_units else ''}"
274
274
  + (f" eta {eta}" if eta is not None else "")
275
275
  + (
@@ -309,6 +309,13 @@ class LoggingHook(_StatsHook):
309
309
  ):
310
310
  super().__init__(interval, sync)
311
311
 
312
+ def on_before_step(self, trainer: BaseTrainer):
313
+ super().on_before_step(trainer)
314
+ self.lrs = [
315
+ (param_group.get("name", f"group_{i}"), param_group["lr"])
316
+ for i, param_group in enumerate(trainer.optimizer.param_groups)
317
+ ] # record the LR before the scheduler steps
318
+
312
319
  def process_stats(
313
320
  self,
314
321
  trainer: BaseTrainer,
@@ -320,10 +327,6 @@ class LoggingHook(_StatsHook):
320
327
  max_memory: float | None,
321
328
  records: Records,
322
329
  ):
323
- lrs = [
324
- (param_group.get("name", f"group_{i}"), param_group["lr"])
325
- for i, param_group in enumerate(trainer.optimizer.param_groups)
326
- ]
327
330
  trainer.log(
328
331
  {
329
332
  "train": records
@@ -334,7 +337,7 @@ class LoggingHook(_StatsHook):
334
337
  "data_time": data_time,
335
338
  "step_time": step_time,
336
339
  "non_finite_grad_retry_count": non_finite_grad_retry_count,
337
- "lr": {name: lr for name, lr in lrs},
340
+ "lr": {name: lr for name, lr in self.lrs},
338
341
  }
339
342
  }
340
343
  )
@@ -346,7 +349,8 @@ class CheckpointingHook(BaseHook):
346
349
  Args:
347
350
  interval: Save every ``interval`` steps.
348
351
  keep_previous: Keep the last N checkpoints in addition to the latest.
349
- keep_interval: Keep checkpoints every ``keep_interval`` steps.
352
+ keep_interval: Save and keep checkpoints every ``keep_interval`` steps.
353
+ keep_steps: Save and keep checkpoints at these explicit step numbers.
350
354
  path: Directory (relative to workspace unless absolute) for checkpoints.
351
355
  load: Path to load at startup or ``\"latest\"`` to auto-resume.
352
356
  exit_signals: Signals that trigger a checkpoint then exit.
@@ -358,7 +362,9 @@ class CheckpointingHook(BaseHook):
358
362
  self,
359
363
  interval: int,
360
364
  keep_previous: int = 0, # keep N previous checkpoints
361
- keep_interval: int = 0, # keep checkpoints of every N-th step
365
+ keep_interval: int = 0, # save and keep checkpoints of every N-th step
366
+ keep_steps: Sequence[int]
367
+ | None = None, # save and keep checkpoints at these steps
362
368
  path: Path | str = "checkpoints",
363
369
  load: Path | str | Literal["latest"] | None = "latest",
364
370
  exit_signals: list[signal.Signals] | signal.Signals = None,
@@ -370,6 +376,7 @@ class CheckpointingHook(BaseHook):
370
376
  self.interval = interval
371
377
  self.keep_previous = keep_previous
372
378
  self.keep_interval = keep_interval
379
+ self.keep_steps = set(keep_steps or [])
373
380
  self.path = Path(path)
374
381
  self.load_path = Path(load) if load is not None else None
375
382
 
@@ -435,18 +442,23 @@ class CheckpointingHook(BaseHook):
435
442
  save_and_exit = exit_signal != -1
436
443
 
437
444
  # NOTE: Check if last step here (not in on_after_train) to avoid saving twice
438
- if (
445
+ should_keep = trainer.step in self.keep_steps or (
446
+ self.keep_interval > 0 and trainer.step % self.keep_interval == 0
447
+ )
448
+ should_save = (
439
449
  trainer.step % self.interval == 0
440
450
  or trainer.step == trainer.max_steps
451
+ or should_keep
441
452
  or save_and_exit
442
- ):
453
+ )
454
+ if should_save:
443
455
  if save_and_exit:
444
456
  trainer.logger.info(
445
457
  f"=> Caught signal {exit_signal}. Saving checkpoint before exit ..."
446
458
  )
447
459
  self._save_checkpoint(
448
460
  trainer,
449
- keep=self.keep_interval > 0 and trainer.step % self.keep_interval == 0,
461
+ keep=should_keep,
450
462
  )
451
463
  if save_and_exit:
452
464
  _dist_barrier()
@@ -276,7 +276,9 @@ class BaseTrainer:
276
276
  < self.max_non_finite_grad_retries
277
277
  ):
278
278
  non_finite_grad_retry_count += 1
279
- self.step_info["non_finite_grad_retry_count"] = non_finite_grad_retry_count
279
+ self.step_info["non_finite_grad_retry_count"] = (
280
+ non_finite_grad_retry_count
281
+ )
280
282
  self.logger.warning(
281
283
  f"Gradient is non-finite. Retrying step {self.step} (retry {non_finite_grad_retry_count}"
282
284
  + (
File without changes