本主题主要说明Tensor的属性及其使用;这些属性的设置,影响着数据的使用;Tensor最大的好处就是提供了计算跟踪,这个跟踪用来提供动态图的构建,这个与Tensorflow的静态图是不同滴。同时还提供了导数的逆向计算,这是目前神经网络中最成功的网络思想:前馈神经网络的实现基础(梯度下降算法);
回顾
- 因为Torch的核心是Tensor,Tensor的数据由Storage管理,所以这两个类的关系搞清楚,就可以使用Tensor了。
Tensor的Python构造器定义如下
Tensor.__init__(torch.device device)
Tensor.__init__(torch.Storage storage)
Tensor.__init__(Tensor other)
Tensor.__init__(tuple of ints size, torch.device device)
Tensor.__init__(object data, torch.device device)
Storage的Python构造器定义如下
FloatStorage.__init__() no arguments
FloatStorage.__init__(int size)
FloatStorage.__init__(Sequence data)
FloatStorage.__init__(torch.FloatStorage view_source)
FloatStorage.__init__(torch.FloatStorage view_source, int offset)
FloatStorage.__init__(torch.FloatStorage view_source, int offset, int size)
关于Tensor
Tensor与Numpy
-
实际上按照Python一贯的思路,会提供很多函数来替代构造器的使用,这样做有两个原因:
- 个性化,方便,简单;
- 使用工厂模式来创建对象,符合软件的常见设计模式,Python大量采用;
- 今后不要动不动就说面向对象最好,最方便。最直观,最方便的还是函数,拿来就用,不需要构建对象才能使用。
-
Torch号称是GPU版本的Numpy,Numpy有的Tensor都有,所以按照Numpy的思路,在构建好对象后,有三大块功能是需要数理下的,掌握这三大基础功能,后面的内容就容易理解:
- 基本属性
- 了解对象的内存与数据结构
- 基本操作
- 数据进出
- 数学运算
- 构建数据对象的最终目的就是计算;
- 计算的类别很多,基本数学运算,随机采样,线性代数的矩阵运算,统计计算,......
- 这里先明白基本的数学运算。
- 构建数据对象的最终目的就是计算;
- 基本属性
Tensor的官方文档结构
torch
Tensors
Creation Ops
Indexing, Slicing, Joining, Mutating Ops
Generators
Random sampling
In-place random sampling
Quasi-random sampling
Serialization
Parallelism
Locally disabling gradient computation
Math operations
Pointwise Ops
Reduction Ops
Comparison Ops
Spectral Ops
Other Operations
BLAS and LAPACK Operations
Utilities
- 这里先搞定Tensor本身的基本属性与操作
- 基本属性(从C/C++文档对应)
- 基本操作
- Indexing(索引访问操作)
- Slicing (切片访问操作【是索引的批量级升级版本】)
- Joining(数据组合与合并)
- Mutating Ops(数据访问:索引与切片的函数版本)
- 数学运算:
- Pointwise Ops(元素运算)
- Reduction Ops(降维运算)
- Comparison Ops(比较运算)
- Spectral Ops(谱运算)
- Other Operations(其他运算)
- BLAS and LAPACK Operations(线性代数运算)
- BLAS
- Basic Linear Algebra Subprograms(Fortran语言编写,Fortran史上经典古老的数学计算语言);
- LAPACK
- Linear Algebra Package,底层使用的也是BLAS;
- ATLAS
- Automatically Tuned Linear Algebra Software;
- OpenBLAS:
- 在编译时根据目标硬件进行优化,生成运行效率很高的程序或者库。OpenBLAS的优化是在编译时进行的,所以其运行效率一般比ATLAS要高。因此OpenBLAS对硬件的依赖比较高,换一个硬件平台可能会重新进行编译。
- cuBLAS与ACML:
- Intel的MKL和AMD的ACML都是在BLAS的基础上,针对自己特定的CPU平台进行针对性的优化加速。以及NVIDIA针对GPU开发的cuBLAS。
- BLAS
Tensor的基本属性与属性函数
- 先构建一个张量(Tensor)使用;
import torch
t_vector = torch.LongTensor(
data= [1, 2, 3, 4, 5]
)
print(t_vector)
t_matrix = torch.LongTensor(
data= [
[1, 2, 3, 4],
[5, 6, 7, 8]
]
)
print(t_matrix)
tensor([1, 2, 3, 4, 5])
tensor([[1, 2, 3, 4],
[5, 6, 7, 8]])
属性
属性-T
- 返回Tensor的转置;
print(t_vector.T) # 向量转置还是本身,不产生转置效果
print(t_matrix.T)
tensor([1, 2, 3, 4, 5])
tensor([[1, 5],
[2, 6],
[3, 7],
[4, 8]])
属性-data
- 返回张量的数据, 返回的也是张量,就是张量本身;
- 返回不同的id;
- 共享同一个Stroage;
- 但是data返回的数据状态改变:require s_grad = False,就是不能求导。
# 地址不同
print(t_vector.data)
print(type(t_vector.data))
print(id(t_vector), id(t_vector.data))
tensor([1, 2, 3, 4, 5])
4481379352 4510253344
# 数据相互影响
d = t_vector.data
t_vector[2] =88
print(d)
tensor([ 1, 2, 88, 4, 5])
# data与原张量的差异
属性-dtype
- Tensoor元素类型
print(t_vector.dtype)
torch.int64
属性-grad,grad_fn,requires_grad
- 导数:
- 默认是None
- 调用backward计算导数,导数是累加的。如果每次单独计算,需要清空;
- 导数的计算需要导数函数grad_fn(没有指定函数的张量无法计算导数)。
- grad_fn函数自动跟踪,需要设置requires_grad=True
- grad属性
print(t_vector.grad)
None
t_vector.backward()
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
in ()
----> 1 t_vector.backward()
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/torch/tensor.py in backward(self, gradient, retain_graph, create_graph)
116 products. Defaults to ``False``.
117 """
--> 118 torch.autograd.backward(self, gradient, retain_graph, create_graph)
119
120 def register_hook(self, hook):
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/torch/autograd/__init__.py in backward(tensors, grad_tensors, retain_graph, create_graph, grad_variables)
91 Variable._execution_engine.run_backward(
92 tensors, grad_tensors, retain_graph, create_graph,
---> 93 allow_unreachable=True) # allow_unreachable flag
94
95
RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn
- 属性-grad_fn
- 张量所在的导数函数
t1 = torch.Tensor([1.0])
t2 = torch.Tensor([1.0])
t3 = t1 + t2
print(t3)
print(t3.grad_fn)
tensor([2.])
None
- requires_grad属性
t1.requires_grad=True
t2.requires_grad=True
t4 = t1 + t2
print(t4)
print(t4.grad_fn)
print(type(t4.grad_fn))
print(t4.requires_grad)
print(t1.grad_fn)
tensor([5.], grad_fn=)
True
None
print(t4.grad)
t4.backward()
print(t4.grad) # 没有导数
print(t1.grad) # t1与t2导数(偏导数)
print(t2.grad)
print(t1.grad_fn)
None
None
tensor([2.])
tensor([2.])
None
属性-is_cuda,device
- 判断是否是cuda计算(GPU计算)
- device使用专门的类构造;
t1 = torch.Tensor([2.5])
print(t1.is_cuda)
False
t2 = torch.Tensor([2.5], device=torch.device('cpu:0'))
t2 = torch.Tensor([2.5], device=torch.device('cpu'))
print(t2.is_cuda)
False
t3 = torch.Tensor([2.5], device=torch.device('cuda'))
print(t3.is_cuda) # 苹果电脑不支持,请在Nvidia的显卡上运算,其他支持GPU运算的电脑上运行
# 在window上还需要安装厂商驱动
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
in ()
----> 1 t3 = torch.Tensor([2.5], device=torch.device('cuda'))
2 print(t3.is_cuda) # 苹果电脑不支持,请在Nvidia的显卡上运算,其他支持GPU运算的电脑上运行
RuntimeError: legacy constructor for device type: cpu was passed device type: cuda, but device type must be: cpu
# 判定电脑是否之处GPU运算
print(torch.cuda.is_available())
False
属性-is_leaf,grad与retain_grad函数
-
这个属性用来判定张量Tensor是否是Leaf Tensor,下面两种情况都应该是Leaf Tensor:
- 属性requires_grad为False的。
- 属性requires_grad=True,但是用户构建的Tensor,表示该张量不是计算结果,而是用户构建的初始张量。
运行backward后,仅仅只有Leaf Tensor在才会有grad属性。如果非Leaf Tensor需要具有grad属性,需要使用retain_grad函数开启grad属性。
# 演示叶子Tensor与grad,backward的关系
import torch
t1 = torch.Tensor([1.0]) # 用户构建的都是Leaf Tensor
t1.requires_grad=True
t2 = torch.Tensor([2.0])
t2.requires_grad=True
t3 = t1 + t2
t3.backward()
print(t1.is_leaf, t2.is_leaf, t3.is_leaf)
print(t1.grad) # Leaf Tensor的grad属性由backward函数产生。
True True False
tensor([1.])
# 演示Non-Leaf Tensor 与 retain_grad的关系
import torch
t1 = torch.Tensor([1.0]) # 用户构建的都是Leaf Tensor
t1.requires_grad=True
t2 = torch.Tensor([2.0])
t2.requires_grad=True
t3 = t1 + t2
t3.retain_grad() # 调用该函数后,t3才有grad属性,可以注释这个语句体验
t3.backward()
print(t1.is_leaf, t2.is_leaf, t3.is_leaf)
print(t3.grad)
True True False
tensor([1.])
属性-ndim与dim函数
- Tensor的维度
import torch
t1 = torch.Tensor([1.0, 20]) # 用户构建的都是Leaf Tensor
t2 = torch.Tensor(
[
[2.0, 1.0],
[1.0, 2.0]
]
)
print(t1.ndim) # 1 维
print(t2.ndim) # 2 维
print(t2.dim())
1
2
2
属性-shape与size函数
- Tensor的形状,与size函数一样
import torch
t1 = torch.Tensor([1.0, 20]) # 用户构建的都是Leaf Tensor
t2 = torch.Tensor(
[
[2.0, 1.0],
[1.0, 2.0]
]
)
print(t2.shape) # 属性shape
print(t2.size()) # 函数size()
torch.Size([2, 2])
torch.Size([2, 2])
属性-is_sparse
-
是否稀疏张量:
- 在矩阵中,若数值为0的元素数目远远多于非0元素的数目,并且非0元素分布没有规律时,则称该矩阵为稀疏矩阵;与之相反,若非0元素数目占大多数时,则称该矩阵为稠密矩阵。定义非零元素的总数比上矩阵所有元素的总数为矩阵的稠密度。
-
is_sparse该属性是只读,不可写的
- 稀疏张量提供专门的API产生。
- 稀疏张量有自己的构造规则:
- 稀疏张量被表示为一对致密张量:一维张量和二维张量的索引。可以通过提供这两个张量来构造稀疏张量,以及稀疏张量的大小。
# 默认的张量都是稠密张量
import torch
t1 = torch.Tensor([0, 0]) # 用户构建的都是Leaf Tensor
t2 = torch.Tensor(
[
[1, 0],
[0, 0]
]
)
print(t1.is_sparse) # 属性shape
print(t2.is_sparse) # 函数size()
t3 = torch.Tensor(1000,1000)
t3.fill_(0)
t3[0,0]=1
print(t3.is_sparse)
print(t3)
t3.is_sparse=True # 不能修改该属性,该属性是只读,不可写的。
False
False
False
tensor([[1., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
...,
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.]])
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
in ()
17 print(t3.is_sparse)
18 print(t3)
---> 19 t3.is_sparse=True
20 print()
AttributeError: attribute 'is_sparse' of 'torch._C._TensorBase' objects is not writable
# 稀疏矩阵
import torch
ts = torch.sparse.FloatTensor(2, 3)
print(ts.is_sparse)
print(ts)
print(ts.to_dense())
True
tensor(indices=tensor([], size=(2, 0)),
values=tensor([], size=(0,)),
size=(2, 3), nnz=0, layout=torch.sparse_coo)
tensor([[0., 0., 0.],
[0., 0., 0.]])
属性-layout
-
张量Tensor使用Storage表示都是一维的,其构成张量只要采用布局计算。这个布局使用layout属性设置
- 一般都是采用strided
- 稀疏矩阵的布局使用的是:
torch.sparse_coo
目前常用的就是这两种布局layout。
import torch
t1 = torch.Tensor([0, 0]) # 用户构建的都是Leaf Tensor
t2 = torch.Tensor(
[
[1, 0],
[0, 0]
]
)
print(t1.layout, t2.layout)
torch.strided torch.strided
属性-output_nr
- 在反向传播中存放输出。
- 具体用途先存疑。
import torch
t1 = torch.Tensor([0, 0]) # 用户构建的都是Leaf Tensor
t2 = torch.Tensor(
[
[1, 0],
[0, 0]
]
)
print(t2.output_nr)
0
# 演示Non-Leaf Tensor 与 retain_grad的关系
import torch
t1 = torch.Tensor([2.0]) # 用户构建的都是Leaf Tensor
t1.requires_grad=True
t2 = torch.Tensor([2.0])
t2.requires_grad=True
t3 = t1.sin()
print(t1.output_nr, t2.output_nr, t2.output_nr)
t3.retain_grad() # 调用该函数后,t3才有grad属性,可以注释这个语句体验
t3.backward()
print(t3)
print(t1.is_leaf, t2.is_leaf, t3.is_leaf)
print(t3.grad)
print(t1.output_nr, t2.output_nr, t2.output_nr)
0 0 0
tensor([0.9093], grad_fn=)
True True False
tensor([1.])
0 0 0
属性-其他
- is_mkldnn:intel提供的加速CPU运算的方法,判定是否CPU加速
- is_quantized:是否被量化(量化指将信号的连续取值近似为有限多个离散值)
- name:张量名
- volatile:新版本已经停用;
# 演示Non-Leaf Tensor 与 retain_grad的关系
import torch
t1 = torch.Tensor([2.0]) # 用户构建的都是Leaf Tensor
t1.requires_grad=True
t2 = torch.Tensor([2.0])
t2.requires_grad=True
t3 = t1 + t2
print(t1.is_mkldnn)
print(t2.name)
print(t2.is_quantized)
False
None
False
附录:mkldnn的使用
-
下载地址
https://github.com/intel/mkl-dnn
-
安装
- cmake安装,直接套路
如果torch不支持mkldnn,就需要使用源代码重新安装!