一、反向传播算法
上一篇中已经知道了神经网络的正向计算,那本篇将学习神经网络训练中非常重要的反向传播。
神经网络的每个连接上的权值如果知道,那么就可以将输入数据代入得到希望的结果。
神经网络是一个模型,那么这些权值就是模型的参数,也就是模型要学习的东西。
反向传播算法其实就是链式求导法则的应用。
按照机器学习的通用求解思路,我们先确定神经网络的目标函数,然后用随机梯度下降优化算法去求目标函数最小值时的参数值。
本篇会涉及到非常多的公式推导,比较枯燥无趣,在这里也就不照搬公式了,有兴趣的同学可以去学习反向传播的数学推导过程。
这里直接给出反向传播的关键的理论公式。
1)目标函数
其中, Ed 表示是样本 d 的误差, t是样本的标签值,y是神经网络的输出值。
目标函数是用来求梯度,然后根据梯度来更新模型参数的基础。
使用随机梯度下降算法对目标函数进行优化:
最后,推导出的权重w的最终的公式如下图:
其中, wji 是节点 i 到节点 j 的权重, η 是一个成为学习速率的常数, δj 是节点 j 的误差项, xji 是节点 i 传递给节点 j 的输入。
对于输出层来说,
其中, δi 是节点 i 的误差项, yi 是节点 i 的输出值, ti 是样本对应于节点 i 的目标值。
对于隐藏层来说:
其中, ai 是节点 i 的输出值, wki 是节点 i 到它的下一层节点 k 的连接的权重, δk 是节点 i 的下一层节点 k 的误差项。
由公式4可知,计算一个节点的误差项,需要先计算每个与其相连的下一层节点的误差项,这就要求误差项的计算顺序必须是从输出层开始,然后反向依次计算每个隐藏层的误差项,直到与输入层相连的那个隐藏层,这就是反向传播算法的名字的含义。
二、神经网络算法实现
1、数据准备
import numpy as np
from sklearn import datasets, linear_model
import matplotlib.pyplot as plt
from sklearn.metrics import accuracy_score
# generate sample data
np.random.seed(0)
x, y = datasets.make_moons(200, noise=0.20)
y_true = np.array(y).astype(float)
# generate nn output target
t = np.zeros((x.shape[0], 2))
print(x.shape[0])
t[np.where(y==0), 0] = 1
t[np.where(y==1), 1] = 1
# plot data
plt.scatter(x[:, 0], x[:, 1], c=y, cmap=plt.cm.Spectral)
plt.show()
2、神经网络训练程序
首先生成神经网络模型,初始化权重数据,接着定义sigmoid和网络正向计算函数,具体如下:
# generate the NN model
class NN_Model:
epsilon = 0.01 # learning rate
n_epoch = 1000 # iterative number
nn = NN_Model()
nn.n_input_dim = x.shape[1] # input size
nn.n_hide_dim = 8 # hidden node size
nn.n_output_dim = 2 # output node size
# initial weight array
nn.W1 = np.random.randn(nn.n_input_dim, nn.n_hide_dim) / np.sqrt(nn.n_input_dim)
nn.b1 = np.zeros((1, nn.n_hide_dim))
nn.W2 = np.random.randn(nn.n_hide_dim, nn.n_output_dim) / np.sqrt(nn.n_hide_dim)
nn.b2 = np.zeros((1, nn.n_output_dim))
# define sigmod & its derivate function
def sigmod(x):
return 1.0/(1+np.exp(-x))
# network forward calculation
def forward(n, x):
n.z1 = sigmod(x.dot(n.W1) + n.b1)
n.z2 = sigmod(n.z1.dot(n.W2) + n.b2)
return n
# 使用随机权重进行训练
forward(nn, x)
y_pred = np.argmax(nn.z2, axis=1)
# plot data
plt.scatter(x[:, 0], x[:, 1], c=y_pred, cmap=plt.cm.Spectral)
plt.show()
程序中使用了未经训练的随机权重进行了预测,结果是所有的数据的分类结果都是同一类,接下来使用反向传播来进行训练,然后再看预测结果:
# 反向传播
def backpropagation(n, x, t):
for i in range(n.n_epoch):
# 正向计算每个节点的输出
forward(n, x)
# print loss, accuracy
L = np.sum((n.z2 - t)**2)
y_pred = np.argmax(nn.z2, axis=1)
acc = accuracy_score(y_true, y_pred)
if i % 100 == 0:
print("epoch [%4d] L = %f, acc = %f" % (i, L, acc))
# 计算误差
d2 = n.z2*(1-n.z2)*(t - n.z2)
d1 = n.z1*(1-n.z1)*(np.dot(d2, n.W2.T))
# 更新权重
n.W2 += n.epsilon * np.dot(n.z1.T, d2)
n.b2 += n.epsilon * np.sum(d2, axis=0)
n.W1 += n.epsilon * np.dot(x.T, d1)
n.b1 += n.epsilon * np.sum(d1, axis=0)
nn.n_epoch = 2000
backpropagation(nn, x, t)
# plot data
y_pred = np.argmax(nn.z2, axis=1)
plt.scatter(x[:, 0], x[:, 1], c=y, cmap=plt.cm.Spectral)
plt.title("ground truth")
plt.show()
plt.scatter(x[:, 0], x[:, 1], c=y_pred, cmap=plt.cm.Spectral)
plt.title("predicted")
plt.show()
真实分类,和预测的结果如下:
预测精度:
93.5%
|