您的位置:首页 > 理论基础 > 计算机网络

详解神经网络中的反向传播

2017-02-23 17:53 393 查看

原理

反向传播在神经网络中非常重要,是现在热门的深度学习的基础.所谓的反向传播,本质上是一种在一个邻域内求多元函数局部极大值(极小值)的方法.以求极大值为例,反向传播过程是先求出多元函数在某一点的梯度.然后沿梯度方向,更新各个自变量,使函数值变大.重复这个过程,就可以得到函数的局部极大值.

解释

下面是当年高等数学书上的一个例题.看看会不会做例4,例5.如果不会做请回去翻翻高等数学的书.一定要看明白梯度的含义和计算方法.梯度是神经网络要用到的最基本也是最重要的概念.另外简单的复合函数求偏导也要确保会求,求梯度要用到.



再直观地解释下梯度的含义.首先,梯度是一个向量,这个向量的方向是函数增加最快的方向.

\[
\begin{equation}\label{simple}
z=-x^2-y^2
\end{equation}
\]

对于函数\ref{simple},\(z\)是函数值,\(x\),\(y\)是自变量.\(z\)在任意一点的梯度为

\[
\nabla z(x,y)=(\frac{\partial z}{\partial x},\frac{\partial z}{\partial y})=(-2x,-2y)
\]

在点(1,1,-2)处,\(z\)的梯度是(-2,-2),含义是在点(1,1,-2)沿向量(-2,-2)确定的方向上增加\(x\),\(y\),那么\(z\)增加最快.如何理解这个"增加最快"?通俗地理解,一个人一步可以走75cm,现在某人站在一个山坡上,他想尽快的上山,那么他应该向哪个方向走75cm?肯定是上山的方向.但上山的方向是人的直观感觉,在数学上这个直观感觉的方向就是梯度的方向.下面以函数\(z\)为例用代码解释这个问题.

# coding:utf-8
import math
step = 0.01  # 步长

def func_z(x, y):
return -x * x - y * y

def direction(x, y):
model = math.sqrt(x * x + y * y)
x /= model
y /= model
return x * step, y * step

x = 1
y = 1

x1, y1 = direction(-2, -2)  # 指定增加的方向为梯度方向(-2,-2),步长是step
print func_z(x + x1, y + y1) - func_z(x, y)  # 0.0281842712475
x2, y2 = direction(-2, -3)  # 指定增加的方向为(-2, -3),步长是step
print func_z(x + x2, y + y2) - func_z(x, y)  # 0.0276350098113
x3, y3 = direction(2, 7)  # 指定增加的方向为(2, 7),步长是step
print func_z(x + x3, y + y3) - func_z(x, y)  # -0.0248249015108

上面的代码,分别沿梯度方向(-2,-2),向量(-2, -3)确定的方向,向量(2, 7)确定的方向增加相同的步长step,沿梯度方向的增加\(x\),\(y\)后\(z\)变化

最多.可以用下面的代码,利用反向传播求\(z\)的极大值.

# coding:utf-8
study = 0.00001

def func_z(x, y):
return -x * x - y * y

x = 1
y = 1
for i in range(20000):
dx = -2 * x * study
dy = -2 * y * study
x += dx
y += dy
if i % 2000 == 0:
print func_z(x, y)

代码的输出为

-1.9999200008
-1.84615736726
-1.70421668033
-1.57318901682
-1.45223533558
-1.3405811046
-1.23751134129
-1.14236603408
-1.05453591597
-0.973458563109

上面的代码就是反向传播最简单的例子.通过沿梯度方向增加\(x\),\(y\),使\(z\)的值不断增大.其中变量study叫做学习率,用于调节步长,防止自变量增加过大越过最大值.这个例子虽然简单,但非常重要.神经网络求损失函数极值的方法本质上和这个例子一样.

应用

现在有如下问题.已知

\[
\begin{equation*}
A
=\left[
\begin{array}{cc}
1 & 2.1 \\
3 & 5.1 \\
6 & 11.5
\end{array}
\right]
\end{equation*}
\]

\[
\begin{equation*}
W
=\left[
\begin{array}{c}
w_1 \\
w_2
\end{array}
\right]
\end{equation*}
\]

\[
\begin{equation*}
B
=\left[
\begin{array}{c}
1 \\
2 \\
3
\end{array}
\right]
\end{equation*}
\]

现在求\(w_1\),\(w_2\),使\(AW\)尽可能地接近\(B\).先尝试用数学上的方法精确地求解.即求下面方程组的解.

\[
\begin{equation*}
\begin{cases}
w_1+2.1w_2=1\\
3w_1+5.1w_2=2 \\
6w_1+11.5w_2=3
\end{cases}
\end{equation*}
\]

这个方程无解.所以就不能用数学方法求出精确值.但可以用反向传播来解决这个问题.要使\(AW\)尽可能地接近\(B\),这个问题可以转化为求\(E\)的极小值.

\[
\begin{equation*}
E=\frac{1}{2}\left[
(w_1+2.1w_2-1)^2+(3w_1+5.1w_2-2)^2+(6w_1+11.5w_2-3)^2
\right]
\end{equation*}
\]

\(E\)是大于等于0的.\(E\)越小,\(AW\)就越接近\(B\).理想情况是等于0.可以求出

\[
\begin{equation*}
\frac{\partial E}{\partial w_1}=(w_1+2.1w_2-1)+3(3w_1+5.1w_2-2)+6(6w_1+11.5w_2-3)
\end{equation*}
\]

\[
\begin{equation*}
\frac{\partial E}{\partial w_2}=2.1(w_1+2.1w_2-1)+5.1(3w_1+5.1w_2-2)+11.5(6w_1+11.5w_2-3)
\end{equation*}
\]

之后就能用反向传播的方法不断调整\(w_1\),\(w_2\),让\(E\)不断减小.代码如下:

# coding:utf-8
import numpy as np

study = 0.001

A = np.array([[1, 2], [3, 5.1], [6, 11.5]])
W = np.array([0.5, 0.5])
B = np.array([1, 2, 3])

def E(A, W):
a = A.dot(W.T) - B
return np.sum(a * a) * 0.5

def gradient(A, W):
return A.T.dot(A.dot(W.T) - B)

for i in range(100000):
loss = E(A, W)
if i % 1000 == 0:
print("i=%d,loss=%f" % (i, loss))
W -= gradient(A, W) * study
print(W)

运行这个代码可以看到loss不断减小.需要注意的是,反向传播求的是局部最优解.最终结果的好坏依赖于所选的初始值.

参考资料

同济大学数学系, 高等数学第七版(下册),高等教育出版社, pp54-111, 2014.

http://cs231n.github.io/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: