
简介深度学习模型设计是决定竞赛排名的核心环节而非仅仅依赖可运行的Python源码。在图像分类、目标检测等实际任务中模型性能受任务边界、数据分布与算力约束的直接影响。从稳定基线出发合理选择Backbone并针对性地进行结构改造配合数据增强与学习率策略是提升精度的关键路径。当单模型精度触顶模型融合与后处理技术往往能进一步拉开差距。本文围绕这些工程化要点梳理一整套从架构选型到训练调优的实践方法论帮助你在竞赛中高效迭代。 很多人在百度云深度学习竞赛里拿到赛题后的第一反应是去找一份能跑的Python源码把数据灌进去然后虔诚地等一个分数。实际工作中我见过太多这样的同学代码确实能跑通loss也确实在降但分数就是卡在中游不动。问题往往不在代码本身而在于“源码”背后的模型设计链路——任务边界怎么定、骨干网络怎么选、训练策略怎么配、融合逻辑怎么写、工程结构怎么组织这些才是决定排名的东西。这篇笔记我不想给你一份“一键跑通”的源码那没有任何意义。我想把我在深度学习竞赛里做Python模型设计的一套完整思路拆开讲从动手前必须想清楚的三件事到Backbone选型和结构改造再到训练策略、模型融合、源码组织最后是那些反复出现的坑。整个过程用代码和表格配合说明适合正在准备竞赛、或者想把模型设计能力补齐的同学参考。1. 动手写模型前先把这三件事钉死任务边界、数据特性和算力预算1.1 任务边界决定模型的“终点线”拿到任何赛题第一件事不是打开PyTorch写类而是把任务边界彻底搞明白。同样是“图像”类任务图像分类看的是Top-1准确率还是宏平均F1目标检测看的是COCO mAP还是Pascal mAP语义分割看的是mIoU还是Pixel Accuracy这些指标上的差异会直接影响你怎么设计损失函数、怎么挑阈值、怎么做后处理。我习惯把评价指标写在项目的README第一行然后反推设计。举个例子如果比赛评测用的是宏平均F1那类别不平衡就必须优先处理光靠准确率刷上去没有意义。这时候你可能会在损失函数里给少数类加权或者在预测时按类别搜索最优阈值但如果是Top-1准确率你只需要把模型在绝大多数样本上的置信度拉高就行类别不均衡的影响就没那么大。这是两种完全不同的优化路径。可以用一段非常短的代码把指标计算固化下来方便每次验证都用同一套标准from sklearn.metrics import f1_score, accuracy_score def get_metric(y_true, y_pred, metricf1_macro): if metric f1_macro: return f1_score(y_true, y_pred, averagemacro) elif metric acc: return accuracy_score(y_true, y_pred) else: raise ValueError(fUnsupported metric: {metric})别小看这一步。很多队伍后期折腾半天最后发现预测结果在评测指标上不涨是因为训练时用loss做早停验证时又只看acc跟线上指标完全脱节那就白折腾了。1.2 数据特性决定模型的“想象空间”深度学习模型的性能上限很大程度上由数据决定而不是由网络结构决定。所以在写模型之前我会先写一段数据探查代码把样本数量、类别分布、图像尺寸分布全部统计一遍。有一个非常典型的误区是不看分辨率分布就直接把图像resize到固定尺寸。这在人脸识别和OCR比赛里尤其致命。如果原图长宽比差异很大统一resize到正方形会引入大量形变模型学到的是被拉伸的特征推理时遇到正常比例的图就拉胯。数据探查阶段至少要确认三件事训练集总数、验证集划分方式官方给的是固定划分还是需要自己K折每个类别的样本数量是否有长尾分布图像的宽高比分布、分辨率范围、是否有大量低分辨率样本。如果发现类别严重不均衡一个很实用的做法是用PyTorch的WeightedRandomSampler让每个batch里每个类别被采样的概率接近均匀。这只是几行代码但对不平衡数据的收敛效果远大于你换一个更强的Backbonefrom torch.utils.data import WeightedRandomSampler, DataLoader def make_sampler(labels): labels np.array(labels) class_counts np.bincount(labels) weights 1.0 / (class_counts[labels] 1e-6) weights weights / weights.sum() return WeightedRandomSampler(weights, num_sampleslen(labels), replacementTrue)另一个容易被忽略的点是“有没有重复样本”。很多竞赛数据里存在几乎一模一样的图片出现在训练集和验证集的情况。如果你不做去重验证集分数会虚高线上分数却很低。我一般会对图像做感知哈希或简单计算像素均值把重复度较高的样本挑出来检查一遍。1.3 算力预算决定模型的“体积上限”竞赛模型不是越重越好而是要在你的GPU和线上推理时限内做到最好。我在动手选型之前一定会先确认两个硬约束单卡显存多大训练时间有多少线上推理是否有时间限制。这里我把常见的几类Backbone在典型配置下的表现列出来供参考Backbone显存占用224输入batch 64训练速度精度潜力典型适用场景ResNet-50低快中快速基线小数据量ResNeXt-50中中中高中等数据量分类/检索任务EfficientNet-B3中中中高受限算力下的高精度需求Swin-Transformer-T中中高视觉竞赛、数据量较大时ViT-B/16高慢高数据量足够大且预训练充足时算力不够的时候解决问题的顺序应该是混合精度训练、梯度累积、降低输入分辨率、冻结预训练骨干的前几层。注意降低输入分辨率是最后手段因为它会直接损伤细粒度特征但在很多比赛里224和192的精度差距远小于你想象中那么大可用它换取更大的batch size。2. 从基线到进阶Backbone选型与网络结构改造实录2.1 为什么不建议一上来就魔改结构我见过不少朋友拿到赛题后第一件事就是去GitHub找最新论文的复现代码往项目里塞一个Swin-Transformer-Large然后开始调参。这通常是效率最低的做法。因为你的训练管线还没有验证过数据增强、损失函数、评测脚本可能都有bug这时候用一个大模型去跑出了问题你根本不知道是模型的问题还是管线的问题。正确的做法是先用一个稳定的预训练小模型跑通全流程比如ResNet-50或EfficientNet-B0。先跑出一个能复现的结果确认Dataloader、Loss、验证逻辑都没问题再逐步换更大的Backbone或改结构。我常说的“基线是来消错的不是用来刷分的”就是这个意思。基线模型跑通之后接下来所有结构上的改动都应该围绕“提升什么指标”来回答。比如细粒度分类任务加注意力模块多尺度目标检测任务加FPN图像分割任务换DeepLabV3的ASPP结构——这些改动是有明确目标的不是为了炫技。2.2 Backbone选型的几个判断标准Backbone选型没有绝对最优只有最合适。我自己的判断标准按照优先级排是这样的第一优先级预训练权重是否可得。如果只能用ImageNet预训练那别选一个冷门结构因为你没法在有限算力下从零训出好效果。第二优先级输入分辨率是否友好。Swin Transformer需要相对大的输入分辨率才能发挥优势否则patch embedding阶段就会丢失很多空间细节。第三优先级参数和显存是否在预算内。别只看论文里的Top-1准确率要看在你们卡上跑不跑得动。第四优先级这个结构和你任务的数据量是否匹配。数据量几千张的时候大模型更容易过拟合不如用小模型配合更强的数据增强。这里特别提一下ResNeXt-50。它在竞赛中知名度很高原因倒不是它比ResNet-50准确率高多少而是它用分组卷积的方式把基数cardinality提上去了同样的FLOPs下特征表达更多样。如果你显存有限又想比ResNet-50多榨出一点精度ResNeXt-50是性价比很高的选择。2.3 结构改造的四个有效方向一旦基线稳定想进一步提升模型能力不建议自己发明新的block而是优先在以下四个方向做工程化改造。第一个是输入分辨率与多尺度。这是性价比最高的改造方向。拿图像分类来说训练时用224推理时同时跑224和320两个分辨率把两个logits平均通常能提升1-2个点。因为大分辨率可以补充更多纹理细节小分辨率更关注全局结构两个视角互补。第二个是注意力模块。给标准的ResNet加SE模块几乎是无损的代码也很短import torch.nn as nn class SEBlock(nn.Module): def __init__(self, in_channels, reduction16): super().__init__() self.fc nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Conv2d(in_channels, in_channels // reduction, 1), nn.ReLU(inplaceTrue), nn.Conv2d(in_channels // reduction, in_channels, 1), nn.Sigmoid() ) def forward(self, x): scale self.fc(x) return x * scale第三个方向是多分支融合。对于输入信息比较丰富的任务比如同时包含RGB图和深度图、或多光谱图的比赛可以把不同模态输入用不同的分支网络编码然后在某个层做特征拼接。这种设计比把所有模态直接拼成多通道输入要稳定得多因为网络可以各自学习独立的底层特征。第四个方向是Head改造。分类比赛的Head不是只能用一个全连接层。可以用Global Average Pooling加Dropout加FC也可以用Generalized Mean PoolingGeM pooling替代GAP后者在检索类任务里非常管用。检测任务则要仔细调Anchor或Anchor-free头的参数分割任务要考虑Head的ASPP或FPN部分。Head往往比Backbone更值得花时间。3. 真正拉开分数差距的模型融合与后处理技术3.1 模型融合的三种基本姿势及其适用场景当你训练出来的单模型精度从稳定到不再增长时下一个提升点就是模型融合。但融合不是随便拿几个模型平均一下就行要分清楚你的资源适合哪种融合方式。第一种是“单模型多折融合”。把训练集划分成5折训练5个模型推理时把5个模型的预测结果平均。这是最稳定、性价比最高的融合方式几乎适用于所有任务。它本质上是在利用不同训练数据子集上模型的差异性来降低方差。第二种是“同架构多配置融合”。同一个Backbone用不同的随机种子、不同的输入分辨率、不同的数据增强策略训练出多个模型然后融合。这种方式比多折融合涨点更多因为模型之间的多样性更大但训练成本也更高。第三种是“异构模型融合”。比如ResNet和Swin Transformer一起融合。异构模型在特征层面的差异更大融合提升通常更明显但需要你有足够的算力去同时训好多个不同类型的模型。下表总结了这三者的特点融合方式多样性来源训练成本精度提升适用算力单模型多折数据划分低中低同架构多配置种子/分辨率/增强中中高中异构模型网络结构差异高高高3.2 融合权重可不是拍脑袋定的很多人做加权平均时“一拍脑袋”定个0.6、0.4这其实非常浪费。正确做法是用Out-of-FoldOOF预测来确定权重。所谓OOF预测就是每个模型在训练时留出的那一折验证集上的预测结果用来模拟模型对未见数据的表现。举个例子你训练了5折的模型A和5折的模型B把每个模型在所有样本上的OOF预测拼起来得到一个“模型A对全体训练样本的OOF预测”和“模型B的”。然后在这个OOF预测上搜索最优权重。这比直接在线下验证集上瞎试要严谨得多。搜索权重可以用一行代码搞定比如用贪心搜索或简单网格搜索import numpy as np def search_best_weight(pred_a, pred_b, y_true, metric_func, n_grid101): best_w, best_score 0, -1 for w in np.linspace(0, 1, n_grid): score metric_func(y_true, pred_a * w pred_b * (1 - w)) if score best_score: best_score, best_w score, w return best_w, best_score这样做的好处在于你把“融合权重”当成一个超参数来优化而不是靠感觉。本质上融合能提升性能的原因是不同模型的误差不完全相关平均能够抵消掉一部分方差。而加权平均中如果你清楚地知道B模型比A模型强就应该给B更高权重这个权重用OOF搜索比拍脑袋可靠得多。3.3 后处理是决赛圈的胜负手如果你觉得模型融合之后分数就封顶了那说明你还没进入决赛圈。真正的排名差距往往在最后几个千分点上靠后处理拉开。分类任务最常见的后处理是阈值迁移。对于多分类任务可以直接对每个类别的预测概率做温度缩放或者直接用验证集搜索一组类别置信度阈值。对于二分类任务不要默认0.5是阈值很多数据分布不平衡的场景0.5都不是最优的。目标检测任务不要只依赖标准的NMS。尝试Weighted Boxes FusionWBF往往比单独一个模型做NMS涨点更明显因为WBF可以融合多个模型的检测框并使用置信度加权能保留一些被NMS误杀的正样本框。不过要注意WBF对预测框的坐标精度要求较高如果模型回归不稳定融合效果反而可能变差。图像分割任务的后处理则要关注“小区域过滤”。很多分割模型会在背景区域产生一堆零星的误检小连通域。按连通域面积过滤一下去掉小于某个阈值的块往往就能在mIoU上提升不少。还有一些比赛可用CRF来精修分割边界但CRF速度慢需要你权衡线上推理时限。至于NLP类任务伪标签是常见的后处理方式但需要特别小心。用模型预测的高置信度样本加入训练集如果预测有系统性错误伪标签会把错误放大。我一般只在线上分数稳定、且能确认某些类别预测置信度很高时才使用。4. 训练引擎学习率策略、数据增强与交叉验证的工程化实现4.1 数据增强的组合拳数据增强在竞赛里的重要性我个人认为不低于模型结构。但增强策略不能盲堆要“组合拳”式地用起来。基础组合我一般这么配随机水平翻转、随机旋转±15度、随机缩放裁剪、颜色抖动。这个组合在大多数图像任务里都稳。然后再根据任务特性往上加检测任务加随机平移和随机尺度变化分割任务加随机弹性形变分类任务可以加RandomErasing或Cutout。Mixup和CutMix是竞赛里提升分类精度很有效的增强方式。它们的核心思想是把两张图按比例混合同时把标签也按同样比例混合让模型在样本之间做线性插值减小过拟合。这里给一个CutMix的简单实现片段def cutmix(x, y, alpha1.0): lam np.random.beta(alpha, alpha) index torch.randperm(x.size(0)) bbx1, bby1, bbx2, bby2 rand_bbox(x.size(), lam) x[:, :, bbx1:bbx2, bby1:bby2] x[index, :, bbx1:bbx2, bby1:bby2] y_a, y_b y, y[index] lam 1 - ((bbx2 - bbx1) * (bby2 - bby1) / (x.size(-1) * x.size(-2))) return x, y_a, y_b, lam需要特别注意使用Mixup/CutMix时训练loss要按照lam加权计算。很多同学忘记在mixup分支里改loss导致模型训练了却学不到混合信息。另外一个容易被忽视的点是强增强在训练后期可能需要降低强度。因为太强的数据增强会让模型和验证集的分布偏差变大导致验证分数上不去。我在训练后期常用的一种策略是前80%个epoch用强增强最后20%切换成较弱增强让模型慢慢收敛到更贴近真实分布的状态。4.2 学习率与优化器调参经验训练Deep Learning模型学习率策略是除了数据之外影响最大的因素。竞赛里我最常用的组合是warmup加cosine退火。warmup的作用是让模型在前期用较小的学习率稳定性训练避免大步长把参数撞飞cosine退火则让模型在后期的学习率平缓下降到很低有助于收敛到更平坦的局部最优。用PyTorch内置调度器的话optimizer torch.optim.AdamW(model.parameters(), lr1e-4, weight_decay1e-4) total_steps len(train_loader) * epochs scheduler torch.optim.lr_scheduler.OneCycleLR( optimizer, max_lr1e-3, total_stepstotal_steps, pct_start0.1, anneal_strategycos )OneCycleLR本身就包含warmup和anneal在竞赛里非常省心。如果你更想手动控制也可以用CosineAnnealingLR加LinearLR组合。优化器方面我一般不纠结AdamW和SGD。显存允许、数据量中等时AdamW更稳配合weight_decay即可。数据量大或者你愿意多调几轮SGD带动量cosine往往能到更好的点但收敛更慢。一个小经验是当你的训练集有几万张以上时SGD的最终精度通常会高于AdamW但需要更长训练时间如果只是几千张数据AdamW是更省事的选择。指数移动平均EMA是一个几乎零成本又能涨点的技巧。维护一份参数的指数滑动平均副本推理时用这份副本而不是训练中的实时参数能有效平滑训练后期的参数震荡class EMA: def __init__(self, model, decay0.999): self.decay decay self.ema_params {} for name, p in model.named_parameters(): if p.requires_grad: self.ema_params[name] p.detach().clone() def update(self, model): with torch.no_grad(): for name, p in model.named_parameters(): if p.requires_grad: self.ema_params[name].mul_(self.decay).add_(p.detach(), alpha1 - self.decay) def apply(self, model): for name, p in model.named_parameters(): if p.requires_grad: p.data.copy_(self.ema_params[name])EMA的decay一般取0.99到0.999之间。取太小起不到平滑作用取太大会让EMA参数更新太慢。对分类任务我通常从0.995开始试。EMA在检测、分割任务上同样有效建议无脑加进去。4.3 K折交叉验证与OOF预测单次划分训练集和验证集会让你的模型评估结果受数据划分方式影响很大。尤其当训练集只有几千张时一个运气不好的划分会让结果波动1-2个点非常影响你判断“模型改造到底有没有用”。所以要养成分5折交叉验证的习惯。把训练集划分成5份每次用其中4份训练、1份验证循环5次。这里有一个关键操作每折训练结束后把该折的验证集预测结果存下来最终得到整个训练集上每个样本的OOF预测。这份OOF预测后续可用来做融合权重搜索、选择最优阈值、甚至训练Stacking模型。K折的采样方式也要注意如果是分类任务用StratifiedKFold按类别比例分层采样如果是回归任务按目标值分箱后分层采样如果带有时间序列性质只能按时间顺序划分这个划分方式往往需要单独写代码不能直接随机打乱。5. 源码结构怎么组织才不会在迭代中翻车5.1 config、models、data、engine分离竞赛项目迭代速度极快你可能一天要跑十几个实验。如果源码结构混乱经常会出现“改了一个参数、忘了另一个参数”“用了旧checkpoint但代码已经更新”这类问题。到最后不光手忙脚乱还会让你的实验结果没有可比性。我建议竞赛源码一定要按模块划分好目录结构可以参考下面这个competition_repo/ ├── config.py # 所有超参数和路径集中管理 ├── data/ │ ├── __init__.py │ ├── dataset.py # Dataset类定义、数据加载逻辑 │ └── transforms.py # 训练增强、验证增强 ├── models/ │ ├── __init__.py │ ├── backbones.py # Backbone构建 │ └── heads.py # 分类/检测/分割头 ├── engine/ │ ├── train.py # 单epoch训练逻辑 │ ├── validate.py # 验证逻辑 │ ├── infer.py # 推理逻辑 │ ├── ema.py # EMA模块 │ └── losses.py # 损失函数定义 ├── utils/ │ ├── logger.py # 日志记录 │ ├── seeds.py # 随机种子设置 │ └── metrics.py # 评价指标计算 ├── checkpoints/ # 模型保存目录 ├── scripts/ │ ├── train.sh │ └── predict.sh └── requirements.txt很多人觉得文件多很冗余但竞赛到后期你会感谢这种冗余。config.py集中管理超参数意味着你每次实验只需要修改一个文件可以在启动脚本里把当前配置存成日志快照这样复盘的时候能精确知道这一轮实验结果对应的配置是什么。保证“训练脚本只关心梯度下降、推理脚本只关心数据读出和前向”这种职责分离非常重要。训练和推理解耦之后你可以放心地在推理脚本里替换融合策略、后处理逻辑而不会影响训练代码的稳定性。5.2 训练与推理解耦checkpoint别只存权重我在迭代过程中最反感的错误之一就是checkpoint里只存了模型的state_dict。训练到一半环境崩了想从断点续训结果优化器状态、学习率调度器状态全没了只能从头开始跑。保存checkpoint时至少要保存这些信息torch.save({ epoch: epoch, model_state_dict: model.state_dict(), optimizer_state_dict: optimizer.state_dict(), scheduler_state_dict: scheduler.state_dict(), ema_state_dict: ema.state_dict() if ema else None, scaler_state_dict: scaler.state_dict() if scaler else None, best_metric: best_metric, config: config, }, save_path)这样你可以从任意中断点恢复训练。config写进checkpoint是很多人忽略的一点它可以确保你加载模型时能回溯这个模型是在什么配置下训练出来的。如果哪天你改模型结构之后用了旧checkpoint还能发现配置对不上而不是稀里糊涂地继续训练。另外训练和推理时加载模型的方式一定要保持一致。有些同学训练时用了model nn.DataParallel(model)保存时直接保存了module前缀的state_dict推理时没有DataParallel加载模型就报key不匹配的错。解决方法是保存根模型参数# 保存 torch.save(model.module.state_dict() if hasattr(model, module) else model.state_dict(), path)5.3 日志、可视化与实验管理的经验竞赛里每天的实验量很大建议从一开始就用日志把每次实验的参数配置、验证结果记录下来。TensorBoard或WandB都可以选一个你用得顺手的即可。我自己偏爱轻量方案把每次实验的配置以JSON形式存一份再把关键指标打印到控制台同时写进文本日志。这样做后期的对比分析会非常方便。每一条实验记录至少要有Backbone名称、输入分辨率、增强策略名、优化器名、学习率、batch size、epoch数、训练轮次、验证指标、ema是否开启、融合方式、提交分数。有了这套记录你才能回答“某个改动到底涨了多少”这个问题。如果没有实验管理你根本分不清楚分数上涨是因为换了模型还是因为多跑了几轮还是因为调整了学习率。这样就算最后出成绩你也没法总结出可复用的经验。6. 竞赛踩坑实录那些反复出现的经典问题6.1 精度上不去的排查链路如果训练过程中发现精度不涨不要盲目换模型、加数据。我建议按下面的顺序排查第一步看loss是否在下降。如果loss都降不下去问题大概率在数据加载、标签、优化器或学习率。先把增强组件逐个关掉用原始数据跑一个stage来测试。第二步用一小部分训练集比如500张训练看能否过拟合。如果连小样本都过拟合不了说明模型容量或优化器有问题。如果小样本能过拟合但全量训练集上过拟合不了那可能是数据分布或增强策略问题。第三步检查验证集标签是否和训练集标签一致。听起来很蠢但真的发生过验证集标签错位、类别编号不一致、图像文件名和标签对不上。这些bug不会报错只会让你看到“loss很低但指标很差”的诡异现象。第四步检查你的强增强是否在评估时也生效了。有一种很低级的错误是transforms里用了同一个增强对象同时给训练和验证结果验证时还在做CutMix验证分数自然崩坏。务必把训练增强和验证增强分开写并在验证流程里只保留归一化和resize。6.2 显存溢出与训练过程不稳定的解法显存溢出OOM是新手最容易遇到的坎之一。最常见的解法有三个梯度累积、混合精度、减小batch。梯度累积的思路是把一个大的有效batch拆成几个小batch前向传播计算结果后保留梯度累积好几次再更新一次参数。代码写法很简单for i, (images, labels) in enumerate(train_loader): loss compute_loss(model, images, labels) loss loss / accum_steps loss.backward() if (i 1) % accum_steps 0: optimizer.step() scheduler.step() optimizer.zero_grad()混合精度用PyTorch的torch.cuda.amp即可几行代码就能把显存占用降一半同时训练速度也可能提升。需要注意混合精度下的loss scaling推荐直接用torch.cuda.amp.GradScaler不要自己手写缩放这里手写容易出错。训练过程不稳定比如loss突然变NaN通常有几个原因学习率太大、数据里有异常值、混合精度下梯度溢出。解决顺序是先用纯FP32训练看看是否还NaN如果正常再开混合精度并调高init_scale如果还不行降低学习率并给梯度加裁剪torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0)。6.3 模型保存加载与随机种子那些坑说到随机种子很多比赛平台对复现性要求很高。如果比赛以随机种子作为反馈分数的独立机制那么你的源码必须能保证相同种子下结果完全一致。不要只设torch.manual_seedPyTorch的CUDNN后端也有随机性相关开关import random import numpy as np import torch def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) torch.backends.cudnn.deterministic True torch.backends.cudnn.benchmark False需要知道的一点是开启cudnn.deterministic会在某些卷积实现下显著降低训练速度。所以如果你不追求完全复现可以保持benchmarkTrue来加速训练但如果要复现就必须关掉。还有一个坑是DDP分布式数据并行训练时每个进程里的数据采样顺序可能与预期不同。如果你的推理脚本自己实现了一个预测循环而不是直接用训练时的DistributedSampler就会导致验证集和训练集的数据乱序最终OOF预测和真值对不上。这个错误非常隐蔽我建议你在做完OOF预测后一定要打印几行样本检查一下文件名和标签是否对齐。最后再多说一句加载预训练权重时strict参数要谨慎。如果你只是把Backbone替换成新结构加载官方权重通常用strictTrue没问题。但如果你在Backbone后面接了一个全新的Head那么加载时就要设置strictFalse并只加载匹配的key。这里容易出现的坑是“加载权重时报错说缺了xx层”你顺手设成strictFalse结果中间层key拼写错误也被忽略掉了网络用随机初始化的层在跑Precision一路跳水。在使用百度云深度学习竞赛的Python源码时如果你是在多个预训练权重里做迁移强烈建议自己写一个key映射检查函数把预训练权重key和新模型的key做个交集确认覆盖到的层是你预期的层再开始训练。这一点在换模型名称或修改网络结构后尤其关键绝对不要想当然。本文还有配套的精品资源点击获取