前言:
今天小伙伴们对“cadd函数怎么用”都比较关心,姐妹们都想要知道一些“cadd函数怎么用”的相关知识。那么小编也在网络上搜集了一些对于“cadd函数怎么用””的相关内容,希望各位老铁们能喜欢,姐妹们快快来了解一下吧!先看Tensor,
看Tensor的源码之前,先看看如何使用Tensor,这有助于理解Tensor设计的目的。
这个在torch7中最主要的类:
多维矩阵
一个 Tensor 一个 多维矩阵multi-dimensional matrix. 矩阵可以有多少维度?由LongStorage决定
例子程序:
--- creation of a 4D-tensor 4x5x6x2 z = torch.Tensor(4,5,6,2) --- for more dimensions, (here a 6D tensor) one can do: s = torch.LongStorage(6) s[1] = 4; s[2] = 5; s[3] = 6; s[4] = 2; s[5] = 7; s[6] = 3; x = torch.Tensor(s)
用 nDimension() 或者 dim()可以或者到Tensor的维度个数。
具体第i个维度的长度可使用size(i)得到.。
一个LongStorage 所以维度的具体长度。
> x:nDimension()6> x:size() 4 5 6 2 7 3[torch.LongStorage of size 6]
Tensor就是一个多维矩阵,multi-dimensional matrix。矩阵的维度没有被限制,决定于LongStorage,可以是3维矩阵,4维矩阵,...,6维矩阵等。
内部数据
Tensor对应的具体数据保存在Storage中,可用Storage()访问。
Tensor的内存对应唯一的Storage,地址可以不相邻:Storage的地址通过storageOffset()获取,如果要访问某维度i的第j个元素,就需要用stride(i)跳过去,即这个元素的地址在:storageOffset()+stride(i)*(j-1),我们来看个例子,又一个3D Tensor:
x = torch.Tensor(7,7,7)
获取(3,4,5)位置的元素可以这样:
> x[3][4][5]
或者也可以这样: (但是这样访问很慢!)
> x:storage()[x:storageOffset() +(3-1)*x:stride(1)+(4-1)*x:stride(2)+(5-1)*x:stride(3)]
或者我们可以这样说:Tensor只不过是Stroage的一种特殊访问方式,这种访问方式就好像让这一堆存储对应成了多维数据。
x = torch.Tensor(4,5)s = x:storage()for i=1,s:size() do -- 填充Storage s[i] = iend> x -- s这个storage被x解释为一个2D矩阵 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20[torch.DoubleTensor of dimension 4x5]
同时注意到,在Torch7中,同一行的元素(即多维矩阵的最后一个维度的元素)在内存中是连续的。
x = torch.Tensor(4,5)i = 0x:apply(function() i = i + 1 return iend)> x 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20[torch.DoubleTensor of dimension 4x5]> x:stride() 5 1 -- element in the last dimension are contiguous![torch.LongStorage of size 2]
这就非常像C了(不是Fortran).
不同数据类型的Tensors
存在下列Tensor:
ByteTensor -- contains unsigned charsCharTensor -- contains signed charsShortTensor -- contains shortsIntTensor -- contains intsLongTensor -- contains longsFloatTensor -- contains floatsDoubleTensor -- contains doubles
大部分的数值计算,只实现了FloatTensor和DoubleTensor。其他类型的Tensor只在你想节省存储时才有用。
高效的内存管理
Efficient memory management
All tensor operations in this class do not make any memory copy. All these methods transform the existing tensor, or return a new tensor referencing the same storage. This magical behavior is internally obtained by good usage of the stride() and storageOffset(). Example:
所有对tensor的操作,都不能开启新的内存空间。
都是对已经存在的内存做in-place操作,
操作完成后,返回一个新的tensor,但是指向同样的内存。
x = torch.Tensor(5):zero()> x00000[torch.DoubleTensor of dimension 5]> x:narrow(1, 2, 3):fill(1) -- narrow() returns a Tensor -- referencing the same Storage as x> x 0 1 1 1 0[torch.Tensor of dimension 5]
使用copy()才会开启新内存:
y = torch.Tensor(x:size()):copy(x)
或者使用clone()
y = x:clone()
下面我们看Tensor的methods,如果你想操作不同类型的Tensor,使用CharTensor即可。
Tensor有很多methods。像add,mul,填充fill,更改大小,获取子矩阵等。
这里就查看一个比较特殊的methods吧,
[Tensor] gather(dim, index)
挑选出一些元素组成新的矩阵
当dim=1时,挑选的某个元素序号为::index[i][j][k],j, k
当dim=2时,挑选的某个元素序号为::i , index[i][j][k] , k
当dim=3时,挑选的某个元素序号为::i, j, index[i][j][k]
-- dim = 1result[i][j][k]... = src[index[i][j][k]...][j][k]...-- dim = 2result[i][j][k]... = src[i][index[i][j][k]...][k]...-- etc.
src 是被挑选的 Tensor.
例如:src 的 size 是 n x m x p x q, dim = 3, 我们希望在第3维度上取3个值,我们将得到一个 size 为 n x m x k x q 的矩阵。
gather操作返回的矩阵,将保存一个新的地址。
x = torch.rand(5, 5)> x 0.7259 0.5291 0.4559 0.4367 0.4133 0.0513 0.4404 0.4741 0.0658 0.0653 0.3393 0.1735 0.6439 0.1011 0.7923 0.7606 0.5025 0.5706 0.7193 0.1572 0.1720 0.3546 0.8354 0.8339 0.3025[torch.DoubleTensor of size 5x5]y = x:gather(1, torch.LongTensor{{1, 2, 3, 4, 5}, {2, 3, 4, 5, 1}})> y 0.7259 0.4404 0.6439 0.7193 0.3025 0.0513 0.1735 0.5706 0.8339 0.4133[torch.DoubleTensor of size 2x5]z = x:gather(2, torch.LongTensor{{1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 1}})> z 0.7259 0.5291 0.4404 0.4741 0.6439 0.1011 0.7193 0.1572 0.3025 0.1720[torch.DoubleTensor of size 5x2]
去看下Tensor的具体实现代码:
这个文件里只是一些include,先看下general.h
除了一些std库的文件,还有一些预定义,最有用的就是8--9 行:luaT.h,TH.h
TH.h里面果然有内容,THTensor.h,THStorage.h等都是重点。
只看头文件,大概可以猜测:torch7中的tensor,是在对TH库的封装。那么这个TH是啥意思?Torch取第一个字母和最后一个字母的意思?
姑且先这么认为吧。
我们找到TH库中,lib/TH/generic/THTensor.h
THTensor是C中的结构体,这就是张量的基本数据结构了。
long *size: 存储THTensor每个维度的长度的数组
long *stride: 存储每个维度在寻址时跳转的量的数组
这两个量的存在,是为了用维度的方式去访问那一堆内存地址的。
THStorage *storage: Tensor元素存储的内存
ptrdiff_t storageOffset: 访问具体某个元素时,地址的开始量
int refcount: 引用计数,有点像智能指针的计数
char flag:还不清楚,
上图是THTensor结构体的访问函数,这个语法有点奇怪,也不知道该怎么查。
这里函数名字是:THTensor_(storage),难道这是个预定义?
从语法上理解,THTensor_只能是个宏定义,果然:
宏定义内部还有宏TH_CONCAT_4,不过这个从名字上看,就是连接4个字符串的意思啦,我们搜索一下:
其中116行的TH_CONCAT_4_EXPAND是源头的宏定义。
回到最开始的问题:
这里函数名字是:THTensor_(storage),就可以展开为:THRealTensor_storage,这就是实际的函数名。
每个函数前,都有个TH_API,
原来这里是指定extern “C”,C++调用C 时,需要指定。参考:
和dllexport或者dllimport的,
具体:
终于把语法搞清楚了。
可以看这个结构是如何构建的了:
THRealTensornew函数内部先THAlloc,然后又THRealTensorrawInit了一下。
上图又是两个建立新tensor的函数。可以看到,这个过程中,就是在不断调用THStora ge的过程。
再接着看看计算部分:
这里有个cadd函数,它是TH库的函数
它用在了torch7/generic/TensorOperator.c中,被算子所调用了。
但是这个TensorOperator.c中,是定义了几种简单的算子:加,减,乘,除等。
我搜索了整个torch7,都没法优先️地方调用卷积函数。
难道在torch7库中,只是定义了,没有使用它?
终于,看了torch7中的TensorMath.lua,发现了conv2等算子。
torch7中,使用了luaT把 TH库中的C代码封装了一下,然后又些lua代码来调用TH中定义的结构体和函数。
lua这门脚本语言也挺简单的,看这里:
luaT是把C封装到lua中使用的库,在这里:
看torch7的文件结构:
torch7编译好以后,应该可以写lua脚本来调用。
编译过程:
编译好后,写lua脚本即可,可以参考torch7的项目开发示范代码:
找到这个train()函数了,如下
后面可以train函数里面使用的相关函数为起点,再追查到luaT库和TH库,从而把torch7的整个架构搞清楚。
比如说,以
图中所示的model:forward函数为起点。
在查找model时,发现处理torch包,还有nn包,就回到torch的github上看,原来这里不止troch一个包。
整个torch包含torch7,nn,tutorial, distro,demos,cutorch等,
需要将nn,demos等下载,然后再做解读。
总结:
torch7只是一个提供基本数据结构与cpu算子的包,如果需要完成网络构建,需要使用lua语言nn库,如果使用gpu,需要使用cutorch包。
这份代码非常底层,就比较做到逻辑清晰,相比直接看pytorch的代码,显然看torch代码会理解的更快。
标签: #cadd函数怎么用