首页 / 资讯中心 / 文章详情

TensorBoard深度学习训练可视化:损失曲线、直方图与超参数实验

TensorBoard深度学习训练可视化:损失曲线、直方图与超参数实验 ★ FEATURED ARTICLE
Day41写到这一篇的时候我刚好连续调了十天模型。真正让我烦的其实不是模型本身而是训练过程中的状态几乎全靠控制台滚屏在猜。损失值在 3 和 2.5 之间反复横跳验证集精度时上时下到底哪一步开始过拟合只能靠肉眼盯着一堆 print 的输出脑补。直到我把 TensorBoard 接进训练循环把标量曲线、权重直方图、样本输出和超参数实验结果全部分门别类铺在浏览器里很多之前只能靠经验判断的问题终于变成了可以直接回看的证据。它到底解决了什么问题简单说TensorBoard 早就不再是某个框架专属的玩具了现在基本成了实验日志的标准格式。PyTorch 里直接有内置的SummaryWriter你只需要在训练脚本里加几行代码把想记录的指标写进事件文件然后另起一个 TensorBoard 服务做前端展示。Loss、准确率、学习率、梯度分布、模型结构、输入图像、特征向量、超参数组合这些琐碎的训练状态会被统一收进同一个页面以交互式图表的方式呈现。适合谁很明确正在跑实验、需要回答“这次改动到底有没有效果”的人。尤其是长期做消融实验、需要对比十几个运行记录的用它能把“我的模型为什么不收敛”从玄学变成看得见的曲线。1. 为什么是 TensorBoard实验追踪的真正痛点1.1 没有可视化时你其实是在靠感觉判断模型好坏很多人跑训练的习惯是开一个终端不断 print loss 和 accuracy盯到深夜。不是说 print 没用而是当实验规模变大以后这种姿势有非常现实的问题。第一信息不连续。你看到的是某个时刻的数值但前后两秒之间发生了什么完全看不出。比如 loss 突然从 0.8 掉到 0.4你以为模型起飞了结果把完整曲线调出来才发现前面 500 步它一直在平台期只是最后几步爬下来了。平台期持续了多久、反弹过几次、是不是每 50 步规律性上升print 根本看不出来。第二没法对比。你改了个学习率重跑一遍想让新旧两条 loss 曲线重叠在一起看差异。print 做不到只能靠“我记得上次好像跑得更好”这种模糊记忆。对比几个实验时这种记忆基本不可靠尤其是改动不只有一个变量的时候你根本分不清是学习率起的作用还是数据顺序起的作用。第三无法追踪中间状态。模型训练时不只有 loss 一个指标还有权重分布、梯度范数、激活值。如果这些量只在某个阶段出现异常你会完全错过。我遇到过很多次梯度消失其实从第 200 步就开始了打印终端上却直到第 2000 步才出现 loss 异常。TensorBoard 的存在就是为了把这三个痛点一次性处理掉。它把训练过程变成一个有时间轴的、可回放的、可多实验叠加查看的记录系统而不是一串被终端吞掉然后又滚走的文本。1.2 一个页面上能同时回答的几类问题我用 TensorBoard核心其实是在回答五类问题。第一类曲线问题。训练和验证的 loss 分别在什么变化精度什么时候见顶学习率调度和指标变化之间有没有时间上的对应关系。第二类分布问题。权重、梯度的分布随训练怎么演化有没有越变越窄或突然跳变这是判断梯度消失、梯度爆炸、权重退化的重要线索。第三类结构问题。模型各层之间怎么连接输入张量形状有没有在每个节点如实变化很多延迟到前向传播时才能发现的问题提前看图就能定位。第四类是中间结果问题。图像模型的输入增强长什么样、生成器输出的样本在变好还是变坏、分割结果的掩膜和原图是否对齐这些不是数值而是视觉内容就得靠图像面板来看。第五类是实验对比问题。不同超参数组合下哪个配置能在同样轮次里拿到更好的指标需要一张能横向比较的表而不是翻十个 txt 配置文件手动归纳。这几类信息在 TensorBoard 里分别对应标量面板、直方图与分布图、计算图、图像/文本面板、超参数面板和嵌入投影仪。不用全部精通但每个面板都是为解决一类具体观察需求而存在。刚开始用先把标量和直方图吃透收益就很大。1.3 别被名字误导它早已不是某个框架专属的组件很多从 PyTorch 入门的人一开始会绕开 TensorBoard觉得名字里带个 Tensor 字样应该是另一个框架的配套工具。实际完全不是这样。TensorBoard 读的是目录下的事件文件谁去写这个文件并不重要。PyTorch 官方直接内置了一个torch.utils.tensorboard.SummaryWriter把 PyTorch 的各类张量数据转成 TensorBoard 能读的事件格式。所以你在 PyTorch 项目里用 TensorBoard不需要额外引入第三方接入层只需要确保tensorboard这个 Python 包本身是安装好的。这也是我推荐它作为实验日志方案的原因。它足够底层、足够通用几乎成了实验记录格式的标准。哪怕以后你换框架、换别人的代码只要对方生成的事件文件是标准格式你依然能用同一个浏览器页面把历史实验调出来不需要为每个项目重写一套可视化逻辑。这一点对长期维护实验档案的人非常关键。2. 三分钟跑通第一个 TensorBoard 仪表盘2.1 安装与最小监控代码先安装依赖。如果你用的是常规深度学习环境直接执行pip install tensorboard如果你用 PyTorchSummaryWriter已经内置在torch.utils.tensorboard里不需要单独装第三方库。接下来是最小的使用流程from torch.utils.tensorboard import SummaryWriter writer SummaryWriter(log_dirruns/mnist_exp01) for step in range(101): train_loss 1.0 / (step 1) writer.add_scalar(loss/train, train_loss, step) writer.close()运行完这条脚本会在runs/mnist_exp01目录下生成一个类似events.out.tfevents.xxx的文件。之后在终端启动tensorboard --logdirruns浏览器打开http://localhost:6006就能看到一条单调下降的曲线。整个流程不超过三分钟。第一次跑通以后你自然会想原来记录一个标量这么简单那为什么不把所有能记录的东西都记下来2.2 日志目录设计的底层逻辑这里有个特别关键的概念要先想明白TensorBoard 的对比维度基本由目录结构决定。--logdir指向的目录下每个子目录会被当成一个独立的实验运行记录官方术语里叫 run。比如runs/ ├── exp01_lr0.001/ ├── exp02_lr0.01/ └── exp03_lr0.001_bs64/启动服务时指定--logdirruns左侧边栏就会出现三个可勾选的 run。勾选任意多个所有同名指标曲线会叠加到同一张图上非常直观。所以你在设计日志目录时其实是在设计实验对比维度。把不同实验放在同一个父目录下TensorBoard 才会把它们归为一组如果你直接把--logdir指向单次实验的子目录就只能看到那一次数据。我的习惯是上层统一一个runs目录每跑一个配置就新建一个带特征命名的子目录。子目录名尽量把关键差异写进去比如lr001_bs64_wd0001这样不用打开配置文件就能从侧边栏里认出实验身份。2.3 启动服务时值得记住的几个参数最常用的启动命令就一行tensorboard --logdirruns。实际使用中我会加几个参数。tensorboard --logdirruns --port6007 --host0.0.0.0 --reload_interval5--port默认是 6006被占用时换 6007、6008 都可以。--host0.0.0.0的作用是允许外部机器访问一般用在远程服务器上配合本地端口转发使用。如果你只是本地单机调试不建议绑0.0.0.0保持默认只允许本机访问更安全。--reload_interval控制 TensorBoard 多久重新扫描一次事件文件默认大概是 5 秒。如果训练脚本还在跑你希望尽快看到新曲线把这个值调小如果训练已经结束只是静态查看可以调大减少无谓扫描。还有一点容易忽略训练过程中一定要定期执行writer.flush()训练结束前调用writer.close()。如果不 flush很多事件还会积在内存缓冲区里磁盘上的事件文件并没有更新TensorBoard 自然看不到最近的数据。进程被强杀或者没正常退出时尤其容易丢尾部数据。我现在写训练脚本习惯用with SummaryWriter(...) as writer的写法上下文管理器会在作用域结束时稳妥关闭少一个手动 close 的漏网之鱼。3. 核心面板逐个拆解标量、直方图与计算图3.1 标量曲线怎么记录决定你能不能顺利完成实验对比标量面板是大多数人最先接触也是使用频率最高的部分。add_scalar的用法很简单标签、数值、步数。但标签怎么起直接决定后期对比是否顺畅。我的命名规则是“大类/小类”训练损失写成loss/train验证损失写成loss/val训练精度写成acc/train验证精度写成acc/val。这样在标量面板左侧的标签列表里loss和acc会各自折叠成组层级清晰。相反如果全部平铺成train_loss、val_loss、train_acc、val_acc标签一多列表就乱成一锅粥。写代码时的习惯是训练循环里每个 step 记录一次loss/train但是验证集的指标不要也在每个 step 记录。验证集通常是每个 epoch 算一次所以loss/val的横坐标是 epoch 序号和loss/train的 step 横坐标不在一个量纲上。更稳的写法是记录时把横轴统一换算成 step或者保证你在对比时能意识到两者时间基准不同。否则两条曲线落差巨大你会误以为验证损失出了异常其实只是横轴粒度不同。还有一个被低估的功能是面板上的平滑调节。TensorBoard 标量图右下角有一个 smoothing 滑块调大可以让曲线更平滑去掉高频抖动。建议至少调一个中档因为原始 loss 曲线噪声往往很大会在趋势之外制造大量干扰。但也不是越大越好平滑过度会掩盖阶段性的反弹和突变调参时我一般用 0.6 左右看趋势关掉平滑看细节。3.2 直方图与分布图给权重和梯度做体检标量只能看到最终数值但模型内部状态是否健康得靠直方图和分布图来看。面板对应的 API 是add_histogram它把不同时间点的张量取值分布记录下来然后在页面里按时间轴叠加展示。比较典型的用法是监控权重和梯度。比如每 100 步往hist/weight里写一次当前模型某一层权重的统计每 500 步写一次hist/grad的梯度统计。这样在训练后期打开直方图面板能很清楚地看到分布宽度是否在收缩。如果梯度直方图逐渐变成一条又细又窄的竖线基本可以判断梯度已经趋近于零模型陷入梯度消失。如果某个时刻分布突然出现离群尖峰则可能发生了梯度爆炸或者学习率过大导致参数跳变。我实际踩过的坑是直方图记录太频繁日志文件膨胀得非常快。训练一个中等规模的 CV 模型如果每个 step 都记录所有可学习参数的直方图几分钟就能产生几个 G 的事件文件TensorBoard 读起来也会卡顿。后来我改成“每隔 N 步记录一次”或者“不同层错开记录”日志体积立刻降下来能看到的模式信息并没有少太多。另外distributions和histograms两个面板展示的是同一份数据前者用压缩百分位折线画后者用分桶直方图堆叠画。两者选一个自己顺眼的就行。我平时看直方图更多一些因为能同时看到分布的形态变化。3.3 计算图看过一次模型结构更清晰但别指望它替代论文插图计算图面板对应add_graph。用法很直接writer.add_graph(model, torch.randn(1, 3, 32, 32))传进去模型和一个形状正确的示例输入它就会把前向传播过程中记录到的算子连接关系画出来。对初学者第一次看到整个网络结构以展开图的形式出现在浏览器里会有一种“原来我的模型长这样”的直观感受。对排查复杂模型也很有帮助比如输入张量在哪个节点形状变化分支结构是否按预期拼接都能在图上核对。但说实话我不建议把所有精力都放在计算图上。它展示的是算子与数据流的关系和论文里的那种网络结构示意图不在一个抽象层。深度学习框架优化过的计算图往往节点非常多层层展开之后可读性并不好。我更常把它当作辅助工具当模型结构比较复杂、怀疑某个分支没按预期接上时用计算图扫一眼确认平时训练过程监控重心还是放在标量、直方图和图像面板上。4. 可视化中间结果图像、文本与嵌入投影仪4.1 用 add_image 检查模型真正看到的输入在很多视觉任务里只看 loss 曲线完全不够。数据增强有没有做过头、采样出的 batch 是否类别失衡、GAN 生成的样本质量有没有在逐步改善这些信息必须用眼睛看而不是看数字。add_image可以把张量、PIL 图像或 numpy 数组写进事件文件。最常用的进阶用法是torchvision.utils.make_grid把一批图片拼成网格后一次记录from torchvision.utils import make_grid import torch grid make_grid(batch_tensor, nrow8) writer.add_image(train/batch, grid, global_stepepoch)这里有个容易踩的坑add_image默认期望 CHW 格式即通道在前。如果你直接传入一张 HWC 的 numpy 数组比如形状是(224, 224, 3)画面颜色就会错乱。要么提前转成 CHW要么在调用时显式指定参数dataformatsHWC。第一次用这个功能的人十有八九会在颜色上栽一跤。图像面板另外一个实用场景是记录预测结果。比如语义分割模型训练时每个 epoch 存一版原图、真实掩膜和预测掩膜三张并排的网格图。这样你在查看曲线趋势的同时可以直接翻看某一轮模型在典型样本上的实际表现。很多早期过拟合就是从这个面板上先发现的曲线还没什么明显异常但预测图已经出现了边缘噪声。4.2 用 add_text 记下实验里没法量化的临时结论文本面板往往被忽视但其实很好用。训练过程中你会有大量非结构化判断“从第 300 步开始验证集精度停滞怀疑过拟合”“这个配置下模型在样本 A 上表现很差下次要检查预处理”“学习率 0.01 组合似乎比 0.001 更快到达平台期”。这些话写在实验笔记本上很容易丢写在聊天工具里更不可能和训练数据绑定。add_text可以让你把这些备注直接写进当前 run 的事件文件之后打开 TensorBoard 的文本面板就能看到writer.add_text(note/exp01, 验证损失从第 300 步开始回升怀疑过拟合下一组实验加大权重衰减, step300)除了临时备注我还会把关键配置以文本形式记录进去比如数据集的组成、数据预处理方法、是否冻结了某几层。这样即使过了一个月我打开某个 run 的文本面板也能立刻知道这个实验当时是怎么搭的不用再去翻代码提交记录和命令行历史。这个习惯在长期项目里极其重要。4.3 嵌入投影仪把特征空间摊开看最后一个高级面板是嵌入投影仪。它对应add_embedding作用是把你喂进去的高维特征向量投影到二维或三维空间里用点图展示数据在特征空间中的聚类关系。典型场景是用训练好的模型把测试集的图像编码成特征向量再按类别着色观察不同类别的特征是否分离得足够清楚。用法大概是writer.add_embedding( features, # [N, D] 张量每个样本一行 metadatalabels, # 每个样本对应的标签或名称 label_imgimages, # 可选投影点对应的原始图像 global_step0, )实践下来有两点必须提醒。第一输入样本量别贪多。投影仪要维护一个可交互的 3D 点云几千个点已经会让浏览器变得吃力几万个点基本就是灾难。一般每类挑几十到几百个样本足够观察聚类形态。第二特征向量通常维度很高直接扔进去TensorBoard 会先用 PCA 或 t-SNE 降维。如果原始特征没有做过归一化不同维度的量纲差距过大降维结果就可能被少数几个极端维度主导。更稳的做法是先用 L2 归一化或者让特征提取最后一层输出维度适中再做投影。嵌入投影仪并不能替代严谨的聚类分析但它是一个很好的探针。我发现模型特征没有在空间上按类别区分开时去查 loss 和梯度往往已经能佐证“模型能力不足”但能直观看到空间里点与点之间的区域交叠对定位问题出在特征层还是分类头还是有独特价值的。5. 用超参数仪表盘把调参过程系统化5.1 从“手动比较”到 HParams 面板前面提到的实验对比主要靠左侧栏勾选多个 run 来叠加曲线。但是当你需要同时比较几组超参数配置时更顺手的是超参数面板也就是 HParams。对应 API 是add_hparams。示例逻辑如下from torch.utils.tensorboard import SummaryWriter writer SummaryWriter(runs/hparam_search) for lr in [0.001, 0.01, 0.1]: for batch_size in [32, 64]: # 这里替换成真正的训练返回验证精度 acc run_training(lrlr, batch_sizebatch_size) writer.add_hparams( {lr: lr, bs: batch_size}, # 实验超参数 {hparam/val_acc: acc}, # 实验指标 run_nameflr{lr}_bs{batch_size} # 该组实验名称 )打开 TensorBoard 后点击 HParams 标签页你会看到一张表格每一行是一组超参数组合同时列出对应的指标。还能用平行坐标图筛选参数范围选定几组配置做高亮比较。5.2 实验命名和指标选择的经验这里有一个使用习惯问题很多人把add_hparams和add_scalar混着写结果发现 HParams 面板里行数很多却很难读懂。原因通常是run_name起得太随便或者指标字典里塞了太多不需要比较的度量。我的做法是每一次完整训练对应一组 4 到 8 个关键超参数包括学习率、批量大小、权重衰减、优化器、是否换用某个模块等指标精简到 2 到 3 个核心值比如验证精度和训练耗时。不用把所有东西都放进去面板是用来做“配置级别”的横向比较的细节指标继续用标量面板看。另一个容易踩的坑是add_hparams里如果出现不可哈希的类型比如把optimizer对象直接塞进去会导致记录失败或者 HParams 面板解析异常。要用字符串或数值描述配置不要直接把对象传进去。这是文本领域经常忽略但在实际编程中比较常见的小错误。5.3 平行坐标图的读法当超参数数量超过两个以后传统两两对比的散点图就不够直观了。HParams 面板默认提供的平行坐标图把每个超参数作为一根垂直轴每组实验画成一条折线穿过各个参数轴上它对应的取值并在最后一根轴上落在结果指标上。你可以通过框选某个参数轴上的一段范围筛选出满足条件的实验观察它们最终指标的分布。读平行坐标图有个技巧先不看具体参数只看终点轴上的高分区域被哪些折线穿过然后反推这些折线在哪些参数轴上形成了相近的颜色带。如果所有高精度折线都在某根参数轴同一个取值附近集中那大概率这个参数是关键敏感项如果高精度折线分散在各处说明当前参数空间里这个指标受该参数影响不大。我靠这套方法做过一次学习率的敏感度分析十分钟就筛出了一个后续所有实验通用的量级区间。当然HParams 面板也有局限。它非常依赖你已经定义好比较的指标如果实验指标在训练中途发生变化旧实验中这个指标历史值就没有了。所以比较合理的流程是先在小规模 epoch 上快速跑多组配置用 HParams 横向筛选出有潜力的组合再对少数候选做完整训练进入标量曲线精细对比。两阶段各干各的效率最高。6. 常见问题与排查手法6.1 “No dashboards are active”先查路径再查进程这是初学者遇到最多的问题。打开页面一片空白提示 No dashboards are active。先说排查顺序九成是--logdir指向的目录不对。确认一下你使用 TensorBoard 启动服务时的工作目录--logdirruns是相对路径如果你的 shell 当前目录和训练脚本目录不在同一个位置自然读不到事件文件。换成绝对路径立刻解决。第二类原因是你以为写了数据但事件文件没有刷到磁盘。训练脚本还开着或者事件文件还在缓冲区里。解决办法是训练进程保持运行等待--reload_interval刷新如果训练已经结束那基本就是没调用writer.flush()或writer.close()。第三类是目录里有事件文件但文件名不对。TensorBoard 只识别events.out.tfevents.*这种命名规格的文件。如果你自己手写了别的文件名放进去它不会读。不要手动改事件文件名会造成识别失败。6.2 端口占用与远程访问问题启动报错说端口被占用最常见的原因是上一个 TensorBoard 进程没退出。先用命令行查进程kill -9 $(lsof -t -i:6006)这条命令会强制结束占用 6006 端口的进程。如果还想保留旧端口也可以直接换--port6007。我个人习惯固定用 6007因为分享代码到团队里时统一端口能减少沟通成本。远程服务器使用有两点提醒第一--host0.0.0.0后任何能访问你服务器 IP 的人都能打开训练曲线页面这不是一个带鉴权的服务公网场景要小心。第二如果你本地只是临时查看最好用 SSH 端口转发把远端 TensorBoard 服务转发到本地浏览器而不要直接对公网开放。6.3 曲线诡异跳动、直方图卡死、投影仪崩溃的常见原因曲线突然来回跳可能不是模型问题而是多个进程往同一个 log_dir 目录写数据。两台训练脚本共享同一个日志目录就会互相覆盖事件文件坐标数据和标签完全错乱。解决方法是严谨的目录隔离每个 run 独立子目录不要跨进程共享。直方图面板加载卡顿基本可以锁定为事件文件过大。我去翻过最常见的原因有人把每个 step 的 batch 数据都写进了add_histogram日志文件快速增长到好几 G。TensorBoard 前端需要解析这些分桶数据体量一大渲染自然拖垮。解决办法是降采样间隔 50 到 200 步记录一次或者干脆只记录代表性的网络层不要全模型所有张量都塞进去。嵌入投影仪崩溃或者白屏通常是样本量太大或者浏览器 GPU 加速问题。先降低add_embedding的样本量再把浏览器硬件加速关掉试试。如果数据量必须很大不要依赖投影仪直接在训练脚本里用 sklearn 的 PCA 降维导出 CSV 或散点图会比 TensorBoard 投影仪更从容。6.4 我自己用得最顺的一套日志工作流最后分享一套我长期维护实验项目时固定下来的工作流。所有实验日志统一放在runs/目录下目录命名格式是日期_实验代号_关键参数摘要比如0527_swin_t_lr0005_bs32。每次启动训练前在脚本开头用time.strftime拼一个不冲突的子目录名并建议顺手记录代码版本。如果用的是版本控制工具可以把当前提交哈希也写进add_text这样以后看到某个异常曲线能精确回到当时那份代码。训练过程中标量曲线和文本笔记必然记录直方图按每 100 步采样图像按每个 epoch 记录一组验证集预测图HParams 只在小规模粗跑阶段使用。训练结束后确认服务能正常读出数据再关训练脚本顺手清理掉中间不再使用的临时日志目录避免事件文件越积越多。这套工作流不复杂但能保证三个月后再打开某个日志目录依旧能还原当时发生过什么。对我来说TensorBoard 的价值不是那几条漂亮的曲线而是它让整个实验过程变得可追溯、可复现、可向别人解释。长期做模型训练这种“有据可查”的踏实感比任何花哨的可视化都重要。我个人用下来的体会是工具选型不需要追新TensorBoard 虽然历史久但在“本地起服务、读取通用事件文件、覆盖训练全流程监控”这件事上依旧是最省心、最稳的方案。如果你也在做长期的训练项目哪怕之前没用过今天照着第 2 节那个最小示例写一段把第一次实验的标量曲线留下来等跑完几十轮再回头打开你就会理解为什么我说这一步值得早做。
阅读完成 · 觉得有帮助?
咨询建站