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

569 lines
22 KiB
Python

# Copyright (c) 2023 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.
from __future__ import annotations
from dataclasses import dataclass, field
import paddle
from ...trainer.trainer import ShardingOption, TrainingArguments, logger
from ...trainer.trainer_utils import IntervalStrategy
from ...transformers.configuration_utils import llmmetaclass
@dataclass
@llmmetaclass
class TrainingArguments(TrainingArguments):
global_batch_size: int = field(
default=8,
metadata={"help": "Global batch size for input prompt."},
)
global_gen_batch_size: int = field(
default=-1,
metadata={"help": "Global generation batch size for dynamic sampling."},
)
global_mini_batch_size: int = field(
default=-1,
metadata={"help": "Mini-batch size (global) for the training dataloader."},
)
per_device_rollout_batch_size: int = field(
default=-1,
metadata={"help": "Batch size (per device) for the training dataloader."},
)
per_device_logprob_batch_size: int = field(
default=-1,
metadata={"help": "Batch size (per device) for the training dataloader."},
)
per_device_reward_batch_size: int = field(
default=-1,
metadata={"help": "Batch size (per device) for the training dataloader."},
)
per_device_value_batch_size: int = field(
default=-1,
metadata={"help": "Batch size (per device) for the training dataloader."},
)
per_device_train_batch_size: int = field(
default=1,
metadata={"help": "Batch size (per device) for the training dataloader."},
)
kl_coeff: float = field(
default=0.02,
metadata={"help": "The coefficient for the KL divergence between the reference and actor policy."},
)
kl_loss_coeff: float = field(
default=0.001,
metadata={"help": "The coefficient for the KL loss for GRPO."},
)
pg_loss_coeff: float = field(
default=1.0,
metadata={"help": "The coefficient for the PG loss for GRPO."},
)
entropy_coeff: float = field(
default=0.0,
metadata={"help": "The coefficient for the entropy loss for GRPO."},
)
clip_range_ratio: float = field(
default=0.2,
metadata={
"help": "The clipping range for ratio between the old and new policy. "
"This is the epsilon parameter in the PPO algorithm."
},
)
clip_range_ratio_low: float = field(
default=None,
metadata={
"help": "The clipping range for ratio between the old and new policy. "
"This is the epsilon parameter in the PPO algorithm."
},
)
clip_range_ratio_high: float = field(
default=None,
metadata={
"help": "The clipping range for ratio between the old and new policy. "
"This is the epsilon parameter in the PPO algorithm."
},
)
clip_range_score: float = field(
default=10.0,
metadata={
"help": "The clipping range for the output of the score model. "
"The reward is clipped into [-clip_range_score, clip_range_score]."
},
)
enable_overlong_reward_buffer: bool = field(
default=False,
metadata={},
)
overlong_reward_buffer: int = field(
default=256,
metadata={"help": "The allowed buffer before applying penalty."},
)
overlong_penalty_factor: float = field(
default=1.0,
metadata={
"help": "The penalty factor for the overlong reward buffer. "
"The penalty is deleted to the reward when the buffer is full."
},
)
clip_range_value: float = field(
default=5.0,
metadata={
"help": "The clipping range for the value function. The value is clipped into [value_estimate - "
"clip_range_value, value_estimate + clip_range_value] during training."
},
)
update_iters: int = field(
default=1,
metadata={"help": "The number of repeated updates on a generated batch."},
)
critic_learning_rate: float = field(
default=None,
metadata={"help": "Initial learning rate (after the potential warmup period) for the critic model training."},
)
critic_weight_decay: float = field(
default=None,
metadata={"help": "Weight decay to for the critic model training."},
)
critic_lr_scheduler_type: str = field(
default=None,
metadata={"help": "The scheduler type for critic model."},
)
critic_warmup_ratio: float = field(
default=None,
metadata={"help": "Ratio of warm steps over total training steps for the critic lr scheduler."},
)
critic_recompute: bool = field(
default=None,
metadata={"help": "Enable gradient checkpointing for critic model."},
)
normalize_reward: bool = field(
default=None,
metadata={"help": "Whether to normalize the reward during RL training."},
)
normalize_advantage: bool = field(
default=None,
metadata={"help": "Whether to normalize the advantage during RL training."},
)
temperature: float = field(
default=1.0,
metadata={"help": "The value used to module the next token probabilities."},
)
top_p: float = field(
default=1.0,
metadata={
"help": "If set to float < 1, only the smallest set of most probable tokens "
"with probabilities that add up to`top_p` or higher are kept for generation."
},
)
rollout_n: int = field(
default=1,
metadata={"help": "The number of independently computed returned sequences for each element in the batch."},
)
repetition_penalty: float = field(
default=1.0,
metadata={"help": "The parameter for repetition penalty. 1.0 means no penalty."},
)
rollout_quant_type: str = field(
default="",
metadata={"help": "Quantization dtype, optional for: weight_onlt_int8."},
)
per_device_prompt_batch_size: int = field(
default=16,
metadata={"help": "Batch size (per device) for the training dataloader."},
)
dynamic_sampling: bool = field(
default=False,
metadata={"help": "whether enable dynamic sample https://arxiv.org/abs/2503.14476"},
)
max_gen_batches: int = field(
default=32,
metadata={"help": "max gen batches for dynamic sampling"},
)
eval_mode: str = field(
default=None,
metadata={
"help": "eval mode for actor model and reward_critic_model, optional for: None, single, tensor_parallel."
},
)
offload_level: str = field(
default="",
metadata={"help": "Offload model, optional for: eval, reward, optimizer, train_model"},
)
max_dec_len: int = field(default=512, metadata={"help": "Maximum output length."})
min_dec_len: int = field(default=1, metadata={"help": "Minimum output length."})
max_src_len: int = field(default=3072, metadata={"help": "Max length of src."})
eos_token: str = field(
default="",
metadata={"help": "Use it as an eos_token if set it to non empty."},
)
use_fusemt: bool = field(
default=True,
metadata={"help": "use fused inference model to speedup in rollout generation"},
)
recompute_use_reentrant: bool = field(
default=True,
metadata={"help": "use recompute_use_reentrant to recompute"},
)
critic_min_learning_rate: float = field(
default=None,
metadata={"help": "Minimum learning rate deacyed to for critic model."},
)
critic_decay_steps: int = field(
default=None,
metadata={
"help": "The steps use to control the learing rate for critic model. If the step > decay_steps, "
"will use the min_learning_rate."
},
)
min_learning_rate: float = field(
default=None,
metadata={"help": "Minimum learning rate deacyed to."},
)
decay_steps: int = field(
default=None,
metadata={
"help": "The steps use to control the learing rate. If the step > decay_steps, "
"will use the min_learning_rate."
},
)
autotuner_benchmark: bool = field(
default=False,
metadata={"help": "Whether to run benchmark by autotuner. True for from_scratch."},
)
early_stopping: bool = field(
default=False,
metadata={"help": "Whether apply early stopping strategy."},
)
early_stopping_patience: int = field(
default=4,
metadata={
"help": "Stop training when the specified metricworsens for early_stopping_patience evaluation calls"
},
)
early_stopping_threshold: float = field(
default=0.0,
metadata={"help": "how much the specified metric must improve to satisfy early stopping conditions."},
)
use_fused_head_and_loss_fn: bool = field(
default=False,
metadata={"help": "use fused_head_and_loss_fn."},
)
tensor_parallel_output: bool = field(
default=True,
metadata={"help": "use tensor_parallel_output."},
)
# save_generation_output: bool = field(
# default=False,
# metadata={"help": "Whether to save generated text to file when eval"},
# )
dropout_warmup_steps: int = field(
default=0,
metadata={"help": "dropout warmup steps"},
)
hidden_dropout_prob: float = field(
default=0.0,
metadata={"help": "dropout probability for hidden layers"},
)
attention_probs_dropout_prob: float = field(
default=0.0,
metadata={"help": "dropout probability for attention layers"},
)
rl_algorithm: str = field(
default="ppo",
metadata={"help": "RL algorithm (supports PPO, GRPO and Reinforce++)."},
)
use_tgt_len_value: bool = field(
default=False,
metadata={"help": "Whether to use tgt for KL."},
)
use_rm_server: bool = field(default=False, metadata={"help": "Use reward server instead of reward model."})
use_rule_reward: bool = field(default=False, metadata={"help": "Use rule-based reward only for gsm8k, to date."})
use_fp32_compute: bool = field(
default=False, metadata={"help": "Use fp32 to compute xx_log_prob,rewards, advantages and loss."}
)
rollout_tensor_parallel_degree: int = field(
default=-1,
metadata={"help": ("Tensor parallelism for rollout.")},
)
balance_batch: bool = field(
default=False,
metadata={"help": "Whether to balance the number of valid tokens on each dp/sharding rank."},
)
use_remove_padding: bool = field(
default=False,
metadata={"help": "Whether to remove paddings before computing transformer."},
)
rollout_max_num_seqs: int = field(
default=8,
metadata={
"help": "The maximum number of sequences that can be processed in a single inference. Default is 8."
},
)
def __post_init__(self):
"""
Function executed after initialization, used to set some default values and validate parameters.
If autotuner_benchmark is True, set related parameters to default values and prohibit any other operations.
Args:
None.
Returns:
None.
Raises:
None.
"""
# set the unified_checkpoint to True, it will change two cases:
# 1. use unified_checkpoint
# 2. data_parallel use hybrid group
self.unified_checkpoint = True
# obtain the parallrl degree from the training arguments
# for auto config the accumulation steps
self._post_init_parallel_degree()
if self.global_mini_batch_size < 0:
self.global_mini_batch_size = self.global_batch_size
if (
self.global_batch_size % self.dataset_world_size != 0
or self.global_mini_batch_size % self.dataset_world_size != 0
):
raise ValueError(
"global_batch_size(global_mini_batch_size) must be divisible by dataset_world_size! "
f"Hint: global_batch_size={self.global_batch_size}, global_mini_batch_size={self.global_mini_batch_size}, dataset_world_size={self.dataset_world_size}. "
f"dataset_world_size({self.dataset_world_size})=data_parallel_degree({self.data_parallel_degree})*sharding_parallel_degree({self.sharding_parallel_degree})."
)
if not self.dynamic_sampling or self.global_gen_batch_size <= 0:
self.global_gen_batch_size = self.global_batch_size
if self.per_device_rollout_batch_size <= 0:
self.per_device_rollout_batch_size = self.global_batch_size // self.dataset_world_size
if self.per_device_logprob_batch_size <= 0:
self.per_device_logprob_batch_size = self.per_device_train_batch_size
if self.per_device_reward_batch_size <= 0:
self.per_device_reward_batch_size = self.per_device_train_batch_size
if self.per_device_value_batch_size <= 0:
self.per_device_value_batch_size = self.per_device_train_batch_size
# conserve kv cache, select the minimum value as the rollout max num seqs for the inference engine
# self.rollout_max_num_seqs = min(self.per_device_rollout_batch_size * self.rollout_n, self.rollout_max_num_seqs)
# `gradient_accumulation_steps` specifies the number of mini-batches per gradient update.
# This value must be set prior to calling `super().__post_init__()`.
# It is utilized within `super().__post_init__()` for configuring the DistributedStrategy.
self.gradient_accumulation_steps = (
self.global_mini_batch_size
* self.rollout_n
* self.update_iters
// self.per_device_train_batch_size
// self.dataset_world_size
)
if self.gradient_accumulation_steps <= 0:
logger.warning(
f"gradient_accumulation_steps: {self.gradient_accumulation_steps} must be greater than zero!"
" Please check your configuration, gradient_accumulation_steps = global_mini_batch_size * rollout_n * update_iters / per_device_train_batch_size / dataset_world_size."
" dataset_world_size = {self.dataset_world_size} = data_parallel_degree * sharding_parallel_degree."
" We will set it to 1!"
)
self.gradient_accumulation_steps = 1
train_batch_size_info = {
"global_batch_size": self.global_batch_size,
"global_mini_batch_size": self.global_mini_batch_size,
"rollout_n": self.rollout_n,
"rollout_max_num_seqs": self.rollout_max_num_seqs,
"dataset_world_size": self.dataset_world_size,
"per_device_rollout_batch_size": self.per_device_rollout_batch_size,
"per_device_logprob_batch_size": self.per_device_logprob_batch_size,
"per_device_reward_batch_size": self.per_device_reward_batch_size,
"per_device_value_batch_size": self.per_device_value_batch_size,
"per_device_train_batch_size": self.per_device_train_batch_size,
"gradient_accumulation_steps": self.gradient_accumulation_steps,
}
logger.info("{:^40}".format("{} Configuration Arguments".format("Train Batch Size")))
for key, value in train_batch_size_info.items():
logger.info("{:30}: {}".format(key, value))
logger.info("===========================================")
super().__post_init__()
if self.autotuner_benchmark:
self.num_train_epochs = 1
self.max_steps = 5
self.do_train = True
self.do_export = False
self.do_predict = False
self.do_eval = False
self.overwrite_output_dir = True
self.load_best_model_at_end = False
self.report_to = []
self.save_strategy = IntervalStrategy.NO
self.evaluation_strategy = IntervalStrategy.NO
self.per_device_prompt_batch_size = self.per_device_train_batch_size
self.min_dec_len = self.max_dec_len
# self.skip_profile_timer = False
if not self.disable_tqdm:
self.logging_steps = 1
self.logging_strategy = IntervalStrategy.STEPS
paddle.set_device(self.device)
assert self.rl_algorithm in [
"ppo",
"grpo",
"reinforce_plus_plus",
], 'self.rl_algorithm should be one of ["ppo", "grpo", "reinforce_plus_plus"]'
if self.rl_algorithm == "grpo":
self.normalize_reward = False
self.normalize_advantage = False
max_per_device_eval_batch_size = (
self.global_batch_size * self.rollout_n * self.update_iters // self.dataset_world_size
)
if self.per_device_eval_batch_size > max_per_device_eval_batch_size:
logger.warning(
f"per_device_eval_batch_size: {self.per_device_eval_batch_size} is larger than "
f"global_batch_size: {self.global_batch_size} * rollout_n: "
f"{self.rollout_n} * update_iters: {self.update_iters}, which may cause infer error. "
f"We will set it to global_batch_size * rollout_n * update_iters // dataset_world_size!"
)
self.per_device_eval_batch_size = max_per_device_eval_batch_size
self.offload_level = self.offload_level.split()
if self.sequence_parallel:
if self.tensor_parallel_degree <= 1:
self.sequence_parallel = False
logger.info("Tensor_parallel_degree = 1. Set sequence_parallel to False.")
if self.tensor_parallel_degree <= 1:
self.tensor_parallel_output = False
logger.info("Tensor_parallel_degree = 1. Set tensor_parallel_output to False.")
if self.sharding_parallel_degree > 1:
if ShardingOption.SHARD_GRAD_OP in self.sharding or ShardingOption.FULL_SHARD in self.sharding:
if self.release_grads is True:
self.release_grads = False
if self.unified_checkpoint and "async_save" in self.unified_checkpoint_config:
self.unified_checkpoint_config.remove("async_save")
logger.warning(
"PPO training currently does not support asynchronous saving! "
"Remove `async_save` from unified_checkpoint_config."
)
if self.eval_mode is not None and len(self.eval_mode) == 0:
self.eval_mode = None
# if self.eval_mode is None and self.offload_level is not None:
# self.offload_level = self.offload_level.replace("eval", "")
if self.decay_steps is None:
self.decay_steps = self.max_steps
if self.rollout_tensor_parallel_degree == -1:
self.rollout_tensor_parallel_degree = self.tensor_parallel_degree
logger.info(
f"Set rollout_tensor_parallel_degree to tensor_parallel_degree: {self.tensor_parallel_degree}."
)
@property
def model_dtype(self):
# Load model
if self.fp16_opt_level == "O2":
if self.fp16:
dtype = "float16"
elif self.bf16:
dtype = "bfloat16"
else:
raise ValueError("Please specific dtype: --fp16 or --bf16")
else:
dtype = "float32"
return dtype
@property
def use_kl_in_reward(self):
if self.rl_algorithm in ["ppo", "reinforce_plus_plus"]:
return True
else:
return False
@dataclass
class ModelArgument:
actor_model_name_or_path: str = field(
default=None,
metadata={"help": "Built-in pretrained model name or the path to local model."},
)
reward_model_name_or_path: str = field(
default=None,
metadata={"help": "Built-in pretrained model name or the path to local model."},
)
reward_server: str = field(default=None, metadata={"help": "Reward server address."})
critic_model_name_or_path: str = field(
default=None,
metadata={"help": "Built-in pretrained model name or the path to local model."},
)
actor_tokenizer_alpha: float = field(default=None, metadata={"help": "Tokenizer will tokenize randomly"})
reward_tokenizer_alpha: float = field(default=None, metadata={"help": "Tokenizer will tokenize randomly"})
critic_tokenizer_alpha: float = field(default=None, metadata={"help": "Tokenizer will tokenize randomly"})
stage: str = field(default="PPO", metadata={"help": "The type of training."})
critic_recompute_granularity: str = field(
default="full",
metadata={
"help": "The granularity of recompute in critic model, "
"can be selected as `full` or `full_attn` or `core_attn`. "
},
)
chat_template: str = field(
default="none",
metadata={
"help": "the path of `chat_template.json` file to handle multi-rounds conversation. "
"If is None(do not set --chat_template argument), it will use the default `chat_template.json`;"
"If is equal with `model_name_or_path`, it will use the default loading; "
"If is directory, it will find the `chat_template.json` under the directory; If is file, it will load it."
"If is none string, it will not use chat_template.json."
},
)
@dataclass
class DataArgument:
train_datasets: str = field(default=None, metadata={"help": "Dataset name(s) registered in the raw dataset."})
eval_datasets: str = field(default=None, metadata={"help": "Dataset name(s) registered in the raw dataset."})
max_length: int = field(
default=2048,
metadata={
"help": "The maximum length that model input tokens can have. When intokens is set to True, it's also the maximum length for InTokens data stream"
},
)
max_prompt_len: int = field(default=4096, metadata={"help": "Maximum prompt length."})
prompt_key: str = field(default="src", metadata={"help": "The key of prompt(question) in the dataset."})
response_key: str = field(default="tgt", metadata={"help": "The key of response(answer) in the dataset."})