学校网站建设方面汇报,网站移动端优化工具,横沥镇仿做网站,徐州便民信息网关于Pytorch中的张量学习
张量的概念和创建
张量的概念
Tensor是pytorch中非常重要且常见的数据结构#xff0c;相较于numpy数组#xff0c;Tensor能加载到GPU中#xff0c;从而有效地利用GPU进行加速计算。但是普通的Tensor对于构建神经网络还远远不够#xff0c;我们需…关于Pytorch中的张量学习
张量的概念和创建
张量的概念
Tensor是pytorch中非常重要且常见的数据结构相较于numpy数组Tensor能加载到GPU中从而有效地利用GPU进行加速计算。但是普通的Tensor对于构建神经网络还远远不够我们需要能够构建计算图的 tensor这就是 Variable。Variable 是对 tensor 的封装主要用于自动求导。
data被包装的tensorgraddata的梯度grad_fn创建tensor的function是自动求导的关键requires_grad指示是否需要梯度is_leaf指示是否是叶子节点用户创建的张量
从pytorch 0.4.0开始Variable并入了tensor也就是这些属性直接存在于tensor
张量的创建
torch.tensor() 从data创建tensor
data数据可以是listNumpydtype数据类型默认与data一致device所在设备cuda/cpurequires_grad是否需要梯度pin_memory是否存于锁页内存
torch.from_numpy()从Numpy创建tensor 利用torch.from_numpy创建tensor与原ndarray共享内存修改其中一个的数据另一个也会改变。
torch.zeros()以size创建全0张量
size张量的形状如(3, 3), (3, 224, 224)out输出的张量layout内存中的布局形式有stridedsparse_coo等device所在设备,cuda/cpurequires_grad是否需要梯度
torch.zeros_like()以输入张量的形状创建全0张量 torch.ones torch.ones_like()
torch.full()以size来创建指定数值的张量
size张量的形状fill_value张量的值 torch.full_like()
torch.arange()创建等差的1维张量
start数列起始值end数列结束值左闭右开step数列公差默认为1
torch.linspace()创建均分的1维张量 注意事项区间为[start, end]
start数列起始值end数列结束值steps数列长度步长(end-start)/(steps-1)
torch.logspace()创建对数均分的1维张量
start数列起始值end数列结束值steps数列长度base对数函数的底默认为10
torch.eye()创建单位对角矩阵
torch.normal()创建正太分布高斯分布
mean均值std标准差size张量形状当mean和std均为标量时需要设定
mean为标量std为标量 mean为标量std为张量 mean为张量std为标量 mean为张量std为张量
torch.randn():生成标准正太分布即均值为0标准差为1 torch.randn_like()
torch.rand()在区间[0, 1)上生成均匀分布 torch.rand_like()
torch.randint()在区间[low, high)生成整数均匀分布 torch.randint_like()
张量操作
张量的拼接和切分
torch.cat()和torch.stack()
torch.cat()将张量按照维度dim进行拼接 torch.stack()在新创建的维度dim上进行拼接
tensors张量序列dim要拼接的维度
import torch
a torch.ones((2, 3))
print(a)
tensor([[1., 1., 1.],[1., 1., 1.]])t0 torch.cat([a, a], dim0)
print(t0)
tensor([[1., 1., 1.],[1., 1., 1.],[1., 1., 1.],[1., 1., 1.]])t1 torch.cat([a, a], dim1)
print(t1)
tensor([[1., 1., 1., 1., 1., 1.],[1., 1., 1., 1., 1., 1.]])torch.cat()是在原来的维度上进行拼接假设针对二维张量在第0维进行拼接就是增加行数在第1维进行拼接则是增加列数。
t0 torch.stack([a, a], dim2)
print(t0)
print(t0.shape)
tensor([[[1., 1.],[1., 1.],[1., 1.]],[[1., 1.],[1., 1.],[1., 1.]]])
torch.Size([2, 3, 2])t1 torch.stack([a, a], dim0)
print(t1)
print(t1.shape)
tensor([[[1., 1., 1.],[1., 1., 1.]],[[1., 1., 1.],[1., 1., 1.]]])
torch.Size([2, 2, 3])torch.stack()是在新建的维度上进行拼接假设针对二维张量只有0维和1维那么stack可以选择在第2维上进行拼接拼接原理 a的size为(2, 3)第2维拼接那么就需要给a增加第2维即
[[[1.], [1.], [1.]],
[[1.], [1.], [1.]]]然后再把要拼接的张量按顺序添加到第2维中即
[[[1., 1.], [1., 1.], [1., 1.]],
[[1., 1.], [1., 1.], [1., 1.]]]针对二维张量只有0维和1维如果stack选择在第0维进行拼接那么就会将张量的原来两维后移然后新建0维拼接原理 a的size为(2, 3)新建第0维即外面再加一层大括号
[[[1., 1., 1.],[1., 1., 1.]]
]然后再把要拼接的张量添加到第0维中即
[[[1., 1., 1.],[1., 1., 1.]],[[1., 1., 1.],[1., 1., 1.]]
]torch.chunk()和torch.split()
torch.chunk()将张量按维度dim进行平均切分 注意事项若不能整除最后一份张量小于其他张量参数
input要切分的张量chunks要切分的份数dim要切分的维度
a torch.rand((2, 5))
print(a)
tensor([[0.6590, 0.3914, 0.0760, 0.8725, 0.2828],[0.3575, 0.8045, 0.9501, 0.9880, 0.7684]])print(torch.chunk(a, chunks2, dim1))
(tensor([[0.6590, 0.3914, 0.0760],[0.3575, 0.8045, 0.9501]]),
tensor([[0.8725, 0.2828],[0.9880, 0.7684]]))切分得到的张量维度大小计算5(dim1维度的大小)/2(chunks切分的份数),结果向上取整
torch.split()将张量按维度dim进行切分参数
tensor要切分的张量split_size_or_sections为int时表示每一份的长度为list时按照list元素切分dim要切分的维度
print(torch.split(a, 2, dim1))
(tensor([[0.6590, 0.3914],[0.3575, 0.8045]]), tensor([[0.0760, 0.8725],[0.9501, 0.9880]]), tensor([[0.2828], [0.7684]])
)print(torch.split(a, [2, 1, 2], dim1))
(tensor([[0.6590, 0.3914],[0.3575, 0.8045]]), tensor([[0.0760],[0.9501]]), tensor([[0.8725, 0.2828],[0.9880, 0.7684]])
)这里第2个参数指定为list时其和应该等于要切分张量指定dim维度大小否则会报错。
张量索引
torch.index_select()和torch.masked_select() torch.index_select()在维度dim上按index索引数据 返回值以index索引数据拼接的张量参数
input要索引的张量dim要索引的维度index要索引数据的序号
a torch.randint(0, 9, size(3, 3))
tensor([[0, 5, 1],[8, 0, 6],[2, 8, 5]])
# dtype必须设置为long类型
idx torch.tensor([0, 2], dtypetorch.long)
torch.index_select(a, dim0, indexidx)
tensor([[0, 5, 1],[2, 8, 5]])torch.index_select(a, dim1, indexidx)
tensor([[0, 1],[8, 6],[2, 5]])
torch.masked_select()按mask中的True进行索引返回一维张量参数
input要索引的张量mask与input同形状的布尔类型张量
mask a.ge(5)
print(mask)
tensor([[False, True, False],[ True, False, True],[False, True, True]])
torch.masked_select(a, mask)
tensor([5, 8, 6, 8, 5])
ge表示greater than or equal即大于等于 gt表示greater than即大于 le表示less than or equal即小于等于 lt表示less than即小于
张量变换
tensor.view()、torch.reshape()、torch.resize_()
torch.view()可以调整tensor的形状但必须保证调整前后元素总数一致。view不会修改自身的数据返回的新tensor与原tensor共享内存即更改一个另一个也随之改变。
import torcha torch.arange(0,6).view(2, 3)
print(a)tensor([[0, 1, 2],[3, 4, 5]])b a.view(-1, 2) # 当某一维是-1时会自动计算它的大小
print(b)tensor([[0, 1],[2, 3],[4, 5]])
这里建议先看一下后面关于连续性的知识torch.view()无法用于不连续的tensor只有将不连续的tensor转化为连续的tensor利用contiguous()才能使用view()。
reshape() 和 view() 的区别 1当 tensor 满足连续性要求时reshape() view()和原来 tensor 共用存储区 2当 tensor不满足连续性要求时reshape() **contiguous() view()会产生有新存储区的 tensor与原来tensor 不共用存储区。
前面说到的 view()和reshape()都必须要用到全部的原始数据比如你的原始数据只有12个无论你怎么变形都必须要用到12个数字不能多不能少。因此你就不能把只有12个数字的 tensor 强行 reshape 成 2×5 的。 但是 resize_() 可以做到无论原始存储区有多少个数字我都能变成你想要的维度数字不够怎么办随机产生凑数字多了怎么办就取我需要的部分
截取时会改变原tensor a但不会改变storage地址和值都不变且a和b共用storage这里是2638930351680 。
import torch# 原tensor a
a torch.tensor([1,2,3,4,5,6,7])
print(a)
print(a.storage())
print(a.storage( ).data_ptr())
tensor([1, 2, 3, 4, 5, 6, 7])1234567
[torch.LongStorage of size 7]
2638930351680# b是a的截取并reshape成2×3
b a.resize_(2,3)
print(a)
print(b)
tensor([[1, 2, 3],[4, 5, 6]]) # a变了
tensor([[1, 2, 3],[4, 5, 6]])print(a.storage())
print(b.storage())1234567
[torch.LongStorage of size 7]1234567
[torch.LongStorage of size 7]print(a.storage( ).data_ptr())
print(b.storage( ).data_ptr())
2638930352576
2638930352576
添加时会改变原tensor a且会改变storage地址和值都变但a和b还是共用storage这里是2638924338752 。
a torch.tensor([1,2,3,4,5])
print(a)
print(a.storage())
print(a.storage( ).data_ptr())
tensor([1, 2, 3, 4, 5])12345
[torch.LongStorage of size 5]
2638924334528b a.resize_(2,3)
print(a)
print(b)
tensor([[1, 2, 3],[4, 5, 0]]) # a变了
tensor([[1, 2, 3],[4, 5, 0]])print(a.storage())
print(b.storage())123450
[torch.LongStorage of size 6]123450
[torch.LongStorage of size 6]print(a.storage( ).data_ptr())
print(b.storage( ).data_ptr())
2638924338752
2638924338752
pytorch中实际还有torch.resize()方法网上基本很少有介绍
unsequeeze()和sequeeze()
unsequeeze(dim)用来在维度dim上增加1维sequeeze(dim)用来在dim上减少维度。
import torcha torch.tensor([[1, 2], [3, 4], [5, 6]])
print(a.size())
torch.Size([3, 2])b a.unsqueeze(0) # 与unsqueeze(-3)等价倒数第三维
c a.unsqueeze(1) # 与unsqueeze(-2)等价
d a.unsqueeze(2) # 与unsqueeze(-1)等价倒数第一维即第二维也是最后一维
e a.unsqueeze(3) # 报错提示dim的可选范围为[-3, 2]# dim0在最外层加一维即加一组[]
tensor([[[1, 2],[3, 4],[5, 6]]])# dim1, 进去一层[],然后加一维即加[]且每遇到一个逗号都加一组[]
tensor([[[1, 2]],[[3, 4]],[[5, 6]]])# dim2, 进去两层[],然后加一维同样每遇到一个逗号都加一组[]
tensor([[[1],[2]],[[3],[4]],[[5],[6]]])
sequeeze(dim)用来在dim上减少维度但是如果指定的dim的size不等于1是无法删掉维度的。
import torcha torch.tensor([[1, 2], [3, 4], [5, 6]])
print(a.squeeze(0)) # 删除无效第0维size为3
tensor([[1, 2],[3, 4],[5, 6]])print(a.squeeze(1)) # 删除无效第1维size为2
tensor([[1, 2],[3, 4],[5, 6]])
# 与unsqueeze相似这里dim的取值范围为[-2,1],-2与1等价-1与0等价a torch.tensor([[1], [2], [3]])
a.squeeze(0) # 删除无效第0维size为3
print(a.squeeze(1)) # 删除成功tensor([1, 2, 3])
如果不指定要删除的dim则删除所有size为1的维度
a torch.tensor([[[1], [2], [3]]])
print(a.squeeze())tensor([1, 2, 3])
tensor.expand()
参数为传入指定shape在原shape数据上进行高维拓维根据维度值进行重复赋值。
import torchx torch.tensor([1,2,3,4])
x.shape
torch.Size([4])# x拓展一维变1x4
x1 x.expend(1,4)
print(x1)
tensor([[1, 2, 3, 4]])
print(x1.shape)
torch.Size([1, 4])# x1拓展一维,增加2行变2x4多加的一行重复原值
x2 x1.expend(2,1,4)
print(x2)
tensor([[[1, 2, 3, 4]],[[1, 2, 3, 4]]])
print(x2.shape)
torch.Size([2, 1, 4])# x2拓展一维增加2行变为2x2x1x4,多加的一行重复原值
x3 x2.expand(2,2,1,4)
print(x3)
tensor([[[[1, 2, 3, 4]],[[1, 2, 3, 4]]],[[[1, 2, 3, 4]],[[1, 2, 3, 4]]]])
print(x3.shape)
torch.Size([2, 2, 1, 4])# x直接拓展2个维度变为2x1x4,
x4 x.expand(2,1,4)
print(x4)tensor([[[1, 2, 3, 4]],[[1, 2, 3, 4]]])
注意
只能拓展维度比如A的shape为2x4的不能 A.expend(1,4)只能保证原结构不变在前面增维比如A.expand(1,2,4)可以增加多维比如x的shape为(4),x.expand(2,2,1,4)只需保证本身是4不能拓展低维比如x的shape为(4)不能x.expand(4,2)。
维度交换和转置
torch.transpose()和torch.t()后者是前者的简化版本当tensor的维度为2时直接使用t()就是转置当维度大于2时则不能使用t()要使用transpose()且必须指定要交换的两个维度。
对于两维的tensor
import torcha torch.tensor([[1, 2, 3], [4, 5, 6]])
print(a.t()) # 转置tensor([[1, 4],[2, 5],[3, 6]])# 必须指定要交换的两个维度
print(a.transpose(1, 0)) # 等价于a.transpose(0, 1)tensor([[1, 4],[2, 5],[3, 6]])
对于三维的tensor
a torch.tensor([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])print(a)
tensor([[[ 1, 2, 3],[ 4, 5, 6]],[[ 7, 8, 9],[10, 11, 12]]])print(a.transpose(0, 1))
tensor([[[ 1, 2, 3],[ 7, 8, 9]],[[ 4, 5, 6],[10, 11, 12]]])print(a.transpose(0, 2))
tensor([[[ 1, 7],[ 4, 10]],[[ 2, 8],[ 5, 11]],[[ 3, 9],[ 6, 12]]])print(a.transpose(1, 2))
tensor([[[ 1, 4],[ 2, 5],[ 3, 6]],[[ 7, 10],[ 8, 11],[ 9, 12]]])
如何理解三维张量的维度交换操作呢可以把张量a每个元素的坐标列出来
(0, 0, 0), (0, 0, 1), (0, 0, 2)
(0, 1, 0), (0, 1, 1), (0, 1, 2)
(1, 0, 0), (1, 0, 1), (1, 0, 2)
(1, 1, 0), (1, 1, 1), (1, 1, 2)
a.transpose(0, 1)以元素4为例其坐标为(0, 1, 0)交换0维和1维则变为(1, 0, 0)所以交换完成后元素4的位置坐标为(1, 0, 0)。
permute()方法与transpose方法的功能类似 但是其作用是让tensor按照指定维度顺序进行转置其参数的个数就是张量的维度数。
当tensor为两维时
import torchc torch.tensor([[1, 2, 3], [4, 5, 6]])print(c.permute(1, 0)) # 等价于.t()
tensor([[1, 4],[2, 5],[3, 6]])# c.permute(0, 1)不会做任何改变
当tensor为三维时
import torcha torch.tensor([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])print(a)
tensor([[[ 1, 2, 3],[ 4, 5, 6]],[[ 7, 8, 9],[10, 11, 12]]])# 等价于transpose(0, 1)
print(a.permute(1, 0, 2))
tensor([[[ 1, 2, 3],[ 7, 8, 9]],[[ 4, 5, 6],[10, 11, 12]]])# 等价于transpose(0, 2)
print(a.permute(2, 1, 0))
tensor([[[ 1, 7],[ 4, 10]],[[ 2, 8],[ 5, 11]],[[ 3, 9],[ 6, 12]]])# 等价于transpose(1, 2)
print(a.permute(0, 2, 1))
tensor([[[ 1, 4],[ 2, 5],[ 3, 6]],[[ 7, 10],[ 8, 11],[ 9, 12]]])
除了上述这些转换方式permute还可以permute(1, 2, 0)、
permute(2, 0,1)也就是同时改变三个维度的顺序所以permute的功能要比transpose更强大。
另外还有a.T的用法.T是permute的简化版本其功能是把张量的维度逆序重新排列假设一个tensor-a共有n维a.T等价于a.permute(n-1, n-2, ..., 0)。
针对上述例子c(二维张量), c.T与c.t()等价a(三维张量), a.T与a.permute(2, 1, 0)等价。
张量的数学运算
加减乘除
torch.add() torch.addcdiv() torch.addcmul() torch.sub() torch.div() torch.mul()
对数指数幂函数
torch.log() torch.log10() torch.log2() torch.exp() torch.pow()
三角函数
torch.abs() torch.acos() torch.cosh() torch.cos() torch.asin() torch.atan() torch.atan2()
torch.add()逐元素计算 input alpha x other参数
input第一个张量alpha乘项因子默认为1other第二个张量
torch.addcmul()加法结合除法参数
input输入张量value乘项因子默认为1tensor1除法运算的被除数tensor2除法运算的除数
out input value x (tensor1/tensor2)
torch.addcmul()加法结合乘法参数
input输入张量value乘项因子默认为1tensor1乘法运算的第一个张量tensor2乘法运算的第二个张量
out input value x tensor1 x tensor2
torch.max()与tensor.min()
torch.max() 获取张量中的最大值和索引torch.min()获取张量中的最小值和索引两个方法用法完全一致下面以max举例。
两个主要参数input张量和dim维度
当输入张量为1维即向量
import torcha torch.tensor([1, 2, 3, 4, 5, 6])
print(torch.max(a)) # 不指定dim则仅返回最大值tensor(6)value, index torch.max(a, dim0)
print(value)
print(index)tensor(6) # 最大值
tensor(5) # 最大值所在的索引
当输入张量为2维
vec torch.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])print(torch.max(vec)) # 不指定dim则仅返回张量中的最大值tensor(12)# dim0获取每一列的最大值和索引
value, index torch.max(a, dim0)
print(value)
print(index)tensor([10, 11, 12])
tensor([3, 3, 3])# dim1获取每一行的最大值和索引
value, index torch.max(a, dim1)
print(value)
print(index)tensor([ 3, 6, 9, 12])
tensor([2, 2, 2, 2])
当输入张量为3维(这时候有点难理解啦需要动脑思考特别是需要立体思维想象)
# 二维张量是一个平面三维就看成多个平面叠加在一起
vec torch.tensor([[[7, 2, 3], [4, 11, 6]],[[1, 8, 9], [10, 5, 12]]
])# dim0就是获取不同平面上同一位置值最大的一个3D空间想象起来
value, index torch.max(vec, dim0)
print(value)
print(index)tensor([[ 7, 8, 9],[10, 11, 12]])
tensor([[0, 1, 1],[1, 0, 1]])# dim1(1)、先单独看一个平面相当于二维张量时dim0即获取同一平面内每一列的最大值(2)、与(1)类似获取每一个平面内每一列的最大值
value, index torch.max(vec, dim1)
print(value)
print(index)tensor([[ 7, 11, 6],[10, 8, 12]])
tensor([[0, 1, 1],[1, 0, 1]])# dim2, (1)、先单独看一个平面相当于二维张量时dim1即获取同一平面内每一行的最大值(2)、与(1)类似获取每一个平面内每一行的最大值
value ,index torch.max(vec, dim2)
print(value)
print(index)tensor([[ 7, 11],[ 9, 12]])
tensor([[0, 1],[2, 2]])
当输入张量为4维那就不想了…
tensor.sum()和torch.mean()张量求和、求平均
其实与torch.max()非常相似一维张量求和没啥好说的二维张量如果不指定dim参数则是求所有元素的和dim0是求每一列的和dim1则是求每一行的和。
import torcha torch.tensor([[1, 2, 3], [4, 5, 6]])
print(a.sum())
tensor(21)print(a.sum(dim0))
tensor([5, 7, 9])print(a.sum(dim1))
tensor([ 6, 15])
当张量为3维时与上面一样想象一下3D空间
# 二维张量是一个平面三维就看成多个平面叠加在一起
vec torch.tensor([[[7, 2, 3], [4, 11, 6]],[[1, 8, 9], [10, 5, 12]]
])
# dim0就是将不同平面上同一位置的元素累加起来
print(vec.sum(dim0))
tensor([[ 8, 10, 12],[14, 16, 18]])# dim1,(1)、先单独看一个平面相当于二维张量时dim0即计算同一平面内每一列的和(2)、与(1)类似计算每一个平面内每一列的和
print(vec.sum(dim1))
tensor([[11, 13, 9],[11, 13, 21]])# dim2,(1)、先单独看一个平面相当于二维张量时dim1即计算同一平面内每一行的和(2)、与(1)类似计算每一个平面内每一行的和
print(vec.sum(dim2))
tensor([[12, 21],[18, 27]])
torch.mean()是求平均值有一点要求是张量的类型为float类型其他的与torch.sum()基本是一致的。
其他张量操作
tensor.item()与tensor.tolist()
item()是将一个张量的值以一个python数字形式返回但该方法只能包含一个元素的张量对于包含多个元素的张量可以使用tolist()方法。
import torchb torch.tensor([1])
print(b.item()) # 输出为1c torch.tensor([1, 2, 3])
print(c.tolist()) # 输出为[1, 2, 3]
tensor.is_contiguous()、tensor.contiguous()
在pytorch中tensor的实际数据是以一维数组storage的方式存于某个连续的内存中的。而且pytorch的tensor是以**“行优先”**进行存储的。
所谓tensor连续contiguous指的是tensor的storage元素排列顺序与其按行优先时的元素排列顺序相同。如下图所示 之所以会出现不连续现象本质上是由于pytorch中不同tensor可能共用同一个storage导致的。
pytorch的很多操作都会导致tensor不连续比如tensor.transpose()、tensor.narrow()、tensor.expand()。
以转置为例因为转置操作前后共用同一个storage但显然转置后的tensor按照行优先排列成1维后与原storage不同了因此转置后结果属于不连续。
import torcha torch.tensor([[1,2,3],[4,5,6]])
print(a)
tensor([[1, 2, 3],[4, 5, 6]])
print(a.storage())123456
[torch.LongStorage of size 6]
print(a.is_contiguous()) # a是连续的
Trueb a.t() # b是a的转置
print(b)
tensor([[1, 4],[2, 5],[3, 6]])
print(b.storage())123456
[torch.LongStorage of size 6]
print(b.is_contiguous()) # b是不连续的
False# 之所以出现b不连续是因为转置操作前后是共用同一个storage的
print(a.storage().data_ptr())
print(b.storage().data_ptr())
2638924341056
2638924341056
tensor.contiguous()返回一个与原始tensor有相同元素的 “连续”tensor如果原始tensor本身就是连续的则返回原始tensor。
import torchc b.contiguous()# 形式上两者一样
print(b)
print(c)
tensor([[1, 4],[2, 5],[3, 6]])
tensor([[1, 4],[2, 5],[3, 6]])# 显然storage已经不是同一个了
print(b.storage())
print(c.storage())123456
[torch.LongStorage of size 6]142536
[torch.LongStorage of size 6]# b不连续c是连续的
print(b.is_contiguous())
False
print(c.is_contiguous())
True# 此时执行c.view()不会出错
c.view(2,3)
tensor([[1, 4, 2],[5, 3, 6]])