作者️♂️:让机器理解语言か
专栏:PyTorch
描述:PyTorch 是一个基于 Torch 的 Python 开源机器学习库。
寄语:没有白走的路,每一步都算数!
在上一节实验中,我们初步完成了梯度下降算法求解线性回归问题的实例。在这个过程中,我们自己定义了损失函数和权重的更新,其实 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)
pre
# tensor([0., 0., 0., 0.], grad_fn=)
接下来,让我们通过 nn.MSELoss()
计算此时预测值和真实值之间的损失:
# 这里使用均方差损失计算预测值和真实值之间的距离
loss = nn.MSELoss()
# 测试此时的损失
loss(forward(X), Y)
# tensor(30., grad_fn=)
优化器可以理解为一种利用梯度下降算法自动求解所需参数的工具包。在 PyTorch 中提供了 torch.optim
方法优化我们的模型。 torch.optim
工具包中存在着各种梯度下降的改进算法,比如 SGD、Momentum、RMSProp 和 Adam 等。这些算法都是以传统梯度下降算法为基础,提出的改进算法,这些算法可以更快更准确地求解最佳模型参数。
我们可以通过下面方式定义一个 SGD 优化器:
optimizer = torch.optim.SGD([w], lr=learning_rate)
由于一般的模型都是复杂的多元函数,每次使用梯度下降算法时,我们都需要手动的对每个变量进行更新,这无疑是非常繁琐的。而使用优化器,我们可以一次性对所有的变量进行更新。函数如下:
optimizer.step()
:对模型(神经网络)中的参数进行更新,即所有参数值向梯度相反方向走一步。optimizer.zero_grad()
:对损失函数的相关变量进行梯度的清空。综上,让我们完整的进行一次线性回归的求解。
首先,定义损失函数和优化器:
# 定义损失和优化器
learning_rate = 0.01
n_iters = 100
loss = nn.MSELoss()
optimizer = torch.optim.SGD([w], lr=learning_rate)
optimizer
接下来,根据正向传播结果,更新梯度,进而更新权重值:
# 模型的训练过程
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}')
这里我们进行了 100 次的迭代,可以发现得到的权重 w 和实际值相同,损失无限接近于 0 。
除了梯度的求解、权重的更新和梯度的清空外,PyTorch 实际上还为我们提供了模型的定义。也就是说,我们不用手动定义 forward
函数了。PyTorch 中为我们提供了预定义模型,可以直接使用。如 torch.nn.Linear(input_size, output_size)
表示线性函数模型。
总结一下,我们可以将一个线性问题的求解分为下面三个步骤:
首先,让我们利用 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)
在模型训练时,我们可以直接利用 model(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
最后,我们就可以利用上面定义的模型、优化器和损失函数进行模型的训练了(即利用梯度下降算法,求解损失最小时的权重值):
# 3. 模型的训练,固定的步骤:正向传播、计算损失、反向传播、更新权重、梯度清空
for epoch in range(n_iters):
# 正向传播
y_predicted = model(X)
# 损失
l = loss(Y, y_predicted) # 将真实值和预测值传入loss,计算损失
# 反向传播
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))
可以看到其实求解一个线性问题的步骤是固定的:
nn.Linear
定义模型。nn.MSELoss
定义损失。torch.optim
定义优化器。并且模型的训练步骤也是固定的:
- 利用
model(X)
进行正向传播。- 利用
loss(Y, y_predicted)
计算模型损失。- 利用
loss.backward()
计算模型梯度。- 利用
optimizer.step()
更新权重。- 利用
optimizer.zero_grad()
清空梯度。- 重复 1-5 的操作。
因此,使用 PyTorch 可以大大的简化我们的编程难度。我们只需要改变模型的形式、损失函数的形式、优化器的形式以及各个参数的值,就能够训练出不同的模型,进而解决不同的深度学习问题了。
本实验详细的阐述了如何使用 PyTorch 对模型进行求解。这个过程既可以适用于传统机器学习问题的求解,也可以适用于神经网络的模型求解。