目录
[1、 torch.tensor()](#1、 torch.tensor())
[3、torch.IntTensor() 等](#3、torch.IntTensor() 等)
[与 Numpy 数据转换](#与 Numpy 数据转换)
[1、张量转 Numpy](#1、张量转 Numpy)
[2、Numpy 转张量](#2、Numpy 转张量)
[transpose(dim0, dim1):两个维度转换](#transpose(dim0, dim1):两个维度转换)
[permute(dim0, dim1, ... , dimN):重排所有维度](#permute(dim0, dim1, ... , dimN):重排所有维度)
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()
代码示例:
python
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()
代码示例:
python
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位整数)......等。如果数据类型不匹配,那么在创建的过程中会进行类型转换,防止数据丢失。
代码示例:
python
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 个元素的一维张量(相当于等差数列)
代码示例如下:
python
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()
代码实例:
python
def test5():
'''创建随机张量'''
torch.manual_seed(42)# 设置随机数种子
t1 = torch.randn(2,3) #生成随机张量,标准正态分布,均值 0,标准差 1
t2 = 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,且不同设备上的数据是不能相互运算的。切换操作如下:
python
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 进行转换:
python
t1 = t1.cuda()
当然也可以直接在 GPU 上创建张量:
python
data = torch.tensor([1, 2, 3], device='cuda')
print(data.device)
类型转换
在写代码时,类型的不同有时也会导致各种错误,所以类型的转换也是很重要的,有三种类型转换的方法:
python
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) # float32
t1 = t1.half()
print(t1.dtype) # float16
t1 = t1.double()
print(t1.dtype) # float64
t1 = t1.long()
print(t1.dtype) # int64
t1 = 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()
代码示例(浅拷贝):
python
def test8():
'''张量转numpy'''
#浅拷贝
t1 = torch.tensor([[1,2,3], [4,5,6],[7,8,9]])
t2 = t1.numpy() 转 numpy
print(t1)
print(f't2:{t2}')
t2[0][1] = 666 #内存共享
print(t1)
print(f't2:{t2}')
运行结果:

可以看到,t1 转成 t2 后,修改 t2 还是会改变 t1,如果想要避免内存共享可以用深拷贝:
python
#深拷贝
# t2 = t1.numpy().copy()
其他同上
2、Numpy 转张量
同样分为内存共享和内存不共享:
浅拷贝:内存共享,修改 tensor 也会改变原数组,API:torch.from_numpy()
深拷贝:内存不共享,相当于建了一个独立的副本,API:torch.tensor()
代码示例(深拷贝):
python
'''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,如果想要浅拷贝的话:
python
#浅拷贝
# n1_tensor = torch.from_numpy(n1)
tensor常见操作
接下来介绍一些在深度学习中基本的 tensor 操作:
1、获取元素值
我们可以把只含有单个元素的 tensor 转换为Python数值,这和 tensor 的维度无关,但若有多个元素则会报错,且仅适用于 CPU张量。
API为 item():
python
def test9():
'''获取元素值'''
t1 = torch.tensor([[[18]]])
t2 = torch.tensor([20,13])
print(t1.item())
print(t2.item())
运行结果:

可以看到佑报错,这就是因为 t2 含有不止一个元素所以不能用 item()
2、元素值运算
常见的加、减、乘、除、次方、取反、开方等各种操作,但是也分是否修改原始值
不修改原始值代码示例:
python
'''元素值运算'''
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 的元素并没有被改变
修改原始值:
python
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 的乘法
代码示例如下:
python
import torch
def 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() 来实现,代码如下:
python
'''阿达玛积'''
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 表示展平向量,放在元组中表示自动计算某一维度的元素数量(必须与总元素一致)
代码如下:
python
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 是要交换的第二个,代码如下:
python
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() 方法自由重新排列所有维度,可一次性交换多个维度,适用于复杂的维度重组(如调整图像通道顺序),代码如下:
python
#多维度重排 permute
t3 = 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 开始索引),代码示例如下:
python
def test4():
'''升维 unsqueeze()'''
t1 = torch.rand(2,3,4)
t1 = t1.unsqueeze(2)
print(t1.shape)
运行结果:

squeeze():降维
降维是去除不必要的维度,API为 squeeze(dim),dim 是指定要移除的维度。如果指定了 dim,则只移除该维度(前提是该维度大小为 1),如果不指定,则移除所有大小为 1 的维度,代码示例:
python
'''降维squeeze()'''
t2 = torch.tensor([[[1,2,3]]]) #形状为 (1,1,3)
t3 = t2.squeeze() #不指定dim
t4 = t2.squeeze(0) #指定dim为0
print(t2.shape)
print(t3.shape)
print(t4.shape)
运行结果:

可以看到,当不指定 dim 时移除了所有大小为1的维度。指定之后只移除了第0维的1。
这篇就到这里,但其实还有很多其他的操作只是我不想写了[晕],居然写了8000多字。。下一篇写啥我也没想好,先这样吧(๑•̀ㅂ•́)و✧
以上有问题可以指出