ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

深度学习与计算机视觉:从CNN原理到图像分类实战全解析

深度学习与计算机视觉:从CNN原理到图像分类实战全解析

1. 深度学习和计算机视觉的底层逻辑

1.1 为什么CNN天然适合图像数据

接触深度学习这么久,我越发觉得图像任务和卷积神经网络(CNN)之间的匹配,几乎就是天作之合。第八部分的内容,我会从最核心的底层逻辑重新梳理一遍,因为很多新同学在调模型时总是很迷茫,本质上就是没想明白“图像数据到底是什么样的结构”。

普通全连接网络处理一张图,需要把二维像素拉成一维向量。以一张 224×224 的 RGB 图为例,拉平后有 224×224×3 = 150528 个值,全连接层的第一个隐层哪怕只设1024个神经元,权重参数量就到了 1.5 亿级别,训练起来内存直接爆炸。更麻烦的是,拉平操作会彻底破坏图像的二维空间关系——两个相邻像素之间的关联性,在向量里可能被拆到相隔很远的位置,模型根本学不到“邻近像素相关”这个核心特征。

CNN 的思路完全不同,它用卷积核在图像上滑动,每次只看一小块局部区域。这个“局部连接、权重共享”的设计,使参数量直接降到全连接的千分之一甚至万分之一,同时天然保留了空间结构。用一个不算严谨但特别好懂的生活类比:你看一张人脸照片,不会一个个像素地读,而是先扫眼睛、再扫鼻子、最后组合判断——CNN模仿的就是这种分块扫描再汇总的大脑视觉策略。这种处理方式在第八讲的实践项目里,几乎贯穿了从数据预处理到模型设计的全部环节。

所以在任何图像任务开始之前,我都会先让自己想清楚一个问题:如果把图像当成普通表格数据塞进全连接模型,大概率效果会很差。只有理解了 CNN 的三个基本层次——局部感知、参数共享、层次化特征提取,后续讨论数据增强、迁移学习、模型调优才有扎实的根基。

1.2 卷积层、池化层、全连接层各自在做什么

要理解一个 CNN 模型,就得把它的三个基本构件拆开来看,每一个都有不可替代的作用。

卷积层负责提取局部特征。一个 3×3 的卷积核在图像上滑动,每次计算 3×3 邻域与核的点积加偏置,得到一个响应值。多个卷积核可以并行叠加,每个核相当于一个“特征探测器”——有的核学会检测水平边缘,有的学会检测垂直边缘,深层的核则进一步组合成眼睛、车轮这些复杂概念。核心参数有三个:卷积核大小、步长 stride、填充 padding。设置 stride=2 时输出尺寸直接减半,相当于下采样;padding 则控制边缘像素的贡献程度,一般用“same padding”保持分辨率不变。

池化层做降维和细节丢弃。最常用的是 2×2 最大池化:在 2×2 的区域里取最大值,把 224×224 变成 112×112。它的意义不仅仅是降低计算量,更重要的是引入平移不变性——物体在图像里移动几个像素,最大池化的结果基本不变,这让模型对位置抖动更鲁棒。当然,池化层对小目标的检测是不太友好的,因为每次下采样都会丢失位置细节。这个问题在第八部分的目标检测实践里暴露得特别明显,我后面会专门讲到如何处理。

全连接层放在网络尾部,它的作用是把前面提取到的“特征图谱”整合成最终的分类向量。最后一个全连接层的输出维度直接等于类别数,后面接 softmax 把输出变成概率分布。

这三层配合起来,就是一句话:卷积层负责“看”,池化层负责“记住重点扔掉次要”,全连接层负责“做决定”。我建议初学者别急着调参,先用一个简单的三层结构跑通全部流程,再逐步加深,否则代码还没跑起来,就一头扎进高级模块的坑里,只会越陷越深。

1.3 一个完整的图像识别链路:从输入张量到输出概率

很多同学在跑模型时,一张图片进来就卡住了。我记得带过一些初学者,问题出在最基本的预处理上。实际上,一张普通的照片要进模型,要经过严格的张量转换链路。

第一是尺寸统一。模型输入层是固定尺寸,比如 224×224,图片必须缩放或裁剪到这个大小。缩放过快会丢失高宽比信息,物体容易变形,简单分类任务不太敏感,但检测和分割任务就很敏感。推荐做法是先用短边缩放再中心裁剪,尽量保留原图比例。

第二是数值类型转换。图片读进来是 0-255 的 uint8,需要除以 255 变成 0-1 的浮点数,再按通道做标准化。这里的标准化不是拍脑袋的,一般用的是每个通道的均值 mean 和标准差 std。ImageNet 预训练模型的通用数值是 mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225]。标准化的逻辑是让每个通道服从近似零均值单位方差分布,加速收敛。

第三是维度变换。模型默认输入批次结构是 [batch, channel, height, width],单张图要补上 batch 维度,从 [3, 224, 224] 变成 [1, 3, 224, 224],channel 维度要提到最前面。这里最容易出错,很多框架内部张量布局是 NCHW,有一次我把 HWC 的数据直接丢进去,报错找不到问题,排查了半天才发现是形状排错。

第四是推理与后处理。前向传播得到 logits 后,softmax 得到概率,argmax 得到类别索引。要注意的是,不同框架的 softmax 实现细节略有差异,但最终效果基本一致。

我建议每个做视觉的朋友,在自己机器上把这段“图像→张量→模型→概率”的最小链路写一遍,不要用现成的 API 一把梭,这样才能真正理解中间每一步的作用。第八部分的实践项目就是从这一条链路起手的。

2. 经典网络架构的演进脉络

2.1 从 AlexNet 到 VGG 的“加深刻”

深度学习视觉真正爆发的起点,公认是 AlexNet 在 2012 年夺得 ImageNet 冠军,错误率降到 15.3%,比传统方法领先十个百分点。AlexNet 的核心贡献,其实不是网络结构多么复杂——5 个卷积层加 3 个全连接层,在今天看来很朴素——而是证明了在大规模数据上,深度网络可以自动学到层次化的特征表示,不需要人工设计特征。

VGG 的提出进一步验证了“深度”的价值。它把卷积层加到 16 到 19 层,思路完全遵循一个非常简单的设计准则:全部只用 3×3 卷积核,连续堆叠。为什么执着于 3×3 而不是 5×5 或 7×7?因为两个 3×3 感受野等于一个 5×5,三个 3×3 等于一个 7×7,但参数量只有后者的 27% 到 55%,而且中间多了非线性激活层,网络表达能力更强。这种小卷积核加深的思路,基本奠定了后来所有 CNN 结构的设计基调。

我当时自己搭了一个 VGG 风格的小型网络做实验,在只有几万张图的私人数据集上,明显感知到:深度越深模型拟合能力越强,但伴随着两个问题——参数量暴涨导致训练极慢,以及深层梯度传递容易出问题。VGG 全连接层有三个,前两层各 4096 个神经元,参数量占了整个网络的大头,这在后来的架构中被平均池化层替换,彻底解决了参数冗余的问题。

所以,学习 VGG 的要点不是抄结构,而是理解“小卷积核加深”带来的收益和代价:受益于更强大的非线性表达能力,代价是训练收敛变得困难。这也就为 ResNet 的登场做好了铺垫。

2.2 ResNet 的残差学习到底在解决什么问题

VGG 加深到一定程度后,出现了一个反直觉的现象:网络层数继续增加,训练集上的误差反而升高。这不是过拟合,因为训练误差都在上升,说明模型根本学不进去。这就是经典的“退化”问题。原因在于,深层网络的反向传播经过多层的链式连乘,梯度容易出现指数级衰减或爆炸,前面的层几乎接收不到有效的更新信号。

ResNet(残差网络)的核心改动非常巧妙:在两层卷积之间引入一条“跳跃连接”(skip connection),让输出变成 F(x) + x。这个结构用通俗的话说就是——与其让每个堆叠层去学一个全新的映射,不如让它们去学“和输入相比,还差多少”。残差部分 F(x) 在极端情况下可以趋近于零,此时网络退化为恒等映射,理论上深度再大也不会比浅层差。实际中,这种“恒等加残差”的路径,让梯度可以像抄近路一样直接流过跳跃连接,深层训练问题迎刃而解。

ResNet 的另一个伟大贡献是 bottleneck 设计:用 1×1 卷积先降维再升维,把 3×3 卷积夹在中间。以 ResNet-50 为例,一个 block 的参数量被压缩到普通两层结构的三分之一到一半,可以在同样算力下搭更深的网络。我实际在训练 ResNet-50 与 VGG-16 时,显存占用和单轮耗时都有明显下降,收敛速度也更快,在小型数据集上甚至可以用 ResNet-18 达到接近 ResNet-50 的效果。

现在的几乎所有现代视觉网络,包括后面要讲的各种检测分割模型,骨架都建立在残差结构之上。理解 ResNet 的跳连思想和 bottleneck 设计,就拿到了进入现代视觉网络的入场券。

2.3 注意力机制与视觉 Transformer 的崛起

从 2020 年开始,视觉领域最大的变量是 Transformer 从自然语言处理领域“跨界”过来,并逐渐成长到与 CNN 分庭抗礼的程度。核心思想非常直白:图像中并不是所有区域对分类判断都同等重要,注意力机制可以让模型在推理时重点关注关键区域,忽略背景干扰。

最早把注意力引入视觉的代表作是 SE-Net(Squeeze-and-Excitation Network)。它在通道维度上做门控:先对特征图做全局平均池化,得到每个通道的全局描述,经过两个全连接层生成每个通道的权重,再把权重乘回原特征图。这个设计在实际项目里收益很高,代码量非常小,却能在大多数任务上稳定涨点,我一般会在小型网络后面加上 SE 模块试试水,往往会收到意外惊喜。

后来的视觉 Transformer(ViT) 走得更激进:把图像切成固定大小的 patch(比如 16×16),每个 patch 铺平映射成一个 token 向量,加上位置编码后丢进标准的 Transformer Encoder 里做自注意力计算。自注意力层可以让任意两个位置的像素直接建立关联,感受野不再受卷积核大小限制,这对捕捉全局上下文信息很有优势。ViT 在超大数据集上训练的最终精度可以和最强 CNN 打平甚至超越,但在中小规模数据上,如果没有预训练权重,效果会明显输给 ResNet。

我自己在实际项目中的权衡经验是:数据量低于十万级别时,ResNet 系列依然是最稳妥的选择;数据量再大一些,并且算力允许,ViT 或 Swin Transformer 这类结构才更占优。这是“架构选型看数据规模”最典型的一个例子。

3. 数据工程与训练实操

3.1 数据采集、标注与质量控制

很多做算法的同学,把绝大多数时间花在网络结构和调参上,但到了第八讲这个阶段,我愈发明白:视觉项目的上限,80% 取决于数据的质量,模型结构只占很小一部分。数据采集第一原则是覆盖真实使用场景。做一个工业缺陷检测项目,必须去产线实拍不同光照、不同角度、不同批次下的真实样本,不能用实验室摆拍图糊弄,否则模型切到线上直接翻车。

标注工作也很有门道。分类任务就是打一个标签,看起来简单,实际上一万个样本的人工标注很容易疲劳出错。一定要建立“双人标注 + 抽检仲裁”的流程:两个人独立标同一批数据,计算一致性系数,不一致的交给第三人仲裁。我在一个实际项目里,发现两个标注员对“轻缺陷”类别的判定一致性只有 75%,后来重新定义了标注细则,把一致性提升到 96%,模型效果立刻上了一个台阶。

对于目标检测和分割任务,标注框的起点和长宽都有严格约定。很多标注工具导出的是 VOC 格式的 XML,坐标是左上角和右下角;而 COCO 格式是左上角加宽高,使用前必须确认自己的训练框架要哪种格式。踩过一次坑:标注坐标本来是整数像素,我直接除以了图像尺寸做归一化,结果因为整数除法加归一化双失误,训练出来全部偏了。建议所有坐标转换都保留浮点数,并且在归一化后抽样可视化检查几个样本。

数据清洗同样不能省。不需要的数据(如模糊图、重复图、含敏感信息的图)要逐类剔除。曾经有个垃圾分类项目,训练数据里混入了大量背景与物体相似的截图,导致模型明显偏向背景纹理判断。清洗之后,准确率直接从 82% 提升到 91%,可见脏数据的影响。

3.2 数据增强策略

数据增强是视觉项目里“性价比最高”的提升手段之一。它的本质是:通过对训练样本做合理变换,生成更多样化的样本,让模型学到不变性特征。比如随机水平翻转,相当于样本数量翻倍;随机旋转和裁剪让模型对位置和尺度变化更鲁棒;颜色抖动则模拟不同光照。

在实践中,增强策略不能盲目堆叠。有一次我把随机擦除、mixup、cutmix、颜色抖动全部一起开,模型在训练集上收敛很慢,测试精度也没有提升,反而因为增强过度导致模型学不到真实特征。合理的姿态是:先做小幅增强(翻转 + 随机裁剪 + 轻微颜色抖动),确认模型没明显欠拟合后,再逐步加入更激进的策略,每加一项就在验证集上做一次对比。

另外一个关键细节是:增强操作必须在训练集和测试集上区分对待。测试集只能做 resize 和归一化,绝不能做随机翻转或裁剪,否则推理结果不稳定,指标也会失真。我发现一些新手经常把训练和数据增强函数用在验证集上,导致评估出来的准确率低得离谱,白折腾半天。

针对检测分割任务,数据增强要更谨慎。几何变换(旋转、裁剪)会影响框和掩码的坐标,必须同步变换标注信息,否则训练就直接崩了。主流框架自带的增强管线像 mosaic、mixup 都是把图像和标注绑在一起做变换的,自己写增强时一定要保证这一点。

3.3 训练配置与调参记录

训练视觉模型,最核心的参数就是学习率、批次大小、训练轮数和优化器选择。我常用的配置是:优化器选 AdamW 或 SGD + Momentum。对于中小型数据集,AdamW 收敛省心,但最终精度往往不如精心调学习率的 SGD。一个比较稳妥的实践是:先用 AdamW 快速跑通流程确认数据没问题,再用 SGD + momentum(0.9) + weight_decay(1e-4) 做最终精调。

学习率调度方案,我推荐余弦退火或 warmup + 余弦退火。warmup 的意思是前几个 epoch 学习率从很小线性升到设定值,避免训练初期因为学习率太大导致梯度震荡。一个典型的配置是:基础学习率 0.01(batch size 256,SGD),warmup 5 个 epoch,之后按余弦曲线降到底。如果显存不够调小 batch size,学习率也要相应缩放。经验上,batch size 减半,学习率大约减半,这背后的原理是梯度估计的噪声变了。

训练轮数的确定,看验证集上的指标变化。我会在训练目录里写一个简单的自动保存逻辑:每个 epoch 都计算验证集准确率,只有比历史最优更高时才保存新的权重文件。这样即使跑了一百轮,最终拿到的也是验证集排名第一的模型,不用手动去回忆哪个 epoch 的效果最好。这个习惯帮我省下过大量重复训练时间。

还有一项容易被忽略的配置:多卡训练时的批次分配和随机种子。如果用了多卡同步批归一化,效果与单卡会有差异;固定随机种子可以保证实验可复现,否则调参时不同训练结果之间没法公平比较。在实际项目中,我都是把随机种子固定好,每次改一个变量,实验表简单清晰。

4. 从分类到检测与分割:视觉任务的横向扩展

4.1 目标检测:两阶段与单阶段的主流方案

分类只回答“图里有什么”,目标检测要求在分类基础上再给出“每个物体在哪”,输出一组带类别标签的边界框。检测方法主要分成两大家族。

两阶段检测的代表是 Faster R-CNN 系列。它先通过区域建议网络(RPN)生成几千个候选框,再对候选框逐一做分类和边框回归。通俗地理解,就是“先找出所有可能有物体的位置,再精判每一个位置是什么”。两阶段方法最大的特点是精度高,尤其在密集小目标上表现不错,但速度相对慢。我的经验是,如果做学术实验追求指标,两阶段是稳妥选择;如果要落地实时系统,直接下单阶段方案。

单阶段检测的代表是 YOLO 系列和 SSD。YOLO 的思路简单粗暴:把图像划分成网格,每个网格直接预测边界框和类别概率,一次前向就输出结果。速度极快,但早期 YOLO 对小目标和密集场景不友好。现在到了较新的 YOLO 版本,引入了 anchor-free、多尺度特征融合、注意力模块,在速度和精度上已经和两阶段方法差距缩小。做实时的安防项目,我基本首选 YOLO 系列。

锚框(anchor)是检测里最容易劝退新人的概念。简单说,锚框就是一系列预设好的不同尺寸、不同长宽比的矩形候选框,模型要学习的是“这个锚框距离真实目标差多少、是不是一个目标”。后来 anchor-free 的方案直接让模型预测目标的中心点和宽高,省去了手工设计锚框参数的过程,在工程上省心很多,这类方法在小目标上往往也有不错表现。

4.2 语义分割与实例分割的要点

分割是像素级分类,要比检测更细。语义分割把图像里的每个像素都标上一个类别,比如把整片天空标为“天空”,不去区分是哪一朵云。实例分割则要区分同一类别下的不同个体,比如图片里有三辆车,就要给每辆车单独一个掩码。前者用 U-Net、DeepLab 系列是主流,后者则较多用 Mask R-CNN 这类扩展框架。

U-Net 是我最推荐入门的分割结构。它是一个对称的编码器-解码器结构,编码器逐层下采样提取特征,解码器逐层上采样恢复分辨率,中间通过跳跃连接把浅层特征与深层特征拼接,从而保留边界和细节信息。医疗影像分割、卫星图像分割这种小样本任务,U-Net 配合数据增强往往就能有不错的效果。

分割模型的训练一般用交叉熵损失或 Dice loss。交叉熵逐像素计算损失,简单直观,但遇到前景背景比例悬殊的情况,模型会偏向预测背景,导致目标区域被吞掉。Dice loss 对类别不平衡更鲁棒,它衡量预测掩码和真实掩码的重合度。实际项目中我常把两个损失按权重加起来用,同时兼顾逐像素准确率和目标区域完整度。

一个重要的后处理细节:分割模型的输出是每个像素的类别概率图,直接 argmax 得到的掩码往往有小噪声和毛刺。用条件随机场或者简单的形态学开闭运算可以去掉小孔洞和小噪点,让掩码更完整。在工业场景里,分割后处理甚至比模型本身还影响最终效果。

4.3 轻量化模型与边缘端部署

视觉项目的终点不总是训练完看指标,很多实际场景要部署到嵌入式设备或移动端,这就涉及模型轻量化。最常用的手段有三种:结构轻量化、模型剪枝与量化、知识蒸馏。

结构轻量化是一开始就选轻量网络,比如 MobileNet、ShuffleNet。MobileNet 的核心是深度可分离卷积:把标准卷积拆成逐通道卷积和逐点卷积两步,计算量降到原来的十分之一左右,精度损失控制在实体任务可接受范围。我的实测经验是在一个嵌入式缺陷检测项目里,MobileNetV3 比 ResNet-50 快 6 倍,精度只低了 1.2 个百分点,对于很多业务来说完全够用。

量化是把 FP32 权重压缩成 INT8,模型体积缩小四倍,推理速度也大幅提升。在特定硬件上,INT8 量化可以跑得很流畅。但量化需要注意一个坑:训练后直接量化有时会有明显掉点。解决办法是量化感知训练——在训练过程中模拟量化的数值误差,让模型提前适应低精度。我在一个巡检项目里,用量化感知训练把精度损失从 3% 压到了 0.8% 以内。

模型剪枝和知识蒸馏更适合已有大模型、想瘦身的场景。剪枝把权重接近零的通道删掉,蒸馏则让一个小模型学习大模型的软输出,后者在分类任务里效果尤其好。如果在资源受限的环境里做视觉任务,我建议的顺序是:先选轻量网络,加知识蒸馏,最后再做量化。

5. 常见问题与排查技巧

5.1 过拟合与欠拟合,loss 不下降怎么办

视觉训练最常见的两个问题症状是:训练集 loss 不断下降,但验证集指标停滞不前,这是典型的过拟合;训练集 loss 根本降不下去,则可能是欠拟合。

过拟合的第一反应不是调网络结构,而是先检查数据量、增强策略和正则化。最简单有效的方法是:增加数据增强强度,加入权重衰减,加入 dropout。如果效果不明显,再考虑用小一点的网络或者降低模型复杂度。还有一个容易被忽略的点:检查训练集和验证集是否同分布。我遇到过一个分类项目,训练集来自网络爬的图片,验证集用手机实拍,分布差异很大,模型指标上不去,数据清洗后效果立竿见影。

欠拟合时,先确认模型容量是否足够。如果数据量不小但模型太浅,考虑加深网络或换成更强的骨干。其次是检查学习率设置:学习率过大,loss 会震荡不降;过小,模型学得极慢,像原地踏步。我一般会用一个小工具:随机取几个 batch 的样本,只看训练 loss 的变化速度,快速判断学习率是否在合理范围。

还有一类特别隐蔽的问题:标签错乱。有人训练猫狗分类,数据 AUGMENT 管线里把标签列表和图像列表的对应关系搞错位了,训练 loss 一直下不到一个理想值。建议每轮训练前打印一两个样本的标签和路径,人工核对,这比事后排查高效得多。

5.2 类别不平衡的实战处理

真实项目的分类问题,各类别数量往往严重不均,有的类别可能只占总样本的 1%。此时模型会偏向学多数类,导致少数类几乎不识别。

最简单的缓解手段是重采样:对少数类做上采样(重复采样)或对多数类做下采样。上采样要配合数据增强,否则模型会对少数类样本过拟合,记住那几张图的细节。在视觉任务中,我一般会对少数类施加更强的增强变换,达到“虚拟扩充”的效果。另一种思路是加权损失:给少数类更大的权重,让错分少数类产生更大的梯度。Focal Loss 是一个经典方案,它在交叉熵基础上加了调制因子,让模型把精力集中在难分样本上,天然缓解了样本不均衡。

类别不平衡严重时,还可以用更高级的解耦训练策略:先正常训练特征提取部分,再单独用平衡采样微调分类头。这个“分步训练、解耦分类”的思路,在一些公开榜单的实践中拿到了不错的提升。真实项目里,先做数据层面的处理(重采样和增强),再考虑加权损失,一般就能解决大多数问题。

5.3 训练崩溃与显存不足的排查

训练跑着跑着 loss 变成 NaN,这是很多初学者最崩溃的问题。NaN 大概率来源于梯度爆炸或者数值不稳定。排查顺序我总是固定不变:先降低学习率看是否恢复,这是最快的试错。如果学习率已经很低还 NaN,检查归一化和标签是否有异常值;FP16 混合精度下的 NaN 则要检查损失缩放因子设置。此外,检查模型最后一层是否接 softmax 后再算交叉熵,有的框架会重复计算导致数值问题。

显存不足(OOM)则更偏向工程问题。优先调小 batch size,如果模型还是太大,考虑混合精度训练(FP16),显存几乎减半。再用梯度累积模拟大 batch,比如目标 batch 是 64、实际只能放下 16,可以累积 4 个 step 的梯度再更新一次,效果相当于用了 64 的批大小。如果仍不够,就得检查模型本身是否有冗余,或考虑梯度检查点技术,牺牲少量训练时间换显存。

我遇到过一个很奇葩的 OOM 情况:单卡训练正常,换到多卡反而 OOM,排查发现是某些中间变量在分布式环境里被重复复制到各卡。解决办法是显式调用释放中间张量、控制好进程组通信。工程问题五花八门,但底层就是“显存规划”和“梯度流控制”两件事。

6. 第八阶段:经典问题速查与调参总结

6.1 常用训练配置参考表

我把一个典型视觉分类任务的最终配置整理成下面这张表,可以直接作为新项目的起点。这些参数来自我多个项目的经验值,不能保证所有场景通用,但作为基线足够。

配置项推荐值说明
输入尺寸224×224兼顾精度与速度
骨干网络ResNet-50 或 MobileNetV3精度优先选前者,效率优先选后者
优化器SGD + momentum 0.9最终精调首选,收敛精度通常更高
基础学习率0.01(batch=256)批次减半时学习率约减半
学习率策略warmup 5 epochs + 余弦退火防止初期震荡,后期精调
权重衰减1e-4配合 SGD 使用
批次大小64-256按显存量调整
数据增强翻转 + 随机裁剪 + 颜色抖动检测任务要同步标注
损失函数交叉熵 / Dice 按任务选择分割任务可复合加权
评估指标Accuracy / mAP / mIoU按任务类型选择

这张表基本覆盖了分类、检测、分割三类任务的所有核心超参数。建议使用者在自己的项目里,把这张表当作“快速启动包”,不要盲目改来改去,等模型结构或数据分布有重大变化时再针对性调整。

6.2 训练过程监控三板斧

训练视觉模型时,我最重视三个监控维度:loss 曲线、验证指标曲线、梯度统计。这三个维度能帮你快速定位问题,省去大量盲目调参时间。

loss 曲线关注的是收敛速度和稳定性。正常情况是训练集 loss 稳步下降,验证集 loss 先降后升(出现过拟合)。梯度统计则是看反向传播时梯度范数是否异常大或异常小。如果梯度范数爆炸,减少学习率或加梯度裁剪;如果梯度消失,则检查是否用了不合适的激活函数或残差连接有问题。

可视化也很重要。很多人只在训练结束后看最终结果,却忽略了训练过程中的特征图变化。我习惯在几个关键 epoch 把中间层的特征图打印出来看一眼,如果特征图一片均匀或全是噪声,说明网络没有学到有效特征。这种“训练中可视化检查”,往往能发现随机种子都救不回来的深层问题。

6.3 快速上手的迷你项目建议

最后一部分,给刚进入这一阶段的朋友一个可以直接抄作业的迷你项目:CIFAR-10 图像分类。

用 PyTorch 写一个只含两个卷积层的简易 CNN,配合随机裁剪、水平翻转和归一化,在 CIFAR-10 上可以达到 80% 以上的测试准确率。我把这个项目当作“视觉入门体检”:跑通整个流程,确认数据加载、模型定义、训练循环、评估逻辑都无误,再去接触检测分割那些复杂的系统会顺畅得多。

参考代码框架很简单,这里给出一个可以跑的 PyTorch 教程级别的核心片段:

import torch import torch.nn as nn class SimpleCNN(nn.Module): def __init__(self, num_classes=10): super().__init__() self.features = nn.Sequential( nn.Conv2d(3, 32, kernel_size=3, padding=1), nn.BatchNorm2d(32), nn.ReLU(inplace=True), nn.MaxPool2d(2), nn.Conv2d(32, 64, kernel_size=3, padding=1), nn.BatchNorm2d(64), nn.ReLU(inplace=True), nn.MaxPool2d(2), ) self.classifier = nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Linear(64, num_classes), ) def forward(self, x): x = self.features(x) x = self.classifier(x) return x

训练时注意数据增强的实现放在 Dataset 的 __getitem__ 里,验证时只做 resize 和归一化。我建议把训练和验证分成两套 transform,避免验证集被增强污染。另外,用上面这张配置表里偏小的一组参数:batch=128,SGD lr=0.05 配合 warmup 和余弦退火,20 个 epoch 基本就能看到效果了。

我在实际使用中发现,做视觉项目最大的阻碍往往不在算法本身,而是“对数据流的掌控力”。今天分享的这些内容,从底层逻辑讲到网络演进,再讲到数据工程和落地方案,其实都在围绕这一件事:先把图像数据在模型中的完整旅程走通,再谈优化和创新。第八部分的笔记到这里就算收尾了,如果这个系列继续往后写,我会专门补一期关于 Transformer 在检测任务中的实战对比,以及强化学习在数据增强搜索中的应用,这两个方向目前来看实战价值很大。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进