一个有意思pytorch的简单应用小实验

发布于:2024-12-06 ⋅ 阅读:(196) ⋅ 点赞:(0)

        通过一个简单的脚本,来学习pytorch的基本应用,比如:前向传播、反向传播、学习率以及预测、模型的基本原理和套路。

        得到结果。。。保存模型。。。输入参数。。。预测。。。像不像?。。。像多少?。。。

        设计目标:一个包含了两个元素的输入张量,经过一个线性模型的运算后输出预测结果,经过前向传播、反向传播、学习调整后,使预测的结果尽量接近目标结果。

        输入张量:in_tensor=[2.0, 9.0]

        线性模型:model(k0 * in_tensor[0] + k1 * in_tensor[1])

        目标结果:100。

        总结来说就是:设计目标:2.0*k0 + 9.0*k1 = 100,通过Pytorch的惯用框架和套路,经过多次学习和迭代优化之后,求出k0和k1的最优值。

基本代码

import torch
import random

# 定义常量
TARGET_VALUE = 100
LR = 0.01  # 学习率

# 初始化张量和权重
in_tensor = torch.tensor([2.0, 4.0])
k0 = torch.tensor(random.random(), requires_grad=True)  # 权重需要计算梯度
k1 = torch.tensor(random.random(), requires_grad=True)  # 权重需要计算梯度

# 定义模型
def model(in_tensor, k0, k1):
    return k0 * in_tensor[0] + k1 * in_tensor[1]   # 定义了一个简单的线性模型

# 定义损失函数
def loss_fn(y_pred, y_true):
    return (y_pred - y_true) ** 2   # 均方误差(MSE),计算预测值与真实值之间的平方差。

# 训练过程
def train(iterations, in_tensor, k0, k1):
    for i in range(iterations):
        # 前向传播
        y_pred = model(in_tensor, k0, k1)   # 预测结果
        loss = loss_fn(y_pred, TARGET_VALUE)   # 损失值(可以理解为误差)

        # 反向传播
        loss.backward()

        # 更新权重
        with torch.no_grad():   # 停止梯度跟踪
            k0 -= LR * k0.grad  # k0减去它的梯度*学习率,完成一次权重的调整
            k1 -= LR * k1.grad  # k1减去它的梯度*学习率,完成一次权重的调整
            # 清零权重的梯度
            k0.grad.zero_()
            k1.grad.zero_()

        print(f"Iteration {i}: y_pred = {y_pred}, loss = {loss.item()}")

# 开始训练
train(10, in_tensor, k0, k1)

运行结果:

 可以看到,由于模型很简单,收敛很快,经过10次训练,loss已经降到了0.98。

学习率的实验

上面是学习率LR = 0.01得到的训练结果,现改为LR = 0.015:

同样的10次训练,当学习率增加之后,loss已经降到了0.000625,模型的收敛速度加快。 

继续加大学习率,改为LR = 0.03: 

 loss已经降到了2.85e-9,模型的收敛速度更快了。

继续加大,LR = 0.06:

预测值剧烈震荡,模型无法收敛。

知识点:

        加大学习率,可以加快模型收敛速度,但是也不能过大,学习率过大的后果:

        1. 无法收敛

        • 跳过最优解: 学习率过大时,每次参数更新的步长也会很大,这可能导致模型在优化过程中跳过最优解。

        • 震荡: 模型参数可能会在最佳值附近来回震荡,无法稳定地达到收敛。

        • 梯度爆炸: 在极端情况下,学习率过大可能导致梯度值变得非常大,进而使得参数更新步长过大,甚至导致数值溢出(如NaN)。  

        2. 训练不稳定

        • 损失函数波动: 损失函数的值可能会在每次迭代中剧烈波动,而不是逐渐减小。

        • 泛化能力差: 由于模型参数未能稳定收敛,可能导致模型在测试集上的表现不稳定,泛化能力差。  

        3. 过拟合风险增加

        • 在某些情况下,即使模型最终收敛,也可能因为学习率过大而错过最优解,导致过拟合。

再来,将学习率变小,LR = 0.006:

模型也在持续收敛,但是比起LR = 0.01,收敛变慢了。 

LR = 0.004:

收敛更慢了。

知识点:

        学习率过小的后果:

        1. 收敛速度慢

        • 训练时间长: 由于每次参数更新的步长很小,模型需要更多的迭代次数才能达到最优解,导致训练时间显著增加。

        • 陷入局部最优: 在某些情况下,学习率过小可能导致模型陷入局部最优解,而不是全局最优解。  

        2. 过拟合风险增加

        • 过度训练: 由于训练时间过长,模型可能在训练集上过度拟合,导致在测试集上的表现下降。  

        3. 梯度消失

        • 接近零的梯度: 学习率过小,尤其是在深度神经网络中,可能导致梯度值变得非常小,进而使得参数更新几乎停滞,这种现象称为梯度消失。

早停机制

将局部代码改为:

LR = 0.016
train(100, in_tensor, k0, k1)

在训练了16次之后,loss已经为0。所以,就可以停止训练了。

局部代码修改为:

# 训练过程
def train(iterations, in_tensor, k0, k1):
    for i in range(iterations):
        # 前向传播
        y_pred = model(in_tensor, k0, k1)   # 预测结果
        loss = loss_fn(y_pred, TARGET_VALUE)   # 损失值(可以理解为误差)

        if loss <= 0.00001:   # 早停机制
            print(f"Iteration {i}: y_pred = {y_pred}, loss = {loss.item()}, ki = {k0}, k1 = {k1}")
            break

        # 反向传播
。。。。。。。。。。。

 当偏差足够小时,停止训练,并输出训练结果。

保存模型和使用模型预测

import torch
import random

# 定义常量
TARGET_VALUE = 100
LR = 0.016  # 学习率

# 初始化张量和权重
in_tensor = torch.tensor([2.0, 4.0])
pre_tensor = torch.tensor([2.2, 4.0])
k0 = torch.tensor(random.random(), requires_grad=True)  # 权重需要计算梯度
k1 = torch.tensor(random.random(), requires_grad=True)  # 权重需要计算梯度
model_state = []   # 模型参数

# 定义模型
def model(in_tensor, k0, k1):
    return k0 * in_tensor[0] + k1 * in_tensor[1]

# 定义损失函数
def loss_fn(y_pred, y_true):
    return (y_pred - y_true) ** 2

# 训练过程
def train(iterations, in_tensor, k0, k1):
    for i in range(iterations):
        # 前向传播
        y_pred = model(in_tensor, k0, k1)
        loss = loss_fn(y_pred, TARGET_VALUE)

        if loss <= 0.00001:
            print(f"Iteration {i}: y_pred = {y_pred}, loss = {loss.item()}, ki = {k0}, k1 = {k1}")
            return [k0, k1]   # 返回训练后的模型参数
            break

        # 反向传播
        loss.backward()

        # 更新权重
        with torch.no_grad():   # 停止梯度跟踪
            k0 -= LR * k0.grad
            k1 -= LR * k1.grad
            # 清零权重的梯度
            k0.grad.zero_()
            k1.grad.zero_()

        # print(f"Iteration {i}: y_pred = {y_pred}, loss = {loss.item()}")

# 开始训练
model_state = train(100, in_tensor, k0, k1) # 训练100次

# 保存模型
torch.save(model_state, "model_state.pt")

# 加载模型
model_state = torch.load("model_state.pt")
print(model_state)

# 预测
y_pred = model(pre_tensor, model_state[0], model_state[1])
print(f"y_pred = {y_pred}, loss = {loss_fn(y_pred, TARGET_VALUE)}")

        在上面的代码中,我们保存了一个模型,并且用它预测了一个张量[2.2, 4.0],与我们训练用的数据[2.0, 4.0]相差不多,所以预测结果也相差不多。如果换成不同的数据,那么预测的结果也将会不同。

        推而广之,如果把输入的张量换成一个图像的像素阵列,预测结果换为判断类别,模型换为多层的卷积神经网络,再加上一些层间池化、输出激活函数,那么就是pytorch最常见的图像识别套路了。所以,无论模型和应用框架多么复杂,也是由最简单的结构迭加、衍生而成,将一个复杂的任务分解成一个个简单任务,它就不再复杂。

        以上为一点点初学者的肤浅心得,与大家交流共勉,望多指教!


网站公告

今日签到

点亮在社区的每一天
去签到