深度学习的一些基础知识

Posted by Leo on 2023-09-16
Estimated Reading Time 4 Minutes
Words 1.2k In Total

梯度下降

  • 引入: 对下图的三个点进行线性拟合,要优化的参数是$b$,使得三个点到直线的距离最小,我们可以求出距离之和的式子(最小二乘),作为损失函数,在初中我们可以对这个二次函数求最值,现在换个思路,即梯度下降法(GD)

    image-20230916145705344
  • 随机初始化一个$b$点,求出当前点的斜率,在乘一个常数$\varepsilon$,对$b$进行更新,得到的新值比之前的损失函数更小,然后不断迭代,就找到了最小值

    image-20230916145839905 image-20230916150054669

    更一般的形式如下:要优化的参数是$\theta$,首先求出$x_i$点处损失函数$L$关于$\theta$的偏导$\nabla_{\theta}L(f(x_i,\theta),y_i)$,对$N$个点求和取平均得到梯度$g$,$\theta$沿着梯度的负方向每次移动一个$\varepsilon$(learning rate)就可以让损失函数更小

    image-20230916150324899
  • 优化方法:

    • 随机梯度下降(SGD):如果样本点很多,计算的内存开销较大,就每次从所有样本中随机选取小部分样本拿来训练,并且不重复

    • 动量随机梯度下降:为了防止在 “山谷” 之间 “振荡” ,每次增加一个阻尼,每次计算保留一部分上一次计算的梯度(历史动量),加在一起就是新的梯度
      $$
      v \leftarrow \alpha v-\varepsilon g \
      \theta \leftarrow \theta + v
      $$
      其中$\alpha$用来控制历史动量对新方向的影响程度

      image-20230916151344967
    • AdaGrad算法:在训练过程中希望梯度一开始快速找到下降方向,学习率要大,后期要找到最小值,不能一直震荡,学习率要小。于是引入一个新的量$r$表示梯度在时间上的积累量,然后将$\sqrt{r}$放在学习率的分母上,可以实现自动调整。其中$\delta$是为了稳定数值计算
      $$
      r \leftarrow r+g^2 \
      \theta \leftarrow \theta - \frac{\varepsilon}{\sqrt{r}+\delta}
      $$
      但AdaGrad算法可能会使学习率过早下降,于是后来提出的RMSProp算法在$r$更新的式子中加入了可以手动调节的$\rho$
      $$
      r\leftarrow \rho r+(1-\rho)g^2
      $$

    • Adam算法:结合自适应学习和动量两种方法,自适应动量由$s$表示
      $$
      s \leftarrow \rho_1s+(1-\rho_1)g \
      r \leftarrow \rho_2r+(1-\rho_2)g^2 \
      \hat{s} \leftarrow \frac{s}{1-\rho_1^t} \
      \hat{r} \leftarrow \frac{r}{1-\rho_2^t} \
      \theta \leftarrow \theta-\frac{\epsilon s}{\sqrt r+\delta}g
      $$

反向传播

  • 引入:下面是一个线性拟合的例子,$w$和$b$各自随机化一个初始值,通过损失函数去反向更新$w$和$b$的值。根据上面的梯度下降算法,我们需要先计算损失函数$L$对$w$和$b$的梯度值(偏导数),为了方便计算,先计算$L$对$y$的偏导,然后通过链式法则去计算$L$对$w$和$b$的偏导。然后沿着梯度的反方向更新$w$和$b$的值。沿着黄色箭头从后往前计算参数梯度值的方法就是反向传播

    反向传播实际就是神经网络中加速计算参数梯度值的方法

    image-20230916154220136
  • 计算机中的模块化计算(computational graph, 计算图):

    下面是正向计算图,输入$x$、$w_1$、$b_1$,输出损失函数$L$的值

    image-20230916154558833

    下面是反向传播计算图,

    image-20230916154730452
  • 代码实现

    image-20230916155110042
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Multiply(torch.autograd.Function):
    @staticmethod
    def forward(ctx, x, y):
    ctx.save_for_backward(x,y)
    z = x * y
    return z

    @staticmethod
    def backward(ctx, x, y):
    x, y = ctx.save_tensors
    grad_x = grad_z * y
    grad_y = grad_z * x
    return grad_x, grad_y

激活函数

  • 神经网络的矩阵乘法只是线性变换,为了解决非线性问题,使用非线性函数进行激活,这就是激活函数

  • 激活函数的条件:

    1. 为了能反向传播,一定要连续可导
    2. 能映射所有实数
    3. 激活函数只是增加非线性,不改变对输入的响应状态,应该单调递增
  • 常用的激活函数

    1. sigmoid函数:$\sigma=\frac{1}{1+e^{-y}}$,导数最大值是0.25,意味着反向更新权重时,每层梯度会至少缩小$1/4$,导致最前面几层梯度几乎为0,权重几乎不更新,这就是梯度消失现象

      image-20230916160627746
    2. tanh函数:$tanh=\frac{1-e^{-y}}{1+e^{-y}}$,优于sigmoid函数,但也存在梯度消失的问题

      image-20230916161057745
    3. ReLU函数:$ReLU(y)=\left{
      \begin{matrix}
      y,y\geq0 \
      0,y\leq0
      \end{matrix}
      \right.$,函数没有上限,梯度累积可能过大导致梯度爆炸

      具有稀疏性,信息耦合程度低,表现更好

      image-20230916161247649

      为了避免函数值等于零导致神经元坏死,出现了Leaky ReLU和Parametric ReLU…


本着互联网开源的性质,欢迎分享这篇文章,以帮助到更多的人,谢谢!