MegaThinking

better tokens, better intelligence, contributing superior tokens to models

Proximal Policy Optimization Algorithms

两阶段循环

为什么可以“多轮”

通常情况下,如果对同一批数据进行多轮优化,策略会因为更新过头而崩溃。但 PPO 引入了 Clipped Objective(裁剪目标函数)

  • 安全护栏:在每一轮优化中,PPO 会计算新策略和采样时的旧策略的概率比。如果这个比值超出了设定的范围(比如 0.81.20.8 \sim 1.2),梯度就会被“截断”。
  • 效果:这确保了即使在这一批数据上反复“薅羊毛”优化,新策略也不会跑得离旧策略太远,从而保证了训练的稳定性。

1. 采样阶段 (Sampling Phase)

  • 动作:让当前的策略 πθold\pi_{\theta_{old}} 在环境中运行一段时间。
  • 产出:收集一批轨迹数据(包括状态 ss、动作 aa、奖励 rr 等)。
  • 性质:这些数据是“新鲜”的,反映了当前策略的行为模式。

在这个阶段,神经网络的参数是固定不动的(即 θold\theta_{old})。Actor (策略网络):在环境中根据概率分布选择动作。数据收集:把 (st,at,rt,st+1)(s_t, a_t, r_t, s_{t+1}) 存入一个临时的 Buffer。目标:收集足够数量的轨迹(比如 2048 个时间步)。

1.1. 计算“标签” (Preprocessing)

在开始训练前,利用收集到的数据计算两个关键值:

  • A^t\hat{A}_t (Advantage):优势函数,用来衡量这个动作比平均水平好多少。
  • RtR_t (Returns):这一步动作带来的累积奖励。

注意到

  • rt(θ)r_t(\theta):新旧策略概率比(用于 Actor)。
  • A^t\hat{A}_t:优势估计(用于 Actor,决定更新方向)。
  • RtR_t:回报目标值(用于 Critic,提升估值精度)。

如果只用即时奖励 rtr_t 作为目标,Critic 就会变得非常“短视”。即时奖励 rtr_t:只代表当前这一秒好不好。回报目标 RtR_t:代表了“做出这个动作后,直到最后我一共拿了多少分”。目的:我们希望 Critic 能够预判未来。所以我们要让 V(st)V(s_t) 去拟合这个 RtR_t

Rt=A^t+V(st)R_t = \hat{A}_t + V(s_t)

RtR_t (Returns):作为 Critic 网络的监督信号(标签)。

  • 计算逻辑:通过 A^t\hat{A}_t(优势)与采样时旧的 V(st)V(s_t) 相加得到:Rt=A^t+V(st)R_t = \hat{A}_t + V(s_t)
  • 物理意义:它代表了在当前策略下,从状态 sts_t 开始预期能获得的折现总奖励。Critic 的优化目标就是让预测值 Vθ(st)V_\theta(s_t) 尽可能接近这个 RtR_t

这意味着:

  1. 先用 GAE 算出了优势估计 A^t\hat{A}_t
  2. 通过 A^t+V(st)\hat{A}_t + V(s_t),你就反向推导出了这一步动作对应的“目标回报” RtR_t
  3. 价值损失 (Value Loss) 就变成了:MSE(Vnew(st),Rt)MSE(V_{new}(s_t), R_t)
总结
  • Actor:利用 A^t\hat{A}_t(相对好坏)来决定 θ\theta 的更新方向。
  • Critic:利用 RtR_t(绝对得分)来修正自己对世界的认知。

2. 优化阶段 (Optimization Phase)

多轮优化 (Several Epochs)

  • 动作:将刚才采样的这一批数据反复输入神经网络进行多次梯度更新。
  • 关键点:在传统的 On-policy 算法(如普通的策略梯度)中,这批数据更新一次就必须扔掉。但 PPO 允许你在同一批数据上跑 3 轮、5 轮甚至 10 轮(Epochs)。

要把 Buffer 里的数据,分成更小的 Mini-batches,重复训练 KK 个 Epochs(比如 K=10K=10)。在每一个 Epoch 里的微观操作:计算概率比 rt(θ)r_t(\theta):用当前正在更新的 θ\theta 计算动作概率,除以采样时的 θold\theta_{old} 计算的概率。应用裁剪 CLIPCLIP:如果 rt(θ)r_t(\theta) 偏离 1 太远(比如超过 20%),就强行截断。梯度更新:通过反向传播更新参数 θ\theta

为什么 rt(θ)r_t(\theta) 允许“多轮更新”。PPO 能够从 On-policy 转向近乎 Off-policy 的理论支柱,PPO 本质上是利用了重要性采样技术。

  • 理论背景:我们想优化新策略 πθ\pi_\theta,但手里只有旧策略 πθold\pi_{\theta_{old}} 采到的数据。
  • 补偿机制:通过概率比率 rt(θ)r_t(\theta),我们修正了数据分布的偏差。
  • 约束:重要性采样要求两个分布不能差太远,否则方差会爆炸。这正是 LCLIPL^{CLIP} 存在的根本原因——它在数学上维护了重要性采样的有效区间。

2.1. 优势估计

通常采用 GAE (Generalized Advantage Estimation)。

简单来说,优势函数 A^t\hat{A}_t 的目标是回答:“在状态 sts_t 下采取动作 ata_t,比平均情况(即 Baseline)好多少?”

2.1.1. 计算时序差分残差(Temporal Difference Error)

首先计算每一个时间步的即时偏差 δt\delta_t。它衡量了“实际观测到的奖励 + 下一步的估值”与“当前估值”之间的差距:

δt=rt+γV(st+1)V(st)\delta_t = r_t + \gamma V(s_{t+1}) - V(s_t)

  • rtr_t:当前步获得的奖励。
  • V(st+1)V(s_{t+1}):神经网络(Critic)对下一步状态的估值。
  • V(st)V(s_t):神经网络(Critic)对当前状态的估值。
2.1.2. 累加衰减

[0, T)

优势估计 A^t\hat{A}_t 不是只看当前这一步,而是要把未来的 δ\delta 都考虑进来,但要进行指数衰减。公式如下:

A^t=δt+(γλ)δt+1+(γλ)2δt+2++(γλ)T1tδT1\hat{A}_t = \delta_t + (\gamma\lambda)\delta_{t+1} + (\gamma\lambda)^2\delta_{t+2} + \cdots + (\gamma\lambda)^{T-1-t}\delta_{T-1}

这里有两个关键的超参数:

  • γ\gamma (Gamma):折扣因子(通常 0.99),决定了对远期奖励的重视程度。
  • λ\lambda (Lambda):GAE 因子(通常 0.95),用于在偏差(Bias)和方差(Variance)之间做权衡。

实现时,逆序(t)计算

  • 如果 λ=0\lambda = 0A^t=δt\hat{A}_t = \delta_t。这叫 1-step TD。它很稳定(方差小),但如果你的 VV 函数估值不准,它就会错得离谱(偏差大)。

  • 如果 λ=1\lambda = 1A^t\hat{A}_t 变成了从当前步到截断点 TT 的所有奖励累加。这很真实(无偏差),但环境随机性太强,导致数值跳变剧烈(方差大)。

这就是 λ\lambda 用于在偏差(Bias)和方差(Variance)之间做权衡的物理意义。PPO 选取 λ=0.95\lambda = 0.95 它在“相信神经网络的估值”和“相信实际观测到的奖励”之间取了一个折中。

2.1.3. 标准化 (Advantage Normalization)

在算出 TT 个时间步的所有 A^t\hat{A}_t 后,工程上通常会进行一次标准化处理:

A^t=A^tmean(A^)std(A^)+108\hat{A}_t = \frac{\hat{A}_t - \text{mean}(\hat{A})}{\text{std}(\hat{A}) + 10^{-8}}

  • 稳定梯度:在一个 Batch 中,优势值的数值跨度可能很大。标准化后,它们的均值为 0,标准差为 1。
  • 逻辑闭环:这确保了在一个 Batch 里,大约有一半的动作会被认为是“好于平均”(正值,增加概率),另一半是“差于平均”(负值,减小概率)。这对于 Adam 优化器的稳定收敛极其重要。
总结计算流程

rt(θ)=πθ(atst)πθold(atst)r_t(\theta) = \frac{\pi_\theta(a_t | s_t)}{\pi_{\theta_{old}}(a_t | s_t)}

  1. 运行 TT 步采样,收集所有的 rr 概率比例和 VV 状态价值。
  2. 从后往前计算(这样可以用 At+1A_{t+1} 算出 AtA_t):
  • At=δt+(γλ)At+1A_t = \delta_t + (\gamma\lambda) A_{t+1}
  1. 对整个 Batch 进行标准化。
  2. 将算好的 A^\hat{A} 输入 LCLIPL^{CLIP} 进行优化。

2.2. 损失函数

优势估计 A^t\hat{A}_t 和概率比率 rt(θ)r_t(\theta) 都准备好了,进入 PPO 执行阶段构建 Loss 函数并进行参数更新

Adam 优化器并不是只优化策略,它其实是在同时优化三个目标。

总损失函数 LtCLIP+VF+SL^{CLIP+VF+S}_t 通常长这样:

Lttotal(θ)=LtCLIP(θ)c1LtVF(θ)+c2S[πθ](st)L_t^{total}(\theta) = L_t^{CLIP}(\theta) - c_1 L_t^{VF}(\theta) + c_2 S[\pi_\theta](s_t)

LCLIP(θ)=E^t[min(rt(θ)A^t,clip(rt(θ),1ϵ,1+ϵ)A^t)]L^{CLIP}(\theta) = \hat{\mathbb{E}}_t \left[ \min \left( r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1 - \epsilon, 1 + \epsilon) \hat{A}_t \right) \right]

这三个部分分工明确:

  • LtCLIP(θ)L_t^{CLIP}(\theta) (策略损失):利用 A^t\hat{A}_trt(θ)r_t(\theta) 进行裁剪优化。它负责告诉 Actor:“哪些动作该多做,但别改得太猛。”
  • LtVF(θ)L_t^{VF}(\theta) (价值损失):通常是均方误差 MSE(Vθ(st),Vtarget)MSE(V_\theta(s_t), V_{target})。它负责告诉 Critic:“你的预言(估值)要更准一点。”
  • S[πθ](st)S[\pi_\theta](s_t) (熵奖励):鼓励策略保持一定的随机性。它负责告诉模型:“别太早固定死某一个动作,多去探索其他可能性。”

MSE 均方误差

2.2.1. 执行 Adam 更新

Adam 优化器, 梯度下降 (Gradient Descent)

有了总损失后,流程如下:

  1. 计算梯度:对总损失关于参数 θ\theta 求导(即之前提到的 L wrt θL \text{ wrt } \theta)。
  2. 反向传播:将梯度传回神经网络。
  3. 参数更新:Adam 优化器根据动量和自适应学习率微调 θ\theta

进入 KK 个 Epoch 的循环

针对同一批采样数据(那 NTNT 个样本),反复进行 KK 次上述的“计算 Loss -> 更新参数”过程。

  • 在第 1 遍时:rt(θold)=1r_t(\theta_{old}) = 1,大家都在正常学习。
  • 在第 KK 遍时:由于参数已经改了好几次,新旧策略的偏差 rt(θ)r_t(\theta) 可能会很大。这时候 Clipping(裁剪) 就会大显身手,强行把那些偏移过大的梯度归零,防止模型跑飞。

注:虽然 PPO 的理论目标是最大化奖励,但在代码实现中,我们通过对总目标函数取负值,将其转化为一个最小化损失的问题,从而利用 Adam 优化器进行参数更新。

2.2.2. 更新旧策略 (θoldθ\theta_{old} \leftarrow \theta)

KK 次迭代结束,这一批数据的价值就被“榨干”了。
此时,我们将当前的最新参数 θ\theta 赋值给 θold\theta_{old}。然后清空缓存的数据,回到环境里,开启下一轮 N×TN \times T 的数据采集。

3. Hyperparameters 参考

参数 常用值 作用
ϵ\epsilon 0.10.20.1 \sim 0.2 裁剪阈值,限制单次更新步长
γ\gamma 0.990.99 长期奖励折扣因子
λ\lambda 0.950.95 GAE 平衡因子
c1c_1 0.50.5 价值损失权重(MSE 权重)
c2c_2 0.010.01 熵系数(鼓励探索,防止过早收敛)
KK 3103 \sim 10 每个 Batch 的重复训练次数(Epochs)

在 KubeRay 里,Ray Job 由 Ray Cluster 承载与管理,真正的难点往往在于 如何把 Ray Job 与 Ray Cluster 的生命周期对齐

rayjob-raycluster

先看通用 Job:常用 init container 拉数据、下发配置等;init 失败即整次 Job 失败,这很直观。

Ray Job 不同:它并不是「那一组实际跑在集群里的 Pod」——资源实体是 Ray Cluster;所谓 Ray Job 的 init container,也落在 Cluster 侧。结果是 Job 侧的 init 语义,会和 Cluster 的 init / bootstrap 语义绑在一起

  • 站在 Cluster 视角:init 失败时反复重试直到 bootstrap 成功,常常说得通——先得把集群建立起来。
  • 站在 Job 视角:Job 是一次性任务,init 失败更合理的预期是 fail fast,而不是长期跟着 Cluster 重试。

也是在借助大模型拆解 issue、梳理场景,并对照代码与线上行为逐项验证的过程中,才逐渐理解:Ray Job 对 init container 的生命周期管理,很难用一套简单直白、一步到位的规则实现。

1h vibe issue, 8h vibe coding
70M tokens cost

https://github.com/ray-project/kuberay/issues/4637

两种典型模式:

  • 新建 Ray Cluster:Ray Job 的 init container 会生效;在这条路径下,它实际就是跑在 Ray Cluster 上的 init container,与 Cluster 的 bootstrap 同一条链路。
  • 使用已有 Ray Cluster:Ray Job 的 init container 不生效;Job 只消费已有 Cluster,不会为本次 Job 再单独跑一轮 init。

Cluster 自身的生命周期也要分开看:

  • Job 新建的 Cluster:可用 Ray Job 的 delete rule 决定在 Job 结束后是否删除 Cluster / Workers 等;默认为保留 Cluster
  • 沿用已有 Cluster:Ray Job 结束 不改变 Cluster 的生命周期(Cluster 可能继续服务其他任务或由别处托管)。

回到 Ray Job 自定义 init 失败——这发生在 Job 新建并绑定的专属 Cluster 上。Job 结束后如果 Cluster 长期保留,语义上确实容易别扭;而更合理的模式通常是 短时间保留现场(以收集日志和进行问题诊断),然后自动回收这个临时 Cluster。这本质上是一种「单次 Job + 一套临时 Cluster」的短生命期部署方式,与长期共用 Cluster 是两套完全不同的心智模型。

不过需要注意,当前 Ray Job 实际上并不支持这样的能力:Job 自定义 init 失败后,Job 仍然处于 initializing 状态,系统还不会自动实现「短暂保留现场再回收 Cluster」的行为。可以继续 vibe issue, 基于 issue vibe coding。

尚未系统整理,先记一个粗判断:整体架构在走向成熟,部署形态普遍解耦——不止训推分离,还出现了 Agent 应用与训推平台、训推 API(如 Tinker API)、训推框架的分层

在这种形态下,常见会并行做两件事:一是用 OpenTelemetry(以 spans 为主) 做标准化 trace,沉淀模型 / Agent 的行为轨迹,再回流进 RL 训练闭环;二是通过 LLM Proxy 统一 Agent 侧使用的模型 API,在训练态把请求 转发到当前正在更新的模型,由它承担 RL 里的推理侧,避免应用侧调用与训练态模型服务两条路径对不齐。

关于轨迹记录与训练回流,直觉上和早年搜索推荐那一套并无本质不同:线上记录与埋点 → 数据回流 → 离线实验与训练

https://www.hiascend.com/document/detail/zh/mindcluster/70rc1/clustersched/dlug/mxdlug_007.html

有如下几类 configmap

  • cmDevice: ns, kube-system; cmName, mindx-dl-deviceinfo-{NodeName}; which is reported by device-plugin
  • cmNode: ns, mindx-dl; cmName, mindx-dl-nodeinfo-{NodeName}; which is reported by nodeD
  • cmPingMesh: ns, cluster-system; cmName, pingmesh-config;
  • cmSuperPodDevice: ns, cluster-system; cmName, super-pod-{SuperPodId}; clusterD 维护
    • 特别的 {RAS_NET_ROOT_PATH}/cluster/super-pod-{SuperPodId}/super-pod-{SuperPodId}.json; clusterD 维护
  • cmPubicFault: mc-consumer-publicfault=true label;

其中 cmDevice configmap mindx-dl-deviceinfo-{NodeName}, 由 device-plugin 上报, 包括如下信息

  • DeviceInfoCfg
  • SwitchInfoCfg

cmPubicFault configmap, 包括如下信息

  • PublicFault

pingmesh-config 的格式为 global pingmesh 任务的配置或者是指定 superpodid 的任务配置

1
2
3
4
{
"activate": "on",
"task_interval": 5
}

node annotation 中包括如下信息

  • product-serial-number
  • superPodID
  • baseDeviceInfos
  • serverType
  • serverIndex

THP

https://alexandrnikitin.github.io/blog/transparent-hugepages-measuring-the-performance-impact/

增加 page 大小, 从而减少 TLB 大小; 由于 walk TLB 开销较大, 所以是个优化

THP 会让 os 申请连续的内存空间大小, 但如果申请不到, 则 os 会开始 compact, reclaim or page out other pages;

That process is expensive and could cause latency spikes (up to seconds)

cat /proc/buddyinfo

Each column represents the number of pages of a certain order which are
available. In this case, there are 0 chunks of 2^0PAGE_SIZE available in
ZONE_DMA, 4 chunks of 2^1
PAGE_SIZE in ZONE_DMA, 101 chunks of 2^4*PAGE_SIZE
available in ZONE_NORMAL, etc…

https://andorian.blogspot.com/2014/03/making-sense-of-procbuddyinfo.html

https://wangcong.net/article/FPandBP.html

pathways

https://blog.research.google/2022/04/pathways-language-model-palm-scaling-to.html

a single model that could generalize across domains and tasks while being highly efficient. An important milestone toward realizing this vision was to develop the new Pathways system to orchestrate distributed computation for accelerators.

few-shot

TPU v4 Pods

Pipelining is typically used with DCN

word to vector, This vector represents the word’s meaning and context within the given language

embedding layer, lookup table

Positional encoding

https://medium.com/@tech-gumptions/transformer-architecture-simplified-3fb501d461c8

This means that the output of a layer is added to the initial input, allowing the model to learn to only make small changes to the input

The decoder’s job is to produce the English sentence based on both the original French sentence and the bits of the English sentence it has generated so far.

Input Embedding: Just as with the Encoder, the input to the Decoder (which is the target sequence during training) is first embedded into continuous vectors.

It’s important to note that this masking is only applied during training. During inference, the decoder can attend to all words in the target sequence, including future words.

To summarize, the Decoder in the Transformer architecture processes its input through self-attention, cross-attention with the Encoder’s output, and position-wise Feed-Forward networks, repeatedly for each stacked block, culminating in a final output sequence after the softmax operation.

https://jalammar.github.io/illustrated-transformer/

https://nlp.seas.harvard.edu/2018/04/03/attention.html

https://jalammar.github.io/illustrated-gpt2/

PPO/GRPO/重要性采样/拒绝采样

megatron 保存 ckpt 原理

多节点测试依赖 mpi。编译时打开 MPI 开头。

测试时配置多节点 ssh 免密。另外如果是 RoCE 网络,注意正确配置 NCCL 无损队列匹配 RoCE 无损队列。

逐渐调大 size 衡量网络带宽情况。

https://docs.nvidia.com/deeplearning/nccl/user-guide/docs/env.html#nccl-algo

NCCL_ALGO=ring 衡量网络带宽时较为稳定。

https://github.com/NVIDIA/go-nvml

The nvml.h file is a direct copy of nvml.h from the NVIDIA driver. Since the NVML API is guaranteed to be backwards compatible, we should strive to keep this always up to date with the latest.

https://github.com/xlab/c-for-go.git

golang cgo

https://www.rectcircle.cn/posts/go-static-compile-and-cgo

https://chai2010.cn/advanced-go-programming-book/ch2-cgo/ch2-05-internal.html

poc env, windows11 + wsl2 ubuntu 18.04

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
~/projects/go-nvml ❯ nvidia-smi
Sun Dec 17 18:57:57 2023
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 545.29.04 Driver Version: 546.17 CUDA Version: 12.3 |
|-----------------------------------------+----------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+======================+======================|
| 0 NVIDIA GeForce RTX 3070 Ti On | 00000000:06:00.0 On | N/A |
| 0% 33C P8 13W / 290W | 1139MiB / 8192MiB | 1% Default |
| | | N/A |
+-----------------------------------------+----------------------+----------------------+

+---------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=======================================================================================|
| 0 N/A N/A 23 G /Xwayland N/A |
+---------------------------------------------------------------------------------------+

test code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
"fmt"
"log"
"os"

"github.com/NVIDIA/go-nvml/pkg/nvml"
)

func getNvidiaDeviceCount() {
ret := nvml.Init()
if ret != nvml.SUCCESS {
log.Fatalf("Unable to initialize NVML: %v", nvml.ErrorString(ret))
}
count, ret := nvml.DeviceGetCount()
if ret != nvml.SUCCESS {
log.Fatalf("Unable to get device count: %v", nvml.ErrorString(ret))
}
fmt.Printf("%d\n", count)
}

func main() {
args := os.Args
if len(args) < 2 {
fmt.Println("hello")
} else {
getNvidiaDeviceCount()
}
}

build commands

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
export CGO_LDFLAGS="-Wl,-z,now"

go build main.go
./main
./main: symbol lookup error: ./main: undefined symbol: nvmlGpuInstanceGetComputeInstanceProfileInfoV

./main fake
./main: symbol lookup error: ./main: undefined symbol: nvmlGpuInstanceGetComputeInstanceProfileInfoV

# now to lazy
export CGO_LDFLAGS="-Wl,-z,lazy"
go build main.go
./main
hello

./main fake
1

go clean --cache && rm -rf main
go build -work -x main.go

go build -x

1
2
3
4
5
6
7
8
9
10
11
12
cd /root/go/pkg/mod/github.com/!n!v!i!d!i!a/go-nvml@v0.12.0-1/pkg/nvml
TERM='dumb' CGO_LDFLAGS='"-Wl,-z,lazy" "-Wl,--unresolved-symbols=ignore-in-object-files" "-Wl,--unresolved-symbols=ignore-in-object-files"' /root/tools/go/pkg/tool/linux_amd64/cgo -objdir $WORK/b002/ -importpath github.com/NVIDIA/go-nvml/pkg/nvml -- -I $WORK/b002/ -g -O2 -DNVML_NO_UNVERSIONED_FUNC_DEFS=1 -DNVML_NO_UNVERSIONED_FUNC_DEFS=1 ./cgo_helpers.go ./const.go ./init.go ./nvml.go
cd $WORK/b002
TERM='dumb' gcc -I /root/go/pkg/mod/github.com/!n!v!i!d!i!a/go-nvml@v0.12.0-1/pkg/nvml -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b002=/tmp/go-build -gno-record-gcc-switches -I ./ -g -O2 -DNVML_NO_UNVERSIONED_FUNC_DEFS=1 -DNVML_NO_UNVERSIONED_FUNC_DEFS=1 -o ./_x001.o -c _cgo_export.c
TERM='dumb' gcc -I /root/go/pkg/mod/github.com/!n!v!i!d!i!a/go-nvml@v0.12.0-1/pkg/nvml -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b002=/tmp/go-build -gno-record-gcc-switches -I ./ -g -O2 -DNVML_NO_UNVERSIONED_FUNC_DEFS=1 -DNVML_NO_UNVERSIONED_FUNC_DEFS=1 -o ./_x002.o -c cgo_helpers.cgo2.c
TERM='dumb' gcc -I /root/go/pkg/mod/github.com/!n!v!i!d!i!a/go-nvml@v0.12.0-1/pkg/nvml -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b002=/tmp/go-build -gno-record-gcc-switches -I ./ -g -O2 -DNVML_NO_UNVERSIONED_FUNC_DEFS=1 -DNVML_NO_UNVERSIONED_FUNC_DEFS=1 -o ./_x003.o -c const.cgo2.c
TERM='dumb' gcc -I /root/go/pkg/mod/github.com/!n!v!i!d!i!a/go-nvml@v0.12.0-1/pkg/nvml -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b002=/tmp/go-build -gno-record-gcc-switches -I ./ -g -O2 -DNVML_NO_UNVERSIONED_FUNC_DEFS=1 -DNVML_NO_UNVERSIONED_FUNC_DEFS=1 -o ./_x004.o -c init.cgo2.c
TERM='dumb' gcc -I /root/go/pkg/mod/github.com/!n!v!i!d!i!a/go-nvml@v0.12.0-1/pkg/nvml -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b002=/tmp/go-build -gno-record-gcc-switches -I ./ -g -O2 -DNVML_NO_UNVERSIONED_FUNC_DEFS=1 -DNVML_NO_UNVERSIONED_FUNC_DEFS=1 -o ./_x005.o -c nvml.cgo2.c
TERM='dumb' gcc -I /root/go/pkg/mod/github.com/!n!v!i!d!i!a/go-nvml@v0.12.0-1/pkg/nvml -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b002=/tmp/go-build -gno-record-gcc-switches -I ./ -g -O2 -DNVML_NO_UNVERSIONED_FUNC_DEFS=1 -DNVML_NO_UNVERSIONED_FUNC_DEFS=1 -o ./_cgo_main.o -c _cgo_main.c
cd /root/projects/go-nvml
TERM='dumb' gcc -I /root/go/pkg/mod/github.com/!n!v!i!d!i!a/go-nvml@v0.12.0-1/pkg/nvml -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b002=/tmp/go-build -gno-record-gcc-switches -o $WORK/b002/_cgo_.o $WORK/b002/_cgo_main.o $WORK/b002/_x001.o $WORK/b002/_x002.o $WORK/b002/_x003.o $WORK/b002/_x004.o $WORK/b002/_x005.o -Wl,-z,lazy -Wl,--unresolved-symbols=ignore-in-object-files -Wl,--unresolved-symbols=ignore-in-object-files
TERM='dumb' /root/tools/go/pkg/tool/linux_amd64/cgo -dynpackage nvml -dynimport $WORK/b002/_cgo_.o -dynout $WORK/b002/_cgo_import.go
  1. /tmp/go-build2475505462/b002/nvml.cgo1.go
  2. /tmp/go-build2475505462/b002/nvml.cgo2.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CGO_NO_SANITIZE_THREAD
void
_cgo_c813f6172e91_Cfunc_nvmlGpuInstanceGetComputeInstanceProfileInfoV(void *v)
{
struct {
nvmlGpuInstance_t p0;
unsigned int p1;
unsigned int p2;
nvmlComputeInstanceProfileInfo_v2_t* p3;
nvmlReturn_t r;
char __pad28[4];
} __attribute__((__packed__, __gcc_struct__)) *_cgo_a = v;
char *_cgo_stktop = _cgo_topofstack();
__typeof__(_cgo_a->r) _cgo_r;
_cgo_tsan_acquire();
_cgo_r = nvmlGpuInstanceGetComputeInstanceProfileInfoV(_cgo_a->p0, _cgo_a->p1, _cgo_a->p2, _cgo_a->p3);
_cgo_tsan_release();
_cgo_a = (void*)((char*)_cgo_a + (_cgo_topofstack() - _cgo_stktop));
_cgo_a->r = _cgo_r;
_cgo_msan_write(&_cgo_a->r, sizeof(_cgo_a->r));
}

ChatGPT 3.5

-Wl,-z,lazy, -Wl,-z,now

-Wl,-z,lazy: The -Wl,-z,lazy flag in the gcc command is a linker option used to instruct the linker to utilize lazy binding for dynamic libraries during the linking process.
When a program uses shared libraries (dynamic libraries), such as .so files in Linux, the linking process involves resolving symbols (functions or global variables) from these libraries. Lazy binding delays the resolution of these symbols until they are actually referenced during the program’s execution, rather than resolving all symbols at startup.
Lazy binding delays the resolution of these symbols until they are actually referenced during the program’s execution, rather than resolving all symbols at startup.

-Wl,-z,now: When you compile a program using gcc with the -Wl,-z,now flag, it influences how the dynamic linker behaves at runtime, particularly when the program is executed and loaded into memory. This flag impacts the linking stage, ensuring that symbols from shared libraries are resolved and bound immediately during the linking phase.
During the binary’s execution, when shared libraries are loaded, immediate binding might help in reducing the overhead associated with symbol resolution at runtime because the symbols are already resolved and bound during the linking process.
In summary, the -Wl,-z,now flag influences the behavior of the linker while creating the binary, affecting how symbol resolution occurs when the binary is loaded and executed, potentially impacting the startup performance by pre-resolving symbols.

近期的一些杂项

infra decouple from kind of internal production system

重点并不是 infra 如何帮忙自动恢复, 只是略有提到; 重点还是训练的调参

famous uncorrectable ECC error

we just restart the run

try to make run stable (数学上的稳定)

FP16

Lost GPU
CUDA errors
Job hanging
NCCL error
Job Slowdown

High DRAM correctable errors etc.
blob storage issues

when we are training these models, we kind of just stare at tensorboard all day

in general the mixture of hardware issues, training like numerical converting issues

~30days change the hyperparameter to try to get through

56days, 53 - 54 restarts, OPT-175B survived 143K steps

Andrej Karpathy

LLM

LAMA-2-70B

fp16, 2bytes, 70B

2 * 70B = 140B bytes = 140 * 1,000,000,000 bytes = 140,000,000,000 bytes = 140 gigabytes (bytes, kbytes, mbytes, gbytes)

140GB

tokenize

encoder, 将字符串转换为整数编码
decoder, 将整数编码转为字符串

Terms

  • SXM: Server PCI Express Module, a high bandwidth socket solution for connecting Nvidia Compute Accelerators to a system
  • NVL: NVLink is a wire-based serial multi-lane near-range communications link developed by Nvidia. Unlike PCI Express, a device can consist of multiple NVLinks, and devices use mesh networking to communicate instead of a central hub.
  • PCIe: PCI Express (Peripheral Component Interconnect Express), officially abbreviated as PCIe or PCI-e,[1] is a high-speed serial computer expansion bus standard

from Wikipedia

H800 vs H100

  1. https://resources.nvidia.com/en-us-tensor-core/nvidia-tensor-core-gpu-datasheet

  2. NVIDIA H100 Tensor Core GPU

  3. H800 没找到 NVIDIA 官网 Specification, 只能从代理商和一些B站UP主看到的数据

H800 SXM H100 SXM
FP64 1 teraFLOPS 34 teraFLOPS
FP64 Tensor Core 1 teraFLOPS 67 teraFLOPS
FP32 67 teraFLOPS 67 teraFLOPS
TF32 Tensor Core 989 teraFLOPS 989 teraFLOPS
BFLOAT16 Tensor Core 1,979 teraFLOPS 1,979 teraFLOPS
FP16 Tensor Core 1,979 teraFLOPS 1,979 teraFLOPS
FP8 Tensor Core 3,958 teraFLOPS 3,958 teraFLOPS
INT8 Tensor Core 3,958 TOPS 3,958 TOPS
GPU memory 80GB 80GB
GPU memory bandwidth 3.35TB/s 3.35TB/s
Interconnect NVLink 400GB/s PCIe Gen5: 128GB/s NVLink 900GB/s PCIe Gen5: 128GB/s
  • H800 FP64 算力限制

Driver

https://resources.nvidia.com/en-us-tensor-core/gtc22-whitepaper-hopper

https://www.nvidia.com/content/dam/en-zz/Solutions/gtcs22/data-center/h100/PB-11133-001_v01.pdf

Software Specifications

Specification Description
Driver support Linux: R520 or later

CUDA

https://docs.nvidia.com/datacenter/tesla/drivers/index.html#cuda-arch-matrix

Architecture CUDA Capabilities First CUDA Toolkit Support
Hopper 9.0 CUDA 11.8
CUDA 12.0

TensorFlow

https://www.tensorflow.org/install/source#tested_build_configurations

Version Python version Compiler Build tools cuDNN CUDA
tensorflow-2.15.0 3.9-3.11 Clang 16.0.0 Bazel 6.1.0 8.8 12.2
tensorflow-2.14.0 3.9-3.11 Clang 16.0.0 Bazel 6.1.0 8.7 11.8
tensorflow-2.13.0 3.8-3.11 Clang 16.0.0 Bazel 5.3.0 8.6 11.8
tensorflow-2.12.0 3.8-3.11 GCC 9.3.1 Bazel 5.3.0 8.6 11.8
tensorflow-2.11.0 3.7-3.10 GCC 9.3.1 Bazel 5.3.0 8.1 11.2
tensorflow-2.6.0 3.6-3.9 GCC 7.3.1 Bazel 3.7.2 8.1 11.2

candidates on H800

  • >= tensorflow-2.12.0

docker images

1
2
3
docker pull tensorflow/tensorflow:2.14.0-gpu
docker pull tensorflow/tensorflow:2.13.0-gpu
docker pull tensorflow/tensorflow:2.12.0-gpu

PyTorch

https://pytorch.org/get-started/previous-versions/

Version CUDA
v1.13.1 11.6, 11.7
v2.0.0 11.7, 11.8
v2.0.1 11.7, 11.8
v2.1.0 11.8, 12.1
v2.1.1 11.8, 12.1

candidates on H800

  • >= v2.0.0, with cuda 11.8 support

docker images

1
2
3
4
5
docker pull pytorch/pytorch:2.1.0-cuda11.8-cudnn8-devel
docker pull pytorch/pytorch:2.1.0-cuda11.8-cudnn8-runtime

docker pull pytorch/pytorch:2.0.1-cuda11.7-cudnn8-devel
docker pull pytorch/pytorch:2.0.1-cuda11.7-cudnn8-runtime
0%