1. 街景字符识别赛题到底在考什么
第一次接触这个赛题的人,很容易把它当成普通的OCR任务来处理,觉得无非就是检测加识别,套个开源框架跑一遍就完事了。但真正打过几轮之后你会发现,这个赛题的设计其实相当刁钻,它考察的东西远比"能不能识别出字符"要深得多。
1.1 从任务本质说起
街景字符识别,英文通常叫Street View Text Recognition,核心场景是:给定一张在街道上拍摄的自然场景图片,图片中可能包含一个或多个由数字和字母组成的文本串(比如门牌号、路牌编号、商铺招牌上的编号等),要求算法定位出文本区域并识别出具体的字符内容。
这个任务之所以难,难在"自然场景"四个字。跟扫描文档不同,街景图片里的文字会受到各种干扰:光照不均、透视变形、遮挡、模糊、字体多样、背景复杂。你在实验室里用印刷体训练出来的模型,直接拿到街景数据上跑,效果往往惨不忍睹。
这个赛题通常给出的数据格式是:每张图片对应一个JSON标注文件,里面包含该图片中所有文本区域的边界框坐标和对应的文本内容。评估指标一般是准确率,也就是完全识别正确的样本占总样本的比例。注意,是"完全正确",一个字符错了整张图就算错,这对精度的要求非常高。
1.2 为什么值得认真做
很多人觉得学习赛嘛,随便跑跑就行了。但我个人的经验是,这个赛题麻雀虽小五脏俱全,它几乎涵盖了工业级OCR项目的完整流程:数据探索、标注解析、模型选型、训练调优、后处理、结果集成。你在这个赛题上踩过的坑,在实际项目中一个都不会少。
而且这个赛题的另一个好处是数据量适中,单卡就能跑,不需要动辄八卡起步。对于想入门计算机视觉实战的人来说,性价比极高。
1.3 整体技术路线概览
解决这个赛题,主流思路无非两条:一是检测加识别的两阶段方案,二是端到端的单阶段方案。两阶段方案就是先用检测模型(比如DBNet、EAST)把文本区域框出来,再用识别模型(比如CRNN+CTC、SVTR)对每个框做字符识别。端到端方案则是用一个模型直接输出文本内容,比如基于Transformer的架构。
两种方案各有优劣,后面我会详细拆解。但不管选哪条路,有几个核心环节是绕不开的:数据预处理、数据增强、模型选择、损失函数设计、学习率调度、后处理策略。每一个环节都有讲究,每一个环节都可能成为你上分的瓶颈或者突破口。
2. 数据理解与预处理的关键细节
我见过太多人一上来就开始搭模型,结果训练了半天loss不降,回头一看数据都没搞清楚。这个赛题的数据有几个容易踩坑的地方,我逐一拆解。
2.1 标注格式解析与常见陷阱
赛题给出的标注通常是JSON格式,每张图对应一个标注项。标注里包含图片文件名、文本区域的四个顶点坐标、以及对应的文本内容。看起来很简单对吧?但实际操作中你会遇到这些问题:
第一,标注框可能是倾斜的。四个顶点不一定构成一个正矩形,可能是任意四边形。如果你直接用外接正矩形去裁剪,会引入大量背景噪声,影响识别精度。
第二,有些图片中文本区域非常小。小到你可能在可视化的时候都看不清。这类样本在训练中容易被模型忽略,但在评估时又确实存在,需要特别处理。
第三,标注中可能存在空文本或者特殊字符。你需要确认字符集的范围,是只包含数字,还是数字加字母,还是包含更多字符。字符集的大小直接影响分类头的设计。
提示:拿到数据后第一件事不是写模型,而是写一个可视化脚本,随机抽取几十张图,把标注框画上去,肉眼检查一遍。这一步花不了多少时间,但能帮你避免后面大量的返工。
2.2 数据分布分析与策略制定
可视化之后,你需要做几个统计:文本长度的分布、文本区域大小的分布、字符类别的分布、图片分辨率的分布。这些统计直接决定了你后续的策略。
比如,如果文本长度集中在4到6个字符,那你用固定长度的识别头就够了。如果长度变化很大,从1到20都有,那你就需要考虑CTC或者Attention这类能处理变长序列的解码方式。
再比如,如果小目标占比很高,那你在检测阶段就需要特别关注多尺度特征融合,或者在数据增强时注意不要过度缩小图片。
我当时的做法是画了几张分布图,发现文本长度主要集中在5到8之间,但有一小部分特别长的样本。针对这种情况,我最终选择了CTC解码,因为它对变长序列的处理更加自然。
2.3 数据增强的取舍与实操
数据增强是上分的关键手段之一,但不是越多越好。你需要根据赛题数据的特点来选择增强方式。
对于街景字符识别,我推荐以下几种增强:
- 随机旋转:模拟拍摄角度变化,但角度不要太大,一般控制在正负10度以内
- 随机缩放:模拟不同距离拍摄的效果
- 颜色抖动:调整亮度、对比度、饱和度,模拟不同光照条件
- 随机裁剪:注意裁剪后要保证文本区域完整
- 模糊处理:模拟运动模糊或失焦
不推荐的增强:翻转(水平翻转会让文字变成镜像,除非你的识别模型能处理镜像文字,否则不要用)、大角度旋转(超过15度后文字可读性急剧下降)。
这里有个细节很多人忽略:增强操作要和标注框同步变换。如果你只对图片做了旋转,标注框没跟着转,那训练数据就是错的。建议使用支持同步变换的增强库,或者自己写变换逻辑时格外小心。
3. 模型选型与方案对比
这是整个项目最核心的决策点。选错了方向,后面再怎么调优都是事倍功半。
3.1 两阶段方案:检测加识别
两阶段方案是最经典也最稳妥的路线。检测阶段负责定位文本区域,识别阶段负责读取字符内容。
检测模型选择:DBNet是目前最主流的选择,它的优势在于可微分二值化,能端到端训练,而且对不规则形状的文本区域处理得比较好。EAST也不错,速度更快,但对长文本的检测效果稍逊。如果你的数据中文本区域大多是水平矩形,那用EAST就够了;如果倾斜和弯曲的比较多,DBNet更合适。
识别模型选择:CRNN+CTC是经典组合,结构简单,训练稳定,对变长文本友好。SVTR是近几年比较新的方案,基于Transformer架构,精度更高但训练成本也更大。如果你追求精度且算力充足,可以试试SVTR;如果追求性价比和稳定性,CRNN+CTC是不会错的选择。
两阶段方案的优点是模块化,每个阶段可以独立优化,出了问题也容易定位。缺点是检测和识别之间的误差会累积,检测框不准会直接影响识别结果。
3.2 端到端方案:一步到位
端到端方案试图用一个模型直接完成检测和识别,省去中间环节。代表性的方法有基于Transformer的架构,比如把检测和识别统一到一个序列生成框架里。
这种方案的理论上限更高,因为它可以联合优化检测和识别,避免误差累积。但实际落地时有几个问题:训练数据需求更大、训练时间更长、调参更困难。对于学习赛这种数据量有限的场景,端到端方案未必比两阶段方案效果好。
我个人的建议是:如果你是第一次做这个赛题,先从两阶段方案入手,把baseline跑通,再考虑要不要换端到端。
3.3 方案对比与选择建议
| 对比维度 | 两阶段方案 | 端到端方案 |
|---|---|---|
| 实现难度 | 中等 | 较高 |
| 训练成本 | 较低 | 较高 |
| 精度上限 | 高 | 理论上更高 |
| 调试难度 | 低(模块化) | 高(耦合性强) |
| 适合人群 | 入门到进阶 | 进阶到高级 |
| 数据需求 | 中等 | 大 |
注意:不要盲目追求最新最复杂的方案。在数据量有限的情况下,一个调优充分的CRNN往往比一个没调好的Transformer效果更好。
4. 训练调优与上分技巧实录
这部分是我踩坑最多、也收获最大的环节。很多技巧看起来不起眼,但实际效果非常明显。
4.1 学习率调度与优化器选择
学习率是训练中最敏感的超参数。我试过固定学习率、余弦退火、阶梯下降、OneCycle等多种策略,最终发现对于这个赛题,余弦退火配合热重启效果最稳定。
具体设置:初始学习率设为1e-3,用AdamW优化器,权重衰减设1e-4。训练过程中每5个epoch做一次余弦退火,最低降到1e-6。如果发现loss震荡,可以适当降低初始学习率。
另外一个技巧是warmup。前500到1000个step用线性增长的方式把学习率从很小的值升到初始值,能有效避免训练初期的震荡。这个技巧在Transformer类模型上尤其重要,但对CNN模型也有帮助。
4.2 损失函数设计与调优
识别阶段最常用的损失函数是CTC Loss。它的好处是不需要对齐标注,直接输入序列和标签就能算loss。但CTC有几个需要注意的地方:
第一,CTC要求输入序列长度大于等于标签长度。如果你的文本区域裁剪得太窄,特征图的时间步数不够,CTC会报错。解决办法是保证裁剪后的图像有足够的宽度,或者在模型里加一个自适应池化层。
第二,CTC容易出现"blank"占主导的情况,导致模型倾向于输出空序列。可以通过调整blank的权重或者使用focal loss来缓解。
如果你用的是Attention解码,那损失函数就是交叉熵。这时候需要注意标签平滑,一般设0.1就够了,太高反而会损害精度。
4.3 模型集成与后处理策略
单模型的天花板是有限的,想要进一步上分,模型集成是必经之路。常用的集成方式有:
- 权重平均:把多个epoch的模型权重取平均,简单有效
- 多模型投票:训练多个不同初始化的模型,推理时取多数投票
- TTA(测试时增强):对测试图片做多种变换,分别推理后融合结果
后处理方面,有几个实用技巧:
第一,置信度过滤。对于识别置信度低于阈值的样本,可以考虑用语言模型或者规则进行纠正。比如门牌号通常都是数字,如果识别结果里出现了字母,大概率是错的。
第二,长度约束。如果你知道文本长度的大致分布,可以对识别结果做长度约束,过滤掉明显不合理的输出。
第三,字符集约束。如果赛题明确说明只包含数字,那识别结果里出现字母就可以直接判定为错误,需要重新解码。
4.4 常见问题速查表
| 问题现象 | 可能原因 | 解决思路 |
|---|---|---|
| loss不下降 | 学习率过大/过小 | 调整学习率,加warmup |
| loss震荡严重 | batch size太小 | 增大batch size或梯度累积 |
| 过拟合 | 数据量不足/模型太大 | 加数据增强,加正则化,减小模型 |
| 识别结果重复字符 | CTC解码问题 | 检查blank标签设置 |
| 小目标识别差 | 特征图分辨率不够 | 加FPN,用更高分辨率输入 |
| 检测框不准 | 回归损失权重不合理 | 调整损失权重,加IoU损失 |
5. 实操流程完整复盘
说了这么多理论,最后我把整个实操流程串一遍,方便你直接照着做。
5.1 环境搭建与数据准备
环境方面,Python 3.8以上,PyTorch 1.10以上,另外需要OpenCV、NumPy、Pandas、Matplotlib这些基础库。如果要用DBNet,可以装一个开源的OCR工具箱,省去自己写检测模型的麻烦。
数据准备阶段,先把标注解析成统一的格式,建议用一个DataFrame来管理,包含图片路径、文本框坐标、文本内容、文本长度等字段。然后划分训练集和验证集,比例大概8比2。注意要按文本长度分层抽样,保证验证集的分布和训练集一致。
5.2 检测模型训练要点
如果选择两阶段方案,先训练检测模型。DBNet的训练相对简单,主要注意几点:输入分辨率不要太小,建议至少736x736;学习率用1e-3配合余弦退火;训练epoch数一般50到100就够了。
训练完成后,在验证集上评估检测的召回率和精确率。如果召回率偏低,说明有些文本区域没检测到,可以降低二值化阈值;如果精确率偏低,说明误检太多,可以提高阈值。
5.3 识别模型训练要点
识别模型的训练是重头戏。输入是检测框裁剪出来的文本区域图像,统一缩放到高度32或48,宽度按比例缩放。标签就是文本内容对应的字符索引序列。
训练时注意几点:batch size尽量大,至少64以上;学习率用1e-3配合warmup;CTC Loss的blank标签设为0;训练epoch数一般100到200。
如果验证集准确率停滞不前,可以尝试以下操作:增大输入分辨率、加深网络、加Attention机制、换用更强的backbone。
5.4 推理与提交
推理阶段,先把检测模型跑一遍,得到所有文本框,然后对每个框做识别,最后按赛题要求的格式输出结果。注意处理边界情况:没有检测到文本框的图片、识别结果为空的情况、置信度过低的情况。
提交前一定要在本地做一次完整的验证,确保格式正确、没有遗漏、没有多余的字符。
6. 几个容易被忽略的提分细节
最后分享几个我在实战中总结的、文档里不会写的细节。
第一个是数据清洗。赛题数据里难免有一些标注错误或者模糊不清的样本,这些样本留着只会干扰训练。我的做法是先用一个baseline模型跑一遍训练集,把loss特别高的样本挑出来人工检查,确认是标注问题的就直接剔除。
第二个是字符集顺序。字符集的索引顺序会影响模型的收敛速度。建议把出现频率高的字符放在前面,blank放在0的位置。这个细节看起来微不足道,但实测能加快收敛。
第三个是验证集的选择。不要随便切一部分训练集当验证集,要确保验证集的分布和测试集尽可能一致。如果测试集里长文本比较多,验证集也要相应调整。
第四个是模型保存策略。不要只保存最后一个epoch的模型,要保存验证集上表现最好的那个。另外建议保存多个checkpoint,方便后续做模型集成。
第五个是推理时的batch处理。推理时把图片按宽度排序后分批,能减少padding,提高推理速度。这个技巧在图片数量多的时候效果很明显。
提示:以上所有技巧都不是孤立的,需要根据你的实际情况组合使用。不要一次性把所有技巧都用上,那样出了问题你都不知道是哪个环节导致的。建议每次只改一个变量,观察效果后再决定下一步。