pytorch张量运算
2.1 数据操作
- 深度学习落实到计算表现为矩阵计算
- pytorch、tensorflow中,计算的基本组件是Tensor。张量即多维数组,是矩阵计算的基本单元。
- Tensor:张量,一维张量即向量vector,二维张量即 二维数组。张量是n维数组的统称
- python中有专门进行矩阵计算的库:numpy。pytorch和tensorflow等 和 numpy除了矩阵计算的相似点之外,多了如下额外的功能:
- 张量支持GPU加速;numpy仅仅支持CPU
- 张量支持自动微分,可以自动进行求导
2.1.1 创建张量
- 创建向量 & 改变向量的大小
x = torch.arrange(12, dtype=torch.float) # 创建vector,除非额外指定,否则其存储于GPU中
x = x.reshape(3,4) # 改变tensor的形状
x = torch.zeros((2,3,4)) # 创建全0向量
x = torch.ones((2,3,4)) # 创建全1向量
x = torch.randn(3,4) # 创建 均值为0,方差为1的标准高斯分布
x = torch.tensor([[2,1,4,3],[1,2,3,4],[4,3,2,1]]) # 将list转化为tensor
x.shape # 矩阵维度
x.size() # 张量维度
x.T # 矩阵转置
查看张量位置
id(x) #查看tensor x引用地址
x.device #查看tensor x位于cpu还是gpu上
2.1.2 运算符
- 按元素计算,下列常见运算均支持:
- 常用的+、-、x,/ 均支持按照元素计算
- 求幂运算\(x**y\)
- 一元运算符 比如 \(torch.exp(x)\)
- x == y
- 当x和y相等的时候,逐元素判断是否相等
- 向量叠加或者连结(concatenate)
- 多个张量concatenate
- dim = 0 按照行连接
- dim = 1 按照列连接
torch.cat((x,y), dim=0) # 按行叠加
torch.cat((x,y), dim=1) # 按列叠加
2.1.3 广播机制
- 广播机制指的是:当两个向量之间进行操作的时候,由于不满足传统条件,numpy 或者 pytorch会自动进行补全
a = torch.arange(3).reshape((3,1))
b = torch.arange(2).reshape((1,2))
a+b
换算成numpy
a=np.arange(3).reshape((3,1))
b=np.arange(2).reshape((1,2))
>>> a
array([[0],
[1],
[2]])
>>> b
array([[0, 1]])
a进行复制列,b进行复制行。将a和b分别变成长度为 3*2的矩阵后,进行想加
>>> a+b
array([[0, 1],
[1, 2],
[2, 3]])
2.1.4 索引和切片
张量中的元素可以进行索引操作,和python的数组操作一致
- 0表示第一个元素;-1表示最后一个元素,可以通过指定范围选择某个范围中的数据 ;
- 特殊符号":" 表示 沿某一个轴的所有元素
- 可以利用切片操作对选择范围中的数据进行赋值
X[1,2]=9
>>> x=np.arange(9).reshape(3,3)
>>> x
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
>>> x[0,:]
array([0, 1, 2])
2.1.5 原位计算
矩阵操作某些写法会导致变量重新分配内存,即变量对应的内存地址会发生变化。ex: X = X + Y
。但是有时候为了节省内存,我们更希望原位操作。针对矩阵,如下是表示原位操作:
- 更新矩阵:
X[:] = X + Y
或者X += Y
*=
函数id(变量)
用来表示变量引用指向的内存空间的地址,可以用来判断两个变量是否是内存上相同。
>>> X=np.zeros(4).reshape(2,2)
>>> X
array([[0., 0.],
[0., 0.]])
>>> Y = np.ones(4).reshape(2,2)
>>> id(X)
140502555798464
>>> X += Y
>>> id(X)
140502555798464
注意:Pytorch代码中很多操作都是原位操作,远不止这种写法
函数中,以_
结尾的函数,都是原位操作,会改变tensor本身。
比如:y.add_(x)
,x.copy_(y)
,x_t()
, x.squeeze_()
,x.unsqueeze_()
,mul_
,reshape_()
In [69]: id(a)
Out[69]: 139970692801520
In [70]: a.add_(a)
Out[70]:
tensor([[ 0., 4., 8.],
[12., 16., 20.]])
In [71]: id(a)
Out[71]: 139970692801520
2.1.6 转换为其他python对象
- 将pytorch定义的tensor 转换为numpy(底层都一样)
A = X.numpy()
B = torch.tensor(A) # 进行转换
type(A), type(B)
(numpy.ndarray, torch.Tensor)
- 将常规张量转化为numpy
a=torch.tensor([2,3])
b=a.numpy()
type(a),type(b)
(torch.Tensor, numpy.ndarray)
- 将大小为1的张量转换为python标量,使用item 或者 python的内置函数
a = torch.tensor([3.5])
a.item(), float(a), int(a) # 将张量转化为标量
适用于numpy
>>> a=np.array([1.0])
>>> type(a.item())
<type 'float'>
>>> type(int(a))
<type 'int'>
2.1.7 张量叠加与切割
张量的拼接:几个小张量拼接成一个大的张量。主要的函数有torch.cat
,torch.stack
张量的切割:将一个张量切割成几个小的。主要的函数有torch.chunk
,torch.split
下面注意介绍:
torch.cat
功能:在给定维度上对张量序列进行叠加操作,所有的张量必须有相同的形状(连接维度除外)或者为空
In [87]: a
Out[87]:
tensor([[0., 1., 2.],
[3., 4., 5.]])
In [88]: torch.cat((a,a),dim=0) # 沿行叠加
Out[88]:
tensor([[0., 1., 2.],
[3., 4., 5.],
[0., 1., 2.],
[3., 4., 5.]])
In [89]: torch.cat((a,a),dim=1) # 沿列叠加
Out[89]:
tensor([[0., 1., 2., 0., 1., 2.],
[3., 4., 5., 3., 4., 5.]])
torch.stack
功能:在一个新的维度上,叠加张量,所有的张量必须是相同的大小
In [92]: a # shape: 3*2
Out[92]:
tensor([[0., 1., 2.],
[3., 4., 5.]])
In [93]: torch.stack((a,a),dim=0) # shape 2 * 2 * 3
Out[93]:
tensor([[[0., 1., 2.],
[3., 4., 5.]],
[[0., 1., 2.],
[3., 4., 5.]]])
In [97]: torch.stack((a,a),dim=1) # shape torch.Size([2, 2, 3])
Out[97]:
tensor([[[0., 1., 2.],
[0., 1., 2.]],
[[3., 4., 5.],
[3., 4., 5.]]])
In [100]: torch.stack((a,a),dim=2) # shape torch.Size([2, 3, 2])
Out[100]:
tensor([[[0., 0.],
[1., 1.],
[2., 2.]],
[[3., 3.],
[4., 4.],
[5., 5.]]])
torch.chunk
用法:torch.chunk(*input*, *chunks*, *dim=0*) → List of Tensors
功能:将一个大的tensor按照不同的维度分割成k个小的tensor。每个数据块都是输入张量的一个视图。
In [4]: a=torch.arange(6).reshape(2,3)
In [5]: a.chunk(3)
Out[5]: (tensor([[0, 1, 2]]), tensor([[3, 4, 5]]))
In [6]: a.chunk(3,dim=0) # 按照维度为0进行分割
Out[6]: (tensor([[0, 1, 2]]), tensor([[3, 4, 5]]))
In [7]: a.chunk(3,dim=1) # 按照维度为1进行分割
Out[7]:
(tensor([[0],
[3]]),
tensor([[1],
[4]]),
tensor([[2],
[5]]))
torch.split
用法:torch.split(*tensor*, *split_size_or_sections*, *dim=0*)
功能:将张量分成数据块,每个数据块都是原始张量的视图。和 torch.chunk功能相同
- 当split_size_or_sections 为整数的时候,函数会沿着维度方向尽量切割成长度为plit_size_or_sections的tensor
- 当split_size_or_sections 是list的时候,函数根据list的设置,沿着设定的维度方向,切割成长度不同的tensor
In [13]: a
Out[13]:
tensor([[0, 1],
[2, 3],
[4, 5],
[6, 7],
[8, 9]])
In [16]: torch.split(a,2,dim=0) # 按照行分
Out[16]:
(tensor([[0, 1],
[2, 3]]),
tensor([[4, 5],
[6, 7]]),
tensor([[8, 9]]))
In [17]: torch.split(a,2,dim=1) # 按照列分
Out[17]:
(tensor([[0, 1],
[2, 3],
[4, 5],
[6, 7],
[8, 9]]),)
2.1.8 维度变化
维度变化函数可以直接对tensor的维度进行更改,比如 torch.flatten
, torch.squeeze
,torch.unsqueeze
。严格意义上来讲,torch.split
,torch.cat
还是传统的 聚合函数如torch.sum
也是维度变化。
torch.flatten:扁平化
用法:torch.flatten(*input*, *start_dim=0*, *end_dim=-1*) → [Tensor]
功能:将张量进行进行压扁操作(扁平化操作)。 如果张量是1维,不需要压扁;如果张量是2维,压扁到1维;如果张量是3维,那么可以根据实际的需求进行全部压扁或者部分压扁操作。
输入:
- start_dim:从那一维度开始flatten
- end_dim:从那一维度结束flatten的操作
注意:维度dim从0开始,-1表示最后一维。
案例: 二维压一维
In [115]: a=torch.arange(4).reshape(2,2)
In [116]: a
Out[116]:
tensor([[0, 1],
[2, 3]])
In [117]: torch.flatten(a)
Out[117]: tensor([0, 1, 2, 3])
三维压二维
In [126]: a=torch.arange(12).reshape(2,2,3)
In [127]: a.flatten(0,1) # 将第0维消除 变成 4*3
Out[127]:
tensor([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
In [128]: a.flatten(0,2) # 等价于a.flatten, a.flatten(0,-1)直接压到以维
Out[128]: tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
In [129]: a.flatten(1,2) # 将第1维压了,变成 2 * 6 按照什么方向压的
Out[129]:
tensor([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11]])
torch.squeeze
用法:torch.squeeze(*input*, *dim=None*) → [Tensor]
功能:squeeze 是挤压,消除,去掉的意思
- 对维度进行压缩,不设置参数,就是去掉矩阵中所有维数为1的维度。比如矩阵(1*3) 去掉维数为1的,大小变为长度为(3) 的tensor
- 如果设置dim,那么就是去掉指定维为1的操作 如 矩阵(
2*1*2
), dim=1,那么输出矩阵就是2*2
- squeeze 和 unsqueeze对矩阵的数据个数都没有损失
案例
In [138]: a=torch.arange(3).reshape(1,3)
In [139]: b=torch.squeeze(a)
In [140]: b,b.shape
Out[140]: (tensor([0, 1, 2]), torch.Size([3]))
去掉指定维度
In [147]: a=torch.arange(6).reshape(2,1,3)
In [148]: b=torch.squeeze(a,dim=1)
In [149]: b.shape
Out[149]: torch.Size([2, 3])
torch.unsequeeze
用法:torch.unsqueeze(*input*, *dim*) → [Tensor]
功能:在指定位置增加维数为1的维度,比如一个长度为3的向量,在dim=0位置增加一个维度,就是(1*3),在dim=1的位置增加一个维度,张量维度就变为(3*1
)
In [151]: a=torch.tensor([1,2,3])
In [152]: b=a.unsqueeze(dim=0)
In [153]: c=a.unsqueeze(dim=1)
In [154]: b.shape,c.shape
Out[154]: (torch.Size([1, 3]), torch.Size([3, 1]))
2.2 矩阵乘法介绍
torch中涉及矩阵乘法函数较多:如
- torch.mul, torch.multiply,*
- torch.dot, torch.mv,torch.mm,torch.bmm,torch.matmul
矩阵乘法最基本有两种方式:
- 点乘:按元素相乘。即两个矩阵中相同位置元素的乘积构成了新矩阵相应位置的元素
- 叉乘:
2.2.1 点乘
*
、torch.mul
,torch.multiply
三者含义相同。
两个张量的点乘注意:
- 两个张量维度相同 ,按相同位置相同元素相乘
- 两个张量维度不同的时候,先通过广播转化为相同
向量和向量
In [29]: a=torch.ones(3,dtype=torch.float)
In [30]: b=torch.arange(3,dtype=torch.float)
In [31]: a*b,torch.mul(a,b)
Out[31]: (tensor([0., 1., 2.]), tensor([0., 1., 2.]))
In [32]: a*b == torch.mul(a,b)
Out[32]: tensor([True, True, True])
向量和矩阵
In [33]: a=torch.arange(6,dtype=torch.float).reshape(2,3)
In [34]: b=torch.ones(3,dtype=torch.float)
In [35]: a
Out[35]:
tensor([[0., 1., 2.],
[3., 4., 5.]])
In [36]: b
Out[36]: tensor([1., 1., 1.])
In [37]: torch.mul(a,b)
Out[37]:
tensor([[0., 1., 2.],
[3., 4., 5.]])
矩阵和矩阵
In [41]: a=torch.arange(6,dtype=torch.float).reshape(2,3)
In [42]: a
Out[42]:
tensor([[0., 1., 2.],
[3., 4., 5.]])
In [43]: b=a
In [45]: torch.mul(a,b)
Out[45]:
tensor([[ 0., 1., 4.],
[ 9., 16., 25.]])
2.2.2 叉乘
叉乘就是传统的矩阵方法,两个向量的叉乘即两个向量的内积。
向量和向量 torch.dot
In [3]: a=torch.ones(3,dtype=torch.float)
In [4]: b=torch.arange(3,dtype=torch.float)
In [5]: a
Out[5]: tensor([1., 1., 1.])
In [7]: b
Out[7]: tensor([0., 1., 2.])
In [8]: torch.dot(a,b) # 矩阵点乘
Out[8]: tensor(3.)
向量和矩阵 torch.mv
In [11]: a=torch.arange(6,dtype=torch.float).reshape(2,3)
In [12]: b=torch.ones(3,dtype=torch.float)
In [13]: a
Out[13]:
tensor([[0., 1., 2.],
[3., 4., 5.]])
In [14]: b
Out[14]: tensor([1., 1., 1.])
In [15]: torch.mv(a,b)
Out[15]: tensor([ 3., 12.])
矩阵和矩阵 torch.mm
In [16]: a=torch.arange(6,dtype=torch.float).reshape(2,3)
In [17]: b=torch.ones(3,2,dtype=torch.float)
In [18]: a
Out[18]:
tensor([[0., 1., 2.],
[3., 4., 5.]])
In [19]: b
Out[19]:
tensor([[1., 1.],
[1., 1.],
[1., 1.]])
In [20]: torch.mm(a,b)
Out[20]:
tensor([[ 3., 3.],
[12., 12.]])
批量矩阵乘法 torch.bmm
bmm 表示批量矩阵乘法。能够进行多个矩阵的点乘。bmm分别表示batch(批量)、matrix(矩阵)、matrix(矩阵)的首个字母
In [22]: a=torch.tensor([[[1,2],[3,4]],[[1,2],[3,4]],[[1,2],[3,4]]],dtype=torch.float)
In [23]: b=torch.tensor([[[3,4],[1,2]],[[3,4],[1,2]],[[3,4],[1,2]]],dtype=torch.float)
In [26]: a.shape,b.shape
Out[26]: (torch.Size([3, 2, 2]), torch.Size([3, 2, 2]))
In [27]: torch.bmm(a,b)
Out[27]:
tensor([[[ 5., 8.],
[13., 20.]],
[[ 5., 8.],
[13., 20.]],
[[ 5., 8.],
[13., 20.]]])
**矩阵乘法统一接口 **torch.matmul
torch.matmul 可以用来计算向量与向量,向量与矩阵,矩阵与矩阵,批量矩阵之间的乘法
2.2.3 torch.einsum
可以包含上述所有操作。
torch.einsum('ij->ji',a) # 矩阵转置
In [48]: torch.einsum('ij->',a) # 矩阵元素整体求和
Out[48]: tensor(15.)
In [49]: torch.einsum('ij->i',a) # 矩阵元素列求和
Out[49]: tensor([ 3., 12.])
In [50]: torch.einsum('ij->j',a) # 行求和
Out[50]: tensor([3., 5., 7.])
矩阵相乘
In [51]: a
Out[51]:
tensor([[0., 1., 2.],
[3., 4., 5.]])
In [52]: b = a
In [53]: torch.einsum('ij,jk->ik',a,b.T) # 矩阵相乘
Out[53]:
tensor([[ 5., 14.],
[14., 50.]])
向量矩阵相乘
In [60]: a
Out[60]:
tensor([[0., 1., 2.],
[3., 4., 5.]])
In [61]: c
Out[61]: tensor([1., 1., 1.])
In [62]: torch.einsum('ij,j->i',a,c)
Out[62]: tensor([ 3., 12.])
2.3 数据预处理
2.4 自动微分
深度学习需要求导,通过导数明确优化方向。深度学习的工具都支持自动求导,即自动微分。自动微分的实现依赖于 计算图 和 链式求导规则。通过链式求导法则,可以将求导计算分解为一系列有限的可微分算子,而后通过计算图将整个运算过程描述出来
计算图
计算图是用来描述运算过程的有向无环图。计算图主要有两个元素:节点(node) 和边(edge)。
-
节点表示数据变量,如向量、矩阵,张量
-
边表示运算,比如加减乘除
torch.mul()
,torch.mm()
,torch.div
等
叶子节点
叶子借点即leaf_node。叶子节点是用户自己构建的变量,反向传播过程即终点对叶子阶段进行求导。
链式求导法则
线索条
- https://www.cnblogs.com/leimu/p/14578392.html
1、pandas 操作
2、数据类型
3、原位操作
- https://blog.csdn.net/qq_34243930/article/details/106886993
- https://blog.csdn.net/m0_38129460/article/details/90405086
4、构建layer
5、自动微分
- 自动微分:通过计算机求微分
- 计算图
- 动态图
- 静态图
- 叶子节点
- 引申:原位计算
6、detach、eval
标签:dim,运算,tensor,torch,矩阵,张量,pytorch,Out From: https://www.cnblogs.com/douniwanli/p/18344412