深度学习02 pytorch框架
pytorch是python中对张量进行处理的包,提供各种模块实现各种功能
本期主要讲解张量的创建以及一部分pytorch api
PyTorch的安装: pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple
其数据是以张量类型存储(张量只能存放数字类型)
张量的概念:
一种数据存储的方式,类似于ndarray数组

魔方可以看成是一个3,3,3的张量

张量的创建:有点类似于pandas,numpy的创建,可以使用单个元素,列表切片创建
torch.tensor(data=,dtype=) 只能指定数据,dtype指定类型
torch.Tensor(data=, size=()) 既能指定数据,又能指定形状
torch.IntTensor()/FloatTensor()... Tensor直接指定类型创建
张量的基础创建
tensor传入数据的创建:有几个中括号就是几维(虽然不能指定,但是可以传多维数据)
传入列表创建
使用dtype指定类型创建
import torch
# TODO 1.tensor(data)创建张量
print(torch.tensor(10).ndim)
print(torch.tensor([10]).ndim)
print(torch.tensor([[10]]).ndim)
print(torch.tensor([[[10]]]).ndim)
# 通过列表创建张量
print(torch.tensor([[10, 20], [40, 50]]), torch.tensor([[10, 20], [40, 50]]).dtype)
# 通过numpy创建张量
print(torch.tensor(np.array([[10, 20], [40, 50]])))
print('----------------------------------------')
# dtype 指定类型创建张量
t1 = torch.tensor([[10, 20], [40, 50]], dtype=torch.float)
print(t1, t1.dtype)
# TODO 注意: 浮点数转为整数会丢失精度,自动向下取整
t1 = torch.tensor([[10.9, 20.9], [40.9, 50.9]], dtype=torch.int)
Tensor创建:可以传入数据可以指定形状
# TODO 2.Tensor(data,size)创建张量
print(torch.Tensor(5)) # 指定形状
print(torch.Tensor(2, 3)) # 指定形状
print(torch.Tensor(size=(2, 3))) # 指定形状
print(torch.Tensor([10]).ndim)
print(torch.Tensor([[10]]).ndim)
print(torch.Tensor([[[10]]]).ndim)
# 通过列表创建张量
print(torch.Tensor([[10, 20], [40, 50]]), torch.tensor([[10, 20], [40, 50]]).dtype)
# 通过numpy创建张量
print(torch.Tensor(np.array([[10, 20], [40, 50]])))
使用IntTensor/FloatTensor指定类型创建
# TODO 3.IntTensor/FloatTensor创建张量
print(torch.ShortTensor([[10, 20]]))
print(torch.IntTensor([[10, 20]]))
print(torch.LongTensor([[10, 20]]), torch.LongTensor([[10, 20]]).dtype)
print(torch.HalfTensor([[10, 20]]))
print(torch.FloatTensor([[10, 20]]), torch.FloatTensor([[10, 20]]).dtype)
print(torch.DoubleTensor([[10, 20]]))
张量的线性和随机创建
使用arange,linspace,创建线性张量,使用rand,randn,randint创建随机或整数张量
import torch
# TODO 1.创建线性张量
# arange() 包左不包右
print(torch.arange(10))
print(torch.arange(2, 10))
print(torch.arange(2, 10, 2))
print('-------------------------------------')
# linspace() 包左包右
print(torch.linspace(2, 10, 5))
print(torch.linspace(2, 10, 6))
print('==================================================')
# TODO 2.创建随机张量
print(torch.rand(2, 3)) # rand随机生成0-1的浮点数张量
print(torch.randn(2, 3)) # randn随机生成正态分布的浮点数张量
print(torch.randint(10, (2, 3))) # randint随机生成整数张量
print(torch.randint(10, 20, (2, 3)))
使用manual_seed设置种子,使用initial_seed查看种子
# TODO 随机种子的设置和获取
# manual_seed() : 设置随机种子,保证随机数生成一致
print(torch.manual_seed(666))
print(torch.rand(2, 3))
print(torch.randn(2, 3))
print(torch.randint(10, (2, 3)))
print(torch.randint(10, 20, (2, 3)))
# initial_seed(): 获取当前随机种子
print(torch.initial_seed())
张量的全0/1/指定值(自动识别整数浮点数)创建
张量的like相似形状创建
import torch
# TODO 1.创建全0张量
# zeros(size) 创建全0张量
print(torch.zeros(2, 3))
print(torch.zeros(4, 5))
# zeros_like(张量) 模仿指定张量的形状创建全0张量
# 提前创建一个形状为(2, 3)的张量
x = torch.randint(0, 10, (3, 3))
print(torch.zeros_like(x))
print('---------------------------------------')
# TODO 2.创建全1张量
# ones(size) 创建全1张量
print(torch.ones(2, 3))
print(torch.ones(4, 5))
# ones_like(张量) 模仿指定张量的形状创建全1张量
# 提前创建一个形状为(2, 3)的张量
x = torch.randint(0, 10, (3, 3))
print(torch.ones_like(x))
print('---------------------------------------')
# TODO 3.创建全指定值张量
# full(size,value) 创建全1张量
print(torch.full((2, 3), 8.6))
print(torch.full((4, 5), 6))
# full_like(张量) 模仿指定张量的形状创建全指定值张量
# 提前创建一个形状为(2, 3)的张量
x = torch.randint(0, 10, (3, 3))
print(torch.full_like(x, 9))
张量的数据类型转换
使用类型函数转换数据类型和数据大小,如short int long half double float...
import torch
# 创建一个张量,然后数据类型转换
data = torch.randint(0, 10, [2, 5])
print(data.dtype)
# TODO 张量.类型函数()
print(data.short())
print(data.int())
print(data.long())
print(data.half())
print(data.float())
print(data.double())
print('==========================================')
使用.type()指定要转换的数据类型
# 类型转换方式1 torch.小写类型名
print(data.type(torch.short))
print(data.type(torch.int))
print(data.type(torch.long), data.type(torch.long).dtype)
print(data.type(torch.half))
print(data.type(torch.float), data.type(torch.float).dtype)
print(data.type(torch.double))
print('--------------------------')
# 类型转换方式2 torch.int位数/torch.float位数
print(data.type(torch.int16))
print(data.type(torch.int32))
print(data.type(torch.int64))
print(data.type(torch.float16))
print(data.type(torch.float32))
print(data.type(torch.float64))
print('--------------------------')
# 类型转换方式3 torch.大写类型Tensor 有警告
print(data.type(torch.ShortTensor))
print(data.type(torch.IntTensor))
print(data.type(torch.LongTensor))
print(data.type(torch.HalfTensor))
print(data.type(torch.FloatTensor))
print(data.type(torch.DoubleTensor))
张量和numpy互转,使用.tensor和.numpy互转,使用copy来避免共享内存(支持任意维度互转)
import numpy as np
import torch
# TODO 1.numpy数组转换为张量
# todo from_numpy()将numpy数组转换为张量,但是共享内存
# 创建numpy数组
n1 = np.array([1, 2, 3])
t1 = torch.from_numpy(n1)
print(n1, type(n1))
print(t1, type(t1))
# 演示from_numpy()结果共享内存
n1[0] = 100
print(n1, id(n1))
print(t1, id(t1))
print('------------------------------')
# todo torch.tensor(ndarray)将numpy数组转换为张量,不会共享内存
# 创建numpy数组
n2 = np.array([1, 2, 3])
t2 = torch.tensor(n2)
print(n2, type(n2))
print(t2, type(t2))
# 演示tensor(ndarray)结果不共享内存
n2[0] = 200
print(n2, id(n2))
print(t2, id(t2))
print('========================================================')
# TODO 2.张量转换为numpy数组
# todo numpy()将张量转换为numpy数组
# 创建张量
t3 = torch.tensor([1, 2, 3])
# 转换
n3 = t3.numpy()
print(t3, type(t3))
print(n3, type(n3))
# 演示numpy()结果共享内存
t3[0] = 300
print(t3, id(t3))
print(n3, id(n3))
print('------------------------------')
# todo numpy().copy()将张量转换为numpy数组
# 创建张量
t4 = torch.tensor([1, 2, 3])
# 转换
n4 = t4.numpy().copy()
print(t4, type(t4))
print(n4, type(n4))
# 演示numpy().copy()结果不共享内存
t4[0] = 300
print(t4, id(t4))
print(n4, id(n4))
张量和标量(单个数据,无方向)互转
import torch
# TODO 标量转张量
a1 = 10
t1 = torch.tensor(a1)
print(a1, type(a1))
print(t1, type(t1))
print('------------------')
# TODO 张量转标量
a2 = t1.item()
print(t1, type(t1))
print(a2, type(a2))
张量的基本运算(加减乘除负号) 不包括点乘
import torch
# TODO 张量的加减乘除负号基本运算
t1 = torch.tensor([[1, 2], [3, 4]])
print(t1 + 2)
print(t1 - 2)
print(t1 * 2)
print(t1 / 2)
print('----------------------')
print(torch.add(t1, 2))
print(torch.sub(t1, 2))
print(torch.mul(t1, 2))
print(torch.div(t1, 2))
print(torch.neg(t1))
print('----------------------')
# TODO add_, sub_, mul_, div_, neg_直接修改原有张量,但是类型不能变更
print(t1)
t1.add_(2)
print(t1)
t1.sub_(2)
print(t1)
t1.mul_(2)
print(t1)
# t1.div_(2) # 注意:除以2后结果是浮点类型,原来数据是整数类型,此行报错! 因为不能直接变更原有张量元素类型
t1.neg_()
print(t1)
张量的点乘使用torch.matmul或@(.dot(a,b)只能用于一维张量)
import numpy as np
import torch
# 矩阵的乘法运算 已知A(n,m)和B(m,p)
# 如果A列=B行, 则A和B可以相乘. 结果是C(n,p)
# 创建张量
A = torch.tensor([[1, 2], [3, 4], [5, 6]])
B = torch.tensor([[5, 6], [7, 8]])
# TODO 张量(矩阵)乘法运算 @和matmul
# 方式1: @
print(A @ B)
print('-----------------------')
# 方式2: torch.matmul
print(torch.matmul(A, B))
print('-----------------------')
# TODO 张量中有dot()函数,但是只能用于一维张量!!!
# C3 = torch.dot(A, B) # todo 报错,因为torch中dot只支持1维!!!
print(torch.dot(A[0], B[0]))
print('=================================================')
# TODO 回顾numpy的矩阵乘法运算
AA = np.array([[1, 2], [3, 4], [5, 6]])
BB = np.array([[5, 6], [7, 8]])
print(AA @ BB)
print('-----------------------')
print(np.matmul(AA, BB))
print('-----------------------')
print(np.dot(AA, BB)) # todo numpy中dot可以用于2维!!!
print('-----------------------')
print(np.dot(AA[0], BB[0])) # numpy中dot可以用于1维!!!
张量的其他运算,按行列求和,按行列求平均,求最大值,求最小值,求n次方,求平方根,求对数,求e的指数
# 张量的其他运算函数
import torch
# 设置随机种子(方便使用统一数据)
torch.manual_seed(666)
# 创建张量
t = torch.randint(1, 10, (2, 3), dtype=torch.float64)
print(t)
print('----------------------------------------------')
# TODO 演示sum()/mean()/max()/min()/pow()/sqrt()/log()/log2()/log10()/exp()
print(t.sum())
print(t.sum(dim=0)) # dim=0,按列求和
print(t.sum(dim=1)) # dim=1 按行求和
print('----------------------------------------------')
print(t.mean())
print(t.mean(dim=0)) # dim=0 按列求均值
print(t.mean(dim=1)) # dim=1 按行求均值
print('----------------------------------------------')
print(t.max()) # 打印t的最大值
print('----------------------------------------------')
print(t.min()) # 打印t的最小值
print('----------------------------------------------')
print(t.pow(2)) # 打印t的平方
print(t.pow(3)) # 打印t的立方
print('----------------------------------------------')
print(t.sqrt()) # 打印t的平方根
print(t.pow(0.5))
print('----------------------------------------------')
print(t.log()) # 打印t的自然对数
print(t.log2()) # 打印t的以2为底的对数
print(t.log10()) # 打印t的以10为底的对数
print('----------------------------------------------')
print(t.exp()) # 打印t的指数函数结果:以e为底,tensor中每个元素的指数函数结果
print('----------------------------------------------')
张量的索引获取数据,单个索引,列表索引,切片索引,使用布尔列表获取 列表索引与numpy有点区别
import torch
# 提前设置随机数种子,保证数据统一
torch.manual_seed(666)
# 创建张量
data = torch.randint(0, 10, (4, 5))
print(data)
print('======================================')
# TODO 演示索引获取数据格式为: 张量[行,列]
# TODO 注意: 行列表现形式:单个 列表 切片 布尔索引
# TODO 1.演示单个索引获取数据
# 需求: 获取张量中第2行数据
print(data[1, :])
print(data[1,])
print(data[1])
print('-------------------------------')
# 需求: 获取张量中第2列数据
print(data[:, 1])
print('======================================')
# TODO 2.演示列表指定多个索引获取数据
# 需求: 获取张量中第1行和第3行数据
print(data[[0, 2], :])
print('-------------------------------')
# 需求: 获取张量中第1列和第3列数据
print(data[:, [0, 2]])
print('-------------------------------')
# 需求: 获取张量中第1行和第3行数据中第1列和第3列数据
# TODO 注意: 如果用以下方式的话:它是自动把找的(0,0)(2,2)两个位置的数据
print(data[[0, 2], [0, 2]])
# TODO 如果要真正拿到符合当前需求的数据,需要用列表嵌套的方式
print(data[[[0], [2]], [0, 2]])
print('======================================')
# TODO 3.演示切片获取数据
# 需求: 获取张量中第1行到第3行数据
print(data[0:3, :])
print(data[:3, :])
print('-------------------------------')
# 需求: 获取张量中第1列到第3列数据
print(data[:, 0:3])
print(data[:, :3])
print('======================================')
# TODO 4.演示布尔索引
# 需求: 获取张量中数据大于5的数据
print(data[data > 5])
print('-------------------------------')
# 需求: 判断第1行数据是否大于5
print(data[0] > 5) # tensor([False, False, True, True, False])
# 需求: 先判断第1行是否大于5,根据布尔索引获取数据
print(data[:, torch.tensor([False, False, True, True, False])])
print(data[:, data[0] > 5])
print('-------------------------------')
# 需求: 判断第1列数据是否大于3
print(data[:, 0] > 3) # tensor([False, False, True, False])
# 需求: 先判断第1列是否大于3,根据布尔索引获取数据
print(data[torch.tensor([False, False, True, False]), :])
print(data[data[:, 0] > 3, :])
张量的多维索引
import torch
# 提前设置种子
torch.manual_seed(666)
# 创建三位张量
data = torch.randint(1, 10, (3, 4, 5))
print(data)
print(data[:, :, :])
# 格式: data[0轴索引,1轴索引,2轴索引]
print('----------------------------------')
# 获取0轴上的第一个数据
print(data[0, :, :])
print(data[0])
print('----------------------------------')
# 获取1轴上的第一个数据
print(data[:, 0, :])
print('----------------------------------')
# 获取2轴上的第一个数据
print(data[:, :, 0])
搭配魔方更好看,

输出结果

张量的形状获取和修改形状,升维和降维,reshape squeeze view
import torch
# 创建张量
t = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(t)
print('===================================')
# TODO shape获取形状
print(t.shape, t.shape[0], t.shape[1], t.shape[-1])
print(t.size(), t.size()[0], t.size()[1], t.size()[-1])
print('===================================')
# TODO reshape修改形状(元素个数不会变化)
print(t.reshape(3, 2))
print('--------------------')
print(t.reshape(1, 6))
print('--------------------')
print(t.reshape(6, 1))
print('--------------------')
# print(t.reshape(2, 2)) # 个数不匹配就报错!!!
# 创建张量
t = torch.tensor([1, 2, 3, 4, 5, 6])
print(t, t.shape, t.ndim)
print('==============================================')
# TODO 先使用reshape()变向完成升维操作,可以操作连续喝非连续的张量
t2 = t.reshape(2, 3)
print(t2, t2.shape, t2.ndim)
t3 = t.reshape(1, 2, 3)
print(t3, t3.shape, t3.ndim)
print('----------------------------')
# TODO 再使用专业的升维操作: unsqueeze()
t4 = t.unsqueeze(dim=0) # (1,6)
print(t4, t4.shape, t4.ndim)
t5 = t.unsqueeze(dim=1) # (6,1)
print(t5, t5.shape, t5.ndim)
# tt = t.unsqueeze(dim=2) # 报错: 维度超出范围(预期应在[-2, 1]范围内,但得到的是 2)
tt = t.unsqueeze(dim=-1)
print(tt, tt.shape, tt.ndim)
tt = t.unsqueeze(dim=-2)
print(tt, tt.shape, tt.ndim)
print('==============================================')
# TODO 使用专业的降维操作: squeeze()
t6 = t4.squeeze()
print(t6, t6.shape, t6.ndim)
t7 = t5.squeeze()
print(t7, t7.shape, t7.ndim)
# 创建张量
t = torch.tensor([1, 2, 3, 4, 5, 6])
print(t, t.shape, t.ndim)
print('==============================================')
# TODO 其他升维操作
# todo 1 使用类似reshape()的view()函数,不同点事view()只能操作连续的张量
t2 = t.view(2, 3)
print(t2, t2.shape, t2.ndim)
t3 = t.view(1, 2, 3)
print(t3, t3.shape, t3.ndim)
print('==============================================')
# todo 2 利用索引
t4 = t[None, :]
print(t4, t4.shape, t4.ndim)
t5 = t[:, None]
print(t5, t5.shape, t5.ndim)
t6 = t[None, :, None]
print(t6, t6.shape, t6.ndim)
print('==============================================')
print(t6, t6.shape, t6.ndim)
# TODO 注意: squeeze()默认删除所有1维,完成降维操作
t7 = t6.squeeze()
print(t7, t7.shape, t7.ndim)
# TODO 创建一个高维的张量,然后完成降维操作
t8 = torch.randint(1, 5, size=(1, 2, 1, 1, 5, 1, 3))
print(t8.shape,t8.ndim)
t8.squeeze_()
print(t8.shape,t8.ndim)
张量的维度交换操作 transpose与permute
transpose一次只能交换两个,permute可以随意排列维度
import torch
# 提前设置一个种子
torch.manual_seed(666)
# 创建三维张量
t = torch.randint(1, 5, (3, 4, 5))
print(t, t.shape)
# TODO 需求: 把张量形状(3,4,5)转变为(4,5,3)
print('=====================================================')
# TODO transpose方式: 交换多次
t2 = t.transpose(1, 0) # (3,4,5)->(4,3,5)
print(t2, t2.shape)
print('------------------------------------------------')
t3 = t2.transpose(2, 1) # (4,3,5)->(4,5,3)
print(t3, t3.shape)
print('=====================================================')
# TODO permute方式: 一次指定多个维度
t4 = t.permute(dims=(1, 2, 0)) # (3,4,5)->(4,5,3)
print(t4, t4.shape)
# 注意: 还可以直接使用torch调用transpose()和permute()
张量的内存地址是否连续及修改操作,使用is_contiguous判断地址是否连续
import torch
# 创建张量
t1 = torch.tensor([[10, 20, 30], [40, 50, 60]])
print(t1, t1.shape, t1.ndim)
print('-----------------------------')
t2 = t1.transpose(1, 0)
print(t2, t2.shape, t2.ndim)
print('========================================')
# TODO is_contiguous()判断张量是否连续
# 判断t1是否连续
print(t1.is_contiguous())
# 判断t2是否连续
print(t2.is_contiguous())
print('========================================')
# TODO 演示reshape和view的区别:
# todo 1.reshape连续和不连续张量都能操作,以后工作中常用reshape()
print(t1.reshape(1, 6))
print(t2.reshape(1, 6))
print('--------------------------------')
# todo 2.view只能操作连续张量,如果是不连续的怎么办? 使用contiguous()
print(t1.view(1, 6))
# print(t2.view(1, 6)) # 报错,因为t2是不连续的
# TODO 使用contiguous()把数据变为连续的,然后使用view()
t3 = t2.contiguous()
print(t3.is_contiguous())
print(t3.view(1, 6))
张量的拼接操作,cat拼接时,除了拼接维度外,其他维度必须相同,stack拼接所有维度都必须相同
import torch
# 提前设置一个随机种子
torch.manual_seed(666)
# TODO cat()拼接三维案例:对应维度上的维数相加
# 创建张量
t1 = torch.randint(1, 5, (1, 2, 3))
t2 = torch.randint(1, 5, (1, 2, 3))
print('----------------------------')
# 拼接
print(torch.cat([t1, t2], dim=0)) # (2, 2, 3)
print('----------------------------')
# 拼接
print(torch.cat([t1, t2], dim=1)) # (1, 4, 3)
print('----------------------------')
# 拼接
print(torch.cat([t1, t2], dim=2)) # (1, 2, 6)
print('=========================================================')
# TODO cat()拼接的是除拼接维度外,其他所有张量的形状必须完全相同。
t1 = torch.randint(1, 5, (2, 3))
t2 = torch.randint(1, 5, (1, 3))
# 拼接0轴上
print(torch.cat([t1, t2], dim=0)) # (3, 3)
print('=========================================================')
# TODO stack()拼接的是所有张量的形状必须完全相同(所有维度一致)。
t1 = torch.randint(1, 5, (2, 3))
t2 = torch.randint(1, 5, (2, 3))
print(torch.stack([t1, t2], dim=0)) # (2,2,3)
print(torch.stack([t1, t2], dim=1)) # (2,2,3)
print(torch.stack([t1, t2], dim=2)) # (2,3,2)
使用pytorch自动微分
# 导包
import torch
# 准备x训练数据
x = torch.ones(2, 5)
print(x)
# 准备y训练数据
y = torch.zeros(2, 3)
print(y)
# 准备w权重矩阵,开启自动微分!!!
w = torch.randn(5, 3, requires_grad=True)
print(w)
# 准备b偏置矩阵,开启自动微分!!!
b = torch.randn(3, requires_grad=True)
print(b)
print('=========================================')
# TODO 最终目的根据上述数据,使用自动微分推导w和b的梯度
# 1.首先获取损失函数
loss_fn = torch.nn.MSELoss()
# 2.然后,计算预测值-> z=wx+b 注意: 这里面的wx是矩阵乘法需要遵循 (n,m)*(m,p)=(n,p)
z = x.matmul(w) + b
# 3.接着,根据损失函数计算损失值
loss = loss_fn(z, y)
# 4.最后,反向传播推导更新梯度
loss.sum().backward()
# TODO 打印更新后w和b梯度
print(f'w.grad: {w.grad}')
print(f'b.grad: {b.grad}')
案例:pytorch实现回归模型
步骤:导包,创建数据,数据分批,创建模型,创建损失函数对象用于计算损失值,创建SGD优化器对象更新后续模型参数,训练模型并计算预测值和损失值,使用梯度下降逐渐减少损失值,最后得出权重w与偏差值b.
详细可以回看线性回归(不知道更没更)

# 导包
from sklearn.datasets import make_regression
import torch
from torch.utils.data import DataLoader, TensorDataset
from torch.nn import Linear, MSELoss
from torch.optim import SGD
import matplotlib.pyplot as plt
# 解决中文乱码问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# TODO 1.获取数据集,提前封装数据为张量
def get_data():
X, y, coef = make_regression(n_samples=100, # 样本数量
n_features=1, # 特征数量
n_targets=1, # 目标数量
noise=10, # 噪声,目的是让数据离散,模拟真实数据
random_state=1, # 随机数种子,保证每次运行结果一致
coef=True,
bias=14.5,
)
# 提前把x,y转换为张量
X = torch.tensor(X, dtype=torch.float32)
y = torch.tensor(y, dtype=torch.float32)
return X, y, coef
# TODO 2.模型训练预测以及计算损失值
def get_model_and_loss_list(x, y, epochs):
# todo 1.处理数据封装为数据加载器: 为了后续分批次训练,避免内存溢出
dataloader = DataLoader(TensorDataset(x, y), batch_size=5, shuffle=True)
# todo 2.创建模型对象: 为了后续模型预测
model = Linear(in_features=1, out_features=1)
# todo 3.创建损失函数对象: 为了后续计算损失值
loss_fn = MSELoss()
# todo 4.创建SGD优化器对象: 为了后续模型参数的更新
optimizer = SGD(model.parameters(), lr=0.01)
# todo 5.模型训练预测以及计算损失值[重点]
# 5.1 提前定义一个列表,用于保存每轮损失值,还需要定义累加损失和批次数用于计算每轮损失值
loss_list, total_loss, batch_cnt = [], 0.0, 0
# 5.2 TODO 开始训练 for循环嵌套:外层轮次,内层批次
for epoch in range(epochs):
for batch_x, batch_y in dataloader:
# TODO 正(前)向传播:从输入到输出: 计算预测值和损失值
# 5.3 计算预测值.
y_pred = model(batch_x)
# 5.4 计算损失值. todo MSE要求形状必须一致,否则自动广播,计算出无效的损失值
loss = loss_fn(y_pred, batch_y.reshape(-1, 1))
# 5.5 累加损失和批次数用于计算每轮损失值.
total_loss += loss.item()
batch_cnt += 1
# TODO 反向传播:从输出到输入: 梯度计算和参数更新
# 5.6 梯度清零!!!
optimizer.zero_grad()
# 5.7 反向传播:梯度计算
loss.backward()
# 5.8 参数更新
optimizer.step() # 底层就是w1 = w0-lr*梯度
# TODO 内层循环结束,就是一轮结束,可以计算每轮损失值,并保存到列表中
epoch_loss = total_loss / batch_cnt
loss_list.append(epoch_loss)
print(f"第{epoch + 1}轮,损失值为:{epoch_loss:.2f}")
# TODO 外层循环结束,就是所有轮次结束,可以返回模型和损失值列表
# 打印模型参数字典
print(f"模型参数:{model.state_dict()}")
# print(f"模型权重:{model.weight}, 偏置:{model.bias}")
# 返回结果
return model, loss_list
# TODO 3.绘制损失曲线以及预测和真实值拟合线
def plot_show(epochs, model, loss_list, x, y, coef):
# todo 1.绘制每轮损失曲线
plt.plot(range(epochs), loss_list)
plt.xlabel('训练的轮数')
plt.ylabel('该轮的平均损失')
plt.title('损失变化曲线图')
plt.grid()
plt.show()
# todo 2.绘制预测和真实值拟合线
plt.scatter(x, y)
# 使用x的最小值,最大值生成1000个x值等差张量
X = torch.linspace(x.min(), x.max(), 1000)
# 细节: numpy * 张量 -> 不可以.张量 * numpy -> 可以, 要么把v放到前面位置要么把coef转为张量
y_pred = torch.tensor([v * model.weight + model.bias for v in X])
y_true = torch.tensor([v * coef + 14.5 for v in X])
plt.plot(X, y_pred, label='训练', color='red')
plt.plot(X, y_true, label='真实', color='green')
plt.legend()
plt.grid()
plt.show()
# 程序的主入口
if __name__ == '__main__':
# TODO 1.获取数据集,提前封装数据为张量
x, y, coef = get_data()
# TODO 2.模型训练预测以及计算损失值
epochs = 160
model, loss_list = get_model_and_loss_list(x, y, epochs)
# TODO 3.绘制损失曲线以及预测和真实值拟合线
plot_show(epochs, model, loss_list, x, y, coef)
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)