写在前面
总觉得自己看了很多知识,但是过一段时间就忘记了。好比这个CTC loss明明记得一个多月前看过的,但是现在重新拾起,和新的没什么区别。所以今天结合github上的某个cnn_lstm_ctc 项目,重新温故一下,记录自己的一些收获。
https://github.com/weinman/cnn_lstm_ctc_ocrgithub.com在这里记录一下。
正文
一、自己的一些疑问
- mjsynth dataset的数据集了解
这个数据集的每张图片,表示一个输入,文件命名是144_mgm_48146.jpg,分别表示id_text in image_(??).jpg,最后一串数字不是很清楚,不过目前来看没有用到。如下图所示。
![ddd8cebbe80254a11680d06d30875b27.png](https://i-blog.csdnimg.cn/blog_migrate/1f1af961eac58ef98ade4d5769ca3fe9.jpeg)
2.label是如何表示的?
以下代码可以一目了然的知道label的处理方式
out_charset="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
out_charset_dict = { key: val for val, key in enumerate( out_charset ) }
int_to_string_dict = dict(enumerate(out_charset)) #{0: 'A', 1: 'B', 2: 'C', 3: 'D'..}
def string_to_label ( string ):
"""Convert a string to a list of labels"""
label = [out_charset_dict[c] for c in string]
return label
def label_to_string ( labels ):
"""Convert a list of labels to the corresoponding string"""
string = ''.join( [int_to_string_dict[c] for c in labels] )
return string
def get_text_and_labels( filename ):
# 2697/6/466_MONIKER_49537.jpg --> MONIKER
text = os.path.basename( filename ).split( '_', 2 )[1]
labels = charset.string_to_label(text)
return text, labels
基本逻辑是:提取图片中的文本信息--->分割每一个字符,并转化成标签,标签格式是{key:value},如{1:'B'}表示第1类是字符B. 从这里也可以想象,网络最终不是直接回归每个字符,而是给出一个分类的概率,是一个分类问题。如果了解CTC loss layer 的原理,这里就不会有疑惑啦。
3.所以CTC loss 到底做了什么事情呢?
首先,需要明确,lstm网络的最后一层,会做全连接操作,这个跟一般的分类网络一样,需要注意的是分类会多出一个unit,这个用来表示no label 或者空格。可参考代码。
#full connection layer
rnn_logits = tf.layers.dense( rnn2,
num_classes+1,
activation=logit_activation,
kernel_initializer=weight_initializer,
bias_initializer=bias_initializer,
name='logits' )
接下来,就是CTC loss layer需要干的事情了。
***通过softmax获得每个frame在不同分类上的概率,
***计算不同sequence下的概率
***alignment ,得到和label一样的sequence。(valid sequence)比如原本是cc--a--tt,alignment成cat,包含了遍历、删除、合并这些操作。
3.1 为什么这里需要动态规划?
对于每一个sequence都需要align,这样开销就比较大。所以文章利用动态规划的思想,因为可以根据上一阶段的状态和决策来导出本阶段的状态。那么对于同一时刻t下生成的相同output,我们可以认为是同一个状态,不需要重复计算。
3.2 但是如何体现在代码里呢?判断是相同的output还是少不了那些操作吖?
并不是这样理解,借助图片理解可以发现,对于同样的两个节点,即使第三个节点不一样,但对于前面的状态,不需要计算两次。注意这里生成相同的output是直接通过路径相同来判断的,而不是判断align的结果是否一样。
![cb718a9256d630c5588cb22f1095870c.png](https://i-blog.csdnimg.cn/blog_migrate/ef8b26d4ed7d8586547a293656269f97.jpeg)
***计算负对数似然
实际上tensorflow已经提供了这个API ,应用来说没有问题,api如下。但对于动态规划的推导,以及后向,梯度计算,还没有了解pending for update
losses = tf.nn.ctc_loss( sequence_labels,
rnn_logits,
sequence_length,
time_major=True,
ignore_longer_outputs_than_inputs=True )
- 最后疑惑的问题是,中文如果用这个进行training,因为中文的字很多,怎么样做label呢?
该问题还有待解决。pending for update