Coverage for  / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / trainer / callbacks / checkpoint_callback.py: 84%

233 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-10-25 05:45 +0800

1# Copyright 2025-2026 Bytedance Ltd. and/or its affiliates 

2# Copyright 2026 Huawei Technologies Co., Ltd 

3# 

4# Licensed under the Apache License, Version 2.0 (the "License"); 

5# you may not use this file except in compliance with the License. 

6# You may obtain a copy of the License at 

7# 

8# http://www.apache.org/licenses/LICENSE-2.0 

9# 

10# Unless required by applicable law or agreed to in writing, software 

11# distributed under the License is distributed on an "AS IS" BASIS, 

12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

13# See the License for the specific language governing permissions and 

14# limitations under the License. 

15"""CheckpointerCallback --- save/restore policy on top of a Checkpointer.""" 

16 

17__all__ = ["CheckpointerCallback"] 

18 

19import os 

20import random 

21from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple 

22 

23import torch # pylint: disable=forbidden-backend-import 

24 

25from hyper_parallel.components.checkpoint import build_checkpointer 

26from hyper_parallel.components.checkpoint.dcp_checkpointer import ( 

27 STEP_PREFIX, 

28 initialize_optimizer_state, 

29) 

30from hyper_parallel.components.optim.mixed_precision_optimizer import ( 

31 MixedPrecisionOptimizer, 

32) 

33from hyper_parallel.core.distributed_checkpoint.utils import flatten_state_dict 

34from hyper_parallel.models._transformers.model_builder import ( 

35 validate_model_init_dtype, 

36) 

37from hyper_parallel.trainer.runtime.logging import create_logger 

38from hyper_parallel.trainer.runtime.memory import empty_cache 

39from hyper_parallel.trainer.runtime.device import ( 

40 get_device_rng_state, 

41 set_device_rng_state, 

42) 

43from hyper_parallel.models.external_state import CheckpointRuntime, get_model_external_state 

44from .base import Callback, TrainerState 

45 

46 

47if TYPE_CHECKING: 

48 from hyper_parallel.trainer.base import BaseTrainer 

49 

50 

51logger = create_logger(__name__) 

52 

53 

54def _as_list(value: Any) -> List[Any]: 

55 """Normalize an optional single-or-list component into a list.""" 

56 if value is None: 

57 return [] 

58 return list(value) if isinstance(value, list) else [value] 

59 

60 

61def _state_leaf_keys(value: Any) -> frozenset[str]: 

62 """Use DCP's own traversal to list persisted dense optimizer leaves.""" 

63 flat, _ = flatten_state_dict({"optimizer": value}) 

64 return frozenset(key.removeprefix("optimizer.") for key in flat) 

65 

66 

67def _unwrap_single(values: List[Any]) -> Any: 

68 """Collapse a one-element list so single-component runs keep a flat state.""" 

69 if len(values) == 1: 

70 return values[0] 

71 return values 

72 

73 

74class CheckpointerCallback(Callback): 

75 """Decide when to checkpoint and what goes in it. 

76 

77 This callback owns *policy* --- the save cadence, duplicate suppression, and 

78 the mapping between trainer objects and the persisted payload. Writing that 

79 payload to disk and reading it back belongs to the 

80 :class:`~hyper_parallel.components.checkpoint.CheckpointerBase` it delegates 

81 to, so the storage format can change without touching this file. 

82 

83 The payload is the model and optimizer state dicts plus an ``extra_state`` 

84 bundle holding what those cannot represent: ``global_step`` / ``epoch``, the 

85 LR scheduler, the dataloader position, and the CPU / device / Python RNG 

86 states. 

87 

88 Saving and restoring are independent: ``save_ckpt`` gates the write path, 

89 ``restore_from`` the read path. Turning saving off while pointing at a 

90 checkpoint is therefore a supported combination --- start from these weights 

91 and write nothing further. 

92 

93 Restore runs in :meth:`on_train_begin`, i.e. after the model, optimizer, 

94 scheduler and dataloader exist but before the first training step. 

95 """ 

96 

97 def __init__(self, trainer: "BaseTrainer") -> None: 

98 """Read the checkpoint configuration and build the checkpointer.""" 

99 super().__init__(trainer) 

100 ckpt_cfg = trainer.config.checkpoint 

101 # ``save_ckpt`` gates the write path only --- this callback is also 

102 # registered for restore-only runs. Folding it into the cadence fields 

103 # makes "saving is off" structural (there is simply no cadence) instead 

104 # of a second enable check inside every save hook. 

105 self._save_ckpt = ckpt_cfg.save_ckpt 

106 self._checkpoint_dir = ckpt_cfg.checkpoint_dir 

107 self._save_steps = ckpt_cfg.save_steps if self._save_ckpt else 0 

108 self._save_epochs = ckpt_cfg.save_epochs if self._save_ckpt else 0 

109 self._is_async = ckpt_cfg.is_async 

110 self._is_peft = ckpt_cfg.is_peft 

111 self._save_optimizer = ckpt_cfg.save_optimizer 

112 self._save_train_state = ckpt_cfg.save_train_state 

113 self._save_extra_state_per_rank = ckpt_cfg.save_extra_state_per_rank 

114 

115 self._restore_from = ckpt_cfg.restore_from 

116 self._restore_optimizer = ckpt_cfg.restore_optimizer 

117 self._restore_train_state = ckpt_cfg.restore_train_state 

118 self._restore_dataloader_state = ckpt_cfg.restore_dataloader_state 

119 

120 external = get_model_external_state(getattr(trainer, "model", None)) 

121 if external is not None: 

122 if self._is_async or self._is_peft: 

123 raise ValueError("Host external state requires synchronous non-PEFT checkpointing") 

124 if self._save_ckpt and (not self._save_optimizer or not self._save_train_state): 

125 raise ValueError("Host checkpoint requires optimizer and train-state saving") 

126 if self._restore_optimizer != self._restore_train_state: 

127 raise ValueError("Host restore requires optimizer and train state together") 

128 if trainer.mesh.pp_size > 1 and not self._save_extra_state_per_rank: 

129 raise ValueError("Host PP checkpoint requires per-rank extra state") 

130 

131 self._last_saved_step: int = -1 

132 self.checkpointer = build_checkpointer( 

133 extra_state_per_rank=self._save_extra_state_per_rank, 

134 ) 

135 

136 # ------------------------------------------------------------------ 

137 # Hook dispatchers 

138 # ------------------------------------------------------------------ 

139 

140 def on_train_begin(self, state: TrainerState, **kwargs: Any) -> None: 

141 """Log the checkpoint configuration and restore any requested state. 

142 

143 Args: 

144 state: Trainer or external model state. 

145 """ 

146 logger.info( 

147 "Checkpoint configuration: " 

148 "checkpoint_dir=%s, save_ckpt=%s, save_steps=%s, save_epochs=%s, " 

149 "is_async=%s, is_peft=%s, " 

150 "save_extra_state_per_rank=%s, restore_from=%s", 

151 self._checkpoint_dir, 

152 self._save_ckpt, 

153 self._save_steps, 

154 self._save_epochs, 

155 self._is_async, 

156 self._is_peft, 

157 self._save_extra_state_per_rank, 

158 self._restore_from, 

159 ) 

160 self._load_checkpoint() 

161 

162 def on_step_end(self, state: TrainerState, **kwargs: Any) -> None: # pylint: disable=arguments-differ 

163 """Surface a failed async save, then save on the configured step cadence. 

164 

165 Args: 

166 state: Trainer or external model state. 

167 """ 

168 # Asked every step on purpose. An async save that failed can only be 

169 # reported on a thread that cannot raise into this loop, so waiting for the 

170 # next save to notice would throw away every step in between. 

171 self.checkpointer.raise_for_failed_async_save() 

172 if self._save_steps > 0 and state.global_step % self._save_steps == 0: 

173 if state.global_step == self._last_saved_step: 

174 return 

175 self._save_checkpoint(state) 

176 

177 def on_epoch_end(self, state: TrainerState, **kwargs: Any) -> None: 

178 """Save on the configured epoch cadence. 

179 

180 Args: 

181 state: Trainer or external model state. 

182 """ 

183 if self._save_epochs > 0 and (state.epoch + 1) % self._save_epochs == 0: 

184 if state.global_step != self._last_saved_step: 

185 self._save_checkpoint(state) 

186 else: 

187 logger.info( 

188 "Skipping duplicate checkpoint save at epoch_end " 

189 "(global_step %s already saved at step_end).", 

190 state.global_step, 

191 ) 

192 

193 def on_train_end(self, state: TrainerState, **kwargs: Any) -> None: 

194 """Persist the final step, then drain any in-flight async save. 

195 

196 Always saved when saving is on and the step is not already on disk: 

197 losing the last stretch of training to a cadence that happened not to 

198 land on the final step is never what anyone wants. 

199 

200 Args: 

201 state: Trainer or external model state. 

202 """ 

203 if ( 

204 self._save_ckpt 

205 and state.global_step > 0 

206 and state.global_step != self._last_saved_step 

207 ): 

208 # The process is about to exit, so the last checkpoint is written 

209 # synchronously regardless of ``is_async``. 

210 self._save_checkpoint(state, force_sync=True) 

211 self.wait_for_pending_save() 

212 

213 def wait_for_pending_save(self) -> None: 

214 """Block until the checkpointer's in-flight async save is persisted.""" 

215 self.checkpointer.maybe_wait_for_async_save() 

216 

217 # ------------------------------------------------------------------ 

218 # Payload assembly 

219 # ------------------------------------------------------------------ 

220 

221 def _model_state_dict(self) -> Dict[str, Any]: 

222 """Return the model state to persist, trainable-only under PEFT.""" 

223 model = self.trainer.model 

224 state_dict = model.state_dict() 

225 external = get_model_external_state(model) 

226 if external is not None: 

227 missing = set(external.parameters) - set(state_dict) 

228 if missing: 

229 raise RuntimeError(f"External model FQNs missing from state_dict: {sorted(missing)}") 

230 state_dict = {key: value for key, value in state_dict.items() 

231 if key not in external.parameters} 

232 if not self._is_peft: 

233 return state_dict 

234 

235 trainable = { 

236 name for name, param in model.named_parameters() if param.requires_grad 

237 } 

238 return {name: value for name, value in state_dict.items() if name in trainable} 

239 

240 def _collect_extra_state(self, state: TrainerState) -> Dict[str, Any]: 

241 """Build the extra_state bundle (progress / scheduler / dataloader / RNG).""" 

242 # Prefer the iterator snapshot: with background prefetching the loader has 

243 # already advanced past the batch the training step actually consumed. 

244 dataloader_state: Dict[str, Any] = {} 

245 data_iterator = getattr(self.trainer, "data_iterator", None) 

246 if data_iterator is not None and hasattr(data_iterator, "state_dict"): 

247 dataloader_state = data_iterator.state_dict() 

248 elif self.trainer.train_dataloader is not None and hasattr( 

249 self.trainer.train_dataloader, "state_dict" 

250 ): 

251 dataloader_state = self.trainer.train_dataloader.state_dict() 

252 

253 schedulers = _as_list(self.trainer.lr_scheduler) 

254 lr_scheduler_sd = _unwrap_single([sch.state_dict() for sch in schedulers]) 

255 

256 return { 

257 "global_step": state.global_step, 

258 "epoch": state.epoch, 

259 "lr_scheduler": lr_scheduler_sd, 

260 "train_dataloader": dataloader_state, 

261 "rng_state": { 

262 "torch_cpu": torch.get_rng_state(), 

263 "torch_device": get_device_rng_state(), 

264 "python": random.getstate(), 

265 }, 

266 } 

267 

268 def _resume_position(self) -> Tuple[int, int]: 

269 """Return the ``(epoch, step)`` position the training loop resumes from. 

270 

271 Read off the same two values the loops use rather than re-derived from 

272 ``global_step``: they re-enter ``state.epoch`` and skip the optimizer steps 

273 its predecessors consumed, and ``state.epoch`` is restored from the 

274 checkpoint. Dividing ``global_step`` instead would be a second answer that 

275 can disagree with the loop, and it would have to divide by 

276 ``train_steps`` --- the optimizer steps one epoch holds --- not by the 

277 dataloader's length, which counts micro-batches. 

278 

279 Returns: 

280 The epoch training resumes in, and its offset within that epoch. 

281 """ 

282 state = self.trainer.state 

283 return state.epoch, state.global_step - state.epoch * self.trainer.train_steps 

284 

285 # ------------------------------------------------------------------ 

286 # Save 

287 # ------------------------------------------------------------------ 

288 

289 def _save_checkpoint(self, state: TrainerState, force_sync: bool = False) -> None: 

290 """Assemble the payload for this step and hand it to the checkpointer.""" 

291 save_dir = os.path.join(self._checkpoint_dir, f"{STEP_PREFIX}{state.global_step}") 

292 save_async = self._is_async and not force_sync 

293 

294 logger.info( 

295 "Saving checkpoint: global_step=%s, epoch=%s, dir=%s, is_async=%s, " 

296 "extra_state_per_rank=%s, optimizer=%s, train_state=%s", 

297 state.global_step, 

298 state.epoch, 

299 save_dir, 

300 save_async, 

301 self._save_extra_state_per_rank, 

302 self._save_optimizer, 

303 self._save_train_state, 

304 ) 

305 

306 checkpoint_state: Dict[str, Any] = {"model": self._model_state_dict()} 

307 if self._save_optimizer: 

308 checkpoint_state["optimizer"] = _unwrap_single( 

309 [optimizer.state_dict() for optimizer in _as_list(self.trainer.optimizer)] 

310 ) 

311 if self._save_train_state: 

312 checkpoint_state["extra_state"] = self._collect_extra_state(state) 

313 

314 external = get_model_external_state(self.trainer.model) 

315 if external is not None: 

316 persisted_keys = (_state_leaf_keys(checkpoint_state["optimizer"]) 

317 if "optimizer" in checkpoint_state else frozenset()) 

318 external.before_checkpoint_save(CheckpointRuntime( 

319 step_dir=save_dir, global_step=state.global_step, 

320 mesh_context=self.trainer.mesh, 

321 include_optimizer=self._save_optimizer, 

322 include_train_state=self._save_train_state, 

323 persisted_optimizer_keys=persisted_keys, 

324 )) 

325 

326 model_integration = getattr(self.trainer, "model_integration", None) 

327 if model_integration is not None: 

328 model_integration.capture_checkpoint_payload( 

329 "before_save", 

330 save_dir, 

331 checkpoint_state, 

332 ) 

333 

334 self.checkpointer.save( 

335 save_dir, 

336 checkpoint_state, 

337 global_step=state.global_step, 

338 save_async=save_async, 

339 ) 

340 

341 # Bookkeeping reflects the dispatched step immediately, including in async 

342 # mode: otherwise on_epoch_end would queue the same step again while the 

343 # first save is still in flight. 

344 self._last_saved_step = state.global_step 

345 

346 # ------------------------------------------------------------------ 

347 # Load / restore 

348 # ------------------------------------------------------------------ 

349 

350 def _resolve_restore_path(self) -> Optional[str]: 

351 """Resolve ``restore_from`` (including ``LATEST``) to a directory.""" 

352 if self._restore_from is None: 

353 logger.info("No checkpoint to restore (restore_from is None).") 

354 return None 

355 

356 restore_path = self._restore_from 

357 if restore_path.upper() == "LATEST": 

358 logger.info( 

359 "restore_from='LATEST', searching for the latest checkpoint in %s", 

360 self._checkpoint_dir, 

361 ) 

362 resolved = self.checkpointer.find_latest_checkpoint(self._checkpoint_dir) 

363 if resolved is None: 

364 logger.warning( 

365 "restore_from='LATEST' but no checkpoint found in %s; " 

366 "starting from scratch.", 

367 self._checkpoint_dir, 

368 ) 

369 return None 

370 logger.info("Resolved LATEST checkpoint: %s", resolved) 

371 return resolved 

372 

373 if not os.path.isdir(restore_path): 

374 raise FileNotFoundError(f"Checkpoint directory not found: {restore_path}") 

375 return restore_path 

376 

377 def _build_restore_skeleton(self, restore_path: str, external: Any) -> tuple[Dict[str, Any], List[Any]]: 

378 """Initialize lazy dense moments and create the DCP load skeleton.""" 

379 optimizers = _as_list(self.trainer.optimizer) if self._restore_optimizer else [] 

380 for optimizer in optimizers: 

381 target = external.optimizer_for_dcp() if external is not None else optimizer 

382 if not initialize_optimizer_state(target): 

383 logger.warning( 

384 "Could not materialize optimizer state before loading; " 

385 "optimizer moments may not be restored from %s.", 

386 restore_path, 

387 ) 

388 

389 checkpoint_state: Dict[str, Any] = {"model": self._model_state_dict()} 

390 if optimizers: 

391 checkpoint_state["optimizer"] = _unwrap_single( 

392 [optimizer.state_dict() for optimizer in optimizers] 

393 ) 

394 return checkpoint_state, optimizers 

395 

396 def _load_optimizer_payload(self, checkpoint_state: Dict[str, Any], optimizers: List[Any], 

397 persisted_keys: Optional[frozenset[str]]) -> None: 

398 """Restore dense optimizer leaves or reload dense main parameters.""" 

399 optimizer_sds = _as_list(checkpoint_state.get("optimizer")) 

400 for optimizer, optimizer_sd in zip(optimizers, optimizer_sds): 

401 optimizer.load_state_dict(optimizer_sd) 

402 drop_primed = getattr(optimizer, "drop_unpersisted_dense_state", None) 

403 if drop_primed is not None and persisted_keys is not None: 

404 drop_primed(persisted_keys) 

405 if not optimizers: 

406 for optimizer in _as_list(self.trainer.optimizer): 

407 if isinstance(optimizer, MixedPrecisionOptimizer): 

408 optimizer.reload_model_params() 

409 

410 def _apply_restored_payload(self, checkpoint_state: Dict[str, Any], optimizers: List[Any], 

411 external: Any, runtime: Any, restore_path: str, 

412 load_plan: Any) -> None: 

413 """Install the loaded dense and external model, optimizer, and train state.""" 

414 if external is not None: 

415 full_model_keys = set(self.trainer.model.state_dict()) 

416 dense_model_keys = set(checkpoint_state["model"]) 

417 if full_model_keys - dense_model_keys != set(external.parameters): 

418 raise RuntimeError("Dense checkpoint model keys differ from exact external exclusions") 

419 load_result = self.trainer.model.load_state_dict( 

420 checkpoint_state["model"], strict=not self._is_peft and external is None 

421 ) 

422 if external is not None and load_result is not None and ( 

423 set(load_result.missing_keys) != set(external.parameters) 

424 or load_result.unexpected_keys): 

425 raise RuntimeError("Dense checkpoint model keys differ from exact external exclusions") 

426 validate_model_init_dtype( 

427 self.trainer.model, 

428 self.trainer.config.model_init_dtype, 

429 ) 

430 persisted_keys = load_plan.persisted_optimizer_keys if load_plan is not None else None 

431 self._load_optimizer_payload(checkpoint_state, optimizers, persisted_keys) 

432 

433 if self._restore_train_state: 

434 self._apply_extra_state(checkpoint_state["extra_state"]) 

435 else: 

436 logger.info( 

437 "restore_train_state=False: loaded weights only from %s " 

438 "(step, scheduler, dataloader and RNG start fresh).", 

439 restore_path, 

440 ) 

441 

442 if external is not None: 

443 external.after_checkpoint_load(runtime, load_plan) 

444 if not self._restore_optimizer: 

445 dense = external.optimizer_for_dcp() 

446 if isinstance(dense, MixedPrecisionOptimizer): 

447 dense.reload_model_params() 

448 external.after_weights_only_load() 

449 

450 def _load_checkpoint(self) -> None: 

451 """Restore a checkpoint into the trainer's live objects.""" 

452 restore_path = self._resolve_restore_path() 

453 if restore_path is None: 

454 return 

455 

456 logger.info("Loading checkpoint from %s", restore_path) 

457 external = get_model_external_state(self.trainer.model) 

458 runtime = None 

459 load_plan = None 

460 if external is not None: 

461 runtime = CheckpointRuntime( 

462 step_dir=restore_path, global_step=None, 

463 mesh_context=self.trainer.mesh, 

464 include_optimizer=self._restore_optimizer, 

465 include_train_state=self._restore_train_state, 

466 ) 

467 load_plan = external.before_checkpoint_load(runtime) 

468 

469 checkpoint_state, optimizers = self._build_restore_skeleton(restore_path, external) 

470 

471 # The skeleton gives an embedded extra_state bundle keys to be read into; 

472 # the checkpointer decides whether it is actually needed for this layout. 

473 extra_state_skeleton = ( 

474 self._collect_extra_state(self.trainer.state) 

475 if self._restore_train_state 

476 else None 

477 ) 

478 

479 self.checkpointer.load( 

480 restore_path, 

481 checkpoint_state, 

482 strict_model=not self._is_peft, 

483 extra_state_skeleton=extra_state_skeleton, 

484 ) 

485 if load_plan is not None and self._restore_optimizer: 

486 missing_optimizer_keys = (load_plan.persisted_optimizer_keys 

487 - _state_leaf_keys(checkpoint_state["optimizer"])) 

488 if missing_optimizer_keys: 

489 raise RuntimeError(f"Dense optimizer skeleton lost persisted keys: {sorted(missing_optimizer_keys)}") 

490 model_integration = getattr(self.trainer, "model_integration", None) 

491 if model_integration is not None: 

492 model_integration.capture_checkpoint_payload( 

493 "after_load", 

494 restore_path, 

495 checkpoint_state, 

496 ) 

497 

498 self._apply_restored_payload( 

499 checkpoint_state, optimizers, external, runtime, restore_path, 

500 load_plan, 

501 ) 

502 

503 empty_cache() 

504 # Computed here because this log is the only consumer: the training loops 

505 # derive their own starting position from ``state`` instead, so carrying it 

506 # on the trainer would be state nobody reads. 

507 start_epoch, start_step = self._resume_position() 

508 logger.info( 

509 "Checkpoint loaded successfully: path=%s, global_step=%s, " 

510 "start_epoch=%s, start_step=%s", 

511 restore_path, 

512 self.trainer.state.global_step, 

513 start_epoch, 

514 start_step, 

515 ) 

516 

517 def _apply_extra_state(self, extra: Dict[str, Any]) -> None: 

518 """Restore progress, scheduler, dataloader position and RNG state.""" 

519 trainer = self.trainer 

520 trainer.state.global_step = extra["global_step"] 

521 persisted_epoch = extra.get("epoch", 0) 

522 trainer.state.epoch = persisted_epoch 

523 

524 # The restored step is already on disk. Without this, resuming a run that 

525 # had nothing left to do would have ``on_train_end`` rewrite the very 

526 # checkpoint it just loaded. 

527 self._last_saved_step = trainer.state.global_step 

528 

529 lr_scheduler_sd = extra.get("lr_scheduler") 

530 schedulers = _as_list(trainer.lr_scheduler) 

531 if lr_scheduler_sd and schedulers: 

532 scheduler_sds = _as_list(lr_scheduler_sd) 

533 if len(scheduler_sds) != len(schedulers): 

534 logger.warning( 

535 "Checkpoint carries %s LR scheduler state dict(s) but this " 

536 "run has %s scheduler(s); only the first %s pair(s) are " 

537 "restored and any extra scheduler(s) keep their freshly " 

538 "initialized state.", 

539 len(scheduler_sds), 

540 len(schedulers), 

541 min(len(scheduler_sds), len(schedulers)), 

542 ) 

543 for scheduler, scheduler_sd in zip(schedulers, scheduler_sds): 

544 scheduler.load_state_dict(scheduler_sd) 

545 

546 dataloader_sd = extra.get("train_dataloader") 

547 if dataloader_sd and not self._restore_dataloader_state: 

548 logger.info( 

549 "restore_dataloader_state=False: retaining the configured " 

550 "dataloader start instead of restoring its checkpoint cursor." 

551 ) 

552 elif dataloader_sd and hasattr(trainer.train_dataloader, "load_state_dict"): 

553 trainer.train_dataloader.load_state_dict(dataloader_sd) 

554 elif dataloader_sd: 

555 logger.warning( 

556 "Checkpoint carries a dataloader position but %s is not stateful; " 

557 "the resumed epoch replays samples from its start.", 

558 type(trainer.train_dataloader).__name__, 

559 ) 

560 

561 rng_state = extra.get("rng_state") or {} 

562 torch_cpu_rng = rng_state.get("torch_cpu") 

563 if torch_cpu_rng is not None: 

564 torch.set_rng_state(torch_cpu_rng) 

565 set_device_rng_state(rng_state.get("torch_device")) 

566 python_rng = rng_state.get("python") 

567 if python_rng is not None: 

568 random.setstate(python_rng)