欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

理解并实现:前向传播和反向传播的基本机制

最编程 2024-07-24 22:31:50
...

「这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战」。

许多关于神经网络的书中,都会引入各种图的概念,以及并从计算图的角度出发介绍神经网络的原理,引入相当多的分支和运算。但对于简单的神经网络从宏观上,可能可以用一种更简洁的方式理解它的具体运行过程。

假设我们有一个这样的神经网络,它只有一个隐藏层和一个输出层。它们的权值数量,输入输出大小都先不去考虑。 我们从这个神经网络上考虑前向传播与反向传播的具体过程。


flowchart LR
    input([输入]) --> linear1[[隐藏层 f]] --> sigmoid1{{激活函数g}}-->linear2[[隐藏层 h]] --> sigmoid2{{激活函数J}}

这里把线性单元和激活函数分开了。出于简单考虑,假设激活函数都使用 sigmoid 函数

那么这其中只有两种元素,线性函数和 sigmoid 函数。 我们先不考虑函数的具体输入。

对于线性函数 f(x)=w×x+bf(x) = w \times x + b ,考虑它的梯度,对于它所在的层,在神经网络中它应该看作是关于 w,x,bw, x, b 的三元函数,对于其他的层而言,它则应该被看作是一个关于 xx 的一元函数。所以它的梯度分别是对wwxxbb 求偏导。具体如下:

  • 对于 x 有
fx=w\frac{\partial f }{\partial x}= w
  • 对于 w 则有
fw=x\frac{\partial f }{\partial w}= x
  • 对于 b 则有
fb=1\frac{\partial f }{\partial b}= 1

再来看所谓的激活函数 sigmoid ,它是一个一元函数,所以它的偏导数如下

gx=(1g(x))×g(x)\frac{\partial g }{\partial x}= (1-g(x))\times g(x)

现在我们考虑一下前向传播的过程。


隐藏层:f(x)=w×x+b f(x) = w \times x + b

第一次激活:g(f)=sigmoid(f)g(f) = sigmoid(f), 其中 xx 为上述的f(x)f(x)

输出层: h(g)=w×g+bh(g) = w \times g + b , 其中 xx 为上述的g(x)g(x)

第二次激活:J(h)=sigmoid(h)J (h) = sigmoid (h),其中 其中 hh 为上述的h(x)h(x)


这样写是有原因的,这点后续再表。现在我们得到了神经网络的输出,考虑计算一下它的准确程度,这里使用所谓的平方差损失函数,出于便利考虑,这里会添加一个系数。

Loss(J)=12×(tJ)2Loss(J) = \frac{1}{2} \times (t - J)^2

其中 t 是标签值

假设有误差,那么就需要我们想办法让它的值尽可能地减小,依据的原理就是梯度下降方法,也就是让函数的变量朝让损失函数减小的目标调整。

一个很经典的错误认识是过分关注 x 或者说输入这个部分,然而实际上神经网络中的参数都可以看作是函数中的变量,而输入实际上则是不可变的。也就是中间调整权值 ww 和偏置 bb 以尽量使得损失的值较小。

我们先考虑对输出层的调整,考虑调整它的权值。

梯度下降法告诉我们,数据应该向负梯度的方向移动,学习率 lr 在这里实际上不是重点,这里我们不关心它的值,尽管它相当重要。

一般来说,调整的方式下述过程。

w=wlr×Lww = w - lr\times \frac{\partial L }{\partial w}

这需要一个偏导数,我们尝试求解这个偏导数,这会涉及到链式法则的应用。

Lw=LJ×Jh×hw\frac{\partial L }{\partial w} = \frac{\partial L }{\partial J} \times \frac{\partial J }{\partial h} \times \frac{\partial h }{\partial w}

前面我们考虑过两种函数的梯度,没考虑的仅仅是损失函数。现在将三个偏导一同考虑一下

LJ=tJ\frac{\partial L }{\partial J } = t - J

损失函数对 J 求导,先前特别设置的系数就是为了偏导更简洁。

Jh=J(h)×(1J(h))\frac{\partial J }{\partial h} = J(h)\times(1-J(h))
hw=x\frac{\partial h }{\partial w} = x

sigmoidsigmoid 这个函数属于比较神奇的一类,一般来说,导数本身和函数是无关的,但有些函数比较特别,它的导数可以通过函数知道,有点类似 exe^x,尽管从数学上来讲它们不应该是有关系的,但是实际上它们却有一定的关系,这可以让我们省下一些计算的开销。

可以发现,对于输出层的权值而言,要调整它依赖前两个偏导的计算。 再来看偏置 b 的调整。公式如下:

w=wlr×Lbw = w - lr\times \frac{\partial L }{\partial b}

其实基本是一样的,向负梯度方向移动。

展开偏导数如下:

Lb=LJ×Jh×hb\frac{\partial L }{\partial b} = \frac{\partial L }{\partial J} \times \frac{\partial J }{\partial h} \times \frac{\partial h }{\partial b}

可以看到,对于这个一层的参数而言,它们都依赖后续的层提供的一部分值(两个偏导的乘积)。

为了更准确深度的理解,我们再来考虑隐藏层的权值的更新。

同样是遵从梯度下降的原则,也就是需要计算梯度来更新。

w=wlr×Lww = w - lr\times \frac{\partial L }{\partial w}

注意这里的 w 和上面的 w 不是指同一个。接下来同样是展开偏导式