百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

「C/C++」 torch7中的基本数据结构与cpu算子:TH库和luaT库

toyiye 2024-06-24 19:12 12 浏览 0 评论


先看Tensor,

看Tensor的源码之前,先看看如何使用Tensor,这有助于理解Tensor设计的目的。

这个在torch7中最主要的类:https://gitee.com/anjiang2020_admin/torch7/blob/master/doc/tensor.md

多维矩阵

一个 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] = i
end
> 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 = 0

x:apply(function()
  i = i + 1
  return i
end)

> 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 chars
CharTensor -- contains signed chars
ShortTensor -- contains shorts
IntTensor -- contains ints
LongTensor -- contains longs
FloatTensor -- contains floats
DoubleTensor -- 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()
> x
0
0
0
0
0
[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 = 1
result[i][j][k]... = src[index[i][j][k]...][j][k]...

-- dim = 2
result[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 时,需要指定。参考:https://www.cnblogs.com/xiangtingshen/p/10980055.html

和dllexport或者dllimport的,

具体:https://www.cnblogs.com/foohack/p/4119207.html

终于把语法搞清楚了。

可以看这个结构是如何构建的了:


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这门脚本语言也挺简单的,看这里:https://www.runoob.com/lua/lua-object-oriented.html

luaT是把C封装到lua中使用的库,在这里:https://gitee.com/anjiang2020_admin/torch7/tree/master/lib/luaT#luat_newmetatable

看torch7的文件结构:

torch7编译好以后,应该可以写lua脚本来调用。

编译过程:https://blog.csdn.net/real_myth/article/details/52291636

编译好后,写lua脚本即可,可以参考torch7的项目开发示范代码:

https://gitee.com/anjiang2020_admin/torch7/tree/master/tutorials-master

找到这个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代码会理解的更快。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码