7.3 人工神经网络
人脑是一种强大的信息处理装置,在视觉、听觉、语言知识和学习方面是机器无法替代的。通过前面两节对回归问题的讲述,我们知道可以让机器“学习”使它拥有某种能力去为人类服务。而人脑与计算机最大的不同是计算机的处理器是有限的,而人脑包含着大量的神经元去传输信息。基于神经元的启发,科学家建立了一种新的运算模型,人工神经网络。神经网络是由大量的节点,或称为神经元,相互连接构成。信息经过输入层进入神经网络,在节点中不断进行信息传输与运算,最后到达输出层,得到最终处理后的信息。人工神经网络经过数据训练后,它就具有类似于人脑的能力,人工神经网络的研究使得“听歌识曲”,“图像识别”等应用得到高速发展。如果数据量足够用于训练和机器运算速度足够快的话,制造一个有人类智力的机器也是有可能的。
BP(Back Propagation)神经网络是一种处理分类和回归问题很有效的神经网络。本节我们重点介绍BP神经网络及其前向传播和反向传播的机制,相信通过本节的学习,读者能够对人工神经网络有一个快速的入门。
1.神经网络模型
图7-7展现了一个3层的神经网络。我们使用圆圈来表示神经网络的节点,标上“+1”的节点被称为偏置节点。第一层是网络的输入层,最后一层是输出层,其余的都称为隐藏层,图7-7只有一个隐藏层。我们可以看到输入层有3个输入单元(不包括偏置单元),3个隐藏单元和一个输出单元。
图7-7 神经网络模型
我们用nl 来表示网络的层数。本例中nl =3,我们将第l层记为Ll ,于是输入层记为L1 ,输出层记为Lnl ,我们用st 表示第l层的节点数。在本例中神经网络参数有(W,b)=(W(1) ,b(1) ,W(2) ,b(2) ), 表示第l层第j单元与第l+1层第i单元之间的连接系数(注意标号顺序), 是第l+1层第i单元的偏置项。在本例中,W(1) ∈ ,W(2) ∈ 。
2.前向传播
我们用 表示第l层第i单元的激活值。当l=1时, 。继续以图7-7的网络为例,给定参数集合(W,b)和激活函数f后,我们可以按照下面的公式计算第二层的激活值a(2):
我们用 表示第l层第i单元的加权和,如 ,则 。我们可以使用矩阵乘法对上面的过程进行简化:
具体地,在本例中:
hW,b (x)得出的结果就是输出层的输出。上面整个计算过程称为前向传播。设定参数矩阵和激活函数后,模型将信息一层层地从输入层往输出层传播,因此称为前向传播。常用的激活函数有下面几种,如表7-4所示。
表7-4 常用激活函数
我们一般以随机值初始化参数矩阵,后面我们将用数据训练网络。这是一个不断优化参数集合(W,b),使得在训练集处理的结果更优的过程。而BP神经网络训练参数的方法是反向传播。
3.反向传播
假设我们现有一个数据集{(x(1) ,y(1) ),…,(x(m) ,y(m) )},它包含了m个样本。我们首先设定代价函数,对于一个样例(x(i) ,y(i) ):
而对于整体代价函数我们定义为:
第一项表示残差平方和,第二项是正则化项,目的是为了防止权重过大以致过度拟合。这个代价函数经常用于分类和回归问题。在二分类问题中,我们用y=0和y=1代表两种类型的标签。我们可以在输出层使用sigmoid激活函数使得最终的输出结果在(0,1)之间,通过代价函数计算模型预测的误差。而对于回归问题,我们可以对真实的y值做一个变换,如使用sigmoid函数,使得样例y值的范围在(0,1)之间。接着输出层同样使用sigmoid激活函数,预测的y值也在(0,1)之间,最后使用代价函数计算误差。
有了代价函数,神经网络的任务就是使得“代价”尽量低。我们将使用梯度下降法 对参数(W,b)进行优化,每一步迭代更新(W,b)使得代价函数的值不断减少(如图7-8所示)。我们将使用W和b的偏导数对它们进行更新:
图7-8 梯度下降法示意图
其中α是学习效率,其中关键在于计算偏导数。而反向传播算法是计算偏导数的一种有效方法。由于篇幅限制,我们不讲解公式的具体推导,直接讲述反向传播算法的计算步骤和推导得到的计算公式。给定一个样本(x(i) ,y(i) ),反向传播算法可以分为下面几个步骤:
1)利用前向传播算法,得到L2 ,L3 ,…直到输出层 的激活值。
2)计算输出层(第nt 层)的残差:
3)计算l=nt -1,nt -2,…,2的各层的残差:
4)计算最终需要的偏导数值:
当样本数量为m时,我们批量梯度下降法的迭代如下:
1)初始化,对于所有l,令ΔW(l) =0,Δb(l) =0。
2)对i=1到m:
a)使用反向传播算法计算 和
b)
c)
3)更新权重参数:
a)
b)
反复进行上述迭代,减少代价函数J(W,b)的值,进而求解我们的神经网络。
4.进行实验
我们尝试使用BP神经网络进行实验。数据集采用scikit-learn提供的make_moons数据集。产生的数据如图7-9所示,“+”表示女性病人,“x”表示男性病人,x和y轴表示两个指标。将数据分为训练集和测试集后,使用训练集训练神经网络,将训练好的神经网络作用于测试集,得到预测的错误率。BP神经网络的代码见代码清单7-4,我们需要设置网络层数和每层的节点数,学习速率α,正则化系数λ,迭代次数。对于此数据集我们采用[2,3,1]的网络,输入的节点数是2个,设置一个有3个节点的隐藏层。α设为0.2,λ设为0.005,迭代次数设为10000。程序得到的结果为:训练集的错误率为0.159375,测试集的错误率为0.15。BP神经网络的分类效果很不错,如果提高迭代次数,分类的效果还可以进一步提高。
图7-9 make_moons数据集
代码清单7-4 BP神经网络
# BP神经网络 Python实现 import numpy as np from numpy import random import math import copy import sklearn.datasets import matplotlib.pyplot as plt # 获取数据并分为训练集与测试集 trainingSet, trainingLabels = sklearn.datasets.make_moons(400, noise=0.20) plt.scatter(trainingSet[trainingLabels==1][:,0], trainingSet[trainingLabels==1][:,1], s=40, c='r', marker='x',cmap=plt.cm.Spectral) plt.scatter(trainingSet[trainingLabels==0][:,0], trainingSet[trainingLabels==0][:,1], s=40, c='y', marker='+',cmap=plt.cm.Spectral) plt.show() testSet = trainingSet[320:] testLabels = trainingLabels[320:] trainingSet = trainingSet[:320] trainingLabels = trainingLabels[:320] # 设置网络参数 layer =[2,3,1] # 设置层数和节点数 Lambda = 0.005 # 正则化系数 alpha = 0.2 # 学习速率 num_passes = 20000 # 迭代次数 m = len(trainingSet) # 样本数量 # 建立网络 # 网络采用列表存储每层的网络结构 ,网络的层数和各层节点数都可以自由设定 b = [] # 偏置元 ,共 layer-1个元素 ,b[0]代表第一个隐藏层的偏置元 (向量形式 ) W = [] for i in range(len(layer)-1): W.append(random.random(size = (layer[i+1],layer[i]))) # W[i]表示网络第 i层到第 i+1层的转移矩阵 (NumPy数组 ),输入层是第 0层 b.append(np.array([0.1]*layer[i+1])) # 偏置元 b[i]的规模是 1*第 i+1个隐藏层节点数 a = [np.array(0)]*(len(W)+1) # a[0] = x,即输入 ,a[1]=f(z[0]),a[len(W)+1] = 最终输出 z = [np.array(0)]*len(W) # 注意 z[0]表示网络输入层的输出,即未被激活的第一个隐藏层 W = np.array(W) def costfunction(predict,labels): # 不加入正则化项的代价函数 # 输入参数格式为 numpy的向量 return sum((predict - labels)**2) def error_rate(predict,labels): # 计算错误率 ,针对二分类问题 ,分类标签为 0或 1 # 输入参数格式为 numpy的向量 error =0.0 for i in range(len(predict)): predict[i] = round(predict[i]) if predict[i]!=labels[i]: error+=1 return error/len(predict) def sigmoid(z): # 激活函数 sigmoid return 1/(1+np.exp(-z)) def diff_sigmoid(z): # 激活函数 sigmoid的导数 return sigmoid(z)*(1-sigmoid(z)) activation_function = sigmoid # 设置激活函数 diff_activation_function = diff_sigmoid # 设置激活函数的导数 # 开始训练 BP神经网络 a[0] = np.array(trainingSet).T # 这里一列为一个样本,一行代表一个特征 y = np.array(trainingLabels) for v in range(num_passes): # 前向传播 for i in range(len(W)): z[i] = np.dot(W[i],a[i]) for j in range(m): z[i][:,j]+=b[i] # 加上偏置元 a[i+1] = activation_function(z[i]) # 激活节点 predict = a[-1][0] # a[-1]是输出层的结果 ,即为预测值 # 反向传播 delta = [np.array(0)]*len(W) # delta[0]是第一个隐藏层的残差, delta[-1]是输出层的残差 # 计算输出层的残差 delta[-1] = -(y-a[-1])*diff_activation_function(z[-1]) # 计算第二层起除输出层外的残差 for i in range(len(delta)-1): delta[-i-2] = np.dot(W[-i-1].T,delta[-i-1])*diff_activation_function(z [-i-2]) # 这里是倒 # 序遍历 # 设下标 -i-2代表第 L层,则 W[-i-1]是第 L层到 L+1层的转移矩阵, delta[-i-1]是 # 第 L+1层的残差,而 z[-i-2]是未激活的第 L层 # 计算最终需要的偏导数值 delta_w = [np.array(0)]*len(W) delta_b = [np.array(0)]*len(W) for i in range(len(W)): # 使用矩阵运算简化公式 ,下面两行代码已将全部样本反向传播得到的偏导数值求 #和 delta_w[i] = np.dot(delta[i],a[i].T) delta_b[i] = np.sum(delta[i],axis=1) # 更新权重参数 for i in range(len(W)): W[i] -= alpha*(Lambda*W[i]+delta_w[i]/m) b[i] -= alpha/m*delta_b[i] print '训练样本的未正则化代价函数值 :',costfunction(predict,np.array(trainingLabels)) print '训练样本错误率 :',error_rate(predict,np.array(trainingLabels)) # 使用测试集测试网络 a[0] = np.array(testSet).T # 一列为一个样本,一行代表一个特征 # 前向传播 m = len(testSet) for i in range(len(W)): z[i] = np.dot(W[i],a[i]) for j in range(m): z[i][:,j]+=b[i].T[0] a[i+1] = activation_function(z[i]) predict = a[-1][0] print '测试样本的未正则化代价函数值 :',costfunction(predict,np.array(testLabels)) print '测试样本错误率 :',error_rate(predict,np.array(testLabels))
*代码详见:示例程序/code/7-3.py
5.其他神经网络
卷积神经网络 (Convolutional Neual Network,CNN)是一种前馈神经网络,与BP神经网络不同的是,它包括卷积层 (Alternating Convolutional Layer)和池层 (Pooling Layer),在图像处理方面有很好的效果,经常用作解决计算机视觉问题。递归神经网络 (RNN)分为时间递归神经网络 (Recurrent Neural Network)和结构递归神经网络 (Recursive Neural Network)。RNN主要用于处理序列数据。在BP神经网络中,输入层到输出层各层间是全连接的,但同层之间的节点却是无连接的。这种网络对于处理序列数据效果很差,忽略了同层间节点的联系,而在序列中同层间节点的关系是很密切的。CNN和RNN是深度学习中著名的两种结构,建议读者借助其他书籍熟悉它们。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论