1 前言
序列映射我们当前准备使用Transformer的结构;
2 致谢
感谢公众号《人工智能技术干货》提供的资料,《如何优雅的使用pytorch内置torch.nn.CTCLoss的方法》
让我收获了很棒的知识!
3 词表建立的规则
我们使用","作为统一的分隔符;
3.1 词典类——Vocab
我们使用set来作为词典的数据结构;
4 模型设计
3.1 backbone——rec_r34_vd
主干网络的设计我们是参考的PaddleOCR的识别模型,
代码的链接如下:
https://github.com/PaddlePaddle/PaddleOCR/blob/develop/ppocr/modeling/backbones/rec_resnet_vd.py
3.2 loss函数—— CTC Loss
CTC最初论文:
关于 CTC Loss函数的数学推导:
https://zhuanlan.zhihu.com/p/43534801
关于 CTC Loss中CTC动态规划的计算过程,可以参考 Deep Systems的PPT文档:
3.2.1 代码写作——torch.nn.
CTCLoss
我们使用PyTorch自带的CTCLoss;
CTCLoss的输入有两种方式:
padded和un-padded;
这里我们是推荐un-padded方式,因为不用预先做padding的操作,会方便一些,也更好理解;
nn.CTCLoss()参数说明:
blank——空白标识:
类似于一种占位符,在CTC-Loss中用来分隔两种字符区间,例如“ap-ple”,可以使用blank来分隔字符串中相同的元素;
我们在词典中也会加入<blank>字符,并将词典中的ID闯入到这里的blank参数中;
ctc_loss()参数说明:
参考代码是
loss = ctc_loss(output.log_softmax(2), target, input_lengths, target_lengths)
input——预测数组:
input模型的预测值,在输入到loss之前需要log_softmax(2);
targets——目标数组的长数组:
称它为长数组,是因为它是由目标值拼接而成的,拼接的方法是使用了“long_array += array”;
形象化的解释可以参考我发的博文,这里粘贴一下,
input_lengths——N维相同值的向量,:
input_lengths代表了所有输入序列的长度,由于这里我们使用的是CTCLoss,所以所有序列的长度都是相同的,即“时间长度T”;
5 模型调试
5.1 loss出现了“nan”——模型在计算时出现了数值溢出
5.1.1 由梯度爆炸引起的“nan”现象——而不是学习率lr过大引起的
梯度爆炸是会引起loss出现“nan”现象的,这是因为梯度在反向传播的过程中出现了“数值溢出”的问题,(这样的问题并不一定是代码的问题导致的)
观察点一:调小学习率之后,未出现梯度爆炸,说明不是loss计算的问题,而是梯度反向传播出现的问题;
观察点二:在使用最初大学习率时,会偶然出现“loss is nan”的情况,而不是每次训练都会出现,说明loss的前向计算不存在问题;
观察点三:使用最初大学习率时,模型有一定几率(例如50%)收敛的很好,说明学习率的设置不存在问题;
综上所述,这可能是由于模型存在大量的“全连接”结构,梯度在反向传递时,出现了多次的“乘法”运算而产生了梯度爆炸;
猜想:可以尝试使用梯度裁剪,来避免梯度回传时产生的数值溢出;
实验结果:使用梯度裁剪是可行的;
6 提问备忘
4.1 为什么Transformer不能指定输出encoding的大小呢?
今天在写作的时候,想到了这个问题,为什么Transformer不能指定输出encoding的大小呢?
我看了一下PyTorch的接口说明,的确是没有的,
然后请教了一下亮亮老师,老师说是因为PyTorch的实现版本没有提供这个功能,
他建议我看看OpenNMT-py,感谢亮亮老师!