1. 从零构建计算机视觉知识体系为什么大多数人卡在“只会调包”这一步搞计算机视觉这几年我见过太多人拿着YOLO的预训练权重跑通一个检测demo就觉得自己“入门了”结果一问NMS的IoU阈值怎么选、Anchor Box为什么要聚类、特征金字塔到底解决了什么问题就支支吾吾说不清楚。这个项目标题里提到的“神经网络架构、PyTorch、目标检测算法、Python编码者计算机视觉应用”其实是一条非常完整的从底层到应用的链路但绝大多数教程把它们拆成了互不相关的碎片——讲CNN的只讲卷积核怎么滑讲PyTorch的只讲nn.Module怎么继承讲目标检测的直接甩一个torchvision.models.detection的调用示例。这种学法学完还是只会调包。我自己带过几个刚入行的同学发现一个很普遍的规律能跑通代码和能改代码之间隔着一整套对数据流、梯度流、特征流的理解。比如你拿到一个预训练的ResNet做迁移学习什么时候冻结backbone、什么时候解冻、BatchNorm的track_running_stats在微调时该不该改这些决策背后都是对网络架构和训练动态的理解不是查文档能查出来的。这个系列我想做的事情就是把这条链路上的关键节点一个个拆开讲清楚每个设计选择背后的“为什么”同时给出可以直接复现的PyTorch代码。这篇文章适合谁看如果你已经会用Python写基本的脚本对NumPy的数组操作不陌生知道什么是矩阵乘法但面对一个完整的CV项目时不知道从哪下手——比如给你一批标注好的图片让你训练一个目标检测器你脑子里没有一个清晰的pipeline——那这篇内容就是为你准备的。我不会假设你有深度学习框架的使用经验但我会假设你愿意动手敲代码而不是只看不练。整篇内容会围绕四个核心板块展开神经网络架构的设计逻辑重点是CNN和ResNet系列、PyTorch的工程化使用不只是nn.Module还有数据加载、训练循环、混合精度、模型保存与加载的坑、目标检测算法的演进脉络从R-CNN到YOLO到DETR重点讲清楚每个阶段解决了什么问题、以及一个完整的Python编码实战项目从数据准备到模型部署的完整流程。每个板块我都会给出可运行的代码片段和参数选择的计算过程确保你看完能直接上手改自己的项目。2. 神经网络架构从卷积核到残差连接的设计逻辑2.1 卷积到底在“卷”什么用一个小例子把特征图尺寸算清楚很多人第一次学卷积的时候被padding、stride、dilation、kernel_size这几个参数搞得头晕。我用一个最直观的方式帮你理清楚假设你有一张32x32的单通道图片用一个5x5的卷积核stride1padding0那么输出特征图的尺寸是多少公式是(H - K 2P) / S 1代入就是(32 - 5 0) / 1 1 28。这个28x28就是卷积核在图片上“滑”了28次的结果。如果你把padding改成2输出就变成(32 - 5 4) / 1 1 32尺寸不变。这就是为什么很多网络设计里用padding (kernel_size - 1) / 2来保持特征图尺寸不变——为了让网络可以做得更深而不至于特征图过早缩小。但这里有一个容易被忽略的细节卷积核的参数量是K * K * C_in * C_out。假设输入是256通道输出也是256通道用3x3的卷积核参数量就是3 * 3 * 256 * 256 589,824。如果换成5x5参数量直接变成5 * 5 * 256 * 256 1,638,400翻了将近三倍。这就是为什么VGG之后大家都用3x3堆叠——两个3x3卷积的感受野等价于一个5x5但参数量只有2 * 3 * 3 * C^2 18C^2而5x5是25C^2省了将近30%的参数同时因为中间多了一层非线性激活表达能力还更强。我在实际项目里踩过一个坑有一次做一个细粒度分类任务输入图片分辨率很高1024x1024我直接套了一个标准的ResNet-50结果第一层7x7 stride2的卷积直接把特征图降到了512x512再经过maxpool变成256x256后面几层下来特征图太小小目标的细节全丢了。后来改成第一层用3x3 stride1的卷积虽然计算量上去了但精度提升了将近8个百分点。卷积核大小和stride的选择本质上是在计算量和空间分辨率之间做权衡没有绝对的对错关键看你的任务对空间细节有多敏感。2.2 批归一化与激活函数为什么ReLU之后还要有BNBatchNorm这个层刚出来的时候很多人不理解为什么它有效。原始论文说是解决了“内部协变量偏移”但这个解释后来被MIT的研究者质疑了。我自己的理解更偏向工程视角BN本质上是在每个mini-batch上做了一次标准化让每一层的输入分布保持稳定从而允许你用更大的学习率加快收敛。你可以做一个简单的实验去掉BN层用同样的学习率训练loss曲线会震荡得很厉害甚至发散加上BN之后loss下降平滑得多。但BN有几个坑必须注意。第一训练和推理阶段的行为不一样。训练时用的是当前batch的均值和方差推理时用的是训练过程中累积的running_mean和running_var。如果你在推理时忘了调用model.eval()BN层会继续用batch统计量导致输出不稳定。第二batch size太小的时候BN效果会变差。因为batch统计量本身噪声就大如果batch size只有2或者4均值和方差的估计非常不准。这种情况下可以考虑用GroupNorm或者LayerNorm替代。第三迁移学习时BN层要不要冻结。如果你的目标域和源域差异很大比如源域是自然图像目标域是医学影像冻结BN的running stats通常更好如果差异不大让BN继续更新往往能带来更好的精度。激活函数这块ReLU是默认选择但它有一个“死亡ReLU”的问题如果某个神经元的输入一直为负梯度就一直是0这个神经元就再也活不过来了。LeakyReLU和ELU就是为了解决这个问题。我在实践中发现对于深层网络超过50层用ReLU配合BN通常就够了对于GAN或者强化学习这种对梯度非常敏感的场景LeakyReLU更稳。另外GELU在Transformer架构里是标配但在CNN里用得不多除非你在做视觉Transformer相关的任务。2.3 残差连接为什么“跳连接”能让网络深到1000层ResNet的核心思想其实一句话就能说清楚让网络学习残差映射F(x) H(x) - x而不是直接学习H(x)。这样如果最优的映射就是恒等映射网络只需要把F(x)学成0就行了比直接学一个恒等映射容易得多。这个设计让梯度可以通过跳连接直接传回浅层缓解了梯度消失的问题。但这里有一个细节很多人没注意残差块里的BN层位置很关键。原始ResNet v1是把BN放在卷积之后、ReLU之前顺序是Conv - BN - ReLU。后来ResNet v2也叫Pre-activation ResNet改成了BN - ReLU - Conv把BN和ReLU放到卷积前面。这个改动看起来很小但效果很明显v2的梯度流更干净因为跳连接上没有任何非线性操作梯度可以无阻碍地传回去。我在做超深网络比如ResNet-152的时候v2的结构收敛更快最终精度也略高一点。还有一个工程上的经验如果你要自己实现ResNet注意下采样块downsample block的处理。当输入输出通道数不一致或者特征图尺寸减半时跳连接上需要加一个1x1卷积来匹配维度。这个1x1卷积的stride要跟主分支的stride一致否则特征图尺寸对不上会报错。我见过不少人在这一步卡住其实就是忘了对齐维度。3. PyTorch工程化从写模型到训练一个能用的模型3.1 Dataset和DataLoader数据加载的瓶颈往往在这里很多人训练慢第一反应是模型太大或者GPU不够好但实际上数据加载经常是真正的瓶颈。PyTorch的DataLoader有几个参数直接影响吞吐量num_workers、pin_memory、prefetch_factor。num_workers设成CPU核心数通常是个好的起点但也不是越大越好——每个worker都会复制一份数据集对象如果数据集很大比如加载了所有图片到内存内存会爆。我一般设成min(8, os.cpu_count())然后根据GPU利用率调整。pin_memoryTrue这个选项当你的数据需要从CPU传到GPU时它会使用锁页内存传输速度更快。但如果你用的是IterableDataset或者数据已经在GPU上了这个选项就没用。还有一个坑自定义Dataset的__getitem__里不要做太重的预处理比如实时做数据增强随机裁剪、颜色抖动是可以的但不要在里面读大文件或者做复杂的计算。如果预处理很重考虑提前把数据预处理成二进制格式比如LMDB或者HDF5训练时直接读速度会快很多。我做过一个对比实验同样的模型和数据用原始JPEG图片实时解码增强GPU利用率只有40%左右换成提前解码好的二进制格式GPU利用率直接拉到85%以上训练时间缩短了将近一半。数据管道的优化往往比换一个更大的模型带来的收益更直接。3.2 训练循环的细节学习率调度、梯度裁剪、混合精度PyTorch的训练循环看起来很简单但魔鬼在细节里。先说学习率调度。StepLR是最简单的每过多少个epoch把学习率乘以一个gamma。但实践中我发现CosineAnnealingLR或者OneCycleLR往往效果更好因为它们在训练后期把学习率降得很低让模型能收敛到一个更平滑的极小值。OneCycleLR还有一个好处它先 warmup 再退火对初始学习率不敏感适合你不太确定该设多大学习率的情况。梯度裁剪是另一个容易被忽略的点。如果你的loss突然变成NaN大概率是梯度爆炸了。torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0)这行代码能救命。但注意裁剪的阈值需要根据模型和任务调整设得太小会限制模型的表达能力设得太大等于没裁。我一般从1.0开始试如果训练不稳定就降到0.5如果训练太慢就升到2.0。混合精度训练AMP是这几年最实用的工程优化之一。torch.cuda.amp可以让你用FP16做前向和反向传播同时用FP32维护一份主权重既省显存又提速。但有几个坑某些操作在FP16下会溢出比如exp、log、大数乘法。PyTorch的GradScaler会自动处理梯度的缩放但如果你自己写了自定义的loss或者层需要确保它们支持FP16。另外BN层在FP16下容易出问题通常建议把BN保持在FP32。我实测下来ResNet-50用AMP训练显存占用减少约40%速度提升约30%精度损失几乎可以忽略。3.3 模型保存与加载state_dict之外你还应该知道的事torch.save(model.state_dict(), path)是最常见的保存方式但如果你只保存了state_dict加载的时候必须重新定义模型结构而且结构必须完全一致。如果模型结构变了比如改了层名或者加了新层加载就会报错。更稳妥的方式是同时保存模型结构和权重但这样会依赖具体的类定义跨项目迁移时可能出问题。我自己的习惯是训练时保存两个文件一个是state_dict用于继续训练一个是TorchScript或者ONNX格式用于部署。TorchScript的好处是它把模型结构和权重打包在一起不依赖Python环境可以直接在C里加载。ONNX的兼容性更好几乎所有的推理引擎都支持但转换过程中可能会遇到不支持的操作需要自己写自定义算子。还有一个坑加载模型时map_location参数一定要设。如果你在GPU上保存的模型在CPU上加载时不指定map_locationcpu会报错。反过来如果你在CPU上保存的模型要在GPU上加载也需要指定map_location。这个细节看起来小但在部署时经常卡住人。4. 目标检测算法从R-CNN到DETR的演进逻辑4.1 两阶段检测器R-CNN家族到底在解决什么问题R-CNN的思路很直接先用Selective Search生成一堆候选区域region proposals然后对每个区域用CNN提取特征最后用SVM分类。但这个方法慢得离谱——一张图片要跑2000个候选区域每个区域都要过一次CNN根本没法实时。Fast R-CNN做了两个关键改进一是用ROI Pooling把不同大小的候选区域统一到固定尺寸二是把分类和回归放在同一个网络里联合训练。这样只需要对整张图片过一次CNN然后在特征图上做ROI Pooling就行了速度提升了十几倍。Faster R-CNN的贡献是用RPNRegion Proposal Network替代了Selective Search把候选区域的生成也交给了神经网络。RPN本质上是在特征图的每个位置上放一组Anchor Box然后预测每个Anchor是前景还是背景以及相对于真实框的偏移量。这里的关键参数是Anchor的尺度和长宽比通常用K-means对训练集的真实框做聚类来得到。我做过一个实验在COCO数据集上用聚类得到的Anchor比默认的9个Anchor3种尺度x3种长宽比的mAP高了将近3个百分点。Anchor的设计直接影响到RPN的召回率进而影响整个检测器的性能。但Faster R-CNN也有它的问题ROI Pooling在量化坐标时会有精度损失对小目标不友好。Mask R-CNN提出了ROI Align用双线性插值代替了量化操作解决了这个问题。如果你在做实例分割或者小目标检测ROI Align几乎是必选项。4.2 单阶段检测器YOLO和SSD的取舍YOLO把检测问题直接建模成回归问题把图片分成SxS的网格每个网格预测B个边界框和对应的置信度以及C个类别的概率。这个设计让YOLO的速度极快但早期版本对小目标和密集场景的检测效果不好。YOLOv3引入了FPNFeature Pyramid Network和多尺度预测在三个不同的特征图尺度上分别预测小目标的检测能力大幅提升。YOLOv4和v5在数据增强、损失函数、网络结构上做了大量工程优化比如Mosaic增强、CIoU损失、CSPDarknet backbone把速度和精度的平衡推到了一个新的高度。SSD的思路和YOLO类似但它在多个尺度的特征图上都设置了不同尺度和长宽比的默认框default boxes然后预测偏移量。SSD的一个关键设计是用卷积层直接预测检测结果而不是全连接层这让它可以处理任意尺寸的输入图片。但SSD的Anchor设置非常依赖人工经验不同数据集上需要重新调整。我在实际项目里选检测器的时候通常会问三个问题第一你的场景对速度的要求是多少如果是实时视频流30FPS以上YOLO系列是首选如果是离线处理Faster R-CNN或者DETR可能精度更高。第二你的目标尺度分布是什么样的如果小目标很多FPN几乎是必须的YOLOv3之后的版本都自带FPN。第三你的标注数据有多少如果数据量很小几千张从预训练模型微调是唯一可行的方案如果数据量很大几十万张可以考虑从头训练。4.3 DETR用Transformer做检测到底香不香DETRDetection Transformer是Facebook在2020年提出的它把检测问题建模成一个集合预测问题用Transformer的encoder-decoder结构直接输出一组检测框不需要Anchor也不需要NMS。这个设计非常优雅但训练收敛很慢——原始论文需要500个epoch才能收敛而Faster R-CNN通常12个epoch就够了。后来Deformable DETR通过可变形注意力机制把收敛速度提升到了50个epoch左右。DETR的优势在于它没有Anchor和NMS这些手工设计的组件pipeline非常干净。但它的劣势也很明显对小目标的检测效果一般因为Transformer的注意力是全局的小目标在特征图上占的像素太少注意力容易分散。另外DETR的推理速度比YOLO慢不少不适合实时场景。我个人的看法是DETR适合那些对精度要求高、对速度不敏感、且目标尺度比较均匀的场景比如文档版面分析或者医学影像检测。如果你要做的是自动驾驶或者视频监控YOLO系列仍然是更务实的选择。5. Python编码实战从数据准备到模型部署的完整流程5.1 数据准备标注格式转换与数据增强策略假设你拿到了一批标注数据格式是Pascal VOC的XML文件但你想用YOLO训练需要转成YOLO的txt格式。转换的逻辑很简单YOLO的标注是class_id x_center y_center width height所有坐标都归一化到0到1之间。写一个脚本遍历所有XML文件解析出bndbox的坐标然后做归一化就行了。但这里有一个坑VOC的坐标是xmin, ymin, xmax, ymax而YOLO需要的是中心点和宽高转换公式是x_center (xmin xmax) / 2 / image_widthwidth (xmax - xmin) / image_width。如果图片有旋转或者裁剪还需要考虑坐标变换。数据增强方面目标检测的增强比分类复杂得多因为你不仅要变换图片还要同步变换标注框。常用的增强包括随机水平翻转、随机缩放、随机裁剪、颜色抖动、Mosaic把四张图片拼成一张。Mosaic增强是YOLOv4引入的它让模型在训练时看到更多样的上下文对小目标的检测效果提升明显。但Mosaic有一个副作用它会让batch里的图片分布发生变化BN的统计量可能不准。如果用了Mosaic建议把BN的momentum调小一点或者用SyncBN。还有一个经验验证集不要做增强只做必要的resize和归一化。因为增强后的验证集不能真实反映模型在真实数据上的表现。我见过有人为了“提升”验证集精度在验证集上也做随机裁剪结果模型上线后效果一塌糊涂。5.2 模型训练损失函数的选择与调参目标检测的损失函数通常由两部分组成分类损失和回归损失。分类损失一般用交叉熵或者Focal Loss。Focal Loss是为了解决正负样本不平衡的问题——在单阶段检测器里背景框的数量远远多于前景框如果不加权模型会倾向于把所有框都预测成背景。Focal Loss通过(1 - p_t)^gamma这个因子让容易分类的样本权重降低难分类的样本权重保持。gamma通常设成2alpha设成0.25。回归损失这块从最早的Smooth L1到后来的IoU Loss、GIoU Loss、DIoU Loss、CIoU Loss演进的方向是让损失函数更好地反映检测框的质量。Smooth L1的问题在于它独立地优化四个坐标没有考虑框的整体性。IoU Loss直接优化预测框和真实框的交并比但当两个框不相交时IoU为0梯度消失。GIoU引入了最小闭包区域解决了不相交时的梯度问题。DIoU进一步考虑了中心点距离CIoU再加上长宽比的一致性。我实测下来CIoU Loss在大多数场景下比Smooth L1的mAP高1到2个百分点而且收敛更快。学习率方面目标检测通常用比分类更小的学习率因为检测网络有多个分支梯度尺度不一致。我一般用1e-4到1e-3之间的初始学习率配合warmup前500到1000个iteration线性增加学习率然后用Cosine退火。Batch size如果允许的话尽量大一点8到16因为BN需要足够的样本才能估计准统计量。如果显存不够可以用梯度累积来模拟大batch。5.3 模型评估与部署mAP的计算与推理优化mAPmean Average Precision是目标检测最常用的评估指标。计算过程是对每个类别把所有的预测框按置信度排序然后计算在不同召回率下的精度画出PR曲线曲线下的面积就是AP。所有类别的AP取平均就是mAP。但这里有一个细节IoU阈值的选择会影响mAP。COCO的mAP是IoU从0.5到0.95每隔0.05计算一次然后取平均这样能更全面地评估模型在不同定位精度下的表现。Pascal VOC的mAP通常只算IoU0.5。部署的时候推理优化是重点。第一把模型转成ONNX或者TensorRT推理速度通常能提升2到5倍。第二用半精度FP16或者整型INT8量化进一步压缩模型大小和加速。但量化会带来精度损失需要做校准calibration。第三如果输入图片分辨率很高考虑用滑动窗口或者图像金字塔但要注意这会增加推理时间。我做过一个对比同样的YOLOv5s模型PyTorch原生推理在V100上大概是10ms一张图转成TensorRT FP16之后降到3msINT8之后降到1.5ms但mAP掉了大概2个百分点。速度和精度的平衡最终还是要看你的应用场景能容忍多大的精度损失。6. 常见问题与排查技巧实录6.1 训练不收敛或者loss震荡怎么办这是最常见的问题原因可能有很多。第一步检查数据。把几张图片和对应的标注可视化出来看看标注框有没有画错、类别有没有标错。我遇到过有人把类别ID从0开始和从1开始搞混了导致模型怎么训都训不好。第二步检查学习率。学习率太大loss会震荡甚至发散太小收敛太慢。可以做一个学习率扫描从很小的学习率开始逐渐增大观察loss的变化找到一个使loss下降最快的值然后从这个值的一半开始训练。第三步检查BN层。如果batch size太小BN的统计量不准可以试试GroupNorm或者冻结BN。第四步检查梯度。用torch.autograd.gradcheck或者直接打印梯度的norm看看有没有梯度消失或者爆炸。还有一个容易被忽略的点数据预处理和模型预训练时的预处理不一致。比如你用了ImageNet预训练的权重但你的输入没有做ImageNet的归一化mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]模型的输入分布和预训练时差很多收敛会变慢甚至不收敛。这个坑我踩过好几次后来养成了一个习惯每次用预训练模型第一件事就是确认预处理是否匹配。6.2 显存不够用的几种解决方案显存不够是训练CV模型时的家常便饭。最直接的方法是减小batch size但batch size太小会影响BN的效果。第二种方法是梯度累积用小的batch size做多次前向传播把梯度累积起来然后再做一次参数更新。这样等效于大batch但显存占用不变。第三种方法是混合精度训练用FP16做前向和反向显存占用能减少30%到50%。第四种方法是梯度检查点gradient checkpointing它在前向传播时不保存中间激活值反向传播时重新计算用时间换空间。PyTorch的torch.utils.checkpoint可以很方便地实现。第五种方法是模型并行把模型的不同层放到不同的GPU上但这需要修改模型代码比较麻烦。我自己的经验是优先用混合精度梯度累积这两个组合能解决90%的显存问题。如果还不够再考虑梯度检查点。模型并行一般是最后的选择因为通信开销大而且调试困难。6.3 模型在验证集上表现好但实际部署效果差这个问题通常有几个原因。第一数据分布不一致。训练集和验证集可能来自同一个分布但实际部署时的数据分布不同。比如训练集是白天拍的图片部署时是晚上拍的光照条件差很多。第二过拟合。模型在验证集上表现好可能是因为验证集太小或者和训练集太相似。第三预处理不一致。训练时用的resize方法、归一化参数部署时可能忘了同步。第四后处理参数不同。比如NMS的IoU阈值、置信度阈值训练时和部署时可能设得不一样。排查的方法把部署时出错的样本拿回来可视化模型的输出看看是漏检、误检还是定位不准。如果是漏检可能是置信度阈值设得太高如果是误检可能是NMS阈值设得太低如果是定位不准可能是Anchor设置和实际目标尺度不匹配。我一般会做一个错误分析表把错误分成几类统计每类的比例然后有针对性地优化。问题现象可能原因排查方法解决方案loss震荡不收敛学习率太大、数据标注错误、BN统计量不准可视化数据、学习率扫描、打印梯度norm降低学习率、修正标注、换GroupNorm显存不足batch size太大、模型太大、未用AMP打印显存占用、逐步减小batch梯度累积、混合精度、梯度检查点验证集好但部署差数据分布不一致、过拟合、预处理不一致对比训练和部署的预处理、可视化错误样本增加数据多样性、正则化、统一预处理小目标检测效果差特征图分辨率太低、Anchor尺度不匹配可视化小目标的特征图、统计Anchor匹配率用FPN、调整Anchor尺度、提高输入分辨率推理速度慢模型太大、未优化、后处理耗时profile各阶段耗时转ONNX/TensorRT、量化、优化NMS6.4 几个我踩过的坑和对应的技巧第一个坑忘了设model.eval()。训练完模型直接拿来做推理BN层还在用batch统计量Dropout还在随机丢弃结果输出完全不对。这个坑我踩过不止一次后来养成了习惯推理前第一件事就是model.eval()训练前第一件事就是model.train()。第二个坑数据加载的num_workers设得太大。有一次我把num_workers设成了32结果内存直接爆了因为每个worker都复制了一份数据集。后来改成8问题解决。num_workers不是越大越好要根据数据集大小和内存来定。第三个坑学习率调度器的step调用时机。PyTorch的StepLR是在每个epoch结束后调用scheduler.step()但如果你用的是OneCycleLR它是在每个iteration后调用。搞混了会导致学习率变化不符合预期。用之前一定要看清楚文档。第四个坑多GPU训练时BN的统计量。用DataParallel的时候BN是在每个GPU上独立计算的等效于减小了batch size。如果batch size本来就小BN的效果会很差。解决方案是用DistributedDataParallel配合SyncBatchNorm但配置起来比较麻烦。如果条件允许尽量用单卡训练或者用足够大的batch size。第五个坑模型保存时忘了保存优化器的状态。如果你只保存了model.state_dict()想继续训练的时候优化器的动量信息丢了loss可能会突然跳一下。如果要断点续训一定要同时保存optimizer.state_dict()和scheduler.state_dict()。7. 一些关于学习路径的个人建议如果你刚开始学计算机视觉我建议不要一上来就啃Transformer或者DETR这些新东西。先把CNN的基础打牢手动实现一个简单的卷积层不用nn.Conv2d用unfold和矩阵乘法手动实现一个ResNet block手动写一个训练循环。这些看起来“重复造轮子”的练习能让你对数据流和梯度流有肌肉记忆。等你对CNN的每一个操作都了然于胸了再去看Transformer的注意力机制你会发现很多概念是相通的。PyTorch的学习我建议从读源码开始。torchvision.models里的ResNet实现只有两百多行但涵盖了卷积、BN、残差连接、下采样等所有核心概念。把它读懂读透比看十篇教程都有用。然后自己从头实现一遍不参考任何代码看看能不能跑通。这个过程会很痛苦但收获也最大。目标检测这块不要一上来就追求SOTA。先用YOLOv5或者Faster R-CNN跑通一个baseline然后逐步调整换backbone、加FPN、改损失函数、调Anchor。每改一个地方记录下mAP的变化慢慢你就知道每个组件的作用了。我见过太多人直接拿DETR或者Swin Transformer来训结果因为数据量不够或者调参不当效果还不如YOLOv5。最后保持动手的习惯。看再多的论文和教程不自己写代码跑一遍都是纸上谈兵。我自己的经验是每学一个新概念就写一个最小的可运行示例。比如学ROI Align就写一个脚本输入一个特征图和一个ROI输出对齐后的特征然后可视化出来看看对不对。这种“小步快跑”的学习方式比一次性啃一个大项目要高效得多。