# Tutorial (中文) — AReaL Documentation

Skip to main content
`Ctrl`+`K`

# Tutorial (中文)

## 前置要求

### 硬件要求

为了能正常完成训练流程，请参照下表确认你的硬件是否满足要求：

模型大小 | 1.5B | 1.5B | 1.5B | 7B | 7B | 32B
--- | --- | --- | --- | --- | --- | ---
节点 | 1 | 4 | 16 | 4 | 16 | 16
GPU | 8 张 H800 | 每节点 8 张 H800 | 每节点 8 张 H800 | 每节点 8 张 H800 | 每节点 8 张 H800 | 每节点 8 张 H800
CPU | 48 核 | 每节点 48 核 | 每节点 48 核 | 每节点 48 核 | 每节点 48 核 | 每节点 48 核
内存 | 1 TB | 每节点 1 TB | 每节点 1 TB | 每节点 1 TB | 每节点 1 TB | 每节点 1 TB
通信 | NVSwitch | NVSwitch+RoCE 带宽 3.2 Tbps | NVSwitch+RoCE 带宽 3.2 Tbps | NVSwitch+RoCE 带宽 3.2 Tbps | NVSwitch+RoCE 带宽 3.2 Tbps | NVSwitch+RoCE 带宽 3.2 Tbps
存储 | 1TB | 共享存储（NAS）10TB | 共享存储（NAS）10TB | 共享存储（NAS）10TB | 共享存储（NAS）10TB | 共享存储（NAS）10TB
BatchSize x GroupSize | 512x16 | 512x16 | 512x16 | 512x16 | 512x16 | 512x16
单步训练时间（秒） | 3461 | 997 | 391 | 2275 | 815 | 6707
训练至收敛需要步数 | ~250 | ~250 | ~250 | ~400 | ~400 | -
总训练时间（小时） | ~240 | ~69 | ~27 | ~252 | ~90 | -

关于硬件要求的说明：

- GPU 需要 80GB 显存，可以选择同级别其他 GPU 型号。

- 单节点训练时可以使用本地存储，但多节点训练必须要提供共享存储，否则无法进行训练。

- 目前32B模型没有训练出有意义的结果，所以无法估计训练到收敛需要的步数和时间。

### 软件要求

本教程提供 Docker镜像。以下是经过测试的软件版本，可以参考如下软件版本进行配置。

| 版本说明
--- | ---
OS | CentOS 7 / Ubuntu 22.04 或其他满足下方软件运行的系统
NVIDIA Driver | 版本：550.127.08
CUDA | 版本：12.8
Git LFS | 参考：Git LFS 安装指南 主要用于下载模型，数据集，AReaL 工程代码
Docker | 版本：27.5.1
NVIDIA Container Toolkit | NVIDIA Container Toolkit 安装指南
镜像 | ghcr.io/inclusionai/areal-runtime:v0.3.0 这个镜像中包含运行依赖和 Ray 的相关组件

由于 NVIDIA Driver 和 CUDA 的安装以及共享存储的挂载与节点和系统版本有关，请自行完成安装，本教程不进行介绍。

如果是多节点训练，请先将共享存储挂载到每个节点的 `/storage` 目录上，后续下载的内容都将放在这个目录下，并且 AReaL 容器也会将该目录挂载到容器的 `/storage`，以便训练时访问。

## 一键搭建环境并启动训练

本节提供一个一键安装脚本，自动完成节点的环境配置工作：

1. 安装 Docker，Git LFS，NVIDIA Container Toolkit

2. 在每个节点上拉取 AReaL 镜像

3. 下载 AReaL 代码，模型，数据集

4. 搭建 Ray 集群

5. 【可选】在 Ray 集群中启动一个训练任务

请选择任意一个节点执行如下操作：

    mkdir -p /storage/codes
    cd /storage/codes/
    git clone https://github.com/inclusionAI/AReaL.git
    cd /storage/codes/AReaL

    python ./examples/env/setup_env_and_start_train.py setup --private_key_file /path/to/ssh_key --ssh_port 22 --username root --hostnames NODE_IP_1 NODE_IP_2 NODE_IP_3 NODE_IP_4 --train_param 1.5B_n1

`setup_env_and_start_train.py setup` 参数说明：

- `private_key_file`：SSH 私钥文件，用于连接节点

- `ssh_port`：SSH 端口

- `username`：SSH 用户名

- `hostnames`：IP 列表，用空格分割。可以是 1/4/16 个节点 IP

- `train_param`：【可选】训练参数，用于在完成环境搭建后直接启动一个训练任务。可选值为 `1.5B_n1`，`1.5B_n4`，`1.5B_n16`，`7B_n4`，`7B_n16`

如果因为环境差异，无法运行本节中的脚本或运行出现错误，也可以按照本教程后续章节的内容手动完成环境配置和启动训练。

## 环境配置

由于使用了共享存储，下载操作只需要在一个节点上完成。

### 代码

将 AReaL 项目代码克隆到 `/storage/codes` 中：

    mkdir -p /storage/codes
    cd /storage/codes/
    git clone https://github.com/inclusionAI/AReaL.git

### 数据集

我们提供了用于训练的数据集，请下载数据集并放置在 /storage/datasets/

    mkdir -p /storage/datasets/
    cd /storage/datasets/
    wget https://huggingface.co/datasets/inclusionAI/AReaL-RL-Data/resolve/main/data/boba_106k_0319.jsonl?download=true
    wget https://huggingface.co/datasets/inclusionAI/AReaL-RL-Data/resolve/main/data/orz-zero_56k_0319.jsonl?download=true

### 模型

我们基于开源模型进行训练，该模型可以从 HuggingFace Hub 直接下载（请确保已经安装了 Git LFS）：

    mkdir -p /storage/models
    cd /storage/models
    GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
    cd DeepSeek-R1-Distill-Qwen-7B
    git lfs pull

你也可以在安装 PyPI 和 huggingface_hub 后利用 huggingface CLI 进行下载，具体请参考官方文档

### 启动 Ray 集群

在执行这一步之前，请先拉取 AReaL 环境镜像，这个镜像中已经包含了 Ray 相关的组件。

在第一个节点上执行如下命令启动 Ray Head：

    docker run -d --name r1-ray-head --privileged --gpus all --network host --shm-size 700g -v /storage:/storage ghcr.io/inclusionai/areal-runtime:v0.3.0 /bin/bash -c "ray start --head --port=6379 && tail -f /dev/null"

在除了第一个节点以外的每个节点上执行如下命令启动 Ray Worker（如果只有一个节点，这一步就不用执行了）：

    # RAY_HEAD_IP 是第一个节点的 IP
    RAY_HEAD_IP=xxx.xxx.xxx.xxx
    docker run -d --name r1-ray-worker --privileged --gpus all --network host --shm-size 700g -v /storage:/storage ghcr.io/inclusionai/areal-runtime:v0.3.0 /bin/bash -c "ray start --address=$RAY_HEAD_IP:6379 && tail -f /dev/null"

全部启动完成后，在第一个节点上通过 docker exec 进入容器，查看 Ray 集群的状态：

    docker exec -it r1-ray-head bash
    ray status

可以看到 Ray 的资源情况，输出如下（这是一个 16 节点 128 卡的集群，根据你的节点数量，这里的输出会有所不同）：

    ======== Autoscaler status: 2025-02-22 14:08:51.061250 ========
    Node status
    ---------------------------------------------------------------
    Active:
     1 node_d5634ae61bfe6732d957811bed65c8a39f13ece07e0326f941acbc4e
     1 node_23b0c08045c9a39bc4c454cae298ee531d9a474215ac5e77a5b01e74
     1 node_bc1016320658e92645f29cecb8aaf51c0b7e01a44e8ac9c814dfee59
     1 node_4e7d15e9cee9ee0da5d65e45f1e346228c52bc0c557511c6eeab40dc
     1 node_c5bcf15e28a00515be5d2a7e8e33d71f0f57cdfaf1003db9e0c74788
     1 node_ec3f6ee8f6fdf3a5392bb4dac244668da75d094e084dcbb520ce2525
     1 node_dc2f1eef88126ae4ac7902574714af9ab74b78ba037217e73e063639
     1 node_a4728608c1fda187dc33bb24e831c42fe5c8a582ad428b6e595933bc
     1 node_970379a3ba750ee3b13e31612b6a6b758d50bd4943555b2a13d1bd61
     1 node_bf6b658bea9e437fcb642a2d881425662a689d668c92fe1545899b36
     1 node_2c69511f410d9360f1d05893fde2c97dd32240e0315afea9b2d286a3
     1 node_e4c90c17cc48ad469d123041d3302dcff1f7a82a4805279300812b19
     1 node_3f772cbffb206c30b6ccedade83789d78397804bab874ee59563cb96
     1 node_429bd5115b5590b612590bb455f2d3ed4f77055d746a184baf807655
     1 node_75071820f2c16dc51fa271316b72cd45335ec877c06450d292ab7d54
     1 node_6f4323f9038248d82b91321e2c4ca5fa99e65efa2d976c0b896a8964
    Pending:
     (no pending nodes)
    Recent failures:
     (no failures)

    Resources
    ---------------------------------------------------------------
    Usage:
     0.0/2128.0 CPU
     0.0/128.0 GPU
     0B/21.08TiB memory
     0B/2.91TiB object_store_memory

    Demands:
     (no resource demands)

## RL训练

在进行分布式训练之前，请确保已经启动了 Ray 集群，并且集群状态正常。 然后在第一个节点（Ray Head 所在节点），进入容器：

    docker exec -it r1-ray-head bash
    cd /storage/codes/AReaL

选择匹配硬件环境的一个配置运行即可：

    python3 -m realhf.apps.quickstart ppo-math --config ./examples/configs/7B-distill/ppo-7B-distill-gpus-128.yaml

启动后，在终端可以看到启动日志：

                  ╭─────────────────────────────────────────────────╮
                  │ Setting PPOMATHConfig with the Following Values │
                  ╰─────────────────────────────────────────────────╯

    ───────────────────────── Current Configuration Begin ──────────────────────────
    actor (ModelTrainEvalConfig)
        actor.type (ModelFamily)
            actor.type._class (str) - qwen2
            actor.type.size (int) - 7
            actor.type.is_critic (bool) - False
    ...
    ────────────────────────── Current Configuration End ───────────────────────────

    20250222-10:26:34.877 quickstart INFO: Running ppo-math experiment.
    20250222-10:44:15.581 quickstart INFO: Logs will be dumped to /storage/ray/experiments/logs/root/ppo-7B-distill-gpus-128/512x16
    20250222-10:44:15.581 quickstart INFO: Model checkpoints will be saved to /storage/ray/experiments/checkpoints/root/ppo-7B-distill-gpus-128/512x16
    20250222-10:26:36.408 quickstart INFO: Launching experiments with RAY...

如果运行过程中出现错误（比如出现 Error 关键字），请参考Troubleshooting解决。

### Commandline Options

    python3 -m realhf.apps.quickstart ppo-math --help

其中重要的参数的说明如下：

- mode：总是为 ray，参考本教程进行训练时不要改成其他值。

- {actor|critic|ref}.path：模型的路径

- dataset.path：数据集 jsonl 文件的路径

- external_configs.cluster_config：设置 cluster_config 的配置，比如 fileroot 是存放训练输出的根目录。

- n_nodes：节点数量

- n_gpus_per_node：每个节点的 GPU 数量

- allocation_mode：实验中模型的 GPU 分配和 3D 并行策略，推荐的策略有以下形式:

  - `sglang.d${DP1}m${TP1}p${PP1}+d${DP2}m${TP2}p${PP2}`: 分别配置 SGLang 生成和训练的并行策略，生成和训练分离，使用两部分不同的 GPU。二者所用的GPU数量相加要等于总的 GPU 数量，即 DP1xTP1xPP1+DP2xTP2xPP2=#GPUs。

- exp_ctrl.total_train_epochs：训练的 epoch 数量（即迭代整个数据集的次数）

- exp_ctrl.save_freq_{epochs|steps|secs}：保存持久化存储模型参数的频率，如果设成 null 会不保存模型

- exp_ctrl.ckpt_freq_{epochs|steps|secs}：保存临时参数用于重启的频率

- dataset.train_bs_n_seqs：训练的批量大小，即每次训练需要采样的 prompt 数量

- group_size：每个 prompt 需要采样的答案数量

- {actor_train|ref_inf}.mb_spec.max_tokens_per_mb：reference模型推理和actor模型训练每次forward/backward数据中最大的token数量，可以减小以避免OOM错误。这些数据会累积梯度进行一次参数更新。

- ppo.ppo_n_minibatches：每次PPO更新中会把所有数据划分成多少份以此进行loss计算和参数更新。

- ppo.gen.max_new_tokens：每条prompt生成的最大token数，默认训练脚本中为16k。

- ppo.gen.min_new_tokens：每条prompt生成的最小token数，默认为0。

### 过程观测

这里以 16 节点的运行日志为例（1 节点和 4 节点也一样），说明几个观察训练进度和效果的方法。

#### 查看训练进度

搜索日志中的 Epoch 关键字，查看总的 Epoch 数量和 Step 数量：

    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-11:11:56.997 master worker INFO: Epoch 1/1 step 1/19 (global step 1) finishes. Average #tokens per batch is 111847. #End to end# execution time: *2124.429*s. Total time consumption: 2283.862s.
    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-11:52:02.719 master worker INFO: Epoch 1/1 step 2/19 (global step 2) finishes. Average #tokens per batch is 111847. #End to end# execution time: *2405.716*s. Total time consumption: 4689.584s.
    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-12:27:25.084 master worker INFO: Epoch 1/1 step 3/19 (global step 3) finishes. Average #tokens per batch is 111847. #End to end# execution time: *2122.318*s. Total time consumption: 6811.949s. Estimated remaining time: 33957.093s.
    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-13:05:58.246 master worker INFO: Epoch 1/1 step 4/19 (global step 4) finishes. Average #tokens per batch is 111847. #End to end# execution time: *2313.134*s. Total time consumption: 9125.111s. Estimated remaining time: 33265.891s.
    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-13:44:14.349 master worker INFO: Epoch 1/1 step 5/19 (global step 5) finishes. Average #tokens per batch is 111847. #End to end# execution time: *2296.076*s. Total time consumption: 11421.214s. Estimated remaining time: 31413.800s.
    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-14:22:33.864 master worker INFO: Epoch 1/1 step 6/19 (global step 6) finishes. Average #tokens per batch is 111847. #End to end# execution time: *2299.448*s. Total time consumption: 13720.729s. Estimated remaining time: 29350.673s.

出现了 6 条日志信息，以最后一条信息的内容说明各个字段的含义：

- `Epoch 1/1`：表示总共需要训练 1 个 Epochs，当前在训练第 1 个。这里作为例子总共只训练 1 个 Epoch，正常训练应该是 10 个 Epochs 或者更多。

- `step 6/19`：表示当前 Epoch 有 19 个 Steps，当前在训练第 6 个

- `global step 6`： 表示当前 Step 在所有 Epochs 的 Steps 里的序号

- `#End to end# execution time: *2299.448*s`：表示当前 Step 训练耗费了 2299.448 秒

- `Total time consumption: 13720.729s`：从训练启动开始一共耗费了 13720.729 秒

- `Estimated remaining time: 29350.673s`：预计完成训练还需要 29350.673 秒

#### 查看训练的效果

搜索日志中的 `task_reward` 关键字

    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-11:11:56.991 master worker INFO: RPC name actor_train returns {'ppo_approx_kl': -2.2640759198111482e-05, 'actor_loss': 1.1128166761409375e-06, 'actor_clip_ratio': 2.1122002635820536e-07, 'importance_weight': 1.0000014305114746, 'task_reward': -0.2996826171875, 'kl_reward': -2.27004832709099e-07, 'final_reward': -0.30145370960235596, 'advantage': 0.003593671601265669, 'avg_seq_len': 7907.8955078125, 'avg_prompt_len': 105.845703125, 'n_tokens': 127828786.0, 'n_valid_tokens': 127828786.0, 'n_seqs': 16384.0, 'no_eos_ratio': 0.122802734375, 'disable_value': 1.0, 'mask_no_eos_with_zero': 0.0}
    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-11:52:02.712 master worker INFO: RPC name actor_train returns {'ppo_approx_kl': -2.493159263394773e-05, 'actor_loss': -3.846728588996484e-07, 'actor_clip_ratio': 3.16789424914532e-07, 'importance_weight': 0.9999996423721313, 'task_reward': -0.6793212890625, 'kl_reward': -2.536311853873485e-07, 'final_reward': -0.6813737154006958, 'advantage': 0.004844569601118565, 'avg_seq_len': 8203.9453125, 'avg_prompt_len': 111.892578125, 'n_tokens': 132580185.0, 'n_valid_tokens': 132580185.0, 'n_seqs': 16384.0, 'no_eos_ratio': 0.13812255859375, 'disable_value': 1.0, 'mask_no_eos_with_zero': 0.0}
    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-12:27:25.077 master worker INFO: RPC name actor_train returns {'ppo_approx_kl': -2.572356243035756e-05, 'actor_loss': -5.036404786551429e-07, 'actor_clip_ratio': 1.8960582792715286e-07, 'importance_weight': 0.9999992251396179, 'task_reward': -0.6280517578125, 'kl_reward': -2.988609537624143e-07, 'final_reward': -0.6303607225418091, 'advantage': 0.004505862481892109, 'avg_seq_len': 7834.6328125, 'avg_prompt_len': 108.900390625, 'n_tokens': 126578395.0, 'n_valid_tokens': 126578395.0, 'n_seqs': 16384.0, 'no_eos_ratio': 0.11761474609375, 'disable_value': 1.0, 'mask_no_eos_with_zero': 0.0}
    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-13:05:58.239 master worker INFO: RPC name actor_train returns {'ppo_approx_kl': -2.4861981728463434e-05, 'actor_loss': 1.3935685672095133e-07, 'actor_clip_ratio': 3.02603467616791e-07, 'importance_weight': 0.9999998807907104, 'task_reward': -0.78857421875, 'kl_reward': -3.672174671009998e-07, 'final_reward': -0.791388750076294, 'advantage': 0.005053278990089893, 'avg_seq_len': 7773.39404296875, 'avg_prompt_len': 108.7890625, 'n_tokens': 125576883.0, 'n_valid_tokens': 125576883.0, 'n_seqs': 16384.0, 'no_eos_ratio': 0.117919921875, 'disable_value': 1.0, 'mask_no_eos_with_zero': 0.0}
    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-13:44:14.342 master worker INFO: RPC name actor_train returns {'ppo_approx_kl': -2.516058702894952e-05, 'actor_loss': -7.665488510610885e-07, 'actor_clip_ratio': 1.9505058901359007e-07, 'importance_weight': 0.9999997615814209, 'task_reward': -0.6158447265625, 'kl_reward': -4.6867208425283025e-07, 'final_reward': -0.6195111274719238, 'advantage': 0.004475570283830166, 'avg_seq_len': 7928.50830078125, 'avg_prompt_len': 105.517578125, 'n_tokens': 128171874.0, 'n_valid_tokens': 128171874.0, 'n_seqs': 16384.0, 'no_eos_ratio': 0.12353515625, 'disable_value': 1.0, 'mask_no_eos_with_zero': 0.0}
    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-14:22:33.857 master worker INFO: RPC name actor_train returns {'ppo_approx_kl': -2.4821250917739235e-05, 'actor_loss': -3.922649227661168e-07, 'actor_clip_ratio': 3.323623900541861e-07, 'importance_weight': 1.0000001192092896, 'task_reward': -0.7025146484375, 'kl_reward': -5.863367960046162e-07, 'final_reward': -0.7071446776390076, 'advantage': 0.004277692176401615, 'avg_seq_len': 8002.4873046875, 'avg_prompt_len': 105.951171875, 'n_tokens': 129376851.0, 'n_valid_tokens': 129376851.0, 'n_seqs': 16384.0, 'no_eos_ratio': 0.12286376953125, 'disable_value': 1.0, 'mask_no_eos_with_zero': 0.0}

以最后一条说明其中几个重点字段的含义：

- `task_reward`：这个step中采样的所有答案的平均奖励值，训练稳步进行的话这个值会持续上升，最终维持不变

- `importance_weight`: PPO loss中重要性采样比率在所有token上的平均值，通常接近1。

- `actor_clip_ratio`: PPO loss中被clip掉的token占所有token的比率，通常小于0.1。

- `actor_loss`: PPO loss，不会随着训练过程有明显的上升或下降趋势，不应作为模型表现的参考。

- `avg_seq_len`: 这一步中采样的所有序列（即提示词和答案相加）的平均长度。在完整的多阶段训练中，这个值会先下降再上升。

- `no_eos_ratio`: 这一步中采样的所有答案因为超出最大生成长度被截断的比率。这个值上升也代表了答案的平均长度在上升。

## 评估

### 评估流程

评估代码包含在仓库的`evaluation`文件夹中。按照以上的教程，训练得到的checkpoint会保存在`/storage/ray/experiments/checkpoints/root/`路径下，例如`/storage/ray/experiments/checkpoints/root/ppo-zero-distill-7B-n16/1024x16-n16/actor/epoch1epochstep20globalstep20/`。

启动一个新的容器用于运行评估脚本（评估需要更新部分 python 库，请不要在训练容器中进行）：

    docker run -d --name r1-eval --privileged --gpus all --network host --shm-size 700g -v /storage:/storage ghcr.io/inclusionai/areal-runtime:v0.2.0 /bin/bash -c "tail -f /dev/null"
    docker exec -it r1-eval bash

在docker容器内部运行以下脚本进行评估：

    cd /storage/codes/AReaL/evaluation
    cd latex2sympy
    pip install -e .
    cd ..
    pip install -r requirements.txt
    pip install vllm --no-build-isolation
    pip install transformers==4.47.0
    pip install prettytable timeout_decorator
    mkdir /storage/ray/eval_output/
    nohup python eval_and_aggregate.py \
    	--model_path /storage/ray/experiments/checkpoints/root/ppo-zero-distill-7B-n16/1024x16-n16/actor/epoch1epochstep20globalstep20/ \
    	--output_path /storage/ray/eval_output/ \
    	--data_names "math_500,aime24,amc23" \
    	--max_gen_tokens 32768 &> /storage/ray/eval_output/eval_and_aggregate_parallel.log &

- `--model_path`：模型参数的保存路径

- `--output_path`：评估过程中生成的答案和日志文件路径

- `--data_names`: 可以指定评测某个数据，多个数据集用逗号隔开，默认为 math_500, aime24, amc23

- `--max_gen_tokens`：最长的答案生成长度，默认值 32768

### 评估结果

评估脚本运行完后会在 /storage/ray/eval_output/eval_and_aggregate_parallel.log 日志文件输出一个表格，例如：

    +----------+---------------+---------------+---------------+------------+---------------+--------+---------+
    | dataset  | num_questions | greedy_length | sample_length | greedy_acc | sample_pass@1 | pass@8 | pass@16 |
    +----------+---------------+---------------+---------------+------------+---------------+--------+---------+
    | math_500 |      500      |     6757.4    |     4139.5    |    84.4    |      92.7     |  97.3  |   97.7  |
    |  aime24  |       30      |    19328.0    |    13663.5    |    50.0    |      50.4     |  77.3  |   80.0  |
    |  amc23   |       40      |     8850.0    |     6526.2    |    80.0    |      90.5     |  96.8  |   98.8  |
    +----------+---------------+---------------+---------------+------------+---------------+--------+---------+

- `{greedy|sample}_length`: 在greedy或随机采样策略下生成的平均答案长度

- `greedy_acc`：在greedy采样下的平均准确率

- `sample_pass@{k}`：在随机采样下平均每k个答案产生正确答案的概率

### 额外说明

#### 关键参数

- 我们提供的评估脚本默认采样32次取平均值，采样温度值为0.6

- 我们发现vLLM的`enforce_eager`参数很大程度影响评估性能，当`enforce_eager=True`时我们才能够复现先前工作汇报的模型表现，否则评估结果会低于先前工作汇报的结果，因此我们会在执行 `eval_and_aggregate_parallel.py` 时将`enforce_eager`强制开启。

由于以上原因，评估过程通常会消耗较长时间。

#### 运行时间

评估的运行时间取决于最长生成长度、数据集的题目数量和模型大小等等。在1台8*H100机器上，7B模型，数据集为`math_500,aime24,amc23`，生成长度为32768，评估脚本运行时间为 5 个小时。

## Troubleshooting

如果以下内容没有解答你的问题，欢迎在 GitHub Issue 中进行提问。

### 自动恢复

当设置了 `recover_mode=auto` 并且训练配置和之前相同，AReaL 会尝试找到之前生成的 checkpoints 并且从这个 checkpoints 恢复训练。

如果自动恢复失败，有这些可能性：

- 训练配置里的 `experiment_name` 和 `trial_name` 与之前的不一样

- Batch Size（参数里的 `dataset.train_bs_n_seqs`），Group Size（参数里的 `group_size`），节点数（参数里的 `n_nodes`）三个值发生了变化

- 之前的训练没有创建过 recover checkpoint 。默认的 recover checkpoint 规则有 2 个：

  - 从第 2 个 step 完成后才生成 recover checkpoint

  - 一个 step 训练完成，且距离上次 recover checkpoint 时间超过 600s，则生成一个新的 recover checkpoint。这个参数在 `./examples/configs/*/*.yaml` 文件里，参数名为 ：`exp_ctrl.ckpt_freq_secs=600`。

可以通过搜索 `Dumped recover` 确认是否生成过 recover checkpoint

    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-11:52:02.760 master worker INFO: Dumped recover info to file.
    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-12:27:25.105 master worker INFO: Dumped recover info to file.
    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-13:05:58.264 master worker INFO: Dumped recover info to file.
    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-13:44:14.411 master worker INFO: Dumped recover info to file.
    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-14:22:33.883 master worker INFO: Dumped recover info to file.
    (master_worker/0 pid=96390, ip=xxx.xxx.xxx.xxx) 20250222-14:59:44.925 master worker INFO: Dumped recover info to file.

### 一系列OutOfMemory错误

我们提供的脚本已经尽最大努力避免了OOM错误的发生，但是OOM问题仍然会随着训练进行，在内存碎片增加和生成序列长度越来越长时偶尔发生。虽然这些问题通常可以通过自动重启解决，当重启频繁时，用户还可以尝试以下针对性的解决方式。

#### torch.cuda.CudaOutOfMemoryError

解决这个问题的关键是定位错误发生的阶段。

- 如果发生在初始化阶段（在进入到actor_gen之前）:

  - 检查当前GPU上是否存在残留进程。在分布式场景下，可以通过重启ray cluster解决；在单机场景下，可以通过pkill解决。

- 该错误通常不会发生在actor_gen阶段。

- 如果发生在ref_inf或actor_train阶段

  - 改变相应计算任务的microbatch大小，例如`actor_train.mb_spec.max_tokens_per_mb=20480`，这个参数代表每次模型forward/backward的数据最多只会包含20480个token，这个值最小可以设为生成序列的最长长度（包括prompt）

  - 改变模型的并行策略，即`allocation_mode`，可以尝试减少数据并行的大小，增加张量或流水线并行的大小。

#### CUDA error: out of memory

这个问题可能会发生在vLLM初始化CPU KV cache时，表示每台机器的内存不够了。可以减小`actor.vllm.swap_space`解决。

#### RuntimeError: Aborted due to the lack of CPU swap space.

问题的原因是序列长、对KV cache需求大，在GPU显存不够时KV cache会被卸载到内存，而内存中设置的swap space不够。这个问题和Preemption的报错紧密相关。解决方案是增加`actor.vllm.swap_space`，如果同样的错误出现，请减少`actor.vllm.max_num_seqs`并参考vLLM官方文档。

#### CUDA error: an illegal memory access was encountered

通常会在vLLM生成阶段出现，同样是显存不足的一种表现。解决方案包括：

- 减小训练batch size或者每个prompt生成的答案数量，但减小后会降低样本效率、延长训练时间

- 将vLLM的attention backend换成xformers