遗传+BP神经网络 求解故障诊断问题(python)

遗传+BP神经网络 求解拖拉机齿轮箱故障诊断问题(python3)

通过学习书籍《matlab智能优化算法30个案例分析(第2版)》中有关神经网络算法的编程知识,初步了解神经网络的编码思想,主要学习第三章中提到的“基于遗传算法的BP神经网络优化算法”。

由于网络结构、初始连接权值和阈值的选择对BP神经网络训练的影响很大,而这些影响因素无法准确获得,所以考虑采用遗传算法来优化BP神经网络的初始连接权值和阈值,使优化后的BP神经网络能够更好地对样本进行预测。

1.了解BP神经网络的原理

学习资料:
【深度学习】神经网络入门(最通俗的理解神经网络)
https://blog.csdn.net/lyl771857509/article/details/78990215

神经网络算法介绍(Nerual NetWorks)
https://blog.csdn.net/qq_37406130/article/details/79157025

深入理解BP神经网络(Java实现)
https://www.jianshu.com/p/6ab6f53874f7

BP神经网络与Python实现
https://blog.csdn.net/qq_41235053/article/details/81488238

BP神经网络的Matlab实现——人工智能算法
遗传+BP神经网络 求解故障诊断问题(python)_第1张图片
BP网络的学习过程分为两个阶段:
(1)样本信号的正向传播,在这个过程中,工作信号由输入层经隐含层再到输出层,完成正向传播,在这个过程中各层之间的权值与阈值是不变的。
(2)误差信号的反向传播,将实际输出与期望输出做比较,误差信号由输出层经隐含层到达输入层,完成逆向传播,在这一过程中,沿着误差信号的梯度反向修正权值与阈值,通过重复这两个过程,不断调整权值与阈值,直到实际输出与期望输出尽可能的达到相近。
遗传+BP神经网络 求解故障诊断问题(python)_第2张图片

2.BP神经网络的网络结构确定

2.1 特征参数的选取

拖拉机齿轮箱发生故障的原因中60%左右是由于齿轮故障导致的,所以研究拖拉机齿轮箱故障诊断问题可以转化为求解齿轮故障问题。齿轮故障问题特征参数的选取,详情请自行查阅书籍《matlab智能优化算法30个案例分析(第2版)》。
遗传+BP神经网络 求解故障诊断问题(python)_第3张图片
下面这段解释引用自BP神经网络学习笔记,说明了输入数据需要归一化处理的原因。
遗传+BP神经网络 求解故障诊断问题(python)_第4张图片

2.2 确定输出特征的形式

由齿轮箱样本状态数据的分析可知,齿轮状态主要存在以下三种情形:(1)无故障;(2)齿轮裂纹;(3)断齿。
因此,输出层的神经元有3个。详情请自行查阅书籍《matlab智能优化算法30个案例分析(第2版)》。
在这里插入图片描述

2.3 确定网络结构

书中提到对于一般的模式识别问题,三层网络就能够很好的解决,就是说只需要一层隐含层。在三层网络中隐含层的神经元个数m与输入层的神经元个数n存在如下关系:m = 2 * n + 1(2乘以n再加上1)。 输入层的神经元个数是15个,隐含层的神经元个数就是31个,输出层的神经元个数为3个。因此,输入层到隐含层的权值有15 * 31 = 465 个,阈值有31个,隐含层到输出层的权值有31 * 3 = 93 个,阈值有3个。所以遗传算法需要优化的初始权值和阈值的总数是592个。

2.4 确定激活函数

如何选择神经网络激活函数:有效的改善模型学习模式的能力

学习资料:
神经网络激活函数:sigmoid、tanh、ReLU、softmax
https://blog.csdn.net/wangqianqianya/article/details/82961276

Python的Numpy实现深度学习常用的函数
https://blog.csdn.net/qq_33200967/article/details/79759284

BP神经网络的训练函数的特点
https://blog.csdn.net/kebu12345678/article/details/86590093

BP神经网络隐含层神经元的激活函数采用S型正切函数tansig(),输出层神经元的传递函数采用S型对数函数logsig(),由于S型对数函数logsig()的输出正好是0-1满足求解问题的输出要求。

tansig与tanh激活函数:https://blog.csdn.net/weixin_39982211/article/details/104744988

tansig()函数表达式:
遗传+BP神经网络 求解故障诊断问题(python)_第5张图片
遗传+BP神经网络 求解故障诊断问题(python)_第6张图片
(图片来源:sigmoid函数和tanh函数以及ReLU函数https://blog.csdn.net/lanluyug/article/details/76791271)
遗传+BP神经网络 求解故障诊断问题(python)_第7张图片
遗传+BP神经网络 求解故障诊断问题(python)_第8张图片

2.5 网络训练和测试

神经网络的训练是一个不断修正权值和阈值的过程,通过训练,使网络的输出误差越来越小。网络经过训练后,需要对网络进行测试,利用事先准备好的测试样本集。
遗传+BP神经网络 求解故障诊断问题(python)_第9张图片

3.算法实现

书中给出的算法流程图如下,本人也将按照该流程图来进行python编程。
遗传+BP神经网络 求解故障诊断问题(python)_第10张图片
书中提到的算法流程思路:
遗传+BP神经网络 求解故障诊断问题(python)_第11张图片

3.1 遗传算法部分

3.1.1 遗传算法的相关操作

(1)确定遗传算法的相关参数

(2)确定编码形式
遗传+BP神经网络 求解故障诊断问题(python)_第12张图片
权值和阈值都由10位二进制数表示。

(3)确定选择操作
选择算子采用轮盘赌方法。轮盘赌方法的具体实现可参考文章:遗传算法

(4)确定交叉操作
交叉算子采用最简单的单点交叉算子,单点交叉操作的具体思想可以参考:遗传算法理解(通俗易懂)

单点交叉操作示意图:
遗传+BP神经网络 求解故障诊断问题(python)_第13张图片
(图片来源:[遗传算法](https://my.oschina.net/u/1412321/blog/192454)

本例采用单点交叉的方法,其具体操作过程是:
• 先对群体进行随机配对;
• 其次随机设置交叉点位置;
• 最后再相互交换配对染色体之间的部分基因。

(5)确定变异操作
变异操作采用最简单的单点变异操作,按照变异概率选取某一条染色体后,再随机选取该染色体上的一个基因位,该基因位上的数字为0则变为1,为1则变为0。
遗传+BP神经网络 求解故障诊断问题(python)_第14张图片
(图片来源:[遗传算法](https://my.oschina.net/u/1412321/blog/192454)

3.2 BP神经网络算法部分

采用累计误差的方式来计算BP神经网络训练过程产生的误差,即将9个训练样本数据全部一次性的投入网络中计算出输出层的累计加权误差。
遗传+BP神经网络 求解故障诊断问题(python)_第15张图片
遗传+BP神经网络 求解故障诊断问题(python)_第16张图片
遗传+BP神经网络 求解故障诊断问题(python)_第17张图片
(图片来源:神经网络算法介绍(Nerual NetWorks)
https://blog.csdn.net/qq_37406130/article/details/79157025)

4.结果展示

加入遗传算法来确定最佳的BP神经网络初始权值和阈值,训练的时间太长了,为了验证代码结构是正确的,在尽量不破坏已经建好的程序框架的前提下,利用遗传算法生成的一个染色体解码后再归一化处理的结果来作为BP神经网络初始的权值和阈值,而不再新建函数来得到区间【-1,1】内随机生成的初始权值和阈值。

4.1训练过程中出现的问题

4.1.1初始权值和阈值太大,导致隐含层的输出为1

在这里插入图片描述
如果完全按照书上说的,10位二进制数转换成10进制数后权值和阈值的数值就会非常大,BP神经网络隐含层神经元的传递函数tansig(),当输入值较大时,由该函数的图像可知,函数值趋近于1,导致隐含层的输出结果都为1,同理,输出层神经元的传递函数采用logsig(),当乘以的权值较大时,会使输出层最后的输出值也都为1。

后来我才明白,除了对输入的样本数据进行归一化操作,应该也要对初始权值和阈值进行归一化操作。

神经网络的初始权值和阈值为什么都归一化0到1之间
BP神经网络(原理及MATLAB实现)

数据归一化:将数据映射到[0, 1]或[-1, 1]区间或其他的区间。
归一化算法:
1.y = ( x - min )/( max - min );
2.y = 2 * ( x - min ) / ( max - min ) - 1。

采用第二种方法对初始权值和阈值进行归一化操作。

4.1.2反向传播更新权值和阈值后,导致权值和阈值为0

遗传+BP神经网络 求解故障诊断问题(python)_第18张图片
结合logsig()函数的图像说明输出层的每一个神经元的累加输入和都为0,怀疑可能是该层的权值和阈值有问题,参看输出权值和阈值发现其值都为0。

4.1.3输出层得到的数据不是0或1

输出层采用logsig()函数对输入数据进行处理,按理说logsig()函数得到的值只能是0-1内的数,但实际输出值有大于1的情况,见下面截图,开始百思不得其解,后来发现logsig()函数入参的数据类型是float64,将数据类型转化为int32就可以了。
遗传+BP神经网络 求解故障诊断问题(python)_第19张图片

def logsig(x):
    s = 1 / (1 + np.exp(-x))
    print(type(s[0]))
    print('转化数据类型前:', s)
    s = s.astype('int32')
    print('转化数据类型后:', s)
    print(type(s[0]))
    return s

遗传+BP神经网络 求解故障诊断问题(python)_第20张图片

4.1.4网络训练结束后,测试到的结果都是无故障

遗传+BP神经网络 求解故障诊断问题(python)_第21张图片
输入三组测试数据,得到的实际结果都是无故障,明显不对。
由于训练样本数量较少,才九组,同一特征情况的样本才三个,所以训练模型采用投放一个样本就调整一次网络权值的模式。
遗传+BP神经网络 求解故障诊断问题(python)_第22张图片
上述现象怀疑训练样本进入网络的顺序是不是会影响网络训练的效果。做了几次不同的实验,发现我训练的神经网络第一个输入的特征是什么,在用测试样本进行测试时,给出的结果就是什么,感觉就认识第一眼看到的。调整程序后,发现最后输入的特征是什么,测试样本进行测试时,网络的实际输出表现就是什么,这次又是遗忘了前面的印象只认最后的特征。这个网络现在肯定是有问题的。后面再想办法调程序,应该是前向传播时误差为0时,对多样本数据的处理判断有问题。后面网络的训练数据是以下面这组数据来进行的,数据的排序如下:
遗传+BP神经网络 求解故障诊断问题(python)_第23张图片

4.2算例结果展示

目前只实现了一般的BP神经网络求解故障诊断问题,遗传算法已经部署好了,但还没有比较使用遗传算法来优化BP神经网络初始权值和阈值,和随机生成【-1,1】区间内的初始权值和阈值这两种方法在性能上的优劣。但是书中已经对比得出了使用遗传算法或其他算法来初始化BP神经网络的初始权值和阈值比随机生成要好,毕竟第一印象很重要嘛。
遗传+BP神经网络 求解故障诊断问题(python)_第24张图片
从测试样本的数据看:网络的实际输出值和样本数据期望的输出值是对应的,但这个结果存在随机性,并不能保证每次测试的结果都是对的。当获取到一组可以使得测试样本通过的权值和阈值时,应该保存下来,作为备选的网络初始权值和阈值,下次训练就可以直接调用这组数据进行BP网络的训练。

4.3主程序代码

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author : Logintern09


import BP_data
from BP import BP_NN_process
from initialize_BP_param_GA import GA_operation
import numpy as np
import numpy.linalg as LA


# BP神经网络的相关参数
back_update_param_model = 0  # 该可调变量的值为0表示将样本数据一个个的输入得到每一个的输出后,逐个调整网络的参数,当样本数据较大时,也许还没全部输入就已经使网络满足了停止迭代的条件
# back_update_param_model = 1  # 该可调变量的值为1表示将所有的样本数据输入得到整体的输出后,一次性的调整整个网络的参数
network_layer = 2  # 二层网络
hiddenLayer_num = network_layer - 1
inputLayer_neuronNum = np.size(BP_data.test_sample_data, 1)
hiddenLayer_neuronList = [31]  # 只有一层隐含层,隐含层的神经元数量是31
hiddenLayer_neuronNum = np.array(hiddenLayer_neuronList)
outputLayer_neuronNum = 3
network_link_paramNum = 2  # 神经元间的连接参数主要是两个,权值和阈值
network_neuronNum_list = [inputLayer_neuronNum] + hiddenLayer_neuronNum.tolist() + [outputLayer_neuronNum]


net_param_dict = {}
net_param_dict['network_layer'] = network_layer
net_param_dict['inputLayer_neuronNum'] = inputLayer_neuronNum
net_param_dict['hiddenLayer_neuronNum'] = hiddenLayer_neuronNum
net_param_dict['outputLayer_neuronNum'] = outputLayer_neuronNum
net_param_dict['network_link_paramNum'] = network_link_paramNum
net_param_dict['network_neuronNum_list'] = network_neuronNum_list
net_param_dict['train_error'] = 0.01
net_param_dict['Learn_rate'] = 0.09
net_param_dict['back_update_param_model'] = back_update_param_model
BP_train_steps = 500

# 遗传算法参数
GA_parameter_dict = dict()
GA_parameter_dict['popSize'] = 1
GA_parameter_dict['max_steps'] = 0
GA_parameter_dict['Pc'] = 0.7
GA_parameter_dict['Pm'] = 0.01

# 确定遗传算法的编码形式
GA_parameter_dict['solution_layer'] = 4  # 输入层到隐含层的权重、阈值、隐含层到输出层的权重、阈值
GA_parameter_dict['each_layer_num'] = [inputLayer_neuronNum * hiddenLayer_neuronList[0], hiddenLayer_neuronList[0], hiddenLayer_neuronList[0] * outputLayer_neuronNum, outputLayer_neuronNum]
GA_parameter_dict['ind_size'] = 10  # 个体的二进制编码长度


GA_class = GA_operation(GA_parameter_dict)


# 得到遗传算法的适应度值
def get_fitness(population):
    fitness = np.zeros((np.size(population, 0), 1))
    for i in range(np.size(population, 0)):
        chrom = population[i, :]
        # 网络训练的过程是一个不断修正权值和阈值的过程
        BP_class = BP_NN_process(net_param_dict, GA_parameter_dict)
        # 第一步先确定神经网络的结构
        BP_class.set_network_structure()
        # 第二步初始化网络的参数
        BP_class.initialize_network_param(chrom)
        for _ in range(BP_train_steps):
            error = BP_class.net_train_process()
            if np.sum(error) < net_param_dict['train_error']:
                break
        # 网络训练之后需要对网络进行测试
        output_array = BP_class.net_test_process()
        # 测试样本实际输出与期望输出之间的范数作为遗传算法的适应度函数值
        expected_output_array = BP_data.test_output_data
        print('real_output_array', output_array)
        print('expected_output_array', expected_output_array)
        fitness_value = LA.norm(output_array - expected_output_array)
        fitness[i, :] = 1 / fitness_value
    return fitness


def BP_process(best_solution):
    # 将最佳的解结构传回BP神经网络中作为初始网络参数训练网络
    BP_class = BP_NN_process(net_param_dict, GA_parameter_dict)
    # 第一步先确定神经网络的结构
    BP_class.set_network_structure()
    # 第二步初始化网络的参数
    BP_class.initialize_network_param(best_solution)
    output_array = BP_class.net_test_process()
    # 根据测试数据的网络测试结果来衡量BP神经网络是否被训练好了
    exit_flag = BP_class.final_test_step(output_array)
    if exit_flag:
        print('BP神经网络训练合格')
    else:
        print('BP神经网络训练失败')


def GA_process():
    value_list, solution_list = GA_class.iter_operation()
    # 解码得到本次BP迭代操作的最佳解结构,即初始权值和阈值的参数矩阵
    best_solution = solution_list[value_list.index(min(value_list))]
    return best_solution


def main():
    best_solution = GA_process()
    # BP_process(best_solution)

main()

5.小结

后期要继续深入学习神经网络算法,企图将本次训练的网络作为基础,不断提高网络训练的深度,并用其他问题的数据来测试该网络模型的稳健性。(理论还不是很懂,上面都是我的大白话,没有什么学术依据,用的可能不是什么学术的专有词汇。。。)

题外话:

1.Python代码规范和命名规范
https://www.jianshu.com/p/9f60a54ea067
2.camelCase命名约定 https://blog.csdn.net/steven088/article/details/7174546
3.Pycharm编写代码PEP规范提示:https://www.jianshu.com/p/d76d9e5b2c06

代码写完了,测试很重要,至少要让文章布局看起来你的算法计算结果是正确的,我常用的测试手段就是单元测试、输入数据的边界测试,我编代码常犯的错误是:(1)输入变量没有初始化;(2)while循环没有考虑退出条件,如果输入数据的范围变化,可能会陷入死循环,要考虑用try…except来捕获异常,并且做好输入数据的边界测试,使用多组测试用例;(3)变量的命名不规范,好的命名方式应该是不需要看注释就能够通过读代码了解每行代码要表示的意思。

参考文献:
1.基于遗传算法的BP神经网络优化算法:https://blog.csdn.net/dulingtingzi/article/details/51114832
(也是对《matlab智能优化算法30个案例分析》一书该章节的知识分享)

2.@staticmethod怎么用? https://www.cnblogs.com/594504110python/p/10062336.html
3.python二进制怎么转十进制? https://www.php.cn/python-tutorials-421751.html
4.Python numpy中矩阵的用法总结:https://www.cnblogs.com/wj-1314/p/10244807.html
5.python3_实现BP神经网络 + BP神经网络应用实例
6.NumPy中的维度(dimension)、轴(axis)、秩(rank)的含义
7.np.astype() https://blog.csdn.net/qq_41621362/article/details/94405846

你可能感兴趣的:(遗传+BP神经网络 求解故障诊断问题(python))