目录
Tensor
概念
数据类型
创建tensor
基本创建方式
1、 torch.tensor()
2、torch.Tensor()
3、torch.IntTensor() 等
创建线性张量和随机张量
1、创建线性张量
2、创建随机张量
切换设备
类型转换
与 Numpy 数据转换
1、张量转 Numpy
2、Numpy 转张量
tensor常见操作
1、获取元素值
2、元素值运算
3、tensor相乘
4、形状操作
view():
reshape():
5、维度转换
transpose(dim0, dim1):两个维度转换
permute(dim0, dim1, ... , dimN):重排所有维度
6、升维降维
unsqueeze():升维
squeeze():降维
PyTorch是一个基于Python的深度学习框架,它提供了一种灵活、高效、易于学习的方式来实现深度学习模型,最初由Facebook开发,被广泛应用于计算机视觉、自然语言处理、语音识别等领域。PyTorch提供了许多高级功能,如自动微分(automatic differentiation)、自动求导(automatic gradients)等,这些功能可以帮助我们更好地理解模型的训练过程,并提高模型训练效率。
没错苯人开始 pytorch的学习了,这篇来写一下 Torch 的一些基本API。
Tensor
概念
PyTorch会将数据全都封装成张量(Tensor)进行计算,所谓张量就是元素为相同类型的多维矩阵,或者多维数组,通俗来说可以看作是扩展了标量、向量、矩阵的更高维度的数组。张量的维度决定了它的形状(Shape),例如:
-
标量 是 0 维张量,如 a = torch.tensor(5)
-
向量 是 1 维张量,如 b = torch.tensor([1, 2, 3])
-
矩阵 是 2 维张量,如 c = torch.tensor([[1,2], [3,4]])
-
更高维度的张量,如3维、4维等,通常用于表示图像、视频数据等复杂结构。
数据类型
PyTorch中有3种数据类型:浮点数类型、整数类型、布尔类型。其中,浮点数和整数又分为8位、16位、32位、64位,加起来共9种。为什么要分这么多种类型呢是因为场景不同,对数据的精度和速度要求不同。通常,移动或嵌入式设备追求速度,对精度要求相对低一些。精度越高,往往效果也越好,自然硬件开销就比较高。
创建tensor
以下讲的创建 tensor 的函数中有两个有默认值的参数 dtype 和 device, 分别代表数据类型和计算设备,可以通过属性 dtype 和 device 获取,如 print(t1.dtype)
基本创建方式
1、 torch.tensor()
代码示例:
def test1():# 创建张量t1 = torch.tensor(88) #创建标量t2 = torch.tensor(np.random.randn(3, 2)) #用numpy随机数组创建1维张量t3 = torch.tensor([[1, 2, 3], [4, 5, 6]]) #创建2维张量print(t1)print(t2)print(t2.shape)print(t3)print(t3.shape)
运行结果:
注意,如果出现 UserWarning: Failed to initialize NumPy: _ARRAY_API not found 错误,一般是因为 numpy和pytorch版本不兼容,可以降低 numpy版本,直接卸载重装,最好是1.x版本
2、torch.Tensor()
代码示例:
def test2():#torch.Tenor()创建张量t1 = torch.Tensor(3) #根据形状创建print(t1)t2 = torch.Tensor([[1,2,3],[4,5,6]]) #根据具体的值创建print(t2)print(t2.shape)
运行结果:
这里可以看到,当传入的是一个整数时,tensor() 是直接创建标量,而 Tensor() 则是初始化一个形状为该整数的张量。除此之外,它俩的区别还在于:
特性 | torch.Tensor() | torch.tensor() |
---|---|---|
数据类型推断 | 强制转为 torch.float32 | 根据输入数据自动推断(如整数→int64 ) |
显式指定 dtype | 不支持 | 支持(如 dtype=torch.float64 ) |
设备指定 | 不支持 | 支持(如 device='cuda' ) |
输入为张量时的行为 | 创建新副本(不继承原属性) | 默认共享数据(除非 copy=True ) |
推荐使用场景 | 需要快速创建浮点张量 | 需要精确控制数据类型或设备 |
一般是使用 torch.tensor() 更多,更安全、功能更全,可避免未初始化张量的隐患。
3、torch.IntTensor() 等
用于创建指定类型的张量,诸如此类还有 torch.FloatTensor(32位浮点数)、 torch.DoubleTensor(64位浮点数)、torch.ShortTensor(16位整数)、 torch.LongTensor(64位整数)......等。如果数据类型不匹配,那么在创建的过程中会进行类型转换,防止数据丢失。
代码示例:
def test3():#创建指定类型的张量tt1 = torch.IntTensor(3,3)print(tt1)tt2 = torch.FloatTensor(3, 3)print(tt2, tt2.dtype)tt3 = torch.DoubleTensor(3, 3)print(tt3, tt3.dtype)tt4 = torch.LongTensor(3, 3)print(tt4, tt4.dtype)tt5 = torch.ShortTensor(3, 3)print(tt5, tt5.dtype)
运行结果就不贴了
创建线性张量和随机张量
1、创建线性张量
可使用 torch.arange() 和 torch.linspace() 创建一维线性张量
torch.arange(start, end, step):生成一个等间隔的一维张量(序列),区间为 [start, end)(左闭右开),步长由 step 决定。
torch.linspace(start, end, step):生成一个在区间 [start, end](左右均闭合)内均匀分布的 steps 个元素的一维张量(相当于等差数列)
代码示例如下:
def test4():# 创建线性张量t1 = torch.arange(0, 10, step=2)print(t1)t2 = torch.linspace(1, 2, steps=5)print(t2)
运行结果:
2、创建随机张量
使用torch.randn 和 torch.randint 创建随机张量。
torch.randn(size, dtype , device , requires_grad) :生成服从标准正态分布(均值为 0,标准差为 1)的随机张量,size 可以传元组或者直接传数字,生成的都是二维张量;后两个参数就不过多介绍;requires_grad 表示是否需要计算梯度,默认为False,这个参数很重要,可能下一篇会说。
torch.randint(low, high, size):在指定的[ low, high)范围内生成离散的、均匀分布的整数,low 为最小值,high 为最大值,size 为张量的形状
另外还要介绍一下随机数种子:随机数种子(Random Seed) 是控制计算机生成“随机数”的一个关键参数,它的存在让“随机”变得可复现,设置种子后算法会固定从同一个“起点”开始计算,生成的随机序列完全一致,至于设置什么数字并不重要,只是不同的数字生成的数字序列不同罢了,API为 torch.manual_seed()
代码实例:
def test5():'''创建随机张量'''torch.manual_seed(42)# 设置随机数种子t1 = torch.randn(2,3) #生成随机张量,标准正态分布,均值 0,标准差 1t2 = torch.rand(2,3) #生成随机张量,均匀分布,范围在 [0, 1)t3 = torch.randint(0, 10, (2, 3)) #在0到10内(不包括10)生成 2x3 的张量print(t1)print(t2)print(t3)
运行结果:
这里因为设置了随机数种子所以每次输出的结果都相同,如果将第一行注释掉,每次生成的张量就不一样了,同时注意一下 size 有没有括号都是生成的二维数组,当然也可以生成三维张量,例如
torch.rand(2, 3, 4)
切换设备
tensor 相关操作默认在cpu上运行,但是可以显式地切换到GPU,且不同设备上的数据是不能相互运算的。切换操作如下:
def test6():'''切换设备'''t1 = torch.tensor([1,2,3])print(t1.shape, t1.dtype, t1.device)#切换到GPU 方法一device = "cuda" if torch.cuda.is_available() else "cpu"print(t1.device)# 切换到GPU 方法二 to()方法t1 = t1.to(device="cuda")print(t1.device)
运行结果:
因为苯人电脑没有显卡所以转不过去,这里只是一个代码示范
或者直接用 cuda 进行转换:
t1 = t1.cuda()
当然也可以直接在 GPU 上创建张量:
data = torch.tensor([1, 2, 3], device='cuda')
print(data.device)
类型转换
在写代码时,类型的不同有时也会导致各种错误,所以类型的转换也是很重要的,有三种类型转换的方法:
def test7():'''类型转换'''t1 = torch.tensor([1,2,3])print(t1.dtype) #torch.int64# 1、使用 type() 进行转换t1 = t1.type(torch.float32)print(t1.dtype) #torch.float32# 2、使用类型方法转换t1 = t1.float()print(t1.dtype) # float32t1 = t1.half()print(t1.dtype) # float16t1 = t1.double()print(t1.dtype) # float64t1 = t1.long()print(t1.dtype) # int64t1 = t1.int()print(t1.dtype) # int32# 3、使用 dtype 属性指定t1 = torch.tensor([1,2,3], dtype=torch.float32)print(t1.dtype) #torch.float32
与 Numpy 数据转换
1、张量转 Numpy
浅拷贝:内存共享,修改 Numpy数组也会改变原张量,API:numpy()
深拷贝:内存不共享,相当于建了一个独立的副本,API:numpy().copy()
代码示例(浅拷贝):
def test8():'''张量转numpy'''#浅拷贝t1 = torch.tensor([[1,2,3], [4,5,6],[7,8,9]])t2 = t1.numpy() 转 numpyprint(t1)print(f't2:{t2}')t2[0][1] = 666 #内存共享print(t1)print(f't2:{t2}')
运行结果:
可以看到,t1 转成 t2 后,修改 t2 还是会改变 t1,如果想要避免内存共享可以用深拷贝:
#深拷贝# t2 = t1.numpy().copy()
其他同上
2、Numpy 转张量
同样分为内存共享和内存不共享:
浅拷贝:内存共享,修改 tensor 也会改变原数组,API:torch.from_numpy()
深拷贝:内存不共享,相当于建了一个独立的副本,API:torch.tensor()
代码示例(深拷贝):
'''numpy转张量'''# 深拷贝n1 = np.array([[11,22,33],[44,55,66]])n1_tensor = torch.tensor(n1) #转张量print(f'n1:{n1}')print(f'n1_tensor:{n1_tensor}')n1_tensor[0][1] = 88 #内存不共享print(f'n1:{n1}')print(f'n1_tensor:{n1_tensor}')
运行结果:
可以看到,修改 n1_tensor 不会改变 n1,如果想要浅拷贝的话:
#浅拷贝# n1_tensor = torch.from_numpy(n1)
tensor常见操作
接下来介绍一些在深度学习中基本的 tensor 操作:
1、获取元素值
我们可以把只含有单个元素的 tensor 转换为Python数值,这和 tensor 的维度无关,但若有多个元素则会报错,且仅适用于 CPU张量。
API为 item():
def test9():'''获取元素值'''t1 = torch.tensor([[[18]]])t2 = torch.tensor([20,13])print(t1.item())print(t2.item())
运行结果:
可以看到佑报错,这就是因为 t2 含有不止一个元素所以不能用 item()
2、元素值运算
常见的加、减、乘、除、次方、取反、开方等各种操作,但是也分是否修改原始值
不修改原始值代码示例:
'''元素值运算'''t1 = torch.randint(0, 10, (2,3))print(f't1:{t1}')print(t1.add(1))print(t1.sub(1))print(t1.mul(2))print(t1.div(2))print(f't1:{t1}')
运行结果:
可以看到,经过加减乘除后 t1 的元素并没有被改变
修改原始值:
t2 = torch.randint(0, 10, (2, 3))print(f't2:{t2}')t2.add_(1)print(f't2:{t2}')
运行结果:
可以看到 t2 已被改变,所以带下划线 “_” 的运算会改变原始值
3、tensor相乘
tensor 之间的乘法有两种,一种是矩阵乘法,还有一种是阿达玛积。
矩阵乘法是线性代数中的一种基本运算,用于将两个矩阵相乘,生成一个新的矩阵。
举个例子,假设有两个矩阵:
-
矩阵 A的形状为 m×n(m行 n列)。
-
矩阵 B的形状为 n×p(n行 p列)。
则矩阵 A和 B的乘积 C=A×B,是一个形状为 m×p的矩阵,可以理解为前一个矩阵控制行数,后一个矩阵控制列数,计算公式为:
要求如果第一个矩阵的shape是 (N, M),那么第二个矩阵 shape必须是 (M, P),最后两个矩阵点积运算的shape为 (N, P),而在 pytorch 中,使用 matmul() 或者 @ 来完成 tensor 的乘法
代码示例如下:
import torchdef test1():'''矩阵乘法'''t1 = torch.tensor([[1,2,3],[4,5,6]])t2 = torch.tensor([[7,8],[9,10],[11,12]])# 两种方法print(t1 @ t2)print(t1.matmul(t2))if __name__ == '__main__':test1()
运行结果:
下面介绍阿达玛积。阿达玛积是指两个形状相同的矩阵或张量对应位置的元素相乘。它与矩阵乘法不同,矩阵乘法是线性代数中的标准乘法,而阿达玛积是逐元素操作。
假设有两个形状相同的矩阵 A和 B,则它们的阿达玛积 C=A∘B定义为:
在 pytorch 中,可以使用 * 或者 mul() 来实现,代码如下:
'''阿达玛积'''t3 = torch.tensor([[1,2,3],[4,5,6]])t4 = torch.tensor([[7,8,9],[11,22,33]])#两种方法print(t3 * t4)print(t3.mul(t4))
运行结果:
4、形状操作
在 PyTorch 中,张量的形状操作是非常重要的,因为它允许你灵活地调整张量的维度和结构,以适应不同的计算需求。 下面介绍两种方法:
view():
快速改变张量的形状,但不复制数据,新张量与原始张量共享内存。前提条件是原张量在内存中连续,可用 is_contiguous() 判断,连续会输出 True,否则需要先调用 contiguous() 转成连续的
API为 view(size),size即新形状,可以是元组或者 -1,单独的 -1 表示展平向量,放在元组中表示自动计算某一维度的元素数量(必须与总元素一致)
代码如下:
def test2():'''形状操作'''# view()t1 = torch.arange(6) #创建一个0到5的一维张量print(t1.is_contiguous())t2 = t1.view(-1) #展平 虽然本来就是一维张量t3 = t1.view(2,3) #转成 2x3 的张量t4 = t1.view(3,-1) #转成3行 至于几列由电脑自动计算 只要保证总元素相同print(f't1:{t1}')print(f't2:{t2}')print(f't3:{t3}')print(f't4:{t4}')
运行结果:
若t1 不连续,那么用 t1.contiguous().view() 进行操作
reshape():
更灵活的变形方法,自动处理连续性(如果原始张量不连续,会复制数据),连续的话内存就共享,否则相当于独立数据。
API为 reshape(size),用法与 view() 相同,这里就不再展示代码了,看一下两者的区别吧:
特性 | view() | reshape() |
---|---|---|
共享内存 | 总是共享(需连续) | 可能共享(若连续)或复制 |
连续性要求 | 必须连续 | 自动处理连续性 |
使用场景 | 确定张量连续时的高效操作 | 不确定连续性时的通用操作 |
5、维度转换
pytorch 中维度转换分为两个维度转换和多个维度转换,分别用不同的方法:
transpose(dim0, dim1):两个维度转换
transpose() 仅用于两个维度的转换如矩形转置,返回的是原张量的视图,参数 dim0 是要交换的第一个维度,dim1 是要交换的第二个,代码如下:
t1 = torch.tensor([[1,2],[3,4]])t2 = t1.transpose(0,1) #交换维度,相当于转置print(t1)print(t2)
运行结果:
t2 = t1.transpose(0,1) 表示交换第0维和第1维,第0维表示张量的最外层结构,也就是行方向,第1维就表示列方向,所以交换的是行列方向,就相当于矩阵转置了
permute(dim0, dim1, ... , dimN):重排所有维度
permute() 方法自由重新排列所有维度,可一次性交换多个维度,适用于复杂的维度重组(如调整图像通道顺序),代码如下:
#多维度重排 permutet3 = torch.rand(2,3,4)t4 = t3.permute(2,1,0)print(t4.shape)
运行结果:
两者的区别在于:
特性 | transpose(dim0, dim1) | permute(dim0, dim1, ..., dimN) |
---|---|---|
交换维度数量 | 只能交换两个维度 | 可以一次性重排所有维度 |
灵活性 | 低 | 高 |
常用场景 | 矩阵转置、简单维度交换 | 复杂的维度重组(如NCHW→NHWC) |
连续性 | 通常输出不连续 | 通常输出不连续 |
注意,使用了这两个方法后输出的新张量通常不连续,所以后续可能要用 contiguous() 转换
6、升维降维
升维和降维是常用的操作,需要掌握:
unsqueeze():升维
升维操作是在一个指定的位置插入一个大小为 1 的新维度,API为 unsqueeze(dim) ,dim 为指定要增加维度的位置(从 0 开始索引),代码示例如下:
def test4():'''升维 unsqueeze()'''t1 = torch.rand(2,3,4)t1 = t1.unsqueeze(2)print(t1.shape)
运行结果:
squeeze():降维
降维是去除不必要的维度,API为 squeeze(dim),dim 是指定要移除的维度。如果指定了 dim,则只移除该维度(前提是该维度大小为 1),如果不指定,则移除所有大小为 1 的维度,代码示例:
'''降维squeeze()'''t2 = torch.tensor([[[1,2,3]]]) #形状为 (1,1,3)t3 = t2.squeeze() #不指定dimt4 = t2.squeeze(0) #指定dim为0print(t2.shape)print(t3.shape)print(t4.shape)
运行结果:
可以看到,当不指定 dim 时移除了所有大小为1的维度。指定之后只移除了第0维的1。
这篇就到这里,但其实还有很多其他的操作只是我不想写了[晕],居然写了8000多字。。下一篇写啥我也没想好,先这样吧(๑•̀ㅂ•́)و✧
以上有问题可以指出