Deep Learning 学习笔记6:深度前馈网络(二)

深度前馈神经网络(FNN)一般有两种结构,包括反向传播网络(BP)与径向基函数神经网络(RBF)。在这片文章中,重点研究反向传播网络。而BP网络也是众多神经网络里最简单的一种。

最简单的BP网络如下所示:

                                  

输入层是一个线性函数,激活函数则选用sigmoid函数。在这个网络中,只要输入两个参数x和y,就可以开始训练了。        

神经网络训练

首先考虑最简单的结构,第一层是隐藏层h,第二层是输出层o,如下图:

每一次的输入后都会产生一个输出值,我们将误差定义如下:

                                                                            Loss=\frac{1}{2}(y-y_{o})^{2}

其中y是真实值,yo是神经网络的输出值。

现在要做的就是减小这个误差。也就是通过改变我们最初的权重值与偏置值来减小误差。反向传播法的原理可以看我之前的文章:Deep Learning 学习笔记4:神经网络的反向传播法。通过反向传播法减小误差的公式为:

                                                                     (w_{h})^{n}=(w_{h})^{n-1}-\eta *\frac{\partial Loss}{\partial w_{h}}

                                                                     (b_{h})^{n}=(b_{h})^{n-1}-\eta *\frac{\partial Loss}{\partial b_{h}}

                                                                     (w_{o})^{n}=(w_{o})^{n-1}-\eta *\frac{\partial Loss}{\partial w_{o}}

                                                                     (b_{o})^{n}=(b_{o})^{n-1}-\eta *\frac{\partial Loss}{\partial b_{o}}

我们现在考虑一个最简单的BP网络,其中输入层有两个神经元和一个偏置项,隐藏层有两个神经元,输出层一个神经元。

具体代码如下:

import math
import random

random.seed(0)

#创建一个 a <= rand < b 的随机数
def rand(a,b):
    return (b - a)*random.random() + a  #random.random()是一个0到1之间的数

#创建一个矩阵
#该矩阵结构为I行J列,每个矩阵的元素都为0.0
def makeMatrix(I,J,fill=0.0):
    m = []
    for i in range(I):
        m.append([fill]*J)
    return m

#随机初始化矩阵
#将matrix进行赋值
def randomizeMatrix(matrix,a,b):
    for i in range(len(matrix)):
        for j in range(len(matrix[0])):
            matrix[i][j] = random.uniform(a,b)  #random.uniform返回一个a到b之间的值
        
#sigmoid函数
def sigmoid(x):
    return 1.0/(1.0+math.exp(-x))

#sigmoid导数
def dsigmoid(y):
    return y*(1-y)

#定义神经网络结构
#ni:输入单元数量
#nh:隐藏单元数量
#no:输出单元数量     
#在该测试案例中,ni = 2 , nh = 2 , no = 1
class NN:
    def __init__(self,ni,nh,no):
        
        #ni+1是为了偏置节点
        self.ni = ni + 1           #self.ni = 3
        self.nh = nh               #self.nh = 2
        self.no = no               #self.no = 1
        
        #激活值
        self.ai = [1.0]*self.ni    #self.ai = [1.0, 1.0, 1.0]
        self.ah = [1.0]*self.nh    #self.ah = [1.0, 1.0]
        self.ao = [1.0]*self.no    #self.ao = [1.0]
        
        #初始化权重矩阵
        self.wi = makeMatrix(self.ni,self.nh)#输入层到隐藏层的权重结构
        self.wo = makeMatrix(self.nh,self.no)#隐藏层到输出层的权重结构
        #self.wi = [[0.0, 0.0], 
        #           [0.0, 0.0], 
        #           [0.0, 0.0]]
        
        #self.wo = [[0.0], [0.0]]
       
    
    
        #权重矩阵进行随机化赋值
        randomizeMatrix(self.wi,-0.2,0.2)
        randomizeMatrix(self.wo,-2.0,2.0)
        
        #self.wi = [[0.13776874061001926, 0.10318176117612099], 
        #           [-0.031771367667662004, -0.09643329988281467], 
        #           [0.004509888547443414, -0.03802634501983429]]
        
        #self.wo = [[1.1351943561390905], [-0.7867490956842902]]
        
        
        
        #权重矩阵的上次梯度
        self.ci = makeMatrix(self.ni,self.nh)
        self.co = makeMatrix(self.nh,self.no)
        print(self.ci)
        print(self.co)
    
    
    #前向传播过程
    #假设inputs=[1,0]
    def runNN(self,inputs):
        if len(inputs) != self.ni - 1:               #训练数据里的元素个数必须与神经元个数相同,不然没法训练
            print ('incorrect number of inputs')     #不相同返回错误信息
        for i in range(self.ni - 1):        
            self.ai[i] = inputs[i]                   #将输入的训练数据赋值给输入神经元
        for j in range(self.nh):                     #遍历每一个隐藏层的神经元
            sum = 0.0
            for i in range(self.ni):
                sum += ( self.ai[i]*self.wi[i][j])   #将输入层的每一个神经元的值乘以相应的权重得到一个权重和
            self.ah[j] = sigmoid(sum)                #将这个求和值用激活函数激活,得到了隐藏层的激活值
        for k in range(self.no):
            sum = 0.0
            for j in range(self.nh):
                sum += ( self.ah[j]*self.wo[j][k])   #将隐藏层的激活值乘以相应的权重得到一个权重和
            self.ao[k] = sigmoid(sum)                #将权重和用激活函数激活,得到了最终的输出层的输出值
        return self.ao                               #该函数最后的返回结果就是输出层的输出值
    
    #后向传播算法
    #targets是最后输出的目标值
    #N代表本次学习率,M代表上次学习率
    def backPropagate(self,targets,N,M):
        #输出层deltas的公式:dE/dw[j][k] = (t[k] - ao[k]) * s'( SUM( w[j][k]*ah[j] ) ) * ah[j]
        #计算输出层偏导
        output_deltas = [0.0]*self.no
        for k in range(self.no):                     #对输出层的每一个神经元进行遍历
            error = targets[k] - self.ao[k]          #误差=目标值-输出值
            output_deltas[k] = error*dsigmoid(self.ao[k])   #根据偏导公式求出误差对输出层激活前的值的偏导
        
        #更新隐藏层到输出层权值
        for j in range(self.nh):
            for k in range(self.no):
                #误差对隐藏层到输出层权重的偏导:dError/dweight[j][k]=output_deltas[k] * self.ah[j]
                change = output_deltas[k]*self.ah[j]
                #更新隐藏层到输出层的权值
                self.wo[j][k] += N*change
                
        #计算隐藏层偏导
        hidden_deltas = [0.0]*self.nh
        for j in range(self.nh):
            error = 0.0
            for k in range(self.no):                       #对输出层的每一个神经元进行遍历
                error +=output_deltas[k]*self.wo[j][k]     
            hidden_deltas[j] = error*dsigmoid(self.ah[j])  #根据偏导公式求出误差对隐藏层激活前的值的偏导
            
        #更新输入层权值
        for i in range(self.ni):
            for j in range(self.nh):
                change = hidden_deltas[j]*self.ai[i]       #求出误差对输入层到隐藏层权重的偏导
                self.wi[i][j] += N*change                  #更新输入层到隐藏层的权重
        
        
        #计算误差平方和
        error = 0.0
        for k in range(len(targets)):
            error = 0.5*(targets[k] - self.ao[k]) **2
        return error
    
    def weights(self):
        #打印权值矩阵
        print('Input weights')
        for i in range(self.ni):
            print(self.wi[i])
        print('')
        print('Output weights')
        for j in range(self.nh):
            print(self.wo[j])
        print('')
        
    def test(self,patterns):
        
        for p in patterns:
            inputs = p[0]
            print('Inputs:',p[0],'-->',self.runNN(inputs),'\tTarget',p[1])
    
    def train(self,patterns,max_iterations=1000,N = 0.5, M =0.1):
        for i in range(max_iterations):                #每一次的轮循训练时,权重都已经更新过了,之后的误差会越来越小
            for p in patterns:
                inputs = p[0]
                targets = p[1]
                self.runNN(inputs)
                error = self.backPropagate(targets,N,M)
            if i % 50 == 0 :
                print('Combined error',error)
        self.test(patterns)
        
        
def main():
    #样本数据
    pat = [
        [[0, 0], [1]],
        [[0, 1], [1]],
        [[1, 0], [1]],
        [[1, 1], [0]]
    ]
    #定义神经网络结构
    myNN = NN(2,2,1)
    
    myNN.train(pat)
    
if __name__ == "__main__":
    main()

最后输出的结果是:

[[0.0, 0.0], [0.0, 0.0], [0.0, 0.0]]
[[0.0], [0.0]]
Combined error 0.16906801636001426
Combined error 0.2022541222164743
Combined error 0.14751072656012554
Combined error 0.09067295508313507
Combined error 0.05211136622318693
Combined error 0.03190152344604797
Combined error 0.021292603028146156
Combined error 0.01529430447882564
Combined error 0.011619980883316913
Combined error 0.00920978281054238
Combined error 0.007538945763625707
Combined error 0.006328214315990261
Combined error 0.005418959184633719
Combined error 0.004715870978438192
Combined error 0.004158892087856742
Combined error 0.0037086172192786716
Combined error 0.0033382840828446256
Combined error 0.003029169810055748
Combined error 0.0027678349698653138
Combined error 0.002544413787582293
Inputs: [0, 0] --> [0.9978141244366157] 	Target [1]
Inputs: [0, 1] --> [0.9594710984808869] 	Target [1]
Inputs: [1, 0] --> [0.9572240131050892] 	Target [1]
Inputs: [1, 1] --> [0.06823150510798977] 	Target [0]

我们可以看到,经过训练之后,输出层的误差在不断减小。这就是BP网络的基本思路。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值