通过 连接主义时间分类 loss 以及编码操作

如果想用使用计算机识别文字,神经网络很好用。使用一些列 CNN 从序列中提取特征,使用 RNN 来传播需略的信息。它会输出字符得分,给每个序列元素,通过一个简单的矩阵表示。现在,有两个事情我们想要对矩阵进行处理。

  1. 训练:计算损失值来训练神经网络
  2. 推理:解码矩阵来获得图片中的字符

两个任务可以同时被 CTC 操作完成。对于手写数字系统的描述,可以参见图像 1.

我们更进一步看看 CTC 操作,并且讨论一下它如完成的,以及它背后的公式是如此巧妙。最后,我将会指点你来找到 Python 代码以及不复杂的公式,如果你感兴趣的话。

为什么我们使用 CTC

当然我们可以创建一个数据集,这个数据集有文本行,然后指出每列属于哪一个字符,就像图 2 中展示的那样。然后,我们可以训练一个神经网络来输出每一列的得分。而然,对于这个简单的解法,这里有两个问题。

  1. 这个十分的耗时(以及无聊)来在字符层面上标注数据
  2. 我们仅仅能够得到字符的得分,因此还需要一些操作来获取最终的文本。一个简单地字符可以跨越多个位置,比如,我们得到 “ttooo”,是因为 “o” 是一个比较宽的字符。我们已经删除了多余的 “t” and “o”,但是,如果要识别的字符是 “too”,我们应该怎么办?如果删除了多余的 “o”,将会给我们错误的答案。我们应该如何处理呢?

CTC 解决了几个问题

  1. 我们只需要告诉 CTC loss function,文本在图像中出现了,因此我们忽略位置和宽度文中在图像中。
  2. 不需要更多的文本识别处理

CTC 如何工作的

就像是我们已经讨论的,我们不希望在图像的每一列标注数据(这曾经被我们成为时间步)。神经网络的训练将会被 CTC 损失函数所指引。我们只需要把数据矩阵给 CTC 函数,以及对应的真实值即可。但是它是怎么知道每一个字符出现的呢?他不知道。相对而言,它尝试了图片中所有的真实文本,以及计算了所有的加和。通过这个方式, This way, the score of a GT text is high if the sum over the alignment-scores has a high value.

编码文本

如何编码重复的文本曾经是一个问题。这个问题通过引入一个虚假字符来解决了(称为空,但是不要把它和真正的 space 混淆。)。这个特殊的字符被标记为 “-”,在下面的文本中。我们使用了一个聪明的编码策略来解决重复字符的问题。当编码一个文本的时候,我们可以随机加入许多空在任何位置中,当我们解码的时候,我们将会把的这些删除。但是,我们必须在重复字符串中加入空,例如 “hello”,如此一来,重复字符就不是问题了。

Let’s look at some examples:

“to” → “—ttttttooo”, or “-t-o-”, or “to” “too” → “—ttttto-o”, or “-t-o-o-”, or “to-o”, but not “too”

正如你所见,这些模式也允许我们简单的创建一些相同文本串的不同对取,比如 “t-o”, 以及 “too”,以及 “-to”,所有的表示都是同一个文本 “to”,但是通过对图片不同对其获得的。神经网络被训练于输出一个编码的文本(在神经网络的矩阵中编码)。

损失计算

我们需要计算每一个损失值,这个损失值是由图像核真实文本给出来训练 NN 的。你已经知道 NN 输出一个矩阵,包含一个得分,为每个文本在每个时间步上。一个小矩阵在图三中展示:有许多的时间步(t0, t1),以及三个字符(”a”, “b”, 以及 blank “-“).

此外,你已经知道,loss 是通过加和所有积分来进行计算的,通过这个方式,字符出现在图片的哪个位置不重要。

对于一个 alignment 的得分(或者 path;在文学中一般这么称呼)通过将相应的字符相乘。在上面的例子中,path”aa” 的得分是:0.4*0.4=0.16,”a-” 的得分是 0.4*0.6=0.24,”-a” 的得分是 0.6*0.4=0.24. 为了获得 GT 文本的得分,我们加和这个文本的所有 path 的得分。我们假设,GT 文本是 “a”,我们已经计算了所有长度为 “2” 的 path,分别是 “aa”,“a-”,“-a”,我们已经计算了这些 path 所有的得分,所以我们只需要把他们加起来,得到 0.4×0.4 + 0.4×0.6 + 0.6×0.4 = 0.64。如 GT 文本可能是 “”,我们可以看到只有一种相关的 path,那么就是 “–”,获得的得分是 0.6×0.6 =0.36.

如果仔细看,你已经发现我们计算了 GT 的可能性,但是不是 loss 值,而然,loss 知识概率的负对数。这个 loss 值是反向传播算法以及 NN 的参数更新使用的,我这里没有进行详细的讨论。

解码

当我们训练一个 NN,我们想要使用它来识别那些之前没有看到的图像。或者更多在更多的技术术语:我们想要计算,NN 输出的矩阵最可能是什么。你已经知道一个方法来计算给出文本的得分,但是现在,我们没有被给出任何文本,事实上,它正是我们正在寻找的文本。尝试所有可能的文本,如果他们只有很少的时间步以及字符,但是对于练习用例而言,这不可行。

一个简单而快速的算法,是最佳 path 解码,包含两个步骤:

  1. 它计算了最佳 path,通过获取最可能的每一个时间步的字符
  2. 首先删除重复的字符,然后删除 path 里面所有的空。这仍然表示了识别的文本。

正如 FIG4 所展示的,字符是 “a”,“b” 以及 “-”(空),一共有 5 个时间步。让我们应用最佳 path decoder 来处理这个矩阵。在 t0,最可能的是“a”,同样应用于 t1,t2. 空字符在 t3 是最可能的。最后,“b” 是 t4 时刻最可能的。这将给出我们一个 path“aaa-b”,我们删除了重复的字符,这将会返回“a-b”,然后我们删除 path 中的空,这给我们一个“ab”,作为我们输出的识别结果。

最佳 path 解析是,当容纳,仅仅是一个近似。构建样例容易给出错误的结果,比如用这个方法构建 FIG3,将会得到 “”,作为识别文本。但是,我们已经知道“” 结果的概率是 0.36,而 “a” 的概率是 0.64。而然,近似算法经常在练习的情景下给出比较好的结果。也有许多其他的比较好的 decoder,例如 beam-search,prefix-search 以及 token passing,这些关于语言结构的方法,都有利于提升结果。

结论以及展望

首先我们看得是,神经网络如何解决这个问题;然后,我们展示了 CTC 如何解决这些问题,然后,我们解释了 CTC 为啥能够工作,如何计算的 loss,以及如何解码 CTC 训练的 NN。

This should give you a good understanding of what is happening behind the scenes when you e.g. call functions like ctc_loss or ctc_greedy_decoder in TensorFlow. However, when you want to implement CTC yourself, you need to know some more details, especially to make it run fast. Graves et al. [1] introduce the CTC operation, the paper also shows all the relevant math. If you are interested in how to improve decoding, take a look at the articles about beam search decoding [2][3]. I implemented some decoders and the loss function in Python and C++, which you can find on github [4][5]. Finally, if you want to look at the bigger picture of how to recognize (handwritten) text, look at my article on how to build a handwritten text recognition system [6].