先说一个我见过无数次的场景:模型跑起来了,准确率卡在百分之七八十,怎么调都上不去。老板说准确率低,你就去换网络、加层、加数据,忙活一周,曲线纹丝不动。问题通常不在网络,而是你根本没搞清楚“分类”这件事在工程里到底卡在哪一环。
这篇文章我从一个实际做图像分类项目的角度出发,把“用CNN实现高效分类”这件事从头到尾拆开讲。不仅讲卷积层怎么搭、损失函数怎么选,更会把数据清洗、类别不平衡、训练过程调参、评估指标设计这些决定准确率的关键环节逐一展开。适合刚入门深度学习、正在跑分类项目的工程师,也适合打算系统梳理分类流程的同学。文里的配置和经验都是我实际踩坑总结出来的,可以直接拿去用。
1. 先搞清楚问题:准确率低,到底是识别问题还是分类问题
很多人把“图像识别”和“图像分类”混成同一个词,结果修错了方向。严格说,图像识别是一个更大的流程,它包含了目标检测、图像分类、目标定位、图像分割等子任务。CNN分类只是其中一种:给一张图,输出它属于哪个预定义类别。如果你做的是“判断图片里有没有猫”,那是二分类;如果是“框出猫在哪里”,那是目标检测,不是单靠分类网络能解决的。
工程上更大的误区,是把准确率低归因于“识别不准”,却没有检查前面几个环节。分类系统的完整链路通常是:图像采集与解码,预处理与增强,特征提取,分类决策,评估与反馈。任何一个环节出问题,最终表现都是准确率下降,但你以为问题出在模型。
举一个很常见的例子。做视频图像识别的时候,很多人纠结视频解码要不要做,其实必须做。视频是编码压缩过的流式数据,不能直接喂给CNN,得先解码成帧,再按帧或抽帧策略交给模型。帧率、关键帧间隔、码率都会影响画面清晰度。摄像头画面本身又糊又暗,模型再强也白搭。反过来,如果原图质量很好,只是某个易混淆类别老分错,那问题往往在数据标注或类别定义上,跟解码无关。
我的建议是,遇到准确率问题,先别急着动模型。先把流程图上每一步走一遍,确认不是输入侧问题。对于纯结构化表格数据,比如鸢尾花分类、红酒品质分类这类任务,特征列已经是数字,用决策树、KNN、逻辑回归就足够了,完全没必要上CNN。CNN存在的意义,是当输入是原始像素、传统特征工程难以手工设计时,它自己能从数据里学特征。
因此开篇第一件事是正确定位问题:是分类任务本身设计有问题,是数据没送到位,还是决策边界不够清晰。定位准了,后面的大多数操作能少走弯路。
2. 搭建一个能跑的CNN基线:从卷积核到损失函数
把问题定位清楚之后,下一步是搭一个“有下限”的基线模型。我的经验是,第一版CNN不要上来就堆ResNet、EfficientNet这些复杂结构,先搭一个三层卷积加全连接的小网络,把数据跑通、把训练流程跑通,拿到一个有理有据的基准分数。这个分数能告诉你,在现有数据下模型到底能做到什么水平,后面所有优化才有参照。
2.1 卷积层到底在做什么
卷积层的核心思想是“局部感受野加权值共享”。一张224x224的RGB图,如果直接用全连接层,输入维度是224x224x3,光第一层就有十几万甚至上百万个权重,训练非常容易过拟合且速度极慢。卷积层只用一个尺寸很小的卷积核,比如3x3,滑过整张图像,同一组权重在不同位置复用,所以参数数量大幅减少。
这里有一个关键点很多人没想明白:卷积层的输出不是一张图,而是一组特征图。比如第一层用了32个3x3卷积核,输入是三通道图,那么输出就是32个通道的特征图。每一个卷积核相当于一个模板,有的负责检测边缘,有的检测纹理,有的负责颜色块。越往后的层,卷积核会把前面的特征组合成更高层的语义信息,比如眼睛、车轮、翅膀这些部件。
卷积输出的尺寸也一定要会算。假设输入尺寸是H,卷积核大小是K,填充是P,步长是S,那么输出尺寸是(H - K + 2P) / S + 1。很多人网络写完了,维度对不上,跑了个寂寞。尺寸计算是基本功,直接决定网络层与层能不能衔接上。
实际项目中我常用的做法是:每层卷积之后紧跟BatchNorm和ReLU,再接一层最大池化。BatchNorm能把数据分布拉回正常范围,让深层网络更容易训练,ReLU提供非线性,池化则降采样、压缩特征、增强平移不变性。这三件套几乎是现代CNN的标配。
2.2 直接能用的基线网络结构
我说一个足够简单、适应大部分中小数据集的基线结构,用PyTorch写大概长这样:
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), nn.Conv2d(64, 128, kernel_size=3, padding=1), nn.BatchNorm2d(128), nn.ReLU(inplace=True), nn.MaxPool2d(2), ) self.classifier = nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Linear(128, 256), nn.ReLU(inplace=True), nn.Dropout(0.5), nn.Linear(256, num_classes), ) def forward(self, x): return self.classifier(self.features(x))注意代码里用了一个非常关键的设计:AdaptiveAvgPool2d(1),不管输入图片是32x32还是224x224,最后都会统一池化成1x1的特征,再送进全连接层。这样网络对输入尺寸不敏感,后续换数据集会省掉大量维度计算的麻烦。全连接层之前的Dropout(0.5)也是常规操作,防止全连接层过拟合。
训练参数给一组比较稳妥的起点:
| 配置项 | 推荐起点 | 说明 |
|---|---|---|
| 输入尺寸 | 224x224或128x128 | 分辨率低则训练快,但会丢失细节 |
| Batch Size | 32或64 | 显存允许就尽量大一点 |
| 优化器 | Adam | 默认lr=1e-3,适用于起步 |
| 迭代轮数 | 30-60 | 配合EarlyStopping观察 |
| 学习率策略 | StepLR或CosineAnnealing | 中后期降lr才能收敛充分 |
这套结构在不少公开分类数据集上都能拿到一个尚可的基线。如果连这个基线都达不到预期,比如训练loss完全没下降,那基本可以排除“网络复杂度不够”这个因素,问题大概率在数据处理或者训练配置上。
2.3 损失函数为什么几乎都用交叉熵
图像分类的标配损失是交叉熵,不是均方误差。面试时经常有人被问这个问题,我解释一下。网络的最后一层通常是线性层输出每个类别的得分,也叫logits,然后接Softmax把这些得分变成概率分布。交叉熵衡量的是“预测概率分布”和“真实标签分布”之间的距离。
用MSE做分类损失有个很实际的问题:Softmax输出的概率是饱和的,在接近0或1的区域梯度极小,MSE算出来的梯度信号很弱,模型学得慢。交叉熵配合Softmax,在预测错误时能产生较大的梯度,让模型快速修正。形象点说,MSE是“差不多就行,离得远了也慢慢挪”,交叉熵是“错了就要立刻大力纠正”。分类任务要的是明确的离散决策,交叉熵和Softmax的组合非常配套,所以它成了几乎所有CNN分类模型的标准配置。
如果遇到类别不平衡,直接算标准交叉熵会偏向样本多的类。这时可以给loss加类别权重。PyTorch里CrossEntropyLoss自带weight参数,把少数类权重调大,多数类权重调小即可。这个操作比重采样数据简单,而且不用改动网络结构。
2.4 优化器选择:Adam起步,SGD冲刺
很多初学者一上来就选Adam,训练到中后期发现精度上不去。不是Adam不行,而是Adam的适应性学习率在训练后期容易产生波动,可能停在次优解附近。我的实践经验是:先用Adam把模型快速训练到接近收敛,得到一个较低的loss,再切换成带动量的SGD配合小的学习率继续训练十几个epoch,精度通常还能再涨1到3个百分点。
SGD加动量的配置一般为lr=0.01到0.1,momentum=0.9,weight_decay按需设1e-4。这个“先自适应后动量”的思路在多个项目里验证过,尤其在细粒度分类上效果明显。
3. 数据是最大的杠杆:让准确率真实提升的7个方向
模型结构决定的是表征能力的上限,数据决定的是这个上限到底能被利用多少。我做过的十几个分类项目里,纯调模型带来的收益通常在两三个点以内,而认真处理数据带来的提升往往有十几个点。数据问题是最先要被量化探测的。
3.1 先把类别不平衡补上,而不是调结构
类别不平衡是多分类任务最普遍的问题。拿垃圾分类举例,“塑料瓶”样本可能占了60%,“纽扣电池”只有0.1%。如果直接训练,模型学到的最优策略是见到什么都猜塑料瓶,准确率也能达到60%,看起来不错,实际毫无实用价值。
两个核心处理手段。第一,loss加权,给少数类更大权重,多数类更小权重,达到“就算样本少,错了也很疼”的效果;第二,重采样,对少数类做上采样(重复采样),对多数类做下采样。相比起来,loss加权更平滑,重采样更容易实现且通用。两类方法可以结合,上采样少数类的副本再配加权loss,效果不会差。
类别不平衡严重时,准确率这个指标本身也会失真,需要用加权F1分数或宏平均F1来评估。这一点后面章节细说。
3.2 数据增强要贴合物理世界的真实变化
数据增强的本质,是人为告诉模型“这些变换不应该改变类别”。比如,一张猫的照片水平翻转后仍然是猫,但旋转180度后可能就违背了物理世界里的正常视角。增强策略要贴近应用场景,工业质检里产品方向固定,不需要随机旋转;做街景识别,水平翻转很合理,垂直翻转则通常不合理。
实践时增强分成两类。一类是离线增强,批量对图片做变换后存成新文件,适合数据集比较小且需要手动检查的场合。另一类是在线增强,训练时随机做变换,每个epoch看到的是同一张图片的不同版本,数据多样性更丰富,一般推荐在PyTorch的DataLoader里用torchvision.transforms直接实现。
常用增强操作及适用场景整理如下:
| 增强方式 | 参数建议 | 适用场景 |
|---|---|---|
| RandomHorizontalFlip | p=0.5 | 通用物体、街景 |
| RandomRotation | 15度以内 | 文字、工业物料方向大致固定时尽量小 |
| ColorJitter | brightness=0.2, contrast=0.2 | 光照变化较大的室外数据 |
| RandomResizedCrop | scale=0.7到1.0 | 对象尺度变化明显的场景 |
| RandomErasing | p=0.2 | 遮挡类场景,可以提升鲁棒性 |
| Normalize | 按ImageNet均值方差 | 使用预训练模型的必备操作 |
这里最容易踩的坑是:增强只应该加在训练集,验证集和测试集不能做随机的旋转或裁剪。验证集代表真实上线时的数据分布,所有随机增强只会让验证结果失去可比性,造成选模型标准漂移。
3.3 标注噪声是准确率的天花板
分类数据集的标注是便宜外包最容易产生问题的地方,错标样本比例超过5%很常见。模型会把错标的内容当真理学,准确率自然上不去,而且你根据验证集选择模型还可能学习到噪声。
排查标注噪声最简单有效的办法,是用当前训练好的模型对训练集做一次预测,把所有预测错的样本打出来,找人对疑似错标项做二次人工审核。实际操作时我会重点看两类错样本:模型预测置信度很高的错标,极可能是标注错了;置信度很低但loss很大的样本,可能是难例,也可能是异常图,要分情况处理。
一句话总结:算法能学会的,只有数据里真正一致存在的规律。错标数据减到最低,是提升准确率最便宜的手段之一,同时也是很容易被忽视的工作。
4. 训练现场:把“不收敛”和“震荡”变成可定位问题
搭好网络和数据处理流程后,就该进入训练过程了。很多文章写训练一上来就甩一堆超参数,看似丰富,实际没有推导过程。我在这个部分把训练现场会遇到的典型情况系统性拆解一下。
4.1 第一步永远是做小批次过拟合测试
正式开始全量训练之前,先取一小部分数据,比如128张图片,单独训练这个小子集。如果模型在这128张上都学不会,loss降不下来,那说明网络结构或数据管线肯定有bug,此时不要浪费时间跑全量。全量跑一次成本高,用小子集调试则能在一两分钟内暴露绝大多数问题。
如果整个小子集上loss能降到接近0,说明模型具备拟合能力,此时再去全量训练才有意义。这一步几乎能筛掉大部分“训练总是不收敛”的疑难杂症,也帮我节省过无数时间。
4.2 学会看loss曲线,比频繁调参更重要
训练开始后,loss曲线的形态要重点关注。最常见的三种问题和处理思路:
曲线一,loss完全不下降,甚至一直往上涨。先查学习率是否过大,常见原因是数据没归一化,像素值范围不一致,网络权重初始化失效。还可以检查标签是否从0开始连续编号,多分类softmax输出维度是否和类别数严格对齐。
曲线二,loss一直震荡,下不去。这在学习率偏大时很常见,更新步长太大,在最优点附近来回弹跳。处理方式是降低学习率,同时把batch size调大一点,让梯度估计更稳定。
曲线三,训练loss不断下降,但验证准确率到某个点开始下滑。这是典型的过拟合信号,模型把训练集背下来却丧失了泛化能力。此时增加增强强度、加大Dropout、或者引入更多数据是最好的应对。
实际训练中,我还会设置EarlyStopping监控验证集loss:连续10到15个epoch没有改善就早停,保存验证集上表现最好的那一版权重,而不是最后一轮权重。
4.3 学习率范围测试:用最少的试错找到最优初值
超参里学习率对训练影响最大,也是新手最易困惑的一项。与其逐个瞎试,我推荐一个被广泛验证的办法——学习率范围测试。选择较小初始学习率,比如1e-6,在每个batch结束后把学习率按指数增长,直到一个较大值如1,同时记录每个学习率对应的loss。你会得到一条曲线,loss先下降后上升,最低点附近的学习率就是适合当前任务和数据的学习率。
PyTorch里可以用torch.optim.lr_scheduler的LambdaLR或自写回调实现。这个操作的原理不复杂:学习率过小,loss几乎不动;学习率合适,loss稳定下降;过大,loss反而升高甚至发散。一次范围测试成本不高,却能给出清晰起点。
不同优化器对应的常用学习率区间:
| 优化器 | 常用初始学习率 | 备注 |
|---|---|---|
| SGD + Momentum | 0.01到0.1 | 配套momentum=0.9 |
| Adam | 1e-3左右 | 很多任务的默认起点 |
| AdamW | 1e-3左右 | 配合weight decay效果更稳 |
| RMSprop | 1e-3到1e-4 | 序列模型更常用 |
4.4 训练检查清单:少踩重复的坑
训练时间长了会积累一堆经验,我整理了一份每次开训练前都会过一遍的清单,分享出来:
- 标签是否从0开始,并且类别数=全连接层输出维度。
- 输入归一化是否在训练和推理一致应用。
- 数据集是否打乱,train/val是否完全没有身份重叠。
- 学习率策略是否做了下降规划。
- 是否记录了每个epoch的准确率和loss曲线,而不只盯着最终精度。
- 是否保存了最佳模型,而不是最后一代。
这份清单不用全记住,遇到问题时逐个对照筛查,大部分“玄学”问题最后都会落地成清单上的某一项。
5. 评估不是算个准确率就完事:用三张图看清模型真实能力
一个分类模型真正的价值,不是测试集准确率有多高,而是在真实使用场景中犯错的代价有多大。用户反馈“准确率低”的时候,很多时候你的整体准确率看起来并不低,但错误全部集中在少数重要类别上,体验极其糟糕。这时候必须有更细致的评估手段。
5.1 混淆矩阵是定位错误的起点
准确率是一个简单的数字,只会告诉你答对了多少,不会告诉你具体错在哪里。混淆矩阵则是一个多行多列的表格,每一行代表真实类别,每一列代表预测类别。矩阵对角线上的数字是正确分类的样本数,非对角线数字则暴露了混淆关系。
举个例子,在垃圾分类里,“玻璃瓶”被模型预测成“陶瓷碎片”的概率很高,说明这两类在外观上过于相似,需要补充更多这类难样本,或者考虑在特征层做细粒度区分。如果矩阵显示大量“无害垃圾”被错分到“可回收物”,可能问题不只在视觉上,而是原始数据里不同类别本身就有语义重叠。
用混淆矩阵定位错误之后,再去针对性采集数据和调结构,效率会高得多。这也是我每次训练结束后的第一件事。
5.2 准确率失效时,用精确率、召回率和F1说话
在二分类或多分类单一类别评估中,准确率具有明显误导性。做一个缺陷检测,产品里99.9%是良品,0.1%是缺陷品。模型只要全部判断为良品,准确率就是99.9%,看起来惊艳无比,实际一个缺陷都没检出,系统毫无价值。这时候要分开看两个指标:精确率(Precision)和召回率(Recall)。
精确率是预测为某类的样本中,真正属于该类别的比例,衡量的是“报出来的是否可靠”。召回率是某个真实类别的样本中,模型成功找回来的比例,衡量的是“该找的是否漏掉”。两者天然存在矛盾,提高召回率通常会降低精确率。实际业务中要结合错误代价去选。质检场景漏放一个次品损失很大,会把决策阈值调低以提升召回率;如果误杀太多良品会浪费产能,又会把阈值调高换精确率。
两个指标的调和版本F1,在类别不平衡时更有参考价值。我习惯同时看Macro F1(每个类别单独算F1再取平均)和类别分布,能准确反映少数类到底有没有被练好。
5.3 不同场景必须对应不同的成功标准
不同项目对指标的侧重差异其实很大。做手写数字识别,因为类别不多、数据集也相对干净,随便一套CNN都能跑到99%以上,此时准确率是合理的汇报指标。工业质检却要看缺陷类别检出率,漏检率通常以单品百万分之几为目标;新闻图片分类则需要平衡各话题的召回率,因为长尾小类可能才是运营重点。
OCR(光学字符识别)项目比较复杂。有些OCR场景还需要在检测框出来之后做文字行分类或文档分类,这时候更关注“整段文字是否被完整还原”,而不仅是单字符准确率。用Java做OCR集成的话,Tess4J是一个相对方便的选择,它把Tesseract封装成Java可调用API。但Tesseract的引擎对图像质量比较敏感,歪斜、反光、模糊都会明显拉低识别率,所以实际接入时必须先做图像矫正和预处理。
一句话总结:指标是为业务服务的。评估维度脱离具体业务场景,调出来的模型即使数字好看,上线后依然会“感觉准确率低”。
6. 当固定架构不够用:按应用场景调整网络设计
基线CNN能覆盖相当一部分分类需求,但真正到生产环境时,你会发现精度瓶颈、推理速度、未知类别等问题,不是单纯加深网络能解决的。一般从这几方面动刀。
6.1 要实时性,就得做轻量化设计
在移动端、嵌入式设备上做图像分类,模型大小和推理速度经常比准确率更关键。拿一个ResNet50做实时垃圾分类监控,帧率可能太慢。此时可以换MobileNet这类轻量网络,核心是用深度可分离卷积替代普通卷积。普通卷积对每个输入通道和输出通道都逐点做卷积,深度可分离卷积则先逐通道卷积,再用1x1点卷积把通道信息融合。参数量和计算量能下降好几倍。
量化是另一个手段。把模型权重从32位浮点数降到8位整数,模型体积变小,推理也能加速。我见过不少项目,在几乎没有精度损失的前提下把推理时间缩短一半以上,值得遇到性能瓶颈时优先考虑。
如果还想保留残差网络这类较强模型的效果,也可以用蒸馏思路:大网络当老师,教一个轻量学生网络。老师网络给出软标签(类别的概率分布),学生网络学着逼近它,比直接拿硬标签训练通常效果更好。
6.2 开集问题:模型要会承认“我不知道”
常规分类模型默认测试样本一定属于训练集里的某个类别。真实场景中,系统经常遇到训练时没见过的类别,此时模型还是会硬着头皮输出一个概率最大的已知类别。这就是开集问题,也称为开集识别。比如做一个工业零件分类系统,产线临时来了一个新型号零件,没有对应的训练样本,模型最好给出的结果是“未知”,而不是把它硬分到最像的旧型号里。
两种可行的判断方法。第一,观察Softmax输出的置信度,对置信度低于某个阈值的样本标记为“不确定”,拒识并转人工处理。第二,从特征层面入手,取全连接层之前的高维特征向量,计算它与各类别中心的距离。距离很远就认为是未知类别。距离法通常比直接阈值softmax更可靠,因为softmax输出的概率存在过分自信的问题,尤其当数据分布有偏移时。
对图像分类项目,我通常会预留一个“unknown”逻辑:在分类头的输出中,不把这部分当独立训练类,而是根据距离和置信度实时判断,交给下游流程决定是人工审核还是抛入待标注池。
6.3 细粒度、多模态检测识别:从全局走向局部
有些分类任务不仅要求“这是一只鸟”,还要分清楚是哪种鸟;不仅要识别物体,还要知道它在画面里的位置。纯分类网络预测整图是远远不够的,此时需要检测模型,例如把区域提议和分类结合的两阶段方法,或用融合卷积与Transformer的单阶段方法。
现在工业界很流行“CNN提取局部细节+Transformer建模全局关系”的融合检测方案。这一组合很符合应用场景:CNN擅长捕捉局部纹理、边缘与结构,Transformer通过自注意力机制联系图像不同区域的上下文。做抓取检测时,要让机械臂知道“抓取位置哪里更稳”,网络既要关注物体局部几何结构,又要理解它在整个工作台上的相对位置。只做全局分类分不出来,只关注局部又缺上下文,适度融合两者才能有更高的精度。
选择这类复杂结构时,不要为了追赶潮流硬上。我的准则是:先用可解释性强的朴素方案建立基准,再判断瓶颈到底在哪,最后才引入复杂的网络和更大规模的数据。
6.4 预训练模型迁移:最容易被低估的起点
如果你手头数据量不大,再精巧的结构也没用。数据量只有几千张时,与其从零训练一个ResNet,不如借助在ImageNet上预训练过的模型进行迁移学习,把网络之前学到的通用视觉特征迁移到新任务上。常见做法是替换网络最后一层全连接,使其输出类别数匹配当前任务,然后冻结前面所有层的权重,只训练最后的分类层。
如果数据量稍大,可以解冻靠后的一些卷积层,用更低的学习率一并微调。迁移学习能明显减少对数据量的依赖,收敛速度也快得多。这个做法的前提是预训练任务与新任务的数据分布有一定重合,比如都用自然图像。如果是医学影像、红外遥感这类特殊域,普通ImageNet预训练收益有限,那就优先考虑有没有同领域的预训练模型。
“图片分类最好用的模型”其实没有固定答案。在ImageNet几百万张图的大规模赛道里,Vision Transformer系列表现很强势;在普通中小型数据集上,ResNet50、EfficientNet、MobileNet仍然非常能打。挑模型时先看数据规模和侧重点,没有绝对最好,只有当前场景下最合适。
7. 部署和长期维护:让模型持续好用的基础设施
写完模型只是万里长征的第一步。模型上线后,准确率会随数据变化而衰减,图像来源、拍摄设备、环境光、人群分布一旦改变,你训练时见过的情况可能变得稀少。如果不做持续维护,再好的CNN也会慢慢变“傻”。
7.1 推理前后的预处理必须和训练完全一致
训练时用了归一化、resize、去均值,推理时也要严格沿同一套代码做处理。很多项目代码上线后,测试精度高,线上却大幅缩水,追溯到最后就是推理图像没有resize到模型输入尺寸,或者归一化参数不一致。这种问题一出现就极难排查,因为不是模型本身的问题,而是前后处理的细微偏差。
我习惯把预处理逻辑单独封装成一个公共函数,训练脚本和推理服务都统一引用。部署时再对测试集和线上数据的图像尺寸、均值、方差、通道顺序等做一遍一致性核对。这样做能避免大量莫名其妙的线上回落。
7.2 置信度分级+人工复核+回流训练形成闭环
模型面对新环境时,要学会“示弱”。部署阶段可以为每个预测结果输出置信度,把结果分到不同置信区间。高置信度区间自动处理,低置信度区间进入人工复核队列。人工复核结果会自动沉淀为新的标注数据,积累到一定量后剔除旧样本重新训练,如此形成数据回流闭环。
这一步对持续提升准确率非常关键。新环境中的数据模式和训练集差异巨大,回流训练是缓解分布漂移最直接有效的方式。引入新样本时保留一部分旧样本混合训练,避免灾难性遗忘——模型一旦只在新样本上重训,可能忘了老数据学到的特征,导致旧场景准确率骤降。
7.3 监控指标不要只看整体准确率
上线后的监控指标,要逐步拆细到类别、摄像头点位、时间段等维度。如果你的模型分类画面是不同场景的图片,夜里和白天、晴天和雨天,很可能模型表现差异巨大。整体准确率看不出这种波动,只有拆开来监控,才能精准知道是什么地方在恶化,才能有的放矢地补充数据。
我个人的体会是,分类模型维护的功夫,前期花在数据和评估体系上,后期花在以监控和回流为支撑的闭环机制上。CNN本身只负责把像素变成可靠的特征表达,准确率能不能一直高,终归取决于整个系统对数据质量与业务变化的响应能力。这也是工程师真正拉开差距的地方。