ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

卷积神经网络小样本图像识别:原型网络与PyTorch实战

卷积神经网络小样本图像识别:原型网络与PyTorch实战 简介面向深度学习与机器学习方向的研究者、高校学生及需完成数据建模任务的工程人员这份PDF文献聚焦小样本条件下的图像识别难题提出以卷积神经网络作为特征提取器的完整方法路径。内容围绕数据增强变换扩充样本、源预训练模型迁移、层冻结与模型微调等关键环节展开并配有实验对比说明该方法在小规模数据集上的准确率与鲁棒性表现适合具备一定CNN基础、需要撰写论文或搭建识别模型的读者参考。资源包为单个PDF文件压缩包约4.75MB即论文全文含摘要、引言、方法设计、实验设置与结果分析、结论及参考文献可完整阅读技术细节与公式推导。目前已有258人学习下载。读者可从中获取小样本识别的技术路线、迁移学习与微调的参数策略、层冻结防过拟合思路以及数据处理与实验设计的可复用经验便于复现与二次改进。1. 小样本图像识别为什么不能直接套用常规 CNN 训练在工业质检、遥感解译和中药饮片分拣里常见局面是新出现的缺陷或新类别只有 5 到 20 张标注图。此时若把 ResNet 从头训到收敛测试准确率往往还不如拿预训练卷积特征加最近邻。原因不复杂卷积核参数量远大于样本能提供的约束网络会先记住训练图里的背景纹理而不是类间可迁移的形状差异。小样本图像识别解决的就是这件事在 N-way K-shot 设定下每个任务只给 N 个类别、每类 K 张带标签样本模型要对查询图给出类别判断。它适合已经理解 cnn卷积神经网络 基本结构、又需要把图像识别落到标注昂贵场景的工程师也适合做 SAR 图像识别、细小缺陷分类、中药饮片图像识别模型这类类间差异细、样本难扩增的任务。2. 卷积神经网络做小样本识别的三种建模范式围绕卷积神经网络做小样本识别主流做法落在三条线上迁移学习微调、度量学习、元学习。它们不是互斥关系而是标注预算和上线方式不同的选择。标注只有几张图、类别还会持续增加时度量学习通常更省事已有几百张标注、但类别固定时微调一个预训练骨干往往最直接只有任务分布清晰、训练类足够多时才值得上元学习。2.1 微调与特征提取迁移学习为什么常被低估预训练卷积核学到的是边缘、纹理、部件组合这些特征跨域可迁移。K 很小时先把骨干冻结只训练一个线性分类头学习率取 1e-3当每类样本超过 20 张再解冻最后两个阶段用 1e-4 到 3e-4 做微调。这里最容易踩的坑是 BatchNorm小 batch 下用少量样本更新 running 统计会让训练和评估分布错位。import torch, torchvision from torch import nn # 加载预训练骨干替换最后的全连接层 backbone torchvision.models.resnet18(weightstorchvision.models.ResNet18_Weights.DEFAULT) for p in backbone.parameters(): p.requires_grad False # 先冻结全部卷积层 backbone.fc nn.Linear(backbone.fc.in_features, n_way) # 只训分类头 # 小 batch 时冻结 BN 的 running 统计避免用少量样本更新方差 for m in backbone.modules(): if isinstance(m, nn.BatchNorm2d): m.eval()上面代码把可训练参数从千万级压到几千级。m.eval()让 BN 沿用预训练统计量而不是被 5 张图带偏。优化器用 Adamweight decay 设 1e-4训练 50 到 100 个 epoch 即可观察趋势。若验证集准确率在 20 个 epoch 后不再上升就把最后两个 stage 解冻并把学习率降到 1e-4。2.2 度量学习与原型网络的小样本图像识别实现度量学习不训练固定分类头而是学一个嵌入空间让同类样本靠近、异类样本远离。原型网络是最小可用的实现每个类的原型等于该类别 support 嵌入的均值query 离哪个原型近就归为哪类。新类到来时只需重新算原型不用重训整个网络这一点对“类别持续新增”的图像识别场景很关键。import torch import torch.nn.functional as F def prototypical_loss(support, query, n_way, k_shot, q_query): # support: [n_way*k_shot, C, H, W], query: [n_way*q_query, C, H, W] z_s model(support) # 嵌入 z_q model(query) z_s F.normalize(z_s, dim1) # 特征做 L2 归一化 z_q F.normalize(z_q, dim1) z_s z_s.view(n_way, k_shot, -1) protos z_s.mean(dim1) # 每类原型 dist torch.cdist(z_q, protos) # 欧氏距离矩阵 logits -dist # 距离越小得分越高 target torch.arange(n_way).repeat_interleave(q_query) return F.cross_entropy(logits, target)F.normalize把嵌入压到单位球面距离和温度更可控。torch.cdist返回[n_way*q_query, n_way]的距离矩阵取负后交给交叉熵。若换余弦距离把dist换成1 - z_q protos.T即可。原型网络在 5-shot 下通常比同骨干的线性分类头更耐过拟合但 1-shot 时方差明显变大需要更多 episode 评估。2.3 元学习与情景式训练需要多少任务元学习把“训练”拆成内循环和外循环内循环在 support 上更新几步外循环在 query 上算元梯度。MAML 是典型代表匹配网络、关系网络也属于这一族。它的收益出现在任务分布清晰、训练类足够多、K 小到 1-shot 的时候代价是显存和时间成倍增加且对任务采样方式敏感。范式最小标注量训练成本新类扩展方式常见实现微调/特征提取每类 5 张以上低重训分类头ResNet18 线性层度量学习每类 5~20 张中只重算原型原型网络、匹配网络元学习每类 1~5 张高需要适配步骤MAML、关系网络选型时先问一句上线后新类会不会频繁增加会就优先度量学习不会且标注能到每类几十张就先用微调打基线。元学习更适合作为研究或离线训练方案而不是第一版工程实现。2.4 骨干网络选择LeNet-5、ResNet-18 与轻量 CNN 在 5-shot 下的取舍LeNet5卷积神经网络 参数量小适合 32×32 以下的灰度图但感受野和表达能力有限在纹理细的饮片、SAR 图像上容易欠拟合。ResNet-18 有成熟的预训练权重是 5-shot 的稳妥起点。若输入是 32×32 或 64×64可以把 stem 改成 3×3 卷积、去掉最大池化避免过早下采样。画卷积神经网络结构图时重点标出嵌入维度、池化位置和归一化层而不是堆叠方块。3. 用 PyTorch 搭一个可复现的 N-way K-shot 训练流程这一章把前面的原型网络落到可运行的训练流程。核心是三件事按 episode 采样、按 support/query 切分、用距离损失更新嵌入网络。训练集和测试集的类别必须分开测试类不能出现在训练类里否则指标没有意义。3.1 情景式数据集与 EpisodicSampler 的关键参数常规 DataLoader 按样本随机打乱不适合小样本训练。需要自己写一个 Sampler每个 episode 随机抽 N 个类每类抽 K 张 support 和 Q 张 query并把它们连续产出。import random from torch.utils.data import Sampler class EpisodicSampler(Sampler): def __init__(self, labels, n_way, k_shot, q_query, episodes): self.labels labels # 全量训练集的标签列表 self.n_way n_way self.k_shot k_shot self.q_query q_query self.episodes episodes self.classes list(set(labels)) def __iter__(self): for _ in range(self.episodes): chosen random.sample(self.classes, self.n_way) for c in chosen: idx [i for i, y in enumerate(self.labels) if y c] picked random.sample(idx, self.k_shot self.q_query) yield from picked def __len__(self): return self.episodes * self.n_way * (self.k_shot self.q_query)参数上n_way 常取 5 或 10k_shot 取 1 或 5q_query 取 5 到 15episodes 每个 epoch 取 500 到 2000。yield的顺序保证每个 episode 的样本连续因此 DataLoader 的batch_size要设成n_way*(k_shotq_query)并且shuffleFalse。否则一个 episode 的 support 和 query 会被混到别的任务里。注意Sampler 里用random.sample抽类训练类数量必须大于 n_way否则会直接报错。3.2 原型网络训练循环与损失函数有了 Sampler训练循环就只剩切分和反向传播。support 是每个 episode 的前n_way*k_shot张query 是剩下的部分。from torch.utils.data import DataLoader import torch.optim as optim loader DataLoader(dataset, batch_sizen_way * (k_shot q_query), samplersampler, shuffleFalse, num_workers4) optimizer optim.Adam(model.parameters(), lr1e-3, weight_decay1e-4) model.train() for episode_idx, batch in enumerate(loader): images, labels batch support images[:n_way * k_shot] query images[n_way * k_shot:] loss prototypical_loss(support, query, n_way, k_shot, q_query) optimizer.zero_grad() loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step()clip_grad_norm_把梯度范数限制在 1.0防止小样本 batch 带来的梯度尖峰。num_workers在磁盘图片多时设 4 到 8如果图片已全部读进内存设 0 更省进程开销。训练时每 200 个 episode 打一次平均损失若损失在 0.2 附近波动但验证准确率不涨优先检查 query 的类别顺序是否和target对齐。3.3 数据增强与正则化小样本下的参数表小样本场景里增强策略比网络深度更影响结果。下面这张表是原型网络在 5-shot 下的常用起点。项目建议值说明n_way5与部署时的类别数对齐k_shot1 或 51-shot 看极端情况5-shot 更常用q_query5~15评估时取 15 可降低方差学习率1e-3冻结骨干/1e-4微调Adamweight decay1e-4抑制嵌入空间塌缩dropout0.1~0.3放在嵌入层前数据增强随机裁剪、水平翻转、颜色抖动SAR 图像慎用颜色抖动增强只对 support 和 query 同时做不要把同一张图增强出多个副本放进 query否则评估会虚高。颜色抖动在中药饮片图像识别里有效因为不同批次光照差异大在 SAR 图像识别里则容易破坏后向散射强度关系只保留翻转和轻微平移更稳妥。4. 训练后的验证、排错与精度提升从 5-shot 到 1-shot小样本模型的验证不能只看一次采样。同一组权重换一批 episode 采样准确率可能相差十个百分点。原因在于每个 episode 的类别组合和样本组合都在变单次结果更像随机抽签。4.1 评估协议多次随机采样与置信区间评估时固定测试类集合但每次 episode 重新抽 support 和 query。跑 600 个 episode 后报告均值和标准差才能判断模型是否真的可用。torch.no_grad() def evaluate(model, dataset, n_way, k_shot, q_query, episodes600): model.eval() accs [] sampler EpisodicSampler(dataset.labels, n_way, k_shot, q_query, episodes) loader DataLoader(dataset, batch_sizen_way * (k_shot q_query), samplersampler, shuffleFalse) for batch in loader: images, _ batch support, query images[:n_way * k_shot], images[n_way * k_shot:] z_s F.normalize(model(support), dim1).view(n_way, k_shot, -1).mean(1) z_q F.normalize(model(query), dim1) pred torch.cdist(z_q, z_s).argmin(dim1) target torch.arange(n_way).repeat_interleave(q_query).to(pred) accs.append((pred target).float().mean()) acc torch.stack(accs) return acc.mean().item(), acc.std().item()返回的均值用于比较模型标准差用于判断波动。5-shot 下标准差通常小于 1-shot若 1-shot 的标准差超过 5 个百分点说明当前嵌入空间对单张 support 太敏感优先加 L2 归一化和更强增强而不是加深网络。4.2 常见失败模式过拟合、类不平衡、骨干冻结不当症状可能原因处理训练损失低验证准确率接近随机训练类与测试类重叠或采样泄漏检查类划分测试类不得出现在训练集5-shot 尚可1-shot 崩原型被单张异常图带偏加 L2 归一化、用多 episode 平均评估某些类准确率明显偏低类间纹理接近或样本不平衡按类重采样检查是否有重复图微调后反而变差BN 统计量被小 batch 破坏冻结 BN或改用 GroupNorm嵌入向量范数差异大未归一化距离被范数主导在距离计算前做F.normalize排错时先把 support 和 query 的图片可视化一遍。很多“模型不行”的案例实际是 query 里混进了 support 的增强副本或者文件名标签解析错位。4.3 提升技巧自监督预训练、特征归一化与微调策略标注少的时候自监督预训练是性价比最高的一步。在域内无标注图上跑 SimCLR 或 MoCo把学到的骨干拿来做原型网络通常比直接 ImageNet 预训练更贴合 SAR 或饮片纹理。特征归一化不要省L2 归一化后再配一个可学习的温度系数能让交叉熵的梯度更平滑。微调时若解冻最后阶段把骨干学习率设成分类头的十分之一并让 BN 保持 eval等验证准确率不再上升再考虑打开 BN。5. 把 CNN 小样本识别落到真实图像任务SAR、饮片与 OCR 场景的迁移技巧真实任务和学术数据集最大的差别在域差距。SAR 图像是单通道强度图直接复制成三通道送进 ImageNet 预训练骨干第一层卷积的响应会偏弱中药饮片图像则受光照、背景和摆放角度影响类间差异可能小于类内差异。迁移时不要先换大模型先把输入处理和增强对齐到目标域。5.1 域差距SAR 图像识别与中药饮片图像识别的输入处理场景通道处理增强策略关键风险SAR 图像识别单通道复制或按分位数拉伸翻转、轻微平移颜色抖动破坏散射关系中药饮片图像识别保持 RGB做白平衡颜色抖动、随机裁剪背景纹理成为捷径工业缺陷灰度或 RGB 均可旋转、对比度扰动缺陷区域被裁掉文档版式 OCR二值化后送 OCR 工具不做 CNN 增强固定版式用 tesseract.exe 一类工具更直接如果任务本身是固定版式文字提取tesseract.exe 图像识别说明书 里的流程反而更省事卷积神经网络小样本识别更适合类间差异在纹理、形状分布上的任务。把工具用在合适的边界上比强行统一到 CNN 更有效。5.2 上线时只更新原型一个 5-shot 的在线扩类技巧部署阶段最实用的做法是离线训练好嵌入网络上线后把每个类的 5 张 support 图过一遍网络L2 归一化后取均值存成类别原型库。新类到来时只补算新原型不重训骨干。torch.no_grad() def build_prototypes(model, class_images): # class_images: {class_id: [PIL.Image, ...]} model.eval() bank {} for cid, imgs in class_images.items(): tensor torch.stack([test_transform(im) for im in imgs]) z F.normalize(model(tensor), dim1).mean(dim0) bank[cid] F.normalize(z, dim0) # 原型也归一化 return bank查询时用F.normalize(model(x), dim1)与原型库做余弦相似度取最大值的类。若新类只有 1 张图先把这张图做 5 种轻微增强分别过网络后取平均原型比单张图直接当原型更抗噪。类别原型库建议持久化到本地并记录每个原型的样本数和更新时间方便后续排查某类为何突然误判。本文还有配套的精品资源点击获取
返回列表