Skip to content

Commit

Permalink
Merge pull request baidu-research#18 from ekelsen/master
Browse files Browse the repository at this point in the history
Chinese tutorial
  • Loading branch information
ekelsen committed Jan 20, 2016
2 parents ffd008a + ab5f8a7 commit 6041ac1
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 0 deletions.
2 changes: 2 additions & 0 deletions torch_binding/TUTORIAL.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Torch Tutorial

[In Chinese 中文版](TUTORIAL.zh_cn.md)

Make sure you have `warp-ctc` installed by running ```luarocks make torch_binding/rocks/warp-ctc-scm-1.rockspec``` at the top level directory.

Using the torch bindings, it is easy to experiment with CTC interactively.
Expand Down
178 changes: 178 additions & 0 deletions torch_binding/TUTORIAL.zh_cn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
## 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的激活序列和标签 ```daceba``` 之间的误差。

一个长度为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不能在连续的时间步上发出重复的符号(更多细节,[请见](http://www.cs.toronto.edu/~graves/icml_2006.pdf))。对于重复的符号必须用一个空白分开,所以唯一可能的对齐序列为`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`

0 comments on commit 6041ac1

Please sign in to comment.