Hy3-preview / train /README_CN.md
yiqichen01's picture
Upload folder using huggingface_hub
cf1003d verified
<p align="left">
<a href="README.md">English</a> | 中文
</p>
# 模型训练
Hy3 preview 提供了模型训练相关流程,您可以在此章节对训练数据格式进行处理以供模型训练使用。
## 训练数据格式及处理
**Hy3 preview 同时支持慢思考与快思考两种模式,模型的默认输出是慢思考模式,若想让模型进行快思考,可通过 `reasoning_effort` 参数控制(可选值:`high`、`low`、`no_think`)。**
训练数据按照以下形式处理为 messages 格式,训练和推理的默认 system prompt 为空,可以根据自己的需求进行设定。
```python
# Fast thinking pattern (no_think)
{"reasoning_effort": "no_think", "messages": [{"content": "你是一个有用的人工智能助手。\n现在的时间是2026-01-01 13:26:12 周四", "role": "system"}, {"content": "1+1=?", "role": "user"}, {"role": "assistant", "content": "1+1=2"}]}
# Slow thinking pattern (high)
{"reasoning_effort": "high", "messages": [{"content": "你是一个有用的人工智能助手。\n现在的时间是2026-01-01 13:26:12 周四", "role": "system"}, {"content": "1+1=?", "role": "user"}, {"role": "assistant", "content": "1+1=2", "reasoning_content": "1+1=2"}]}
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("./models", use_fast=False, trust_remote_code=True)
ids = tokenizer.apply_chat_template(messages, is_training=True)
```
## 权重格式转换
Hy3 preview 的原始 checkpoint 采用每个 expert 独立存储的格式,在训练前需要转换为 expert 融合后的 HuggingFace 标准格式(将同一层的多个 expert 权重融合为 3D 张量,并统一 key 命名),用于提高加载和训练的速率。我们提供了转换脚本 `convert_ckpt_to_outer.py` 和校验脚本 `check_converted.py`,位于 `train/tools` 目录下。
### 转换
```sh
python convert_ckpt_to_outer.py \
--input_dir <原始checkpoint目录> \
--output_dir <输出目录> \
--workers 8
```
**参数说明:**
- `--input_dir`:原始 checkpoint 目录路径(必选)
- `--output_dir`:转换后的 checkpoint 输出目录路径(必选)
- `--workers`:并行转换的进程数,默认为 8(可选)
转换脚本会执行以下步骤:
1. 预扫描 `model.safetensors.index.json`,检测跨 shard 的 expert 分组
2. 逐 shard 并行转换权重(key 重命名 + expert 融合)
3. 后处理跨 shard 的 expert 分组(合并来自多个 shard 的数据)
4. 复制 `config.json`、tokenizer 等其他文件
5. 重建 `model.safetensors.index.json`
### 校验
转换完成后,建议使用校验脚本验证转换结果的完整性:
```sh
python check_converted.py <转换后的checkpoint目录> --spot-check 3
```
**参数说明:**
- 第一个参数:转换后的 checkpoint 目录路径(必选)
- `--spot-check`:随机抽检的 shard 文件数量,会加载 tensor 并检查 shape、dtype、NaN/Inf 等,默认为 3(可选)
校验脚本会检查以下内容:
1. `config.json` 的完整性
2. `model.safetensors.index.json` 中所有预期 key 是否齐全(包括常规层和 MTP 层)
3. 所有引用的 shard 文件是否存在且非空
4. 抽检 shard 文件中 tensor 的 shape、dtype 是否正确,是否存在 NaN/Inf
5. 检测孤立的空 shard 文件(跨 shard 合并残留,可安全删除)
## 快速开始
您可以参照快速开始文档中的内容进行快速上手。
## 模型训练
### 硬件需求
经过测试,不开 make_moe_param_leaf_module 以及 zero3+offload,max_seq_length 为 4096,使用LORA,全量微调最少需要单机 8 卡(显存至少80G)。
如果不使用LORA,最少需要4机32卡(显存至少80G)。
### 启动方式
参考:[HuggingFace Transformers Trainer](https://huggingface.co/docs/transformers/main/en/main_classes/trainer)
#### 单机启动训练
`train`目录下,执行:
```sh
pip install -r requirements.txt
bash train.sh
```
#### 多机启动训练
如果要用多台机器启动训练,请按照以下步骤执行,并保证多台机器在一个集群内。
##### 配置机器间免密 ssh 登录
以下操作以两个机器为例,两台机器的 ip 分别以`${ip1}``${ip2}`标识,以下操作均在 docker container 内执行。
首先,配置多机container免密,在每台机器上执行。
```sh
ssh-keygen # 生成id_rsa和id_rsa.pub,用于免密登录
ssh-keygen -t rsa -A # 生成/etc/ssh/ssh_host_rsa_key和ssh_host_ecdsa_key, 用于后面启动ssh listen
/usr/sbin/sshd -p 36005 -o ListenAddress=0.0.0.0 # 启动 SSH 监听
echo "Port 36005" > ~/.ssh/config # ssh 连接端口修改为 36005
passwd root # 需要配置root密码,否则监测平台会报警
```
注意:这里的`36005`是一个示例端口,可以选用任意端口,但需要保证使用的端口**开放****不被其他的进程占用**
接下来,在每台机器的 container 内,执行:
```sh
cat ~/.ssh/id_rsa.pub
```
**将输出的 ssh 公钥复制并粘贴到`~/.ssh/authorized_keys`文件中,每行一个公钥,每台机器上都要做这个操作**。最终每台机器上的`~/.ssh/authorized_keys`文件内容应当是一致的,并且包含了所有机器的公钥。
需要注意,多节点训练时,每个节点上执行的代码都得一致,建议挂载一个共享的网络盘,如果无法挂载共享网盘,则需要手动将数据集、脚本、代码复制在多台机器的相同目录下。
##### 启动多机训练
在以上准备步骤准备好了之后,以及确认依赖已经安装完成(如未安装,请执行`pip install -r requirements.txt`安装),就可以在`train.sh`中的开头增加以下配置:
```shell
export HOST_GPU_NUM=8
# IP list, comma separated. e.g. "192.168.1.1,192.168.1.2" or single node "192.168.1.1"
IP_LIST=${IP_LIST:-"127.0.0.1"}
```
注意:如果`IP_LIST`环境变量未设置,则将`IP_LIST`替换为IP列表!格式为:
```
如果只有一个IP:
IP_LIST=${ip_1}
如果有多个IP:
IP_LIST=${ip_1},${ip_2}
```
请将`${ip_1}`和`${ip_2}`替换为真实的IP地址。
然后,在`${ip1}`的机器上,在`train/`目录下,执行`bash train.sh`即可,注意第一次启动时可能会看见以下的输出:
```ssh
The authenticity of host '[ip]:36005 ([ip]:36005)' can't be established.
ECDSA key fingerprint is xxxxxx.
ECDSA key fingerprint is MD5:xxxxxx.
Are you sure you want to continue connecting (yes/no)?
```
此时输入`yes`即可继续。
##### 关键参数
脚本中的关键参数如下:
- `--deepspeed`: 此参数应当指向一个 deepspeed 的配置文件,`train`文件夹下提供了三种 DeepSpeed 的默认配置文件:`ds_zero2_no_offload.json`, `ds_zero3_no_offload.json`, `ds_zero3_offload.json`,这三个配置文件所需显存依次减少
- `--model_name_or_path`: 要加载的 Hy3 preview 的 HF 预训练模型权重,否则无法加载
- `--tokenizer_name_or_path`: tokenizer 文件夹路径, 否则无法加载
- `--train_data_file`: 训练文件路径,应该为一个 jsonl 文件
- `--output_dir`: 输出文件夹,log、tensorboard 和权重都会存储在这个路径下
- `--per_device_train_batch_size`: 每张卡上的 batch size
- `--gradient_accumulation_steps`: 梯度累计次数,`per_device_train_batch_size * gradient_accumulation_steps * dp_size`为 global_batch_size
- `--max_steps`: 训练的总步数
- `--save_steps`: 每多少个 step 存储一个 checkpoint
- `--use_lora`: 是否用 lora 训练,同时接收`--lora_rank`,`--lora_alpha`和`--lora_dropout`参数。lora 默认应用于 "q_proj", "k_proj", "v_proj", "o_proj" 四个参数,如果需要改变的话在代码中修改即可。注意:**使用 lora 训练时,只会保存 lora 的权重,而不会保存 base 模型的权重**,如果需要合并 lora 权重,看下面的“Lora 权重合并”一节
- `--make_moe_param_leaf_module`:当用 zero3 以及 MoE 训练时,将 MoE 模块视作一个 leaf module,即它的参数不进行 zero3 切分,这个选项预计会显著增加显存占用
- `--gradient_checkpointing`:开启梯度重计算
- `--train_attention_params_only`: 是否只训练 attention 参数
- `--learning_rate`: 训练时的最大学习率
- `--min_lr`: 训练时的最小学习率
- `--use_flash_attn`: 开启 flash-attention 进行训练加速
**注意:**
- 如果想从一个中途保存的 ckpt 继续训练,而不是加载一个预训练的权重,直接指定`--resume_from_checkpoint`为之前训练保存的 ckpt 路径,不要指定`--model_name_or_path`,这样只会加载权重,而不会加载训练状态
- 从 ckpt 继续训练时,loss 可能会有微小的偏差,这是由一些非确定性算法带来的随机性,是正常现象。参考:[HuggingFace Transformers Trainer Randomness](https://huggingface.co/docs/transformers/main/en/main_classes/trainer#randomness)
- 当 `--model_name_or_path` 有效时,所有模型相关的参数都会被忽略
- 一个 batch 内的样本会通过 padding 对齐 batch 内最长的样本,而每条样本的长度最长为 max_seq_length,超出的部分会被裁剪
- 如果报出 bias 权重没有 load 的 warning,忽略即可,Hunyuan-Large 中不会用到 bias
#### 显存不足怎么办?
参考:[DeepSpeed Configuration](https://www.deepspeed.ai/docs/config-json/)
可以尝试修改 ds config,去掉这几个参数的 auto 属性,改小试试看:
- `stage3_param_persistence_threshold`
- `stage3_prefetch_bucket_size`
- `stage3_max_reuse_distance`
#### Lora 模型合并
保存下来的 lora 权重没法在训练运行时合并到 zero3 模型中,因为 zero3 开启时模型权重会切分到各 dp rank 上。因此如果想把 lora 权重合并到 base 模型上,可以通过离线的方式合并后得到权重文件。执行`merge_lora_weight.sh`即可完成 lora 权重和 base 模型权重的合并,其中的参数有:
- `--base_model_path`:base 模型的权重目录
- `--adapter_model_path`:lora 权重目录
- `--output_path`:合并后的权重保存目录
- `--save_dtype`: 以什么数据格式存储合并后的权重,可选值:fp16,bf16,fp32
#### LLaMA-Factory 支持
如果对 LLaMA-Factory 较为熟悉,可使用LLaMA-Factory进行微调。脚本、代码以及配置文件都归档在`./train/llama_factory_support`目录下。如果没有特别说明,接下来我们提到的文件都是该目录下的文件。
##### 安装
可以通过下载源码 https://github.com/hiyouga/LLaMA-Factory/tree/main ,根据网站的指引进行安装。
##### 配置文件
我们提供了 llama-factory 的训练示例配置文件 `hy_v3_lora_sft.yaml`和`hy_v3_full_sft.yaml`文件,分别对应LORA训练和非LORA训练。
脚本中的关键参数如下:
**模型相关:**
- `model_name_or_path`: Hy3 preview HF 格式预训练模型权重路径
- `trust_remote_code`: 是否信任远程代码, Hy3 preview 需要设置为 `true`
**训练方法:**
- `stage`: 训练阶段, 当前为 `sft`(监督微调)
- `finetuning_type`: 微调类型, 可选 `full`(全量微调) 或 `lora`(LoRA 微调)
- `deepspeed`: DeepSpeed 配置文件路径, 全量微调推荐 `ds_zero3_offload_hy.json`, LoRA 微调推荐 `ds_zero2_offload_lora.json`
**LoRA 参数(仅 LoRA 微调时生效):**
- `lora_rank`: LoRA 秩, 默认 `64`
- `lora_alpha`: LoRA alpha 系数, 默认 `128`
- `lora_dropout`: LoRA dropout 比率, 默认 `0.05`
- `lora_target`: LoRA 应用的目标模块, 默认为 `q_proj,k_proj,v_proj,o_proj`
**数据集:**
- `dataset_dir`: 数据集目录路径
- `dataset`: 数据集名称, 需要在 `dataset_dir` 下的 `dataset_info.json` 中注册
- `template`: 对话模板, Hy3 preview 使用 `hy_v3`
- `cutoff_len`: 最大序列长度, 超出部分会被截断; 全量微调可设为 `262144`(262K), LoRA 微调建议设为 `8192` 以节省显存
- `max_samples`: 每个数据集最多使用的样本数
- `overwrite_cache`: 是否覆盖已缓存的预处理数据集
**输出:**
- `output_dir`: 输出目录, 日志、TensorBoard 和权重都会存储在此路径下
- `logging_steps`: 每多少步记录一次日志
- `save_steps`: 每多少步保存一次 checkpoint
- `plot_loss`: 是否绘制训练 loss 曲线
- `overwrite_output_dir`: 是否覆盖已有的输出目录
- `save_only_model`: 是否只保存模型权重(不保存优化器状态等)
- `report_to`: 日志上报工具, 可选 `none`, `wandb`, `tensorboard`, `swanlab`, `mlflow`
**训练超参数:**
- `per_device_train_batch_size`: 每张卡上的 batch size
- `gradient_accumulation_steps`: 梯度累积步数, `per_device_train_batch_size * gradient_accumulation_steps * dp_size` 为 global batch size
- `learning_rate`: 最大学习率, 全量微调推荐 `1.0e-5`, LoRA 微调推荐 `2.0e-4`
- `num_train_epochs`: 训练轮数
- `lr_scheduler_type`: 学习率调度器类型, 推荐使用 `cosine_with_min_lr`
- `lr_scheduler_kwargs.min_lr_rate`: 最小学习率与最大学习率的比值, 例如 `0.1` 表示最小学习率为最大学习率的 10%
- `warmup_ratio`: 预热阶段占总训练步数的比例
- `bf16`: 是否使用 BFloat16 混合精度训练
- `gradient_checkpointing`: 是否开启梯度重计算以节省显存
- `ddp_timeout`: 分布式训练超时时间(毫秒)
- `flash_attn`: 注意力实现方式, 推荐 `fa2`(FlashAttention-2), 也可选 `sdpa`; 使用 `fa2` 需要安装 flash-attn 包
- `resume_from_checkpoint`: 从指定 checkpoint 路径恢复训练, 设为 `null` 表示从头开始训练
##### 启动训练
请先按照前面章节 [配置机器间免密 ssh 登录](#配置机器间免密-ssh-登录) 配置多机免密登录。
修改`train_lf.sh`中开头的以下配置:
```shell
export HOST_GPU_NUM=8
# IP list, comma separated. e.g. "192.168.1.1,192.168.1.2" or single node "192.168.1.1"
export IP_LIST=${IP_LIST:-"127.0.0.1"}
```
注意:如果`IP_LIST`环境变量未设置,则将`IP_LIST`替换为IP列表!格式为:
```
如果只有一个IP:
IP_LIST=${ip_1}
如果有多个IP:
IP_LIST=${ip_1},${ip_2}
```
请将`${ip_1}``${ip_2}`替换为真实的IP地址。
然后,在每一台机器上,在`train/llama_factory_support/`目录下执行`bash train_lf.sh`