动手学深度学习24-50集合:从线性回归到DenseNet实战笔记
发布时间:2026/9/19 0:50:38 锦皓数字建站

把《动手学深度学习》的 24 到 50 集合完整刷过一遍之后我最大的感受是这一段才真正决定你后面能不能独立看论文、改模型。前面二十几集讲的是环境和预备知识很多人靠照着敲一遍就能混过去但从线性回归从零实现开始到 softmax 回归、多层感知机、正则化、卷积神经网络、LeNet 一直到 DenseNet内容密度陡然上升光靠眼睛看视频会非常容易产生我懂了的错觉。这篇笔记就是围绕这一段的学习路径展开的我会把自己反复回看、动手复现、调参踩坑的过程整理出来重点放在那些视频里一带而过、但实操中一定会卡住的地方。不管你是刚配好环境的新手还是已经能跑通几个 demo 想补基础的人应该都能从里面找到可以直接抄作业的部分。1. 2450 集合在整条学习路线里处于什么位置1.1 这段内容的边界从手写线性回归到 DenseNet先把范围说清楚避免大家找错方向。24 集合附近是从线性回归的从零实现切入的紧接着是简洁实现、softmax 回归、损失函数、多层感知机、模型选择与欠拟合过拟合、权重衰减、Dropout、数值稳定性与参数初始化、Kaggle 房价预测、反向传播、层和块、参数管理、自定义层、读写文件这一串深度学习计算模块相关的内容再往后就是卷积神经网络的基础篇——从全连接到卷积、图像卷积、填充和步幅、多输入多输出通道、池化层然后是 LeNet、AlexNet、VGG、NiN、GoogLeNet、批量归一化、ResNet、DenseNet 这一整条经典网络演进线。这个边界很有意思前半段是计算原理后半段是结构设计。合在一起刚好构成深度学习的两条腿缺了任何一条后面去看 Transformer、检测分割、大模型微调都会觉得很虚。1.2 为什么这段最容易看着懂、动手废我观察过身边一起学的朋友卡住的位置高度集中。第一种是依葫芦画瓢能跑换个数据集就崩典型代表是 softmax 回归和 MLP——视频里的 Fashion-MNIST 跑得漂漂亮亮自己换成手头的数据维度对不上、标签没转成整数、损失直接爆 NaN。第二种是知道原理但说不清为什么这么设计比如为什么交叉熵要配 softmax 一起实现、为什么 BN 训练和推理行为不一样、为什么 ResNet 加一条 shortcut 就能训到上百层。这些问题的共同点是它们不在代码写没写对的层面而在你脑子里有没有一张图的层面。所以我后来的做法是每学完一个网络先在纸上画一遍数据流的形状变化再合上视频自己默写一遍 forward能默写出来才算过。1.3 我给每个集合定的验收标准单纯看完是没有意义的。我给自己定了三条硬指标第一能不看书把核心模块的实现写出来哪怕是伪代码第二能解释清楚模块里每个超参数的作用和它变化时会发生什么第三能在一个不同的数据集上把它跑通哪怕只是换个小图片集做分类。三条都过了这个集合才算过。听起来严格但实际执行下来会发现真正花时间的只有第二条因为一二两条都是体力活只有解释清楚为什么需要动脑。2. 线性回归从零实现把自动求导拆开看一遍2.1 数据迭代器与参数初始化里藏着的两个细节从零实现这个环节很多人是直接复制代码的但里面有两处细节值得停下来想。第一处是数据迭代器它的作用是打乱索引再按 batch 切分。为什么必须打乱因为如果数据是按类别排好序的每个 batch 里就全是同一类样本梯度方向会剧烈震荡收敛会慢得离谱。这一点在后面的 softmax 回归上体现得尤其明显我自己试过不打乱直接喂前几个 epoch 的损失几乎不下降。第二处是参数初始化。线性回归里用正态分布初始化权重、偏置置零就够了看起来像是随便写的。但这其实是后面所有初始化讨论的起点——如果权重全初始化为零会怎样答案是同一层的所有神经元梯度完全相同网络对称性无法打破等于白搭一层。这个坑在学到多层感知机的时候会以一种更隐蔽的方式重现。2.2 自己写 SGD 之后才明白优化器做了什么从零实现的 SGD 核心就几行def sgd(params, lr, batch_size): with torch.no_grad(): for p in params: p - lr * p.grad / batch_size p.grad.zero_()我第一次写的时候漏掉了batch_size这个除法结果损失曲线一路往上飞。原因是框架里计算的损失默认是对 batch 求了均值的如果你在更新时再按每个样本一次更新的直觉去理解就会把学习率等效放大 batch_size 倍。这个除法本质上是在做平均梯度和求和梯度之间的换算理解清楚这一点后面看任何优化器实现都会顺很多。另一个必须注意的是p.grad.zero_()。PyTorch 的梯度是累加的不清零就会出现梯度越积越大。这个坑的麻烦之处在于它不会立刻报错只是让结果慢慢变差非常难定位。2.3 学习率与 batch size 的联动关系线性回归这个简单场景其实非常适合用来建立对学习率的直觉。我的经验是学习率不是一个独立参数它和 batch size 是联动的batch size 翻倍梯度方差变小理论上可以适当放大学习率。反过来如果你把 batch size 调得很小又不降学习率训练就会变得很不稳定。实测下来在这个玩具数据集上学习率 0.03 配 batch size 10 是个很稳的起点改到 0.1 就开始震荡改到 0.001 又慢得看不出来。这类手感必须靠反复改参数才能建立起来光看视频是拿不到的。3. softmax 回归分类任务真正开始的地方3.1 回归和分类在损失函数上的分水岭从线性回归跨到 softmax 回归很多人以为只是把输出从 1 个数变成 n 个数其实真正的分水岭在损失函数。回归用平方损失分类必须换成交叉熵。原因不是惯例而是数学上的平方损失配上 softmax 之后梯度里会带一项 softmax 的导数当预测严重错误时比如真实类别概率接近 0梯度反而会趋近于零也就是错得越离谱、学得越慢。交叉熵就没有这个问题它和 softmax 组合后的梯度恰好是预测概率减去真实标签错得越狠梯度越大。我强烈建议你把这个推导自己写一遍写完之后再看torch.nn.CrossEntropyLoss就不会再困惑为什么它和softmax经常被放在一起讨论。3.2 交叉熵里的数值稳定性不是炫技如果你去看底层实现会发现交叉熵内部不是先算 softmax 再取对数而是用了 log-sum-exp 的技巧先减去最大值。这不是为了炫技而是实打实地防止溢出指数函数增长极快logits 稍微大一点exp就会变成inf之后整个 loss 变成 NaN反向传播全线崩溃。我自己的处理习惯是只要涉及概率和指数的运算一律走框架提供的稳定实现不去手写log(softmax(x))。手写版本在数据分布正常的时候看不出问题一旦遇到异常样本就会炸而且炸得毫无规律非常难查。3.3 损失下降但准确率不动先查这三件事这是我在 softmax 回归阶段反复遇到的现象loss 在降但 accuracy 卡在一个和随机猜差不多的水平。排查下来无非三类原因。第一标签没有转成从 0 开始的整数索引框架按 one-hot 或者字符串处理等于一直在拿错答案训练。第二类别极度不均衡模型学到了全猜多数类这个局部最优loss 确实降了但准确率上不去这时候要看每一类的召回率而不是整体准确率。第三学习率过大导致在最优解附近来回跳表现为 loss 震荡、accuracy 忽高忽低。我建议在这个阶段就把看混淆矩阵变成习惯。它比单一准确率信息量大得多而且能立刻暴露标签错位这种低级问题。4. 多层感知机非线性究竟加在哪个位置4.1 隐藏层宽度比深度更值得先调多层感知机最容易让人产生疑问的地方是为什么加一个隐藏层再套一个激活函数表达能力就上来了直观理解是单层线性模型只能画一条或一个超平面做分隔而加上非线性激活后多个神经元的输出可以组合出任意形状的分段线性边界。隐藏层单元越多能拼出的折线越多。实操上我建议先在宽度上做文章而不是堆深度。在 Fashion-MNIST 这类任务上单个隐藏层从 64 个单元加到 256 个单元带来的收益往往比从 1 层加到 3 层更明显而且训练更稳、更好调。深层网络真正的价值要到后面卷积网络和残差结构才体现出来。4.2 激活函数的选择清单这一段涉及好几个激活函数的对比我整理了一份自己常用的对照表激活函数表达式我一般用在注意点ReLUmax(0, x)默认首选负半轴梯度为零可能死亡LeakyReLUmax(ax, x), a 取 0.01 左右出现大量死亡神经元时多一个需要调的超参数Sigmoid1/(1e^-x)二分类输出层容易饱和深层网络里慎用Tanh(e^x-e^-x)/(e^xe^-x)早期网络、序列建模同样会饱和一个反直觉的结论现代网络里隐藏层基本不再用 sigmoid 和 tanh不是因为它们在数学上不好而是因为它们在绝对值较大的区间导数趋近于零梯度传几层就消失了。ReLU 之所以能胜出很大程度是因为它在正半轴导数恒为 1梯度能顺畅地往回传。4.3 从 MLP 开始出现的过拟合线性回归阶段几乎不会遇到过拟合因为模型太简单了。一旦上了 MLP尤其是隐藏层宽到 256、512 的时候训练集准确率很快冲到 99% 以上但验证集卡在 88% 左右不动这就是典型的过拟合信号。我当时的直觉是再多训一会儿应该会更好结果训练集准确率继续涨到 99.9%验证集反而掉了。这个教训很重要训练集上的表现不能作为模型好坏的依据必须始终盯验证集。后面要讲的权重衰减、Dropout本质上都是围绕这一点展开的手段。5. 模型选择、欠拟合与过拟合的判定方法5.1 训练误差和验证误差的正确读法判断模型状态本质上看两条曲线的相对位置。训练误差高、验证误差也高说明模型容量不够是欠拟合该加层、加宽或者去掉过强的正则。训练误差低、验证误差明显更高说明模型记住了训练集是过拟合该加数据、加正则或者降低模型容量。这里有个容易被忽略的细节训练误差通常是在一个 epoch 结束后、参数已经更新过的情况下算的而验证误差用的是当前参数去评估。严格来说两者不在同一时刻的状态上。想在论文里做严谨对比最好在验证前后保持一致的口径否则几条曲线之间的差距会被高估。5.2 K 折交叉验证什么时候值得用K 折交叉验证把数据切成 K 份轮流做验证最终取平均。它的好处是每个样本都参与过训练和验证评估结果更稳。但代价是训练 K 倍时间。我的实际做法是小数据集比如几千张图以内、或者需要对比几个结构差异不大的模型时用 5 折交叉验证数据量上万之后直接划固定的验证集就够了因为这时候验证集本身已经足够大随机波动可以忽略。很多人不管什么场景都上 K 折其实是把算力浪费在了不需要的地方。5.3 一份可以照着做的诊断流程踩过几次坑之后我固定下来一套排查顺序写在这里供参考。第一步先确保训练误差能降到很低如果连训练集都拟合不了任何正则化手段都是白搭问题一定在模型容量、学习率或者数据本身。第二步训练误差低了之后看验证误差和它的差距判断过拟合程度。第三步根据程度选择手段轻微过拟合优先加数据增强和 Dropout严重过拟合考虑加权重衰减同时降低模型宽度。第四步每次只改一个变量记录曲线。这个流程听起来很朴素但它能避免最常见的错误——一看到验证集效果不好就同时改学习率、改网络结构、加正则最后根本不知道是哪一步起了作用。6. 正则化实战权重衰减与 Dropout 的取舍6.1 权重衰减的 lambda 该怎么给初值权重衰减就是在损失里加上参数平方和乘以一个系数 lambda。它的几何解释很直观在最小化原始损失的同时让参数尽量小等价于把解往原点方向拉。关于 lambda 的初值我的经验是从 1e-4 到 1e-2 这个区间去找具体看参数量和数据量的比值。参数多、数据少的时候可以给大一点。一个很实用的信号是看参数范数的变化如果训练过程中权重范数一直在涨说明衰减力度不够如果范数迅速掉到接近零说明力度太大模型被压得学不动了。需要提醒的是偏置项一般不做权重衰减因为它不参与特征权重的复杂度控制。有些框架默认对所有参数施加衰减需要显式排除偏置和归一化层的参数。6.2 Dropout 为什么训练和推理行为不一致Dropout 在训练时随机把一部分神经元置零推理时全部启用。很多人第一次看到这里会困惑那输出尺度不就对不上了吗答案在于实现上采用了倒置丢弃——训练时把保留下来的神经元输出除以保留概率这样训练和推理时的期望输出是一致的推理阶段就不需要再做任何缩放。我在这个阶段犯过一个错把 Dropout 加在了输出层前面。结果就是模型怎么训都欠拟合因为最后一层输入被随机丢弃输出极不稳定。正确的做法是把 Dropout 放在隐藏层之间通常紧跟在激活函数之后丢弃率在 0.2 到 0.5 之间。输入层的丢弃率要更低0.1 到 0.2 就够太高会丢掉原始信息。6.3 两者同时用会不会互相打架先说结论可以一起用而且实践中很常见但要控制好力度。它们作用机制不同权重衰减是在损失层面约束参数规模Dropout 是在结构层面引入噪声、迫使网络不依赖某几个特定神经元。我一般的组合是中等强度的权重衰减配中等的 Dropout比如 lambda 取 5e-4丢弃率 0.3。如果两个都给得很猛模型会严重欠拟合训练误差和验证误差都很高这时候要做的反而是同时减弱两者。判断依据还是那条老规矩——训练误差能不能降下去。7. 数值稳定性与参数初始化NaN 出现的真正原因7.1 梯度爆炸与消失的直观来源一个 n 层的网络反向传播时梯度是各层雅可比矩阵连乘的结果。如果每层矩阵的平均尺度略小于 1连乘 20 层之后就接近于零这就是梯度消失略大于 1连乘之后就爆炸。这和复利是同一个道理看起来每层只差一点点累积起来是天壤之别。表现在现象上梯度消失会让浅层参数几乎不更新网络退化成只有后面几层在学梯度爆炸则直接让参数飞到数值上限loss 变成 inf 或 NaN。这两种情况在深层网络里非常普遍也是后面 BN 和残差连接要解决的核心问题之一。7.2 Xavier 初始化的推导直觉Xavier 初始化的思路是让每一层的输入和输出方差保持一致。推导下来均匀分布的边界取正比于 sqrt(6/(fan_in fan_out))其中 fan_in 和 fan_out 分别是该层的输入和输出维度。理解这个公式的关键是fan_in 和 fan_out 都要考虑。只考虑 fan_in 是前向传播的要求只考虑 fan_out 是反向传播的要求两个一起考虑是折中。这也解释了为什么在 ReLU 网络上Xavier 往往不是最优——ReLU 把负半轴全部置零相当于输出方差减半所以针对 ReLU 的初始化常被称作 Kaiming 或 He 初始化会把尺度放大到 sqrt(2/fan_in) 附近来补偿。实操建议PyTorch 里nn.Linear默认用的就是 Kaiming 均匀初始化多数情况下不用手动改。但如果你自己拼了一个不含激活的层或者自定义层一定要记得显式初始化否则可能沿用框架的默认值而埋下隐患。7.3 梯度裁剪与 dtype 的实际影响梯度裁剪是把梯度的范数限制在一个阈值内超过就按比例缩小。它是应对梯度爆炸最直接的手段一行代码就能加上在序列建模里几乎是标配。注意它和权重衰减的性质完全不同裁剪是救火衰减是预防。另外一个容易被忽略的因素是数值精度。混合精度训练虽然省显存、提速但小梯度在低精度下可能直接下溢成零表现为某些层永远学不动。我在早期尝试时遇到过这种情况排查了很久才发现是精度问题。稳妥的做法是在混合精度下配合梯度缩放或者干脆在关键实验里用完整精度跑一遍做对照。8. 深度学习计算模块层、块、参数与自定义8.1 nn.Module 的组织方式和嵌套逻辑层和块这一小节在整本书里被低估了。它的核心思想是一个块可以接收一批输入经过内部计算后产生一批输出而这个块内部又可以包含若干子块。整个模型就是一棵块的嵌套树。理解这一点后面看任何复杂模型的源码都会豁然开朗。比如一个残差块内部是两个卷积 BN 激活再加一条旁路GoogLeNet 的一个 Inception 块内部是四条并行分支再拼接它们本质上都是这个嵌套结构的不同排列。我在这个阶段养成了一个习惯拿到一个新模型先用print(net)把结构打出来然后再逐个去看每个块里的 forward比上来就读源码效率高得多。8.2 参数访问、共享与初始化参数管理部分有几个必须掌握的操作。访问某一层的权重可以用索引加属性名的方式获取整个模型的参数字典用state_dict()这个在保存和加载时是主角。参数初始化可以直接对某一层的权重调用初始化函数也可以对整个模型遍历所有子模块后统一处理。参数共享是个不起眼但很实用的技巧。做法是把两个层指向同一个参数对象这样前向传播时它们共享同一组权重反向传播时梯度会累加。它在实际项目里的应用比想象中多比如孪生网络、某些注意力结构里的权重绑定。一个常见的坑是对模型做整体初始化时如果直接遍历子模块并调用初始化方法会把归一化层的权重也重置掉破坏训练好的统计量。稳妥的做法是在初始化时按模块类型做判断只处理需要处理的层。8.3 自定义层与模型保存的常见写法自定义层有两种写法一种是把若干现有层打包成一个新块另一种是从头写一个没有参数的层比如把一个固定的数学运算封装进去。前者用来做结构复用后者用来做特征变换。关于保存我建议一律保存state_dict而不是整个模型对象。原因是保存整个对象依赖类的定义路径换目录或者改类名之后加载会直接失败而state_dict只是一堆张量加载时只要重新创建同样结构的模型再把参数灌进去就行稳定性好太多。9. 卷积神经网络基础填充、步幅、通道与池化9.1 为什么图像不能直接展平前几章的网络处理的是向量而图像是三维张量。最直接的想法是把它拉平成一维扔进多层感知机但这样做有两个致命问题。第一参数量爆炸一张 224x224 的彩色图拉平之后是十几万维接一个 1024 的隐藏层就是上亿参数。第二展平破坏了空间结构相邻像素之间的关系被彻底打乱模型只能从头去学这种关系。卷积的两个核心特性正好解决这两点局部连接让每个输出只依赖输入的一小块区域权值共享让同一个卷积核在整张图上滑动。前者大幅减少参数后者带来了平移等变性——物体在图中稍微移动卷积的输出也会相应移动而不是完全变样。这也是为什么卷积网络能在图像任务上碾压全连接网络。9.2 输出形状的计算与常见算错点输出尺寸的公式是输出 floor((输入 填充之和 - 卷积核) / 步幅) 1。填充之和是上下或左右各自填充数量的总和不是单边的数量。我在这上面至少错过三次原因都是把单边填充当成了总填充。实际调试时的经验是手算一遍然后跑一次print(x.shape)对照对不上就回头检查公式里每一项的单位。这个习惯能省掉大量猜测时间。填充本身是为了让输出尺寸不缩得太快、让边缘像素被多次参与计算。步幅则是用来主动降采样的步幅为 2 时输出尺寸大约减半。很多经典网络的降采样就是靠步幅为 2 的卷积或者池化完成的。9.3 1x1 卷积与池化层各自解决什么问题1x1 卷积乍看毫无意义——核只有一个像素感受野是 1不就是在每个位置上乘个系数吗但它的真正作用是在通道维度上做线性组合。当输入有 256 个通道时一个 1x1 卷积把它压到 64 个通道参数量只有 256x64却能显著降低后续大卷积核的计算量。GoogLeNet 里的降维分支和 NiN 里用 1x1 代替全连接层都是这个思路。池化层的作用则更直白在保留主要信息的前提下压缩空间尺寸。最大池化取局部区域的响应最强的那个位置平均池化取平均。池化没有可学习参数这一点经常被初学者忽略——print一遍会发现池化层里什么参数都没有。也正因为如此它对位置的小幅移动不敏感提供了一定程度的平移不变性。10. 经典网络演进从 LeNet 到 DenseNet 的设计思路10.1 AlexNet 与 VGG堆叠带来的规模效应LeNet 是最早的卷积网络雏形结构很朴素两个卷积块加三个全连接层用的还是 sigmoid 和平均池化。它的价值在于证明了卷积 池化 全连接这套组合是可行的但受限于当时的算力和数据规模很小。AlexNet 的思路很直接把同样的结构放大用 ReLU 替换 sigmoid 解决梯度饱和用 Dropout 抑制过拟合用数据增强扩充样本第一层卷积核开到 11x11 让感受野足够大。它在当年大幅刷新了图像分类成绩也第一次让大规模卷积网络成为主流。VGG 的贡献则在于结构上的规范化。它不再纠结单个卷积核开多大而是统一用 3x3 卷积去堆叠用多个小卷积核替代一个大卷积核。这样做的理由是两个 3x3 卷积的感受野和一个 5x5 相当但参数更少、非线性更多。这个小核堆叠的思想影响非常深远后面很多网络都沿用了这个基础模块。10.2 NiN 与 GoogLeNet中间层的两次创新NiN 的两点创新都很有启发。一是用 1x1 卷积代替全连接层避免了展平带来的巨量参数二是用全局平均池化把最后的特征图直接压成类别数彻底去掉了全连接。后者不仅省参数还天然降低了过拟合风险。GoogLeNet 走的是另一条路既然不知道该用多大的卷积核那就全用上。一个 Inception 块里同时跑 1x1、3x3、5x5 卷积和 3x3 池化然后把结果在通道维度拼接。为了让计算量可控每个分支前面都加了 1x1 卷积做降维。它还引入了辅助分类器在中间层也接一个小的分类头让梯度能更直接地传到浅层。这两个网络给我的最大启发是结构设计没有唯一答案关键是想清楚每一步要解决什么问题——是降参数量、还是增加感受野多样性、还是改善梯度回传。10.3 批量归一化与残差连接解决的根本问题批量归一化放在卷积之后、激活之前对每个通道在小批量维度上做标准化。它在训练时用当前批次的均值和方差推理时用训练过程中累计的滑动平均。这一点必须记牢因为它解释了为什么训练和推理结果会有细微差别也解释了为什么小批量训练时 BN 效果会变差。残差连接解决的是退化问题。理论上网络越深表达力越强但实验发现把 20 层加到 56 层后训练误差反而变大了——不是过拟合是优化变难了。残差块把输出写成 f(x) x这样一来网络只需要学习相对于恒等映射的残差最差情况下的优化目标就变成了恒等映射梯度也能通过那条 shortcut 直接回传。ResNet 能做到上百层就是这个道理。10.4 DenseNet 把特征复用推到了极致DenseNet 的思路比 ResNet 更进一步ResNet 是跨层相加DenseNet 是跨层在通道维度拼接。每一层的输入都是前面所有层输出的拼接每一层的输出也都传给后面所有层。这样做的好处是特征复用充分、梯度路径短而且用更少的参数就能达到相近的精度。代价是显存占用高因为每一层的中间特征都得留着给后面用。实际实现时通常会在每个稠密块之间加一个过渡层用 1x1 卷积压缩通道、用池化减半尺寸控制住增量。11. 环境配置、数据集与复现踩坑记录11.1 环境版本对齐的实际做法这一段内容涉及的代码对版本比较敏感尤其是框架主版本升级后一些接口的行为会发生细微变化。我的做法是在项目里固定一个依赖清单把所有库的版本号写死然后配套记录运行环境的信息。这样做的好处是几个月后回来看能一键还原出当时的环境。具体到实践中我一般会单独建一个虚拟环境避免和系统里其他项目冲突。框架版本尽量选稳定发布版而不是最新的开发版因为不少教程的代码是针对稳定版写的。如果遇到某个函数签名对不上第一件事是去查官方文档的版本说明而不是上网随便找一段代码替换。11.2 数据集下载慢与显存不够的应对数据集下载是个高频痛点。常见图像数据集动辄几个 G网络状况不好的时候能卡半天。我一般会先把数据下载到本地缓存目录之后所有实验都从这里读避免重复下载。如果实在下不动可以先用小尺寸的替代数据集把流程跑通确认代码逻辑没问题再换回完整数据。显存不够则是另一个常见情况。应对手段按优先级排先减小批量大小这个最有效再考虑把输入分辨率适当降低然后是减少模型宽度实在不行才上梯度累积用时间换显存。需要注意的是批量大小会影响到批量归一化的统计量质量改小之后如果精度掉了可能是因为 BN 而不是模型本身的问题。11.3 高频报错对照表我把这一段学习里反复遇到的报错整理成了表格方便快速定位现象常见原因处理方向loss 变成 NaN 或 inf学习率过大、指数运算溢出、输入未归一化降学习率、改用稳定的损失实现、检查输入尺度维度不匹配报错通道数或批量维度顺序搞错打印张量形状逐层核对准确率长期不变标签格式错误、类别不均衡检查标签范围、看混淆矩阵训练慢且显存吃紧批量过大、未用合适的降采样调小批量、检查步幅设置验证集效果远差于训练集过拟合加数据增强、加正则、降低模型容量自定义层加载失败保存了整个模型对象改存参数字典并重建结构这张表里的每一条我几乎都亲自踩过。最想强调的是第一条和第三条因为它们不会直接报错只是安静地让结果变差特别消耗时间。最后分享一个我坚持了很久的小习惯每学完一个网络就在自己的小数据集上从头训一遍记录下损失曲线和验证准确率再和书上的结果对比。差距明显的时候不要急着改代码先想想是不是训练轮数、学习率调度、数据增强这些外围因素没对齐。很多所谓的复现不出来最后查下来都是这类细节问题而不是模型本身有多玄妙。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。