pytorch深度学习基础6

上一节我们建立了一个简单的模型进行分析散点图,利用均方差来实现损失函数的计算,但是并没有计算出具体的参数值,这次我们来计算损失函数的损失值以及不断减小损失值,计算出最优的参数,代码原理非常简单大家可以自行理解查阅资料

import numpy as np
import torch
import time
torch.set_printoptions(edgeitems=2)
t_c = torch.tensor([0.5, 14.0, 15.0, 28.0, 11.0, 8.0,
                    3.0, -4.0, 6.0, 13.0, 21.0])
t_u = torch.tensor([35.7, 55.9, 58.2, 81.9, 56.3, 48.9,
                    33.9, 21.8, 48.4, 60.4, 68.4])
t_un = 1 * t_u
def model(t_u, w, b):
    return w * t_u + b
def loss_fn(t_p, t_c):
    squared_diffs = (t_p - t_c)**2
    return squared_diffs.mean()
params = torch.tensor([1.0, 0.0], requires_grad=True)
loss = loss_fn(model(t_u, *params), t_c)
loss.backward()
if params.grad is not None:
    params.grad.zero_()


def training_loop(n_epochs, learning_rate, params, t_u, t_c):
    for epoch in range(1, n_epochs + 1):
        if params.grad is not None:  # <1>
            params.grad.zero_()

        t_p = model(t_u, *params)
        loss = loss_fn(t_p, t_c)
        loss.backward()

        with torch.no_grad():  # <2>
            params -= learning_rate * params.grad

        if epoch:
            # time.sleep(0.2)
            print('Epoch %d, Loss %f' % (epoch, float(loss)))
            print('params:',params)
    return params
training_loop(
    n_epochs = 10000,
    learning_rate = 1e-2,
    params = torch.tensor([1.0, 0.0], requires_grad=True), # <1>
    t_u = t_un, # <2>
    t_c = t_c)

我来简单说一下代码的思想,代码中规定了一个model()函数用于计算出输入参数得到的预测值,也就是t_p,把预测的值存在t_p中,然后利用t_p与实际值也就是t_c计算出一个方差,loss_fn()函数的作用就是计算预测值与实际值之间的方差,由于我们使用梯度下降算法,所以每次计算完都需要把梯度重新归零防止梯度累加,training_loop()这个函数作用就是循环训练,并更新所有训练的参数,接下来,我们运行代码

实际上,我们得到的损失值竟然在快速变大,越来越离谱,发生了什么呢?也就是出现我们经常说的梯度爆炸的现象

梯度爆炸

梯度爆炸是指在训练深度神经网络时,梯度(即损失函数对参数的导数)变得异常大,导致参数更新幅度过大,破坏了模型的稳定性,并可能使损失函数值急剧增加。这通常发生在以下几种情况:

  1. 不合适的初始学习率:如果学习率设置得过高,可能会导致在每次参数更新时,参数的改变量过大,从而使得损失函数值快速增加。

  2. 激活函数选择不当:某些激活函数(如Sigmoid或Tanh)在极端值(如非常大或非常小的输入)下可能会导致梯度消失或梯度爆炸。虽然这种情况更常见于梯度消失,但在某些情况下,特别是与其他因素结合时,也可能导致梯度爆炸。

  3. 深度过深的网络:非常深的网络可能导致梯度在反向传播过程中累积放大,特别是当每一层的梯度都比较大时。

  4. 损失函数或优化器设置不当:某些损失函数或优化器可能在特定条件下表现不佳,导致梯度不稳定。

针对你的问题,你可以尝试以下几种方法来解决梯度爆炸问题:

  • 降低学习率:使用更小的学习率可以减缓参数更新的速度,从而减少梯度爆炸的风险。

  • 使用梯度裁剪(Gradient Clipping):在梯度更新之前,对梯度值进行裁剪,确保梯度的最大绝对值不超过某个阈值。

  • 更换激活函数:尝试使用ReLU及其变体(如Leaky ReLU、PReLU等),这些激活函数在大多数情况下能更好地缓解梯度消失或爆炸的问题。

  • 简化网络结构:如果可能的话,尝试简化网络结构,减少网络深度或宽度,以减少梯度累积放大的可能性。

  • 更换优化器:某些优化器(如Adam、RMSprop等)自带了一定的梯度缩放机制,可能更适合你的任务。

  • 正则化:通过添加L1或L2正则化项来限制参数的更新幅度,这有助于保持参数的稳定性。

  • 数据预处理:确保输入数据已经过适当的预处理(如归一化、标准化等),以避免由于数据规模不当导致的梯度问题。  

实际上也就是发生了过度修正的现象,就是每次修正的步长太大了,导致参数接收到的更新太大了,参数开始来回波动,每一次的修正过度,就会导致下一次的修正也发生过度的现象,优化不稳定,反而是其变得发散而不是收敛,从损失值快速变大就可以发现,问题显而易见,params -= learning_rate * params.grad这行代码所导致的,那我们如何优化来限制它的大小呢,调整学习率和梯度

调整学习率

我们可以调整一下学习率,较小的学习率会使得损失值慢慢减小,我们修改一下学习率继续实验

training_loop(
    n_epochs = 10000,
    learning_rate = 1e-4,
    params = torch.tensor([1.0, 0.0], requires_grad=True), # <1>
    t_u = t_un, # <2>
    t_c = t_c)

不难发现,和上一次的结果截然相反,这个损失值慢慢减小,直到训练轮数的不断提升,参数和损失值都趋于稳定,由于学习率降低了,从原理出发,我们需要提高训练的轮数来提高结果的稳定,我这里训练轮数改为了10w轮,看看结果吧

可以看到损失值变化非常小,如果加大轮数很容易得到一个最近似的参数值

我们除了调整学习率之外,还可以进行梯度的调整,我们采用的方法叫做归一化输入

归一化输入

从第一次运行完代码的图片中可以发现,权重w的梯度与偏置b的梯度相差大概50倍,这很好地说明权重与偏置的存在于不同的比例空间中,在这中情况下如果学习率的值偏大,很容易导致只能有效更新其中一个参数,对于另外一个参数而言,学习率就会变得不稳定,无法对其进行有效的更新,每个参数都有自己的学习率,他们彼此是独立的,除非参数之间相差不大,但是为了得到准确的结果,还是建议使用适合参数自身的学习率,可以有效的保证模型训练的精度,这里的话小编就不介绍复杂的归一化算法,我们就使用最简单的归一化输入算法。

那我们改变输入,这样梯度就不会有太大的不同,也就是对输入的值进行缩放,仅仅用作计算,最后输出把倍率放回去就行,我们采用对x进行缩小成原来的0.1倍

t_un = 0.1 * t_u

我自己设置的是训练1k轮,看看效果吧

上面10w轮损失值才到3.7,而我归一化处理以后1k就可以到达3.8,可见效率大大提升了。正常的网络当中的参数都是百万级别的,我们仅仅用了最简单的两个办法就可以大大加快了训练的效率,在真实的训练当中我们需要花费大量的时间在进行网络的训练,有两许多优化器以及许多优化算法,可以大大减少了训练的时间,这在深度学习中非常重要。

最后我们来看看计算出来的参数的可视化效果

可视化

params =training_loop(
    n_epochs = 10000,
    learning_rate = 1e-2,
    params = torch.tensor([1.0, 0.0], requires_grad=True),
    t_u = t_un,
    t_c = t_c)

from matplotlib import pyplot as plt

t_p = model(t_un, *params)

fig = plt.figure(dpi=100)
plt.xlabel("Temperature (°Fahrenheit)")
plt.ylabel("Temperature (°Celsius)")
plt.plot(t_u.numpy(), t_p.detach().numpy()) # <2>
plt.plot(t_u.numpy(), t_c.numpy(), 'o')
plt.show()

  • 25
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值