TensorBoard 和 W&B(Wandb) 到底该怎么接进 LLMOps 平台?

8 阅读29分钟

TensorBoard 和 W&B 到底该怎么接进 LLMOps 平台?

——从“看曲线”到“管实验”,再到平台该做哪一层

适合读者:正在或准备给训练平台接入实验追踪能力的人,尤其是分不清“平台该做什么、工具该做什么”的工程师。

读完后你应该能:说清 TensorBoard 和 W&B 的本质差异、各自该以什么形态接入平台、平台还剩下哪些必须自己做的事,以及“自己搭”的边界在哪里。

全文一句话:TensorBoard 是“写文件 + 读文件的网页”,W&B 是“SDK + 有状态的服务端”。平台接入它们,不是把功能抄一遍,而是做入口和治理——命名、凭据、归属、权限、降级。平台是入口,工具是引擎。


周五晚上十点,一个 8 卡微调任务跑到第 3000 步。平台显示 run 状态是绿色的 running,一切正常;但 loss 曲线从 2.3 突然弹到 4.1,之后开始缓慢爬升。

你同时冒出三个问题:模型到底学得好不好?是哪张卡、哪台机器出的问题?三个月前那次效果很好的实验,当时用的是什么配置?

第一个问题属于数据面,后两个属于控制面。TensorBoard、W&B 和 LLMOps 平台,解决的正是这三类问题里不同的部分。这篇文章不站队“选 TensorBoard 还是 W&B”,而是想把它们放回一个更完整的坐标系:训练观测分几层,每一层该由谁负责,平台又该做到哪一步。

先说明三个前提:你至少跑过一次 PyTorch 或 TensorFlow 训练;本文只讨论训练侧追踪,推理侧的延迟、吞吐、幻觉率另开一篇;文中的平台叫 PorryLLMOps,是一个用于讲解的玩具实现,路径和数字都是示意。


一、先立坐标:两本账,三层能力

在讨论工具之前,先把“观测”拆成两本账。

第一本,控制面账本。 它记录“这次运行本身发生了什么”:谁提交了任务,调度到哪台机器,哪个 worker 掉线了,重试了几次,最后是 succeeded 还是 failed。主角是运行(Run),关心生命周期和资源状态。

第二本,数据面账本。 它记录“模型到底学得怎么样”:loss、学习率、梯度范数、吞吐、显存,以及权重分布、图像这些更细的信号。主角是指标(Metric),关心训练质量和收敛趋势。

这两本账不能互相背书。

“run 状态是 succeeded”,只说明进程正常退出了,不代表 loss 收敛。

“loss 在下降”,也只说明模型在学,不代表进程没掉线、没重试、没悄悄换了数据。

这里有一个特别容易混淆的点:很多平台里有一张 Event 表,名字看着像 TensorBoard 的 event file,其实完全无关。它是平台的控制面流水:谁在什么时候从 pending 变成 scheduling,哪个 worker 掉线了。TensorBoard 的 events.out.tfevents.* 则是数据面的指标流水。两者只是都叫“event”而已。

把两本账分开之后,训练观测可以分成三层:

  1. 看见:把 loss 曲线、权重分布、计算图画出来。
  2. 管住:把几十上百次实验组织起来,能对比、能筛选、能复现、能控制权限。
  3. 串起来:把一次实验和它的数据版本、代码版本、模型版本、部署环境、回滚策略关联起来。

TensorBoard 主要解决第一层,W&B 把第一层和第二层做厚,LLMOps 平台则要把三层串成一条生命周期。


二、本质:数据流方向相反

这两个工具解决的是同一个问题:训练跑起来之后,我怎么知道它学得好不好。 但它们的做法完全相反。

TensorBoard:把曲线写成文件,再找个人来播放

训练代码里加一行:

writer.add_scalar("loss", 2.31, step=100)

它不发网络请求,而是往一个目录里追加一个二进制文件:

events.out.tfevents.1720000000.hostname.12345.0

这个目录叫 logdir。然后你起一个服务:

tensorboard --logdir=/ckpt/run-0001/a1/tfevents

浏览器打开就能看到曲线。

所以 TensorBoard 的本质是:一个文件格式 + 一个读文件的网页。 数据始终在存储上,谁有目录权限谁就能看。它最初是 TensorFlow 的配套工具,现在跟框架已经解耦,PyTorch 用 torch.utils.tensorboard.SummaryWriter 写的也是同一套格式。

  • 好处:简单、免费、离线可用、数据在自己手里,几乎成了训练生态的标配。
  • 坏处:多人共享要自己搭服务,没有账号和权限体系;几十个实验放一起对比很别扭;文件只增不减,时间长了加载慢。

W&B:训练进程主动上报到服务端

同样是记一个点:

wandb.log({"loss": 2.31}, step=100)

SDK 把指标、配置、系统信息打包,通过 HTTP 发到服务端,网页上实时出图。除了曲线,它还管实验对比、超参搜索、产物版本、可分享的报告。

它围绕一次 run 建立了一个比较完整的数据模型:指标和配置;GPU/CPU 利用率;这次 run 对应的代码 commit;checkpoint、数据集等产物的版本和血缘;以及 project、entity、group、job_type、tags 这些组织方式。

这几样东西绑在一起,才让“复现一次实验”从一句口号变成可操作的事情。

  • 好处:开箱即用,多人协作和对比做得好,不用自己运维。
  • 坏处:依赖网络和第三方服务,数据出了自己的边界,合规敏感场景过不去;免费额度有限,长期用要付费;自托管是另一套运维成本。

一张表看清区别

TensorBoardW&B
数据去哪写到本地/共享存储的目录上传到服务端(SaaS 或自建)
要不要网络写的时候不要,看的时候要能访问目录训练和看都要
谁运维你自己起服务厂商,或你自己自托管
强项免费、离线、数据可控、生态标配实验对比、协作、超参搜索、报告
弱项多租户、权限、跨实验对比合规、断网、成本
一句话行车记录仪存 U 盘,谁拿到谁能回放行车记录仪实时传云端,云端帮你剪辑对比

一个类比要说清失真的地方:TensorBoard 也能读 S3/HDFS 上的远程目录,W&B 也能私有化部署;两者都不只存曲线,也存配置和产物。

为什么 TensorBoard 不自己做“管理”

很多人会问:为什么 TensorBoard 不能像 W&B 一样,按项目组织实验、按人分配权限、把几十次 run 放在一张表里对比?

这不是“做不到”,而是设计目标和出身决定了它不该做。

TensorBoard 的核心假设是“一个开发者,在自己的机器上,看着自己的训练跑”。它的数据模型非常朴素:训练脚本往本地目录写文件,它读这个目录画图。它没有“用户”“项目”“团队”这些概念,因为它的设计里根本不需要。

W&B 的出发点,就是解决 TensorBoard 用户“只能看、不能管”的不满。代码版本、系统指标、checkpoint 版本化、跨机器集中管理、团队共享——这些都需要一个有状态的服务端来存元数据、做权限、做聚合。TensorBoard 的本地文件模型承载不了这些;硬加进去,会把它从“轻量可视化工具”变成“重型实验管理平台”,偏离了它的定位。

所以答案是:TensorBoard 不是不具备基础能力,而是它的架构和定位决定了它不该承担那些职责。 W&B 填补的是“从个人可视化到团队实验管理”这个断层。

一句话收束:TensorBoard 是“够用的起点”,W&B 是“省掉造轮子的中间层”。


三、为什么“看上去不难”的东西要直接用现成的

你的直觉是对的:核心逻辑确实不难。写个脚本收集 loss、存个 JSON、画个图,一天就能做出来。但 TensorBoard 和 W&B 的价值不在核心逻辑,而在核心逻辑之外的那些东西。

你自己搭,第一天会做出能用的版本。接下来会遇到这些:

  • 多机多卡时日志怎么汇总? 8 台机器各写本地文件,你要么写集中收集服务,要么手动 scp。W&B 是每个进程直接推到服务端,打开一个页面就看到全部。
  • 怎么快速对比 50 次实验? 自建的话,大概率是打开 50 个文件夹看 50 张图。W&B 提供表格视图,按超参数筛选、排序、分组。这背后的数据模型设计和前端交互,工作量远超“画个图”。
  • checkpoint 怎么和那次实验的代码版本、超参数绑定? 自建得设计一套元数据方案。W&B 的 Artifacts 直接把 checkpoint 和产生它的 run、代码版本、配置绑定,形成 lineage。
  • 团队 10 个人怎么共享、怎么控制谁能看谁不能看? 这是权限系统和多租户架构,不是脚本能解决的。
  • 断网了、跑在保密内网怎么办? TensorBoard 天然离线,这是它的优势。W&B 需要私有部署,但至少提供了这个选项。

所以根本原因是:你“自己搭”的成本不在第一天,而在第 100 天。 当你有了几十次实验、多个团队成员、多台训练机、需要回溯三个月前某次实验的配置时,自建脚本的维护成本会指数级上升。

TensorBoard 和 W&B 卖的不是“记录日志”这个功能,而是围绕日志的一整套工程化能力:存储、索引、查询、权限、协作、可复现性。

当然,这并不意味着所有团队都该买 W&B。如果只有三个人、几十次实验、都在一个内网里,TensorBoard 加共享存储完全够用。工具的选择应该由实验量和协作复杂度决定,而不是由“别人都在用”决定。

一句话收束:小团队用 TensorBoard 是理性选择;实验一多、协作一复杂,才轮到 W&B 这类平台出场。


四、它们和 LLMOps 平台是什么关系

LLMOps 训推平台是一个把训练和推理全流程管起来的系统。它的核心组件包括数据管道、模型管道、API 层、工作流编排、部署与服务、监控,以及重训练/回滚策略。监控是其中不可或缺的一块,而 TensorBoard / W&B 做的正是“训练过程监控与实验追踪”。

具体关系可以分三层看。

第一,它们是平台“监控与追踪”能力的常见实现。

平台需要记录每次运行(Run)的指标、配置、产物,以便回答“这次训练为什么效果不好”“上周那个实验的配置是什么”。TensorBoard 和 W&B 就是实现这个能力的现成工具。很多平台会直接集成它们,或者提供类似的底层能力,比如用 MLflow 做追踪。

第二,它们属于“训练侧”的观测,不覆盖“推理侧”。

LLMOps 的监控范围更广。平台不仅要看训练 loss,还要看推理服务的延迟、吞吐、GPU 利用率、成本,以及模型输出的质量指标,比如幻觉率、人工反馈。TensorBoard / W&B 主要服务训练阶段的实验追踪,推理侧的监控通常由平台自己的可观测性体系承担,比如 Prometheus + Grafana,或者专门的 LLM 可观测性工具。

第三,它们是“零件”,平台是“整机”。

一个团队可以只用 W&B 管实验、用脚本手动部署模型,也能跑通,但协作和自动化程度有限。LLMOps 平台的价值在于把实验追踪、模型注册、部署、监控、回滚串成一条自动化流水线。TensorBoard / W&B 在这个流水线里扮演“训练过程记录器”,平台负责让这些记录和上下游环节(数据版本、模型版本、部署环境)关联起来。


五、拆开看:指标系统和指标入口,是两层东西

这是接入时最容易混的地方。把“实验对比”这个模块拆成两层,才能分清“平台该做什么”和“买来的引擎该做什么”。

指标系统回答的是:数据存哪、按什么索引查、怎么聚合、怎么画。核心资产是存储、索引、查询、渲染、保留策略。典型代表是 TensorBoard、W&B、MLflow、Prometheus/TSDB。

指标入口回答的是:这次 Run 写到哪个 project、用哪把钥匙、谁能看、从平台哪里点进去。核心资产是映射关系、凭据、权限、链接、命名规范。典型代表是平台的提交表单、Run 详情页、跳转链接。

指标系统负责“数据怎么活”,指标入口负责“人怎么找到它”。

平台需要“实验对比能力”没错,但实现上通常是:入口层自己做,引擎层外购或开源。平台做命名规范、密钥、归属、权限、triage 联动;W&B 做存储、曲线、对比、报告。

有一个中间地带要提前说清:入口做厚了会变成“半个系统”。比如平台为了做告警,定时从 W&B API 拉 loss、学习率、吞吐,存进自己的时序库做规则判断——这已经在读侧建了一个薄的聚合层。但它仍然不是指标系统:写入和主存储还在 W&B,平台只镜像“告警需要的少量指标”。这个边界要主动划,否则会不知不觉自研一套。


六、TensorBoard 以什么形态接入平台

TensorBoard 虽然常被说成“本地工具”,但它本身就是一个轻量级的 Web 服务进程。平台上接入,不是让用户在自己电脑上跑,而是把 TensorBoard 进程作为训练任务的“附属服务”或“旁路容器”跑起来。

从实践看,主要有两种形态。

形态一:训练任务内嵌“一键开启”的附属服务。

以百度百舸的公开实践为例,用户在创建训练任务时,可以在高级配置里勾选“开启 TensorBoard”。平台自动做几件事:为这个任务创建 TensorBoard 服务实例、分配访问入口(Ingress 或反向代理)、把日志读取路径指向训练代码写日志的目录。训练跑起来后,用户从任务页面点“TensorBoard”就能跳过去。这种形态下,TensorBoard 对用户透明,平台替你管进程和路由。

形态二:由独立的“监控服务”拉起 TensorBoard 子进程。

平台里有一个 MonitoringService 基类,TensorBoard 是其中一种后端实现。当实验启动时,服务在空闲端口上以子进程方式拉起 TensorBoard,并把各任务的日志目录软链接到一个统一的 runs/ 目录下让它读。这种形态更适合平台控制生命周期,也方便后续替换成其他后端。

核心要点:TensorBoard 的接入不依赖“云”,它依赖的是——平台能否把日志目录暴露给一个 TensorBoard 进程,并把这个进程的 Web 端口暴露给用户。日志目录可以挂载共享存储(S3/OBS 挂载),端口可以通过 Ingress 或反向代理暴露。

一个值得抄的细节:有的平台把 TensorBoard 实例默认保留 24 小时,被回收后下次查询时自动重建。这是平台侧的资源管理策略,不是 TensorBoard 本身的能力。

一句话收束:平台接 TensorBoard,接的是“一个进程 + 一个目录 + 一个端口”。


七、W&B 以什么形态接入平台

先做一个关键区分:TensorBoard 是“日志格式 + 可视化进程”,W&B 是“SDK + 服务端”。两者的接入路径不同,但可以统一在平台的“训练观测”模块下。

接入前的前置决策:SaaS 还是自托管

如果平台面向内网,或者对数据出域有要求,需要走自托管。

路径 A:接 W&B SaaS,最快。

平台只需要在训练容器里注入 WANDB_API_KEY 环境变量,确保用户代码调用了 wandb.init()。平台侧可以做轻量集成:在任务详情页嵌入 W&B Run 的链接,或通过 API 拉取 run 摘要展示。不需要运维任何服务端。

路径 B:自托管 W&B,适合生产平台。

W&B 自托管是一个完整的服务端栈,通过 Kubernetes Operator 部署,依赖 Kubernetes、MySQL 8.x、S3 兼容对象存储和 Redis(具体小版本以官方兼容矩阵为准)。平台需要做三件事:

  1. 部署 W&B 服务端。 平台本身在 K8s 上,用 Operator 相对省事;否则要额外准备一套 K8s 环境。
  2. 配置训练容器连接自托管实例。 设置 WANDB_BASE_URL 指向你的实例地址,而不是默认的 api.wandb.ai;同时设置 WANDB_API_KEY。自托管实例的 Key 和 SaaS 不通用,需要在自托管实例里创建。
  3. 处理证书。 如果用了自签名证书或内部 CA,需要在训练容器里设置 REQUESTS_CA_BUNDLE 指向 CA 证书路径。

同步 TensorBoard 只是过渡,不是前提

一个常见的误解是:用 W&B 之前,必须先接 TensorBoard。

不需要。 两者是独立的。W&B 提供了一个可选的桥接开关:

wandb.init(project="finetune-7b", sync_tensorboard=True)

打开之后,W&B 会监听你代码里写 TensorBoard 日志的动作,把同样的指标转发到云端。这对已有大量 TensorBoard 代码、想逐步迁移的团队很方便。但如果你从零开始用 W&B,完全不需要碰 TensorBoard,直接 wandb.log() 就行。同步功能只是给存量用户的一条平滑迁移路径,不是前置依赖。

一个务实的落地顺序

如果平台刚起步、资源有限,建议先接 TensorBoard。它没有服务端依赖,日志格式简单,一个进程加一个端口映射就能跑通,能最快让用户“看到训练曲线”。

等平台有了多实验对比、团队共享、checkpoint 版本化的真实需求后,再评估 W&B 自托管(或类似方案)的投入产出比。W&B 的工程价值在“管理”而不在“看”,没有足够的实验量和协作场景,自托管的运维成本可能大于收益。

一句话收束:平台接 W&B,接的是“SDK + 服务端 + 一套治理规则”。


八、分工:那行 add_scalar 谁写,2.31 从哪来

这是平台和训练代码之间最容易扯不清的一条边界。

先给结论:归训练脚本,也就是算法/模型侧的代码。 平台的代码里没有这行,也不该有。平台不知道你的模型输出什么、loss 怎么定义。

真实项目里通常有三种形态:

  • 算法同学手写:writer.add_scalar("train/loss", loss.item(), step),最灵活,也最容易写错。
  • 框架自动记:HuggingFace 的 TrainingArguments(report_to="tensorboard", logging_dir=...),或者 Lightning 的 TensorBoardLogger(save_dir=...),只填配置,框架每隔 logging_steps 自动记 loss、学习率。
  • 平台在基础镜像或启动模板里封装:镜像里放一个统一的 callback 或 wrapper,读取 PORRY_TF_EVENTS_PATH,自动挂 writer。算法同学什么都不用写,只要用这个镜像。

平台工程师做的四件事

不管哪种形态,Infra/平台工程师在这件事里做的是另外四件事,都不碰 add_scalar:

  1. 注入地址。 比如在 build_runtime_spec() 里注入 PORRY_TF_EVENTS_PATH。
  2. 挂存储。 把 /ckpt 或 S3 挂进容器,保证那个目录真的能写。
  3. 给模板。 在基础镜像里预装 tensorboard/wandb,或者封装自动打点的 callback。
  4. 起服务、回填链接。 多租户 TensorBoard 服务、鉴权、Run 详情页放链接。

所以,“平台工程师要不要在训练脚本里加这行?”——通常不要。平台工程师要做的是让这行能拿到正确的路径。如果平台连模板都不给,那这行就落到算法同学头上。

一个具体的失败现场

平台把 PORRY_TF_EVENTS_PATH 注入得很漂亮:

PORRY_TF_EVENTS_PATH=s3://porry-ckpt/finetune-7b/run-0001/a1/tfevents

但训练脚本里根本没有 writer。那个目录就是空的,TensorBoard 页面打开一片空白。平台这时只能证明“目录存在且可写”,不能证明“训练在学”。

在 PorryLLMOps 的玩具实现里,这条链路只完成了第一步:配置里有 tf_events_path,默认值是 {ckpt_path}/tfevents,启动时注入成环境变量;但没有起 TensorBoard 服务,也没有 tracker。所以平台对 TensorBoard 是**“给了地址,没给播放器”;对 W&B 是“知道它需要一把钥匙,但门还没装”**——wandb_api_key 只出现在密钥掩码名单里,没有字段、没有注入。

2.31 到底是什么

它是训练代码在某一步算出来的 loss 标量,通常是 loss.item() 的结果。不是平台给的,也不是 W&B 算的。W&B 只是把这个数原样收走。

以语言模型为例:

for step, batch in enumerate(loader):          # batch = 8 条样本
    logits = model(**batch)                    # 前向,得到每个 token 的分数
    loss = F.cross_entropy(                     # 和 label 算交叉熵
        logits.view(-1, VOCAB), batch["labels"].view(-1)
    )
    writer.add_scalar("train/loss", loss.item(), step)   # ← 2.31 在这里

loss 一开始是个张量:还在 GPU 上,带着梯度图。.item() 把它取成一个普通 Python 浮点数,再交给 TensorBoard 或 W&B。

第 100 步算出来是 2.31,第 101 步可能是 2.28,第 102 步可能反弹到 2.35。

为什么恰好是 2.31?可以用交叉熵反推:e^-2.31 ≈ 0.099,意思是模型给“正确那个 token”的概率大约 9.9%。如果模型完全瞎猜、词表是 5 万,loss 应该接近 ln(50000) ≈ 10.8。所以 2.31 说明它已经学了一段时间,或者任务本身比较简单。单看一个点没有意义,要看的是它随 step 的趋势:有没有稳定下降、有没有尖刺、有没有长时间平台期。

两个很容易踩的坑:

  • 记得 .item() 或 .detach()。 直接把带梯度的张量传进去,轻则日志变慢,重则把计算图拖住、显存上涨。
  • 横轴的 step 用优化器步,不是 micro-batch 数。 开了梯度累积(比如累积 4 步更新一次),如果按 batch 计数,曲线会被拉长 4 倍,和别人对比时横轴对不上。

一句话收束:平台管“写到哪、谁能看”,训练脚本管“记什么、这个数怎么算”。2.31 属于后者,平台不该也不可能替它算。


九、如果已经买了 W&B,平台还剩下什么

假设团队已经采购了 W&B SaaS 或 Dedicated Cloud,训练集群能出网或有代理,平台是控制面,跑在 K8s 上。这时候一个自然的问题是:是不是只接 W&B 就行了?

主流做法是以 W&B 为唯一实验追踪后端;但不是“完全不产生 TensorBoard 文件”。

准确地说,有三种角色:

  • W&B = system of record:曲线、对比、报告、Artifact 都以它为准,平台详情页只放 W&B 链接。
  • TensorBoard = 兼容/降级格式:很多开源脚本和 HuggingFace Trainer 默认写 TensorBoard;W&B 故障或断网时,TensorBoard 文件还在本地;历史实验也大量是 TensorBoard 格式。所以常见做法是“主 W&B,副 TensorBoard”,而不是把 TensorBoard 删干净。
  • 平台 = 入口与治理:即使只买 W&B,下面这些仍然得平台自己做——project/entity 命名规范、service account 与密钥轮换、平台 run_id + attempt 到 W&B run id 的映射、团队权限对齐、离线模式、成本控制、triage 联动。

所以结论是:可以只接 W&B,但不建议把 wandb.init() 硬编码到十个地方。 至少收敛到一个 tracker 函数或模块,成本很低,换来的是换供应商、私有化、合规降级时不用改主链路。

目标架构

┌────────────────────────────────────────────────────┐
│ 控制面:平台 API / UI                                │
│  submit 表单:tracking 配置(project/entity/tags)    │
│  Run 详情页:W&B 链接、配置快照、attempt、事件流       │
│  tracker 模块:生成 run id、注入 env、回填 URL        │
└───────────────┬────────────────────────────────────┘
                │ env + secret + 挂载
┌───────────────▼────────────────────────────────────┐
│ 训练容器                                            │
│  rank0: wandb.init(...) → wandb.log(...)            │
│  所有 rank: 可选 SummaryWriter 写 TB(降级/兼容)     │
└───────┬────────────────────────────┬───────────────┘
        │ HTTPS 上报                  │ tfevents 文件
┌───────▼──────────────┐   ┌─────────▼──────────────┐
│ W&B SaaS / 自建       │   │ 共享存储 / 对象存储      │
│ 曲线、对比、Artifact  │   │ 降级读取、历史归档       │
└───────┬──────────────┘   └─────────┬──────────────┘
        │ API(可选)                  │
┌───────▼────────────────────────────▼──────────────┐
│ 平台:链接回填 / 薄采集器 / 告警 / triage 联动       │
└───────────────────────────────────────────────────┘

分层职责

命名与归属,平台负责。 project 按团队或任务划分,run id 按 run.id + attempt 生成,group 按逻辑 Run 聚合,job_type 区分 train/eval。

凭据,平台和 W&B 共同负责。 service account、短期 token、掩码、轮换、审计。密钥不能进配置快照,配置里只存 secret://wandb/service-account 这样的引用,运行时从 Vault 或 K8s Secret 解析;出口仍然要掩码。

注入,平台负责。 WANDB_PROJECT、WANDB_ENTITY、WANDB_RUN_ID、WANDB_RESUME、WANDB_MODE、WANDB_DIR。

上报,训练代码或框架负责。 wandb.init / wandb.log,或者 HuggingFace Trainer 的 report_to="wandb"。

存储与渲染,W&B 负责。 曲线、对比、报告、Artifact。

降级,平台和共享存储负责。 TensorBoard 文件、offline 模式、后续 sync。

告警与 triage,平台负责,优先级放在 P3。 薄采集器 + 规则 + 事件流关联。

六个核心设计决策

第一,run id 必须带 attempt。

WANDB_RUN_ID = f"{run.id}-a{run.attempt}"
WANDB_RESUME = "allow"

否则重试会覆盖旧曲线,或者和上一次的 step 冲突。

第二,多卡只让 rank 0 初始化。 否则 N 张卡会建 N 个 W&B run。需要区分时用 group=run.id、job_type,不要用多个 run。

第三,密钥不进配置快照。 配置里只存引用,运行时解析;日志脱敏。

第四,W&B 为主,TensorBoard 保留为降级。 tf_events_path 字段继续保留,作为 W&B 故障、断网、历史脚本的兜底;平台 UI 默认只展示 W&B 链接,TensorBoard 折叠为“备用入口”。

第五,checkpoint 默认不进 W&B Artifact。 Artifact 适合“关键模型版本/数据集版本”这种少量、需要血缘的对象;全量 checkpoint 放自己的对象存储,W&B 只存引用和元数据,否则存储成本会失控。

第六,平台只镜像告警需要的少量指标。 采集器只拉 loss、学习率、梯度范数、吞吐这类关键 scalar,不把全部曲线抄进平台库。

一个窄接口

平台侧保留一个窄接口,即使当前只有 W&B 一个实现:

class Tracker(Protocol):
    name: str

    def prepare(self, run: Run, cfg: Mapping) -> TrackingContext: ...
    def link(self, ctx: TrackingContext) -> str | None: ...


@dataclass(frozen=True)
class TrackingContext:
    backend: str            # "wandb" | "tensorboard"
    external_id: str        # W&B run id 或 TB logdir
    url: str | None
    env: dict[str, str]

Run 上存 backend、external_id、project、entity、url、attempt 这几个字段。CLI 和详情页只读它,不直接拼 W&B 的 URL。自托管时换 base URL 即可。

落到 PorryLLMOps

改动其实集中在五处:配置层把 wandb_api_key 从掩码名单升级成 secret 引用,并在运行时注入 W&B 环境变量;Run 增加 tracking 元数据;事件流增加 run.tracking.ready,payload 带上跳转 URL;CLI 的 status 打印链接、submit 解析 tracking 配置;新增 observability/tracker.py。调度器不用动。

一句话收束:W&B 是指标引擎,平台是指标入口和治理层。选了 W&B 不等于把平台变成 W&B 的壳。


十、落地阶段与验收

P0,约一周。 建 W&B team/project 规范;service account;镜像预装 SDK;注入最小环境变量;跑通一次训练。验收标准是平台提交后,W&B 能看到 run 和曲线。风险是命名混乱、key 权限过大。

P1,一到两周。 Run 存 tracking 元数据;CLI/详情页回填链接;密钥引用与掩码;审计。验收标准是用户从平台一键跳到自己的 run,越权看不到。风险是 URL 拼接错、secret 泄露。

P2,两到三周。 多卡 rank0-only;重试 attempt 映射;offline 模式与 sync;团队权限对齐。验收标准是 8 卡只产生 1 个 run,重试曲线不串,断网后能补传。风险是多进程重复 init、step 冲突。

P3,一到两个月。 薄采集器;告警规则;triage 联动;成本与保留策略。验收标准是 loss 平台期能关联到平台事件流。风险是采集延迟、API 限流。

P4,可选。 关键模型版本进 W&B Artifact;与模型 registry 对接。验收标准是能从模型版本反查训练 run。风险是存储成本、血缘断链。

上线前必须检查的八件事

  1. 多进程重复 init。 torchrun 起 8 个进程,每个都 wandb.init() 就是 8 个 run。只让 rank 0 初始化,其余 rank 用 barrier 或直接跳过。
  2. step 必须单调递增。 resume 时如果从 0 重新开始,W&B 会报错或覆盖。要读回 global_step 接着记。
  3. offline 模式忘记 sync。 数据留在容器本地,容器一删就没了。退出前 wandb sync,或者把 WANDB_DIR 挂到持久存储。
  4. run id 冲突。 WANDB_RUN_ID 在 project 内唯一,只用 run-0001 会在重试时撞车,必须带 attempt。
  5. key 泄露。 环境变量会被 ps、崩溃日志、子进程看到。用 secret 文件挂载或短期 token,日志脱敏。
  6. 成本失控。 W&B 按席位和用量计费,Artifact 存储另算。全量 checkpoint 进 Artifact 是最常见的账单爆炸原因。
  7. 网络抖动阻塞训练。 W&B 默认会重试,但长时间断网可能拖慢训练。设置合理的 timeout,或直接 offline。
  8. SDK 与 server 版本不匹配。 自托管时两者要匹配,升级前先看兼容矩阵。

十一、五个常见误区

误区一:用了 W&B,就不需要 TensorBoard 了。

W&B 可以是唯一的 system of record,但 TensorBoard 仍然适合作为兼容格式和降级方案。很多开源脚本默认写 TensorBoard;断网时,本地文件是最后的证据。

误区二:平台接了 TensorBoard,就等于有了训练观测。

接入 TensorBoard 只是“给了播放器”。如果训练脚本没有 writer,页面就是空的;如果目录不可写,日志就是断的。入口不等于内容。

误区三:loss 是平台算的。

loss 由训练代码定义和计算,平台只负责传递、展示和关联。平台不知道你的模型输出什么,也不该替算法做这个决定。

误区四:run 状态是 succeeded,就说明模型训好了。

这是把控制面账本当成了数据面账本。进程正常退出,和模型收敛,是两件事。

误区五:自建一套观测很简单。

第一天确实简单。第 100 天,当你要汇总 8 台机器、对比 50 次实验、追溯三个月前的配置、控制 10 个人的权限时,才知道自己真正需要的是什么。


十二、一页速查

只想知道“模型学没学”:用 TensorBoard。一个 SummaryWriter,一个 --logdir,一个浏览器页面,足够。

想对比几十次实验、做团队协作:用 W&B。它把 run、config、系统指标、代码版本、Artifact 组织在一起,重点是“管住”。

想串起数据、训练、模型、部署、回滚:用 LLMOps 平台。它不一定自己实现指标引擎,但必须提供命名、凭据、权限、链接、告警和生命周期关联。

平台接入顺序:先接 TensorBoard,成本最低,最快让用户看到曲线;等实验量和协作需求上来,再接 W&B 或类似方案。

代码分工:训练脚本负责“记什么、这个数怎么算”;平台负责“写到哪、谁能看、怎么找回来”。

已经买了 W&B:平台仍然要做 project/run 命名、service account、密钥轮换、run + attempt 映射、权限对齐、离线降级和成本控制。

最容易踩的坑:rank0 才 init;run id 带 attempt;step 单调递增;offline 记得 sync;日志前先 .item();全量 checkpoint 别塞 Artifact。


十三、全文总结

第一层:本质。

TensorBoard 是“文件格式 + 读文件的网页”,数据留在存储上;W&B 是“SDK + 服务端”,数据主动上报。前者赢在免费、离线、可控,后者赢在对比、协作、管理。

第二层:边界。

TensorBoard 不做团队管理,是设计目标决定的,不是能力缺失。W&B 填补的是“从个人可视化到团队实验管理”这个断层。而“自己搭”的成本不在第一天,在第 100 天——多机汇总、实验对比、血缘绑定、权限体系,都不是脚本能解决的。

第三层:分工。

指标系统负责“数据怎么活”,指标入口负责“人怎么找到它”。平台自己做入口和治理(命名、凭据、归属、权限、降级),引擎层外购或开源(TensorBoard / W&B)。即使只买 W&B,平台这一层也省不掉——换供应商、断网降级、合规私有化,都要靠它兜住。

最核心的三句话:

  1. TensorBoard 是够用的起点,W&B 是省掉造轮子的中间层,LLMOps 平台是把这些能力嵌进完整生命周期的那一层。
  2. 平台接 TensorBoard,接的是“一个进程 + 一个目录 + 一个端口”;平台接 W&B,接的是“SDK + 服务端 + 一套治理规则”。
  3. 选了 W&B 不等于把平台变成 W&B 的壳。 平台真正的价值在入口和治理——命名、密钥、归属(run + attempt)、权限、triage 联动,这些引擎不会替你做。

毕竟,训练跑起来之后,真正难的不是画出一条曲线,而是回答:这条曲线属于谁,为什么是这样,以及下一次怎么做得更好。