ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

大模型训练评估与性能优化:MindSpore实践指南

大模型训练评估与性能优化:MindSpore实践指南 在做大模型训练的时候很多人的第一反应是只要 loss 在掉、显存不爆这活儿就算成了。但真正跑过千亿 token 级别的预训练任务之后你会发现一个很扎心的现实——模型能跑起来和模型能高效地跑完完全是两码事。我去年接手了一条基于昇思 MindSpore 的 LLM 预训练流水线。刚开始的两周大部分时间都花在“猜问题”上感觉训练变慢了是数据加载卡的还是通信卡了loss 突然波动一下是学习率没调好还是数据集里混入了脏数据那段时间最痛苦的不是报错而是问题不知道从哪查起。后来我下决心把训练评估体系完整建起来同时把性能优化从“凭感觉调参”改成“按数据定位、分层优化”整个流程才真正稳定下来训练吞吐提升了接近一倍。这篇文章就围绕这套“评估体系 性能优化”的思路展开讲清楚三件事评估体系到底应该怎么搭、核心指标有哪些坑、以及从 MindSpore 的实际特性出发怎么做性能优化。内容更适合正在用 MindSpore 做大规模训练、或者刚接手训练流水线想系统排查问题的人平台研发和算法同学都能用得上。1. 先想清楚一件事评估体系的核心价值不是“看指标”而是“让问题提前暴露”1.1 大模型训练的评估到底在评估什么很多团队其实不是没有评估而是把评估做窄了。最常见的情况是只盯 training loss其他全靠人工巡检。真正稳定的评估体系至少得覆盖三个维度第一个维度是训练质量也就是模型有没有收敛、收敛得够不够快。这部分关注的是最终 loss 水平、收敛所需的 step 数、以及不同阶段的 loss 曲线形态。大模型训练成本极高早停判断、模型版本选择、超参调优全都依赖这一层的数据不能靠“看起来差不多”来决策。第二个维度是训练效率对应的是吞吐量、算力利用率这些数字。大家常算的 MFUModel FLOPs Utilization就是典型指标。它的定义是实际吞吐中真正用来做有效计算的浮点运算比例除以硬件理论峰值算力后得到的百分比。对大模型来说一次前向加反向的浮点运算量大约可以用6 × N × D来估算N 是参数量D 是训练 token 数。假设你的模型参数量是 7B训练数据量是 500B token总浮点预算大概是6 × 7e9 × 500e9 ≈ 2.1e19 FLOPs如果你设备的理论算力是 100 PFLOPS但是实测训练时有效算力只有 30 PFLOPS算出来 MFU 就是百分之三十左右。这个数字如果太低说明模型并行策略、数据管道或者通信结构里一定有环节在拖后腿单纯调大 batch size 是救不回来的。第三个维度是训练稳定性专门盯异常信号loss 是否出现尖峰、梯度范数是否突然冲高、是否有 NaN/INF 出现、checkpoint 保存是否抖动、训练会不会在某个 step 无响应。大模型训练周期短则几天、长则几周很多中断事故开始时的征兆就藏在这些异常信号里。能提前捕获一个异常比事后恢复节省的时间成本是小时级别的。所以评估体系的意义不是“有了几个数字可以写周报”而是当训练出问题时你手里有足够的数据去判断问题出在数据侧、模型侧还是运行环境侧。没有这套数据所有的排查都像是在黑盒里用打火机找掉落的螺丝。1.2 三段式评估时机训练前、训练中、训练后我习惯把评估体系拆成三个阶段来设计分别解决“能不能跑”“跑得好不好”“这次跑完总结了什么”三个问题。训练前评估的重点是静态检查和配置校验。模型结构、超参数、数据 shape、迭代 step 数、checkpoint 保存频率、并行切分方式、数据集分片逻辑这些都要在正式训练前过一遍。为什么这一步重要因为大模型一旦跑起来一次失败重来可能就浪费几小时甚至几天。在 MindSpore 里我会先打印出网络各层输出的 shape再把model.train前的数据 shape 对齐检查一遍。尤其是用了张量并行、流水线并行这类模型并行方案后层的切分会引入额外的维度要求比如多头注意力按头拆分时隐藏维度必须能被并行卡数整除。这种问题在训练前没暴露run 起来就是各种维度对不上报错信息又长又绕排查成本极高。训练中评估是动态监控的核心。用回调机制记录每个 step 的 loss、当前学习率、每 step 耗时、吞吐、梯度范数设置阈值告警。这部分我会在下一节详细展开。训练后评估是整体复盘。训练完了把整段训练过程数据汇总分析loss 曲线平滑后的走势、收敛点的 step 位置、平均吞吐和峰值吞吐、不同阶段的资源利用率变化、有没有异常 step 的分布。最后沉淀成一份评估报告和上次实验结果做对比。这里有个容易被忽略的习惯一定要把训练 hyperparameter、数据集版本、框架版本和评估结果绑定存档。否则你只记了“这批效果更好”却说不清楚好在哪里下一轮实验又从头开始。三个阶段配合好评估体系就不是事后诸葛而是训练过程中随时可用的“仪表盘”。2. 评估指标与配套工具哪些数字值得盯哪些工具能帮你省事2.1 六类关键指标详解真正在大模型训练里长期有用、且能驱动决策的指标我按下面六类来归纳类别核心指标用途说明注意事项吞吐类samples/s、tokens/s、steps/s衡量训练速度用来做产能预估统计时要排除编译期、checkpoint 保存期和空转 step算力利用类MFU、有效 FLOPs判断硬件算力是否被充分压榨需结合模型结构和理论峰值算力一起算收敛类step loss、平滑 loss、收敛 step 数判断模型训练方向和收敛速度用滑动平均看趋势别被单 step 抖动误导稳定性类梯度范数、参数更新量、NaN率提前捕获训练异常梯度范数冲高时优先检查学习率和数据异常资源类显存占用、计算单元利用率、带宽利用率定位资源瓶颈显存峰值要区分参数、梯度和中间激活的分布通信类AllReduce 耗时、通信占比、通信带宽多机多卡场景下排查通信瓶颈通信占比超过总耗时 25% 就要重点关注这六类指标里吞吐和通信耗时我都建议用“e2e 时间”统计不要只看算子在计算单元上的执行时间。原因是端到端时间才是用户感知的训练速度而算子执行时间只反映了一部分工作量。数据加载、Python 侧调度、框架 runtime 开销这三部分经常会让你以为“计算已经很忙了”但实际上整机吞吐就是上不去。收敛类的指标里我比较推荐算平滑 loss。直接用原始 loss 做判断经常会因为单步噪声被误导。平滑窗口可以设成 50 到 100 个 step算移动平均。如果你用 MindSpore 训练可以把这个平滑逻辑写进 callback 里每 50 步输出一次比每步打一条日志干净很多也避免打印太频繁对训练的额外干扰。2.2 MindInsight 与 Callback怎么组合才顺手MindSpore 生态里评估体系的落点主要靠两块MindInsight做可视化Callback做过程数据采集。MindInsight 可以看训练曲线、计算图结构、数据图和数据管道耗时、以及 Profiler 采集到的算子性能和通信性能。我的习惯是训练过程中通过 callback 把关键指标写进 summary 文件训练后用 MindInsight 打开做定向分析。尤其是性能分析模块里的“步骤耗时”和“算子耗时 TopN”视图对大模型的瓶颈定位非常直接。Callback 方面MindSpore 内置了LossMonitor、TimeMonitor、ModelCheckpoint等常用回调。我自己会在内置回调之外再写一个指标采集回调把吞吐、平滑 loss、当前学习率统一打印出来。大致思路是这样的import time from mindspore.train.callback import Callback class MetricMonitor(Callback): def __init__(self, print_per_step100, smoothing50): self.print_per_step print_per_step self.smoothing smoothing self.step_cnt 0 self.loss_history [] self.t0 time.time() def step_begin(self, run_context): self.t0 time.time() def step_end(self, run_context): self.step_cnt 1 loss float(run_context.original_args().net_outputs.asnumpy()) self.loss_history.append(loss) if self.step_cnt % self.print_per_step 0: smooth_loss sum(self.loss_history[-self.smoothing:]) / min(self.smoothing, len(self.loss_history)) elapsed_ms (time.time() - self.t0) * 1000.0 print(fstep{self.step_cnt} smooth_loss{smooth_loss:.4f} step_ms{elapsed_ms:.1f})注意不同 MindSpore 版本对run_context.original_args()的处理有细微差异以你实际使用的版本为准。但核心思路是一样的在 step 边界埋点记录时间和 loss按固定间隔输出。如果你习惯在 VS Code 里做开发调试MindSpore 也有相应的内核支持可以把训练指标直接关联到编辑环境里。不过我的个人建议是调代码用 IDE看训练趋势还是用 MindInsight 或者自建的看板更直观IDE 里刷日志容易刷到眼睛发花。2.3 评估中的两个“隐形坑”采样频率与统计口径这一节是在实际项目里踩过、也帮别人排查过的问题值得单独拎出来说。第一个坑是吞吐统计口径不一致。很多人统计吞吐时用“总样本数除以总时间”但这个总时间往往包含了模型编译时间、checkpoint 保存时间、以及最开始数据缓存还没热起来的那段空转。这样算出来的吞吐会偏低而且每次实验偏低程度还不一样导致实验之间没法比较。我的做法是取训练稳定后的 N 个连续 step只统计这些 step 的平均耗时再换算成 tokens/s。这样才是一个“稳态吞吐”用来评估优化效果才有意义。类比一下的话跑一千米配速不会把系鞋带和换衣服的时间也算进去道理一样。第二个坑是loss 采样频率没有设计。每步都打印 loss 会导致日志膨胀、IO 压力提升在大规模训练时甚至会对性能造成可感知的影响。但采样太稀疏又会漏掉 loss 尖峰和瞬间异常。我建议按 step 维度每 50 到 100 步采样一次同时保留梯度范数的监控。梯度范数比 loss 对异常更敏感很多 NaN 实际上在 loss 变怪之前梯度范数已经爆了。所以我的评估配置里grad norm和loss永远是成对出现的。另外统计 loss 时要注意是否包含蒸馏/辅助 loss 项。如果你的训练任务里有辅助 loss比如某些多任务模型建议分开记录别混在一起。否则主 loss 变化趋势会被辅助项的波动掩盖收敛判断就容易跑偏。3. 性能优化方法论从定位瓶颈到动手落地3.1 定位瓶颈显存、计算、通信先从哪一层看起性能优化最忌讳的就是一上来直接“调参玄学”。我的基本方法分的很朴素先把瓶颈定位到显存、计算、通信三层中的某一层再决定优化手段。如果显存接近上限而且 batch size 提不上去那么瓶颈大概率在显存侧。优先考虑的操作是梯度累积、激活重计算、混合精度、优化器状态 offload。这些手段的核心思路都是牺牲一小部分计算效率或 I/O 开销换来显存空间的释放从而让 batch size 变大或者支持更大模型。如果显存没爆但是硬件计算资源利用率很高那么训练已经处于计算受限状态。这时候再调 batch size、数据管道收益都不会太大应该转向并行策略和算子层面的优化比如调整张量并行和流水线并行的切分方式、融合相邻算子、开启编译优化等。如果计算利用率不高大量时间花在同步等待上那么很可能是通信瓶颈。多机多卡场景里大模型的梯度同步量非常大。7B 模型如果用 Adam 优化器梯度数据量差不多也有 7B 个浮点数换算下来一次 AllReduce 就有几十 GB 的通信量。通信占比一旦偏高优先查网络拓扑、通信算法、以及流水线并行阶段间的调度方式。判断这三个方向我用的是下面这套快速排查表观察现象优先排查方向第一候选优化手段显存占用接近上限batch 提不上去显存侧混合精度 梯度累积显存还有余量计算单元忙得很满计算侧算子融合 并行策略调整计算忙碌但整体吞吐低有大量同步等待通信侧通信算法 网络拓扑 流水线 micro-batch 调度这套方法的依据是任何优化方案都要能回答“它到底在缓解哪一层瓶颈”。如果答不上来多半是无效优化。3.2 混合精度FP16 还是 BF16不是越省显存越好混合精度是现在大模型训练最常规的优化手段但“开 FP16”和“开 BF16”之间的差别很多人没太细究。FP16 的优点是显存占用减半、并能在部分硬件上获得明显加速。但它的动态范围比较窄训练过程中一旦梯度过小就容易下溢成 0梯度过大又容易溢出成 inf。所以 FP16 训练通常要配 loss scaling用一个大数把梯度放大到安全范围内再在更新参数前缩回去。BF16 的设计思路不一样它保留了和 FP32 相同的指数位宽度只是减少了尾数位数。特点是动态范围大不容易溢出所以大模型训练里经常用 BF16 配合混合精度而不需要那么小心翼翼地调 loss scale。代价是尾数精度低对小数位的保留不如 FP16 精准。不过在大规模训练里当 batch size 和样本量都很大的时候梯度的统计平滑效应会部分抵消低精度带来的噪声所以 BF16 在很多场景下反而更稳。MindSpore 里开启混合精度可以通过amp_level参数控制。一般来说LLM 预训练我建议优先尝试 BF16 方案如果硬件不支持再退回 FP16。FP16 方案里loss scale 初始值可以设在 1024 或 2048 附近然后观察梯度范数的变化动态调整。动态 loss scale 机制如果框架支持就开启它能根据溢出情况自动缩放省去手工干预。有一个细节要注意混合精度不是把所有层都切成低精度。BatchNorm、loss 计算、以及部分数值敏感的归一化层通常要保留 FP32。开混合精度后如果发现 loss 曲线变糙、收敛变慢先别急着关掉把 FP32 保留层检查一遍往往问题就出在这里。3.3 梯度累积、并行策略与通信优化把“大”的问题拆小很多时候我们做性能优化的最终目的是在显存有限的前提下用更大的全局 batch size 跑得更稳。这里就绕不开梯度累积。梯度累积的思路很直接每步只处理一个 micro batch先不更新参数攒够若干个 micro batch 的梯度后再统一更新。全局 batch size 的计算公式是global_batch_size micro_batch_size × 数据并行卡数 × 梯度累积步数举个例子假设你有 32 张卡每张卡的 micro batch 是 4梯度累积 8 步那么全局 batch size 就是4 × 32 × 8 1024为什么这个公式重要因为它把“显存限制”和“训练稳定性需求”解耦了。显存不够可以调小 micro batch但为了保证全局 batch 足够大用梯度累积步数来补。需要注意的是梯度累积并不能提升单卡吞吐反而会因为跳过参数更新而略微降低整体效率所以累积步数不要盲目设大够用就好。另外开了梯度累积之后喂给模型的 batch 统计、学习率预热策略、以及 BN 类层的统计方式都要跟着调整否则容易出现批次统计不一致的问题。并行策略方面MindSpore 支持数据并行、张量并行、流水线并行以及自动并行和半自动并行模式。数据并行的逻辑最简单但模型大到单卡显存放不下时就需要模型并行来切分。张量并行把单层内的矩阵运算拆分到多卡流水线并行则是把不同层切分到不同设备设备间按阶段流水执行。流水线并行有一个常见经验值micro batch 数量要大于等于流水线 stage 数量。否则第一张卡计算完自己的层之后要等后面的卡慢慢处理流水线气泡会变得很大性能损耗甚至能达到三成以上。通信优化方面我实际踩过的一个重点是通信协议和同步方式的适配。梯度同步时如果是一种“全量直接同步”的方式通信数据量等于模型参数量的两倍在千卡量级时网络压力非常大。改成梯度分段通信比如 reduce-scatter 加 all-gather 的组合能把通信总量降下来因为每张卡只需要同步跟自己负责的切片有关的那部分梯度。MindSpore 里像enable_parallel_optimizerTrue这类配置就是在干类似的事情把优化器状态也切到各卡上减少跨卡通信和显存冗余。这一行配置在某些场景下的收益能到 20% 到 40%。多机场景下网络拓扑也很关键。如果机间网络走的是普通万兆网机内走高速互联那么做数据并行切分时要尽量让同一批数据切分后的梯度同步发生在机内。这里的本质降低的是“跨机通信”的高延迟成本。虽然这属于集群部署层面的事情但训练代码侧的set_auto_parallel_context和device_num配置直接影响任务被调度到哪些设备动手前想清楚拓扑可以少踩很多坑。4. 实操过程与可复现配置清单4.1 从零接入评估与 Profiler这部分说一个比较实用的接入路径照着走一遍就能把评估链路搭起来。第一步是在训练脚本里注册 callback。内置的LossMonitor用于输出 lossTimeMonitor用于记录 step 耗时ModelCheckpoint用于保存模型。但我更建议用自定义的MetricMonitor代替LossMonitor因为可以同时打印吞吐和梯度范数等关键信息。第二步是开启 Profiler。MindSpore 的 Profiler 可以这样接from mindspore.profiler import Profiler profiler Profiler(output_path./profiler_result) # 在训练主循环或 model.train 前后启动/结束 # profiler.start() # 训练代码 # profiler.end()注意Profiler 会引入额外开销所以大型训练任务里不建议全程开着。我的做法是正式训练和性能测试分两轮跑。性能测试轮开启 Profiler跑三五十个 step 拿到数据分析完就关掉正式训练只开轻量 callback 采集指标不跑 Profiler。第三步是配置 MindInsight 的 summary 记录。如果你想从训练开始就记录曲线可以使用 MindSpore 的SummaryCollector之类的能力把数据落到 summary 文件训练结束时统一用 MindInsight 打开。这样评估、可视化、Profiler 三个工具各司其职callback 管实时打印summary 管长周期曲线Profiler 管性能热点。4.2 一份可复现的评估与优化配置清单下面是我在实际项目里用过的配置模板基于 MindSpore 2.x 常见写法小版本之间可能有差异但思路通用。import mindspore as ms from mindspore import nn, Model from mindspore.train.callback import LossMonitor, TimeMonitor, ModelCheckpoint, CheckpointConfig # 基础上下文 ms.set_context(modems.GRAPH_MODE, device_targetAscend) # 数据并行 梯度累积示例32卡micro batch 4累积8步全局batch1024 ms.set_auto_parallel_context( parallel_modems.ParallelMode.DATA_PARALLEL, device_num32, gradients_meanTrue, enable_parallel_optimizerTrue, ) # 模型、优化器、loss # network create_model(...) # optimizer nn.AdamWeightDecay(network.trainable_params(), learning_rateconfig.lr) # 评估相关 callback metric_cb MetricMonitor(print_per_step100, smoothing50) time_cb TimeMonitor() ckpt_config CheckpointConfig(save_checkpoint_steps1000, keep_checkpoint_max5) ckpt_cb ModelCheckpoint(prefixllm_7b, directory./ckpt, configckpt_config) # 训练入口 # model Model(network, optimizeroptimizer, amp_levelO2) # model.train(epochs, train_dataset, callbacks[metric_cb, time_cb, ckpt_cb])这份配置里有几个点需要强调。gradients_meanTrue表示梯度同步时会做平均值规约这个配置在数据并行里对训练稳定性更友好因为不同设备上 micro batch 的数据量一致时“求和”和“求平均”差别不大但求平均能避免大步长下 loss 波动。enable_parallel_optimizerTrue是把优化器状态切开存储它的收益是显存降低、通信量下降代价是实现上比完整复制复杂度更高一些如果你的模型已经稳定跑起来了这个开关可以逐步打开观察收益。ModelCheckpoint 的save_checkpoint_steps1000是经验值。正常大模型训练一天几千步每 1000 步存一个 checkpoint一天也就几个恢复成本可接受。存得太密会拖慢训练存得太稀疏又增加意外中断的恢复代价。keep_checkpoint_max5是保留最近 5 个避免磁盘堆积同时留出回退空间。4.3 从 950 tokens/s 优化到 1800 tokens/s一个案例复盘拿我之前调过的一个 7B 参数规模模型为例32 张加速卡数据并行开混合精度。初始吞吐大概在 950 tokens/s 左右怎么看都觉得不对劲。我按评估体系定位了一圈逐步推进了四个优化点第一步开 Profiler发现数据预处理耗时占了总 step 耗时的三成以上而且 map 操作在训练进程里同步执行。解决方案是调整数据管道的并行 worker 数、把 decode 和 tokenize 操作挪到独立的异步管道里。改动后数据侧耗时下降了约一半吞吐从 950 提升到 1250 tokens/s。第二步是显存侧优化。开启 BF16 混合精度后单卡显存释放了大约 1.2GB。我把释放出来的显存换成了更大的 micro batch从 4 提成到 6。这一步的吞吐提升约 12%达到 1400 tokens/s 左右。第三步是通信侧优化。打开enable_parallel_optimizerTrue之后优化器状态不再每卡完整复制一份顺带把梯度通信量也降了下来。通信耗时占比从 28% 降到 18%整体吞吐进一步提升到 1550 tokens/s 附近。第四步是通信算法微调。把梯度同步方式从全量 AllReduce 切成分段 reduz 风格reduce-scatter 加 all-gather多卡场景下的通信峰值压力降得更明显最终稳定到 1800 tokens/s 左右。整个过程的对比数据大概是这样的阶段核心动作显存变化吞吐变化通信耗时占比初始状态默认配置基线950 tokens/s28%数据管道优化异步解码 并行worker基本不变1250 tokens/s约25%开启BF16混合精度 增大micro batch单卡释放1.2GB1400 tokens/s约22%并行优化器优化器状态切分显存进一步下降1550 tokens/s18%梯度分段通信通信算法调整不变1800 tokens/s约11%这个案例最值得参考的不是最终数据而是每次改动都对应了明确的瓶颈判断。如果当时直接把 batch size 调大大概率会先显存溢出如果直接换通信算法很可能优化完发现数据管道还是最大瓶颈。5. 常见问题速查训练不收敛、NaN、性能上不去5.1 训练不收敛的三个检查方向loss 不降或降得极慢一般不是单一原因。我按出现频率排序先查这三个方向第一个方向是数据和标签对齐问题。大模型训练的数据管道长一旦出现 label shift 或者 token 错位模型学到的耦合关系是错的loss 很难正常下降。我遇到过几次“loss 卡住”的问题最后都是在数据管道里查出来的。可以用一个小样本身先过一遍有监督的 loss 减法测试比如只喂一个 batch 反复训练几十步看 loss 能否降到接近 0。如果降不下去大概率是数据配对或模型输出侧的 shape 逻辑有误。第二个方向是学习率和优化器参数设置。常见的问题是学习率过大导致在 loss landscape 上来回震荡或者预热步数太短导致初期梯度冲击过大。相对规模越大的模型学习率往往越小而 batch size 增大时学习率也必须跟着调整。一个粗略经验是batch size 翻倍时学习率可以考虑按 k 倍根号规律去调但更稳妥的做法是参考框架里已有的同类模型配置。第三个方向是并行切分导致的计算错误。张量并行时如果注意力的头切分和矩阵维度对应不上训练不会像显存溢出那样直接报错而是表现为 loss 收敛变慢或跳变。遇到这种情况可以先关掉模型并行单卡或纯数据并行下跑小规模实验看看收敛是否恢复正常。如果正常问题就在并行切分逻辑。5.2 loss 正常下降但突然变成 NaN/INF训练跑得好好的突然 loss 变成 NaN这种问题最让人崩溃。我的排查顺序是这样的数据侧先查最近几个 step 的数据样本里有没有 inf 或超大值。尤其文本模型里偶尔会有异常长的文本或特殊字符数值化后可能产生异常值。学习率侧查看 NaN 出现前学习率是否处于陡增阶段。如果刚好在 warmup 结束、学习率达到峰值的位置爆发 NaN多半是峰值设置过高。精度侧如果开了 FP16 或 BF16优先查 loss scale 是否失效。BF16 相对 FP16 会更稳但也不是绝对安全。检查梯度裁量和 loss scale 策略必要时临时切换成 FP32 跑几十步做对照实验。优化器状态侧一些动态学习率优化器或参数更新逻辑在 fp16 下优化器状态也可能因为累积误差爆掉。并行优化器开启后如果切分不均衡也可能出现局部优化器状态异常。我的实践经验是6 成以上的 NaN 问题出在数据或学习率上而不是模型代码本身。所以在盲调代码之前先用小 batch 快速定位“是否每个 batch 都 NaN”因为如果是数据里某个固定样本触发的那会在固定的 step 位置反复出现查找难度会直线下降。5.3 吞吐上不去的“先看三张图”当训练吞吐低于预期且显存也没爆、代码也没报错时我习惯让团队先开 Profiler 看三张图不要在日志堆里猜来猜去第一张是数据预处理耗时和迭代间隙曲线。如果这张图显示训练 step 之间的间隙很大、或者数据预处理算子的耗时吃掉了很多时间那问题基本在数据管道侧。常见原因是预处理没有并行化或者num_parallel_workers设置太保守又或者是输入数据没有做预取。第二张是算子执行耗时 TopN。如果 TopN 里计算密集型算子占据大头说明计算侧已经是热点应该从这里找算子融合、算子切分的空间。如果 TopN 里某些看起来很小的算子耗时异常大则有可能是并行执行时的调度开销或内存搬运太频繁。第三张是通信耗时占比和通信算子耗时。这张图在多卡场景里尤其重要。如果你发现通信耗时占比超过 25%那么不管数据管道和算子怎么优化都很难把整体吞吐拉到合理水平。优先调整通信方式、减少通信频率、或者增大计算粒度来掩盖通信延迟。这三张图的名字你可以自己定义关键是“用 Profiler 或类似工具先把事实摆出来”再动手优化。我见过太多“明明数据管道卡出天际还在那里调模型结构”的案例就是因为没有先看图。结尾做了这么多大模型训练的评估和性能优化我个人最深的体会是评估体系和性能优化其实是同一个工程问题的一体两面。没有评估优化就是盲人摸象没有优化评估就是看着一堆数字干瞪眼。我现在的习惯是任何一个训练任务开头先把评估配置写好哪怕只是最简陋的 callback 加吞吐统计也绝不直接裸跑。每次性能改动都先记录改动前后的吞吐、通信占比、显存这三类数据再判断这个改动值不值得保留。最后再分享一个小技巧建议把评估指标和优化动作做成一张简单的看板每次训练任务跑完都更新一次。很多平台团队会把这个看板做成自动化的但如果你是个人开发者用一个表格文件记录也足够了。数据攒上三四轮之后你会发现很多以前觉得玄学的训练问题其实都有规律可循。大模型训练的稳定性从来不是什么“调参玄学”而是你手里掌握了多少可信数据的问题。
返回列表