数学公式识别一直是OCR领域公认的"硬骨头"。与普通文字识别不同,公式具有复杂的二维空间结构——上下标、分式、根号等元素在水平和垂直方向上都存在语义关联。传统OCR系统采用CRNN(卷积循环神经网络)架构,这种串行处理方式会破坏公式的二维结构信息。举个例子,当识别"x²+y²"时,CRNN可能会将其误读为"x2+y2",完全丢失了平方的数学含义。
我在实际测试中发现,即使是商业OCR软件,对印刷体公式的识别准确率也不足60%,手写公式更是低于30%。这主要是因为:
经过对比实验,最终采用Encoder-Decoder with Attention的架构:
这个设计的精妙之处在于:
python复制def add_timing_signal_nd(x):
# 获取特征图形状 [batch, height, width, channels]
static_shape = x.get_shape().as_list()
num_dims = len(static_shape) - 2 # 空间维度数(2D图像为2)
# 计算各维度的位置编码
for dim in range(num_dims):
length = tf.shape(x)[dim + 1] # 当前维度长度(宽/高)
position = tf.to_float(tf.range(length)) # 位置序列
# 生成频率递增的sin/cos波形
inv_timescales = tf.exp(
tf.range(num_timescales) * -log_timescale_increment)
# 位置与频率的外积
scaled_time = position[:, None] * inv_timescales[None, :]
signal = tf.concat([tf.sin(scaled_time), tf.cos(scaled_time)], axis=1)
# 将编码添加到原始特征
x += signal
return x
这段代码实现了二维位置编码,通过不同频率的正余弦函数组合,使得模型能够:
归一化处理:
python复制img = (tf.cast(img, tf.float32) - 128) / 128 # 归一化到[-1,1]区间
这种处理比常规的[0,1]归一化更能保持数值稳定性,特别是在深层网络中
动态Padding策略:
| 层数 | 类型 | 滤波器数 | 核大小 | 步长 | 输出尺寸 |
|---|---|---|---|---|---|
| 1 | Conv | 64 | 3x3 | 1 | H×W×64 |
| 2 | MaxPool | - | 2x2 | 2 | H/2×W/2×64 |
| 3 | Conv | 128 | 3x3 | 1 | H/2×W/2×128 |
| ... | ... | ... | ... | ... | ... |
| 6 | Conv | 512 | 3x3 | 1 | H/8×W/8×512 |
经过实测,这种渐进式下采样结构能在保留空间信息的同时,逐步扩大感受野。特别需要注意的是:
采用label smoothing交叉熵损失:
python复制loss = tf.nn.softmax_cross_entropy_with_logits_v2(
labels=label_smoothing(targets),
logits=logits)
其中label_smoothing将硬标签转换为软标签(如0.9正例+0.1均匀分布),能有效缓解过拟合。
梯度消失问题:
Attention权重发散:
python复制attention_weights = tf.nn.softmax(energy) # 原始版本
attention_weights = tf.nn.softmax(energy/temperature) # 加入温度系数
通过temperature参数(通常取0.5)控制注意力分布的尖锐程度
Beam Search优化:
在CROHME数据集上的测试结果:
| 方法 | ExpRate(%) | Top-1 Acc |
|---|---|---|
| 传统WYGIWYS | 32.7 | - |
| 基于树的方法 | 48.2 | - |
| 本方案 | 62.1 | 68.4 |
典型识别案例:
code复制输入:手写 "∫_a^b f(x)dx"
输出:\int_{a}^{b} f(x) dx (LaTeX格式)
失败案例分析:
数据增强策略:
部署优化:
python复制# 转换模型为TensorRT格式
trt_model = trt.create_inference_graph(
input_graph_def=graph_def,
outputs=["output"],
max_batch_size=32,
max_workspace_size=1 << 25)
实测推理速度提升3倍(从120ms降至40ms)
持续改进方向:
这个项目最让我惊喜的是位置编码的泛化能力——即使在训练数据中未出现的复杂公式结构,模型也能通过位置编码的几何推理能力给出合理识别结果。建议后续可以尝试将位置编码模块抽象为可插拔组件,应用到其他二维识别任务中。