目前在学Pytorch下有关张量的操作,网上相关资料很多但都过于零散。为了方便自己学习,综合了网络大部分资料加上自己的理解整理为这篇博客。觉得有用的可以收藏一下。
目录
一、基础知识
1.1 张量定义
首先我们要区分以下几个易混淆概念:
- 标量(scalar):标量只有大小没有方向。
- 矢量(vector):矢量有大小有方向。常用于物理领域。
- 向量(vector):概念同矢量。常用于数学领域。
- 矩阵(matrix):按照长方阵列排列的复数或实数集合。
- 张量(tensor):向量概念的扩充,表示更高维度的向量。
以上概念的维度是不断上升的,分别表示点、线、面和体。标量、向量、矩阵可分别称为0、1、2维张量。每增加一个维度就相当于在原始量的某个方向上扩展。
Pytorch官方文档对张量的定义: 张量是一个包含单一数据类型元素的多维矩阵。
1.2 张量类型
1.2.1 张量的元素类型
PyTorch中定义的张量元素类型
类型名 | dtype | CPU tensor | GPU tensor |
---|---|---|---|
8位无符号整数 | torch.uint8 | torch.ByteTensor | torch.cuda.ByteTensor |
8位有符号整数 | torch.int8 | torch.CharTensor | torch.cuda.CharTensor |
16位有符号整数 | torch.int16 / torch.short | torch.ShortTensor | torch.cuda.ShortTensor |
32位有符号整数 | torch.int32 / torch.int | torch.IntTensor | torch.cuda.IntTensor |
64位有符号整数 | torch.int64 / torch.long | torch.LongTensor | torch.cuda.LongTensor |
16位半精度浮点数 | torch.float16 / torch.half | torch.HalfTensor | torch.cuda.HalfTensor |
32位浮点数 | torch.float32 / torch.float | torch.FloatTensor | torch.cuda.FloatTensor |
64位双精度浮点数 | torch.float64 / torch.double | torch.DoubleTensor | torch.cuda.DoubleTensor |
32位复数 | torch.complex32 | ||
64位复数 | torch.complex64 | ||
128位复数 | torch.complex128 / torch.cdouble | ||
布尔型 | torch.bool | torch.BoolTensor | torch.cuda.BoolTensor |
CPU tensor一列表示在CPU上创建张量使用的函数
GPU tensor一列表示在GPU上创建张量使用的函数
Pytorch张量默认数据类型为32位浮点数。
可以通过torch.set_default_tensor_type()函数设置默认的数据类型,但是该函数只支持设置浮点型数据类型。
# PyTorch默认创建32位浮点型元素
# 张量中所有元素为同一数据类型,因此当元素中有一个为浮点型时所有元素会自动向上扩展
t1 = torch.tensor([[1.,2],[3,4],[5,6]])
print(t1.dtype) # 查看张量元素类型
torch.set_default_tensor_type(torch.DoubleTensor) # 修改默认数据类型
t1 = torch.tensor([[1., 2], [3, 4], [5, 6]])
print(t1.dtype)
'''
torch.float32
torch.float64
'''
在神经网络计算中使用64位浮点数计算并不会提高模型精度,反而会带来更多的内存占用与计算时间。
现代标准的CPU不支持16位半精度浮点计算,但GPU支持,可以将数据转化为半精度浮点型在GPU上加速运算。
在CPU上创建元素类型为16位整型的张量(两种方式)
t1 = torch.tensor([[1.,2],[3,4],[5,6]], dtype=torch.short) # 默认在CPU上创建
t1 = torch.ShortTensor([[1., 2], [3, 4], [5, 6]]) # 等效于上面
在GPU上创建元素类型为16位半精度浮点型的张量(两种方式)
t2 = torch.ones(2,3, dtype=torch.float16, device='cuda')
t2 = torch.cuda.FloatTensor([1,2])
转换元素类型(两种方式)
t3 = t1.double()
t3 = t1.to(torch.double) # 使用to()方法会检查转化是否有必要,例如数据会自动向较大类型转化。
注意:空张量与下面要讲的torch.empty()未初始化张量有区别
创建空张量
t1 = torch.tensor([])# 注意是空值,而不是0或随机值填充
创建布尔型张量
t1 = torch.tensor([True,False]) # 注意不能用0,1表示
创建标量(0维张量)
t1 = torch.tensor(3.14)
创建复数张量
t1 =torch.tensor(1+2j)
t2 =torch.tensor(2-4j)
print(t1+t2)
'''
tensor(3.-2.j)
'''
1.2.2 广义张量
无论是在CPU上还是GPU上的张量都是通过分派机制(dispatching mechanism)来实现,该机制可以通过面向用户的API连接到恰当的后端函数来满足其他类型张量的需要。
还有其他种类的张量,如专用于特定类别的硬件设备(如谷歌TPU)的张量以及数据表示策略与目前我们看到的密集数组格式不同的张量。如,稀疏张量只存储非0项,以及索引信息。
我们常使用的张量称为密集张量或步长张量,以区别于其他存储布局的张量。
随着PyTorch支持的硬件和应用程序越来越广泛,张量的种类也在增加。
1.3 张量存储
了解张量在计算机底层的存储视图有助于理解后续对张量的操作。
张量中的值被分配到由torch.Storage实例所管理的连续内存块中。
存储区可视为一个指定数据类型的一维数组。
同一个存储区只能存储同一种数据类型,如不指定则默认是32位浮点型。
#使用storage()访问给定张量的存储区
t1 = torch.tensor([[1.1,1.2],[1.3,1.4]])
print(t1.storage())
'''
1.100000023841858
1.2000000476837158
1.2999999523162842
1.399999976158142
[torch.storage._TypedStorage(dtype=torch.float32, device=cpu) of size 4]
'''
可以看到存储区中的数值似乎与我们定义的数值不一致。是因为我们定义的小数为十进制定点数,存入计算机时要先转化为二进制,再通过浮点数标准映射为浮点数。计算机在表示一个数字时,宽度是有限的,无限循环的小数存储在计算机时,只能被截断,所以就会导致小数精度发生损失的情况。计算机的浮点数只能无限接近我们想要表示的数值,位数越高精度越高。
尽管给定张量有2行2列,但底层存储是一个大小为4的连续数组。
# 手动索引存储区
print(t1.storage()[0])
print(t1.storage()[1])
'''
1.100000023841858
1.2000000476837158
'''
注意:由于底层存储是一维数组因此不能用2个索引来索引存储区。
多个张量可以索引同一存储区。例如:后续对张量形状改变的操作并不是创建了新张量,存储区的内容和排序不变,变的只是张量的元数据。又或者提取某个张量的子张量,该子张量也是索引同一个存储区。因此需要注意的是:对于索引的是同一存储区的张量,改变存储区的值意味着所有与之关联的张量对应位置的值也会变。详细讲解在下一节。
张量可以存储在CPU上,也可以存储在GPU上以进行快速大规模并行计算。基于CPU和GPU的张量有着相同的面向用户的API,这使得开发者无需关注数据运算在哪个设备上进行。
在GPU上创建张量
t1_G = torch.tensor([[1,2],[3,4],[5,6]], device='cuda') # 默认为GPU0
在指定GPU上创建张量
t1_G = torch.tensor([[1,2],[3,4],[5,6]], device='cuda:0')
张量在设备间转换
# 将CPU的张量复制到GPU
t1_C = torch.tensor([[1,2],[3,4],[5,6]]) # 默认是在CPU上的
t1_G = t1_C.to(device='cuda')
t1_G = t1_C.cuda()
# 将GPU的张量复制回CPU
t1_C = t1_G.to(device='cpu')
t1_C = t1_G.cpu()
注意:在GPU上的张量是独立于CPU上的,两边的运算互不干扰。因此需要手动转移不同设备上的数据。
在Python数据科学领域离不开NumPy数组。但NumPy只支持在CPU上操作,因此需要将GPU的数据拷贝至CPU。处理完后在拷贝回去。
张量与NumPy转换
# 张量转化为NumPy数组
t1_N = t1_C.numpy()
# NumPy数组转化为张量
t1_C = torch.from_numpy(t1_N)
注意:PyTorch默认的数据类型是32位浮点型,而NumPy默认是64位浮点型。在转换时要注意。
NumPy的共享缓冲区策略
# NumPy使用了共享缓冲区策略,因此原始张量也会变化
t1_N[1][1] = 0
print(t1_N)
print(t1_C)
'''
[[1. 2.]
[3. 0.]
[5. 6.]]
tensor([[1., 2.],
[3., 0.],
[5., 6.]])
'''
1.4 张量元数据
为了在存储区中建立索引,张量依赖于一些额外信息:大小、偏移量和步长。
- 大小:表示张量在每个维度上有多少个元素,在Numpy中也称为形状。
- 偏移量:表示存储区的某个元素相对于第一个元素的索引。
- 步长:表示在存储区中为了获得某个维度的下一个元素需要跳过的元素数量。
获取偏移量
print(t1[1].storage_offset())
'''
2
'''
获取大小
print(t1.size())
print(t1.shape) # 同样的效果,注意不加括号,因为shape是张量对象的属性
'''
torch.Size([2, 2])
torch.Size([2, 2])
'''
获取步长
print(t1.stride())# (第0维步长,第1维步长)
'''
(2, 1)
'''
获取张量中的值
t1 = torch.tensor([[1.,2],[3,4]])
print(t1[0][0]) # 得到的是张量
print(t1[0][0].item()) # 得到的是数值,注意一定要是标量(0维张量)
# print(t1[0].item()) # 错误写法,t1[0]不是标量
'''
tensor(1.)
1.0
'''
获取张量维度
t1 = torch.tensor(3.14)
t2 = torch.tensor([1,2])
t3 = torch.tensor([[1.,2],[3,4]])
print(t1.ndim, t2.ndim, t3.ndim)
'''
0 1 2
'''
查看张量中的元素个数
print(t1.numel(), t2.numel(), t3.numel())
'''
1 2 4
'''
下面是对上一节例子的讲解
t1 = torch.tensor([[1,2],[3,4],[5,6]])
print(t1)
# 转置
t1_t = t1.t()
print(t1_t)
'''
tensor([[1, 2],
[3, 4],
[5, 6]])
tensor([[1, 3, 5],
[2, 4, 6]])
'''
# 共享存储区
print(id(t1.storage()))
print(id(t1_t.storage()))
'''
1640633295944
1640633295944
'''
# 步长改变
print(t1.stride())
print(t1_t.stride())
'''
(2, 1)
(1, 2)
'''
t1 = torch.tensor([[1,2],[3,4],[5,6]])
# 子张量
t1_c = t1[1]
print(t1_c.size()) # 大小
print(t1_c.storage_offset()) # 偏移量
print(t1_c.stride()) # 步长
'''
torch.Size([2])
2
(1,)
'''
# 改变子张量,影响原张量,因为共享存储区
t1_c[0] = 9
print(t1)
'''
tensor([[1, 2],
[9, 4],
[5, 6]])
'''
二、常用操作
2.1 张量创建
2.1.1 列表创建
# 最常用最简单地
t1 = torch.tensor([[1,2],[3,4]]) # 容易漏掉最外层括号
# 通过列表创建
list = [[1,2],[3,4]]
t1 = torch.tensor(list)
2.1.2 元组创建
# 不常用
t1 = torch.tensor(((2,3),(3,4)))
2.1.3 NumPy数组创建
# 通过NumPy创建
np = numpy.array([[1,2],[3,4]])
t1 = torch.tensor(np) # 复制np的数据,创建新张量
t2 = torch.from_numpy(np) # 与np共享缓冲区,改变t2即改变np,反之一样
这里有个疑惑,t1和t2的存储区id值一致,难道索引的是同一存储区?但改变t1并不会改变t2。请教知道的大神。
# 疑惑点
print(id(np))
print(id(t1.storage()))
print(id(t2.storage()))
t1.storage()[0] = 0
print(np)
print(t1)
print(t2)
'''
1835365536272
1836094286856
1836094286856
[[1 2]
[3 4]]
tensor([[0, 2],
[3, 4]], dtype=torch.int32)
tensor([[1, 2],
[3, 4]], dtype=torch.int32)
'''
2.1.4 创建填充张量
创建未初始化的张量
# 创建未初始化的张量,里面的值随机
t1 = torch.empty((2,3))
print(t1)
'''
tensor([[1.8179e+31, 2.7947e+20, 9.1041e-12],
[6.2609e+22, 4.7428e+30, 0.0000e+00]])
'''
# 给其填充值,未填充的部分随机取
t = torch.tensor([1,2,3])
t1 = torch.empty(2,3, out=t)
print(t1)
'''
tensor([[1, 2, 3],
[1, 1, 0]])
'''
创建全0张量
# 创建全0张量
t1 = torch.zeros((2,3))
# 创建同某张量形状一样的全0张量
t1 = torch.tensor([[1,2],[3,4]])
t2 = torch.zeros_like(t1)
创建全1张量
# 创建全1张量
t1 = torch.ones((2,3))
# 创建同某张量形状一样的全1张量
t2 = torch.ones_like(t1)
创建全指定值张量
# 创建全指定值张量
t1 = torch.full((2,3), fill_value=9)
# 创建同某张量形状一样的全指定值张量
t2 = torch.full_like(t1, fill_value=9)
创建全随机值张量
# 创建全随机值张量,随机值为[0,1)区间,服从标准正态分布
t1 = torch.randn((2,3))
# 创建同某张量形状一样的全随机值张量
t1 = torch.tensor([[0.1, 0.2], [0.3, 0.4]])
t2 = torch.randn_like(t1)
随机小数张量
# 随机值从[0,1)均匀采样
t1 = torch.rand((2, 3))
print(t1)
'''
tensor([[0.9356, 0.8596, 0.2918],
[0.1416, 0.3563, 0.6029]])
'''
随机整数张量
# 随机值从[low, high)均匀采样整数,参数为(low, high, size)
t1 = torch.randint(0,5,(2,3))
print(t1)
'''
tensor([[2, 4, 0],
[0, 1, 3]])
'''
生成从0到n-1的随机排列
# 生成从0到n-1的随机排列,可以用来生成乱序的索引
t1 = torch.randperm(10) # 长度为10
print(t1)
'''
tensor([2, 8, 7, 9, 3, 4, 0, 6, 1, 5])
'''
创建单位对角矩阵
# 创建单位对角矩阵,注意最多只能是二维张量
t1 = torch.eye(3)
t2 = torch.eye(3,6)
print(t1)
print(t2)
'''
tensor([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
tensor([[1., 0., 0., 0., 0., 0.],
[0., 1., 0., 0., 0., 0.],
[0., 0., 1., 0., 0., 0.]])
'''
创建等差的1维张量
# 创建等差1维张量,参数为start, end, step,[start,end)
t1 = torch.arange(3, 15, 2)
print(t1)
'''
tensor([ 3, 5, 7, 9, 11, 13])
'''
创建均分的1维张量
# 创建均分1维张量,参数为(开始,结束,列表长度)
t1 = torch.linspace(2, 10, 5)
print(t1)
'''
tensor([ 2., 4., 6., 8., 10.])
'''
创建从 base的start次幂 到 base的end次幂 的等比1维张量
# 创建从base的start次幂到base的end次幂 的等比1维张量,参数为(start, end, step, base)
t1 = torch.logspace(0, 4, 4, 3) # 长度为4,底数为3,起始为3的0次幂,终止为3的4次幂的等比数列
t2 = torch.logspace(0, 4, 5, 3) # 长度为5,底数为3,起始为3的0次幂,终止为3的4次幂的等比数列
print(t1)
print(t2)
'''
tensor([ 1.0000, 4.3267, 18.7208, 81.0000])
tensor([ 1., 3., 9., 27., 81.])
'''
依概率分布创建张量
# 依正态分布创建张量,参数为(mean, std, size)
t1 = torch.normal(0, 1, (4,)) # 注意:即使是一维张量也要加逗号,size要用元组形式
print(t1)
'''
tensor([ 0.0780, -0.1265, 0.4270, -2.0918])
'''
# mean为张量,std为张量,取mean和std中的值作为均值和标准差构成正态分布
mean = torch.arange(1, 5, dtype=torch.float)
std = torch.arange(1, 5, dtype=torch.float)
t1 = torch.normal(mean, std)
print(mean)
print(std)
print(t1)
'''
tensor([1., 2., 3., 4.])
tensor([1., 2., 3., 4.])
tensor([ 1.9330, 2.1289, -2.8910, 4.8478])
'''
2.2 张量索引
2.2.1 一维张量索引
索引单个元素
# 一维张量索引
t1 = torch.arange(1,10,2)
print(t1)
print(t1[1]) # 索引出的是0维张量
print(t1[1].item()) # 索引出的是数值
'''
tensor([1, 3, 5, 7, 9])
tensor(3)
3
'''
索引一维张量
# 类似列表切片,左闭右开,默认step为1
print(t1[2:5])
# 步长step = 2
print(t1[1:4:2])
'''
tensor([5, 7, 9])
tensor([3, 7])
'''
注意张量索引的step要大于1,否则报错
# 在python列表中step=-1时表示倒序排列,但在张量索引中step必须大于1
# print(t1[::-1]) # 错误写法
2.2.2 二维张量索引
索引单个元素
t1 = torch.arange(1,17).reshape(4,4)
print(t1)
print(t1[0,1])
print(t1[0][1]) # 两种索引结果相同,但原理不同
'''
tensor([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]])
tensor(2)
tensor(2)
'''
索引一维张量
# 索引出1维张量
print(t1[2])
# 先索引出一个2维张量,再继续索引出1维张量
print(t1[::2])
print(t1[::2][0])
'''
tensor([ 9, 10, 11, 12])
tensor([[ 1, 2, 3, 4],
[ 9, 10, 11, 12]])
tensor([1, 2, 3, 4])
'''
注意:t1[ : : 2 , : : 2] 与 t1[ : : 2][ : : 2]的索引结果不同
print(t1[::2,::2]) # []里有逗号就说明同时选择,这里同时选择第0维的0,3行,第1维的0,3列
print(t1[::2][::2]) # []是依次出现就依次选择,先选择第0维的0,3行,再从里面选择第0行
'''
tensor([[ 1, 3],
[ 9, 11]])
tensor([[1, 2, 3, 4]])
'''
2.2.3 多维张量索引
由二维张量的索引不难推导出多维张量的索引
注意:只有 ‘:’ 出现就是切片操作;‘:’ 与 ‘,’ 同时出现时,‘:’ 表示取该维度的所有元素, ‘,’ 表示第几维
这里讲一些特殊的索引操作
利用布尔类型过滤出某些维度的张量
t1 = torch.arange(1, 28).reshape(3, 3, 3)
print(t1)
mask = [False,True,True]
print(t1[:,mask,:])
'''
tensor([[[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9]],
[[10, 11, 12],
[13, 14, 15],
[16, 17, 18]],
[[19, 20, 21],
[22, 23, 24],
[25, 26, 27]]])
tensor([[[ 4, 5, 6],
[ 7, 8, 9]],
[[13, 14, 15],
[16, 17, 18]],
[[22, 23, 24],
[25, 26, 27]]])
'''
索引指定维度的张量
mask = [0,1,0,0]
print(t1[:,mask,:]) # 取第1维上的第0、1个张量,数量不限但范围不能超过第1维大小
'''
tensor([[[ 1, 2, 3],
[ 4, 5, 6],
[ 1, 2, 3],
[ 1, 2, 3]],
[[10, 11, 12],
[13, 14, 15],
[10, 11, 12],
[10, 11, 12]],
[[19, 20, 21],
[22, 23, 24],
[19, 20, 21],
[19, 20, 21]]])
'''
print(t1[:,mask]) # 并不一定要标出所有维度,维度按顺序执行,省略的默认为全取
'''
tensor([[[ 1, 2, 3],
[ 4, 5, 6],
[ 1, 2, 3],
[ 1, 2, 3]],
[[10, 11, 12],
[13, 14, 15],
[10, 11, 12],
[10, 11, 12]],
[[19, 20, 21],
[22, 23, 24],
[19, 20, 21],
[19, 20, 21]]])
'''
若指定了2个以上的维度,则原张量可能降维
x = torch.randint(0,t1.shape[1],(3,))
y = torch.randint(0,t1.shape[2],(3,))
print(x,y)
print(t1[:,x,y])
'''
tensor([1, 1, 2]) tensor([0, 2, 2])
tensor([[ 4, 6, 9],
[13, 15, 18],
[22, 24, 27]])
'''
2.2.4 函数索引
索引除了可以通过 ‘[ ]’ 的形式,也可以通过索引函数 index_select()
一维张量索引
t1 = torch.arange(1,5)
indices = torch.tensor([1, 2]) # 索引值
print(t1.index_select(0, indices)) # 0表示第0维,不能省略
'''
tensor([2, 3])
'''
二维张量索引可根据一维张量索引形式推导
2.3 张量变换
2.3.1 形状变化
reshape()
t1 = torch.arange(1,13)
print(t1)
print(t1.reshape(3,4))
'''
tensor([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
tensor([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
'''
view()
print(t1.view(2,6))
'''
tensor([[ 1, 2, 3, 4, 5, 6],
[ 7, 8, 9, 10, 11, 12]])
'''
resize()
print(t1.resize(4,3))
'''
tensor([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12]])
'''
resize_()
print(t1.resize_(3,3)) # 少了就按顺序读取
print(t1.resize_(3,5)) # 多了就用随机值填充
'''
tensor([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
tensor([[ 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10],
[11, 12, 0, 1, 0]])
'''
三者区别
- reshape()、view()、resize(),变化前后元素数量必须一致。
- resize_()变化前后数量可不一致,且改变原张量。
- reshape()、view()、resize()、resize_()都共享存储空间,改变新张量也改变原张量。
- view()方法只能改变连续的(contiguous)张量,否则需要先调用.contiguous()方法,而.reshape()方法不受此限制
2.3.2 维度变化
unsqueeze() 增维
t1 = torch.arange(1,13).reshape(3,2,2)
print(t1.unsqueeze(dim=1))
print(t1.unsqueeze(dim=1).shape)
'''
tensor([[[[ 1, 2],
[ 3, 4]]],
[[[ 5, 6],
[ 7, 8]]],
[[[ 9, 10],
[11, 12]]]])
torch.Size([3, 1, 2, 2])
'''
squeeze() 降维
print(t1.squeeze(dim=1))
'''
tensor([[[ 1, 2],
[ 3, 4]],
[[ 5, 6],
[ 7, 8]],
[[ 9, 10],
[11, 12]]])
'''
flatten将任意维度张量转为一维张量
print(t1.flatten())
'''
tensor([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
'''
transpose()交换两个维度:常用于图片存储格式的变换上
t1 = torch.arange(1,13).reshape(1,3,2,2)
print(t1.transpose(1,3))
print(t1.transpose(1,3).shape)
'''
tensor([[[[ 1, 5, 9],
[ 3, 7, 11]],
[[ 2, 6, 10],
[ 4, 8, 12]]]])
torch.Size([1, 2, 2, 3])
'''
permute()交换任意维度
print(t1.permute(0,2,3,1))
print(t1.permute(0,2,3,1).shape)
'''
tensor([[[[ 1, 5, 9],
[ 2, 6, 10]],
[[ 3, 7, 11],
[ 4, 8, 12]]]])
torch.Size([1, 2, 2, 3])
'''
2.4 张量运算
2.4.1 扩张
repeat(x, y, z) 表示将张量tensor在三个维度上分别重复x, y, z次
t1 = torch.tensor([[1,2],[3,4]])
print(t1.repeat(2,1)) # 第0维上重复两次,第1维不重复
print(t1.repeat(2,2)) # 第0维上重复两次,第1维重复两次
'''
tensor([[1, 2],
[3, 4],
[1, 2],
[3, 4]])
tensor([[1, 2, 1, 2],
[3, 4, 3, 4],
[1, 2, 1, 2],
[3, 4, 3, 4]])
'''
2.4.2 拼接
torch.cat() 拼接
t1 = torch.tensor([[1,2,3],[4,5,6]])
t2 = torch.tensor([[7,8,9],[10,11,12]])
print(torch.cat([t1,t2], 0)) # 第0维拼接
print(torch.cat([t1,t2], 1)) # 第1维拼接
'''
tensor([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12]])
tensor([[ 1, 2, 3, 7, 8, 9],
[ 4, 5, 6, 10, 11, 12]])
'''
torch.stack() 堆叠:拼接后会增加一维,且被拼接的张量大小必须相同。
t1 = torch.tensor([[1, 2], [3, 4]])
t2 = torch.tensor([[5, 6], [7, 8]])
print(torch.stack([t1, t2], 0)) # 第0维拼接
print(torch.stack([t1, t2], 1)) # 第1维拼接
'''
tensor([[[1, 2],
[3, 4]],
[[5, 6],
[7, 8]]])
tensor([[[1, 2],
[5, 6]],
[[3, 4],
[7, 8]]])
'''
2.4.3 分割
torch.split()
t1 = torch.arange(10).reshape(5, 2)
print(torch.split(t1,2)) # 返回元组
print(torch.split(t1,[2,3]))
'''
(tensor([[0, 1],
[2, 3]]),
tensor([[4, 5],
[6, 7]]),
tensor([[8, 9]]))
(tensor([[0, 1],
[2, 3]]),
tensor([[4, 5],
[6, 7],
[8, 9]]))
'''
torch.chunk()
t1 = torch.arange(10).reshape(5, 2)
print(torch.chunk(t1,2,dim=1)) # 从第1维上分割出2个张量块
'''
(tensor([[0],
[2],
[4],
[6],
[8]]),
tensor([[1],
[3],
[5],
[7],
[9]]))
'''
2.4.4 广播
当张量的维度shape不一致时,广播机制会自动调整张量维度使得计算可以顺利进行。
t1 = torch.arange(1,13).reshape(3,4)
t2 = torch.arange(1,5).reshape(1,4)
print(t1)
print(t2)
print(t1 + t2)
'''
tensor([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
tensor([[1, 2, 3, 4]])
tensor([[ 2, 4, 6, 8],
[ 6, 8, 10, 12],
[10, 12, 14, 16]])
'''
2.4.5 加减乘除
加:torch.add
t1 = torch.arange(1, 13).reshape(3, 4)
t2 = torch.arange(1, 5).reshape(1, 4)
print(torch.add(t1,t2))
'''
tensor([[ 2, 4, 6, 8],
[ 6, 8, 10, 12],
[10, 12, 14, 16]])
'''
减:torch.sub
print(torch.sub(t1,t2))
'''
tensor([[0, 0, 0, 0],
[4, 4, 4, 4],
[8, 8, 8, 8]])
'''
累加:torch.sum
print(torch.sum(t1))
'''
tensor(78)
'''
连乘:torch.prod
print(torch.prod(t2))
'''
tensor(24)
'''
一维张量内积:torch.dot
t1 = torch.tensor([1,1,1,1])
t2 = torch.tensor([2,2,2,2])
print(torch.dot(t1,t2))
'''
tensor(8)
'''
高维张量内积:torch.inner
t1 = torch.tensor([[1,1,1],[1,1,1]])
t2 = torch.tensor([[2,2,2],[2,2,2]])
print(torch.inner(t1,t2))
'''
tensor([[6, 6],
[6, 6]])
'''
矩阵乘法:torch.mm
t1 = torch.tensor([[1, 1, 1], [1, 1, 1]]) # (2,3)
t2 = torch.tensor([[2, 2], [2, 2],[2, 2]]) # (3,2)
print(torch.mm(t1,t2)) # (2,2)
'''
tensor([[6, 6],
[6, 6]])
'''
矩阵乘常数:torch.mul
t1 = torch.tensor([1,1,1])
print(torch.mul(t1,3))
'''
tensor([3, 3, 3])
'''
矩阵除常数:torch.div
t1 = torch.tensor([6,6,6])
print(torch.div(t1,2))
'''
tensor([3., 3., 3.])
'''
参考资料
向量、矩阵、张量基础知识
PyTorch中文手册–基础–张量
Pyorch官方文档
PyTorch深度学习实战
张量的基础操作
PyTorch张量创建方法
view()、reshape()、resize()区别
连续张量与非连续张量
张量运算小结
张量加减乘除