forked from NVIDIA/cosmos-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdataloader_state.py
More file actions
107 lines (88 loc) · 3.92 KB
/
Copy pathdataloader_state.py
File metadata and controls
107 lines (88 loc) · 3.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: OpenMDW-1.1
from __future__ import annotations
import os
from dataclasses import dataclass
from typing import Any
import torch
from cosmos_framework.model._base import ImaginaireModel
from cosmos_framework.utils import log
from cosmos_framework.utils.callback import Callback
@dataclass
class NoReplaceShardlistState:
epoch: int = 0
index: int = 0
class DataLoaderStateCallback(Callback):
checkpoint_component: str = "dataloader"
def __init__(
self,
distributor_type: str | None = None,
) -> None:
super().__init__()
self.distributor_type = distributor_type
self.config: Any = None
self.state: dict[int, NoReplaceShardlistState] = {}
self.verbose = True
def _update_state_from_batch(self, data_batch: dict[str, torch.Tensor]) -> None:
worker_ids = data_batch["sample_worker_id"].tolist() # [B]
epochs = data_batch["sample_epoch"].tolist() # [B]
indices = data_batch["sample_index"].tolist() # [B]
for worker_id, epoch, index in zip(worker_ids, epochs, indices, strict=True):
if worker_id not in self.state:
self.state[worker_id] = NoReplaceShardlistState(epoch=epoch, index=index)
elif self.state[worker_id].epoch < epoch or (
self.state[worker_id].index < index and self.state[worker_id].epoch == epoch
):
self.state[worker_id] = NoReplaceShardlistState(epoch=epoch, index=index)
def on_training_step_batch_end(
self,
model: ImaginaireModel,
data_batch: dict[str, torch.Tensor],
output_batch: dict[str, torch.Tensor],
loss: torch.Tensor,
iteration: int = 0,
) -> None:
if self.distributor_type == "no_replace":
self._update_state_from_batch(data_batch)
def on_training_step_end(
self,
model: ImaginaireModel,
data_batch: dict[str, torch.Tensor],
output_batch: dict[str, torch.Tensor],
loss: torch.Tensor,
iteration: int = 0,
) -> None:
if self.distributor_type == "no_replace":
if self.verbose:
if iteration % self.config.trainer.logging_iter == 0:
msg = "\n"
for wid, state in self.state.items():
msg += f"worker {wid}: epoch={state.epoch}, index={state.index}\n"
log.info(msg)
def has_checkpoint_state(self) -> bool:
return self.distributor_type == "no_replace"
def state_dict(self) -> dict[int, dict[str, int]]:
if self.distributor_type != "no_replace":
return {}
state_dict: dict[int, dict[str, int]] = {}
for worker_id, per_worker_state in self.state.items():
state_dict[worker_id] = {"epoch": per_worker_state.epoch, "index": per_worker_state.index}
log.info(
f"Saved dataloader state for worker {worker_id}: "
f"epoch={per_worker_state.epoch}, index={per_worker_state.index}"
)
return state_dict
def load_state_dict(self, state_dict: dict[int, dict[str, int]]) -> None:
if self.distributor_type != "no_replace":
return
if not state_dict:
log.info("No dataloader state found in checkpoint")
return
self.state = {}
for worker_id, per_worker_state in state_dict.items():
epoch = per_worker_state["epoch"]
index = per_worker_state["index"]
self.state[worker_id] = NoReplaceShardlistState(epoch=epoch, index=index)
os.environ[f"NSL_STATE_WORKER_{worker_id}_EPOCH"] = str(epoch)
os.environ[f"NSL_STATE_WORKER_{worker_id}_INDEX"] = str(index)
log.info(f"Loaded no replace dataloader state for worker {worker_id}: epoch={epoch}, index={index}")