神经网络中的反向传播法

直观理解反向传播法

1337511-20190807182552630-1507062128.png
1337511-20190807182610664-1663567029.png
1337511-20190807182626191-826961423.png
1337511-20190807182630890-986867121.png
1337511-20190807182636955-1266134474.png
1337511-20190807182640785-354616127.png
1337511-20190807182646001-390462041.png
1337511-20190807182650803-322489465.png
1337511-20190807182658360-952246483.png
1337511-20190807182704074-1885104832.png
1337511-20190807182709320-1152163154.png
1337511-20190807182715604-1969459542.png
1337511-20190807182721648-555081578.png
1337511-20190807182726564-1071272023.png
1337511-20190807182731686-1351291060.png
1337511-20190807182737254-476221326.png
1337511-20190807182741109-1185165431.png
1337511-20190807182745720-198179682.png
1337511-20190807182750114-1193081244.png


反向传播算法其实就是链式求导法则的应用。按照机器学习的通用套路,我们先确定神经网络的目标函数,然后用随机梯度下降优化算法去求目标函数最小值时的参数值。

反向传播算法

损失函数与正则化项

假设我们有一个固定样本集\(\{(x^{(1)},y^{(1)}),···,(x^{(m)},y^{(m)})\}\)它包含m个样本。我们可以用批量梯度下降法来求解神经网络。具体来讲,对于单个样例(x,y),其代价函数为:\[J(W,b;x,y)=\frac{1}{2}||h_{W,b}{(x)}-y||^2\]
这是一个平方误差损失函数。(这里的\(\frac{1}{2}\)是当求导时,平方会产生一个2,\(\frac{1}{2}*2=1\)进行平均不让2累积)
对于包含m个样本的数据集,我们可以定义整体的损失函数为:\[J\left(W,b\right)=\left[\frac{1}{m}\sum_{i=1}^m{J\left(W,b;x^{\left(i\right)},y^{\left(j\right)}\right)}\right]+\frac{\lambda}{2}\sum_{l=1}^{n_l-1}{\sum_{i=1}^{s_l}{\sum_{j=1}^{s_{l+1}}{\left(W_{ji}^{\left(l\right)}\right)^2}}} \\ =\left[\frac{1}{m}\sum_{i=1}^m{\frac{1}{2}}\parallel h_{W,b}\left(x^{\left(i\right)}\right)-y^{\left(i\right)}\parallel^2\right]+\frac{\lambda}{2}\sum_{l=1}^{n_l-1}{\sum_{i=1}^{s_l}{\sum_{j=1}^{s_{l+1}}{\left(W_{ij}^{\left(l\right)}\right)^2}}}\]
以上关于\(J(W,b)\)定义中的第一项是均方误差项,第二项是一个正则化项,也叫权重衰减项,其目的就是减小权重的幅度,防止过度拟合。权重衰减参数λ用于控制公式中两项的相对重要性。(关于权重衰减部分,写在文尾)

算法讲解

链式法则,具体如下图所示:
链式法则
我们定义整体损失函数为:\[L\left(\theta\right)=\sum_{n=1}^N{C^n\left(\theta\right)}\]
对参数\(w\)求偏导:\[\frac{\partial L\left(\theta\right)}{\partial w}=\sum_{n=1}^N{\frac{\partial C^n\left(\theta\right)}{\partial w}}\]
因此我们只需要求出单个样例的偏导数,就可以推导出整体损失函数的偏导数。根据链式法则,对于某一个节点,如下所示:
1337511-20190827171139459-704898292.png
\[\frac{\partial C}{\partial w}=\frac{\partial z}{\partial w}\frac{\partial C}{\partial z}\]
容易得到\(\partial z/\partial w_1=x_1 \qquad \partial z/\partial w_2=x_2\)
我们可以利用前向传导的方法计算出所有层的\(\frac{\partial z} {\partial w}\)
1337511-20190827171344826-131919036.png
我们已经求出了整个偏导数的左半部分,接下来看右半部分,即\(\frac{\partial C}{\partial z}\)
1337511-20190827171410025-1289700962.png
根据链式法则得到:\[\frac{\partial C}{\partial z}=\frac{\partial a}{\partial z}\frac{\partial C}{\partial a}\]
对于\(\frac{\partial a}{\partial z}\),我们知道就是激活函数对加法器的偏导,知道了激活函数便知道了\(\frac{\partial a}{\partial z}\),我们设其求导结果为\(\partial ' (z)\),因为z在前向传播中已经确定,所以\(\partial ' (z)\)其实是一个常数。接下来看\(\frac{\partial C}{\partial a}\).
1337511-20190827172058687-1287063657.png
根据链式求导法则\[\frac{\partial C}{\partial a}=\frac{\partial z'}{\partial a}\frac{\partial C}{\partial z'}+\frac{\partial z''}{\partial a}\frac{\partial C}{\partial z''}\]
易知\(\frac{\partial z’}{\partial a}\)即为权值,而\(\frac{\partial C}{\partial z’}\)假设其已知,则我们可以得到\[\frac{\partial C}{\partial z}=\sigma '\left(z\right)\left[w_3\frac{\partial C}{\partial z'}+w_4\frac{\partial C}{\partial z''}\right]\]
而对于\(\frac{\partial C}{\partial z}\)的求导,我们需要区分输出层和隐藏层两种情况:
第一种情况,如果还处于隐藏层,我们可以根据上述算法不断递归的计算\(\frac{\partial C}{\partial z}\),直到抵达输出层。
1337511-20190827172442092-1157179557.png
第二种情况,如果已经是输出层了,如下图所示
1337511-20190827172616037-1220716095.png

最后总结一下,我们根据前向传播算法求得所有的\(\frac{\partial z}{\partial w}\),根据反向传播算法求得所有的\(\frac{\partial C}{\partial z}\)(需要用到前向传播算法求得的\(\frac{\partial a}{\partial z}\),即\(\sigma ' \left(z\right)\))。这样就可以用更新公式对参数进行迭代更新了。
1337511-20190827172804442-996943055.png


权重衰减

目的:让权重衰减到更小的值,在一定程度上减少模型过拟合的问题。权重衰减也叫L2正则化。
L2正则化就是在代价函数后面再加上一个正则化项:\[C = C_0+\frac{\lambda}{2n}\underset{w}\sum w^2\]
其中C0代表原始的代价函数,后面那一项就是L2正则化项,它是这样来的:所有参数w的平方的和,除以训练集的样本大小n。λ就是正则项系数,权衡正则项与C0项的比重。另外还有一个系数1/2,1/2经常会看到,主要是为了后面求导的结果方便,后面那一项求导会产生一个2,与1/2相乘刚好凑整为1。系数λ就是权重衰减系数
我们对加入L2正则化后的代价函数进行推导,先求导:\[\frac{\partial C}{\partial w} = \frac{\partial C_0}{\partial w}+\frac{\lambda}{n}w \\ \frac{\partial C}{\partial b} = \frac{\partial C_0}{\partial b}\]
可以发现L2正则化项对b的更新没有影响,但是对于w的更新有影响:\[w \rightarrow w-\eta\frac{\partial C_0}{\partial w} - \frac{\eta \lambda}{n}w \\ =(1-\frac{\eta \lambda}{n})w-\eta\frac{\partial C_0}{\partial w}\]
\(\eta\)学习率,学习率是梯度下降时控制我们每次靠近真实值的幅度。在不使用L2正则化时,求导结果中w前系数为1。现在w前面系数为\(1-\frac{\eta \lambda}{n}\),因为\(\eta,\lambda,n\)都为正,所以\(1-\frac{\eta \lambda}{n}\)小于1,效果是减小w,这也就是权重衰减(weight decay)的由来。当然考虑到后面的导数项,w最终的值可能增大也可能减小。
对于基于mini-batch的随机梯度下降,w和b更新的公式跟上面给出的有点不同:\[w \rightarrow (1-\frac{\eta \lambda}{n})w-\frac{\eta}{m}\underset{x}\sum\frac{\partial C_x}{\partial w} \\ b \rightarrow b-\frac{\eta}{m}\underset{x}\sum\frac{\partial C_x}{\partial w}\]
对比上面w的更新公式,可以发现后面那一项变了,变成所有导数加和,乘以η再除以m,m是一个mini-batch中样本的个数。
思考:L2正则化项有让w变小的效果,但是为什么w变小可以防止过拟合呢?
原理:1. 从模型的复杂度上解释:更小的权值w,从某种意义上说,表示网络的复杂度更低,对数据的拟合更好(这个法则也叫做奥卡姆剃刀),而在实际应用中,也验证了这一点,L2正则化的效果往往好于未经正则化的效果。

  1. 从数学方面的解释:过拟合的时候,拟合函数的系数往往非常大,为什么?如下图所示,过拟合,就是拟合函数需要顾忌每一个点,最终形成的拟合函数波动很大。在某些很小的区间里,函数值的变化很剧烈。这就意味着函数在某些小区间里的导数值(绝对值)非常大,由于自变量值可大可小,所以只有系数足够大,才能保证导数值很大。而正则化是通过约束参数的范数使其不要太大,所以可以在一定程度上减少过拟合情况。
    1337511-20190828113950424-488610882.png

学习率衰减(learning rate decay)

在训练模型的时候,通常会遇到这种情况:我们平衡模型的训练速度和损失(loss)后选择了相对合适的学习率(learning rate),但是训练集的损失下降到一定的程度后就不在下降了。遇到这种情况通常可以通过适当降低学习率(learning rate)来实现。但是,降低学习率又会延长训练所需的时间。
学习率衰减(learning rate decay)就是一种可以平衡这两者之间矛盾的解决方案。学习率衰减的基本思想是:学习率随着训练的进行逐渐衰减。
学习率衰减基本有两种实现方法:

  • 线性衰减。例如:每过5个epochs学习率减半。
  • 指数衰减。例如:随着迭代轮数的增加学习率自动发生衰减,每过5个epochs将学习率乘以0.9998。\(decayed_learning_rate=learning_rate*decay_rate^(global_step/decay_steps)\),其中decayed_learning_rate为每一轮优化时使用的学习率,learning_rate为事先设定的初始学习率,decay_rate为衰减系数,decay_steps为衰减速度。

引用:

  1. https://www.zhihu.com/question/27239198/answer/140093887
  2. https://plushunter.github.io/2017/04/10/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E7%B3%BB%E5%88%97%EF%BC%881%EF%BC%89%EF%BC%9A%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%8E%E5%8F%8D%E5%90%91%E4%BC%A0%E5%AF%BC%E7%AE%97%E6%B3%95/
  3. https://blog.youkuaiyun.com/program_developer/article/details/80867468

转载于:https://www.cnblogs.com/xym4869/p/11316589.html

<think>好的,用户想了解神经网络反向传播的原理和实现。我之前已经处理过类似的问题,所以得先回顾一下之前的回答和相关引用,确保这次回答的准确性和连贯性。 首先,用户提到了反向传播,我需要解释清楚它的基本原理和步骤。根据引用[3],反向传播的核心是梯度下降,用于最小化总误差。可能需要从损失函数开始讲起,然后链式则的应用。 然后,前向传播是反向传播的基础,如引用[2]提到的,前向传播计算各层的输出,直到得到预测值。这部分需要先简要介绍,让读者有基础。 接下来,反向传播的具体步骤。引用[4]中的例子,计算总误差,然后逐层反向计算梯度,更新权重和偏置。可能需要详细说明如何计算输出层和隐藏层的梯度,特别是链式求导的过程。 数学公式方面,用户要求行内用$...$,独立公式用$$...$$。例如,损失函数用均方误差的话,公式要正确,比如$E = \frac{1}{2}\sum (y - \hat{y})^2$。链式中的偏导数也要正确表达,比如∂E/∂w的分解。 实现部分,用户可能需要代码示例。考虑到Python的通用性,用numpy实现一个简单的神经网络,包含前向和反向传播。代码结构要清晰,注释明确,比如初始化参数、前向计算、损失计算、反向梯度计算和参数更新。 引用部分需要自然插入,比如在解释梯度下降时引用[3],提到前向传播时引用[2],举例误差计算时引用[4]。注意引用标识的位置,确保每个引用对应正确的内容。 用户可能的问题需要生成,比如梯度消失、与优化算的比较、实际应用中的挑战。这些问题要围绕反向传播的常见疑问展开,帮助用户深入理解。 最后,检查是否符合格式要求:行内公式正确,独立公式单独成段,中文回答,结构清晰。避免使用Markdown,用自然语言组织内容,同时确保LaTeX语正确。</think>### 神经网络反向传播原理与实现 #### 一、核心原理 反向传播(Backpropagation)是神经网络训练的核心算,通过**梯度下降**动态调整网络权重,最小化预测误差[^3]。其核心流程可分为三个阶段: 1. **前向传播**:输入数据逐层计算输出,直至得到预测值 $$ \hat{y} = f(W^{(n)} \cdot f(W^{(n-1)} \cdots f(W^{(1)}x + b^{(1)}) \cdots ) + b^{(n)}) ) $$ 其中$W$为权重矩阵,$b$为偏置,$f$为激活函数[^2] 2. **误差计算**:使用损失函数量化预测偏差,例如均方误差 $$ E = \frac{1}{2}\sum_{i=1}^{k}(y_i - \hat{y}_i)^2 $$ 3. **反向传播**:通过链式则计算梯度,从输出层逐层回传误差信号 $$ \frac{\partial E}{\partial w_{ij}} = \frac{\partial E}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z} \cdot \frac{\partial z}{\partial w_{ij}} $$ 其中$z$为神经元加权和[^4] --- #### 二、具体实现步骤(Python示例) ```python import numpy as np # 网络结构:2输入 → 3隐层 → 1输出 class NeuralNetwork: def __init__(self): # 初始化参数 self.W1 = np.random.randn(2,3) # 输入→隐层权重 self.b1 = np.zeros(3) # 隐层偏置 self.W2 = np.random.randn(3,1) # 隐层→输出权重 self.b2 = np.zeros(1) # 输出偏置 def sigmoid(self, x): return 1/(1+np.exp(-x)) def forward(self, X): # 前向传播 self.z1 = np.dot(X, self.W1) + self.b1 self.a1 = self.sigmoid(self.z1) self.z2 = np.dot(self.a1, self.W2) + self.b2 self.y_pred = self.sigmoid(self.z2) return self.y_pred def backward(self, X, y, lr=0.1): # 反向传播 m = X.shape[0] # 样本数 # 输出层梯度 dL_dz2 = (self.y_pred - y) * self.y_pred * (1 - self.y_pred) dW2 = np.dot(self.a1.T, dL_dz2)/m db2 = np.sum(dL_dz2, axis=0)/m # 隐层梯度 dL_da1 = np.dot(dL_dz2, self.W2.T) dL_dz1 = dL_da1 * self.a1 * (1 - self.a1) dW1 = np.dot(X.T, dL_dz1)/m db1 = np.sum(dL_dz1, axis=0)/m # 参数更新 self.W2 -= lr * dW2 self.b2 -= lr * db2 self.W1 -= lr * dW1 self.b1 -= lr * db1 # 使用示例 X = np.array([[0,0], [0,1], [1,0], [1,1]]) y = np.array([[0], [1], [1], [0]]) nn = NeuralNetwork() for epoch in range(10000): y_pred = nn.forward(X) nn.backward(X, y) ``` --- #### 三、关键实现细节 1. **激活函数选择**:Sigmoid函数保证可导性,其导数为$\sigma'(x) = \sigma(x)(1-\sigma(x))$ 2. **梯度计算顺序**:必须从输出层反向计算,利用链式则逐层分解偏导数[^4] 3. **学习率控制**:过大的学习率会导致震荡,过小则收敛缓慢 4. **批量处理**:示例中采用全批量梯度下降,实际常使用小批量(Mini-batch) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值