大模型团队通常只公布训练完成后的技术报告,小米 MiMo 团队则把 MiMo 2.6 的后训练过程做成了实时数据面板。面板同时展示 pro 和 flash 两个模型的训练步数、每步接受样本数、累计成本与 token 消耗,让原本只在内部实验平台流动的数据直接出现在公众视野中。
这件事的价值不只在于“公开烧了多少钱”。对工程团队来说,它展示了一种更值得借鉴的思路:把后训练看成持续运行、可以量化和审计的生产系统,而不是等待数周后才检查结果的黑盒任务。
面板上的数字应该怎样读
训练步数是最直观的进度指标,但它不能单独代表模型能力提升。不同训练阶段的 batch 大小、采样策略、任务难度和优化目标可能不同,第 1000 步到第 1100 步消耗的资源与产生的收益未必等同于第 2000 步到第 2100 步。
每步接受样本数更接近后训练流程的核心。对于带有生成、筛选或强化学习环节的任务,系统通常会先产生候选轨迹,再根据规则、奖励模型或验证器决定是否接受。接受量突然下降,可能意味着模型输出退化、验证器变严、数据分布变化,也可能只是某一批任务更难。仅凭一条曲线不能确定原因,但它能快速标记需要调查的时间窗口。
累计 token 与成本则回答两个运营问题:训练吞吐是否稳定,以及当前收益是否值得继续投入。摘要记录的某一时刻,pro 的累计成本约为 83 万美元,flash 约为 36 万美元;这些数字会随训练继续变化,因此更适合视为当时的面板快照,而不是最终训练成本。
真正有分析价值的是指标之间的关系。例如,可以进一步计算:
acceptance_rate = accepted_samples / generated_samplescost_per_accepted_sample = incremental_cost / accepted_samplestokens_per_accepted_sample = incremental_tokens / accepted_samplesstep_duration与失败率、吞吐量之间的相关性
如果成本持续上升,但接受率和离线评测长期没有改善,团队就应检查奖励设计、采样参数或停止条件,而不是只等待更多训练步数。
为什么后训练需要生产级可观测性
后训练任务经常跨越推理服务、数据队列、验证器、奖励模型、优化器和检查点存储。最终 loss 只能描述其中一小部分状态。生成服务变慢、验证器超时或者某类样本被大量拒绝,都可能让 GPU 继续运行,却没有产生相称的有效训练数据。
一个可用的实时面板至少需要覆盖四类信号:
- 进度:全局 step、阶段、检查点版本和预计完成时间。
- 数据质量:生成数、接受数、拒绝原因和任务类型分布。
- 资源效率:输入与输出 token、GPU 时间、吞吐量和单位成本。
- 训练效果:loss、奖励分布以及固定评测集上的趋势。
公开面板还带来了内部看板没有的约束。字段定义必须稳定,成本口径需要说明,断点续训不能造成累计值重复,异常数据也不能泄露训练样本、提示词、内部主机名或供应商凭据。换句话说,公开可观测性同时是数据工程与安全工程问题。
可以这样实践:建立最小后训练遥测流
下面的示例不代表小米面板的真实接口或实现,只演示团队如何用 Python 标准库生成结构化训练事件,并通过本地 HTTP 接口暴露最新状态。运行前把模拟数据替换为训练循环中的真实计数器;对外部署时还需要鉴权、限流和字段脱敏。
from __future__ import annotations
import json
import random
import threading
import time
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
STATE = {
"model": "demo-pro",
"step": 0,
"generated_samples": 0,
"accepted_samples": 0,
"tokens": 0,
"estimated_cost_usd": 0.0,
"updated_at": None,
}
LOCK = threading.Lock()
def training_loop() -> None:
while True:
generated = random.randint(32, 64)
accepted = random.randint(16, generated)
tokens = random.randint(40_000, 90_000)
with LOCK:
STATE["step"] += 1
STATE["generated_samples"] += generated
STATE["accepted_samples"] += accepted
STATE["tokens"] += tokens
# Replace this estimate with your actual billing or cluster accounting data.
STATE["estimated_cost_usd"] += tokens / 1_000_000 * 2.5
STATE["updated_at"] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
time.sleep(2)
class Handler(BaseHTTPRequestHandler):
def do_GET(self) -> None:
if self.path != "/metrics/latest":
self.send_error(404)
return
with LOCK:
payload = dict(STATE)
generated = payload["generated_samples"]
payload["acceptance_rate"] = (
payload["accepted_samples"] / generated if generated else 0.0
)
body = json.dumps(payload, ensure_ascii=False).encode("utf-8")
self.send_response(200)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
if __name__ == "__main__":
threading.Thread(target=training_loop, daemon=True).start()
print("Metrics available at http://127.0.0.1:8000/metrics/latest")
ThreadingHTTPServer(("127.0.0.1", 8000), Handler).serve_forever()
保存为 telemetry_demo.py 后,可以直接启动并查询:
python telemetry_demo.py
curl -s http://127.0.0.1:8000/metrics/latest | python -m json.tool
进入真实系统后,不应只在进程内保存累计值。更稳妥的方案是把原始事件写入消息队列或时序数据库,以 (run_id, step, attempt) 作为幂等键,再由聚合服务生成公开视图。这样即使训练任务重启,也不会重复累计 token 和费用。
公开之前要守住边界
实时公开训练状态可以建立透明度,也会暴露运行节奏、资源规模和异常窗口。面板上线前,应明确哪些数据适合公众查看,哪些数据只能留在内部。
一份实用的发布检查表包括:指标是否注明定义与更新时间;金额是实际账单还是估算值;是否区分累计值和单步值;恢复训练后能否保持幂等;样本内容与拒绝原因是否经过脱敏;外部面板是否读取隔离后的只读数据源;训练异常时是否允许延迟发布。
MiMo 2.6 的实时面板让后训练从“最终发布一个模型”变成了可以观察的过程。对其他团队而言,值得复制的未必是公开全部成本,而是先建立可靠的内部遥测体系,再根据安全边界选择性公开。曲线只有在定义清楚、能够追溯并与模型质量关联时,才真正具有工程价值。