forked from baidu-research/warp-ctc
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request baidu-research#18 from ekelsen/master
Chinese tutorial
- Loading branch information
Showing
2 changed files
with
180 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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`。 |