标准BP算法和累计BP算法训练单隐层网络

本文基于tensorflow搭建和训练单隐层网络,分别实现标准BP算法累计BP算法

标准BP算法每次仅针对一个训练样例更新连接权和阈值,也就是说,BP算法中的更新规则是基于单个的 推导而得,如果类似的推导出基于累积误差最小化的更新规则,就得到了累积误差逆传播算法

标准BP算法和累积BP算法的区别类似于随机梯度下降(Stochastic gradient descent,简称 SGD)标准梯度下降之间的区别。本文通过改变batch_size的大小来覆盖全部样本或者单个样本,进而体现出标准BP算法和累计BP算法的区别。

代码

基于tensorflow搭建的单隐层神经网络

import tensorflow as tf
import numpy as np
from sklearn.metrics import accuracy_score
class BPNN(object):
    def __init__(self,input_n,hidden_n,output_n,lambd):
        """
        这是BP神经网络类的构造函数
        :param input_n:输入层神经元个数
        :param hidden_n: 隐藏层神经元个数
        :param output_n: 输出层神经元个数
        :param lambd: 正则化系数
        """
        self.Train_Data = tf.placeholder(tf.float64,shape=(None,input_n),name='input_dataset')                                  # 训练数据集
        self.Train_Label = tf.placeholder(tf.float64,shape=(None,output_n),name='input_labels')                                 # 训练数据集标签
        self.input_n = input_n                                                                                                    # 输入层神经元个数
        self.hidden_n = hidden_n                                                                                                  # 隐含层神经元个数
        self.output_n = output_n                                                                                                  # 输出层神经元个数
        self.lambd = lambd                                                                                                        # 正则化系数
        self.input_weights = tf.Variable(tf.random_normal((self.input_n, self.hidden_n),mean=0,stddev=1,dtype=tf.float64),trainable=True)                                       # 输入层与隐含层之间的权重
        self.hidden_weights =  tf.Variable(tf.random_normal((self.hidden_n,self.output_n),mean=0,stddev=1,dtype=tf.float64),trainable=True)                                      # 隐含层与输出层之间的权重
        self.hidden_threshold = tf.Variable(tf.random_normal((1,self.hidden_n),mean=0,stddev=1,dtype=tf.float64),trainable=True)                                            # 隐含层的阈值
        self.output_threshold = tf.Variable(tf.random_normal((1,self.output_n),mean=0,stddev=1,dtype=tf.float64),trainable=True)                                            # 输出层的阈值
        # 将层与层之间的权重与偏置项加入损失集合
        tf.add_to_collection('loss', tf.contrib.layers.l2_regularizer(self.lambd)(self.input_weights))
        tf.add_to_collection('loss', tf.contrib.layers.l2_regularizer(self.lambd)(self.hidden_weights))
        tf.add_to_collection('loss', tf.contrib.layers.l2_regularizer(self.lambd)(self.hidden_threshold))
        tf.add_to_collection('loss', tf.contrib.layers.l2_regularizer(self.lambd)(self.output_threshold))
        # 定义前向传播过程
        #activation function为sigmoid
        self.hidden_cells = tf.sigmoid(tf.matmul(self.Train_Data,self.input_weights)+self.hidden_threshold)
        self.output_cells = tf.sigmoid(tf.matmul(self.hidden_cells,self.hidden_weights)+self.output_threshold)
        # 定义损失函数,并加入损失集合
        self.MSE = tf.reduce_mean(tf.square(self.output_cells-self.Train_Label))
        tf.add_to_collection('loss',self.MSE)
        # 定义损失函数,均方误差加入L2正则化
        self.loss = tf.add_n(tf.get_collection('loss'))
    def train_test(self,Train_Data,Train_Label,Test_Data,Test_Label,learn_rate,epoch,iteration,batch_size):
        """
        这是BP神经网络的训练函数
        :param Train_Data: 训练数据集
        :param Train_Label: 训练数据集标签
        :param Test_Data: 测试数据集
        :param Test_Label: 测试数据集标签
        :param learn_rate:  学习率
        :param epoch:  时期数
        :param iteration: 一个epoch的迭代次数
        :param batch_size:  小批量样本规模
        """
        train_loss = []                 # 训练损失
        test_loss = []                  # 测试损失
        test_accarucy = []              # 测试精度
        with tf.Session() as sess:
            datasize = len(Train_Label)
            self.train_step = tf.train.GradientDescentOptimizer(learn_rate).minimize(self.loss)
            sess.run(tf.global_variables_initializer())
            for e in np.arange(epoch):
                for i in range(iteration):
                    start = (i*batch_size)%datasize
                    end = np.min([start+batch_size,datasize])
                    sess.run(self.train_step,
                             feed_dict={self.Train_Data:Train_Data[start:end],self.Train_Label:Train_Label[start:end]})
                    if i % 1000 == 0:
                        total_MSE = sess.run(self.MSE,
                                             feed_dict={self.Train_Data:Train_Data,self.Train_Label:Train_Label})
                        print("第%d个epoch中,%d次迭代后,训练MSE为:%g"%(e+1,i+1000,total_MSE))
                # 训练损失
                _train_loss = sess.run(self.MSE,feed_dict={self.Train_Data:Train_Data,self.Train_Label:Train_Label})
                train_loss.append(_train_loss)
                # 测试损失
                _test_loss = sess.run(self.MSE, feed_dict={self.Train_Data:Test_Data, self.Train_Label: Test_Label})
                test_loss.append(_test_loss)
                # 测试精度
                test_result = sess.run(self.output_cells,feed_dict={self.Train_Data:Test_Data})
                test_accarucy.append(self.Accuracy(test_result,Test_Label))
        return train_loss,test_loss,test_accarucy
    def Accuracy(self,test_result,test_label):
        """
        这是BP神经网络的测试函数
        :param test_result: 测试集预测结果
        :param test_label: 测试集真实标签
        """
        predict_ans = []
        label = []
        for (test,_label) in zip(test_result,test_label):
            test = np.exp(test)
            test = test/np.sum(test)
            predict_ans.append(np.argmax(test))
            label.append(np.argmax(_label))
        return accuracy_score(label,predict_ans)

测试代码:

import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import Normalizer
from BP3 import BPNN
def Load_Data(fileName):
    """
    这是导入数据的函数
    :return: 数据集
    """
    data = []
    label = []
    datas = pd.read_csv(fileName)
    datas['one'] = 1.0
    data = np.mat(datas[['one','density','sugar']])
    label = np.mat(datas[['label']])
    data = np.array(data,dtype=np.float64)
    label = np.array(label,dtype=np.float64)
    return data,label
def run_main():
    """
       这是主函数
    """
    # 导入数据
    #path = './data.txt'
    Data,Label = Load_Data('waterMalon.csv')
    # 分割数据集,并对数据集进行标准化
    Train_Data,Test_Data,Train_Label,Test_Label = train_test_split(Data,Label,test_size=1/5,random_state=10)
    Train_Data = Normalizer().fit_transform(Train_Data)
    Test_Data = Normalizer().fit_transform(Test_Data)
    # 设置网络参数
    input_n = np.shape(Data)[1]
    output_n = np.shape(Label)[1]
    hidden_n = int(np.sqrt(input_n*output_n))
    lambd = 0.001
    batch_size = 64#标准BP算法和累计BP算法的区别可以理解为随机梯度下降(SGD)与经典的梯度下降法的区别,SGD可以理解为这里的mini_batch,而batch_size的不同则表示当前是采用了SGD还是经典的梯度下降
    learn_rate = 0.01
    epoch = 5
    iteration = 1000
    # 训练并测试网络
    bpnn = BPNN(input_n,hidden_n,output_n,lambd)
    train_loss,test_loss,test_accuracy = bpnn.train_test(Train_Data,Train_Label,Test_Data,Test_Label,learn_rate,epoch,iteration,batch_size)
    # 解决画图是的中文乱码问题
    mpl.rcParams['font.sans-serif'] = [u'simHei']
    mpl.rcParams['axes.unicode_minus'] = False
    # 结果可视化
    col = ['Train_Loss','Test_Loss']
    epoch = np.arange(epoch)
    plt.plot(epoch,train_loss,'r')
    plt.plot(epoch,test_loss,'b-.')
    plt.xlabel('Epoch')
    plt.ylabel('Loss')
    plt.grid(True)
    plt.legend(labels = col,loc='best')
    plt.savefig('./累计BP训练与测试损失.jpg')
    plt.show()
    plt.close()
    plt.plot(epoch, test_accuracy, 'r')
    plt.xlabel('Epoch')
    plt.ylabel('Test Accuracy')
    plt.grid(True)
    plt.legend(loc='best')
    plt.savefig('./累计BP测试精度.jpg')
    plt.show()
    plt.close()
if __name__ == '__main__':
    run_main()

这里的batch_size = 64表示的就是每次更行参数覆盖了所有的样本,即实现的是累计BP算法,而当batch_size = 1 则表示的是标准BP算法,每次参数更新仅针对一个训练样例。

结果

训练分别得到了两种BP算法的结果(如下)

标准BP算法和累计BP算法训练单隐层网络_第1张图片 标准BP算法和累计BP算法训练单隐层网络_第2张图片
标准BP训练与测试损失 标准BP测试精度
标准BP算法和累计BP算法训练单隐层网络_第3张图片 标准BP算法和累计BP算法训练单隐层网络_第4张图片
累计BP训练与测试损失 累计BP测试精度

从结果可以看出累计BP与标准BP有以下几个不同点:

  1. 速度:标准BP的损失函数下降的更快而累计BP的损失函数下降的较慢,因为累计BP每次更新权重都要经过全部样本,而标准BP只需要针对单个样本。
  2. 测试精度:测试精度都很高(应该是因为样本太少而神经网络表达能力太强导致测试精度都是1),但是可以看到累计BP的测试损失函数收敛的值更低一点,理论上累计BP每次更行权值都是用的所有样本,因此鲁棒性应该是比标准BP要好一点的。

结论:标准BP训练快但效果不一定高,累计BP训练慢但效果好,因此可以采用先用标准BP再用累计BP的思路来达到最优解,这大概也就是为什么现在都是用的batch gradient descent,采用两者中间的一个mini batch来估计整体的效果,更准确也更快。

你可能感兴趣的:(标准BP算法和累计BP算法训练单隐层网络)