GA-BP VS BP——两种模型的比较,附实验数据和代码

前言

善始者繁多,克终者盖寡。

GA-BP神经网络——遗传算法优化BP神经网络Python实现中我们通过遗传算法(Genetic Algorithm,GA)优化了包含1个隐含层的BP神经网络,经试验,在完成“异或运算”预测任务时,模型预测误差较小。

一句话总结GA-BP和BP的区别,那就是:
GA-BP中使用选择、交叉、变异的操作替代了BP中的反向传播过程。

前期准备:
numpy手写BP神经网络
GA遗传算法-Python实现
GA-BP神经网络

本文在往期文章代码的基础上进行了优化和改进,比较在相同实验环境下GA-BP神经网络和BP神经网络在处理回归问题时的表现。

一、数据来源

已通过某平台收集到用户A、B、C、D、E五项指标数据,经预处理后使用组合赋权法计算各指标权重后得到每条记录的最终得分,依据如下规则将用户划分为由弱至强的5个类别。关于权重和综合得分的计算感兴趣的话可以看一下熵权法计算指标权重离差最大化法计算指标权重组合赋权法计算指标权重

在这里插入图片描述
部分数据如下图所示,文件已上传,可在文章顶部位置下载。

在这里插入图片描述

二、BP神经网络

2.1 模型描述

该BP神经网络模型包含1个输入层、1个隐含层和1个输出层,输入层到隐含层使用sigmod激活函数(代码实现时可能出现数据溢出,所有使用scipy下的expit代替手写sigmod函数),输出层不使用激活函数。

其工作流程如图所示,通过前向传播可计算出模型预测值与真实值间的差距(误差),通过反向传播能够将误差信息传递给各参数,反向传播中最重要的操作是权重和偏置的更新

在这里插入图片描述

2.2 完整代码

通常情况下一个三层的BP神经网络只需要init、forward、backward、train、predict五个方法即可,下列代码中其他方法均为解决第一章问题定制。

import numpy
import pandas
from matplotlib import pyplot
from scipy.special import expit
'''
    构建一个包含1个隐含层的BP神经网络
'''
class BPNet_one_output:
    '''
        input,hidden,output分别表示输入层、隐含层、输出层神经元的个数
    '''
    def __init__(self,input,hidden,output):
        #权重
        self.weight1 = numpy.random.uniform(low=-1, high=1, size=(input, hidden))
        self.weight2 = numpy.random.uniform(low=-1, high=1, size=(hidden, output))
        #偏置
        self.bias1 = numpy.random.uniform(low=-1, high=1,size=(1, hidden))
        self.bias2 = numpy.random.uniform(low=-1, high=1,size=(1, output))
        #准确度,训练后预测正确数目与样本总数之比
        self.accuracy = []
        #精确度,对训练结果而言,模型正确预测某一类别的样本数与模型预测为该类的样本数之比
        self.precision = []
        #召回率,对原始样本而言,样本中某个类别有多少被正确预测了
        self.recall = []
        #损失值
        self.loss = []

    #比较两个列表是否相同
    def compare(self,list1:list,list2:list):
        if len(list1)!= len(list2):
            return
        for i in range(len(list1)):
            if list1[i]!=list2[i]:
                return 0
        return 1

    def caculate_accuracy(self,actual_label,label):
        true_count = 0
        false_count = 0
        result = []
        for i in range(len(actual_label)):
            # 将numpy.ndarray转换为普通的List
            temp = self.one_hot_encoding(actual_label[i])
            result.append(temp)
        actual_label = result
        size = len(label)
        for i in range(size):
            rs = self.compare(actual_label[i],label[i])
            if rs==1:
                true_count += 1
            else:
                false_count += 1
        # print(f"正确个数为{true_count},总个数为{size}")
        return true_count/size

    def caculate_accuracy_primal(self,actual_label,label):
        actual_label = actual_label.tolist()
        label = label.tolist()
        true_count = 0
        size = len(label)
        for i in range(size):
            al = float(actual_label[i][0])
            l = float(label[i][0])
            # print(f"al is {al} l is {l}")
            if al>=0.7 and l>=0.7:
                true_count+=1
            if al>=0.6 and al<0.7 and l>=0.6 and l<0.7:
                true_count+=1
            if al>=0.4 and al<0.6 and l>=0.4 and l<0.6:
                true_count+=1
            if al>=0.2 and al<0.4 and l>=0.2 and l<0.4:
                true_count+=1
            if al>=0.0 and al<0.2 and l>=0.0 and l<0.2:
                true_count+=1
            # print(f"正确个数为{true_count},总个数为{size}")
        return true_count / size

    def one_hot_encoding(self,data:list):
        max = data[0]
        max_index = 0
        for i in range(len(data)):
            if data[i]>max:
                max = data[i]
                max_index = i
        for i in range(len(data)):
            if i==max_index:
                data[i]=1
            else:
                data[i]=0
        return data

    #sigmod激活函数,但是初始sigmod函数可能出现数据溢出情况,所有使用expit代替
    def sigmod(self,x):
        # return 1/(1 + numpy.exp(-x))
        return expit(x)

    #sigmod函数的导数
    def sigmod_derivative(self, x):
        return x * (1 - x)

    #softmax激活函数
    def softmax(self,x):
        #按行计算每一个样本
        exps = numpy.exp(x - numpy.max(x,axis=1,keepdims=True))
        #为避免指数溢出numpy能够表示的上限,使其减去当前数据中的最大值
        return exps/numpy.sum(exps,axis=1,keepdims=True)

    def loss_cross_entropy(self,y,p):
        '''
        :param y: 真实标签
        :param p: 预测标签
        :return: 交叉熵
        '''
        #为了避免出现log(0)的情况,计算时加上一个极小值
        min_data = 1e-60
        # return -1 * numpy.sum(y*numpy.log(p+min_data))
        return -numpy.mean(y*numpy.log(p+min_data))

    def loss_cross_entropy_derivative(self,label_true,label_predict):
        return label_true - label_predict

    #使用方差作为损失函数
    def loss_mse(self,x,y):
        return 1/2*numpy.sum((x-y)*(x-y))

    #前向传播
    def forward(self, data):
        #存储每一层的输入和输出
        self.hidden1_input = numpy.dot(data, self.weight1) + self.bias1
        self.hidden1_output = self.sigmod(self.hidden1_input)

        self.output_input = numpy.dot(self.hidden1_output, self.weight2) + self.bias2
        # self.output_output = self.sigmod(self.output_input)
        self.output_output = self.output_input
        return self.output_output

    #后向传播
    def backward(self, data, label, learning_ration):
        #首先计算误差(损失)
        output_error = self.output_output - label
        #输出层误差项(包含了误差、激活函数导数两部分信息)
        output_delta = output_error * self.sigmod_derivative(self.output_output)
        #将误差传入隐藏层1
        hidden1_delta = numpy.dot(output_delta, self.weight2.T) * self.sigmod_derivative(self.hidden1_output)

        #更新权重
        self.weight1 -= numpy.dot(data.T,hidden1_delta) * learning_ration
        self.weight2 -= numpy.dot(self.hidden1_output.T, output_delta) * learning_ration
        #更新偏置
        self.bias1 -= numpy.sum(hidden1_delta, axis=0) * learning_ration
        self.bias2 -= numpy.sum(output_delta, axis=0) * learning_ration

    #训练数据集
    def train(self,data,label,learning_ration,epoch):
        for i in range(epoch):
            output = self.forward(data)
            self.backward(data, label, learning_ration)

            loss = self.loss_mse(label, output)
            # loss = self.loss_cross_entropy(label, output)
            self.loss.append(loss)
            accuary = self.caculate_accuracy_primal(output, label)
            self.accuracy.append(accuary)
            # print("accuary:",accuary)
            # self.show_weights()

    #使用训练好的数据预测结果
    def predict_ont_hot(self,data:list):
        data = self.predict(data)
        result = []
        for i in range(len(data)):
            #将numpy.ndarray转换为普通的List
            temp = self.one_hot_encoding(data[i].tolist())
            result.append(temp)
        return result

    def predict(self,data):
        return self.forward(data)

    def show_weights(self):
        print(f"{self.weight1}\n{self.weight2}")

    def show_loss(self):
        # print(self.loss)
        pyplot.title("LOSS")
        pyplot.xlabel("epoch")
        pyplot.ylabel("ration")
        pyplot.plot(self.loss)
        pyplot.show()

    def show_accuracy(self):
        # print(self.loss)
        pyplot.title("Accuaracy")
        pyplot.xlabel("epoch")
        pyplot.ylabel("ration")
        pyplot.plot(self.accuracy)
        pyplot.show()

def load_data():
    df = pandas.read_excel("data.xlsx")
    data_temp  = df[["A","B","C","D","E"]]
    label_temp = df["评级分"]
    data = []
    label = []
    DIMENSION = len(data_temp.columns)
    for i in range(df.shape[0]):
        data.append(data_temp.iloc[i].to_list())
        #对训练集标签进行独热编码
        temp = []
        for j in range(DIMENSION):
            temp.append(0)
        index = label_temp[i]-1
        temp[index] = 1
        # temp.append(label_temp[i])
        label.append(temp)
    data = numpy.array(data)
    label = numpy.array(label)
    return data,label

def load_data_primal():
    df = pandas.read_excel("data.xlsx")
    data_temp  = df[["A","B","C","D","E"]]
    label_temp = df["最终得分"]
    data = []
    label = []
    for i in range(df.shape[0]):
        data.append(data_temp.iloc[i].to_list())
        temp = []
        temp.append(label_temp[i])
        label.append(temp)
    data = numpy.array(data)
    label = numpy.array(label)
    return data,label

def test():
    # 创建训练数据集
    X_train = numpy.array([[0, 0],
                           [0, 1],
                           [1, 0],
                           [1, 1]])
    y_train = numpy.array([[0],
                           [1],
                           [1],
                           [0]])

    # 创建测试数据集
    X_test = numpy.array([[0, 0],
                          [0, 1],
                          [1, 0],
                          [1, 1]])
    y_test = numpy.array([[0],
                          [1],
                          [1],
                          [0]])
    learning_ration = 0.01
    network = BPNet_one_output(2, 10, 1)
    network.train(X_train, y_train, learning_ration, 50000)
    print(network.predict(X_test))
    network.show_loss()

def mytest():
    data, label = load_data_primal()
    # print(data[0:10],"\n",label[0:10])
    # 划分训练集与测试集
    data_train = data[0:int(len(data) * 3 / 4)]
    data_train_train = data_train[0:int(len(data_train) * 3 / 4)]
    data_train_test = data_train[int(len(data_train) * 1 / 4) * (-1):-1]
    data_test = data[int(len(data) * 1 / 4) * (-1):-1]

    label_train = label[0:int(len(label) * 3 / 4)]
    label_train_train = label_train[0:int(len(data_train) * 3 / 4)]
    label_train_test = label_train[int(len(data_train) * 1 / 4) * (-1):-1]
    label_test = label[int(len(label) * 1 / 4) * (-1):-1]

    # 创建一个包含两个隐含层的BP神经网络
    network = BPNet_one_output(5, 11, 1)
    # 训练模型
    network.train(data_train, label_train, 0.01, 10000)
    network.show_loss()
    network.show_accuracy()
    # 预测结果
    result = network.predict(data_test)
    print(result)
    print(label_test)
    acc = network.caculate_accuracy_primal(result,label_test)
    print("准确率是{:.2f}%".format(acc*100))

if __name__ == '__main__':
    mytest()

三、GA-BP神经网络

3.1 模型描述

同样采用三层网络结构,相较于BP神经网络,GA-BP神经网络使用选择、交叉、变异操作替代了其反向传播的过程,以达到更新参数的目的,其操作流程下图所示。

在选择、交叉、变异操作中均涉及权重和偏置的更新,各环节详细描述可参照往期文章GA-BP神经网络

要点梳理:

  1. 种群中每一个个体,都是一组权重和偏置的组合;
  2. 选择操作负责从种群中挑选出适合度高(预测误差小)的个体;
  3. 交叉操作负责生成新的个体;
  4. 变异操作负责增加种群的多样性。

在这里插入图片描述

3.2 完整代码

train方法为GA-BP神经网络的核心,是3.1中流程图的代码实现,为了尽可能产生更加优秀的个体,所以在train方法中包含两次计算种群适合度(evaluate_fitness)的操作,第一次是为了挑选出种群中更优秀的个体,第二次是让更优秀的个体拥有更大的繁衍机会

GA-BP神经网络中会涉及大量的数值运算,例如构建“5101”的网络结构,其包含权重60个,偏置11个,假设种群中包含100个个体,种群繁衍一代至少需要进行(60+11)* 100 * 2次运算,为了节省计算机资源,在模型训练完毕后将权重和偏置保存为“.npy”文件留到下次直接使用

模型训练完毕后需要从最终种群中挑选出适合度最高的个体,将其解码后作为神经网络的权重和偏置。

import numpy as np
from matplotlib import pyplot
from scipy.special import expit
from sklearn.utils import check_random_state

class GeneticAlgorithmBP:
    def __init__(self, num_inputs, num_hidden, num_outputs, population_size, mutation_rate):
        self.num_inputs = num_inputs
        self.num_hidden = num_hidden
        self.num_outputs = num_outputs

        self.population = []
        self.population_size = population_size
        #通过变异操作能够增大样本输入空间,类似于学习率,此处也以参数形式传入
        self.mutation_rate = mutation_rate
        #种群中各个体的适合度,fitness_values的大小与种群中个体数相同
        self.fitness_values = np.zeros(population_size)
        #后续操作中需要频繁使用随机取数的操作,所有在类中内置一个随机数生成器
        self.random_state = check_random_state(None)  # 设置随机数种子

        # 准确度,训练后预测正确数目与样本总数之比
        self.accuracy = []
        # 精确度,对训练结果而言,模型正确预测某一类别的样本数与模型预测为该类的样本数之比
        self.precision = []
        # 召回率,对原始样本而言,样本中某个类别有多少被正确预测了
        self.recall = []
        # 损失值
        self.loss = []

    # sigmod激活函数,但是原始sigmod函数会出现数据溢出现象,所有使用expit替代
    def sigmoid(self, x):
        # return 1 / (1 + np.exp(-x))
        return expit(x)

    #使用方差作为损失函数
    def loss_mse(self,x,y):
        return 1/2*np.sum((x-y)*(x-y))

    #初始化种群,也是初始化神经网络权重的过程,将某次随机生成的权重当做种群中的一个对象
    def initialize_population(self):
        # 短横杠表示临时参数,和占位符一个作用
        for _ in range(self.population_size):
            #初始化权重
            input_hidden_weights = np.random.uniform(low=-1, high=1, size=(self.num_inputs, self.num_hidden))
            hidden_output_weights = np.random.uniform(low=-1, high=1, size=(self.num_hidden, self.num_outputs))
            #初始化偏置
            input_hidden_bias = np.random.uniform(low=-1, high=1, size=(1, self.num_hidden))
            hidden_output_bias = np.random.uniform(low=-1, high=1, size=(1, self.num_outputs))
            self.population.append((input_hidden_weights, input_hidden_bias, hidden_output_weights, hidden_output_bias))

    def caculate_accuracy_primal(self,actual_label,label):
        actual_label = actual_label.tolist()
        label = label.tolist()
        true_count = 0
        size = len(label)
        for i in range(size):
            al = float(actual_label[i][0])
            l = float(label[i][0])
            # print(f"al is {al} l is {l}")
            if al>=0.7 and l>=0.7:
                true_count+=1
            if al>=0.6 and al<0.7 and l>=0.6 and l<0.7:
                true_count+=1
            if al>=0.4 and al<0.6 and l>=0.4 and l<0.6:
                true_count+=1
            if al>=0.2 and al<0.4 and l>=0.2 and l<0.4:
                true_count+=1
            if al>=0.0 and al<0.2 and l>=0.0 and l<0.2:
                true_count+=1
            # print(f"正确个数为{true_count},总个数为{size}")
        return true_count / size

    #前向传播
    def forward(self, x , entity):
        input_hidden_weights, input_hidden_bias, hidden_output_weights, hidden_output_bias = entity
        hidden_activations = self.sigmoid(np.dot(x, input_hidden_weights) + input_hidden_bias)
        output_activations = np.dot(hidden_activations, hidden_output_weights) + hidden_output_bias
        return output_activations

    #适合度函数
    def evaluate_fitness(self, x, y):
        # #计算当前方案的所有误差
        # sum_error = []
        # #计算当前方案的所有准确率
        # sum_accuracy = []
        #计算中权重每个对象(权重)的适合度,并将适合度保存到fitness_values中
        for i in range(self.population_size):
            #此处为前向传播,计算中间层和输出层的激活值
            output_activations = self.forward(x, self.population[i])
            #以方差作为损失函数
            error = self.loss_mse(output_activations, y)
            # sum_error.append(error)
            #计算当前种群总准确率
            # accuracy = self.caculate_accuracy_primal(y, output_activations)
            # sum_accuracy.append(accuracy)
            #计算实际输出与标签的真实误差(绝对值)
            # true_error = np.mean(np.abs(output_activations - y))
            #将适合度保存到fitness_values中
            self.fitness_values[i] = 1 / (error + 1e-8)
        #将当前方案下最佳损失加入到loss和accuracy中,以观察随繁衍代数增加时损失的变化(依据是适合度)
        best_indice = np.argmax(self.fitness_values)
        best_entity = self.population[best_indice]
        output = self.forward(x, best_entity)
        self.loss.append(self.loss_mse(output, y))
        self.accuracy.append(self.caculate_accuracy_primal(output, y))

    def selection(self):
        #计算总适合度,并将适合度转换为被选中的概率
        total_fitness = np.sum(self.fitness_values)
        selection_probs = self.fitness_values / total_fitness
        #从种群中随机选择一些对象(依据概率),并将其保存到selected_population中
        selected_indices = self.random_state.choice(np.arange(self.population_size), size=self.population_size, replace=True, p=selection_probs)
        selected_population = []
        for idx in selected_indices:
            selected_population.append(self.population[idx])

        self.population = selected_population

    #交叉(繁衍行为的主要操作)
    def crossover(self):
        offspring_population = []
        # 计算总适合度,并将适合度转换为被选中的概率,因为我们有理由让适合度高的个体参与到繁衍的过程中
        total_fitness = np.sum(self.fitness_values)
        selection_probs = self.fitness_values / total_fitness

        for _ in range(self.population_size):
            #parent是权重和偏置组合而成的元组
            parent1_indices = self.random_state.choice(np.arange(self.population_size),p=selection_probs)
            parent2_indices = self.random_state.choice(np.arange(self.population_size),p=selection_probs)
            input_hidden_weights1, input_hidden_bias1, hidden_output_weights1, hidden_output_bias1 = self.population[parent1_indices]
            input_hidden_weights2, input_hidden_bias2, hidden_output_weights2, hidden_output_bias2 = self.population[parent2_indices]
            #对w1和b1进行交叉操作,交叉点随机(把input1前半部分和Input2后半部分拼接)
            crossover_point = self.random_state.randint(0, self.num_hidden)
            input_hidden_weights = np.concatenate((input_hidden_weights1[:, :crossover_point], input_hidden_weights2[:, crossover_point:]), axis=1)
            input_hidden_bias = np.concatenate((input_hidden_bias1[:, :crossover_point], input_hidden_bias2[:, crossover_point:]), axis=1)
            #对w2和b2进行交叉操作,交叉点随机
            crossover_point = self.random_state.randint(0, self.num_outputs)
            hidden_output_weights = np.concatenate((hidden_output_weights1[:, :crossover_point], hidden_output_weights2[:, crossover_point:]), axis=1)
            hidden_output_bias = np.concatenate((hidden_output_bias1[:, :crossover_point], hidden_output_bias2[:, crossover_point:]), axis=1)
            #将繁衍产生的对象放入新的种群,直至生成population_size个对象
            offspring_population.append((input_hidden_weights, input_hidden_bias, hidden_output_weights, hidden_output_bias))

        self.population = offspring_population

    #变异
    def mutation(self):
        for i in range(self.population_size):
            input_hidden_weights, input_hidden_bias, hidden_output_weights, hidden_output_bias = self.population[i]
            #随机添加一个极小值改变原始数据
            if self.random_state.rand() < self.mutation_rate:
                input_hidden_weights += np.random.uniform(low=-0.1, high=0.1, size=(self.num_inputs, self.num_hidden))

            if self.random_state.rand() < self.mutation_rate:
                input_hidden_bias += np.random.uniform(low=-0.1, high=0.1, size=(1, self.num_hidden))

            if self.random_state.rand() < self.mutation_rate:
                hidden_output_weights += np.random.uniform(low=-0.1, high=0.1, size=(self.num_hidden, self.num_outputs))

            if self.random_state.rand() < self.mutation_rate:
                hidden_output_bias += np.random.uniform(low=-0.1, high=0.1, size=(1, self.num_outputs))

            self.population[i] = (input_hidden_weights, input_hidden_bias, hidden_output_weights, hidden_output_bias)

    def train(self, x, y, num_generations):
        self.initialize_population()
        for generation in range(num_generations):
            self.evaluate_fitness(x, y)
            self.selection()
            #挑选出优秀个体后重新计算种群中个体适合度,因为我们有理由让适合度高的个体参与到繁衍的过程中,此操作会增加计算次数
            self.evaluate_fitness(x, y)
            self.crossover()
            self.mutation()
        best_weights = self.population[np.argmax(self.fitness_values)]
        input_hidden_weights, input_hidden_bias, hidden_output_weights, hidden_output_bias = best_weights
        # 保存权重和偏置
        np.save('my_input_hidden_weights.npy', input_hidden_weights)
        np.save('my_input_hidden_bias.npy', input_hidden_bias)
        np.save('my_hidden_output_weights.npy', hidden_output_weights)
        np.save('my_hidden_output_bias.npy', hidden_output_bias)
        return input_hidden_weights, input_hidden_bias, hidden_output_weights, hidden_output_bias

    #预测结果
    def predict(self, x):
        # 读取权重文件
        input_hidden_weights = np.load('my_input_hidden_weights.npy')
        input_hidden_bias = np.load('my_input_hidden_bias.npy')
        hidden_output_weights = np.load('my_hidden_output_weights.npy')
        hidden_output_bias = np.load('my_hidden_output_bias.npy')
        # 使用训练好的权重进行预测
        entity = (input_hidden_weights, input_hidden_bias, hidden_output_weights, hidden_output_bias)
        predictions = self.forward(x, entity)
        return predictions

    def show_loss(self):
        # print(self.loss)
        pyplot.title("LOSS")
        pyplot.xlabel("epoch")
        pyplot.ylabel("ration")
        pyplot.plot(self.loss)
        pyplot.show()

    def show_accuracy(self):
        # print(self.loss)
        pyplot.title("Accuaracy")
        pyplot.xlabel("epoch")
        pyplot.ylabel("ration")
        pyplot.plot(self.accuracy)
        pyplot.show()

if __name__ == '__main__':
    # 设置神经网络和遗传算法参数
    num_inputs = 2
    num_hidden = 10
    num_outputs = 1
    population_size = 100
    mutation_rate = 0.01
    num_generations = 10000

    # 创建GA-BP神经网络对象
    ga_bp = GeneticAlgorithmBP(num_inputs, num_hidden, num_outputs, population_size, mutation_rate)

    # 创建训练数据集
    x_train = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
    y_train = np.array([[0], [1], [1], [0]])

    #训练模型并输出预测结果
    ga_bp.train(x_train, y_train, num_generations)
    predictions = ga_bp.predict(x_train)

    print("预测结果:")
    print(predictions)

四、GABP_VS_BP测试程序

4.1 完整代码

读取“data.xlsx”文件,将数据前75%作为模型的训练集,将后25%数据作为模型的测试集,让两个模型训练5000轮,比较其在损失值和准确率上的异同

import pandas
import numpy
from src.paper.BP import BPNet_one_output
from src.paper.GABP import GeneticAlgorithmBP
import matplotlib.pyplot as plt

def load_data():
    df = pandas.read_excel("data.xlsx")
    data_temp  = df[["A","B","C","D","E"]]
    label_temp = df["评级分"]
    data = []
    label = []
    DIMENSION = len(data_temp.columns)
    for i in range(df.shape[0]):
        data.append(data_temp.iloc[i].to_list())
        #对训练集标签进行独热编码
        temp = []
        for j in range(DIMENSION):
            temp.append(0)
        index = label_temp[i]-1
        temp[index] = 1
        # temp.append(label_temp[i])
        label.append(temp)
    data = numpy.array(data)
    label = numpy.array(label)
    return data,label

def load_data_primal():
    df = pandas.read_excel("data.xlsx")
    data_temp  = df[["A","B","C","D","E"]]
    label_temp = df["最终得分"]
    data = []
    label = []
    for i in range(df.shape[0]):
        data.append(data_temp.iloc[i].to_list())
        temp = []
        temp.append(label_temp[i])
        label.append(temp)
    data = numpy.array(data)
    label = numpy.array(label)
    return data,label

if __name__ == '__main__':
    data,label = load_data_primal()

    # 划分训练集与测试集
    data_train = data[0:int(len(data) * 3 / 4)]
    data_train_train = data_train[0:int(len(data_train) * 3 / 4)]
    data_train_test = data_train[int(len(data_train) * 1 / 4) * (-1):-1]
    data_test = data[int(len(data) * 1 / 4) * (-1):-1]

    label_train = label[0:int(len(label) * 3 / 4)]
    label_train_train = label_train[0:int(len(data_train) * 3 / 4)]
    label_train_test = label_train[int(len(data_train) * 1 / 4) * (-1):-1]
    label_test = label[int(len(label) * 1 / 4) * (-1):-1]

    #设置神经网络参数
    num_inputs = 5
    num_hidden = 10
    num_outputs = 1
    num_epochs = 5000
    learning_rate = 0.01
    #设置种群参数
    population_size = 100
    mutation_rate = 0.1
    num_generations = 2500

    #创建神经网络对象
    bp = BPNet_one_output(num_inputs, num_hidden, num_outputs)
    gabp = GeneticAlgorithmBP(num_inputs, num_hidden, num_outputs, population_size, mutation_rate)
    #训练模型
    bp.train(data_train, label_train, learning_rate, num_epochs)
    '''
        可直接使用已训练好的模型进行预测
        此时不再需要训练模型
    '''
    gabp.train(data_train, label_train, num_generations)

    #绘制损失函数曲线
    bp_loss = bp.loss
    gabp_loss = gabp.loss
    plt.plot(bp_loss, label='BP',color="blue")
    plt.plot(gabp_loss, label='GABP',color="red")
    plt.xlabel('Epoch')
    plt.ylabel('Loss')
    plt.legend()
    plt.show()
    plt.savefig("loss.PNG")

    #绘制准确率曲线
    bp_accuary = bp.accuracy
    gabp_accuary = gabp.accuracy
    plt.plot(bp_accuary, label='BP', color="blue")
    plt.plot(gabp_accuary, label='GABP', color="red")
    plt.xlabel('Epoch')
    plt.ylabel('Accuracy')
    plt.legend()
    plt.show()
    plt.savefig("accuracy.PNG")

    #预测结果
    y_pred_bp = bp.predict(data_test)
    #计算预测结果的平均误差(方差表示)
    error_bp = bp.loss_mse(y_pred_bp, label_test)
    #计算预测结果的平均准确率
    acc_bp = bp.caculate_accuracy_primal(y_pred_bp,label_test)

    #预测结果
    y_pred_gabp = gabp.predict(data_test)
    #计算预测结果的平均误差(方差表示)
    error_gabp = gabp.loss_mse(y_pred_gabp, label_test)
    #计算预测结果的平均准确率
    acc_gabp = gabp.caculate_accuracy_primal(y_pred_gabp, label_test)

    numpy.savetxt("label_test.csv", label_test, delimiter=",")

    #显示最终结果
    print("BP模型的最终结果:")
    #将预测结果保存为csv文件
    numpy.savetxt("BP_predict_result.csv", y_pred_bp, delimiter=",")
    print("平均误差:{:.5f}".format(error_bp))
    print("平均准确率:{:.2f}%".format(acc_bp*100))
    print("GABP模型的最终结果:")
    #将预测结果保存为csv文件
    numpy.savetxt("GABP_predict_result.csv", y_pred_gabp, delimiter=",")
    print("平均误差:{:.5f}".format(error_gabp))
    print("平均准确率:{:.2f}%".format(acc_gabp*100))

4.2 实验结果

4.2.1 训练集上的表现

实验中使用方差作为损失函数描述模型预测结果与真实值之间的差异,从图中可以发现GA-BP神经网络的预测误差明显小于BP神经网络,并更早的收敛,从这个角度看GA-BP神经网络更加优秀。

在这里插入图片描述

注意,文中构建神经网络模型预测用户的最终得分是一个回归问题,在回归模型中并没有“准确率”这一描述! 此处的准确率是作者本人为满足第一章中的分类需求人工添加的分类方法,该操作会在一定程度内运行模型具有较大的误差,也就是说只要GA-BP和BP两个模型的整体误差相差不大,它们在准确率的计算上不会有很大的差距。

从下图中可以发现,整体上看GA-BP的表现优于BP,但是其准确率曲线变化幅度大,而BP的准确率曲线就平稳得多。

在这里插入图片描述

4.2.2 测试集上的表现

GA-BP神经网络在测试集上的平均误差高于BP神经网络的平均误差,这让人大感意外,但是两个模型的误差均已进入千分位级别,在本文提及的分类任务中,两评估模型的平均误差对几乎没有区别。在准确率上GA-BP神经网络模型优于BP神经网络模型。

在这里插入图片描述

五、总结

从实验结果看,在处理回归问题时GA-BP神经网络模型优于BP神经网络模型,主要表现在模型平均预期误差小、模型更早收敛上。由于初始参数、概率等因素的影响某些时候BP神经网络模型会取得更优秀的表现,例如文中GA-BP神经网络模型的准确率曲线波动较大,而BP神经网络模型的准确率曲线波动较小。如今也有很多用于优化GA算法的策略,可以进一步提高GA-BP的稳定性。

相较于BP模型,相同迭代次数下GA-BP模型会进行更多的运行,消耗更多的资源,在处理实际问题时,如果对于待解决的问题,GA-BP模型和BP模型的预测误差可以忽略不计,BP模型也是一个很不错的选择。

  • 21
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的墨菲特

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值