在前文,我们初步完成了梯度下降算法求解线性回归问题的实例。在这个过程中,我们自己定义了损失函数和权重的更新,其实 PyTorch 也为我们直接定义了相应的工具包,使我们能够简洁快速的实现损失函数、权重的更新和梯度的求解。
损失函数与优化器
import torch
import torch.nn as nn
一、模型的内置函数
1.损失函数 torch.nn.MSELoss()
在Pytorch中的torch.nn
中封装了很多损失函数,避免了我们手动定义损失函数,如均方差损失:torch.nn.MSELoss()
。
首先,我们把需要的变量和数据集定义出来:
#初始化数据集
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
#测试代码:w是0,前向推导的结果也应该是0
pre=forward(X)
pre
输出结果如下:
tensor([0., 0., 0., 0.], grad_fn=<MulBackward0>)
接下来,让我们通过 nn.MSELoss()
计算此时预测值和真实值之间的损失:
loss=nn.MSELoss()
loss(forward(X),Y)
输出结果如下:
tensor(30., grad_fn=<MseLossBackward0>)
2.优化器 torch.optim
优化器可以理解为一种利用梯度下降算法自动求解所需参数的工具包。在 PyTorch 中提供了 torch.optim
方法优化我们的模型。 torch.optim
工具包中存在着各种梯度下降的改进算法,比如 SGD
、Momentum
、RMSProp
和 Adam
等。这些算法都是以传统梯度下降算法为基础,提出的改进算法,这些算法可以更快更准确地求解最佳模型参数。
我们可以通过下面方式定义一个 SGD 优化器:
optimizer = torch.optim.SGD([w], lr=learning_rate)
其中第一个参数,表示的是损失函数中的权重,即我们需要求取的值。 l r lr lr 表示的是梯度下降的步长。
由于一般的模型都是复杂的多元函数,每次使用梯度下降算法时,我们都需要手动的对每个变量进行更新,这无疑是非常繁琐的。而使用优化器,我们可以一次性对所有的变量进行更新。函数如下:
optimizer.step()
:对模型(神经网络)中的参数进行更新,即所有参数值向梯度相反方向走一步。
optimizer.zero_grad()
:对损失函数的相关变量进行梯度的清空。
综上,让我们完整的进行一次线性回归的求解。
首先,定义损失函数和优化器:
# 定义损失和优化器
learning_rate = 0.01
n_iters = 100
loss = nn.MSELoss()
optimizer = torch.optim.SGD([w], lr=learning_rate)
optimizer
输出结果如下:
SGD (
Parameter Group 0
dampening: 0
lr: 0.01
momentum: 0
nesterov: False
weight_decay: 0
)
接下来,根据正向传播结果,更新梯度,进而更新权重值:
# 模型的训练过程
for epoch in range(n_iters):
y_predicted = forward(X)
# 计算损失
l = loss(Y, y_predicted)
# 计算梯度
l.backward()
# 更新权重,即向梯度方向走一步
optimizer.step()
# 清空梯度
optimizer.zero_grad()
if epoch % 10 == 0:
print('epoch ', epoch+1, ': w = ', w, ' loss = ', l)
print(f'根据训练模型预测,当 x =5 时,y 的值为: {forward(5):.3f}')
输出结果如下:
epoch 1 : w = tensor(0.3000, requires_grad=True) loss = tensor(30., grad_fn=<MeanBackward0>)
epoch 11 : w = tensor(1.6653, requires_grad=True) loss = tensor(1.1628, grad_fn=<MeanBackward0>)
epoch 21 : w = tensor(1.9341, requires_grad=True) loss = tensor(0.0451, grad_fn=<MeanBackward0>)
epoch 31 : w = tensor(1.9870, requires_grad=True) loss = tensor(0.0017, grad_fn=<MeanBackward0>)
epoch 41 : w = tensor(1.9974, requires_grad=True) loss = tensor(6.7705e-05, grad_fn=<MeanBackward0>)
epoch 51 : w = tensor(1.9995, requires_grad=True) loss = tensor(2.6244e-06, grad_fn=<MeanBackward0>)
epoch 61 : w = tensor(1.9999, requires_grad=True) loss = tensor(1.0176e-07, grad_fn=<MeanBackward0>)
epoch 71 : w = tensor(2.0000, requires_grad=True) loss = tensor(3.9742e-09, grad_fn=<MeanBackward0>)
epoch 81 : w = tensor(2.0000, requires_grad=True) loss = tensor(1.4670e-10, grad_fn=<MeanBackward0>)
epoch 91 : w = tensor(2.0000, requires_grad=True) loss = tensor(5.0768e-12, grad_fn=<MeanBackward0>)
根据训练模型预测,当 x =5 时,y 的值为: 10.000
这里我们进行了 100 次的迭代,可以发现得到的权重 ω \omega ω和实际值相同,损失无限接近于 0 。
二、模型的建立
除了梯度的求解、权重的更新和梯度的清空外,PyTorch 实际上还为我们提供了模型的定义。也就是说,我们不用手动定义 forward 函数了。PyTorch 中为我们提供了预定义模型,可以直接使用。如 torch.nn.Linear(input_size, output_size)
表示线性函数模型。
input_size:输入数据的维度
output_size:输出数据的维度
总结一下,我们可以将一个线性问题的求解分为下面三个步骤:
1.定义模型(即正向传播函数)。
2.定义损失和优化器。
3.模型的训练(正向传播、反向传播、更新梯度、梯度下降、循环)。
1.定义线性模型 Linear
首先,让我们利用 PyTorch 定义线性函数模型:
# 由于使用 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)
# 1. 定义模型
n_samples, n_features = X.shape
# 这里输入和输出的维度相同
model = nn.Linear(n_features, n_features)
model
输出结果如下:
Linear(in_features=1, out_features=1, bias=True)
2.定义优化器和损失函数
在模型训练时,我们可以直接利用 model(x)
作为模型的正向传播,该函数返回数据
x
x
x的预测结果。
# 2. 定义优化器和损失函数
learning_rate = 0.1
n_iters = 100
loss = nn.MSELoss()
# 在定义优化器时,直接利用 model.parameters() 表示模型中所有需要求的权重
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
optimizer
输出结果如下:
SGD (
Parameter Group 0
dampening: 0
lr: 0.1
momentum: 0
nesterov: False
weight_decay: 0
)
3.模型的训练
最后,我们就可以利用上面定义的模型、优化器和损失函数进行模型的训练了(即利用梯度下降算法,求解损失最小时的权重值):
# 3. 模型的训练,固定的步骤:正向传播、计算损失、反向传播、更新权重、梯度清空
for epoch in range(n_iters):
# 正向传播
y_predicted = model(X)
# 损失
l = loss(Y, y_predicted)
# 反向传播
l.backward()
# 更新权重
optimizer.step()
# 清空梯度
optimizer.zero_grad()
if epoch % 10 == 0:
[w, b] = model.parameters() # unpack parameters
print('epoch ', epoch+1, ': w = ', w[0][0].item(), ' loss = ', l)
print(f'根据训练模型预测,当 x =5 时,y 的值为:', forward(X_test))
输出结果如下:
epoch 1 : w = 2.7811810970306396 loss = tensor(16.4686, grad_fn=<MeanBackward0>)
epoch 11 : w = 1.9356615543365479 loss = tensor(0.0154, grad_fn=<MeanBackward0>)
epoch 21 : w = 1.940834641456604 loss = tensor(0.0054, grad_fn=<MeanBackward0>)
epoch 31 : w = 1.9561294317245483 loss = tensor(0.0030, grad_fn=<MeanBackward0>)
epoch 41 : w = 1.967624545097351 loss = tensor(0.0016, grad_fn=<MeanBackward0>)
epoch 51 : w = 1.9761104583740234 loss = tensor(0.0009, grad_fn=<MeanBackward0>)
epoch 61 : w = 1.9823722839355469 loss = tensor(0.0005, grad_fn=<MeanBackward0>)
epoch 71 : w = 1.9869927167892456 loss = tensor(0.0003, grad_fn=<MeanBackward0>)
epoch 81 : w = 1.990402102470398 loss = tensor(0.0001, grad_fn=<MeanBackward0>)
epoch 91 : w = 1.992917776107788 loss = tensor(7.6951e-05, grad_fn=<MeanBackward0>)
根据训练模型预测,当 x =5 时,y 的值为: tensor([[9.9731]], grad_fn=<MulBackward0>)
三、总结
模型训练的步骤是固定的:
1.利用 nn.Linear
定义模型。
2.利用 nn.MSELoss
定义损失。
3.利用 torch.optim
定义优化器。
4.利用梯度下降算法进行模型的训练。
模型的训练步骤也是固定的:
1.利用 model(X)
进行正向传播。
2.利用 loss(Y, y_predicted)
计算模型损失。
3.利用 loss.backward()
计算模型梯度。
4.利用 optimizer.step()
更新权重。
5.利用 optimizer.zero_grad()
清空梯度。
6.重复 1-5 的操作。
因此,使用 PyTorch 可以大大的简化我们的编程难度。我们只需要改变模型的形式、损失函数的形式、优化器的形式以及各个参数的值,就能够训练出不同的模型,进而解决不同的深度学习问题了。