CTC Loss深度解析:LPRNet如何仅凭CNN实现高效车牌识别
在计算机视觉的诸多应用场景中,车牌识别(LPR)一直是一个兼具挑战性与实用性的经典课题。传统的解决方案,如CRNN(卷积循环神经网络),通常遵循“CNN特征提取 + RNN序列建模 + CTC损失对齐”的范式。然而,2018年由Intel团队提出的LPRNet,却大胆地移除了RNN模块,仅凭精心设计的CNN架构与CTC Loss,就在嵌入式设备上实现了高精度、低延迟的车牌识别。这不禁让人好奇:去除RNN后,模型如何捕捉序列间的上下文依赖?CTC Loss又在此过程中扮演了怎样的核心角色? 本文将深入算法原理,为你揭开LPRNet轻量化与高效能背后的技术奥秘。
1. CTC Loss:解决不定长序列对齐的“动态规划大师”
要理解LPRNet的精妙之处,首先必须攻克CTC Loss这个核心概念。在自然场景下的车牌识别中,输入图像经过网络后,输出的预测序列长度(例如,特征图在宽度方向上的步长数)与真实的字符序列长度(车牌字符数)往往是不一致的。这种“不定长对齐”问题是序列识别任务中的主要障碍。
1.1 CTC的核心思想与“空白符”机制
CTC(Connectionist Temporal Classification)引入了一个巧妙的“空白符”(blank,通常用“-”表示)。这个特殊符号不代表任何有效字符,其作用是允许模型在输出序列中插入“停顿”或“间隔”,从而灵活地对齐输入和输出。
假设一个简化场景:车牌真实标签是“京A12345”。网络在宽度方向上输出了18个时间步(timestep)的预测,每个时间步都对应一个包含68个字符(包括空白符)的概率分布。CTC允许的合法对齐路径非常多,例如:
- 路径1:
--京---A--1-2-3-4-5 - 路径2:
京京---A-111-2-3-4-5-- - 路径3:
京--A-A-1-2-33-4-5-
所有这些路径在经过“合并重复字符并移除空白符”的操作后,都能得到正确的“京A12345”。CTC Loss的目标就是最大化所有能映射到真实标签的路径的概率之和。
注意:CTC并不要求我们显式地标注每个字符在输入序列中的具体位置,这极大地降低了数据标注的难度和成本,实现了真正的端到端训练。
1.2 动态规划算法:前向-后向算法
计算所有有效路径的概率总和,如果采用暴力枚举,其计算量是指数级的,完全不可行。CTC巧妙地借用了动态规划的思想,通过前向-后向算法高效地完成这个计算。
我们定义一个经过扩展的标签序列 L',即在真实标签的每个字符之间以及首尾都插入空白符。例如,“京A”变为“-京-A-”。设网络在时间步 t 输出标签 k 的概率为 y_{t}^{k}。
前向变量 α_t(s) 表示在时间 t,已经匹配到扩展序列 L' 第 s 个位置的所有路径的概率和。其递推公式考虑了三种可能的状态转移:
- 从
s保持不变(当前输出为空白符或重复字符)。 - 从
s-1移动到s。 - 跳过空白符,从
s-2移动到s(当L'中s处的字符是非空白符,且与s-2处的字符不同时)。
类似地,后向变量 β_t(s) 则从序列末尾向开头计算。最终,在任意时间步 t,路径经过标签 L'_s 的概率可以通过 α_t(s) 和 β_t(s) 的乘积求得。对所有时间步和所有标签求和,再取负对数,就得到了CTC Loss。
# PyTorch 中 CTC Loss 的使用示例
import torch
import torch.nn as nn
# 定义损失函数,blank索引通常设置为最后一个类别
ctc_loss = nn.CTCLoss(blank=num_classes-1, reduction='mean')
# 网络输出: (序列长度 T, 批次大小 N, 类别数 C)
# 需要先对类别维度取log_softmax
log_probs = torch.randn(18, 32, 68).log_softmax(2) # T=18, N=32, C=68
# 输入序列长度(每个样本的T,通常相同)
input_lengths = torch.full((32,), 18, dtype=torch.long)
# 目标标签
targets = torch.randint(1, 68, (32, 10), dtype=torch.long) # 假设最长标签为10
# 目标序列长度
target_lengths = torch.randint(5, 10, (32,), dtype=torch.long) # 每个车牌


6214

被折叠的 条评论
为什么被折叠?



