【Pytorch】2025 Pytorch基础入门教程(完整详细版)
想搞定 PyTorch 核心操作?这份张量指南超实用!从创建、运算到梯度计算、模型保存全拆解,帮你快速夯实深度学习基础~📌点赞关注小编哦~
一、张量创建
1.1 基本创建方式
1)torch.tensor 根据指定数据创建张量 (最常用)
2)torch.Tensor 根据形状创建张量,其也可用来创建指定数据的张量
3)torch.IntTensor、torch.FloatTensor、torch.DoubleTensor 创建指定类型的张量
Tensor是PyTorch中最基础的数据结构,简单说就是多维矩阵,能借助GPU加速运算,是承载数据和运算的核心,其基本数据类型如下
数据类型 | CPU 张量(类型别名) | GPU 张量(类型别名) | 备注(dtype 对应) |
---|---|---|---|
8位无符号整型(uint8) | torch.ByteTensor | torch.cuda.ByteTensor | 范围 0~255,对应 dtype=torch.uint8 |
8位有符号整型(int8) | torch.CharTensor | torch.cuda.CharTensor | 范围 -128~127,对应 dtype=torch.int8 |
16位有符号整型(int16) | torch.ShortTensor | torch.cuda.ShortTensor | 对应 dtype=torch.int16 |
32位有符号整型(int32) | torch.IntTensor | torch.cuda.IntTensor | 对应 dtype=torch.int32 |
64位有符号整型(int64) | torch.LongTensor | torch.cuda.LongTensor | 修正拼写错误,对应 dtype=torch.int64 |
16位浮点型(半精度,float16) | torch.HalfTensor | torch.cuda.HalfTensor | GPU 加速常用,对应 dtype=torch.float16 |
32位浮点型(float32) | torch.FloatTensor | torch.cuda.FloatTensor | 对应 dtype=torch.float32(默认类型) |
64位浮点型(float64) | torch.DoubleTensor | torch.cuda.DoubleTensor | 对应 dtype=torch.float64 |
布尔类型(bool) | torch.BoolTensor | torch.cuda.BoolTensor | 对应 dtype=torch.bool(Python 布尔映射) |
import numpy as np
import torch# 1. 根据已有数据创建张量
def test01():# 1. 创建张量标量data = torch.tensor(10)print(data)# 2. numpy 数组,由于 data 为 float64,下面代码也使用该类型data = np.random.randn(2, 3)data = torch.tensor(data)print(data)# 3. 列表,下面代码使用默认元素类型 float32data = [[10., 20., 30.], [40., 50., 60.]]data = torch.tensor(data)print(data)print('-' * 50)# 2. 创建指定形状的张量
def test02():# 1. 创建2行3列的张量,默认dtype为float32data = torch.Tensor(2, 3)print(data)# 2. 注意:如果传递列表,则创建包含指定元素的张量data = torch.Tensor([10])print(data)data = torch.Tensor([10, 20])print(data)print('-' * 50)# 3. 使用具体类型的张量
def test03():# 1. 创建2行3列,dtype为int32的张量data = torch.IntTensor(2, 3)print(data)# 2. 注意:如果传递的元素类型不正确,则会进行类型转换data = torch.IntTensor([-2.5, -3.3])print(data)# 3. 其他的类型# data = torch.ShortTensor([2, 3]) # int16# data = torch.LongTensor([2, 3]) # int64# data = torch.FloatTensor([2.5, 3.3]) # float32# print(data)print('-' * 50)if __name__ == '__main__':test01()test02()test03()
# 运行结果
tensor(10)
tensor([[ 0.7219, 0.7726, -1.1605],[ 0.1611, 0.4240, -0.0588]], dtype=torch.float64)
tensor([[10., 20., 30.],[40., 50., 60.]])
--------------------------------------------------
tensor([[1.7608e+13, 1.2752e-42, 3.0000e+01],[4.0000e+01, 5.0000e+01, 6.0000e+01]])
tensor([10.])
tensor([10., 20.])
--------------------------------------------------
tensor([[1434459200, 910, 1056259574],[1075273419, 1062076309, 1059627882]], dtype=torch.int32)
tensor([-2, -3], dtype=torch.int32)
1.2 创建线性和随机张量
1)torch.arange 和 torch.linspace 创建线性张量
2)torch.random.init_seed 和 torch.random.manual_seed 随机种子设置
-
torch.random.initial_seed() 用于获取当前随机数生成器的初始种子值
在没有手动设置随机种子的情况下,PyTorch 的随机数生成器会根据系统时钟等因素自动生成一个初始种子,调用这个函数就可以查看这个自动生成的种子值 -
torch.random.manual_seed(seed) 用于手动设置随机数生成器的种子
当你设置了一个固定的种子值后,后续的随机数生成操作就会变得可复现,即每次运行代码,只要设置相同的随机种子,就会得到相同的随机数序列
3)torch.randn 创建随机张量
import torch# 1. 创建线性空间的张量
def test01():# 1. 在指定区间按照步长生成元素 [start, end, step)data = torch.arange(0, 10, 2)print(data)# 2. 在指定区间按照元素个数生成data = torch.linspace(0, 11, 10)print(data)# 2. 创建随机张量
def test02():# 1. 创建随机张量data = torch.randn(2, 3) # 创建2行3列张量print(data)# 2. 随机数种子设置print('随机数种子:', torch.random.initial_seed())torch.random.manual_seed(100)print('随机数种子:', torch.random.initial_seed())# 3. 随机种子的复现作用测试
def test03():# 设置随机种子torch.random.manual_seed(100)# 创建一个随机初始化的张量tensor1 = torch.randn(3, 3)print("Tensor 1:\n", tensor1)# 再次设置相同的随机种子torch.random.manual_seed(100)# 再次创建一个随机初始化的张量tensor2 = torch.randn(3, 3)print("Tensor 2:\n", tensor2)if __name__ == '__main__':test01()#test02()#test03()
📌关于计算机中的随机数的原理?
- 本质: 计算机中没有真随机数,全都是 伪随机数!!!
- 伪随机数: 都是一个起始值,按照一系列确定的公式计算出来的值!!!
- 为什么要给一个初始化种子(100)?就是给这个确定的起始值
1.3 创建 01张量
1)torch.ones 和 torch.ones_like 创建全1张量
2)torch.zeros 和 torch.zeros_like 创建全0张量
3)torch.full 和 torch.full_like 创建全为指定值张量
import torch# 1. 创建全0张量
def test01():# 1. 创建指定形状全0张量data = torch.zeros(2, 3)print(data)# 2. 根据张量形状创建全0张量data = torch.zeros_like(data)print(data)print('-' * 50)# 2. 创建全1张量
def test02():# 1. 创建指定形状全1张量data = torch.ones(2, 3)print(data)# 2. 根据张量形状创建全1张量data = torch.ones_like(data)print(data)print('-' * 50)# 3. 创建全为指定值的张量
def test03():# 1. 创建指定形状指定值的张量data = torch.full([2, 3], 10)print(data)# 2. 根据张量形状创建指定值的张量data = torch.full_like(data, 20)print(data)if __name__ == '__main__':test01()test02()test03()
# 运行结果
tensor([[0., 0., 0.],[0., 0., 0.]])
tensor([[0., 0., 0.],[0., 0., 0.]])
--------------------------------------------------
tensor([[1., 1., 1.],[1., 1., 1.]])
tensor([[1., 1., 1.],[1., 1., 1.]])
--------------------------------------------------
tensor([[10, 10, 10],[10, 10, 10]])
tensor([[20, 20, 20],[20, 20, 20]])
1.4 张量类型的转换
1)tensor.type(torch.DoubleTensor)
2)torch.double
import torchdef test():data = torch.full([2, 3], 10)print(data.dtype)# 将 data 元素类型转换为 float64 类型# 1. 第一种方法data = data.type(torch.DoubleTensor)print(data.dtype)# 转换为其他类型(示例,可按需取消注释使用)# data = data.type(torch.ShortTensor)# data = data.type(torch.IntTensor)# data = data.type(torch.LongTensor)# data = data.type(torch.FloatTensor)# 2. 第二种方法data = data.double()print(data.dtype)# 转换为其他类型(示例,可按需取消注释使用)data = data.short()print(data.dtype)data = data.int()print(data.dtype)data = data.long()print(data.dtype)data = data.float()print(data.dtype)if __name__ == "__main__":test()
# 运行结果
torch.int64
torch.float64
torch.float64
torch.int16
torch.int32
torch.int64
torch.float32
📌关于模型参数的精度?
np.random.randn()
— 默认精度为 float64torch.randn()
— PyTorch中的浮点值默认精度为 float32- 混合精度训练:
float32
与float16
、bfloat16
混合训练(节省显存) - DeepSeek-V3:
float8 + float16 + float32
混合训练
二、张量数值运算
2.1 张量基本运算
基本运算中,包括 add、sub、mul、div、neg 等函数, 以及这些函数的带下划线的版本 add_、sub_、mul_、div_、neg_, 其中带下划线的版本为修改原数据
import numpy as np
import torchdef test():data = torch.randint(0, 10, [2, 3])print(data)print('-' * 50)# 1. 不修改原数据new_data = data.add(10) # 等价 new_data = data + 10print(new_data)print(data)print('-' * 50)# 2. 直接修改原数据# 注意: 带下划线的函数为修改原数据本身data.add_(10) # 等价 data += 10print(data)# 3. 其他函数print(data.sub(100)) #对张量 data 中的每个元素都减去 100print(data.mul(100))print(data.div(100))print(data.neg())print(data.abs())if __name__ == '__main__':test()
# 运行结果
tensor([[8, 7, 4],[6, 0, 1]])
--------------------------------------------------
tensor([[18, 17, 14],[16, 10, 11]])
tensor([[8, 7, 4],[6, 0, 1]])
--------------------------------------------------
tensor([[18, 17, 14],[16, 10, 11]])
tensor([[-82, -83, -86],[-84, -90, -89]])
tensor([[1800, 1700, 1400],[1600, 1000, 1100]])
tensor([[0.1800, 0.1700, 0.1400],[0.1600, 0.1000, 0.1100]])
tensor([[-18, -17, -14],[-16, -10, -11]])
tensor([[18, 17, 14],[16, 10, 11]])
2.2 阿达玛积
阿达玛积指的是矩阵对应位置的元素相乘
import numpy as np
import torchdef test():data1 = torch.tensor([[1, 2], [3, 4]])data2 = torch.tensor([[5, 6], [7, 8]])# 第一种方式data = torch.mul(data1, data2)print(data)print('-' * 50)# 第二种方式data = data1 * data2print(data)if __name__ == '__main__':test()
# 运行结果
tensor([[ 5, 12],[21, 32]])
--------------------------------------------------
tensor([[ 5, 12],[21, 32]])
2.3 点积运算
点积运算要求第一个矩阵 shape: (n, m),第二个矩阵 shape: (m, p), 两个矩阵点积运算 shape 为: (n, p)
运算符 @ 用于进行两个矩阵的点乘运算
torch.mm 用于进行两个矩阵点乘运算,要求输入的矩阵为 2 维
torch.bmm 用于批量进行矩阵点乘运算,要求输入的矩阵为 3 维
📌torch.matmul 对进行点乘运算的两矩阵形状没有限定.
a. 对于输入都是二维的张量相当于 mm 运算.
b. 对于输入都是三维的张量相当于 bmm 运算
c. 对数输入的 shape 不同的张量,对应的最后几个维度必须符合矩阵运算规则
import numpy as np
import torch# 1. 点积运算
def test01():data1 = torch.tensor([[1, 2], [3, 4], [5, 6]])data2 = torch.tensor([[5, 6], [7, 8]])# 第一种方式data = data1 @ data2print(data)print('-' * 50)# 第二种方式data = torch.mm(data1, data2)print(data)print('-' * 50)# 第三种方式data = torch.matmul(data1, data2)print(data)print('-' * 50)# 2. torch.mm 和 torch.matmul 的区别
def test02():# matmul 可以两个维度可以不同# 第一个张量: (3, 4, 5)# 第二个张量: (5, 4)# torch.mm 不可以相乘,而 matmul 则可以相乘print(torch.matmul(torch.randn(2, 3, 4, 5), torch.randn(1, 5, 4)).shape)# print(torch.matmul(torch.randn(5, 4), torch.randn(3, 4, 5)).shape)# print(torch.mm(torch.randn(3, 4, 5), torch.randn(5, 4)).shape)# print(torch.mm(torch.randn(5, 4), torch.randn(3, 4, 5)).shape)# 3. torch.mm 函数的用法(注:实际用的是 torch.bmm,这里注释可能笔误)
def test03():# 批量点积运算# 第一个维度为 batch_size# 矩阵的二三维要满足矩阵乘法规则data1 = torch.randn(3, 4, 5)data2 = torch.randn(3, 5, 8)data = torch.bmm(data1, data2)print(data.shape)if __name__ == '__main__':test01() test02() test03()
# 运行结果
tensor([[19, 22],[43, 50],[67, 78]])
--------------------------------------------------
tensor([[19, 22],[43, 50],[67, 78]])
--------------------------------------------------
tensor([[19, 22],[43, 50],[67, 78]])
--------------------------------------------------
torch.Size([2, 3, 4, 4])
torch.Size([3, 4, 8])
2.4 指定运行设备
PyTorch 默认会将张量创建在 CPU 控制的内存中, 即: 默认的运算设备为 CPU。我们也可以将张量创建在 GPU 上, 能够利用对于矩阵计算的优势加快模型训练。 将张量移动到 GPU 上有三种方法:
- 使用 cuda 方法 2. 直接在 GPU 上创建张量 3. 使用 to 方法指定设备
import torch# 1. 使用 cuda 方法
def test01():data = torch.tensor([10, 20, 30])print('存储设备:', data.device)# 如果安装的不是 gpu 版本的 PyTorch# 或电脑本身没有 NVIDIA 卡的计算环境# 下面代码可能会报错data = data.cuda()print('存储设备:', data.device)# 使用 cpu 函数将张量移动到 cpu 上data = data.cpu()print('存储设备:', data.device)# 输出结果:# 存储设备: cpu# 存储设备: cuda:0# 存储设备: cpu# 2. 直接将张量创建在 GPU 上
def test02():data = torch.tensor([10, 20, 30], device='cuda:0')print('存储设备:', data.device)# 使用 cpu 函数将张量移动到 cpu 上data = data.cpu()print('存储设备:', data.device)# 输出结果:# 存储设备: cuda:0# 存储设备: cpu# 3. 使用 to 方法
def test03():data = torch.tensor([10, 20, 30])print('存储设备:', data.device)data = data.to('cuda:0')print('存储设备:', data.device)# 输出结果:# 存储设备: cpu# 存储设备: cuda:0# 4. 存储在不同设备的张量不能运算
def test04():data1 = torch.tensor([10, 20, 30], device='cuda:0')data2 = torch.tensor([10, 20, 30])print(data1.device, data2.device)# RuntimeError: Expected all tensors to be on the same device,# but found at least two devices, cuda:0 and cpu!data = data1 + data2print(data)if __name__ == '__main__':test01()test02()test03()test04()
三、张量类型转换
3.1 张量转换为 numpy 数组
使用 Tensor.numpy 函数可以将张量转换为 ndarray 数组,但是共享内存,可以使用 copy 函数避免共享
# 1. 将张量转换为 numpy 数组
def test01():data_tensor = torch.tensor([2, 3, 4])# 使用张量对象中的 numpy 函数进行转换data_numpy = data_tensor.numpy()print(type(data_tensor))print(type(data_numpy))# 注意: data_tensor 和 data_numpy 共享内存# 修改其中的一个, 另外一个也会发生改变# data_tensor[0] = 100data_numpy[0] = 100print(data_tensor)print(data_numpy)# 2. 使用 torch.tensor 函数
def test02():data_numpy = np.array([2, 3, 4])data_tensor = torch.tensor(data_numpy)# numpy 和 tensor 不共享内存# data_numpy[0] = 100data_tensor[0] = 100print(data_tensor)print(data_numpy)# 3. 标量张量和数字的转换
def test03():# 当张量只包含一个元素时,可以通过 item 函数提取出该值data = torch.tensor([30, ])print(data.item())data = torch.tensor(30)print(data.item())# data1 = torch.tensor([1.55, 2.55])data1 = torch.tensor([1.55])print(data1.item())if __name__ == '__main__':test01()test02()test03()
<class 'torch.Tensor'>
<class 'numpy.ndarray'>
tensor([100, 3, 4])
[100 3 4]
tensor([100, 3, 4], dtype=torch.int32)
[2 3 4]
30
30
1.5499999523162842
📌Python代码中如何比较两个数的大小?
方法1: 直接比较(适用于整数/精确值)
if x == y: print('hello world')
else: print('bad!')
方法2: 浮点数差值比较(避免精度误差)
if (x - y) < 1e-6: # 设定极小区间(如1e-6) print('hello world')
else: print('bad!')
四、张量拼接操作
张量的拼接操作在神经网络搭建过程中是非常常用的方法,例如:在后面将要学习到的残差网络、注意力机制中都使用到了张量拼接
4.1 torch.cat 函数的使用
import torchdef test01():data1 = torch.randint(0, 10, [3, 5, 4])data2 = torch.randint(0, 10, [3, 5, 4])print(data1)print(data2)print('-' * 50)# 1. 按0维度拼接new_data = torch.cat([data1, data2], dim=0)print(new_data.shape)print(new_data)print('-' * 50)# 2. 按1维度拼接new_data = torch.cat([data1, data2], dim=1)print(new_data.shape)print(new_data)print('-' * 50)# 3. 按2维度拼接new_data = torch.cat([data1, data2], dim=2)print(new_data.shape)print(new_data)print('-' * 50)'''new_data = torch.cat([data1, data2], dim=-1)print(new_data.shape)new_data = torch.cat([data1, data2], dim=-3)print(new_data.shape)'''def test02():data1 = torch.randint(0, 10, [2, 3])data2 = torch.randint(0, 10, [2, 3])# data2= torch.randint(0, 10, [2, 3, 4])print(data1)print(data2)new_data1 = torch.cat([data1, data2], dim=0)print('-----------------------------')new_data2 = torch.stack([data1, data2], dim=0)print(new_data1.shape)print(new_data2.shape)print(new_data1)print(new_data2)#print('-' * 50)# x = torch.Tensor([1])# y = torch.Tensor([2])# z = torch.stack([x, y],dim=0)# print(z)'''new_data = torch.stack([data1, data2], dim=1)print(new_data.shape)print(new_data)print('---------------------')new_data = torch.stack([data1, data2], dim=2)print(new_data.shape)print(new_data)'''if __name__ == '__main__':test01() test02()
tensor([[[4, 9, 8, 2],[2, 7, 5, 0],[5, 0, 5, 0],[1, 7, 2, 4],[5, 6, 1, 3]],[[6, 3, 2, 9],[3, 5, 6, 8],[3, 8, 8, 6],[2, 2, 0, 8],[2, 1, 7, 4]],[[8, 0, 3, 6],[4, 6, 1, 6],[6, 3, 6, 6],[3, 1, 2, 6],[5, 5, 8, 1]]])
tensor([[[5, 9, 2, 6],[5, 3, 4, 5],[4, 3, 2, 6],[2, 9, 5, 0],[7, 9, 5, 0]],[[3, 7, 9, 0],[4, 2, 1, 5],[0, 4, 6, 1],[3, 3, 9, 4],[1, 1, 3, 0]],[[4, 5, 4, 6],[7, 6, 2, 0],[8, 4, 9, 5],[3, 9, 7, 1],[5, 8, 0, 2]]])
--------------------------------------------------
torch.Size([6, 5, 4])
tensor([[[4, 9, 8, 2],[2, 7, 5, 0],[5, 0, 5, 0],[1, 7, 2, 4],[5, 6, 1, 3]],[[6, 3, 2, 9],[3, 5, 6, 8],[3, 8, 8, 6],[2, 2, 0, 8],[2, 1, 7, 4]],[[8, 0, 3, 6],[4, 6, 1, 6],[6, 3, 6, 6],[3, 1, 2, 6],[5, 5, 8, 1]],[[5, 9, 2, 6],[5, 3, 4, 5],[4, 3, 2, 6],[2, 9, 5, 0],[7, 9, 5, 0]],[[3, 7, 9, 0],[4, 2, 1, 5],[0, 4, 6, 1],[3, 3, 9, 4],[1, 1, 3, 0]],[[4, 5, 4, 6],[7, 6, 2, 0],[8, 4, 9, 5],[3, 9, 7, 1],[5, 8, 0, 2]]])
--------------------------------------------------
torch.Size([3, 10, 4])
tensor([[[4, 9, 8, 2],[2, 7, 5, 0],[5, 0, 5, 0],[1, 7, 2, 4],[5, 6, 1, 3],[5, 9, 2, 6],[5, 3, 4, 5],[4, 3, 2, 6],[2, 9, 5, 0],[7, 9, 5, 0]],[[6, 3, 2, 9],[3, 5, 6, 8],[3, 8, 8, 6],[2, 2, 0, 8],[2, 1, 7, 4],[3, 7, 9, 0],[4, 2, 1, 5],[0, 4, 6, 1],[3, 3, 9, 4],[1, 1, 3, 0]],[[8, 0, 3, 6],[4, 6, 1, 6],[6, 3, 6, 6],[3, 1, 2, 6],[5, 5, 8, 1],[4, 5, 4, 6],[7, 6, 2, 0],[8, 4, 9, 5],[3, 9, 7, 1],[5, 8, 0, 2]]])
--------------------------------------------------
torch.Size([3, 5, 8])
tensor([[[4, 9, 8, 2, 5, 9, 2, 6],[2, 7, 5, 0, 5, 3, 4, 5],[5, 0, 5, 0, 4, 3, 2, 6],[1, 7, 2, 4, 2, 9, 5, 0],[5, 6, 1, 3, 7, 9, 5, 0]],[[6, 3, 2, 9, 3, 7, 9, 0],[3, 5, 6, 8, 4, 2, 1, 5],[3, 8, 8, 6, 0, 4, 6, 1],[2, 2, 0, 8, 3, 3, 9, 4],[2, 1, 7, 4, 1, 1, 3, 0]],[[8, 0, 3, 6, 4, 5, 4, 6],[4, 6, 1, 6, 7, 6, 2, 0],[6, 3, 6, 6, 8, 4, 9, 5],[3, 1, 2, 6, 3, 9, 7, 1],[5, 5, 8, 1, 5, 8, 0, 2]]])
📌torch.cat() 和 torch.stack() 的区别?
torch.cat()
:连接两个张量,指定维度(如dim=0
),结果维度不变!!!torch.stack()
:拼接两个张量,指定维度(如dim=0
),结果维度+1(在指定dim
上新增一个维度!!!)
五、张量索引操作
我们在操作张量时,经常需要去进行获取或者修改操作,掌握张量的花式索引操作是必须的一项能力
import torch# 1. 简单行、列索引
def test01(data):print("简单行、列索引:")print("第0行数据:", data[0]) # 获取第0行数据print("所有行的第0列数据:", data[:, 0]) # 获取所有行的第0列数据print('-' * 50)# 2. 列表索引
def test02(data):print("列表索引:")# 返回 (0, 1)、(1, 2) 两个位置的元素print("(0,1)和(1,2)位置元素:", data[[0, 1], [1, 2]])# 返回 0、1 行的 1、2 列共4个元素(二维索引)print("0-1行的1-2列元素:\n", data[[[0], [1]], [1, 2]])print('-' * 50)# 3. 范围索引
def test03(data):print("范围索引:")# 前3行的前2列数据(行:0-2,列:0-1)print("前3行前2列:\n", data[:3, :2])# 第2行到最后一行的前2列数据(行:2-末尾,列:0-1)print("第2行及以后的前2列:\n", data[2:, :2])print('-' * 50)# 4. 布尔索引
def test04(data):print("布尔索引:")# 第2列大于5的行数据print("第2列>5的行:\n", data[data[:, 2] > 5])# 第1行大于5的列数据print("第1行>5的列:", data[:, data[1] > 5])print('-' * 50)# 5. 多维索引(三维张量)
def test05():print("多维索引(三维张量):")# 创建3×4×5的三维随机整数张量(0-9)data = torch.randint(0, 10, [3, 4, 5])print("原始三维张量:\n", data)print("=" * 50)print("第0层的4×5矩阵:\n", data[0, :, :]) # 第0个维度全取print("每一层的第0行:\n", data[:, 0, :]) # 第1个维度取第0行print("每一层每一行的第0列:\n", data[:, :, 0]) # 第2个维度取第0列print("张量形状:", data.shape)print('-' * 50)if __name__ == '__main__':# 定义一个5行6列的二维张量(0-9随机整数),供test01-test04使用data_2d = torch.randint(0, 10, [5, 6])print("基础二维张量:\n", data_2d)print("=" * 50)# 依次调用索引测试函数(传入二维张量)test01(data_2d)test02(data_2d)test03(data_2d)test04(data_2d)# 测试三维张量索引(内部独立定义数据)test05()
# 运行结果
基础二维张量:tensor([[1, 1, 7, 9, 6, 3],[4, 7, 2, 1, 3, 4],[6, 6, 0, 9, 9, 6],[0, 2, 9, 6, 3, 1],[7, 4, 2, 6, 7, 4]])
==================================================
简单行、列索引:
第0行数据: tensor([1, 1, 7, 9, 6, 3])
所有行的第0列数据: tensor([1, 4, 6, 0, 7])
--------------------------------------------------
列表索引:
(0,1)和(1,2)位置元素: tensor([1, 2])
0-1行的1-2列元素:tensor([[1, 7],[7, 2]])
--------------------------------------------------
范围索引:
前3行前2列:tensor([[1, 1],[4, 7],[6, 6]])
第2行及以后的前2列:tensor([[6, 6],[0, 2],[7, 4]])
--------------------------------------------------
布尔索引:
第2列>5的行:tensor([[1, 1, 7, 9, 6, 3],[0, 2, 9, 6, 3, 1]])
第1行>5的列: tensor([[1],[7],[6],[2],[4]])
--------------------------------------------------
多维索引(三维张量):
原始三维张量:tensor([[[4, 1, 8, 9, 7],[5, 4, 3, 3, 4],[2, 0, 8, 6, 3],[8, 7, 2, 7, 4]],[[8, 9, 1, 9, 7],[9, 6, 1, 1, 2],[2, 9, 7, 5, 8],[7, 7, 0, 9, 4]],[[6, 6, 3, 3, 1],[7, 6, 7, 1, 6],[3, 4, 2, 5, 2],[3, 5, 3, 0, 1]]])
==================================================
第0层的4×5矩阵:tensor([[4, 1, 8, 9, 7],[5, 4, 3, 3, 4],[2, 0, 8, 6, 3],[8, 7, 2, 7, 4]])
每一层的第0行:tensor([[4, 1, 8, 9, 7],[8, 9, 1, 9, 7],[6, 6, 3, 3, 1]])
每一层每一行的第0列:tensor([[4, 5, 2, 8],[8, 9, 2, 7],[6, 7, 3, 3]])
张量形状: torch.Size([3, 4, 5])
张量操作是 PyTorch 的基石,吃透这些能为深度学习项目铺路。趁热打铁,用这些知识开启你的 AI 开发实战吧!
六、张量形状操作
-
学习目标
-
掌握reshape, transpose, permute, view, contiguous, squeeze, unsqueeze等函数使用
-
在我们后面搭建网络模型时,数据都是基于张量形式的表示,网络层与层之间很多都是以不同的 shape 的方式进行表现和运算,我们需要掌握对张量形状的操作,以便能够更好处理网络各层之间的数据连接
6.1 reshape 函数的用法
- reshape 函数可以在保证张量数据不变的前提下改变数据的维度,将其转换成指定的形状,在后面的神经网络学习时,会经常使用该函数来调节数据的形状,以适配不同网络层之间的数据传递。
import torch
import numpy as npdef test():data = torch.tensor([[10, 20, 30], [40, 50, 60]])# 1、使用 shape 属性或者 size 方法都可以获得张量的形状print(data.shape, data.shape[0], data.shape[1])print(data.size(), data.size(0), data.size(1))# 2、使用 reshape 函数修改张量形状new_data = data.reshape(1, 6)print(new_data.shape)if __name__ == '__main__':test()
# 运行结果
torch.Size([2, 3]) 2 3
torch.Size([2, 3]) 2 3
torch.Size([1, 6])
6.2 transpose 和 permute 函数的使用
-
transpose 函数可以实现交换张量形状的指定维度,例如:一个张量的形状为 (2,3,4) 可以通过 transpose 函数把 3 和 4 进行交换,将张量的形状变为 (2,4,3)
-
📌permute 函数可以一次交换更多的维度
import torch
import numpy as npdef test():data = torch.tensor(np.random.randint(0, 10, [3, 4, 5]))print('data shape:', data.shape)# 1、交换1和2维度new_data = torch.transpose(data, 1, 2)print('new_data shape:', new_data.shape)data[0,0,0] = 1000print("data:",data)print("new_data:", new_data)print("new_data shape:", new_data.size())print("="*50)# 2、将 data 的形状修改为 (4, 5, 3)new_data = torch.transpose(data, 0, 1)print('new_data shape:', new_data.shape)new_data = torch.transpose(new_data, 1, 2)print('new_data shape:', new_data.shape)print("="*50)# 3、使用 permute 函数将形状修改为 (4, 5, 3)new_data = torch.permute(data, [1, 2, 0])print('new_data shape:', new_data.shape)if __name__ == '__main__':test()
# 运行结果
data shape: torch.Size([3, 4, 5])
new_data shape: torch.Size([3, 5, 4])
data: tensor([[[1000, 4, 6, 0, 7],[ 2, 4, 9, 6, 3],[ 4, 8, 8, 9, 3],[ 6, 6, 4, 6, 1]],[[ 1, 4, 2, 0, 5],[ 0, 0, 0, 7, 9],[ 5, 0, 2, 2, 4],[ 1, 2, 5, 6, 0]],[[ 1, 2, 9, 1, 4],[ 3, 2, 0, 7, 6],[ 9, 5, 4, 2, 7],[ 7, 9, 1, 1, 1]]], dtype=torch.int32)
new_data: tensor([[[1000, 2, 4, 6],[ 4, 4, 8, 6],[ 6, 9, 8, 4],[ 0, 6, 9, 6],[ 7, 3, 3, 1]],[[ 1, 0, 5, 1],[ 4, 0, 0, 2],[ 2, 0, 2, 5],[ 0, 7, 2, 6],[ 5, 9, 4, 0]],[[ 1, 3, 9, 7],[ 2, 2, 5, 9],[ 9, 0, 4, 1],[ 1, 7, 2, 1],[ 4, 6, 7, 1]]], dtype=torch.int32)
new_data shape: torch.Size([3, 5, 4])
==================================================
new_data shape: torch.Size([4, 3, 5])
new_data shape: torch.Size([4, 5, 3])
==================================================
new_data shape: torch.Size([4, 5, 3])
6.3 view 和 contigous 函数的用法
- 📌 view 函数也可以用于修改张量的形状,但是其用法比较局限,只能用于存储在整块内存中的张量。在 PyTorch 中,有些张量是由不同的数块组成的,
它们并没有存储在整块的内存中,view 函数无法对这样的张量进行变形处理
。例如一个张量经过了 transpose 或者 permute 函数的处理之后,就无法使用 view 函数进行形状操作
import torch
import numpy as npdef test():data = torch.tensor([[10, 20, 30], [40, 50, 60]])print('data shape:', data.shape)# 1、使用 view 函数修改形状new_data = data.view(3, 2)print('new_data shape:', new_data.shape)# 2、判断张量是否使用整块内存print('data:', data.is_contiguous()) # True# 3、使用 transpose 函数修改形状new_data = torch.transpose(data, 0, 1)print('new_data:', new_data.is_contiguous()) # Falseprint("="*50)# new_data = new_data.view(2, 3) # RuntimeErrornew_data = new_data.reshape(2,3)print('new_data.shape:',new_data.shape)print(new_data.is_contiguous())print("="*50)# 需要先使用 contiguous 函数转换为整块内存的张量,再使用 view 函数print('new_data.contiguous():', new_data.contiguous().is_contiguous())new_data = new_data.contiguous().view(2, 3)print('new_data shape:', new_data.shape)if __name__ == '__main__':test()
# 运行结果
data shape: torch.Size([2, 3])
new_data shape: torch.Size([3, 2])
data: True
new_data: False
==================================================
new_data.shape: torch.Size([2, 3])
True
==================================================
new_data.contiguous(): True
new_data shape: torch.Size([2, 3])
6.4 squeeze 和 unsqueze 函数的用法
- squeeze 函数用于删除 shape 为 1 的维度,unsqueeze 在指定维度添加 1,以增加数据的形状。
import torch
import numpy as npdef test():data = torch.tensor(np.random.randint(0, 10, [1, 3, 1, 5]))print('data shape:', data.size())# 1、去掉值为1的维度new_data = data.squeeze()print('new_data shape:', new_data.size()) # torch.Size([3, 5])# 2、去掉指定位置为1的维度,注意:如果指定位置不是1则不删除new_data = data.squeeze(2)print('new_data shape:', new_data.size()) # torch.Size([3, 5])# 3、在2维度增加一个维度(原代码中维度索引为 -1 ,即最后一维)new_data = data.unsqueeze(-1)print('new_data shape:', new_data.size()) # torch.Size([1, 3, 1, 5, 1])if __name__ == '__main__':test()
# 运行结果
data shape: torch.Size([1, 3, 1, 5])
new_data shape: torch.Size([3, 5])
new_data shape: torch.Size([1, 3, 5])
new_data shape: torch.Size([1, 3, 1, 5, 1])
七、 张量运算函数
- 学习目标
- 掌握张量相关运算函数
7.1 常见运算函数
- PyTorch 为每个张量封装很多实用的计算函数,例如计算均值、平方根、求和等等
import torchdef test():data = torch.randint(0, 10, [2, 3], dtype=torch.float64)print(data)print('-' * 50)# 1. 计算均值 print(data.mean()) print(data.mean(dim=0)) # 📌 按列计算均值 print(data.mean(dim=1)) # 📌 按行计算均值 print('-' * 50) # 2. 计算总和 print(data.sum()) print(data.sum(dim=0)) print(data.sum(dim=1)) print('-' * 50) # 3. 计算平方 print(data.pow(2)) print('-' * 50) # 4. 计算平方根 print(data.sqrt()) print('-' * 50) # 5. 指数计算,e^n 次方 print(data.exp()) print('-' * 50) # 6. 对数计算 print(data.log()) # 以 e 为底 print(data.log10()) print(data.log2()) if __name__ == '__main__': test()
# 运行结果
tensor([[2., 7., 1.],[0., 1., 3.]], dtype=torch.float64)
--------------------------------------------------
tensor(2.3333, dtype=torch.float64)
tensor([1., 4., 2.], dtype=torch.float64)
tensor([3.3333, 1.3333], dtype=torch.float64)
--------------------------------------------------
tensor(14., dtype=torch.float64)
tensor([2., 8., 4.], dtype=torch.float64)
tensor([10., 4.], dtype=torch.float64)
--------------------------------------------------
tensor([[ 4., 49., 1.],[ 0., 1., 9.]], dtype=torch.float64)
--------------------------------------------------
tensor([[1.4142, 2.6458, 1.0000],[0.0000, 1.0000, 1.7321]], dtype=torch.float64)
--------------------------------------------------
tensor([[7.3891e+00, 1.0966e+03, 2.7183e+00],[1.0000e+00, 2.7183e+00, 2.0086e+01]], dtype=torch.float64)
--------------------------------------------------
tensor([[0.6931, 1.9459, 0.0000],[ -inf, 0.0000, 1.0986]], dtype=torch.float64)
tensor([[0.3010, 0.8451, 0.0000],[ -inf, 0.0000, 0.4771]], dtype=torch.float64)
tensor([[1.0000, 2.8074, 0.0000],[ -inf, 0.0000, 1.5850]], dtype=torch.float64)
八、 自动微分模块
-
学习目标
-
掌握梯度计算
-
自动微分(Autograd)模块对张量做了进一步的封装,具有自动求导功能。自动微分模块是构成神经网络训练的必要模块,在神经网络的反向传播过程中,Autograd 模块基于正向计算的结果对当前的参数进行微分计算,从而实现网络权重参数的更新
8.1 梯度基本计算
我们使用 backward
方法、grad
属性来实现梯度的计算和访问。
import torch # 1. 单标量梯度的计算
# y = x**2 + 20
def test01(): # 定义需要求导的张量 # 张量的类型必须是浮点类型 x = torch.tensor(10, requires_grad=True, dtype=torch.float64) # 变量经过中间计算 f = x ** 2 + 20 # 自动微分 f.backward() # 打印 x 变量的梯度 # backward 函数计算的梯度值会存储在张量的 grad 属性中 print(x.grad) # 2. 单向量梯度的计算
# y = x**2 + 20
def test02(): # 定义需要求导张量 x = torch.tensor([10, 20, 30, 40], requires_grad=True, dtype=torch.float64) f1 = x ** 2 + 20 # 注意: # 由于求导的结果必须是标量 # 而 f1 的结果是: tensor([120., 420., ...]) # 所以,不能直接自动微分 # 需要将结果计算为标量才能进行计算 f2 = f1.mean() # f2 = 1/2 * x # 自动微分 f2.backward() # 打印 x 变量的梯度 print(x.grad) if __name__ == '__main__': test01() test02() # 若需测试可取消注释
# 运行结果
tensor(20., dtype=torch.float64)
tensor([5., 10., 15., 20.], dtype=torch.float64)
8.2 控制梯度计算
- 我们可以通过一些方法使得在
requires_grad=True
的张量在某些时候计算不进行梯度计算。
##### 1. 控制不计算梯度 def test01(): x = torch.tensor(10, requires_grad=True, dtype=torch.float64) print(x.requires_grad) # 第一种方式:对代码进行装饰 with torch.no_grad(): y = x ** 2 print(y.requires_grad) # 第二种方式:对函数进行装饰 @torch.no_grad() def my_func(x): return x ** 2 print(my_func(x).requires_grad) # 第三种方式 torch.set_grad_enabled(False) y = x ** 2 print(y.requires_grad) # 2. 注意:累计梯度
def test02(): # 定义需要求导张量 x = torch.tensor([10., 20., 30., 40.], requires_grad=True, dtype=torch.float64) for _ in range(3): f1 = x ** 2 + 20 f2 = f1.mean() print(f'f2=', f2) # 默认张量的 grad 属性会累计历史梯度值 # 所以,需要我们每次手动清理上次的梯度 # 注意:一开始梯度不存在,需要做判断 if x.grad is not None: x.grad.data.zero_() f2.backward() print(x.grad) print('-' * 50) # 3. 梯度下降优化最优解
def test03(): # y = x**2 x = torch.tensor(10., requires_grad=True, dtype=torch.float64) count = 0 for _ in range(50000): # 正向计算 f = x ** 2 # 梯度清零 if x.grad is not None: x.grad.data.zero_() # 反向传播计算梯度 f.backward() # 更新参数 (variable) data: Tensor | Any x.data = x.data - 0.001 * x.grad count += 1 if count % 500 == 0: print(f'{count:10} {x.data}') if __name__ == '__main__': # test01() # test02() test03() if __name__ == '__main__': test01()
九、模型的保存加载
- 学习目标
- 掌握PyTorch保存模型的方法
- 神经网络的训练有时需要几天、几周,甚至几个月,为了在每次使用模型时避免高代价的重复训练,我们就需要将模型序列化到磁盘中,使用的时候反序列化到内存中
9.1 保存模型参数
import torch
import torch.nn as nn # 假设我们有一个模型
class SimpleModel(nn.Module): def __init__(self): super(SimpleModel, self).__init__() self.fc = nn.Linear(10, 1) def forward(self, x): return self.fc(x) model = SimpleModel() # 📌保存模型的参数
torch.save(model.state_dict(), 'model_weights.pth')
9.2 保存全部模型
import torch
import torch.nn as nn # 假设我们有一个模型
class SimpleModel(nn.Module): def __init__(self): super(SimpleModel, self).__init__() self.fc = nn.Linear(10, 1) def forward(self, x): return self.fc(x) model = SimpleModel() # 保存全部模型
torch.save(model, 'model.pth')
9.3 加载模型参数
# 创建一个与保存时相同结构的模型
model = SimpleModel() # 加载模型的参数
model.load_state_dict(torch.load('model_weights.pth'))
print(model) # 保存模型的参数
# torch.save(model.state_dict(), 'model_weights.pth')
# print(model)
# print('---------------------')
# print(model.state_dict()) # 保存完整的模型
# torch.save(model, 'model_weights_1.pth')
# print(model)
9.4 加载全部模型
model = torch.load('model.pth')
print(model)
9.5 注意事项
- 模型结构:如果你只保存了模型的参数,那么在加载时需要确保你有与保存时相同的模型结构
- 设备兼容性:如果你在一个设备上保存了模型(例如GPU),而在另一个设备上加载(例如CPU),你可能需要使用
map_location
参数来指定设备:device = torch.device('cpu') model.load_state_dict(torch.load('model_weights.pth', map_location=device))
📌吃透这些技巧,PyTorch 开发难题迎刃而解,让模型训练更顺畅,助你在 AI 实践中快速进阶!