Keras LSTM层导致OCR模型后续所有层无论输入均输出相同值排查
问题根因
- LSTM门控饱和:第一层Dense的ReLU输出无数值上限,输入到LSTM后会让tanh、sigmoid门控函数进入饱和区,门控失效后无论输入是什么,LSTM的输出都会固定,且梯度无法回传更新参数。
- CTC损失引导偏差:你当前CTCLayer的实现默认所有样本的输入长度等于最大时间步、标签长度等于最大标签长度,如果实际样本不符合这个假设,会导致损失计算错误,模型会快速收敛到所有时间步输出空白符的局部最优解,此时CTC损失会下降到16-22的稳定区间,但所有预测输出固定,和你观测的异常现象完全吻合。
- RNN梯度消失:三层堆叠的双向LSTM本身就容易出现梯度消失问题,没有归一化、梯度裁剪等机制的情况下,参数会很快进入不更新的状态。
代码存在的错误
- 进入LSTM前的特征未做归一化,且第一层Dense仍使用ReLU激活,你之前替换激活函数时遗漏了这一层,仍是无界输出。
- CTCLayer的输入长度、标签长度计算逻辑只适用于定长输入、定长标签的场景,如果你做的是通用OCR的变长识别,该逻辑会直接导致损失计算错误。
- 堆叠LSTM没有添加任何防止梯度消失、门控饱和的防护机制。
修复方案
- 调整输入预处理逻辑,限制进入LSTM的数值范围
Processing = layers.Reshape((12,9472))(encoder) # 拆分激活、添加BN限制输出范围 Processing = layers.Dense(128)(Processing) Processing = layers.BatchNormalization()(Processing) Processing = layers.LeakyReLU(alpha=0.1)(Processing) # 可选:添加层归一化进一步稳定序列分布 Processing = layers.LayerNormalization()(Processing) - 优化LSTM结构,添加防饱和机制
# 每层LSTM添加循环dropout、输出后加层归一化 lstm = layers.Bidirectional(layers.LSTM(256, return_sequences = True, recurrent_dropout=0.2))(Processing) lstm = layers.LayerNormalization()(lstm) lstm = layers.Bidirectional(layers.LSTM(128, return_sequences = True, recurrent_dropout=0.2))(lstm) lstm = layers.LayerNormalization()(lstm) lstm = layers.Bidirectional(layers.LSTM(64, return_sequences = True, recurrent_dropout=0.2))(lstm) lstm = layers.LayerNormalization()(lstm) - 编译模型时添加梯度裁剪,避免梯度爆炸把参数更新到饱和区
optimizer = tf.keras.optimizers.Adam(clipnorm=1.0) model.compile(optimizer=optimizer) - 修正CTC损失计算逻辑(变长识别场景):不要在CTCLayer内部硬编码计算输入长度和标签长度,训练时将真实的输入长度、标签长度作为输入传入模型,调用
ctc_batch_cost时用真实值计算。 - 调整输出层初始化,避免模型前期就收敛到全空白输出
# l为字符集总数+1(含空白符),压低初始空白符概率 outputs = layers.Dense(l, activation='softmax',name='output', bias_initializer=tf.keras.initializers.Constant([-1]*l))(outputs)
内容的提问来源于stack exchange,提问作者jr123456jr987654321
相关产品推荐
相关产品推荐

