Torch是一个广泛支持机器学习算法的科学计算框架。易于使用且高效,主要得益于一个简单的和快速的脚本语言LuaJIT,和底层的C / CUDA实现。
核心特征的总结:
1. 一个强大的n维数组
2. 很多实现索引,切片,移调transposing的例程
3. 惊人的通过LuaJIT的C接口
4. 线性代数例程
5. 神经网络,并基于能量的模型
6. 数值优化例程
7. 快速高效的GPU支持
8. 可嵌入,可移植到iOS,Android和FPGA的后台
Torch目标是让你通过极其简单过程、最大的灵活性和速度建立自己的科学算法。Torch有一个在机器学习领域大型生态社区驱动库包,包括计算机视觉软件包,信号处理,并行处理,图像,视频,音频和网络等,基于Lua社区建立。Torch被Facebook人工智能研究实验室和位于伦敦的谷歌DeepMind大量使用。
我最近在学习torch,在学习的过程中,把学习的内容,总结为博客,在方便自己的同时,也能够分享给真正喜欢编程,喜欢计算机视觉的同行。
Tensor是Torch中最基础的类,是网络基本的数据单元。在Torch7中,一个Tensor可以是任意维度的,维度数目可以通过LongStorage来设定,可以通过dim()或者nDimension()方法获取Tensor的维度,可以用size()获取所有的维度。例如:
x=torch.Tensor(1,2,3,2)
s=torch.LongStorage(5)
s[1] = 2 s[2] =2
s[3] = 2 s[4] = 4 s[5] = 1
print(x:nDimension()) # 5
x:size()
2
2
2
4
1
[torch.LongStorage of size 5]
在CNN网络中,Tensor一般来说是一个四维的数组,分别是batchSize, channel,width,height。在Torch中,数组的下标从1开始,到size()结束,这点和c/c++/java这些语言不同。访问Tensor的内容,可以直接使用下标的方式,或者使用Storage()函数。storage是Torch的一种访问内存的基本方式,有点类似C语言的数据类型。相关细节,参考文档here。
以一个三维的Tensor为例:
th> x = torch.Tensor(5,5,5)
[0.0001s]
th> x[3][4][5]
2.1723719562371e-153
[0.0001s]
th> x:storage()[x:storageOffset()+(3-1)*x:stride(1)+(4-1)*x:stride(2)+(5-1)*x:stride(3)]
2.1723719562371e-153
storageOffset返回Tensor第一个位置(1),stride(i)返回第i维的长度。第二种方式,可以这样理解,storage()操作以后,在内存中本质是一维的数组结构,用相对于第一个位置的偏置,进行访问。*注意的是,在Tensor中同一行的元素总是连续的。*Tensor构造函数是不进行初始化值的,需要手动初始化。
th> x=torch.Tensor(4,5) [0.0001s]
th> i = 0 [0.0000s]
th> x:apply(function()
..> i=i+1
..> return i
..> end)
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
[torch.DoubleTensor of size 4x5] [0.0230s]
th> x
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
[torch.DoubleTensor of size 4x5] [0.0002s]
th> x:stride()
5
1
[torch.LongStorage of size 2]
Torch中,Tensor主要有ByteTensor(无符号char),CharTensor(有符号),ShortTensor(shorts), IntTensor(ints), LongTensor(longs), FloatTensor(floats), DoubleTensor(doubles),默认存放为double类型,如果需要特别指出,通过torch.setdefaulttensortype()方法进行设定。例如torch.setdefaulttensortype(‘torch.FloatTensor’)。
在Torch中,所有的Tensor都共享同一个变量的内存,也就是说所有的操作都是在同一块内存上操作,这样的好处就是减少了内存复制所需要的时间,提高了运行速度,缺点也很明显,就是同一块内存的数据不能够持久,如果需要内存复制,可以使用copy()或者clone()方法。从下面的例子,可以看出这点:
th> x=torch.Tensor(2,2)
[0.0001s]
th> i = 0
[0.0000s]
th> x:apply(function()
..> i = i+1
..> return i
..> end)
1 2
3 4
[torch.DoubleTensor of size 2x2]
[0.0002s]
th> y = torch.Tensor(x)
[0.0001s]
th> y
1 2
3 4
[torch.DoubleTensor of size 2x2]
[0.0002s]
th> y:zero()
0 0
0 0
[torch.DoubleTensor of size 2x2]
[0.0202s]
th> x
0 0
0 0
[torch.DoubleTensor of size 2x2]
Tensor构造函数,创建新的tensor,分配内存,但是不进行初始化值,tensor的值是随机产生的。Tensor主要有以下几种构造函数:
th> s=torch.Storage(10):fill(1)
[0.0001s]
th> x=torch.Tensor(s,1,torch.LongStorage{2,5})
[0.0001s]
th> x
1 1 1 1 1
1 1 1 1 1
[torch.DoubleTensor of size 2x5]
[0.0002s]
th> i = 0
[0.0001s]
th> x:apply(function() i = i +1 ;return i end)
1 2 3
4 5 6
7 8 9
[torch.DoubleTensor of size 3x3]
[0.0003s]
th> torch.le(x,3)
1 1 1
0 0 0
0 0 0
[torch.ByteTensor of size 3x3]
[0.0383s]
th> s = torch.Storage(10):fill(1)
[0.0001s]
th> sz = torch.LongStorage({2,5})
[0.0001s]
th> x = torch.Tensor()
[0.0000s]
th> x:set(s, 1, sz)
1 1 1 1 1
1 1 1 1 1
[torch.DoubleTensor of size 2x5]
[0.0002s]
这种类似于矩阵的切片操作,把tensor看作一个多维的立体,sub-tensor就是从不同的维度进行切分,获得那部分元素。主要方法有narrow,select,sub 和index,indexcopy两种类型,前面三种方法切片和原来的tensor共享同一块内存,修改sub-tensor同样会影响到原来的tensor,后面的两种使用了内存的复制,sub-tensor和原来的tensor之间不存在相互影响关系。
th> x = torch.Tensor(5,5):zero()
[0.0001s]
th> x:sub(2,3):fill(5)
5 5 5 5 5
5 5 5 5 5
[torch.DoubleTensor of size 2x5]
[0.0002s]
th> x
0 0 0 0 0
5 5 5 5 5
5 5 5 5 5
0 0 0 0 0
0 0 0 0 0
[torch.DoubleTensor of size 5x5]
[0.0002s]
th> x:sub(2,3,1,3):fill(4)
4 4 4
4 4 4
[torch.DoubleTensor of size 2x3]
[0.0002s]
th> x
0 0 0 0 0
4 4 4 5 5
4 4 4 5 5
0 0 0 0 0
0 0 0 0 0
[torch.DoubleTensor of size 5x5]
[0.0002s]
th> x:select(1,2):fill(2)
2
2
2
2
2
[torch.DoubleTensor of size 5]
[0.0002s]
th> x
0 0 0 0 0
2 2 2 2 2
4 4 4 5 5
0 0 0 0 0
0 0 0 0 0
[torch.DoubleTensor of size 5x5]
[0.0003s]
th> x:select(2,4):fill(5)
5
5
5
5
5
[torch.DoubleTensor of size 5]
[0.0001s]
th> x
0 0 0 5 0
2 2 2 5 2
4 4 4 5 5
0 0 0 5 0
0 0 0 5 0
[torch.DoubleTensor of size 5x5]
[0.0002s]
th> x = torch.rand(5,5)
[0.0001s]
th> y = x:index(1,torch.LongTensor{1,3,5})
[0.0001s]
th> y
0.1808 0.0048 0.7778 0.9197 0.6483
0.9615 0.7223 0.3947 0.2457 0.3692
0.6159 0.4022 0.8646 0.9339 0.9750
[torch.DoubleTensor of size 3x5]
[0.0003s]
th> x
0.1808 0.0048 0.7778 0.9197 0.6483
0.0281 0.7912 0.2421 0.0364 0.7939
0.9615 0.7223 0.3947 0.2457 0.3692
0.7204 0.4008 0.7808 0.6333 0.1705
0.6159 0.4022 0.8646 0.9339 0.9750
[torch.DoubleTensor of size 5x5]
[0.0003s]
th> x = torch.rand(5,5)
[0.0001s]
th> x
0.5205 0.4188 0.8317 0.5865 0.7641
0.5979 0.6973 0.9968 0.7034 0.9907
0.7545 0.1290 0.6260 0.0854 0.2256
0.9613 0.7150 0.2935 0.4983 0.6745
0.8547 0.0898 0.3636 0.2664 0.2693
[torch.DoubleTensor of size 5x5]
[0.0003s]
th> z = torch.rand(5,2)
[0.0001s]
th> z
0.6829 0.8207
0.6153 0.6383
0.5977 0.0682
0.9388 0.8328
0.0213 0.4133
[torch.DoubleTensor of size 5x2]
[0.0002s]
th> x:indexCopy(2,torch.LongTensor{1,5},z)
0.6829 0.8207
0.6153 0.6383
0.5977 0.0682
0.9388 0.8328
0.0213 0.4133
[torch.DoubleTensor of size 5x2]
[0.0003s]
th> x
0.6829 0.4188 0.8317 0.5865 0.8207
0.6153 0.6973 0.9968 0.7034 0.6383
0.5977 0.1290 0.6260 0.0854 0.0682
0.9388 0.7150 0.2935 0.4983 0.8328
0.0213 0.0898 0.3636 0.2664 0.4133
[torch.DoubleTensor of size 5x5]
[0.0004s]
th> a = torch.Tensor(5,2):fill(100)
[0.0001s]
th> a
100 100
100 100
100 100
100 100
100 100
[torch.DoubleTensor of size 5x2]
[0.0003s]
th> x:indexAdd(2,torch.LongTensor{1,2},a)
100 100
100 100
100 100
100 100
100 100
[torch.DoubleTensor of size 5x2]
[0.0004s]
th> x
100.6829 100.4188 0.8317 0.5865 0.8207
100.6153 100.6973 0.9968 0.7034 0.6383
100.5977 100.1290 0.6260 0.0854 0.0682
100.9388 100.7150 0.2935 0.4983 0.8328
100.0213 100.0898 0.3636 0.2664 0.4133
[torch.DoubleTensor of size 5x5]
[0.0005s]
th> x:indexFill(2,torch.LongTensor{1,2},0)
0
[0.0002s]
th> x
0.0000 0.0000 0.8317 0.5865 0.8207
0.0000 0.0000 0.9968 0.7034 0.6383
0.0000 0.0000 0.6260 0.0854 0.0682
0.0000 0.0000 0.2935 0.4983 0.8328
0.0000 0.0000 0.3636 0.2664 0.4133
[torch.DoubleTensor of size 5x5]
[0.0004s]
th> x = torch.rand(5,5)
[0.0001s]
th> x
0.0607 0.1297 0.5422 0.1601 0.9574
0.8516 0.8973 0.7576 0.9423 0.7036
0.2549 0.5626 0.0977 0.0586 0.3961
0.3502 0.7438 0.3298 0.8991 0.2819
0.7011 0.4185 0.4517 0.5621 0.6326
[torch.DoubleTensor of size 5x5]
[0.0003s]
th> y = x:gather(1, torch.LongTensor{{1, 2, 3, 4, 5}, {2, 4, 3, 5, 1}})
[0.0001s]
th> y
0.0607 0.8973 0.0977 0.8991 0.6326
0.8516 0.7438 0.0977 0.5621 0.9574
[torch.DoubleTensor of size 2x5]
[0.0002s]
th> z = x:gather(2, torch.LongTensor{{1, 3}, {2, 4}, {3, 5}, {4, 1}, {5, 2}})
[0.0001s]
th> z
0.0607 0.5422
0.8973 0.9423
0.0977 0.3961
0.8991 0.3502
0.6326 0.4185
[torch.DoubleTensor of size 5x2]
[0.0003s]
th> x
0.4603 0.5985 0.0979 0.5345 0.8319
0.0198 0.2303 0.6146 0.7637 0.5176
[torch.DoubleTensor of size 2x5]
[0.0002s]
th> y = torch.zeros(3,5):scatter(1,torch.LongTensor{{1,2,3,1,1},{3,1,1,2,3}},x)
[0.0001s]
th> y
0.4603 0.2303 0.6146 0.5345 0.8319
0.0000 0.5985 0.0000 0.7637 0.0000
0.0198 0.0000 0.0979 0.0000 0.5176
[torch.DoubleTensor of size 3x5]
[0.0002s]
th> x = torch.rand(3,3,3):mul(3):floor():int()
[0.0001s]
th> torch.nonzero(x)
1 1 1
1 1 2
1 2 2
1 2 3
1 3 1
1 3 2
2 1 1
2 1 2
2 2 2
2 3 1
2 3 3
3 1 1
3 1 2
3 1 3
3 2 1
3 3 2
[torch.LongTensor of size 16x3]
[0.0004s]
th> x = torch.rand(3,1)
[0.0001s]
th> x
0.5418
0.2531
0.4533
[torch.DoubleTensor of size 3x1]
[0.0002s]
th> y = torch.expand(x,3,3)
[0.0001s]
th> y
0.5418 0.5418 0.5418
0.2531 0.2531 0.2531
0.4533 0.4533 0.4533
[torch.DoubleTensor of size 3x3]
[0.0002s]
th> y:fill(5)
5 5 5
5 5 5
5 5 5
[torch.DoubleTensor of size 3x3]
[0.0002s]
th> x
5
5
5
[torch.DoubleTensor of size 3x1]
[0.0001s]
th> x = torch.rand(3)
[0.0001s]
th> x
0.9585
0.3585
0.9067
[torch.DoubleTensor of size 3]
[0.0002s]
th> torch.repeatTensor(x,3,3)
0.9585 0.3585 0.9067 0.9585 0.3585 0.9067 0.9585 0.3585 0.9067
0.9585 0.3585 0.9067 0.9585 0.3585 0.9067 0.9585 0.3585 0.9067
0.9585 0.3585 0.9067 0.9585 0.3585 0.9067 0.9585 0.3585 0.9067
[torch.DoubleTensor of size 3x9]
[0.0004s]