【深度学习实验】网络优化与正则化(四):参数初始化及其Pytorch实现——基于固定方差的初始化(高斯、均匀分布),基于方差缩放的初始化(Xavier、He),正交初始化

文章目录

  • 一、实验介绍
  • 二、实验环境
    • 1. 配置虚拟环境
    • 2. 库版本介绍
  • 三、优化算法
    • 0. 导入必要的库
    • 1. 随机梯度下降SGD算法
      • a. PyTorch中的SGD优化器
      • b. 使用SGD优化器的前馈神经网络
    • 2.随机梯度下降的改进方法
      • a. 学习率调整
      • b. 梯度估计修正
    • 3. 梯度估计修正:动量法Momentum
    • 4. 自适应学习率
    • 5. Adam算法
  • 四、参数初始化
    • 1. 基于固定方差的参数初始化
      • a. 高斯分布初始化
      • b. 均匀分布初始化
    • 2. 基于方差缩放的参数初始化
      • a. Xavier初始化(也称为Glorot初始化)
      • b. He初始化
    • 3. 正交初始化
    • 4. 代码整合

  好的初始值会使得网络收敛到一个泛化能力高的局部最优解!

一、实验介绍

  深度神经网络在机器学习中应用时面临两类主要问题:优化问题和泛化问题。

  • 优化问题:深度神经网络的优化具有挑战性。

    • 神经网络的损失函数通常是非凸函数,因此找到全局最优解往往困难。
    • 深度神经网络的参数通常非常多,而训练数据也很大,因此使用计算代价较高的二阶优化方法不太可行,而一阶优化方法的训练效率通常较低。
    • 深度神经网络存在梯度消失梯度爆炸问题,导致基于梯度的优化方法经常失效。
  • 泛化问题:由于深度神经网络的复杂度较高且具有强大的拟合能力,很容易在训练集上产生过拟合现象。因此,在训练深度神经网络时需要采用一定的正则化方法来提高网络的泛化能力。

  目前,研究人员通过大量实践总结了一些经验方法,以在神经网络的表示能力、复杂度、学习效率和泛化能力之间取得良好的平衡,从而得到良好的网络模型。本系列文章将从网络优化和网络正则化两个方面来介绍如下方法:

  • 在网络优化方面,常用的方法包括优化算法的选择参数初始化方法数据预处理方法逐层归一化方法超参数优化方法
  • 在网络正则化方面,一些提高网络泛化能力的方法包括ℓ1和ℓ2正则化权重衰减提前停止丢弃法数据增强标签平滑等。

  本文将介绍基于自适应学习率的优化算法:Adam算法详解(Adam≈梯度方向优化Momentum+自适应学习率RMSprop)

二、实验环境

  本系列实验使用了PyTorch深度学习框架,相关操作如下:

1. 配置虚拟环境

conda create -n DL python=3.7 
conda activate DL
pip install torch==1.8.1+cu102 torchvision==0.9.1+cu102 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html
conda install matplotlib
 conda install scikit-learn

2. 库版本介绍

软件包 本实验版本 目前最新版
matplotlib 3.5.3 3.8.0
numpy 1.21.6 1.26.0
python 3.7.16
scikit-learn 0.22.1 1.3.0
torch 1.8.1+cu102 2.0.1
torchaudio 0.8.1 2.0.2
torchvision 0.9.1+cu102 0.15.2

三、优化算法

  神经网络的参数学习是一个非凸优化问题.当使用梯度下降法来进行优化网络参数时,参数初始值的选取十分关键,关系到网络的优化效率和泛化能力.参数初始化的方式通常有以下三种:

0. 导入必要的库

from torch import nn

1. 随机梯度下降SGD算法

  随机梯度下降(Stochastic Gradient Descent,SGD)是一种常用的优化算法,用于训练深度神经网络。在每次迭代中,SGD通过随机均匀采样一个数据样本的索引,并计算该样本的梯度来更新网络参数。具体而言,SGD的更新步骤如下:

  1. 从训练数据中随机选择一个样本的索引。
  2. 使用选择的样本计算损失函数对于网络参数的梯度。
  3. 根据计算得到的梯度更新网络参数。
  4. 重复以上步骤,直到达到停止条件(如达到固定的迭代次数或损失函数收敛)。

a. PyTorch中的SGD优化器

   Pytorch官方教程

optimizer = torch.optim.SGD(model.parameters(), lr=0.2)

b. 使用SGD优化器的前馈神经网络

   【深度学习实验】前馈神经网络(final):自定义鸢尾花分类前馈神经网络模型并进行训练及评价

2.随机梯度下降的改进方法

  传统的SGD在某些情况下可能存在一些问题,例如学习率选择困难和梯度的不稳定性。为了改进这些问题,提出了一些随机梯度下降的改进方法,其中包括学习率的调整和梯度的优化。

a. 学习率调整

在这里插入图片描述

  • 学习率衰减(Learning Rate Decay):随着训练的进行,逐渐降低学习率。常见的学习率衰减方法有固定衰减、按照指数衰减、按照时间表衰减等。
  • Adagrad:自适应地调整学习率。Adagrad根据参数在训练过程中的历史梯度进行调整,对于稀疏梯度较大的参数,降低学习率;对于稀疏梯度较小的参数,增加学习率。这样可以在不同参数上采用不同的学习率,提高收敛速度。
  • Adadelta:与Adagrad类似,但进一步解决了Adagrad学习率递减过快的问题。Adadelta不仅考虑了历史梯度,还引入了一个累积的平方梯度的衰减平均,以动态调整学习率。
  • RMSprop:也是一种自适应学习率的方法,通过使用梯度的指数加权移动平均来调整学习率。RMSprop结合了Adagrad的思想,但使用了衰减平均来减缓学习率的累积效果,从而更加稳定。

b. 梯度估计修正

  • Momentum:使用梯度的“加权移动平均”作为参数的更新方向。Momentum方法引入了一个动量项,用于加速梯度下降的过程。通过积累之前的梯度信息,可以在更新参数时保持一定的惯性,有助于跳出局部最优解、加快收敛速度。
  • Nesterov accelerated gradient:Nesterov加速梯度(NAG)是Momentum的一种变体。与Momentum不同的是,NAG会先根据当前的梯度估计出一个未来位置,然后在该位置计算梯度。这样可以更准确地估计当前位置的梯度,并且在参数更新时更加稳定。
  • 梯度截断(Gradient Clipping):为了应对梯度爆炸或梯度消失的问题,梯度截断的方法被提出。梯度截断通过限制梯度的范围,将梯度控制在一个合理的范围内。常见的梯度截断方法有阈值截断和梯度缩放。

3. 梯度估计修正:动量法Momentum

【深度学习实验】网络优化与正则化(一):优化算法:使用动量优化的随机梯度下降算法(Stochastic Gradient Descent with Momentum)

4. 自适应学习率

【深度学习实验】网络优化与正则化(二):基于自适应学习率的优化算法详解:Adagrad、Adadelta、RMSprop

5. Adam算法

  Adam算法(Adaptive Moment Estimation Algorithm)[Kingma et al., 2015]可以看作动量法和 RMSprop 算法的结合,不但使用动量作为参数更新方向,而且可以自适应调整学习率
【深度学习实验】网络优化与正则化(三):随机梯度下降的改进——Adam算法详解(Adam≈梯度方向优化Momentum+自适应学习率RMSprop)

四、参数初始化

  在神经网络中,参数学习是通过最小化损失函数来进行的,而这通常涉及到解决一个非凸优化问题。非凸优化问题的特点是存在多个局部最小值,而全局最小值不容易找到。梯度下降法是一种常用的优化算法,但容易陷入局部最小值。参数的初始值选择对训练的效果有显著影响,以下是常见的参数初始化方式:

  1. 预训练初始化(Pretraining Initialization)

    • 这种方法通常应用于深度神经网络,特别是在训练深度自编码器或生成对抗网络(GAN)等情况下。
    • 预训练阶段使用无监督学习,然后将学到的权重作为神经网络的初始权重。
    • 这种方法在一些场景下可以帮助避免陷入局部最小值,尤其是在数据集较小或任务较为复杂时。
  2. 随机初始化(Random Initialization)

    • 这是最常用的初始化方法之一。
    • 参数的初始值通过从一个均匀或高斯分布中随机选择得到。
    • 随机初始化有助于打破对称性,避免所有的神经元学到相同的特征。
  3. 固定值初始化(Fixed Value Initialization)

    • 将所有的权重初始化为一个固定的常数。
    • 这种初始化方法在某些情况下可能有效,但通常不如随机初始化灵活。
    • 例如,将所有权重初始化为零可能会导致网络对称性问题,因此通常不建议使用。

  在实践中,通常结合使用不同的技术来初始化网络参数。此外,一些高级的初始化方法,如He初始化Xavier初始化等,针对不同的激活函数和网络结构进行了优化,以提高训练的效果。选择合适的初始化方法往往需要根据具体的任务和网络结构进行实验和调整。

1. 基于固定方差的参数初始化

a. 高斯分布初始化

  它假设参数的分布服从高斯分布(也称为正态分布),其中均值为0,方差为^2。通过从这个分布中随机采样,可以得到参数的初始值。高斯分布初始化在实践中表现良好,尤其适用于深度神经网络的参数初始化。

def init_gaussian(m, mean=0, std=0.01):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, mean=mean, std=std)
        nn.init.zeros_(m.bias)

b. 均匀分布初始化

  它假设参数的分布服从均匀分布,范围为[-, ],其中是根据方差2计算得到的适当的范围。通过从这个范围内均匀采样,可以得到参数的初始值。均匀分布初始化在某些情况下可能比高斯分布初始化更适用,例如在某些激活函数(如ReLU)和某些网络架构中。

def init_uniform(m, a=0, b=1):
    if type(m) == nn.Linear:
        nn.init.uniform_(m.weight, a=a, b=b)
        nn.init.zeros_(m.bias)

2. 基于方差缩放的参数初始化

  基于方差缩放的参数初始化方法旨在根据神经网络的结构和激活函数的特性来选择合适的方差,以更好地初始化参数。两种常见的方差缩放初始化方法是Xavier初始化和He初始化。
【深度学习实验】网络优化与正则化(四):参数初始化及其Pytorch实现——基于固定方差的初始化(高斯、均匀分布),基于方差缩放的初始化(Xavier、He),正交初始化_第1张图片

a. Xavier初始化(也称为Glorot初始化)

  Xavier初始化是一种广泛使用的参数初始化方法,适用于使用双曲正切(tanh)或S型(sigmoid)激活函数的神经网络。它的目标是使每个神经元的输出具有相同的方差。对于具有n个输入和m个输出的全连接层,Xavier初始化将参数从均值为0的高斯分布中随机采样,并使用方差^2 = 1/(n+m)进行缩放。对于具有ReLU激活函数的网络,Xavier初始化可能不是最佳选择。

def init_xavier(m):
    if type(m) == nn.Linear:
        nn.init.xavier_uniform_(m.weight)
        nn.init.zeros_(m.bias)

b. He初始化

  He初始化是专为使用ReLU(修正线性单元)激活函数的神经网络设计的参数初始化方法。与Xavier初始化类似,He初始化也从均值为0的高斯分布中随机采样,但是使用方差^2 = 2/n进行缩放,其中n是输入的数量。He初始化通过适当增加方差来解决ReLU激活函数的梯度消失问题,从而更好地初始化参数。

def init_he(m):
    if type(m) == nn.Linear:
        nn.init.kaiming_uniform_(m.weight, nonlinearity='relu')
        nn.init.zeros_(m.bias)

3. 正交初始化

  正交初始化旨在使神经网络的权重矩阵具有正交性。正交初始化的目标是减少权重之间的冗余性,促进梯度的有效传播,从而改善网络的训练效果。
  在正交初始化中,权重矩阵被初始化为一个正交矩阵或其变体。一种常见的方法是使用QR分解或SVD分解来生成正交矩阵。具体步骤如下:

  1. 对于具有输入维度为n和输出维度为m的权重矩阵W,从均值为0、方差较小的高斯分布中随机初始化W。

  2. 对W进行QR分解或SVD分解,得到正交矩阵Q和对角矩阵D。

  3. 将Q作为初始化后的权重矩阵,即W = Q。

  正交初始化的优点之一是它可以减少参数量,因为正交矩阵具有特殊的结构,其中元素之间存在较强的相关性。这对于具有大量参数的神经网络来说尤为重要,可以减少过拟合的风险,特别是在循环神经网络(RNN)和卷积神经网络(CNN)中。它有助于缓解梯度消失和梯度爆炸问题,促进梯度的传播,从而改善网络的训练稳定性和收敛速度。

def init_orthogonal(m):
    if type(m) == nn.RNN:
        for name, param in m.named_parameters():
            if 'weight' in name:
                nn.init.orthogonal_(param)
            elif 'bias' in name:
                nn.init.zeros_(param)

4. 代码整合

from torch import nn


class FeedForward(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(FeedForward, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.fc2 = nn.Linear(hidden_size, output_size)
        self.act = nn.Sigmoid()

    def forward(self, inputs):
        outputs = self.fc1(inputs)
        outputs = self.act(outputs)
        outputs = self.fc2(outputs)
        return outputs


def init_constant(m):
    if type(m) == nn.Linear:
        nn.init.constant_(m.weight, 1)
        nn.init.zeros_(m.bias)


def init_normal(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, mean=0, std=0.01)
        nn.init.zeros_(m.bias)


def init_gaussian(m, mean=0, std=0.01):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, mean=mean, std=std)
        nn.init.zeros_(m.bias)


def init_uniform(m, a=0, b=1):
    if type(m) == nn.Linear:
        nn.init.uniform_(m.weight, a=a, b=b)
        nn.init.zeros_(m.bias)


def init_xavier(m):
    if type(m) == nn.Linear:
        nn.init.xavier_uniform_(m.weight)
        nn.init.zeros_(m.bias)


def init_he(m):
    if type(m) == nn.Linear:
        nn.init.kaiming_uniform_(m.weight, nonlinearity='relu')
        nn.init.zeros_(m.bias)


def init_orthogonal(m):
    if type(m) == nn.RNN:
        for name, param in m.named_parameters():
            if 'weight' in name:
                nn.init.orthogonal_(param)
            elif 'bias' in name:
                nn.init.zeros_(param)


net = FeedForward(4, 6, 3)
net.apply(init_constant)
print(net.fc1.weight.data)
print(net.fc2.weight.data)

你可能感兴趣的:(深度学习实验,深度学习,pytorch,人工智能,python,神经网络,参数初始化)