ARTICLE DETAIL

资讯详情

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

经典CNN在满文识别中的实战:从数据集构造到泛化陷阱

经典CNN在满文识别中的实战:从数据集构造到泛化陷阱 简介面向满文文字识别与计算机视觉研究者的图像分类资源以666类多字体满文单词图像为基础围绕AlexNet、VGG19、GoogLeNet三种经典深度网络给出完整的模型构建、训练和可视化分析过程。整个资料包约220.5MB共2000个文件其中jpg图像占据主体另有9个Python脚本用于数据加载与模型对比1份PPTX展示文稿汇总了实验结果并含简要说明文档结构清晰便于按需查阅。目前已有224人学习下载适合作为OCR方向课程设计或科研入门的参考样例。学习者可从脚本中看到数据预处理、网络微调和特征可视化的具体写法从PPTX中获取不同模型的准确率对比与可视化结果从而快速复现满文单词识别实验深入理解多模型迁移学习在少数民族文字识别任务中的实际表现。 去年年初接过一个公共文化数字化方向的图像识别需求馆藏有一批满文资料需要尽快转成可检索的文本。我原本以为这类拼音文字识别不算太难上手之后才发现满文这个任务的麻烦程度远超预期。满文是拼音文字一个词由多个音素字母横向连写而成字母在词首、词中、词尾的位置不同写法也会跟着变化视觉上很像阿拉伯文的连写形态。更棘手的是手上的数据是多种字体混合的——古籍刻本、铅印本、手写抄本、现代字库渲染都有字形风格差异极大。于是我们把问题拆成了最朴素的图像分类任务满文数据集666类中的多种字体单词图像分别用AlexNet、VGG19、GoogLeNet三个经典CNN去识别。这篇文章就复盘一下从数据集构造、模型选型到训练排错的完整过程尤其适合正在做低资源文字识别、冷门OCR或小样本图像分类的读者参考。1. 为什么拿三个“老牌CNN”去啃满文单词识别1.1 满文识别到底难在哪先说文字本身。满文属于音素文字系统常见说法是6个元音字母加22个辅音字母共28个基础音位。但这只是字母层面的数量落到字形上同一个字母在词首、词中、词尾往往有独立写法学界对满文字形变体的统计普遍认为超过130个。也就是说模型看到的不是28个简单符号而是130多种局部形状这些形状还要按词内顺序横向连写组合出成百上千种整体外形。满文还有个绰号叫“圈点满文”——大量字母之间的区分全靠一个圈或一个点的位置差异。比如某些辅音字母唯一区别是右侧有没有圈、圈点在哪个位置。这种级别的细节放到古籍扫描件里就是灾难纸张泛黄、笔画断裂、圈点模糊人眼有时候都得凑近看。数据里再混入手写体和印刷体同一个词的笔顺、连笔、笔画粗细又会继续放大差异。这也是我坚持把它当成细粒度图像分类问题而不是普通OCR来看待的原因。1.2 为什么偏偏选了这三个模型市面上比这三个更强的网络一抓一把ResNet、EfficientNet、Vision Transformer都能跑。但我做这个项目的选型逻辑很朴素第一这个方向缺可复现的baseline。查文献能发现少数民族文字识别论文里最常出现的就是AlexNet、VGG19、GoogLeNet这一代经典网络。用它们建立基线后续无论谁提出新方法都能放到同一套指标下比较不用各自凭空报数。第二实际部署环境限制了模型规模。馆藏单位通常只有普通办公电脑没有GPU集群。AlexNet和GoogLeNet的参数量都在可控范围训练和推理成本不高VGG19即使参数量很大也还能靠着显存勉强跑起来。第三数据量是硬约束。完整数据集只有8万多张还要分成666类类别多且分布不均。Transformer没有海量数据做预训练很难在这种规模下发挥优势硬迁移ImageNet上的ViT权重也无法撑起细粒度字形特征的学习。这三个网络的结构差异刚好覆盖了三种思路AlexNet是典型的“深度卷积全连接”奠基结构VGG19把卷积网络推到极深的堆叠路线GoogLeNet则在宽度和多尺度特征上做文章。用同一份满文数据集横向对比它们能一次性看清好几类问题。2. 数据集不是下载的是自己拼出来的2.1 666个类别怎么定项目启动时最尴尬的一件事没有现成的满文数据集。公开渠道能下载到的都是汉字或英文OCR数据集满文这块几乎是空白。我和团队只能自己造。类别选的是满汉词典和高频词表里出现频次靠前的666个词尽量覆盖名词、动词、数词、格助词等不同语法形态。每个类别的图像数并不均匀少的只有60来张多的能到260张整份数据加起来大约8.2万张长尾分布非常明显。这其实是低资源文字数据集的常态——常用词样本多冷门词样本少。如果动手之前没意识到这一点模型后面偏科是必然的。2.2 字体来源与图像预处理这批数据的“多种字体”主要来自四个渠道古籍刻本扫描、铅印出版物扫描、人工手写样本、现代TrueType满文字体渲染图。四种渠道的字形风格差异很大——刻本有木纹噪声铅印体笔画均匀手写体有连笔字库渲染最规整。原始图像预处理没有做太复杂的操作。先把彩色扫描图转灰度因为笔画颜色对文字识别几乎没有帮助然后做一次降噪去掉扫描产生的孤立噪点再做简单的对比度增强让笔画更突出。这里我刻意没有做全局二值化——二值化会把灰度抗锯齿信息直接丢掉笔画边缘在非标准字体下会变得毛糙反而影响后续识别。预处理最关键的一步是尺寸归一。满文单词是典型的横向排布图像大多是细长条高约64像素宽却可能到300甚至500像素。而AlexNet、VGG19、GoogLeNet的常见输入都是224×224正方形。最省事的办法是直接resize但那样单词会被压成矮胖形状圈点细节全糊。我最后采用“等比缩放白边padding”单词图像按比例缩放到合适大小周边补白边凑成224×224再送进网络。这样字形比例不丢模型也能正常吃标准输入。2.3 切分原则按字体切而不是随机切这是整份数据组织里我认为最重要的一次决策。很多人做图像分类时习惯直接随机切训练集和测试集但这个项目随机切会出大问题同一种字体的图像会同时出现在训练集和验证集里。由于字体风格特征太明显模型完全可能靠“这是铅印体”“这是手写体”这种浅层线索做出判断验证准确率虚高一到真实扫描场景就崩。所以训练集、验证集、测试集的划分是按字体来源做的比如字库渲染和部分铅印体进训练集手写体、刻本扫描和另一部分铅印体进验证集和测试集尽量保证测试集里有训练时没见过的字体组合。这个做法会让测试准确率比随机切分低不少但它才是评价泛化能力的正确方式。我后面专门留了一份随机切分的数据做对照结果非常有说服力实验部分会具体说。3. 三套网络的改动与训练配置3.1 灰度图像的首层通道问题torchvision里的预训练模型都是按RGB三通道设计的。满文图像只有单通道如果直接把灰度图复制成三通道送进去等于让网络用三倍显存去学完全相同的特征还会引入冗余。更稳妥的做法是把预训练首层卷积核从3通道融合成1通道。这里有个成熟的小技巧对ImageNet预训练权重首层卷积核形状是[输出通道数, 3, 卷积核高, 卷积核宽]直接对通道维求平均把RGB三个通道的卷积核合并成一个平均核偏置保持不变。代码量很小import torch from torchvision import models def fuse_first_conv(model): state model.state_dict() # AlexNet / VGG19 的首层在 features.0.weightGoogLeNet 在 conv1.weight weight_key next(k for k in state if k.startswith(features.0) or k.startswith(conv1.)) w state[weight_key] # [C, 3, K, K] fused w.mean(dim1, keepdimTrue) # [C, 1, K, K] state[weight_key] fused model.load_state_dict(state) return model这个方法比灰度图复制成三通道更省内存实测在满文数据上几乎没有精度损失。对AlexNet和VGG19改的是features[0]对GoogLeNet改的是conv1。改完首层后还需要把最后的全连接层替换成666类输出。3.2 三个模型的差异性在哪里模型主干卷积层数约参数量全连接层处理本项目测试集准确率AlexNet5约6000万三层全连接约62.8%VGG1916约1.43亿三层全连接约70.4%GoogLeNet22约670万全局平均池化约76.1%这张表能看出不少东西。VGG19的深度让它有更强的特征表达能力但它那三个全连接层占了绝大部分参数在8万张级别的数据上过拟合压力很大。AlexNet五层卷积结构最朴素训练最快但特征抽象程度明显不够。GoogLeNet用Inception模块在同一层里做多尺度卷积再用1×1卷积大幅降维参数总量只相当于VGG19的零头最后用全局平均池化替代全连接这种结构在低资源场景下几乎是为抗过拟合量身定做的。3.3 训练配置与数据增强运行环境是PyTorch 1.13单张RTX 3090。优化器用SGDmomentum0.9weight decay5e-4初始学习率0.01训练80个epoch前5个epoch做线性warmup后续跟余弦退火。batch size设128。验证集准确率连续15个epoch不提升就提前停止。数据增强我用RandomAffine但旋转角度只给±3°平移给0.05缩放0.9到1.1。满文圈点位置对角度极度敏感旋转超过5°基本就是在制造错误样本。我还以p0.25的概率加了RandomErasing模拟古籍扫描时局部笔画被污渍遮挡的情况。有一点要特别提醒不要对满文单词图像做随机裁剪增强。汉字那种方形单字可以随机裁但满文单词是横向连写的随机裁很容易把词的头部或尾部切掉几个不同词被裁完可能长得差不多模型越训越糊涂。GoogLeNet还有一个特殊细节torchvision默认加载的模型在训练阶段会输出主logits和两个辅助logits总损失按loss_main 0.3 × (loss_aux1 loss_aux2)计算。辅助分类器对这个数据集的梯度回传有明显正则作用开着比关掉整体高约1.5个点。但推理时一定要调model.eval()否则辅助分支还会参与计算白白浪费时间。4. 结果不是越高越好泛化才是4.1 按字体划分的真实成绩经过上面这些处理和训练配置三个模型在按字体划分的测试集上的表现以及随机切分对照组的成绩汇总如下模型按字体划分测试集准确率随机切分对照组准确率AlexNet62.8%95.2%VGG1970.4%96.7%GoogLeNet76.1%97.3%两组数据放在一起对比非常刺激。随机切分时三套模型都轻松上95%看起来好像“满文识别已经搞定了”。但按字体划分后AlexNet直接掉到62.8%说明之前高出的那一大部分准确率并不是真的认识满文单词而是认出了字体风格。这正验证了前面坚持按来源切分的必要性。GoogLeNet在这份数据上领先其他两个模型5个多点。我的判断有三层原因一是Inception结构用多种卷积核同时关注局部细节和整体轮廓对多字体带来的尺度变化更鲁棒二是1×1卷积降维配合全局平均池化参数少在8万张数据上不容易过拟合三是辅助分类器带来的隐式正则效果让梯度更平稳地回传。4.2 错误集中在哪里准确率之外更能说明问题的是混淆矩阵。我把所有误分类对拉出来看发现错误高度集中在几类情况。第一是字形相近的单词对。满文里有一批词区别只在词末字母的圈点位置。铅印体还算清楚刻本扫描一旦笔画断裂模型基本只能靠猜。第二是手写体表现明显更差。手写连笔会让字母边界糊在一起这三个模型对连续连笔的容忍度都不高。第三是长尾类别问题。样本数只有六七十张的冷门词准确率普遍比高频词低十几个百分点。我还特意用训练时完全没见过的字体做了次小测试。比如只用字库渲染图训练然后用刻本扫描图测试GoogLeNet准确率掉了将近20个点。这个结果不意外但它提醒我多字体混合识别本质上比单字体识别难上一个维度。真实场景里不可能只遇到训练过的字体这个损失必须从一开始就纳入预期。5. 训练过程中踩过的几个坑5.1 字体泄露最隐蔽的坑这个坑前面反复提但还是要单独说一遍因为它实在太隐蔽了。第一次跑实验时我在验证集上看到94%的准确率一度以为项目可以提前交差。后来把模型拿到另一批手写扫描件上测试准确率骤降到68%。复盘时才发现验证集随机切分后同一种字体同时出现在训练和验证里模型在验证时就是把“字形风格”当成了主要特征。按字体来源重新划分后准确率才回到真实水平。如果你的任务也是多来源、多风格的数据强烈建议先按来源分组再切分。宁可训练集看起来少一些也不要让评估结果自欺欺人。5.2 细长条图像被resize得面目全非满文单词图像是横长条。最初我图省事直接把短边resize成224×224长边被压扁结果三个模型全在55%左右徘徊。改成等比缩放加白边padding后准确率立刻提升。这里还可以再优化一步单词本身宽高比差别很大等比缩放后有的图实际占的面积很小白白浪费计算量。我后来在同一套流程里加了动态适配先统计所有训练图宽高比的分布把常见比例对应的缩放尺寸算出来让单词主体尽量多占像素其余空白继续padding。这一步对细长型文字的OCR类任务几乎是通用方案。5.3 全连接分类头成了过拟合重灾区AlexNet和VGG19最后的全连接层是为1000类ImageNet设计的改成666类后参数依然庞大。实验中发现VGG19的验证loss通常在40个epoch后开始反弹而GoogLeNet的验证loss能一路压到最后。后来我把AlexNet和VGG19的fc6维度从4096降到2048dropout从0.5提到0.6准确率反而小幅提升0.3到0.8个点训练时间也缩短了。这说明在中小规模数据集上模型容量不是越大越好。对小数据文字识别全局平均池化这类无参降维方案明显更省心。5.4 数据增强不是万能药我一度希望通过疯狂增强来弥补数据不足结果旋转加强到10°之后三个模型准确率集体下降——满文字母一旦带圈点转几度后圈点位置就可能和另一个字母的形态重合。这个教训让我明白对局部符号极其敏感的文字数据增强必须保守。可加的是轻微平移、缩放、擦除和噪声不可加的是大角度旋转和随意裁剪。做任何增强前最好先自己看一批增强后的图确认它看起来还是“那个单词”再决定是否上线。6. 如果继续往下做我会怎么走6.1 从分类到序列化识别词级分类只是baseline。满文是拼音文字一个单词的内部结构本质上是音素序列天然适合序列模型。后续真要落地更合理的方向是把单词的字母序列预测出来用CRNN或Transformer encoder-decoder做端到端识别而不是让网络从666类里硬选一个。分类只能处理固定词表序列生成能推到没有见过的词对真实古籍的开放词表适配性高得多。代价是标注成本上涨——每张图都要标字母级序列但这是技术路线上必然要迈的一步。6.2 合成数据是低资源文字识别的生命线如果让我重新做一个满文识别项目第一步不会急着找更多人工标注而是先把合成数据流水线搭起来。现在满文字库和输入法已经比较成熟可以用脚本批量渲染任意词表在不同字体、不同字号、不同噪声下的图像几个小时就能生成几十万张。合成数据虽然和真实扫描存在域差距但用来解决长尾类别样本不足的问题绰绰有余。跑通之后再配合少量真实数据做领域适应性价比极高。6.3 一点个人体会这个项目最大的收获不是“GoogLeNet赢了”而是让我确认了一件事在低资源文字识别里数据组织方式对最终结果的影响常常比模型结构更明显。按字体切分、等比padding、保守增强——每一项调整带来的收益都比单纯换网络更大。如果你也在做类似的文字图像分类别急着上大模型先把数据的来源、划分方式和预处理研究透。这几个点做好了哪怕只用GoogLeNet这种上一代网络也能在真实场景里扛住一阵子。如果后续算力和数据量都上来了再从这个强baseline出发往序列化模型和合成数据方向迭代路会顺很多。本文还有配套的精品资源点击获取
返回列表