pytorch学习基础1——张量的创建与基础操作
一思维导图
tensor的操作
元组和列表
定义一个普通列表,用中括号表示;
listvar = [111,3,13,True,3+4j,"abc"]print (listvar,type(listvar))
列表的特点:可获取,可修改,有序。
定义一个普通元组:
tuplevar = (False,3+4j,“aaa”,456)
元组特点:可获取、不可修改、有序
综上来看,列表和元组存储的数据类型都是多种多样的,最大的区别就是列表创建完成之后可以修改,而元组一旦创建之后不可修改(但是两个元组之间可以连接)。
后面我们将看到张量的很多操作,其参数是以元组或者列表传递的。
张量Tensor的定义与共性
张量在形式上就是多维数组,例如标量就是0维张量,向量就是一维张量,矩阵就是二维张量,而三维张量就可以想象RGB图片,每个channel是一个二维的矩阵,共有三个channel,还可以考虑更多。
在进行张量的各种操作时,需要牢牢掌握张量的两个特性,一是维度dimension(dim),从0开始,在代码中,每一个括号代表一个维度,深度学习中一般会有3-4个维度(batch_size,channel,width,height),;形状shape,这里是指每个维度的数值大小。举个例子来说,输出张量a的形状为(1,2,3,4),则该张量有4维,第0,1,2,3维的形状大小分别为1,2,3,4.特别注意这两个特性,因为张量的所有操作都是针对这两个特性的。
在调用张量api操作时往往有两种形式,一是a.api_name(arg),二是api_name(a,arg)
在代码中创建张量Tensor数据类型时,除了封装张量本身的数据data外,还会附加张量的一些性质和操作,例如数据的梯度(grad),创建tensor的函数(grad_fun,是求导的关键),是否为叶子节点(is_leaf),是否需要梯度(require_grad)。这部分主要涉及到梯度和损失函数,后面再用专题介绍。
张量tensor创建
首先来谈一下张量的创建,创建的本质是有2大类,一是由现有的数据直接转化而来,称为转化法;二是利用各种api函数传入想要创建张量的维度和形状,称为api法。
转化法torch.tensor()和torch.from_numpy
转化法是从现有的数据转化而来,现有的数据从哪里来呢?
(1)tensor()括号里的数据可以是列表list(以“[]”表示),即需要什么手动写什么,很少用;
(2)也可以是numpy,即先用numpy创建一个numpy,然后直接导入(如下);
torch.tensor([1])
arr = np.array([[1, 2, 3], [4, 5, 6]])
t = torch.tensor(arr, device='cuda')
值得注意的点:
(1)注意数据类型,有时候需要在数字后面加“.”表示float,因为求导时候需要float类型;
(2)可以添加device=’cuda’获得加速。
上述从numpy导入的数据是不共享内存的,而还有一种导入方法:torch.from_numpy创建的tensor和原来的numpy共享内存,也即是说修改tensor就会修改原来的numpy。如下
arr = np.array([[1, 2, 3], [4, 5, 6]])
t = torch.from_numpy(arr)
# arr[0, 0] = 0
t[0, 0] = -1
Api法
特殊数字torch.zeros()/ones()/eye()/full()
此类方法的共性就是利用给定的api函数,如zeros,ones,eyes,full,然后在参数里指定想要创建的张量的形状(用元组表示),即可完成创建。
# 通过torch.zeros创建张量
out_t = torch.tensor([1])
#t=torch.zeros((3,3))
t = torch.zeros((3, 3), out=out_t)
值得注意的点:
(1)也可以先创建一个tensor,然后在zeros函数的out接收创建的zeros,二者的size可以不一样,创建完成后二者一致。
(2)还可以torch.ones
(3)全1张量还可以用full函数
t = torch.full((3, 3), 1)
注意试验一下是不是还可以创建全“2”张量?
(4)还可以利用torch.zeros_like(),torch.ones_like(),torch.full_like创建和input张量(即用真实的张量代替指定的形状)一致的全0/1张量。
(5)torch.eye()创建单位对角矩阵
等差均分torch.arange&linespace
此类方法创建的是一维张量,该张量的形状由参数决定,其参数包括3个,(start,end,step),形状大小=(end-start)/step。
t = torch.arange(2, 10, 2)
(1)创建等差数列张量,后面为等差值,默认为1,取值时[start,end)左闭右开.
# t = torch.linspace(2, 10, 5)
t = torch.linspace(2, 10, 6)
(1)参数为(start,end,n),代表创建的张量在[start,end]中均分n等份,这时会出现小数。
(2)还可以等log创建,torch.logspace()
2.5 概率法
此类方法的本质还是依据想要使用的概率分布来创建符合要求的张量,这时需要指定概率分布的超参数,比如正态分布标准值和方差,
(1)注意mean,std可以是标量和张量(float类型)的组合,共四种模式。
(2)mean(为tensor)和std张量的形状不一样时,可以不指定输出张量的形状大小size,输出的张量形状以mean张量的形状为准;
(3)若mean和std均为标量float时,必须要指定输出张量的形状。
# 通过torch.normal创建正态分布张量
# mean:张量 std: 张量
# mean = torch.arange(1, 5, dtype=torch.float)
# std = torch.arange(1, 5, dtype=torch.float)
# t_normal = torch.normal(mean, std)
# mean:标量 std: 标量
# t_normal = torch.normal(0., 1., size=(4,))
# mean:张量 std: 标量
mean = torch.arange(1, 5, dtype=torch.float)
std = 1
t_normal = torch.normal(mean, std)
(2)torch.randn(),torch.randn_like()创建标准正态分布张量;
(3)torch.rand(),torch.rand_like()创建[0,1]均匀分布。
(4)torch.randint(low,high),torch.randint_like(low,high)创建[low,high)均匀分布。
(5)torch.randperm(n),创建从0到n-1的随机排列张量
(6)torch.bernoulli(input),创建以input为概率值的伯努利分布张量。
张量tensor读取
切片tensor[start:step:end,start::step,:]
张量的读取与访问其本质是对数据在各个维度形状位置的确定,访问单个元素时,直接写出其每个维度上的形状上的位置(二维举例来说就是行和列值),切片操作就是以列表的形式用start:end来指代每个维度获取多少数据,这里也可以设置步长,方法是tensor[start:step:end],如果是到张量维度末尾结束,还可以简化为tensor[start::step]
tensor = torch.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 访问单个元素 print("单个元素:", tensor[0, 1])
# 切片操作 print("切片结果:", tensor[0:2, 1:3])
张量索引index_select&maksed_select
torch.index_select(input,dim,select)
t = torch.randint(0, 9, size=(3, 3))
idx = torch.tensor([0, 2], dtype=torch.long) # float
t_select = torch.index_select(t, dim=0, index=idx)
(1)在dim维度上按照index=idx索引数值。其中idx是张量(相当于将切片中规律的取值变为手动指定)。如上,就是取张量t在维度0上的第0个和第2个张量。以二维为例就是取第0行和第2行。
torch.maksed_select(input,mask)
t = torch.randint(0, 9, size=(3, 3))
mask = t.le(5) # ge is mean greater than or equal/ gt: greater than le lt
t_select = torch.masked_select(t, mask)
- mask是和input同大小的布尔类型张量,按照mask张量为TRUE,寻找对应位置的t并返回一维张量。
张量tensor形状shape变化
张量切分chunk&split
torch.chunk(input,chunk,dim)
a = torch.ones((2, 7)) # 7
chunk_tensors = torch.chunk(a, chunks=3, dim=1) # 3
(1)在维度dim上进行chunk均分,如果不能整除,最后一份为余数。
torch.split(input,int/list,dim)
t = torch.ones((2, 5))
split_tensors = torch.split(t, [2, 1, 1], dim=1) # [2 , 1, 2]
for idx, t in enumerate(split_tensors):
# split_tensors = torch.split(t, [2, 1, 2], dim=1)
# for idx, t in enumerate(split_tensors):
(1)为int时,和chunk功能类似;
(2)为list时,可以按照设定值切分,但总和要与输入张量对应维度上的形状大小一致。
张量tensor维度和形状变化
张量拼接cat&stack
torch.cat(tensors,dim)
t = torch.ones((2, 3))
t_0 = torch.cat([t, t], dim=0)
t_1 = torch.cat([t, t, t], dim=1)
(1)是将两个张量在原来的维度上进行拼接,这就要求两个张量其它维度的形状完全一致。
torch.stack(tensor,dim)
t = torch.ones((2, 3))
t_stack = torch.stack([t, t, t], dim=0)
- 是在新创建的维度(维度由参数指定)上进行拼接,如果指定的维度小于现存的维度,比如上面维度为(0和1,指定在维度0上创建,小于现存维度)则创建该维度后,后面的递推。比如t现在维度是2*3,拼接后,则是3*2*3(即新创建的维度0形状为3),其中后两维的2*3是原来的t。
张量reshape&view
在 PyTorch 中,reshape 和 view 方法都用于改变张量(Tensor)的形状,二者变换前后张量的形状乘积需相等。但它们在功能和使用场景上存在一些差异。下面将详细对比这两个方法,并给出实例和输出结果。
相同点
- 功能用途:reshape 和 view 方法的主要目的都是改变张量的形状,且新形状的元素总数必须与原张量的元素总数相同。
- 返回视图:通常情况下,它们返回的都是原张量的视图(view),而不是副本,这意味着对返回的张量进行修改可能会影响原张量,反之亦然。
不同点
- 灵活性:reshape 方法更加灵活,它可以处理不连续的张量(即内存布局不连续的张量),在需要时会自动进行复制操作以得到一个新的连续张量。而 view 方法只能用于连续的张量,如果张量不连续,调用 view 会报错。
- 适用场景:如果不确定张量是否连续,或者张量可能不连续,建议使用 reshape;如果能确保张量是连续的,使用 view 可能会更高效,因为它不会进行额外的复制操作。
torch.reshape(input,shape)
t = torch.randperm(8)
t_reshape = torch.reshape(t, (-1, 2, 2)) # -1
t[0] = 1024
也可以先确定部分维度的形状大小,剩下的一个维度用-1表示,此时该维度的形状即是原形状乘积/其它所有维度的形状乘积。
张量维度交换transpose
transpose 方法用于交换张量的两个指定维度。对于三维及以上的张量,transpose 同样是交换指定的两个维度,下面详细介绍其变化原理并给出示例。
假设原始三维张量的形状为 (2, 3, 4),表示有 2 个大小为 (3, 4) 的二维矩阵。transpose(0, 1) 会将第 0 维和第 1 维交换,交换后张量的形状变为 (3, 2, 4),即现在有 3 个大小为 (2, 4) 的二维矩阵。
四维时,使用 torch.arange(48).reshape(2, 3, 2, 4) 创建一个四维张量,其形状为 (2, 3, 2, 4),表示该张量由 2 个大小为 (3, 2, 4) 的子张量组成,每个子张量又有 3 个大小为 (2, 4) 的二维矩阵。
交换第 0 维和第 2 维:调用 transpose(0, 2) 方法交换第 0 维和第 2 维,交换后张量的形状变为 (2, 3, 2, 4) 中的第 0 维和第 2 维互换,即 (2, 3, 2, 4) 变为 (2, 3, 2, 4) ,这里的元素排列会根据新的维度顺序重新组织。
交换第 1 维和第 2 维:使用 transpose(1, 2) 交换第 1 维和第 2 维,新的形状为原形状中第 1 维和第 2 维互换,即 (2, 2, 3, 4)。
交换第 2 维和第 3 维:通过 transpose(2, 3) 交换第 2 维和第 3 维,形状变为 (2, 3, 4, 2)。
输出分析
- 形状变化:每次交换不同的维度,张量的形状会相应改变,改变规则是所交换的两个维度的大小互换,其他维度大小保持不变。
- 元素排列变化:元素会根据新的维度顺序重新排列,例如在交换第 1 维和第 2 维时,原本在第 1 维的元素会移动到第 2 维的对应位置,反之亦然。
torch.transpose(input,dim1,dim2)
# torch.transpose
t = torch.rand((2, 3, 4))
t_transpose = torch.transpose(t, dim0=1, dim1=2) # c*h*w h*w*c
(1)维度变换之后,数据是如何变化的?
(2)torch.t()二维张量(矩阵)转置
张量维度压缩扩充squeeze&unsqueeze
torch.sequeeze(input,dim)
t = torch.rand((1, 2, 3, 1))
t_sq = torch.squeeze(t)#形状变为(2,3)
t_0 = torch.squeeze(t, dim=0)#形状变为(2,3,1)
t_1 = torch.squeeze(t, dim=1)#形状仍为(1,2,3,1)
(1)squeeze 方法用于移除张量中维度大小为 1 的维度。如果指定了维度参数,则只移除该指定维度上大小为 1 的维度,若指定维度不为1,则不会压缩;若不指定维度参数,则默认移除所有大小为 1 的维度。
(2)torch.unsequeeze(),unsqueeze 方法用于在指定位置插入一个维度大小为 1 的新维度。在指定维度上设置为1,其它维度形状不变。
张量tensor的数学运算
基本算术运算
包括加法、减法、乘法、除法等,这些运算可以是逐元素的操作,也可以在满足广播规则的情况下进行不同形状张量间的运算。
import torch
# 创建两个示例张量
a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])
# 加法
add_result = a + b
print("加法结果:", add_result)
# 减法
sub_result = a - b
print("减法结果:", sub_result)
# 乘法
mul_result = a * b
print("乘法结果:", mul_result)
# 除法
div_result = a / b
print("除法结果:", div_result)
矩阵乘法torch.matmul(t1,t2) 或 @
使用 torch.matmul 或 @ 运算符进行矩阵乘法,要求参与运算的张量形状满足矩阵乘法的规则。
import torch
# 创建两个适合矩阵乘法的张量
m = torch.tensor([[1, 2], [3, 4]])
n = torch.tensor([[5, 6], [7, 8]])
# 矩阵乘法 matmul_result = torch.matmul(m, n)
# 也可以使用 @ 运算符
matmul_result_alt = m @ n
print("矩阵乘法结果 (torch.matmul):", matmul_result)
print("矩阵乘法结果 (@ 运算符):", matmul_result_alt)
torch.bmm(input, mat2, out=None)
torch.bmm 是 PyTorch 中的一个函数,用于执行批量矩阵乘法(Batch Matrix Multiplication)。下面从基本概念、语法、使用示例、注意事项几个方面详细介绍。
在深度学习中,很多时候需要同时处理多个矩阵乘法,例如在处理一批数据样本时,每个样本都需要进行矩阵乘法运算。torch.bmm 可以高效地完成这一任务,它会对批量中的每一对矩阵分别进行矩阵乘法。
参数: input:输入的批量矩阵,形状为 (b, n, m),其中 b 是批量大小,n 是矩阵的行数,m 是矩阵的列数。
- mat2:第二个批量矩阵,形状为 (b, m, p),这里的批量大小 b 必须和 input 的批量大小相同,m 要和 input 矩阵的列数相同,p 是矩阵的列数。
- out(可选):指定输出的张量,如果提供了该参数,结果将存储在这个张量中。
返回值:返回一个形状为 (b, n, p) 的批量矩阵,其中 b 是批量大小,n 是第一个输入矩阵的行数,p 是第二个输入矩阵的列数。
幂运算tensor**
使用 ** 运算符进行逐元素的幂运算。
import torch
# 创建一个示例张量
tensor = torch.tensor([1, 2, 3])
# 幂运算
power_result = tensor ** 2
print("幂运算结果:", power_result)
三角函数运算
PyTorch 提供了如 torch.sin、torch.cos、torch.tan 等三角函数运算。
import torch
# 创建一个示例张量
angle_tensor = torch.tensor([0, torch.pi / 2, torch.pi])
# 正弦函数运算
sin_result = torch.sin(angle_tensor)
print("正弦函数运算结果:", sin_result)
# 余弦函数运算
cos_result = torch.cos(angle_tensor)
print("余弦函数运算结果:", cos_result)
求和、均值、最大值、最小值运算
可以使用 torch.sum、torch.mean、torch.max、torch.min 等函数对张量进行相应的统计运算。
import torch
# 创建一个示例张量
stat_tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
# 求和
sum_result = torch.sum(stat_tensor)
print("求和结果:", sum_result)
# 均值
mean_result = torch.mean(stat_tensor.float())
# 需要转换为浮点型以计算均值
print("均值结果:", mean_result)
# 最大值
max_result = torch.max(stat_tensor)
print("最大值结果:", max_result)
# 最小值
min_result = torch.min(stat_tensor)
print("最小值结果:", min_result)
还可以指定维度。
按指定维度求和 (torch.sum)
torch.sum(input, dim, keepdim=False) 函数用于对输入张量 input 沿着指定的维度 dim 进行求和。keepdim 参数用于控制输出张量是否保持和输入张量相同的维度数。
import torch
# 创建一个示例张量
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
# 按行求和(指定维度 0)
sum_row = torch.sum(tensor, dim=0)
print("按行求和结果:", sum_row)
# 按列求和(指定维度 1)
sum_col = torch.sum(tensor, dim=1)
print("按列求和结果:", sum_col)
# 按行求和并保持维度
sum_row_keepdim = torch.sum(tensor, dim=0, keepdim=True) print("按行求和并保持维度结果:", sum_row_keepdim)
按指定维度求最大值 (torch.max)
torch.max(input, dim, keepdim=False) 函数用于对输入张量 input 沿着指定的维度 dim 找出最大值,并返回最大值和对应的索引。
import torch
# 创建一个示例张量
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
# 按行找最大值(指定维度 0)
max_row, max_row_indices = torch.max(tensor, dim=0)
print("按行找最大值结果:", max_row)
print("按行找最大值对应的索引:", max_row_indices)
# 按列找最大值(指定维度 1)
max_col, max_col_indices = torch.max(tensor, dim=1)
print("按列找最大值结果:", max_col)
print("按列找最大值对应的索引:", max_col_indices)
# 按行找最大值并保持维度
max_row_keepdim, _ = torch.max(tensor, dim=0, keepdim=True) print("按行找最大值并保持维度结果:", max_row_keepdim)
参考资料
torch.Tensor — PyTorch 2.6 documentation