第五章 误差反向传播

文章目录

第四章介绍了一个两层神经网络的实现,其中求取梯度的方法是数值微分,这种方法实现比较简单,但是速度慢。这就非常影响神经网络的性能,这文介绍了一种更快求取梯度的方法,为误差反向传播法,运用计算图进行说明。

为了理解误差反向传播法的原理,可以基于数学式,也可以基于计算图,本书采取的是计算图,理解起来比较容易

5.1 计算图

5.1.1用计算图求解

首先举两个例子:
问题一: 小明在超市买了两个100日元一个的苹果,消费税为10%,计算小明支付的金额。

第五章 误差反向传播

图1:基于计算图求解问题一:苹果的个数和消费税作为变量标在外部

问题二: 小明在超市买了两个100日元一个的苹果,三个150日元一个的橘子,消费税为10%,计算小明支付的金额。

第五章 误差反向传播

图2:基于计算图求解问题二:苹果,橘子的个数和消费税作为变量标在外部

综合上面的解法,能够得到计算图计算的流程:

  1. 构建计算图
  2. 在计算图上,从左到右进行计算

正向传播:从左到右边进行计算称为正向传播
反向传播:那么从右到左计算也是可以的,称为反向传播,在计算导数中发 挥重要作用

5.1.2 局部计算

举例:在超市买了2个苹果和其他的东西,可以画出如下的计算图:

第五章 误差反向传播

图3:买了2个苹果和其他的东西

从上图可以看出: 计算图可以集中精力于局部计算,不管全局计算有多么复杂,各个步骤所要做的就是对象节点的局部计算。

5.1.3 为何用计算图解题

通过计算图,能够使用反向传播进行高效计算梯度。
例子:求解问题一中,支付的总金额价格的导数。

第五章 误差反向传播

图4:买了2个苹果,其中消费金额对于苹果价格的导数

根据上图可知: 消费金额对苹果价格的导数为2.2,即苹果的价格每变动一个单位,消费金额就会增长2.2个单位。在计算苹果价格导数的途中,期间其他的导数也能够求解出来,并且导数能够被共享,如最终消费的总金额对不包含消费税的总金额的梯度。

5.2 链式法则

链式法则:反向传播的核心。

5.2.1计算图的反向传播

第五章 误差反向传播

图5:计算图的反向传播

5.2.2 什么是链式法则

链式法则是关于复合函数的导数的性质:

  • 如果某个函数由复合函数表示,则该复合函数的导数可以用构成复合函数的各个函数的导数的乘积表示。

第五章 误差反向传播

图6:复合函数求导的步骤

5.2.3 链式法则与计算图

第五章 误差反向传播

图7:计算图求解复合函数的梯度

第五章 误差反向传播

图8:计算图求解复合函数的梯度 (带入具体数据)

通过上面的两个图,能够了解计算图通过链式法则求解梯度信息的过程。

5.3 反向传播(基于计算图)

前面介绍了反向传播是基于链式法则成立的,本节将以+和x等运算为例,介绍反向传播的结构

5.3.1 加法节点的反向传播

以z = x + y z = x + yz=x+y为例。

第五章 误差反向传播

图9:加法节点的反向传播 如上图:坐标的图是正向传播,右边是反向传播,对于加法节点的反向传播,下游的梯度值等于上游的梯度值。

局部计算加法节点的反向传播:

第五章 误差反向传播

图10:局部计算的加法节点反向传播 对于一个大型的网络,某一处的加法节点的反向传播结果还是成立的。

实际的一个例子: 10 + 5 = 15 10+5=1510+5=15 ,反向传播时,上游传来的梯度值为1.3

第五章 误差反向传播

图11:局部计算的加法节点反向传播的实例

5.3.2 乘法节点的反向传播

以z = x ∗ y z = x * yz=xy为例。

第五章 误差反向传播

图12:乘法节点的反向传播 如上图:左边的图是正向传播,右边是反向传播

实际的例子:

第五章 误差反向传播

图13:从上游传下来的梯度值为1.3

  • 乘法的反向传播会乘以输入信号的翻转值,即对10求导要1.35=6.5;对5求导要1.310=13;
  • 实现乘法节点的反向传播时,要保存正向传播的输入信号

练手例子:
理解了加法节点和乘法节点的反向传播,可以试一试下面的问题,填写结果:

第五章 误差反向传播

图14:检验的问题

5.4 反向传播的代码实现(基于计算图)

由上文可知,通过计算图求解梯度有两种情况,乘法节点和加法节点,这里我们定义两个层:乘法层和加法层,用来实现对这两种情况进行问题的求导。很多问题是加法层和乘法层的复合。

5.4.1 乘法层的实现

class MulLayer:
    def __init__(self):
        self.x=None
        self.y=None
        
    #前向传播
    def forward(self,x,y):
        self.x=x
        self.y=y
        out=x *y
        
        return out
    def backward(self,dout):#dout是上一层传下来的导数
        #翻转x和y
        dx=dout * self.y
        dy=dout * self.x
        
        return dx,dy

5.4.2 加法层的实现

class Addlayer:
    def __init__(self):
       pass  #不用传参
    
    #forward 前向
    def forward(self,x,y):
        out=x+y
        return out
    
    #backward  反向
    def backward(self,out):
        dx=dout
        dy=dout
        return dx,dy

5.4.3 对于问题一的求解

复述问题一:* 小明在超市买了两个100日元一个的苹果,消费税为10%,计算小明支付的金额,计算支付金额对苹果的单价,苹果的数量,税率的梯度?

#购买两个苹果的误差反向传播:
#深度学习简单层的实现(乘法层和加法层)
class MulLayer:
    def __init__(self):
        self.x=None
        self.y=None
        
    #前向传播
    def forward(self,x,y):
        self.x=x
        self.y=y
        out=x *y
        
        return out
    def backward(self,dout):#dout是上一层传下来的导数
        #翻转x和y
        dx=dout * self.y
        dy=dout * self.x
        
        return dx,dy
class Addlayer:
    def __init__(self):
       pass  #不用传参
    
    #forward 前向
    def forward(self,x,y):
        out=x+y
        return out
    
    #backward  反向
    def backward(self,out):
        dx=dout
        dy=dout
        return dx,dy
    
#定义初始值
apple=100
appel_num=2
tax=1.1

#定义层
mul_apple_layer=MulLayer()
mul_tax_layer=MulLayer()

#forward
apple_price=mul_apple_layer.forward(apple,appel_num)
price=mul_tax_layer.forward(apple_price,tax)
print('price=','%.1f'%price)

# backward
dprice = 1
dapple_price, dtax = mul_tax_layer.backward(dprice)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)

#输出结果
print('dapple=',dapple)
print('dapple_num=',int(dapple_num))
print('dtax=',dtax)

5.4.3 对于问题二的求解

复述问题二:* 小明在超市买了两个100日元一个的苹果,三个150日元一个的橘子,消费税为10%,计算小明支付的金额,支付金额对苹果的单价,苹果的数量,橘子的单价,橘子的数量税率的梯度?

#购买两个苹果和三个橘子的误差反向传播(包含加法和乘法的误差反向传播):
#深度学习简单层的实现(乘法层和加法层)
class MulLayer:
    def __init__(self):
        self.x=None
        self.y=None
        
    #前向传播
    def forward(self,x,y):
        self.x=x
        self.y=y
        out=x *y
        
        return out
    def backward(self,dout):#dout是上一层传下来的导数
        #翻转x和y
        dx=dout * self.y
        dy=dout * self.x
        
        return dx,dy
class AddLayer:
    def __init__(self):
       pass  #不用传参
    
    #forward 前向
    def forward(self,x,y):
        out=x+y
        return out
    
    #backward  反向
    def backward(self,dout):
        dx=dout
        dy=dout
        return dx,dy
    
#定义初始值
apple=100
orange=150
appel_num=2
orange_num=3
tax=1.1

#定义层
mul_apple_layer=MulLayer()
mul_orange_layer=MulLayer()
mul_tax_layer=MulLayer()
add_orange_apple_layer=AddLayer()

#forward
apple_price=mul_apple_layer.forward(apple,appel_num)
orange_price=mul_orange_layer.forward(orange,orange_num)
sum_price=add_orange_apple_layer.forward(apple_price,orange_price)
price=mul_tax_layer.forward(sum_price,tax)
print('sumprice=','%.1f'%price)

# backward
dprice = 1
dsum_price, dtax = mul_tax_layer.backward(dprice)
dapple_price,dorange_price=add_orange_apple_layer.backward(dsum_price)
dorange,dorange_num=mul_orange_layer.backward(dorange_price)
dapple,dapple_num=mul_apple_layer.backward(dapple_price)

#输出结果
print('dapple=','%.1f'%dapple,'dapple_num=',int(dapple_num))
print('dorange=','%.1f'%dorange,'dorange_num=',int(dorange_num))

print('dtax=',dtax,'dapple_price=',dapple_price)

5.5 激活函数层的实现(基于计算图)

将计算图的思维应用到神经网络中,我们把神经网络的层的实现定义为一个类。通过加法层和乘法层来实现激活函数层的实现。激活函数有:ReLU层,Sigmoid层

5.5.1 ReLU层

第五章 误差反向传播

图15:激活函数ReLU的函数表达式和梯度表达式第五章 误差反向传播图16:ReLU层的计算图

实现代码:

class ReLU:
    def __init__(self):
        self.mask=None
     
    #前向传播
    def forward(self,x):
        self.mask=(x<=0)  #mask一个逻辑数组,其中x<=的值都是True
        out=x.copy()      #out与x相等
        out[self.mask]=0  #对mask中True中的值置0
                          #相当于完成了ReLU的功能,小于等于0时为0;大于0时,保持原值
        return out
    #反向传播
    def backward(self,dout):
        dout[mask]=0
        df=dout
        return df

5.5.2 Sigmoid层

根据定义,Sigmoid的函数表达式为:

第五章 误差反向传播

图17:激活函数Sigmoid的计算图第五章 误差反向传播图18:激活函数Sigmoid的简化计算图

代码实现:

class Sigmoid():
    def __init__(self):
        self.out=None
        
    def forward(self,x):
        out=1/(1+np.exp(-x))
        self.out=out
        return out
        
    def backward(self,dout):
        return dout*self.out*(1.0-self.out)

5.5.3 Affine层

在进行神经网络的正向传播的时候,需要计算加权信号的总和,这里用Affine层来定义这一个操作。

Affine层的函数表达式(X, W和B均为矩阵):y = X ∗ W + B y = XW + By*=XW+B

第五章 误差反向传播

图19:批版本的Affine层计算图

代码实现:

class Affine():                     #把重要一点的参数全部变为实例变量
    def __init__(self,W,b):
        self.W=W
        self.b=b
        self.x=None
        self.dW=None
        self.db=None
    
    def forward(self,x):
        self.x=x
        out=self.x*self.W+b
        self.out=out 
        return out
    
    def backward(self,dout):
        dx=np.dot(dout,self.W.T)
        dW=np.dot(self.x.T,dout)
        db=np.sum(dout,axis=0)
        
        return dx

5.5.4 Softmax-with-Loss层

如果神经网络用于分类的话,最后一层是Softmax层,由于需要训练数据,所以在训练网络中,最后一层还需要有一个损失函数层,用来评估选取参数的优劣性。

第五章 误差反向传播

图20:Softmax-with-Loss层计算图

第五章 误差反向传播

图21:简易版-Softmax-with-Loss层计算图

在图21中,正向传播的时候把softmax层和cross entropy error层封装起来,能够更加看清楚。

代码实现:

class SoftmaxWithLoss():
    def __init__(slef):
        self.loss=None   #损失
        self.y=None      #softmax的输出
        self.t=None      #监督数据(one—hot vector)
        
    def forward(self,x,t):
        self.t=t
        self.y=softmax(x)
        self.loss=cross_entropy_error(self.y,self.t)  #运用前面定义的softmax和cross_entropy_error求取误差值
        
        return self.loss
    
    def backward(self,dout=1):
        batch.size=self.t.shape[0]
        dx=(self.y-self.t)/batch_size   #梯度   除以批的原因:前面的误差求解的是一批
                                        #      中误差的总和,这里除以batch——size,是
                                        #      为了传递给前面的层的是单个数据的误差
                                        #      这里的梯度公式是推导出来的
        return dx

5.6 误差反向传播(基于计算图的代码实现)

这一章解的代码实现有两种途径:

  1. 第一种:直接对求解梯度的子函数进行修改,其他部分与上一章一样。
  2. 第二种:对两层神经网络的类进行修改,把神经网络分出层次来。

这两种方法均有优缺点:

  1. 第一种方法中,直接对求解梯度函数进行修改,操作比较简答,但是内容比较复杂,如果网络的层数再加深一下,会比较难写出对应的求解梯度函数。
  2. 第二种方法改的地方比较多,但是层次结构简单,非常适合于拓展到更加深层次的网络结构中。

5.6.1 方法一的实现

这种方法只是把求解梯度的子函数进行改变,这里还是以一个两层网络为例,列出了修改前后的子函数。
(修改前)数值微分法求梯度:

 def numerical_gradient(self,x,t):
        loss_W=lambda W:self.loss(x,t)      #看不懂,什么意思
        
        grads={}  #定义参数的梯度信息,存取权重参数的梯度信息值
        
        #求取四个权重参数的梯度信息值,并且存入grad中
        grads['W1']=numerical_gradient(loss_W,self.params['W1'])
        grads['b1']=numerical_gradient(loss_W,self.params['b1'])
        grads['W2']=numerical_gradient(loss_W,self.params['W2'])
        grads['b2']=numerical_gradient(loss_W,self.params['b2'])
        
        return grads

(修改后)误差反向转播求梯度:

def gradient(self, x, t):
        W1, W2 = self.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']
        
        grads = {}
        
        batch_num = x.shape[0]
        
        # forward
        a1 = np.dot(x, W1) + b1
        z1 = sigmoid(a1)
        a2 = np.dot(z1, W2) + b2
        y = softmax(a2)              #自己定义,可以叠加多层
        
        # backward
        dy = (y - t) / batch_num
        grads['W2'] = np.dot(z1.T, dy)
        grads['b2'] = np.sum(dy, axis=0)
        
        da1 = np.dot(dy, W2.T)
        dz1 = sigmoid_grad(a1) * da1
        grads['W1'] = np.dot(x.T, dz1)
        grads['b1'] = np.sum(dz1, axis=0)

        return grads       

5.6.2 方法二的实现

对两层神经网络的类,进行了比较大的修改。定义了很多类型的激活函数层,后面的预测用前向传播表示,梯度的求取用误差反向传播结果表示,层与层直接的建构一目了然,方便后面结构的扩增。

针对上一篇博客(数值微分法)minist神经网络学习.中实现神经网络的代码,这里修改的地方有5处:

  1. 要额外写一个用于调用激活函数层的文件layer,方便神经网络类的调用。
  2. 在__init__()子函数中,定义了网络中用到的层
  3. 在子函数predict()中,直接对一次对每一层求取前向传播的值,而不用自己一个一个写结果。
  4. 在loss()子函数中,损失函数就不再用cross_entropy_error()函数求取了,而是直接用SoftmaxWithLoss层解决。
  5. 最后一个就是求解梯度函数的改变,这里变化比较大。

5.6.3 方法二的实现代码

实现这个文件有三个主要文件:

  1. TLN_main.py :主函数与上一个博客一样,没有变化
  2. TLN_function.py:存放一些激活函数,也没有变化
  3. Layer.py: 新增文件,用于存放激活函数层(基于计算图)函数
  4. two_layer_net1.py :变化比较多,刚刚已经叙述了变化的地方(下面代码中也标注出来了)
5.6.3.1 文件一:TLN_main.py

文件一:TLN_main.py

'''#ini-batch 进行算法的实现'''
#相关库的调用
import time
import numpy as np
import matplotlib.pyplot as plt
import sys,os
sys.path.append(os.pardir)   #前面这两行代码,纯粹是为了跨文件夹调用文档中的函数
from dataset.mnist import load_mnist        #调用加载mnist数据集的函数
from two_layer_net1 import TwoLayerNet       #调用编辑的两层神经网络构成的类
#from two_layer_net import TwoLayerNet


#手写数据集mnist的数据获取
(x_train,t_train),(x_test,t_test)=load_mnist(flatten=True,normalize=True,one_hot_label=True) 
#load_mnist以(训练图片,训练标签),(测试图片,测试标签)的形式读入数据集
#normalize:是否将输入图片正规化为0.0-1.0的值,这里没有正规化,图片像素仍然为0-255
#flatten:是否展开输入图像(变为一维数组),这里是用一维数组显示的
#one_hot_label:仅正确标签为1,其他均为0的数组,形如[0,0,0,1,0,0],如果为False,则仅保存2,7等正确解的标签

 

#平均每一个epoch重复的次数


#超参数的定义
iters_num=10000
train_size=x_train.shape[0]   #总训练集的大小
batch_size=100                #mini_batch的大小
learning_rate=0.5                #学习率,也相当于是步长

network= TwoLayerNet(input_size=784,hidden_size=100,output_size=10)
#给两层神经网络的基本参数进行定义:
#神经网络:两层,输入为784个,隐藏神经元为50个,输出为10个。

#定义一些计算损失函数和精确度值的矩阵
train_loss_list=[]  #训练损失函数
train_acc_list=[]   #训练数据的准确率
test_acc_list=[]    #测试数据的准确率
iter_per_epoch=max(train_size/batch_size,1)


#主函数
start = time.clock()    #计时开始

for i in range(iters_num):
        
    #获取随机mini——batch
    batch_mask=np.random.choice(train_size,batch_size)  #从总的训练集数中选取batch_size个随机数,不带重复的
    x_batch=x_train[batch_mask]
    t_batch=t_train[batch_mask]

    #计算梯度
    #grad=network.numerical_gradient(x_batch,t_batch)
    #采用误差反向传播法:
    grad=network.gradient(x_batch,t_batch)
    
    #更新参数
    for key in ('W1','b1','W2','b2'):
        network.params[key] -= learning_rate*grad[key]
        
    #记录学习过程
    loss=network.loss(x_batch,t_batch)
    train_loss_list.append(loss)         #不懂代码意思


    #每经过一个epoch(参数更新后),就对所有的训练数据和测试数据计算识别精度
    if i % iter_per_epoch==0:
        #训练数据和测试数据的记录
        train_acc=network.accuracy(x_train,t_train)
        test_acc=network.accuracy(x_test,t_test)
        
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)
        
       # print('train_acc,test_acc| '+str(train_acc)+' , '+test_acc)
        print('train_acc,test_acc|', train_acc,',',test_acc)
        
#增加一个计时功能
end = time.clock()           #计时结束
print ('Running time:',str(end-start))   #显示所耗时间
        
#绘制图形

x = np.arange(len(train_acc_list))                            #绘制图像的最主要的三行,变量与因变量
plt.plot(x, train_acc_list, label='train acc', marker='o')
plt.plot(x, test_acc_list, label='test acc', marker='x',linestyle='--')

plt.xlabel("epochs")            #显示横纵坐标的标签
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')  #显示图例,在右下角

plt.savefig('./test2.jpg')     #保存显示的图片
plt.show()
5.6.3.2文件二:TLN_function.py

文件二:TLN_function.py

#TwoLayerNet中的子函数
import numpy as np
#求梯度函数
def numerical_gradient(f, x):
    h = 1e-4 # 0.0001
    grad = np.zeros_like(x)
    
    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
    while not it.finished:
        idx = it.multi_index
        tmp_val = x[idx]
        x[idx] = float(tmp_val) + h
        fxh1 = f(x) # f(x+h)
        
        x[idx] = tmp_val - h 
        fxh2 = f(x) # f(x-h)
        grad[idx] = (fxh1 - fxh2) / (2*h)
        
        x[idx] = tmp_val # 还原值
        it.iternext()   
        
    return grad

#激活函数(层与层之间使用)
def sigmoid(x):
    return 1 / (1 + np.exp(-x))  

#激活函数(输出层使用)
def softmax(x):
    if x.ndim == 2:
        x = x.T
        x = x - np.max(x, axis=0)
        y = np.exp(x) / np.sum(np.exp(x), axis=0)
        return y.T 

    x = x - np.max(x) # 溢出对策
    return np.exp(x) / np.sum(np.exp(x))

#损失函数的求取
def cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
        
    # 监督数据是one-hot-vector的情况下,转换为正确解标签的索引
    if t.size == y.size:
        t = t.argmax(axis=1)
             
    batch_size = y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

#误差反向传播算法要用的子函数
def sigmoid_grad(x):
    return (1.0 - sigmoid(x)) * sigmoid(x)
5.6.3.3 文件三:Layer.py

文件三:Layer.py

#激活层的实现,四个(ReLU,Sigmoid,Affine,SoftmaxWithLoss()

import numpy as np
from TLN_function import *


class ReLU:
    def __init__(self):
        self.mask=None
     
    #前向传播
    def forward(self,x):
        self.mask=(x<=0)  #mask一个逻辑数组,其中x<=的值都是True
        out=x.copy()      #out与x相等
        out[self.mask]=0  #对mask中True中的值置0
                          #相当于完成了ReLU的功能,小于等于0时为0;大于0时,保持原值
        return out
    #反向传播
    def backward(self,dout):
        dout[self.mask]=0
        df=dout
        return df
  
class Sigmoid():
    def __init__(self):
        self.out=None
        
    def forward(self,x):
        out=1 / (1 + np.exp(-x)) 
        self.out=out
        return out
        
    def backward(self,dout):
        return dout*(1.0-self.out)*self.out

class Affine():                     #把重要一点的参数全部变为实例变量
    def __init__(self,W,b):
        self.W=W
        self.b=b
        self.x=None
        self.original_x_shapr=None             ####定义x的形状
        self.dW=None
        self.db=None
    
    def forward(self,x):
        self.original_x_shape =x.shape        #####保存x的形状  
        x=x.reshape(x.shape[0],-1)            #####恢复x的形状
        self.x=x
        out=np.dot(x,self.W)+self.b
    
        return out
    
    def backward(self,dout):
        dx=np.dot(dout,self.W.T)
        self.dW=np.dot(self.x.T,dout)
        self.db=np.sum(dout,axis=0)
        
        dx=dx.reshape(*self.original_x_shape)   #还原输入数据的形状(对应张量)
        
        return dx

class SoftmaxWithLoss():
    def __init__(self):
        self.loss=None   #损失
        self.y=None      #softmax的输出
        self.t=None      #监督数据(one—hot vector)
        
    def forward(self,x,t):
        self.t=t
        self.y=softmax(x)
        self.loss=cross_entropy_error(self.y,self.t)  #运用前面定义的softmax和cross_entropy_error求取误差值
        
        return self.loss
    
    def backward(self,dout=1):
        batch_size=self.t.shape[0]
        if self.t.size ==self.y.size:    ####监督数据是one-hot-vector情况(加了一个判断是否为one-hot-vector情况)
            dx=(self.y-self.t)/batch_size   #梯度   除以批的原因:前面的误差求解的是一批
                                        #      中误差的总和,这里除以batch——size,是
                                        #      为了传递给前面的层的是单个数据的误差
                                        #      这里的梯度公式是推导出来的
        else:                                      ###
            dx=self.y.copy()                        ###
            dx[np.arange(batch_size),self.t] -= 1
            dx= dx/batch_size
        return dx
5.6.3.4 文件四:two_layer_net1.py

文件四:two_layer_net1.py

#应用误差反向传播的两层网络结构

#库和函数的导入

import sys,os
import numpy as np
sys.path.append(os.pardir)          #前面这两行代码,纯粹是为了跨文件夹调用文档中的函数
from TLN_function import *          #调用TLN_function文件中所有子函数
from Layer import *                 #调用Layer文件中所有子函数                 新增#######
from collections import OrderedDict  #从内带库中调用OrderdDict这个有序字典      新增#######


class TwoLayerNet:
    
    
    def __init__(self,input_size,hidden_size,output_size,weight_init_std=0.01):
        
        #初始化权重,并且定义几个实例变量,(也就是在类中的局部变量)
        self.params={}   #初始化实例变量params,里面有四个变量,分别是W1 ,W2,b1,b2
        
        #W1和W2使用符合高斯分布的随机数进行初始化
        self.params['W1']=weight_init_std*np.random.randn(input_size,hidden_size)  
        self.params['W2']=weight_init_std*np.random.randn(hidden_size, output_size)
        #b1和b2使用0进行初始化
        self.params['b1']=np.zeros(hidden_size)    #初始值全部设为0
        self.params['b2']=np.zeros(output_size)    #初始值全部设为0
    
        #生成层                                                               新增#######
        self.layers=OrderedDict()
        
        self.layers['Affinel'] = Affine(self.params['W1'],self.params['b1'])
        self.layers['Relu1'] = ReLU()
        self.layers['Affine2'] = Affine(self.params['W2'],self.params['b2'])
        
        self.lastLayer = SoftmaxWithLoss()
        
        
    def predict(self,x):                                                     #修改#######
        for layer in self.layers.values():       #value函数是对字典变量进行操作,求取字典变量的值
            x=layer.forward(x)
            
        return x
    '''
    def predict(self,x):
        
        #赋值变量
        W1,W2=self.params['W1'],self.params['W2']
        b1,b2=self.params['b1'],self.params['b2']
        
        #两层网络的前向传播算法
        a1=np.dot(x,W1)+b1
        z1=sigmoid(a1)
        a2=np.dot(z1,W2)+b2
        y=softmax(a2)
        
        return y 
    '''
    
    #x:图像数据 ;t:正确解标签        
    #损失函数(交叉熵误差函数u,求出损失值)
    def loss(self,x,t):                                                       #修改
        y=self.predict(x)
        #loss_x=cross_entropy_error(y,t)                                     #修改,两个东西是一个意思
        loss_x=self.lastLayer.forward(y,t)
        
        return loss_x
    
    #计算准确率函数
    def accuracy(self,x,t):
        y=self.predict(x)
        y=np.argmax(y,axis=1)   #求出y中每一列中的最大值
        t=np.argmax(t,axis=1)   #求出t中每一列中的最大值
        
        accuracy=np.sum(y==t)/float(x.shape[0])   #强制类型转换
        return accuracy
    
    #x:图像数据 ;t:正确解标签
    #计算权重参数的梯度
    
    
    #两种求解梯度的算法
    #负梯度法求解权重参数梯度值 
    def numerical_gradient(self,x,t):
        loss_W=lambda W:self.loss(x,t)      #看不懂,什么意思
        
        grads={}  #定义参数的梯度信息,存取权重参数的梯度信息值
        
        #求取四个权重参数的梯度信息值,并且存入grad中
        grads['W1']=numerical_gradient(loss_W,self.params['W1'])
        grads['b1']=numerical_gradient(loss_W,self.params['b1'])
        grads['W2']=numerical_gradient(loss_W,self.params['W2'])
        grads['b2']=numerical_gradient(loss_W,self.params['b2'])
        
        return grads
    
    #误差反向传播法求解梯度值
    def gradient(self, x, t):                                              #比较大的修改地方
        
        #forward
        self.loss(x,t)
        
        #backward
        dout=1
        dout=self.lastLayer.backward(dout)
        
        layers=list(self.layers.values())
        layers.reverse()                                #对于这个字典向量进行逆转
        for layer in layers:
            dout=layer.backward(dout)
       
        #设定
        grads={}  #定义参数的梯度信息,存取权重参数的梯度信息值
        
        #求取四个权重参数的梯度信息值,并且存入grad中
        #更新后的值存入一个矩阵
        grads['W1']=self.layers['Affinel'].dW
        grads['b1']=self.layers['Affinel'].db
        grads['W2']=self.layers['Affine2'].dW
        grads['b2']=self.layers['Affine2'].db
        
        return grads
5.6.3.5. 运行结果
>>>start = time.clock()    #计时开始
>>>train_acc,test_acc| 0.16121666666666667 , 0.1615
>>>train_acc,test_acc| 0.9480833333333333 , 0.9461
>>>train_acc,test_acc| 0.9716 , 0.9669
>>>train_acc,test_acc| 0.9767333333333333 , 0.972
>>>,test_acc| 0.9813333333333333 , 0.9727
>>>train_acc,test_acc| 0.9871333333333333 , 0.973
>>>train_acc,test_acc| 0.9903833333333333 , 0.9748
>>>train_acc,test_acc| 0.9923666666666666 , 0.9782
>>>,test_acc| 0.9934833333333334 , 0.9767
>>>train_acc,test_acc| 0.99525 , 0.9784
>>>train_acc,test_acc| 0.9958666666666667 , 0.978
>>>train_acc,test_acc| 0.9983833333333333 , 0.9794
>>>Running time: 44.66675650000002   #总耗时

图片输出:

第五章 误差反向传播

图1:输出图片(正确率的变化) 测试集和训练集的准确率变化不大,说明训练的模型没有过拟合,效果比较好。

‘Affine2’].dW
grads[‘b2’]=self.layers[‘Affine2’].db

    return grads

##### 5.6.3.5. 运行结果

```python
>>>start = time.clock()    #计时开始
>>>train_acc,test_acc| 0.16121666666666667 , 0.1615
>>>train_acc,test_acc| 0.9480833333333333 , 0.9461
>>>train_acc,test_acc| 0.9716 , 0.9669
>>>train_acc,test_acc| 0.9767333333333333 , 0.972
>>>,test_acc| 0.9813333333333333 , 0.9727
>>>train_acc,test_acc| 0.9871333333333333 , 0.973
>>>train_acc,test_acc| 0.9903833333333333 , 0.9748
>>>train_acc,test_acc| 0.9923666666666666 , 0.9782
>>>,test_acc| 0.9934833333333334 , 0.9767
>>>train_acc,test_acc| 0.99525 , 0.9784
>>>train_acc,test_acc| 0.9958666666666667 , 0.978
>>>train_acc,test_acc| 0.9983833333333333 , 0.9794
>>>Running time: 44.66675650000002   #总耗时

图片输出:

第五章 误差反向传播

图1:输出图片(正确率的变化) 测试集和训练集的准确率变化不大,说明训练的模型没有过拟合,效果比较好。

上一篇:numpy的interp线性插值使用案例


下一篇:《深度学习入门:基于Python的理论与实现》读书笔记:第3章 神经网络