《深度学习--基于python的理论与实现》学习笔记8:第四章神经网络的学习(2)

前一个博客中介绍了损失函数,梯度,梯度下降等关键词,这一个博客,结合前面的知识,编写程序,使得整个深度学习算法能够进行权值迭代,进行更新

4.5 学习算法的实现

神经网络的学习步骤如下:

  1. 前提
    神经网络存在适合的权重和偏置,调整权重和偏置以便你和训练数据的过程称之为"学习",神经网络的学习分为下面四个步骤:
  2. 步骤一(mini-batch)
    从训练数据中随机选出一部分数据,这部分数据称为mini-batch,我们的目标是减小mini-batch的损失函数的值
  3. 步骤二(计算梯度)
    为了减小mini-batch的损失函数的值,需要求出各种权重参数的梯度。梯度表示损失函数的值减少最多的方向。
  4. 步骤三(更新参数)
    将权重参数沿着梯度方向进行微小更新
  5. 步骤四(重复)
    重复步骤一,二,三,训练数据中的值全部训练完,使得训练后的模型精准。

注: 在进行参数更新的时候,使用的方法是随机梯度下降法,即对随机选择的数据进行的梯度下降法,简称SGD。

4.5.1 定义一个两层网络的类

这个类之中包含个子函数,如下:
《深度学习--基于python的理论与实现》学习笔记8:第四章神经网络的学习(2)_第1张图片

图1: 子函数中的具体函数

文件1:Two_Layer_Net.py

#库和函数的导入
import sys,os
import numpy as np
sys.path.append(os.pardir)   #前面这两行代码,纯粹是为了跨文件夹调用文档中的函数
from TLN_function import *      #调用TLN_function文件中所有子函数


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
    
    #x:图像数据 ;t:正确解标签
    #前向传播算法 
    
    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 
   
    
    #损失函数(交叉熵误差函数u,求出损失值)
    def loss(self,x,t):
        y=self.predict(x)
        loss_x=cross_entropy_error(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):
        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       

4.5.2 子函数

在定义这一个两层网络的时候,需要用到一些子函数,如下:

  1. 求解梯度的函数:numerical_gradient(f, x)
  2. 激活函数:sigmoid(x),softmax(x),sigmoid_grad(x)
  3. 损失函数(交叉熵误差):cross_entropy_error(y, t)
    文件2: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)

4.5. 3 主函数

最后就是主函数了,包括以下几个小模块:

  1. 手写数据集mnist的数据获取
  2. 超参数的定义
  3. 深度学习的四个步骤(见本文的开篇)
  4. 图像的绘制

文件: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_Net 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()

具体的代码文件
链接
提取码:x63e

参考书籍:

  1. 《深度学习–基于python的理论与实现》

你可能感兴趣的:(深度学习笔记,python,神经网络)