4.1 PyTorch纯手工构建模型并训练

欢迎订阅本专栏:《PyTorch深度学习实践》
订阅地址:https://blog.csdn.net/sinat_33761963/category_9720080.html

  • 第二章:认识Tensor的类型、创建、存储、api等,打好Tensor的基础,是进行PyTorch深度学习实践的重中之重的基础。
  • 第三章:学习PyTorch如何读入各种外部数据
  • 第四章:利用PyTorch从头到尾创建、训练、评估一个模型,理解与熟悉PyTorch实现模型的每个步骤,用到的模块与方法。
  • 第五章:学习如何利用PyTorch提供的3种方法去创建各种模型结构。
  • 第六章:利用PyTorch实现简单与经典的模型全过程:简单二分类、手写字体识别、词向量的实现、自编码器实现。
  • 第七章利用PyTorch实现复杂模型:翻译机(nlp领域)、生成对抗网络(GAN)、强化学习(RL)、风格迁移(cv领域)。
  • 第八章:PyTorch的其他高级用法:模型在不同框架之间的迁移、可视化、多个GPU并行计算。

pytorch提供了很多高效的模块与函数能帮助我们迅速建模与训练,在介绍这些工具之前,有必要纯手工地来过一遍构建并训练模型的整个过程,从而清楚理解其内部逻辑和含义,绝不做一个单纯“调包侠”。

本节将构建一个一元线性回归模型,并利用梯度下降来估计参数。来看看具体怎么做吧~

4.1.1 前向计算

准备数据

一元线性回归模型是由自变量x,应变量y,参数w,b组成,因此我们先构造一组x,y的数据,假设它们是真实世界的数据,现在要通过这组数据去求得w,b参数的值,使得能通过x更贴切地预测y。

import torch

x = [0.5, 14.0, 15.0, 28.0, 11.0, 8.0, 3.0, -4.0, 6.0, 13.0, 21.0]
y = [35.7, 55.9, 58.2, 81.9, 56.3, 48.9, 33.9, 21.8, 48.4, 60.4, 68.4]

x = torch.tensor(x)
y = torch.tensor(y)

构建模型

一元线性模型方程为:y = wx + b

def model(x, w, b):
    return w*x + b

构建损失

模型预测值y_p与真实的y值的差距即为损失,一般使用绝对差值,或平方差值:

loss1 = |y_p - y|

loss2 = (y_p - y)^2 # 常用此种

所有样本的损失求均值即为总损失

def loss_fn(y_p, y):
    squared_diffs = (y_p - y) ** 2
    return squared_diffs.mean()

初始化参数

求解问题的本质是去求解参数w,b, 使得损失越小,那么先得随机初始化w,b。

下面设置w初始化值为1, b的初始化值为0,即初始的模型为:y = 1x + 0

w = torch.ones(1)
b = torch.zeros(1)

前向计算得到预测值与损失值

有了x,y,w,b,也准备好了模型函数,我们就可以求y_p, 从而求loss了。

y_p = model(x, w,b)
print(y_p)

loss = loss_fn(y_p, y)
print(loss)
tensor([ 0.5000, 14.0000, 15.0000, 28.0000, 11.0000,  8.0000,  3.0000, -4.0000,
         6.0000, 13.0000, 21.0000])
tensor(1763.8846)

4.2 反向计算

反向计算是指梯度下降更新参数的过程:求每个参数的梯度–>根据学习率更新参数值

(1)求w, b的梯度:

# loss = (y_p - y)^2, loss对(y_p-y)求导
def dloss_fn(y_p, y):
    dsq_diffs = 2 * (y_p - y)
    return dsq_diffs
    
# y_p = wx + b, y_p对w求导
def dmodel_dw(x, w, b):
    return x

# y_p 对b求导
def dmodel_db(x, w, b):
    return 1.0

# 求梯度
def grad_fn(x, y, y_p, w, b):
    dloss_dw = dloss_fn(y_p, y) * dmodel_dw(x, w, b)
    dloss_db = dloss_fn(y_p, y) * dmodel_db(x, w, b)
    
    return torch.stack([dloss_dw.mean(), dloss_db.mean()])  # 注意是所有样本梯度的均值

print(grad_fn(x, y, y_p, w, b))
    

tensor([-989.5273,  -82.6000])

(2)更新w, b梯度:

需要涉及的重要超参数是迭代次数、学习率:n_epoch, learning_rate

更新梯度的公式: w = w - leaning_rate * 损失函数对w的导数(同理b)

def training_loop(n_epochs, learning_rate, params, x, y):
    for epoch in range(1, n_epochs+1):
        w, b = params
        y_p = model(x, w, b)
        loss = loss_fn(y_p, y)
        grad = grad_fn(x, y, y_p, w, b)
        
        params = params - learning_rate * grad
        
        print('Epoch %d, Loss %f' % (epoch, float(loss)))
        
    return params
    
training_loop(
    n_epochs = 10,
    learning_rate = 1e-2,
    params = torch.tensor([w,b]),
    x = x,
    y = y
)
Epoch 1, Loss 1763.884644
Epoch 2, Loss 10424.541016
Epoch 3, Loss 76423.132812
Epoch 4, Loss 579046.562500
Epoch 5, Loss 4406547.000000
Epoch 6, Loss 33552814.000000
Epoch 7, Loss 255500272.000000
Epoch 8, Loss 1945619584.000000
Epoch 9, Loss 14815792128.000000
Epoch 10, Loss 112821534720.000000





tensor([-67495.0859,  -3787.7676])

以上是整个后向计算过程,通过迭代获取更新后的参数。如果是学习代码,那任务完成了,但如果是解决问题,却还没完成,因为我们最终的目的是得到有效的参数w,b,使得模型预测的结果能和真实的结果最接近,即损失最小。

但根据以上每个epoch打印的结果,随着迭代次数上升,损失大幅度上升,显然最终得到的w,b参数肯定有问题。

主要原因有2个:

  • 学习率设置不合适,过大的学习率会导致损失波动,过小的学习率会超慢收敛或陷入局部最优
  • 自变量与应变量的数据规模不一致,应缩小自变量的规模,可以乘以一个小的系数,或进行0均值化等

首先来缩小学习率看一看效果:

training_loop(
    n_epochs = 10,
    learning_rate = 1e-4,  # 注意这里,改变了学习率
    params = torch.tensor([w,b]),
    x = x,
    y = y
)
Epoch 1, Loss 1763.884644
Epoch 2, Loss 1667.137939
Epoch 3, Loss 1577.523560
Epoch 4, Loss 1494.515503
Epoch 5, Loss 1417.625854
Epoch 6, Loss 1346.403809
Epoch 7, Loss 1280.430786
Epoch 8, Loss 1219.319702
Epoch 9, Loss 1162.712036
Epoch 10, Loss 1110.275269





tensor([1.8376, 0.0741])

以上可见,缩小学习率后,损失的规模下降了。

再来看看调整自变量的规模:

x_u = 0.1 * x

training_loop(
    n_epochs = 10,
    learning_rate = 1e-2,  # 学习率不变
    params = torch.tensor([w,b]),
    x = x_u,  # 注意这里
    y = y
)
Epoch 1, Loss 2806.242188
Epoch 2, Loss 2534.330811
Epoch 3, Loss 2289.593994
Epoch 4, Loss 2069.307129
Epoch 5, Loss 1871.018311
Epoch 6, Loss 1692.521973
Epoch 7, Loss 1531.833618
Epoch 8, Loss 1387.168457
Epoch 9, Loss 1256.919678
Epoch 10, Loss 1139.642456

tensor([11.4749,  8.2523])

可见,损失也在变小,若将n_epoch设置得更大,会发现损失会一致保持下降。

©️2020 CSDN 皮肤主题: 终极编程指南 设计师:CSDN官方博客 返回首页