Files
wehub-resource-sync 2aaeece67c
Codestyle Check / Lint (push) Has been cancelled
Codestyle Check / Check bypass (push) Has been cancelled
Pipelines-Test / Pipelines-Test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:37:14 +08:00

213 lines
8.5 KiB
Python

# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import paddle
from ...trainer.argparser import strtobool
from ...trainer.trainer import logger
@paddle.no_grad()
def _move_param(src, device=None, blocking=True):
"""
Move parameters from the source device to the target device and return the parameters on the target device.
If the target device is not specified, the current device is used.
Args:
src (Tensor): The tensor of parameters to be moved.
device (Optional[Union[str, paddle.Device]], optional): The target device. Can be a string or paddle.Device object.
Defaults to None, which means using the current device.
blocking (bool, optional): Whether to block until the operation is complete. Defaults to True.
Returns:
Tensor: The tensor of parameters on the target device.
"""
if isinstance(device, str):
device = paddle.device._convert_to_place(device)
dst = src._copy_to(device, blocking)
dst_tensor = dst.value().get_tensor()
src_tensor = src.value().get_tensor()
src_tensor._clear()
src_tensor._share_data_with(dst_tensor)
def offload_tensor_to_cpu(tensors):
"""
Migrate the given tensors to CPU. This function has no effect if CUDA managed memory is used.
Args:
tensors (tuple, list): A tuple or list containing two elements. The first element is the model or optimizer,
and the second element is a string indicating whether it is a model or optimizer.
Returns:
None: No return value, modifies the original tensors directly.
Raises:
None: Does not raise any exceptions.
"""
if strtobool(os.getenv("FLAGS_use_cuda_managed_memory", "False")):
logger.warning("FLAGS_use_cuda_managed_memory has been set to True, offloading strategy is ineffective.")
return
pin_device = paddle.CUDAPinnedPlace()
def clear_main_grad(model):
for param in model.parameters():
if hasattr(param, "main_grad") and param.main_grad is not None:
param.main_grad._clear_data()
param.main_grad = None
# optimizer
if "optimizer" in tensors[1]:
optimizer = tensors[0]
# offload moment1
for key, value in optimizer._accumulators[optimizer._moment1_acc_str].items():
if value._is_initialized() and not isinstance(value.place, paddle.CUDAPinnedPlace):
optimizer._accumulators[optimizer._moment1_acc_str][key] = value.pin_memory()
# offload moment2
for key, value in optimizer._accumulators[optimizer._moment2_acc_str].items():
if value._is_initialized() and not isinstance(value.place, paddle.CUDAPinnedPlace):
optimizer._accumulators[optimizer._moment2_acc_str][key] = value.pin_memory()
# offload master_weight
for key, value in optimizer._master_weights.items():
if value._is_initialized() and not isinstance(value.place, paddle.CUDAPinnedPlace):
optimizer._master_weights[key] = value.pin_memory()
# model
elif "model" in tensors[1]:
model = tensors[0]
clear_main_grad(model)
for name, src in model.named_parameters():
if src._is_initialized() and not isinstance(src.place, paddle.CUDAPinnedPlace):
_move_param(src, pin_device)
elif "tensor" in tensors[1]:
src = tensors[0]
if src._is_initialized() and not isinstance(src.place, paddle.CUDAPinnedPlace):
_move_param(src, pin_device)
else:
logger.debug(f"[offload_tensor_to_cpu]Can't parse for type {tensors[1]}")
def reload_tensor_to_gpu(tensors):
"""
Transfer the given tensors from CPU to GPU and return new tensors. This function has no effect if the environment variable
FLAGS_use_cuda_managed_memory is not set to True.
Args:
tensors (List[Tuple[Any, str]]): A list containing tuples. Each tuple has two elements: the tensor to be transferred
to GPU and a string indicating the tensor type ("optimizer" or "model").
Returns:
List[Tuple[Any, str]]: The same list as the input, but all tensors have been transferred to GPU.
Raises:
None.
"""
if strtobool(os.getenv("FLAGS_use_cuda_managed_memory", "False")):
logger.warning("FLAGS_use_cuda_managed_memory has been set to True, offloading strategy is ineffective.")
return
# optimizer
if "optimizer" in tensors[1]:
optimizer = tensors[0]
# offload moment1
for key, value in optimizer._accumulators[optimizer._moment1_acc_str].items():
if value._is_initialized() and not isinstance(value.place, paddle.CUDAPlace):
optimizer._accumulators[optimizer._moment1_acc_str][key] = value.cuda()
# offload moment2
for key, value in optimizer._accumulators[optimizer._moment2_acc_str].items():
if value._is_initialized() and not isinstance(value.place, paddle.CUDAPlace):
optimizer._accumulators[optimizer._moment2_acc_str][key] = value.cuda()
# offload master_weight
for key, value in optimizer._master_weights.items():
if value._is_initialized() and not isinstance(value.place, paddle.CUDAPlace):
optimizer._master_weights[key] = value.cuda()
# model
elif "model" in tensors[1]:
model = tensors[0]
device = paddle.device.get_device()
for name, src in model.named_parameters():
if src._is_initialized() and not isinstance(src.place, paddle.CUDAPlace):
_move_param(src, device)
else:
logger.debug(f"[reload_tensor_to_gpu]Can't parse for type {tensors[1]}")
class OffloadController:
def __init__(self, objs):
self.objs = objs
def __enter__(self):
for obj in self.objs:
if hasattr(obj[0], "enable"):
obj[0].enable()
else:
if obj[1] != "":
reload_tensor_to_gpu(obj)
# offload_tensor_to_cpu/reload_tensor_to_gpu use non-blocking copy
# maybe overlap with compute later
if len(self.objs) > 0:
paddle.device.synchronize()
def __exit__(self, *args):
for obj in self.objs:
if hasattr(obj[0], "disable"):
obj[0].disable()
else:
if obj[1] != "":
offload_tensor_to_cpu(obj)
# offload_tensor_to_cpu/reload_tensor_to_gpu use non-blocking copy
# maybe overlap with compute later
if len(self.objs) > 0:
paddle.device.synchronize()
def reload_and_offload_scope(trainer, *args):
offload_map = {
trainer.actor_model: "train_model",
trainer.reference_model: "freeze_model",
**(
{trainer.reward_model: "freeze_model"}
if not trainer.args.use_rm_server and not trainer.args.use_rule_reward
else {}
),
trainer.actor_trainer.optimizer: "optimizer",
}
if trainer.args.rl_algorithm == "ppo":
offload_map.update(
{
trainer.critic_model: "train_model",
trainer.critic_trainer.optimizer: "optimizer",
}
)
objs = [(arg, offload_map.get(arg, "")) for arg in args if offload_map.get(arg, "") in trainer.args.offload_level]
if trainer.actor_model not in [i for i, _ in objs]:
if getattr(trainer.actor_trainer, "_inner_eval_model", None) is not None:
# NOTE(gongenlei): for export_evaluate_model
objs.append((trainer.actor_model, offload_map.get(trainer.actor_model, "")))
if trainer.args.rl_algorithm == "ppo":
if trainer.critic_model not in [i for i, _ in objs]:
if getattr(trainer.critic_trainer, "_inner_eval_model", None) is not None:
# NOTE(gongenlei): for export_evaluate_model
objs.append((trainer.critic_model, offload_map.get(trainer.critic_model, "")))
return OffloadController(objs)