Pytorch中提供了很多的工具包,让我们能够快速的实现损失函数,权重更新,以及梯度的求解.
模型训练
损失函数
在上一节,我们自己手工定义了损失函数来对线性问题进行求解. 其实在torch.nn中存在很多封装好的损失函数.比如均方差损失,用torch.nn.MSELoss()表示
下面我们通过例子来学习它吧!
import torch
import torch.nn as nn
# 初始化数据集
X = torch.tensor([1,2,3,4],dtype=torch.float32)
Y = torch.tensor([2,4,6,8],dtype=torch.float32)
w = torch.tensor(0.0, dtype=torch.float32, requires_grad=True)
def forward(x):
return w*x
# 测试代码
pre = forward(X)
print(pre)
接下来, 让我们通过nn.MSELoss()计算此时预测值和真实值之间的损失:
# 均方差计算预测值和真实值之间的距离
loss = torch.nn.MSELoss()
# 计算此时的损失
y_pre = forward(X)
l = loss(y_pre,Y)
print(f"此时的损失:{l}")
优化器
优化器可以理解为一种利用梯度下降算法自动求解所需要参数的工具包.在PyTorch中提供了torch.optim方法优化我们的模型. torch.optim工具包中存在着各种梯度下降的改进算法,例如SGD, Adam,Momentum,RMSProp等.这些算法都是以传统梯度下降算法为基础,提供的改进算法,他们可以更快更准确的求解最佳模型参数.
我们可以通过下面方式定义一个SGD优化器
optimizer = torch.optim.SGD([W], lr=learning_rate)
其中第一个参数,表示的是损失函数中的权重,即我们需要求取的值.lr 表示的说梯度下降的步长.
由于一般的模型都说复杂的多元函数, 每次使用梯度下降算法时, 我们都需要手动的对每个变量进行更新, 这无疑是非常繁琐的. 而使用优化器,我们可以一次性对所有的变量进行更新. 函数如下:
- optimizer.step(): 对模型中的参数进行更新, 即所有的参数值向梯度相反方向走一步
- optimizer.zero_grad(): 对损失函数的相关变量进行梯度的清空
综上, 让我们完整的进行一次线性回归的求解
首先,我们先定义损失函数和优化器
# 定义损失和优化器
learning_rate = 0.01
n_iters = 100
loss = nn.MSELoss()
optimizer = torch.optim.SGD([W], lr=learning_rate)
print(optimizer)
接下来, 根据正向传播结果, 更新梯度,进而更新权重值
# 模型训练过程
for epoch in range(n_iters):
y_pre = forward(X)
# 计算损失
l = loss(Y, y_pre)
# 计算梯度
l.backward()
# 更新权重,即向梯度方向走一步
optimizer.step()
# 清空梯度
optimizer.zero_grad()
if epoch%10 == 0:
print(f"epoch:{epoch}, w={w},loss={l:.8f}")
这里我们进行100次迭代, 可以发现得到的权重w和实际值相同, 损失无限接近于0
模型的建立
除了梯度的求解, 权重的更新和梯度的清空外, PyTorch实际上还为我们提供了模型的定义. 也就是说, 我们不用手动定义forward函数.Pytorch已经为我们提供了预定义的模型,可以直接使用
例如torch.nn.Linear(input_size,output_size)
表示线性函数模型.
- input_size: 输入数据的维度
- output_size: 输出数据的维度
总结一下,我们可以将一个线性问题的求解分为三个步骤:
- 定义模型,即正向传播函数,用于计算预测值的
- 定义损失和优化器
- 模型的训练,即正向传播,反向传播,更新梯度,梯度下降,循环.
首先, 让我们利用PyTorch定义的线性函数模型:
X = torch.tensor([[1],[2],[3],[4]], dtype=torch.float32)
Y = torch.tensor([[2],[4],[6],[8]], dtype=torch.float32)
X_test = torch.tensor([5],dtype=torch.float32)
# 定义模型参数
n_samples,n_features = X.shape
# 目前定义的是输入,输出维度一致
model = nn.Linear(n_features,n_features)
在模型训练时, 我们可以直接利用model(x)作为模型的正向传播,该函数返回数据x的预测结果
接下来, 让我们定义优化器和损失函数:
# 定义优化器和损失函数
learning_rate = 0.1
n_iters = 100
loss = nn.MSELoss()
# 定义优化器
optimizer = torch.optim.SGD(model.parameters(),lr=learning_rate)
最后, 我们就可以利用上面定义的模型, 优化器和损失函数进行模型的训练了,即利用梯度下降算法,求解损失最小时的权重值
for epoch in range(n_iters):
# 正向传播
y_pre = model(X)
# 损失
l = loss(Y, y_pre)
# 反向传播
l.backward()
# 更新权重
optimizer.step()
# 清空梯度
optimizer.zero_grad()
if epoch%10 == 0:
w,b = model.parameters()
print(f'epoch:{epoch},w={w[0][0].item()},loss={l}')
可以看到其实模型的训练步骤是固定的:
- 利用nn.Linear定义模型
- 利用nn.MSELoss定义损失
- 利用torch.optim定义优化器
- 利用梯度下降算法进行模型的训练
具体代码步骤如下:
- 利用model(X) 进行正向传播
- 利用loss(Y, y_pre)计算模型的损失
- 利用loss.backward() 计算模型的梯度
- 利用optimizer.step() 更新权重
- 利用optimizer.zero_grad() 清空梯度
- 重复1-5的操作
因此,使用PyTorch可以极大的简化我们编程的难度.我们只需要改变模型的形式,损失函数的形式,优化器的形式以及各个参数的值,就能够训练出不同的模型,进而解决不同的深度学习问题了
小结
本节主要阐述了如何使用PyTorch对模型进行求解.这个过程既适用于传统机器学习的求解, 也适用于神经网络的模型求解