人工神经网络这几年太火了,普遍用于预测、语音或图像识别。四五年前曾经想学习,但没有找到好的学习资料,一直一知半解的。最近一段时间因为要用 BP 神经网络做需求预测,就重新捡起来了。中文资料大部分的讲解不清晰(符号普遍比较混乱),于是结合维基百科以及查阅的几个英文资料,写下自己对这一算法的理解。
一、 神经网络算法的内涵
神经网络的目标是:找到一个能把一组输入最好地映射到其正确输出的函数。例如一个简单的分类任务,其中输入是动物的图像,正确的输出将是动物的名称。或者根据历史需求数据,预测未来一期的需求。
神经网络的思想(内涵)类似回归分析中经常用到的拟合,都用到了最小二乘的思想:数学意义上的决策目标是:选取一些参数(神经网络中每个输入的权重),使得拟合的输出与期望输出的误差平方和最小。
下面是一个神经网络示意图(输入层有 3个神经元,隐含层有 3个神经元,输出层有 3个神经元),输入信息经过正向传播到输出,计算实际输出与期望输出的误差后,在反向传播误差;重复这个过程,在传播过程中,不断减少误差,直到误差减少到一定程度终止。
误差反向传播,是该神经网络叫做 BP 神经网络的原因。
二、神经元之间信息的传递
每个神经元接受上一层所有神经元传递过来的信息,然后传递到下一层。接受信号时,按一定权重
w
i
j
w_{ij}
wij 接受信号。 下图是一个接受信号与传递信号的示意图:
w
i
j
w_{ij}
wij 表示从神经元
i
i
i 传递到神经元
j
j
j 时的权重(每个神经元的阈值
θ
j
\theta_j
θj 也能放在权重里表示,为了便于推算和理解,下面就不提阈值了),神经元
i
i
i 处传来的信息值大小为
o
i
o_i
oi(若神经元
i
i
i 在输入层,则
o
i
=
x
i
o_i=x_i
oi=xi),则每个神经元
j
j
j 的激活值 (activation value)
a
j
a_j
aj 等于:
a
j
=
∑
i
w
i
j
o
i
(1)
a_j=\sum_i w_{ij}o_i\tag{1}
aj=i∑wijoi(1)
神经元
j
j
j 有了激活值后,根据它的激活函数(或叫传递函数)
φ
\varphi
φ ,计算得到它的信息量大小
o
j
o_j
oj:
o
j
=
φ
(
a
j
)
=
φ
(
∑
i
w
i
j
o
i
)
(2)
o_j=\varphi(a_j)=\varphi(\sum_i w_{ij}o_i)\tag{2}
oj=φ(aj)=φ(i∑wijoi)(2)
常用的激活函数
φ
\varphi
φ 为 Sigmoid 函数,即:
φ
(
z
)
=
1
1
+
e
−
z
\varphi(z)=\frac{1}{1+e^{-z}}
φ(z)=1+e−z1
使用这个函数的一个重要原因是它的一阶导数方便求解,
∂
φ
∂
z
=
φ
(
z
)
(
1
−
φ
(
z
)
)
(3)
\frac{\partial\varphi}{\partial z}=\varphi(z)(1-\varphi(z))\tag{3}
∂z∂φ=φ(z)(1−φ(z))(3)
三、采用梯度下降法使误差减小
一般来说,神经元
j
j
j 产生的误差
E
E
E 这样定义:
E
j
=
1
2
∑
j
=
1
m
(
t
j
−
y
j
)
2
(4)
E_j=\frac{1}{2}\sum_{j=1}^m(t_j-y_j)^2\tag{4}
Ej=21j=1∑m(tj−yj)2(4)
其中 t j t_j tj 表示输出层的期望输出, y j y_j yj 表示输出层的实际输出,假设输出层有 m m m 个神经元。我们的目标是选取合适的权重 w i j w_{ij} wij,使得 E E E 最小,BP 神经网络一般采用梯度下降法逐渐更新权重(类似最优化中的最速下降法,参见本人另外一篇博客关于最速下降法的迭代公式:https://blog.csdn.net/robert_chen1988/article/details/53167156)。
因此,计算
E
E
E 对
w
i
j
w_{ij}
wij 的一阶导数:
∂
E
j
∂
w
i
j
=
∂
E
j
∂
y
j
∂
y
j
∂
w
i
j
\frac{\partial E_j}{\partial w_{ij}}=\frac{\partial E_j}{\partial y_j}\frac{\partial y_j}{\partial w_{ij}}
∂wij∂Ej=∂yj∂Ej∂wij∂yj
我们统一用
o
j
o_j
oj 表示
y
j
y_j
yj(
o
j
o_j
oj 表示输出的信息值,在最后的输出层,输出的信息值为
y
j
y_j
yj),则:
∂
E
j
∂
w
i
j
=
∂
E
j
∂
o
j
∂
o
j
∂
w
i
j
\frac{\partial E_j}{\partial w_{ij}}=\frac{\partial E_j}{\partial o_j}\frac{\partial o_j}{\partial w_{ij}}
∂wij∂Ej=∂oj∂Ej∂wij∂oj
根据公式 (2) 与公式 (3), 输出信息值
o
j
o_j
oj 又是激活值
a
j
a_j
aj 的函数,而激活值
a
j
a_j
aj 才与
w
i
j
w_{ij}
wij 有直接联系,因此:
∂
E
j
∂
w
i
j
=
∂
E
j
∂
o
j
∂
o
j
∂
w
i
j
=
∂
E
j
∂
o
j
∂
o
j
∂
a
j
∂
a
j
∂
w
i
j
(5)
\frac{\partial E_j}{\partial w_{ij}}=\frac{\partial E_j}{\partial o_j}\frac{\partial o_j}{\partial w_{ij}}=\frac{\partial E_j}{\partial o_j}\frac{\partial o_j}{\partial a_j}\frac{\partial{a_j}}{\partial w_{ij}}\tag{5}
∂wij∂Ej=∂oj∂Ej∂wij∂oj=∂oj∂Ej∂aj∂oj∂wij∂aj(5)
下面分开计算每一项,由公式(1):
∂
a
j
∂
w
i
j
=
∂
(
∑
i
w
i
j
o
i
)
∂
w
i
j
=
o
i
(6)
\frac{\partial{a_j}}{\partial w_{ij}}=\frac{\partial({\sum_i w_{ij}o_i})}{\partial w_{ij}}=o_i\tag{6}
∂wij∂aj=∂wij∂(∑iwijoi)=oi(6)
即这项导数是传递它信息的神经元的信息值。由公式 (3):
∂ o j ∂ a j = φ ( a j ) ( 1 − φ ( a j ) ) = o j ( 1 − o j ) (7) \frac{\partial{o_j}}{\partial a_j}=\varphi(a_j)(1-\varphi(a_j))=o_j(1-o_j)\tag{7} ∂aj∂oj=φ(aj)(1−φ(aj))=oj(1−oj)(7)
而计算 ∂ E ∂ o j \frac{\partial E}{\partial o_j} ∂oj∂E 则要分两种情况讨论:
- 若
o
j
o_j
oj 位于最后的输出层,则
o
j
=
y
j
o_j=y_j
oj=yj,根据公式(4)
∂ E ∂ o j = ∂ E ∂ y j = y j − t j (8) \frac{\partial E}{\partial o_j}=\frac{\partial E}{\partial y_j}=y_j-t_j\tag{8} ∂oj∂E=∂yj∂E=yj−tj(8)
根据公式 (5),(6)(7)(8),得到:
∂
E
j
∂
w
i
j
=
∂
E
j
∂
o
j
∂
o
j
∂
a
j
∂
a
j
∂
w
i
j
=
∂
E
j
∂
o
j
∂
o
j
∂
a
j
o
i
=
(
y
j
−
t
j
)
y
j
(
1
−
y
j
)
o
i
(9)
\frac{\partial E_j}{\partial w_{ij}}=\frac{\partial E_j}{\partial o_j}\frac{\partial o_j}{\partial a_j}\frac{\partial{a_j}}{\partial w_{ij}}=\frac{\partial E_j}{\partial o_j}\frac{\partial o_j}{\partial a_j}o_i=(y_j-t_j)y_j(1-y_j)o_i\tag{9}
∂wij∂Ej=∂oj∂Ej∂aj∂oj∂wij∂aj=∂oj∂Ej∂aj∂ojoi=(yj−tj)yj(1−yj)oi(9)
- 若
o
j
o_j
oj 位于输入层或隐含层, 神经元
j
j
j 输出值
o
j
o_j
oj 造成的误差等于它输出的所有神经元造成的误差之和 (本人的理解),设神经元
j
j
j 输出的神经元的集合为
L
L
L,因此得到:
∂ E j ∂ o j = ∑ l ∈ L ( ∂ E l ∂ o j ) = ∑ l ∈ L ∂ E l ∂ o l ∂ o l ∂ a l ∂ a l ∂ o j = ∑ l ∈ L ∂ E l ∂ o l ∂ o l ∂ a l w j l (10) \frac{\partial E_j}{\partial o_j}=\sum_{l\in L}(\frac{\partial E_l}{\partial o_j})=\sum_{l\in L}\frac{\partial E_l}{\partial o_l}\frac{\partial o_l}{\partial a_l}\frac{\partial{a_l}}{\partial o_{j}}=\sum_{l\in L}\frac{\partial E_l}{\partial o_l}\frac{\partial o_l}{\partial a_l}w_{jl}\tag{10} ∂oj∂Ej=l∈L∑(∂oj∂El)=l∈L∑∂ol∂El∂al∂ol∂oj∂al=l∈L∑∂ol∂El∂al∂olwjl(10)
这两种情况都满足下面式子:(只不过两种情况的
∂
E
∂
o
j
\frac{\partial E}{\partial o_j}
∂oj∂E 不同)
∂
E
j
∂
w
i
j
=
∂
E
j
∂
o
j
∂
o
j
∂
a
j
∂
a
j
∂
w
i
j
=
∂
E
j
∂
o
j
∂
o
j
∂
a
j
o
i
(11)
\frac{\partial E_j}{\partial w_{ij}}=\frac{\partial E_j}{\partial o_j}\frac{\partial o_j}{\partial a_j}\frac{\partial{a_j}}{\partial w_{ij}}=\frac{\partial E_j}{\partial o_j}\frac{\partial o_j}{\partial a_j}o_i\tag{11}
∂wij∂Ej=∂oj∂Ej∂aj∂oj∂wij∂aj=∂oj∂Ej∂aj∂ojoi(11)
为了方便,令
δ
j
=
∂
E
j
∂
o
j
∂
o
j
∂
a
j
\delta_j=\frac{\partial E_j}{\partial o_j}\frac{\partial o_j}{\partial a_j}
δj=∂oj∂Ej∂aj∂oj,则权重的一阶导数可以简化成:
∂
E
j
∂
w
i
j
=
δ
j
o
i
(12)
\frac{\partial E_j}{\partial w_{ij}}=\delta_j o_i\tag{12}
∂wij∂Ej=δjoi(12)
其中,
δ
j
\delta_j
δj 的取值与上面两种情况有关(根据表达式(9),(10)):
δ
j
=
{
(
y
j
−
t
j
)
y
j
(
1
−
y
j
)
若
j
位于输出层
(
∑
l
∈
L
∂
E
l
∂
o
l
∂
o
l
∂
a
l
w
j
l
)
o
j
(
1
−
o
j
)
=
(
∑
l
∈
L
δ
l
w
j
l
)
o
j
(
1
−
o
j
)
若
j
不位于输出层
(13)
\delta_j= \begin{cases} (y_j-t_j)y_j(1-y_j) \quad &若~j~位于输出层\\ \\ \left(\sum_{l\in L}\frac{\partial E_l}{\partial o_l}\frac{\partial o_l}{\partial a_l}w_{jl}\right)o_j(1-o_j)=\left(\sum_{l\in L}\delta_l w_{jl}\right)o_j(1-o_j)\quad &若~j~不位于输出层 \end{cases}\tag{13}
δj=⎩
⎨
⎧(yj−tj)yj(1−yj)(∑l∈L∂ol∂El∂al∂olwjl)oj(1−oj)=(∑l∈Lδlwjl)oj(1−oj)若 j 位于输出层若 j 不位于输出层(13)
于是,表达式(12),(13)就是权重相对于误差的一阶导数取值。BP 神经网络采用梯度下降法使得误差降低。
类似最速下降法的思想,BP 神经网络在迭代时,采用下面的方法更新权重,不断使得误差减小:
w
i
j
=
w
i
j
−
η
∂
E
j
∂
w
i
j
w_{ij}=w_{ij}-\eta\frac{\partial E_j}{\partial w_{ij}}
wij=wij−η∂wij∂Ej
其中 η \eta η 就是梯度下降法的步长,在神经网络算法中称为学习速率,而权重沿负梯度方向更新。
四、BP 神经网络的局限性
- 不能保证得到全局最优解
- 输入的数据不要求标准化,但若标准化,可以提高神经网络的表现。
五、一个 python 例子
用 BP 神经网络预测 sklearn 包中自带的乳腺癌数据例子:
## 测试一下癌症数据
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import classification_report
from sklearn.preprocessing import StandardScaler
cancer = datasets.load_breast_cancer()
cancer_data = cancer['data']
cancer_target = cancer['target']
cancer_data_train, cancer_data_test, cancer_target_train, \
cancer_target_test = train_test_split(cancer_data, cancer_target, test_size = 0.2)
# 数据标准化
stdScaler = StandardScaler().fit(cancer_data_train)
cancer_trainStd = stdScaler.transform(cancer_data_train)
cancer_testStd = stdScaler.transform(cancer_data_test)
# 建立 BP 模型
bpnn = MLPClassifier(hidden_layer_sizes = (20,10),
max_iter = 200, solver = 'adam',random_state=45)
bpnn.fit(cancer_trainStd, cancer_target_train)
# 预测
y_pred = bpnn.predict(cancer_testStd) # 返回预测结果
print('神经网络预测结果评价报告:\n', classification_report(cancer_target_test,y_pred))
生成的结果为:
预测的准确率达到 98%,跟支持向量机的预测结果差不多。
转载于个人公众号:Python 统计分析与数据科学