0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

PyTorch教程-5.4. 数值稳定性和初始化

jf_pJlTbmA9 来源:PyTorch 作者:PyTorch 2023-06-05 15:43 次阅读

到目前为止,我们实现的每个模型都需要我们根据一些预先指定的分布来初始化它的参数。直到现在,我们都认为初始化方案是理所当然的,掩盖了如何做出这些选择的细节。您甚至可能觉得这些选择并不是特别重要。相反,初始化方案的选择在神经网络学习中起着重要作用,对于保持数值稳定性至关重要。此外,这些选择可以以有趣的方式与非线性激活函数的选择联系起来。我们选择哪个函数以及我们如何初始化参数可以决定我们的优化算法收敛的速度。这里的错误选择可能会导致我们在训练时遇到梯度爆炸或消失的情况。在这个部分,

%matplotlib inline
import torch
from d2l import torch as d2l

%matplotlib inline
from mxnet import autograd, np, npx
from d2l import mxnet as d2l

npx.set_np()

%matplotlib inline
import jax
from jax import grad
from jax import numpy as jnp
from jax import vmap
from d2l import jax as d2l

No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)

%matplotlib inline
import tensorflow as tf
from d2l import tensorflow as d2l

5.4.1. 消失和爆炸梯度

考虑一个深度网络L图层,输入x 和输出o. 每层l由转换定义fl权重参数化 W(l), 隐藏层输出为 h(l)(让h(0)=x),我们的网络可以表示为:

(5.4.1)h(l)=fl(h(l−1))and thuso=fL∘…∘f1(x).

如果所有隐藏层的输出和输入都是向量,我们可以写出梯度为o关于任何一组参数 W(l)如下:

(5.4.2)∂W(l)o=∂h(L−1)h(L)⏟M(L)=def⋅…⋅∂h(l)h(l+1)⏟M(l+1)=def∂W(l)h(l)⏟v(l)=def.

换句话说,这个梯度是L−l矩阵 M(L)⋅…⋅M(l+1)和梯度向量v(l). 因此,当将太多概率相乘时,我们很容易遇到同样的数值下溢问题。在处理概率时,一个常见的技巧是切换到对数空间,即将压力从尾数转移到数值表示的指数。不幸的是,我们上面的问题更严重:最初矩阵 M(l)可能有各种各样的特征值。它们可能很小或很大,它们的产品可能很大或 很小。

不稳定梯度带来的风险超出了数值表示。不可预测的梯度也会威胁到我们优化算法的稳定性。我们可能面临以下参数更新:(i) 过大,破坏了我们的模型( 梯度爆炸问题);或 (ii) 过小(梯度消失问题),由于参数几乎不会在每次更新时移动,因此无法进行学习。

5.4.1.1。消失的渐变

导致梯度消失问题的一个常见罪魁祸首是激活函数的选择σ在每一层的线性操作之后附加。从历史上看,sigmoid 函数1/(1+exp⁡(−x))(在第 5.1 节中介绍)很受欢迎,因为它类似于阈值函数。由于早期的人工神经网络受到生物神经网络的启发,神经元要么完全放电要么根本不放电(就像生物神经元一样)的想法似乎很有吸引力。让我们仔细看看 sigmoid,看看它为什么会导致梯度消失。

x = torch.arange(-8.0, 8.0, 0.1, requires_grad=True)
y = torch.sigmoid(x)
y.backward(torch.ones_like(x))

d2l.plot(x.detach().numpy(), [y.detach().numpy(), x.grad.numpy()],
     legend=['sigmoid', 'gradient'], figsize=(4.5, 2.5))

poYBAGR9NLSAaUiRAAD4Yy-HP94664.svg

x = np.arange(-8.0, 8.0, 0.1)
x.attach_grad()
with autograd.record():
  y = npx.sigmoid(x)
y.backward()

d2l.plot(x, [y, x.grad], legend=['sigmoid', 'gradient'], figsize=(4.5, 2.5))

[07:15:48] src/base.cc:49: GPU context requested, but no GPUs found.

pYYBAGR9NLaAQR30AAD4Yx6sDr0480.svg

x = jnp.arange(-8.0, 8.0, 0.1)
y = jax.nn.sigmoid(x)
grad_sigmoid = vmap(grad(jax.nn.sigmoid))
d2l.plot(x, [y, grad_sigmoid(x)],
     legend=['sigmoid', 'gradient'], figsize=(4.5, 2.5))

poYBAGR9NLiAG47UAAD4Wfwoe20706.svg

x = tf.Variable(tf.range(-8.0, 8.0, 0.1))
with tf.GradientTape() as t:
  y = tf.nn.sigmoid(x)
d2l.plot(x.numpy(), [y.numpy(), t.gradient(y, x).numpy()],
     legend=['sigmoid', 'gradient'], figsize=(4.5, 2.5))

poYBAGR9NLuAGHgVAAD4Xi6uXEc210.svg

如您所见,S 形函数的梯度在其输入较大和较小时都消失了。此外,当通过多层反向传播时,除非我们处于 Goldilocks 区,其中许多 sigmoid 的输入接近于零,否则整个产品的梯度可能会消失。当我们的网络拥有很多层时,除非我们小心,否则梯度可能会在某一层被切断。事实上,这个问题曾经困扰着深度网络训练。因此,更稳定(但在神经上不太可信)的 ReLU 已成为从业者的默认选择。

5.4.1.2. 爆炸梯度

相反的问题,当梯度爆炸时,可能同样令人烦恼。为了更好地说明这一点,我们绘制了 100 个高斯随机矩阵并将它们与某个初始矩阵相乘。对于我们选择的尺度(方差的选择σ2=1), 矩阵乘积爆炸。当由于深度网络的初始化而发生这种情况时,我们没有机会让梯度下降优化器收敛。

M = torch.normal(0, 1, size=(4, 4))
print('a single matrix n',M)
for i in range(100):
  M = M @ torch.normal(0, 1, size=(4, 4))
print('after multiplying 100 matricesn', M)

a single matrix
 tensor([[ 0.0837, -0.9784, -0.5752, -0.0418],
    [ 2.0032, 2.0948, -1.4284, -1.5950],
    [-0.9720, -2.1672, -0.2809, 0.2282],
    [-0.7581, 0.0328, -0.2364, -0.5804]])
after multiplying 100 matrices
 tensor([[ 7.5119e+24, -9.2313e+24, -2.1761e+24, 7.0456e+23],
    [-1.3462e+24, 1.6544e+24, 3.8999e+23, -1.2627e+23],
    [ 1.4648e+25, -1.8001e+25, -4.2433e+24, 1.3739e+24],
    [ 8.9242e+24, -1.0967e+25, -2.5852e+24, 8.3702e+23]])

M = np.random.normal(size=(4, 4))
print('a single matrix', M)
for i in range(100):
  M = np.dot(M, np.random.normal(size=(4, 4)))
print('after multiplying 100 matrices', M)

a single matrix [[ 2.2122064  1.1630787  0.7740038  0.4838046 ]
 [ 1.0434403  0.29956347 1.1839255  0.15302546]
 [ 1.8917114 -1.1688148 -1.2347414  1.5580711 ]
 [-1.771029  -0.5459446 -0.45138445 -2.3556297 ]]
after multiplying 100 matrices [[ 3.4459747e+23 -7.8040759e+23 5.9973355e+23 4.5230040e+23]
 [ 2.5275059e+23 -5.7240258e+23 4.3988419e+23 3.3174704e+23]
 [ 1.3731275e+24 -3.1097129e+24 2.3897754e+24 1.8022945e+24]
 [-4.4951091e+23 1.0180045e+24 -7.8232368e+23 -5.9000419e+23]]

get_key = lambda: jax.random.PRNGKey(d2l.get_seed()) # Generate PRNG keys
M = jax.random.normal(get_key(), (4, 4))
print('a single matrix n', M)
for i in range(100):
  M = jnp.matmul(M, jax.random.normal(get_key(), (4, 4)))
print('after multiplying 100 matricesn', M)

a single matrix
 [[-1.531857  -1.6248469 -1.7896363  0.01071274]
 [-0.83422506 -1.583117  -0.04581776 -0.6887173 ]
 [ 0.5935193 -1.4750035  0.5265016 -1.0061077 ]
 [-0.6028679 -0.3464505  1.1737709 -1.3659075 ]]
after multiplying 100 matrices
 [[-2.7093967e+25 6.4777160e+24 -1.1368576e+25 -7.9616848e+25]
 [ 9.1084851e+24 -2.1776870e+24 3.8219019e+24 2.6765695e+25]
 [ 2.6918674e+25 -6.4358044e+24 1.1295020e+25 7.9101732e+25]
 [ 3.1859349e+25 -7.6170382e+24 1.3368117e+25 9.3620141e+25]]

M = tf.random.normal((4, 4))
print('a single matrix n', M)
for i in range(100):
  M = tf.matmul(M, tf.random.normal((4, 4)))
print('after multiplying 100 matricesn', M.numpy())

a single matrix
 tf.Tensor(
[[ 0.6401031 -0.30267003 -1.2595664 -0.09789732]
 [-1.7108601  0.5364894 -0.02595425 0.32053235]
 [-0.40997565 -0.6582443 -0.7346871  1.9668812 ]
 [ 0.16821837 -0.41409513 -0.5977446 -0.38071403]], shape=(4, 4), dtype=float32)
after multiplying 100 matrices
 [[1.5536073e+21 5.0625842e+21 2.7629899e+21 3.1974600e+21]
 [6.6411150e+20 2.1640736e+21 1.1810791e+21 1.3667998e+21]
 [3.0877614e+21 1.0061778e+22 5.4913826e+21 6.3548842e+21]
 [3.9506273e+20 1.2873513e+21 7.0259335e+20 8.1307368e+20]]

5.4.1.3. 打破对称

神经网络设计中的另一个问题是其参数化中固有的对称性。假设我们有一个带有一个隐藏层和两个单元的简单 MLP。在这种情况下,我们可以置换权重 W(1)第一层的权重,同样置换输出层的权重以获得相同的函数。第一个隐藏单元与第二个隐藏单元没有什么特别的区别。换句话说,我们在每一层的隐藏单元之间具有排列对称性。

这不仅仅是理论上的麻烦。考虑前面提到的具有两个隐藏单元的单隐藏层 MLP。为了说明,假设输出层将两个隐藏单元转换为仅一个输出单元。想象一下,如果我们将隐藏层的所有参数初始化为 W(1)=c对于一些常数c. 在这种情况下,在前向传播过程中,任一隐藏单元采用相同的输入和参数,产生相同的激活,并将其馈送到输出单元。在反向传播期间,根据参数区分输出单元W(1)给出一个梯度,其元素都取相同的值. 因此,在基于梯度的迭代(例如,小批量随机梯度下降)之后, W(1)仍然取相同的值。这样的迭代永远不会自行破坏对称性,我们可能永远无法实现网络的表达能力。隐藏层的行为就好像它只有一个单元。请注意,虽然小批量随机梯度下降不会破坏这种对称性,但 dropout 正则化(稍后介绍)会!

5.4.2. 参数初始化

解决或至少减轻上述问题的一种方法是通过仔细初始化。正如我们稍后将看到的,在优化和适当的正则化过程中额外注意可以进一步提高稳定性。

5.4.2.1. 默认初始化

在前面的部分中,例如,在第 3.5 节中,我们使用正态分布来初始化我们的权重值。如果我们不指定初始化方法,框架将使用默认的随机初始化方法,这在实践中通常适用于中等规模的问题。

5.4.2.2. 泽维尔初始化

让我们看一下输出的尺度分布oi对于一些没有非线性的完全连接层。和 nin输入xj及其相关权重 wij对于这一层,输出由下式给出

(5.4.3)oi=∑j=1ninwijxj.

权重wij都是从同一个分布中独立抽取的。此外,我们假设此分布的均值和方差为零σ2. 请注意,这并不意味着分布必须是高斯分布,只是意味着均值和方差需要存在。现在,我们假设层的输入 xj也有零均值和方差γ2并且它们独立于wij并且相互独立。在这种情况下,我们可以计算的均值和方差oi如下:

(5.4.4)E[oi]=∑j=1ninE[wijxj]=∑j=1ninE[wij]E[xj]=0,Var[oi]=E[oi2]−(E[oi])2=∑j=1ninE[wij2xj2]−0=∑j=1ninE[wij2]E[xj2]=ninσ2γ2.

保持方差固定的一种方法是设置 ninσ2=1. 现在考虑反向传播。我们面临着类似的问题,尽管梯度是从更靠近输出的层传播的。使用与前向传播相同的推理,我们看到梯度的方差会爆炸,除非 noutσ2=1, 在哪里nout是这一层的输出个数。这让我们进退两难:我们不可能同时满足这两个条件。相反,我们只是尝试满足:

(5.4.5)12(nin+nout)σ2=1or equivalentlyσ=2nin+nout.

这就是现在标准且实际有益的Xavier 初始化背后的推理,以其创建者的第一作者命名(Glorot 和 Bengio,2010)。通常,Xavier 初始化从具有零均值和方差的高斯分布中采样权重 σ2=2nin+nout. 当从均匀分布中采样权重时,我们也可以调整它来选择方差。注意均匀分布U(−a,a)有方差a23. 堵漏a23进入我们的条件σ2产生根据初始化的建议

(5.4.6)U(−6nin+nout,6nin+nout).

尽管在神经网络中很容易违反上述数学推理中不存在非线性的假设,但 Xavier 初始化方法在实践中表现良好。

5.4.2.3. 超过

上面的推理仅仅触及了现代参数初始化方法的皮毛。深度学习框架通常会实施十几种不同的启发式方法。此外,参数初始化仍然是深度学习基础研究的热点领域。其中包括专门用于绑定(共享)参数、超分辨率、序列模型和其他情况的启发式方法。例如, 肖等人。( 2018 )通过使用精心设计的初始化方法证明了在没有架构技巧的情况下训练 10000 层神经网络的可能性。

如果您对该主题感兴趣,我们建议您深入研究该模块的内容,阅读提出和分析每个启发式的论文,然后探索有关该主题的最新出版物。也许您会偶然发现甚至发明一个聪明的想法并为深度学习框架贡献一个实现。

5.4.3. 概括

梯度消失和爆炸是深度网络中的常见问题。需要非常小心地进行参数初始化,以确保梯度和参数得到很好的控制。需要初始化启发式方法来确保初始梯度既不过大也不过小。随机初始化是确保优化前对称性被破坏的关键。Xavier 初始化表明,对于每一层,任何输出的方差都不受输入数量的影响,并且任何梯度的方差都不受输出数量的影响。ReLU 激活函数减轻了梯度消失问题。这可以加速收敛。

5.4.4. 练习

除了 MLP 层中的置换对称性之外,您能否设计神经网络可能表现出需要破坏的对称性的其他情况?

我们能否将线性回归或 softmax 回归中的所有权重参数初始化为相同的值?

查找两个矩阵乘积的特征值的解析界限。这告诉您有关确保梯度条件良好的什么信息

如果我们知道某些术语存在分歧,我们能否在事后解决这个问题?查看有关分层自适应速率缩放的论文以获取灵感 (You等人,2017 年)。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • pytorch
    +关注

    关注

    2

    文章

    802

    浏览量

    13105
收藏 人收藏

    评论

    相关推荐

    字符型、指针型等变量该如何初始化

    在敲代码的时候,我们会给变量一个初始值,以防止因为编译器的原因造成变量初始值的不确定性。对于数值类型的变量往往初始化为0,但对于其他类型的变
    发表于 09-23 11:50 2152次阅读

    产品程序运行稳定性问题:假如HSE初始化失败,需要做哪些事情?

    下面几点涉及到产品程序稳定性,请教一下大神们1、假如HSE初始化失败,需要做哪些事情?(重启芯片?将时钟配置HSI?或者其他?) if(HSE 成功) {....... } else
    发表于 01-04 09:27

    喂狗(重载计数值)初始化流程

    这里写代码片IWDG_Initializes();//初始化IWDG_ReloadCounter();//喂狗(重载计数值)初始化流程:void IWDG_Initializes(void){IWDG_Enable();//使能
    发表于 08-02 07:37

    手机模块初始化向导

    手机模块初始化向导:为了刚好的对手机模块进行初始化,所以把最基本的向导写下来.本向导适用于本公司的西门子TC35I和华为GT9000模块。一、在初始化手机模块前,请先确定DT
    发表于 09-18 09:41 17次下载

    电感的稳定性

    电感的稳定性 稳定性是表示电感线圈参数随环境条件变化而改变的程度。通常用电感温度系数αL 来评定线圈的稳定程度,它表示电感量相对泪度的稳定
    发表于 08-22 14:33 1542次阅读

    RDA1846S初始化设置

    RDA1846S初始化设置RDA1846S初始化设置RDA1846S初始化设置
    发表于 01-15 17:08 0次下载

    UCOS_III_配置与初始化

    UCOS_III_配置与初始化
    发表于 12-20 22:53 5次下载

    电力系统暂态稳定性快速数值计算方法

    为满足电力系统暂态稳定性实时分析计算的需求,将边界值类方法中的广义向后差分方法应用于暂态稳定性数值计算,提出了一种新的暂态稳定性快速数值计算
    发表于 03-01 15:05 1次下载

    8253初始化程序分享_8253应用案例

    本文首先介绍了8253概念及8253各通道的工作方式,其次详细介绍了8253初始化要求及编程,最后用一个例子介绍了8253的初始化程序。
    发表于 05-23 15:52 2.2w次阅读
    8253<b class='flag-5'>初始化</b>程序分享_8253应用案例

    在51平台下初始化文件的引入导致全局变量无法初始化的问题如何解决

    本文档的主要内容详细介绍的是在51平台下初始化文件的引入导致全局变量无法初始化的问题如何解决。
    发表于 08-20 17:31 0次下载
    在51平台下<b class='flag-5'>初始化</b>文件的引入导致全局变量无法<b class='flag-5'>初始化</b>的问题如何解决

    什么是热电偶稳定性?如何检测热电偶稳定性

    在规定的条件下,热电特性变化大即表明稳定性差,变化小则表明稳定性良好。热电偶的稳定性好坏会直接影响到热电偶测量的准确性,因此,稳定性是衡量热电偶性能的一个重要指标。
    发表于 12-31 09:19 2574次阅读
    什么是热电偶<b class='flag-5'>稳定性</b>?如何检测热电偶<b class='flag-5'>稳定性</b>?

    PyTorch教程5.4数值稳定性初始化

    电子发烧友网站提供《PyTorch教程5.4数值稳定性初始化.pdf》资料免费下载
    发表于 06-05 15:30 0次下载
    <b class='flag-5'>PyTorch</b>教程<b class='flag-5'>5.4</b>之<b class='flag-5'>数值</b><b class='flag-5'>稳定性</b>和<b class='flag-5'>初始化</b>

    PyTorch教程6.4之惰性初始化

    电子发烧友网站提供《PyTorch教程6.4之惰性初始化.pdf》资料免费下载
    发表于 06-05 11:52 0次下载
    <b class='flag-5'>PyTorch</b>教程6.4之惰性<b class='flag-5'>初始化</b>

    怎么分析电路的稳定性

    怎么分析电路的稳定性?  电路的稳定性是指电路在不同条件下保持稳定的能力。稳定性是电路设计中十分重要的一个方面,因为稳定的电路能够提供可靠和
    的头像 发表于 09-17 16:44 1648次阅读

    字符型、指针型等变量等该如何初始化

     对于数值类型的变量往往初始化为0,但对于其他类型的变量,如字符型、指针型等变量等该如何初始化呢?
    的头像 发表于 03-18 11:02 1165次阅读