博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
郑捷《机器学习算法原理与编程实践》学习笔记(第六章 神经网络初步)6.2 BP神经网络...
阅读量:5744 次
发布时间:2019-06-18

本文共 14905 字,大约阅读时间需要 49 分钟。

  6.2.1 略

  6.2.2 BP网络的构成

  • 输入层:样本向量的维度
  • 激活函数:Logistic
  • 误差计算:实际分类-预测分类
  • 输出层:类别标签向量
  • 迭代公式

  如下图所示BP网络的基本结构,该结构分为以下几个部分:

  

  (1)输入层i

  • 输入向量:x = (x1,x2,x3,...,xn
  • 输入层与隐含层链接权值:wih

  输入层就是输入的数据集所构成上午向量集合。第一列偏置值为bi,第二列到最后一列的特征向量(分类标签除外)。输出层是输入层和权重的点积,然后该值与激活函数计算的结果。

   (2)隐含层h

  • 输入向量:hi=(hi1,hi2,hi3,...,hin
  • 输出向量:h0=(h01,h02,h03,...,h0n
  • 阀值:bh
  • 隐含层与输出层链接权值:who
  • 激活函数:Logistic函数

  隐含层可以是一层,也可以是多层,但在BP网络中一般不超过两层。它的输入是上一层的输出层和权重的点积标量,输出是该标量与激活函数的计算结果。

  (3)输出层o。

  • 输入向量:yi=(yi1,yi2,yi3,...,yin
  • 输出向量:y0=(y01,y02,y03,...,y0n
  • 阀值:bo

  输出层只有一层,它的输入是上一层输出层和权重的点积标量,输出是该标量与激活函数的计算结果。

  (4)期望输出:do = (d1,d2,...,dn)就是分类的标签向量。

   6.2.3 BP网络的训练过程

  在流程,BP网络训练包括三个阶段。

  1.正向传播过程

  (1)输入函数:net = wTo+b

  (2)传递(激活)函数:f(net)=1/(1+e-net)

  

In [1]: def logistic(self,net):   ...:     return 1.0/(1.0+exp(-net))   ...:

  其中:

  

 

  2.计算期望与实际分类的误差

  误差向量:error=do-yo

  全局误差函数:

  全局误差函数的代码实现如下:

  

def errorfunc(self,inX):    return sum(power(inX,2))*0.5

  3.计算反向传播过程

   若正向传播过程未能得到期望的输出值,则逐层计算输出与期望输出的误差值,根据误差值调解权重。

  

  传递函数导函数的代码实现如下:

  

In [2]: def dlogit(self,net):   ...:     return multiply(net,(1.0-net))   ...:

  (1)输出层误差:计算误差反向传播的输出层的梯度和微分,用于更新输出层权值。

  输出层的微分可以写成如下的形式:

  

  下面我们将等式的右边的两项分别进行推导:

  其中左项推导得到:

  右项推导得到:

  将左右两项合在一起,得到:

    

   (2)隐含层误差:计算误差反向传播的隐含层的梯度和微分,用于更新隐含层权值。

  隐含层误差可以写成如下的形式:

  其中左项推导得到:

  

  右项推导得到:

  

  将左右两项合并到一起,得到:

  

  4.修正各层的权值

  利用各层的神经元的梯度和微分修正链接权值。

   6.3 BP网络的实现和评估

  6.3.1 BP网络类与主要方法

  BP网络类的基本结构如下:

  

class BPNet(object):    def __init__(self):                 #构造函数    def logistic(self,net):             #激活(传递)函数    def dlogit(self,net):               #激活(传递)函数的导函数    def errorfunc(self,inX):            #矩阵各元素平方之和    def normlize(self,dataMat):         #数据标准化归一化    def loadDataSet(self,filename):     #加载数据集    def addcol(self,matrixl,matrix2):   #增加新列    def init_hiddenWB(self):            #隐藏层初始化    def bpTrain(self):                  #BP网络主程序    def BPClassfier(self,start,end,steps = 30): #BP网络分类器    def classfyLine(self,plt,x,z):              #绘制分类线    def TrenLine(self,plt,color = 'r'):         #绘制趋势线,可调整颜色    def drawClassScatter(self,plt):             #绘制分类点

   1.设置网络初始化的基础参数

   

def __init__(self):                 #构造函数        #以下参数需要手工设置        self.eb = 0.01                  #误差容限,当误差小于这个值时,算法收敛,程序停止        self.iterator = 0               #算法收敛时的迭代次数        self.eta      = 0.1             #学习率,相当于步长        self.mc       = 0.3             #动量因子:引入一个调优参数,是主要的调优参数        self.maxiter  = 2000            #最大迭代次数        self.nHidden  = 4               #隐含层神经元        self.nOut     = 1               #输出层个数        #以下的属性由系统生成        self.errlist = []               #误差列表:保存了误差参数的变化用于评估收敛        self.dataMat = 0                #训练集        self.classLabels = 0            #分类标签        self.nSampNum     = 0            #样本集行数        self.nSampDim     = 0            #样本列数

  上述代码中的部分参数含义如下。

  (1)误差容限(eb)和迭代次数(iterator):当网络的实际分类与期望分类的误差的距离平方和小于这个数是,证明网络已经收敛,程序会自动跳出循环,此时的迭代次数就是网络实际的循环次数。

  (2)学习率(eta):相当于步长,这个值越大,要求迭代的次数就越少,但是越容易跳过最优值;这个值越小,要求迭代的次数越多。

  (3)动量因子(mc):用于网络调优,最早的BP算法在修正权值时,只按当前迭代的次数t的梯度调整,而不考虑t-1的梯度方向。这种方式经常会使网络发生震荡,收敛缓慢,难以调优。经过大量的实验,人们引入动量因子,该因子分配了t时刻和t-1时刻的梯度值,用以修正算法,以下是修正隐含层的权重代码:

  

self.hi_wb = self.hb_wb+(1.0-self.mc)*self.eta*dhi_wb+self.mc*dhi_wbOld

  其中,self.hi_wb是权重值:self.mc是动量因子;dhi_wb是t时刻的权重微分,dhi_wbOld是t-1时刻的权重微分。

  目前,动量项已经成为BP算法的标准参数,含有动量项的配置是BP算法的标准配置。

  (4)最大的迭代次数:BP网络的迭代次数

  (5)隐含层神经元:隐含层神经元个数的选择一般遵从以下法则

  • 隐含层设计。最常见的BP网络结构是三层的结构:一个输入层,一个输出层和一个隐含层。有理论证明:单隐含层BP网络可以映射所有连续函数,只有训练离散函数是才需要一个以上的隐含层,因此BP网络的隐含层最多不超过两层。实际训练时,就必须依靠再增加隐含层达到最优。
  • 隐含层节点上午设计。隐含层节点数的设计比隐含层的设计难度更大,经验公式

  nNode = (m+n)1/2+a

  nNode表示节点数,其中n为输入层节点数;m为输出层节点数,a为1~10之间的常数。

   (6)输出层个数:该数目会根据结果自动调整。默认值1

def init_hiddenWB(self):            #隐藏层初始化        self.hi_w  = 2.0*(random.rand(self.nHidden,self.nSamDim)-0.5)        self.hi_b  = 2.0*(random.rand(self.nHidden,1)-0.5)        self.hi_wb = mat(self.addcol(mat(self.hi_w),mat(self.hi_b)))    def init_OutputWB(self):           #输出层初始化        self.out_w  = 2.0 * (random.rand(self.nOut,self.nHidden)-0.5)        self.out_b  = 2.0 * (random.rand(self.nOut,1)-0.5)        self.out_wb = mat(self.addcol(mat(self.out_w),mat(self.out_b)))

 

  6.3.3 辅助函数

  (1)加载数据集

def loadDataSet(self,filename):     #加载数据集        self.dataMat     = []        self.classLabels = []        fr               = open(filename)        for line in fr.readlines():            lineArr = line.strip().split()            self.dataMat.append([float(lineArr[0]),float(lineArr[1]),1.0])            self.classLabels.append(int(lineArr[2]))        self.dataMat = mat(self.dataMat)        m,n          = shape(self.dataMat)        self.nSampNum = m   #样本数量        self.nSampNum = n-1 #样本维度

  (2)数据集归一化

def normlize(self,dataMat):         #数据标准化归一化        [m,n] = shape(dataMat)        for i in xrange(n-1):            dataMat[:,i] = (dataMat[:,i]-mean(dataMat[:,i]))/(std(dataMat[:,i])+1.0e-10)        return dataMat

  (3)矩阵增加新列

def addcol(self,matrixl,matrix2):   #增加新列        [m1,n1] = shape(matrixl)        [m2,n2] = shape(matrix2)        if m1 != m2:            print "different row,can not merge matix"            return        mergMat               = zeros((m1,n1+n2))        mergMat[:,0:n]        = matrixl[:,0:n1]        mergMat[:,n1:(n1+n2)] = matrix2[:,0:n2]        return mergMat

  (4)绘制分类点

def drawClassScatter(self,plt):             #绘制分类点        i = 0        for mydata in self.dataMat:            if self.classLabels[i] == 0:                plt.scatter(mydata[0,0],mydata[0,1],c='blue',marker = 's' )            else:                plt.scatter(mydata[0,0],mydata[0,1],c='red',marker = 's' )

6.3.4 主函数

下面给出算法的主函数:

def bpTrain(self):                  #BP网络主程序        SampIn   = self.dataMat.T         #输入矩阵        expected = mat(self.classLabels)  #预测输出        self.init_hiddenWB()        self.init_OutputWB()        dout_wbOld = 0.0;dhi_wbOld = 0.0  #默认t-1权值        #主循环        for i in xrange(self.maxiter):            #1.工作信号正向传播            #1.1 信息从输入层到隐含层,这里使用了矢量计算,            # 计算的是整个样本集的结果,结果是4行307列的矩阵            hi_input  = self.hi_wb*SampIn            hi_output = self.logistic(hi_input)            hi2out    = self.addcol(hi_output.T,ones((self.nSampNum,1))).T            #1.2 从隐含层到输出层:结果是5行307列的矩阵            out_input = self.out_wb*hi2out            out_ouput = self.logistic(out_input)        #2.误差计算        err = expected-out_ouput        sse = self.errorfunc(err)        self.errlist.append(sse)        if sse <= self.eb:              #判断是否收敛至最优            self.iterator = i+1            break        #3.误差信号反向传播        DELTA   = multiply(err,self.dlogit(out_ouput)) #DELTA为输出层梯度        #delta为隐含层梯度        delta   = multiply(self.out_wb[:,:-1].T*DELTA,self.dlogit(hi_output))        dout_wb = DELTA*hi2out.T  #输出层权值微分        dhi_wb  = delta.SamIn.T   #隐含层权值微分        if i == 0:               #更新输出层和隐含层权值            self.out_wb = self.out_wb + self.eta*dout_wb            self.hi_wb  = self.hi_wb +self.eta*dhi_wb        else:            self.out_wb = elf.out_wb + (1.0-self.mc)*self.eta*dout_wb+self.mc*dout_wbOld            self.hi_wb = elf.hi_wb + (1.0-self.mc)*self.eta*dhi_wb+self.mc*dhi_wbOld        dout_wbOld = dout_wb        dhi_wbOld  = dhi_wb

 分类曲面由两个权重向量确定:self.hi_wb和self.out_wb

评估参数由一个向量确定:self.errlist

6.3.5  分类器

(1)分类器函数

 

def BPClassfier(self,start,end,steps = 30): #BP网络分类器        x  = linspace(start,end,steps)        xx = mat(ones(steps,steps))        xx[:,0:steps] = x        yy = xx.T        z = ones((len(xx),len(yy)))        for i in range(len(xx)):            for j in range(len(yy)):                xi = []                tauex = []                tautemp = []                mat(xi.append([xx[i,j],yy[i.j],1]))                hi_input = self.hi_wb*(mat(xi).T)                hi_out = self.logistic(hi_input)                taumrow,taucol = shape(hi_out)                tauex = mat(ones((1,taumrow+1)))                tauex[:,0:taumrow] = (hi_out.T)[:,0:taumrow]                out_input = self.out_wb*(mat(tauex).T)                out = self.logistic(out_input)

(2)绘制分类结果

def classfyLine(self,plt,x,z):              #绘制分类线        plt.conour(x,x,z,1,colors = 'black')

(3)绘制误差曲线

def TrenLine(self,plt,color = 'r'):         #绘制趋势线,可调整颜色        X = linspace(0,self.maxiter,self.maxiter)        Y = log2(self.errlist)        plt.plot(X,Y,color)

6.3.6 执行分类并输出结果

 使用BP网络训练数据的主程序如下

 

if '__name__' == '__main__':    #数据集    bpnet = BPNet()    bpnet.loadDataSet("testSet2.txt")    bpnet.dataMat = bpnet.normlize(bpnet.dataMat)    #绘制数据集的散点图    bpnet.drawClassScatter(plt)    #BP神经网络进行数据分类    bpnet.bpTrain()    print  bpnet.out_wb    print  bpnet.hi_wb    #计算和绘制分类线    x,z = bpnet.BPClassfier(-3.0,3.0)    bpnet.classfyLine(plt,x,z)    plt.show()

 

总代码:

#coding:utf-8from numpy import *import matplotlib.pyplot as pltimport operatorclass BPNet(object):    def __init__(self):                 #构造函数        #以下参数需要手工设置        self.eb = 0.01                  #误差容限,当误差小于这个值时,算法收敛,程序停止        self.iterator = 0               #算法收敛时的迭代次数        self.eta      = 0.1             #学习率,相当于步长        self.mc       = 0.3             #动量因子:引入一个调优参数,是主要的调优参数        self.maxiter  = 2000            #最大迭代次数        self.nHidden  = 4               #隐含层神经元        self.nOut     = 1               #输出层个数 #以下的属性由系统生成        self.errlist = []               #误差列表:保存了误差参数的变化用于评估收敛        self.dataMat = 0                #训练集        self.classLabels = 0            #分类标签        self.nSampNum     = 0            #样本集行数        self.nSampDim     = 0            #样本列数    def logistic(self,net):             #激活(传递)函数        return 1.0/(1.0+exp(-net))    def dlogit(self,net):               #激活(传递)函数的导函数        return multiply(net,(1.0-net))    def errorfunc(self,inX):            #矩阵各元素平方之和        return sum(power(inX,2))*0.5    def normlize(self,dataMat):         #数据标准化归一化        [m,n] = shape(dataMat)        for i in xrange(n-1):            dataMat[:,i] = (dataMat[:,i]-mean(dataMat[:,i]))/(std(dataMat[:,i])+1.0e-10)        return dataMat    def loadDataSet(self,filename):     #加载数据集        self.dataMat     = []        self.classLabels = []        fr               = open(filename)        for line in fr.readlines():            lineArr = line.strip().split()            self.dataMat.append([float(lineArr[0]),float(lineArr[1]),1.0])            self.classLabels.append(int(float(lineArr[2])))        self.dataMat = mat(self.dataMat)        m,n          = shape(self.dataMat)        self.nSampNum = m   #样本数量        self.nSampDim = n-1 #样本维度    def addcol(self,matrixl,matrix2):   #增加新列        [m1,n1] = shape(matrixl)        [m2,n2] = shape(matrix2)        if m1 != m2:            print "different row,can not merge matix"            return        mergMat               = zeros((m1,n1+n2))        mergMat[:,0:n1]        = matrixl[:,0:n1]        mergMat[:,n1:(n1+n2)] = matrix2[:,0:n2]        return mergMat    def init_hiddenWB(self):            #隐藏层初始化        self.hi_w  = 2.0*(random.rand(self.nHidden,self.nSampDim)-0.5)        self.hi_b  = 2.0*(random.rand(self.nHidden,1)-0.5)        self.hi_wb = mat(self.addcol(mat(self.hi_w),mat(self.hi_b)))    def init_OutputWB(self):           #输出层初始化        self.out_w  = 2.0 * (random.rand(self.nOut,self.nHidden)-0.5)        self.out_b  = 2.0 * (random.rand(self.nOut,1)-0.5)        self.out_wb = mat(self.addcol(mat(self.out_w),mat(self.out_b)))    def bpTrain(self):                  #BP网络主程序        SampIn   = self.dataMat.T         #输入矩阵        expected = mat(self.classLabels)  #预测输出        self.init_hiddenWB()        self.init_OutputWB()        dout_wbOld = 0.0;dhi_wbOld = 0.0  #默认t-1权值        #主循环        for i in xrange(self.maxiter):            #1.工作信号正向传播            #1.1 信息从输入层到隐含层,这里使用了矢量计算,            # 计算的是整个样本集的结果,结果是4行307列的矩阵            hi_input  = self.hi_wb*SampIn            hi_output = self.logistic(hi_input)            hi2out    = self.addcol(hi_output.T,ones((self.nSampNum,1))).T            #1.2 从隐含层到输出层:结果是5行307列的矩阵            out_input = self.out_wb*hi2out            out_ouput = self.logistic(out_input)        #2.误差计算            err = expected-out_ouput            sse = self.errorfunc(err)            self.errlist.append(sse)            if sse <= self.eb:              #判断是否收敛至最优                self.iterator = i+1                break            #3.误差信号反向传播            DELTA   = multiply(err,self.dlogit(out_ouput)) #DELTA为输出层梯度            #delta为隐含层梯度            delta   = multiply(self.out_wb[:,:-1].T*DELTA,self.dlogit(hi_output))            dout_wb = DELTA*hi2out.T  #输出层权值微分            dhi_wb  = delta*SampIn.T   #隐含层权值微分            if i == 0:               #更新输出层和隐含层权值                self.out_wb = self.out_wb + self.eta*dout_wb                self.hi_wb  = self.hi_wb +self.eta*dhi_wb            else:                self.out_wb = self.out_wb + (1.0-self.mc)*self.eta*dout_wb+self.mc*dout_wbOld                self.hi_wb = self.hi_wb + (1.0-self.mc)*self.eta*dhi_wb+self.mc*dhi_wbOld            dout_wbOld = dout_wb            dhi_wbOld  = dhi_wb    def BPClassfier(self,start,end,steps = 30): #BP网络分类器        x  = linspace(start,end,steps)        xx = mat(ones((steps,steps)))        xx[:,0:steps] = x        yy = xx.T        z = ones((len(xx),len(yy)))        for i in range(len(xx)):            for j in range(len(yy)):                xi = []                tauex = []                tautemp = []                mat(xi.append([xx[i,j],yy[i,j],1]))                hi_input = self.hi_wb*(mat(xi).T)                hi_out = self.logistic(hi_input)                taumrow,taucol = shape(hi_out)                tauex = mat(ones((1,taumrow+1)))                tauex[:,0:taumrow] = (hi_out.T)[:,0:taumrow]                out_input = self.out_wb*(mat(tauex).T)                # out = self.logistic(out_input)                z[i,j] = out_input        return x,z    def classfyLine(self,plt,x,z):              #绘制分类线        plt.contour(x,x,z,1,colors = 'black')    def TrenLine(self,plt,color = 'r'):         #绘制趋势线,可调整颜色        X = linspace(0,self.maxiter,self.maxiter)        Y = log2(self.errlist)        plt.plot(X,Y,color)    def drawClassScatter(self,plt):             #绘制分类点        i = 0        for mydata in self.dataMat:            if self.classLabels[i] == 0:                plt.scatter(mydata[0,0],mydata[0,1],c='blue',marker = 'o' )            elif self.classLabels[i] == 1:                plt.scatter(mydata[0,0],mydata[0,1],c='red',marker = 's' )            else:                plt.scatter(mydata[0,0],mydata[0,1],c='green',marker = '^' )            i += 1

 

#coding:utf-8from numpy import *import matplotlib.pyplot as pltimport operatorfrom BP import *#数据集bpnet = BPNet()bpnet.loadDataSet("testSet2.txt")bpnet.dataMat = bpnet.normlize(bpnet.dataMat)#绘制数据集的散点图bpnet.drawClassScatter(plt)#BP神经网络进行数据分类bpnet.bpTrain()print  bpnet.out_wbprint  bpnet.hi_wb#计算和绘制分类线x,z = bpnet.BPClassfier(-3.0,3.0)bpnet.classfyLine(plt,x,z)plt.show()#绘制误差线bpnet.TrenLine(plt)plt.show()

 

 

 

资料来源:郑捷《机器学习算法原理与编程实践》 仅供学习研究

 

转载于:https://www.cnblogs.com/wuchuanying/p/6370671.html

你可能感兴趣的文章