Skip to content

Latest commit

 

History

History
178 lines (125 loc) · 6.47 KB

TUTORIAL.zh_cn.md

File metadata and controls

178 lines (125 loc) · 6.47 KB

Torch教程

为了确保您成功将‘Warp-CTC’和Torch绑定,请在warp-ctc根目录中运行“luarocks make torch_binding/rocks/warp-ctc-scm-1.rockspec”。

现在,您可以非常容易的通过torch_binding来测试CTC。

假如你的编译没有GPU支持,请用torch.Tensor(...):float()替代torch.Tensor(...):cuda(),用cpu_ctc取代gpu_ctc

CTC是一种计算输入序列与目标输出序列之间相似程度的目标函数。由于CTC普遍运用于神经网络,我们称输入序列为激活序列。目标输出序列是从一个固定的字母表中得出的。 为了在此讨论, 我们选择了四个字母a,b,c,d. 算法需要一个<空白>符号区别于字母。这就意味着激活序列会是一个五维向量序列(字母的数量加<空白>)。这个向量(通过softmax函数)将会被转化成字母以及<空白>上的概率分布。

比如CTC可以用来衡量一个长度为7的激活序列和标签 dacba 之间的误差。

一个长度为7的激活序列就会是(向量的组成部分是任意的)

{<2,0,0,0,0>, <1,3,0,0,0>, <1,4,1,0,0>, <1,1,5,6,0>, <1,1,1,1,1>, <1,1,7,1,1>, <9,1,1,1,1>}

得到的有效输出序列即daceba.

一开始我们会举一个非常简单的例子。在这个例子中我们会用一个长度为1的激活序列,以及一个长度为1的目标输出序列。 为了指定这个激活序列,我们必须写下每一个五维向量的组成部分。我们使用<0,0,0,0,0>作为激活序列的单一向量,得到的概率分布及0.2,0.2,0.2,0.2,0.2. 对于目标输出,我们会用一个单一标签a.

首先,我们如何将数据展现给算法? 像平时使用Torch一样,激活表示要在一个2维张量中放入行。目标标签需要放入lua table, 每个目标标签序列都有一个对应的表。 我们每一个标签仅有一个序列,因此当标签a有指数1时,表即{{1}} (指数0预留给空白符号)。因为我们允许输入不同长度的激活序列的可能性,我们需要指定 输入激活序列的长度,在这个例子即包涵一个lua table{1}的1.

为了计算以上问题(单一元素输入序列,单一输出标签)的CTC损失函数的价值, 只有一种可能的对齐方式,所以符号必须在第一个时间步(time step)发出。 发出符号的概率为0.2。 算法返回的负对数似然值为-ln(0.2)=1.6094.

现在让我们通过代码来做计算。先从Torch部分开始,需要代码库。

假如你有GPU的支持

th>require 'cutorch'  

如果仅有CPU

th>require 'warp_ctc'  

请将激活输入行-- 注意用两个大括号

th>acts = torch.Tensor({{0,0,0,0,0}}):cuda()

假如输入为空,梯度计算则不能完成。

th>grads = torch.Tensor():cuda()

对于目标标签以及输入序列的大小

th>labels = {{1}}
th>sizes ={1}

如果你有CUDA支持,请使用gpu_ctc ,否则请使用cpu_ctc

th> gpu_ctc(acts, grads, labels, sizes)

{
1 : 1.6094379425049
}

对每一组序列,函数会返回CTC损失的一个lua table.

现在,我们来看一个更有意思的例子。假如我们有一个长度为3的输入序列,激活后:

<1,2,3,4,5>,<6,7,8,9,10> and <11,12,13,14,15>.

对应这些帧的概率则为

0.0117, 0.0317, 0.0861, 0.2341, 0.6364

(在这个特殊例子中,每一帧的概率都一样) 对于目标符号,我们将使用序列c,c.

th>acts = torch.Tensor({{1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15}}):cuda()
th>labels = {{3,3}}
th>sizes = {3}

CTC计算了所有可能对齐的概率。请注意目标包涵了重复的符号c.CTC不能在连续的时间步上发出重复的符号(更多细节,请见)。对于重复的符号必须用一个空白分开,所以唯一可能的对齐序列为c <空白> c.

CTC假设,在给定数据的情况下,标签概率是有条件独立的,所以我们期待的答案即Pr(c at frame 1)*Pr(<空白> at frame 2)*Pr(c at frame 3) = 0.2341*0.0117*0.2341 and -ln(0.2341*0.0117*0.2341) = 7.3522.

th> gpu_ctc(acts, grads, labels, sizes)

{
1 : 7.355742931366
}

小的数值差由于其中一个计算人工完成。

假设目标序列为b,c,激活序列则为

<-5,-4,-3,-2,-1>,<-10,-9,-8,-7,-6> and <-15,-14,-13,-12,-11>.

对应这些帧的概率则为

0.0117, 0.0317, 0.0861, 0.2341, 0.6364.

由于重复的符号被清空,空白被取消,现在有五种可能的对齐 <空白> b c, b <空白> c, b c <空白>, b b c and b c c.

结果应当是 -ln(3*0.0117*0.0861*0.2341 + 0.0861*0.0861*0.2341 + 0.0861*0.2341*0.2341) = 4.9390

th>acts = torch.Tensor({{-5,-4,-3,-2,-1},{-10,-9,-8,-7,-6},{-15,-14,-13,-12,-11}}):cuda()
th>labels = {{2,3}}
th>sizes = {3}
th>gpu_ctc(acts, grads, labels, sizes)

{
1 : 4.938850402832
}

因此,我们有三个例子。最后一个例子显示如果通过算法将3个例子做迷你批处理 (minibatch). 标签现在是{{1}, {3,3}, {2,3}},输入序列的长度是{1,3,3}. 我们必须将输入序列放入一个单独的两维矩阵。通过交织输入序列的元素,我们的输入矩阵如下: 为了清楚起见,我们从前两个输入序列开始

entries col1 col2 col3 col4 col5
seq1 item 1 0 0 0 0 0
seq2 item 1 1 2 3 4 5
seq1 item 2 P P P P P
seq2 item 2 6 7 8 9 10
seq1 item 3 P P P P P
seq2 item 3 11 12 13 14 15

由于第一个序列没有第二个或第三个元素,我们用0填入矩阵(在上面一个表格中显示为P)。 现在我们将第三个序列放入表格中

entries col1 col2 col3 col4 col5
seq1 item 1 0 0 0 0 0
seq2 item 1 1 2 3 4 5
seq3 item 1 -5 -4 -3 -2 -1
seq1 item 2 P P P P P
seq2 item 2 6 7 8 9 10
seq3 item 2 -10 -9 -8 7 -6
seq1 item 3 P P P P P
seq2 item 3 11 12 13 14 15
seq3 item 3 -15 -14 -13 -12 -11

在Torch中完整的例子如下

th>acts = torch.Tensor({{0,0,0,0,0},{1,2,3,4,5},{-5,-4,-3,-2,-1},
{0,0,0,0,0},{6,7,8,9,10},{-10,-9,-8,-7,-6},
{0,0,0,0,0},{11,12,13,14,15},{-15,-14,-13,-12,-11}}):cuda()
th>labels = {{1}, {3,3}, {2,3}}
th>sizes = {1,3,3}
th>gpu_ctc(acts, grads, labels, sizes)

{
1 : 1.6094379425049
2 : 7.355742931366
3 : 4.938850402832
}

为了获取接下来激活的梯度,传递和激活张量同样大小的张量即可。 如果想看更多例子,请见torch_binding/tests/test.lua