档案馆里来了个新员工小王,任务是数字化一箱民国手写信件。他刚拿到第一封泛黄的信,纸上的字迹歪歪斜斜,有的地方墨水晕开,有些字挤在一起分不清边界。他要想办法把这张“图片”变成计算机里可检索的电子文字。这恰恰就是 CRNN 与 CTC 要解决的问题:让机器从一张随手拍的照片里,读懂一整行文字,而且不用告诉它每个字在哪个坐标。 CRNN 像小王的读信分工——先用眼睛看笔画(CNN 卷积特征提取),再用大脑按顺序读并记住上下文(RNN 循环序列建模),最后用笔把模糊不确定的读音转成完整句子(CTC 转录对齐)。三者串联成一条流水线,从图像输入直接到字符串输出,整个过程端到端可训练。这篇文章会借用小王的故事,一步步拆解 CRNN 的卷积特征提取、双向 LSTM 序列建模,以及 CTC 损失函数如何处理“变长对齐”这一核心难题,让你轻松掌握 OCR 文字识别背后的原理。
掌握 CRNN/CTC,你就掌握了高精度 OCR 引擎的核心引擎。从银行卡号识别、身份证信息抽取到票据报销自动化,这是 AI 视觉工程师最具落地价值的技能之一。
可视化演示
展示SGD、Momentum、Adam三种梯度下降优化器在损失曲面上的收敛轨迹,可视化不同优化策略对收敛速度和路径的影响。
本节配有交互动效(进入视口后自动加载,约 3KB)。
SGD随机梯度下降 橙色点为当前参数位置,沿负梯度方向更新 绿色点为损失函数最小值,即最优参数 黄色箭头指示梯度方向,椭圆线为等高线 θ ← θ - η · ∇L(θ) SGD每次沿负梯度方向更新,收敛稳定但速度较慢
CRNN整体架构
小王打开第一封信,没有急着盯某个字,而是先把整页纸扫一眼:抬头在哪里、正文有几行、落款是什么。他心里有了一张“总谱”,再逐行细读。CRNN 的设计也正是如此——先把整张图片过一遍,分工明确:眼睛负责看、大脑负责串、手负责写。
CRNN 的全称是 Convolutional Recurrent Neural Network(卷积循环神经网络),它把文字识别拆成三个串联模块。第一个模块是 CNN,把图像当作一张图,用卷积核逐层扫描,提取从笔画到字形的高层视觉特征;第二个模块是 RNN,将 CNN 输出的特征序列按时间顺序逐个读取,保留前后文记忆;第三个模块是 CTC,负责把 RNN 输出的“每一帧的概率分布”翻译成最终的字符串。整个过程从图像进、文本出,无需人工分割字符。
这种设计的巧妙之处在于“端到端”:你不必告诉模型每个字符在图片里的精确坐标,只要给它整张文字行的图片和对应的文本内容,模型就能自己学会如何把视觉特征映射为字符序列。从公式上看,整个识别过程可以写成三级复合函数:
小王很快明白了自己的任务分工:眼睛看笔画、脑子记顺序、笔落成句子。CRNN 就是把这三个动作教给机器的“读信流水线”。
- 输入图像$\mathbf{x}$ 代表一张文本行图片,通常先归一化高度(比如 32 像素),宽度保持原比例。
- 卷积特征提取$\operatorname{CNN}(\mathbf{x})$ 把图像压缩成按列排列的特征序列,相当于小王先用眼睛看清每块区域的笔画结构。
- 循环时序建模$\operatorname{RNN}(\mathbf{f})$ 沿时间步逐列读完特征序列,并用隐藏状态记住前面读过的内容。
- CTC 转录$\operatorname{CTC}(\mathbf{h})$ 把每帧字符概率序列合并成最终的输出文本,完成从图像到字符串的转换。
CNN特征提取
小王从工具箱里拿出一个放大镜,对准信纸上的一个“永”字。他先看到的是起笔顿挫的一点、横折钩的棱角,再退远看,这些笔画组成了“永”的完整轮廓。CNN 的卷积网络干的就是这件事:先用小卷积核看局部像素,识别横竖撇捺等低级结构,再层层组合成更高级的语义特征。
在 CRNN 中,CNN 接受一张高度固定的文本行图像,经过若干卷积层、池化层和归一化层后,输出一个三维特征图。因为池化通常只在高度方向压缩,所以特征图宽度仍然对应原来图像的左右位置。随后,这个特征图的每一列被拉成一个特征向量,整体形成“时间步 × 特征维度”的序列,正好喂给后面的 RNN。这个过程里常用的 BatchNorm 层能让每层输入分布稳定,加速收敛。
卷积运算的本质是“局部加权求和”。一个小窗口在图像上滑动,每到一个位置就把窗口内的像素和卷积核做点乘,得到输出特征图上的一个数值:
小王合上放大镜,心里有了数:每个卷积核就是一种“笔锋探测器”。几十个卷积核叠加在一起,就能把一张照片里的文字结构完整地“看”出来。
- 局部像素窗口$I_{i+m, j+n}$ 是原图中以 $(i,j)$ 为中心的小块像素,镜片聚焦的这一小块区域。
- 卷积核权重$K_{m,n}$ 是卷积核 $k \times k$ 窗口内的权重,决定了镜片关注什么模式(比如横线或竖线)。
- 加权求和把窗口每个位置的像素与对应权重相乘后全部累加,得到一个特征值。
- 加偏置最后加上偏置 $b$,得到该位置的特征输出,相当于镜片的基础亮度调节。
RNN序列建模
小王发现,读信不能一个字一个字孤立地认。比如“明天”的“天”字,如果只看局部可能像个“大”,但只要瞄一眼后面的“气”,就能确定是“天”。RNN 就是这样一台“带记忆的阅读器”,它按顺序读完 CNN 传来的每一帧特征,一边读一边把前面看到的内容压缩成隐藏状态,让后面帧的预测参考前面的信息。
CRNN 中常用的是双向 LSTM。普通单向 RNN 只能看到左边的上下文,而双向 LSTM 同时跑一条正向和一条反向分支:正向从第一帧读到最后一帧,反向从最后一帧读到第一帧,每个时间步把两个方向的输出拼接起来,这样每个位置既知道“左边说过什么”,也知道“右边将要出现什么”。此外,深层 LSTM 之间往往会加入残差连接,把输入直接加到输出上,让梯度在深层网络中顺畅流动,训练更稳定。
RNN 的最终输出是一个长度等于时间步数的序列,每个时间步都对应一个字符概率分布。这个分布覆盖整个字符集加上一个特殊的 blank 符号,比如 3000 个常用汉字加一个 blank。它并不直接给出某个字,而是告诉 CTC:这一刻我“觉得最像哪个字”。
LSTM 内部通过门控机制更新记忆细胞,其中遗忘门和输入门共同决定隐状态的更新方式:
小王读信时越来越熟练,他总是一边看前面的“因”,一边猜后面的“果”,把看不见的上下文在脑子里串成一条流畅的语义线。RNN 的双向读取,学的正是这份本事。
h_t = o_t \odot \tanh\left(c_t\right), \quadc_t = f_t \odot c_{t-1} + i_t \odot \tilde{c}_t- 遗忘门与输入门$f_t$ 控制上一时刻的记忆 $c_{t-1}$ 保留多少,$i_t$ 控制新的候选记忆 $\tilde{c}_t$ 写入多少。
- 输出门$o_t$ 决定从记忆细胞 $c_t$ 中读出多少信息,先经过 $\tanh$ 激活,再与输出门相乘。
- 候选记忆$\tilde{c}_t$ 通常由当前输入和上一隐状态通过线性变换加激活得到,是“此刻想记住的新内容”。
CTC损失函数
小王碰到全信最难的一段:两句诗挤在一起,字与字之间没有空格,有两个字因为墨迹晕染几乎一模一样,根本说不清是“风”字写了两遍,还是只写了一遍。但奇怪的是,他不需要在纸上一笔一笔标出每个字的位置,只需要在录入时写下正确的整句诗。人工智能的 CTC 损失,正是为了这个场景而生。
问题根源在于“变长对齐”:CNN + RNN 输出的特征序列有 T 个时间步,而目标文本只有 U 个字符,两者长度通常不相等,且我们不知道每个字符对应哪几帧。CTC 的解决方案是:引入一个“空白”符号(blank),并把问题转化为——对所有能映射成目标文本的“路径”求概率和。例如文本“AB”,既可以是路径 A→_→B,也可以是 _→A→B→_,只要路径经过合并去重后能还原成“AB”,就算有效。
CTC 的训练目标是最大化所有有效路径的概率总和,通常写成负对数损失形式:
小王豁然开朗:原来他不给每个字“对号入座”,反而给了模型更大的自由度。CTC 用一条条“柔性路径”把模糊的笔画和确定的文字桥接起来,这就是它能处理不定长序列对齐的秘密。
- 单路径概率给定一条对齐路径 $\pi = (\pi_1, \pi_2, \ldots, \pi_T)$,它出现的概率是每个时间步输出对应字符概率的连乘。
- 对齐映射$\mathcal{B}$ 是路径到标签的映射,它先把相邻重复字符合并,再删除 blank 符号,例如 $A, \text{blank}, A \to AA$。
- 求和所有有效路径$\mathcal{B}^{-1}(\mathbf{l})$ 是所有能映射到目标 $\mathbf{l}$ 的路径集合,CTC 把它们的概率全部相加。
- 负对数损失对总概率取负对数,概率越接近 1,损失越接近 0,方便梯度下降优化。
CTC解码推理
模型训练完成后,到了真正“读信”的时刻。小王面对一行模糊字迹,会先凭直觉挑最可能的字试读,如果整句不通,再回头尝试另一个候选。CTC 的推理(解码)也分两种思路:贪心解码和 Beam 搜索。
贪心解码最简单:每个时间步只取概率最大的字符,然后把整条路径做“去重 + 去空白”处理。它的优点是极快,缺点是只看局部最优,忽略了“多条不同路径加起来可能指向同一个更优文本”。Beam 搜索则维护 k 条候选路径,每步都保留累计概率最高的 top-k,最后选出整体概率最大的文本,精度更高但稍慢。二者最后都必须经过同样的合并规则。
贪心解码的形式化描述是:每帧取 argmax,再通过映射函数 $\mathcal{B}$ 得到输出:
小王在实践中发现,贪心法就像“凭第一印象答题”,绝大多数时候可靠,但遇到难以分辨的字就容易出错;Beam 搜索则更像“列出几个候选,通读全句再做决定”。工程上常先用贪心解码快速验证,追求极致精度时再上 Beam。
- 逐帧选优$\hat{\pi}_t$ 是在第 $t$ 帧概率最大的字符,也就是当下“最像”的那个候选。
- 生成完整路径把每一帧的 $\hat{\pi}_t$ 按时间顺序拼起来,得到原始路径 $\hat{\pi}$。
- 合并重复字符路径中相邻且相同的字符只保留一个,例如 $\text{A}, \text{A}$ 合并为 $\text{A}$。
- 删除空白符号去掉所有 blank,剩余字符串成最终文本。
模型训练策略
小王为了提高誊写速度,给自己制定了一套练习计划:每天故意换不同颜色的笔、不同纸张来抄写,增加手感的“难度”;一开始任务量小、适应节奏,后期逐步加大强度。模型训练也一样,需要一套合理的策略让损失函数平稳下降。
数据增强是第一步。真实场景的照片可能有旋转、透视、模糊、光照不均等问题,训练时需要随机对文本行图像做缩放、旋转、仿射变换、加噪声等操作,相当于给模型“制造各种墨迹和纸面情况”,增强泛化能力。第二步是学习率调度,常用的做法是先 warmup 线性升温到较大学习率,让模型快速进入“状态”,再余弦退火或指数衰减慢慢收敛。最后是梯度裁剪,把每个 batch 的梯度向量按范数截断到阈值内,防止 LSTM 这类深层网络在长序列上梯度爆炸。
实践中,很多人只关注网络结构而忽视训练策略,结果同样的模型,一个训练稳定、准确率很高,另一个则反复震荡。数据增强让图像样本更多样,学习率调度让优化更平滑,梯度裁剪让长序列训练更稳健——三者像小王的“练字计划”一样,是模型稳定收敛的基石。
CTC 的训练还有一个特点:它对“对齐”完全不作硬性规定,因此模型要自己探索出合适的路径分布。初期梯度往往混乱,需要配合较高学习率和较大 batch 来稳定统计量。很多工程团队会先用合成数据预训练,再用真实数据微调,目的正是让模型在可控环境下先学会“基本字形”,再适应真实世界的噪声。
小王发现,自己的“练习计划”和模型训练如出一辙:不断变着花样练、控制节奏、防走火入魔。模型也在一次次迭代中,把模糊的墨迹变得越来越“可预测”。
工程化部署
小王终于把整套技能运用到档案室的全部信件上。他发现,要完成全部数字化,必须搭一条流水线:先把信件扫描,找到哪些位置有文字,再把每一行文字剪裁出来,最后送进识别模型转成文本。这正对应真实 OCR 工程的完整链路。
标准 OCR 流水线通常分为三块:文本检测、文本识别、结构化后处理。文本检测负责找到图片里的文字区域,常用 DBNet、EAST 等检测模型输出文本行的包围框;文本识别就是我们讲的 CRNN,把每个检测框内的图像裁成文本行,归一化高度后送入 CRNN,得到字符串;后处理再根据业务需求做纠错和结构化,比如把“姓名:张三”自动填入数据库字段。三个模块各司其职,组成完整的“档案数字化车间”。
工程落地还要考虑推理效率。CRNN 的 CNN 主干和 LSTM 都较耗时,常用的优化手段包括:模型量化(FP16/INT8)、TensorRT 加速、ONNX 导出,以及批量推理多张文本行图像。云端部署用 GPU 拼接大 batch,端侧部署则压缩模型体积、剪枝量化。很多手机上的拍照翻译、车牌识别,就是这些优化后跑在移动端的。
端到端训练是另一大趋势:传统流水线分开训练检测和识别模型,现在一些方案把两者联合微调,让检测框更好地贴合识别需求。比如识别模型发现某个检测框里文字被截断,反馈信号能促使检测器把框放大一些。这种“全局协调”让整条流水线的准确率进一步提升。
小王最后把信件流水线升级成了自动化流程:扫描、切行、识别、归档一气呵成。从一张泛黄照片到可检索的文本,只用了不到一秒钟。
性能评估指标
小王辛辛苦苦录完一批信,质检员却来问:你的识别成绩到底怎么样?怎么量化“错多少”?这就涉及 OCR 的评估指标。最常用的是字符准确率、单词准确率和编辑距离,它们从不同粒度回答“识别得好不好”。
字符准确率(Character Accuracy)统计每个字符是否正确,等于正确字符数除以总字符数,通常用“1 - 编辑距离/长度”来近似。单词准确率(Word Accuracy)则要求整行或整词完全一致才算对,指标更严格,常用于英文单词、车牌号这类整体性强的场景。编辑距离(Levenshtein Distance)量化两个字符串之间最少需要多少次插入、删除、替换才能相等,它不仅是评估工具,也是自动纠错的依据。
编辑距离的动态规划递推式如下:
小王看着质检报告明白了:字符准确率看“单个字有没有错”,单词准确率看“整句完不完整”,编辑距离则告诉他“错了几个字、差在哪”。有了这把尺子,他就能持续迭代,把识别率从 90% 一路推到 99%。
- 删除操作$D(i-1,j) + 1$ 表示预测串多了一个字符,需要删掉它,代价加 1。
- 插入操作$D(i,j-1) + 1$ 表示标准串多了一个字符,预测串需要插入,代价加 1。
- 替换或相等$D(i-1,j-1) + [a_i \ne b_j]$ 中,若当前两个字符相同则代价为 0,不同则需要一次替换。
- 取最小值三者取最小,就是这两个前缀之间的编辑距离。
AI 应用场景
银行卡号识别
阶段:模型推理与后处理
解决的问题:传统银行卡号录入靠人工敲键盘,速度慢且容易抄错。不同银行的卡号长度不一(16-19 位),固定长度的识别模型很难统一处理。CRNN + CTC 天然支持变长输出,只需识别一行数字序列即可。
具体案例:在银行 App 里,用户对准银行卡拍照后,先由文本检测模型定位卡号凸字区域,再将裁切后的卡号行送入 CRNN。模型的卷积层提取凸字立体感特征,双向 LSTM 读完整行数字,CTC 解码输出一串不定长的卡号。整个过程在手机上毫秒级完成,还能自动去除卡号之间的空格。
身份证信息抽取
阶段:多字段协同识别
解决的问题:身份证上有姓名、性别、民族、住址等多个字段,其中住址长短差异巨大,且证件拍摄时常有倾斜、反光。传统方法需要为每个字段单独特制规则,鲁棒性差;CRNN 则可以统一识别任意长度的地址文本。
具体案例:金融开户场景中,系统用检测模型把身份证切分为多个字段区域,分别送入 CRNN 识别。姓名是两三个汉字,地址可能是几十个字的超长序列,CTC 自适应地把不定长帧序列对齐成字符串。识别完成后,规则引擎把“住址”字段自动填入开户表单,把人工录入时间从 3 分钟压缩到 10 秒。
物流运单号批量录入
阶段:端到端训练与批量推理
解决的问题:快递分拣中心每天要扫描成千上万张面单,运单号通常是 12-15 位数字加字母的混合串,且打印字体可能模糊、被遮挡。CTC 的容错能力能有效处理这种部分笔画缺失的序列识别。
具体案例:物流公司用工业相机采集面单图像,检测模块框出条形码下方的运单号文本行,CRNN 识别出完整运单号后自动匹配系统订单。推理阶段用 TensorRT 量化加速,单张面单处理时间小于 5 毫秒,高峰期单台服务器可支撑每天百万级面单扫描。
Python 演示
"""演示 CRNN/CTC 文字识别中的两个核心步骤:1) 贪心解码:从逐帧概率得到文本2) 基于全路径枚举的 CTC 损失计算"""import itertoolsimport numpy as np VOCAB = ['_', 'A', 'B'] # 0=blank, 1=A, 2=B def decode_greedy(probs): """贪心解码:每帧取最可能字符,再去blank合并重复""" best = np.argmax(probs, axis=1) out = [] for c in best: ch = VOCAB[c] if ch == '_': continue if out and out[-1] == ch: continue out.append(ch) return ''.join(out) def encode_to_label(path): """把一条对齐路径映射为标签:先合并重复,再去blank""" merged = [] for c in path: ch = VOCAB[c] if ch == '_': continue if merged and merged[-1] == ch: continue merged.append(ch) return ''.join(merged) def ctc_loss_bruteforce(probs, target): """枚举所有路径,计算能映射到target的概率总和,取负对数""" T, V = probs.shape total = 0.0 for path in itertools.product(range(V), repeat=T): if encode_to_label(path) == target: p = 1.0 for t, c in enumerate(path): p *= probs[t, c] total += p return -np.log(total + 1e-12) if __name__ == '__main__': # 合成一个“AB”识别任务的概率序列,共4帧 probs = np.array([ [0.1, 0.8, 0.1], # t0 最像 A [0.7, 0.2, 0.1], # t1 像空白/分隔 [0.1, 0.3, 0.6], # t2 最像 B [0.8, 0.1, 0.1], # t3 像空白/结尾 ]) print('贪心解码结果:', decode_greedy(probs)) print('CTC 损失(目标为AB):', round(ctc_loss_bruteforce(probs, 'AB'), 4))
VOCAB = ['_', 'A', 'B']定义字符表,其中_是 CTC 专用的 blank 符号,用来分隔重复字符。decode_greedy(probs)每帧取概率最大的字符,然后跳过 blank、合并相邻重复,得到最终识别结果。encode_to_label(path)模拟 CTC 的映射函数,把一条对齐路径转换成目标标签序列。ctc_loss_bruteforce(probs, target)用枚举所有路径的方式精确计算 CTC 损失,直观展示“所有路径概率求和”的过程。itertools.product(range(V), repeat=T)遍历所有可能的路径组合,小规模时精确可算,真实训练中会用动态规划前向算法加速。
常见误区
误区很多初学者以为 CTC 训练需要像目标检测那样,给每个字符标注一个矩形框,才能知道“哪个字在哪一帧出现”。
正解CTC 最大的优势恰恰是只需要整行文本字符串标注。它通过引入 blank 符号和路径映射,自动在训练中寻找最优对齐,完全不需要人工标注字符位置。比如识别一行“你好世界”,只需标注“你好世界”四个字,模型自己会学会每个字对应哪几帧。
为何常见:大家受目标检测、语义分割等“位置标注”任务影响,容易把“识别”和“定位”混为一谈,忽略了 CTC 的设计初衷就是免对齐。
误区有人看到 CTC 里有“blank”这个词,就以为是英语里的空格或中文标点,于是把 blank 当成一个普通字符来训练。
正解CTC 的 blank 是一个额外的虚拟符号,不输出到最终结果,它的作用是分隔两个相邻的相同字符。例如两个连续的“A”必须写成 A blank A,blank 移除后才是“AA”。文本中的真正空格是字符集里的一个字符,两者性质完全不同。
为何常见:“blank”直译为“空白”,容易和排版空格混为一谈,且很多教程没有专门强调 blank 是一种“对齐工具”而非输出符号。
误区初学者认为既然要识别字符,就得先把图片按字切开,否则网络不知道“一个字到哪结束、下一个字从哪开始”。
正解CRNN + CTC 是免分割的端到端方案。CNN 提取整行特征,RNN 按序列建模,CTC 在训练中自动学会对齐,推理时也只需整行图像输入。反而强行切分会引入切错位置、宽度不均等问题,降低识别准确率。
为何常见:老式 OCR 确实依赖字符分割,很多人带着旧经验理解新方法,没意识到 CTC 已经把“分割”这个环节内化到训练目标里了。
误区有同学看到 RNN 是按时间步来读的,就认为图片宽度是多少像素、输出就应该有多少个字,或者字越多图片就应该越宽。
正解输入宽度和字符数没有一一对应关系。CNN 经过多次池化后,宽度被大幅压缩成较短的帧序列;一个字符可能对应好几帧,也可能几帧里只有一帧“激活”。CTC 通过去重和去空白,把任意长度的帧序列映射到可变长度的文本。
为何常见:把时间步与字符一一对应是固定长度分类器的思维惯性,CTC 的设计恰恰就是为了打破这种硬性绑定。
误区既然每帧都选了概率最大的字符,拼起来不就应该是最优文本吗?很多初学者认为贪心解码已经足够好,Beam 搜索没有必要。
正解逐帧最大概率忽略了“路径组合”效应。比如两帧分别对 A 的概率是 0.6、0.6,对 B 的概率是 0.5、0.5,单帧最优都是 A,但两条 B B路径合并后总概率可能超过单条 A A 路径。Beam 搜索会综合整条路径的累积概率,避免这种局部最优陷阱。
为何常见:贪心的“每一步最优化”直觉很符合日常决策习惯,人们容易忘记序列级概率和帧级概率之间的差别,以及重复合并后多种路径会汇合到同一结果。
面试题
Q1. CRNN 中,CNN 部分的核心作用是?
- A.提取图像的局部视觉特征← 正确答案
- B.对输出做序列解码对齐
- C.计算 CTC 损失函数数值
- D.替代 RNN 做时序记忆建模
答案:A。CNN 负责从像素中提取从笔画到字符结构的视觉特征,是模型的“眼睛”。解码对齐由 CTC 完成,损失计算是训练过程而非 CNN 的前向职能,时序记忆则由 RNN 负责。
Q2. CTC 中的 blank 符号最主要的用途是?
- A.表示两个词之间的空格
- B.分隔连续相同的字符← 正确答案
- C.标记序列的结束位置
- D.表示无法识别的像素
答案:B。blank 的关键作用是分隔相邻的重复字符,让 A blank A 能还原成“AA”而不是被合并成“A”。文本空格是字符集里的普通字符,blank 与结束符、模糊像素都无关。
Q3. CTC 解码时,去掉 blank 和合并重复字符的正确顺序是?
- A.先只去掉所有 blank
- B.先合并重复再去 blank← 正确答案
- C.只能逐帧顺序式处理
- D.两者与顺序完全无关
答案:B。标准 CTC 解码先合并相邻重复字符,再删除 blank 符号。例如路径 A A _ B 先合并 AA 为 A,再去掉 blank 得到 AB。若先删 blank,AA 会变成 A,结果一致,但路径 A _ A 先删 blank 再合并会错误地丢失两个 A。因此顺序是先合并、后去空白。
Q4. 为什么 CRNN 需要 RNN 层?
- A.记住图像中的长程顺序信息← 正确答案
- B.减少模型训练所耗总时间
- C.增加可训练参数量便于过拟合
- D.让 CNN 输出特征图的通道变多
答案:A。RNN 通过隐藏状态记住前面帧的上下文,让每个位置的预测不孤立。它不负责减少训练时间,也不是为了过拟合而堆参数,通道数由 CNN 决定。RNN 的核心价值是建模字符之间的序列依赖关系。
Q5. CTC 训练模型时,合理的做法包括?
- A.提供整行文本字符串标注← 正确答案
- B.人工标注每个字符坐标框
- C.使用数据增强提高鲁棒性← 正确答案
- D.对梯度进行裁剪防止爆炸← 正确答案
答案:ACD。CTC 只需要整行文本作为标注,不需要字符级坐标框,因此 A 正确、B 不必要。数据增强能提高模型对旋转模糊等干扰的鲁棒性,梯度裁剪能防止 LSTM 训练中梯度爆炸,二者都是训练阶段的合理操作。
Q6. CTC 推理时,贪心解码的主要不足是?
- A.每帧只取最大概率忽略整体组合← 正确答案
- B.计算速度远慢于 Beam 搜索
- C.需要大量内存存储所有路径
- D.只能处理单个字符的文本场景
答案:A。贪心解码逐帧取最大概率,忽略了多条路径可能合并到同一文本后总概率更大的情况。它速度比 Beam 搜索快得多,内存占用也小,并能处理多字符文本,因此 B、C、D 都不对。
Q7. 训练 CRNN 时,若输出帧数 T 小于目标字符数 U,会发生什么?
- A.模型会自动补足缺少的帧数
- B.寻找不到任何可行的对齐路径← 正确答案
- C.损失变为负值导致梯度消失
- D.仍然可以正常完成训练优化
答案:B。CTC 要求每个非 blank 字符至少占用一帧(且字符间需要 blank 分隔),因此有效路径必须满足约 T ≥ 2U+1。当 T < U 时,任何路径都无法映射回目标标签,概率总和为零,损失变成无穷大,梯度无法有效传播,训练失败。
Q8. 预测字符串长度为 10,与标准答案的编辑距离为 2,则字符准确率(按 1-编辑距离/长度)是多少?
- A.0.80 准确率等于一减编辑距离比← 正确答案
- B.0.20 准确率等于编辑距离的比值
- C.无法仅由这两个数量完全确定
- D.一定是 1.00 表示结果完全正确
答案:A。字符准确率通常定义为 1 - 编辑距离 / 总长度,即 1 - 2/10 = 0.80。它衡量预测串与标准串的整体重合程度,编辑距离越小准确率越高。因此 A 正确,其余选项与公式定义不符。
Q9. CRNN 在推理阶段进行加速优化时,可以采用?
- A.采用量化或 TensorRT 加速← 正确答案
- B.将模型导出为 ONNX 格式← 正确答案
- C.使用 batch 并行处理多行← 正确答案
- D.必须增加 blank 符号数量
答案:ABC。量化、TensorRT、ONNX 导出和 batch 推理都是常见的加速手段。blank 符号数量由字符集设计决定,与推理加速无关。因此 D 错误,A、B、C 都是合理的工程优化方式。
Q10. 编辑距离为 1 表示预测与标准答案之间的关系是?
- A.可能只差一次替换或增删← 正确答案
- B.字符完全相同没有任何差异
- C.至少需要两次修改才能一致
- D.与字符顺序完全无关了
答案:A。编辑距离定义为从预测串变换到标准串所需的最小插入、删除、替换次数。距离为 1 意味着只差一次操作,例如 ABBA 与 ABBC 只需要替换最后一个字符。B 对应距离 0,C 对应距离 2 及以上,D 与定义矛盾。