赞
踩
1.项目概述
本文采用的是python编程,使用的数据集是mnist手写数据集,该数据集主要是对0-9的手写数字体识别,虽然说图像识别方面用CNN识别率较高,但这里LSTM也可以获取较高的准确率。
2.优化参数
本文优化的是LSTM的层数参数和各层神经元参数,其中包含了lstm层和Dense层,其中我们规定了神经网络的层数不超过3层,每层的神经元个数在[32,256]之间。
3.注意事项
1.本文的遗传算法编码并非2进制编码,而是由各个参数组成的一维数组。
2.在遗传算法交叉、变异操作中,关于神经网络层数的参数不进行交叉,只对神经网神经元个数进行交叉、变异。
3.文件为两部分每一部分为lstm的部分,一部分为ga部分
LSTM部分
- #本章节GA_LSTM是关于遗传算法优化lstm算法的层数和全连接层数及每层神经元的个数
- import numpy as np
- import pandas as pd
- import tensorflow as tf
- import matplotlib as plt
- from tensorflow.keras.layers import Input,LSTM,Dropout,Dense,BatchNormalization
- from tensorflow.keras import optimizers,losses,metrics,models,Sequential
- '''
- 本文的主要内容如下:
- 1.本文章是对lstm网络的优化,优化的参数主要有:lstm层的层数,lstm隐藏层的神经元个数,dense层的层数,dense层的神经元个数
- 2.本文章利用的是遗传算法进行优化,其中编码形式并未采用2进制编码,只是将2数组之间的元素交换位置。
- 3.本文的lstm和dense的层数都在1-3的范围内,因为3层的网络足以拟合非线性数据
- 4.程序主要分为2部分,第一部分是lstm网络的设计,第二部分是遗传算法的优化。
- 5.代码的解释已详细写在对应的部分,有问题的同学可以在评论区进行交流
- '''
- #导入数据集,本文用的是mnist手写数据集,该数据主要是对手写体进行识别0-9的数字
- def load_data():
- #从tensorflow自带的数据集中导入数据
- (x_train,y_train),(x_test,y_test)=tf.keras.datasets.mnist.load_data()
- #主要进行归一化操作
- x_train, x_test = x_train / 255.0, x_test / 255.0
- return x_train,x_test,y_test,y_train
-
-
- #定义LSTM模型
- def lstm_mode(inputs, units_num, sequences_state):
- #input主要是用来定义lstm的输入,input的一般是在第一层lstm层之前,units_num即是隐藏层神经元个数,sequence_state即是lstm层输出的方式
- lstm=LSTM(units_num,return_sequences=sequences_state)(inputs)
- print("lstm:",lstm.shape)
- return lstm
-
-
- #定义全连接层、BN层
- def dense_mode(input,units_num):
- #这里主要定义全连接层的输入,input参数定义dense的第一次输入,units_num代表隐藏层神经元个数
- #这里定义全连接层,采用L2正则化来防止过拟合,激活函数为relu
- dense=Dense(units_num,kernel_regularizer=tf.keras.regularizers.l2(0.001),activation='relu')(input)
- print("dense:",dense.shape)
- #定义dropout层,概率为0.2
- drop_out=Dropout(rate=0.2)(dense)
- #定义BN层,可以理解为是隐藏层的标准化过程
- dense_bn=BatchNormalization()(drop_out)
- return dense,drop_out,dense_bn
-
-
- #这里定义的即是评价lstm效果的函数——也是遗传算法的适应度函数
- def aim_function(x_train,y_train,x_test,y_test,num):
- #这里传入数据和参数数组num,num保存了需要优化的参数
- #这里我们设置num数组中num[0]代表lstm的层数。
- lstm_layers=num[0]
- #num[2:2 + lstm_layers]分别为lstm各层的神经元个数,有同学不知道num(1)去哪了(num(1)为全连接层的层数)
- lstm_units = num[2:2 + lstm_layers]
- #将num
- lstm_name = list(np.zeros((lstm_layers,)))
- # 设置全连接层的参数
- #num(1)为全连接的参数
- lstm_dense_layers = num[1]
- #将lstm层之后的地方作为全连接层各层的参数
- lstm_dense_units = num[2 + lstm_layers: 2 + lstm_layers + lstm_dense_layers]
- #
- lstm_dense_name = list(np.zeros((lstm_dense_layers,)))
- lstm_dense_dropout_name = list(np.zeros((lstm_dense_layers,)))
- lstm_dense_batch_name = list(np.zeros((lstm_dense_layers,)))
- #这主要是定义lstm的第一层输入,形状为训练集数据的形状
- inputs_lstm = Input(shape=(x_train.shape[1], x_train.shape[2]))
-
-
- #这里定义lstm层的输入(如果为第一层lstm层,则将初始化的input输入,如果不是第一层,则接受上一层输出的结果)
- for i in range(lstm_layers):
- if i == 0:
- inputs = inputs_lstm
- else:
- inputs = lstm_name[i - 1]
- if i == lstm_layers - 1:
- sequences_state = False
- else:
- sequences_state = True
- #通过循环,我们将每层lstm的参数都设计完成
- lstm_name[i] = lstm_mode(inputs, lstm_units[i], sequences_state=sequences_state)
-
- #同理设计全连接层神经网络的参数
- for i in range(lstm_dense_layers):
- if i == 0:
- inputs = lstm_name[lstm_layers - 1]
- else:
- inputs = lstm_dense_name[i - 1]
-
- lstm_dense_name[i], lstm_dense_dropout_name[i], lstm_dense_batch_name[i] = dense_mode(inputs,units_num=lstm_dense_units[i])
-
- #这里是最后一层:分类层,softmax
- outputs_lstm = Dense(10, activation='softmax')(lstm_dense_batch_name[lstm_dense_layers - 1])
- print("last_dense",outputs_lstm.shape)
-
- # 利用函数式调试神经网络,调用inputs和outputs之间的神经网络
- LSTM_model =tf.keras.Model(inputs=inputs_lstm, outputs=outputs_lstm)
- # 编译模型
- LSTM_model.compile(optimizer=optimizers.Adam(),
- loss='sparse_categorical_crossentropy',
- metrics=['accuracy'])
- print("训练集形状",x_train.shape)
-
- history = LSTM_model.fit(x_train, y_train,batch_size=32, epochs=1, validation_split=0.1, verbose=1)
- # 验证模型,model.evaluate返回的值是一个数组,其中score[0]为loss,score[1]为准确度
- acc = LSTM_model.evaluate(x_test, y_test, verbose=0)
- return acc[1]
-
-
GA优化部分
- #GA优化lstm的遗传算法部分
- import GA_LSTM_lstm as ga
- import numpy as np
- import pandas as pd
- import matplotlib as plt
- import os
- #不显示警告信息
- os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
- #设置遗传算法的参数
- DNA_size = 2
- DNA_size_max = 8 # 每条染色体的长度
- POP_size = 20 # 种群数量
- CROSS_RATE = 0.5 # 交叉率
- MUTATION_RATE = 0.01# 变异率
- N_GENERATIONS = 40 # 迭代次数
-
-
- #接收数据
- x_train,x_test,y_test,y_train = ga.load_data()
-
- # 定义适用度函数,即aim_function函数,接收返回值
- def get_fitness(x):
- return ga.aim_function(x_train,y_train,x_test,y_test,num=x)
-
- # 生成新的种群
- def select(pop,fitness):
- #这里主要是进行选择操作,即从20个种群中随机选取重复随机采样出20个种群进行种群初始化操作,p代表被选择的概率,这里采用的是轮盘赌的方式
- idx = np.random.choice(np.arange(POP_size),size=POP_size,replace=True,p=fitness/fitness.sum())
- #将选择的种群组成初始种群pop
- return pop[idx]
-
- # 交叉函数
- def crossover(parent,pop):
- #这里主要进行交叉操作,随机数小于交叉概率则发生交叉
- if np.random.rand() < CROSS_RATE:
- #从20个种群中选择一个种群进行交叉
- i_ = np.random.randint(0,POP_size,size=1) # 染色体的序号
- #这里将生成一个8维的2进制数,并转换层成bool类型,true表示该位置交叉,False表示不交叉
- cross_points = np.random.randint(0,2,size=DNA_size_max).astype(np.bool) # 用True、False表示是否置换
-
- # 这一部分主要是对针对不做变异的部分
- for i,point in enumerate(cross_points):
- '''
- 第一部分:这里是指该位点为神经元个数的位点,本来该交换,但其中位点为0,
- 什么意思呢?即[2,3,32,43,34,230,43,46,67]和[2,2,32,54,55,76,74,26,0],末尾的0位置就
- 不应该交叉,因为交叉完后,会对前两位的参数产生影响。
-
- 第二部分:即对前两位不进行交叉操作,因为前两位代表的是层数,层数交叉后会对神经元的个数产生影响
- '''
- #第一部分
- if point == True and pop[i_,i] * parent[i] == 0:
- cross_points[i] = False
- #第二部分
- if point == True and i < 2:
- cross_points[i] = False
- # 将第i_条染色体上对应位置的基因置换到parent染色体上
- parent[cross_points] = pop[i_,cross_points]
- return parent
-
-
- # 定义变异函数
- def mutate(child):
- #变异操作也只是针对后6位参数
- for point in range(DNA_size_max):
- if np.random.rand() < MUTATION_RATE:
- #2位参数之后的参数才才参与变异
- if point >= 2:
- if child[point] != 0:
- child[point] = np.random.randint(32,257)
- return child
-
- #初始化2列层数参数
- pop_layers = np.zeros((POP_size,DNA_size),np.int32)
- pop_layers[:,0] = np.random.randint(1,4,size=(POP_size,))
- pop_layers[:,1] = np.random.randint(1,4,size=(POP_size,))
-
- # 种群
- #初始化20x8的种群
- pop = np.zeros((POP_size,DNA_size_max))
- # 将初始化的种群赋值,前两列为层数参数,后6列为神经元个数参数
- for i in range(POP_size):
- #随机从[32,256]中抽取随机数组组成神经元个数信息
- pop_neurons = np.random.randint(32,257,size=(pop_layers[i].sum(),))
- #将2列层数信息和6列神经元个数信息合并乘8维种群信息
- pop_stack = np.hstack((pop_layers[i],pop_neurons))
- #将这些信息赋值给pop种群进行初始化种群
- for j,gene in enumerate(pop_stack):
- pop[i][j] = gene
-
- # 在迭代次数内,计算种群的适应度函数
- for each_generation in range(N_GENERATIONS):
- # 初始化适应度
- fitness = np.zeros([POP_size,])
- # 遍历20个种群,对基因进行操作
- for i in range(POP_size):
- pop_list = list(pop[i])
- # 第i个染色体上的基因
- #对赋值为0的基因进行删除
- for j,each in enumerate(pop_list):
- if each == 0.0:
- index = j
- pop_list = pop_list[:j]
- #将基因进行转换为int类型
- for k,each in enumerate(pop_list):
- each_int = int(each)
- pop_list[k] = each_int
- #将计算出来的适应度填写在适应度数组中
- fitness[i] = get_fitness(pop_list)
- #输出结果
- print('第%d代第%d个染色体的适应度为%f'%(each_generation+1,i+1,fitness[i]))
- print('此染色体为:',pop_list)
- print('Generation:',each_generation+1,'Most fitted DNA:',pop[np.argmax(fitness),:],'适应度为:',fitness[np.argmax(fitness)])
-
- # 生成新的种群
- pop = select(pop,fitness)
-
- # 复制一遍种群
- pop_copy = pop.copy()
- #遍历pop中的每一个种群,进行交叉,变异,遗传操作
- for parent in pop:
- child = crossover(parent,pop_copy)
- child = mutate(child)
- parent = child
-
- 1688/1688 [==============================] - 21s 10ms/step - loss: 0.7340 - accuracy: 0.8076 - val_loss: 0.1616 - val_accuracy: 0.9730
- 第1代第1个染色体的适应度为0.966900
- 此染色体为: [1, 1, 151, 248]
- lstm: (None, 83)
- dense: (None, 200)
- last_dense (None, 10)
- 训练集形状 (60000, 28, 28)
- 1688/1688 [==============================] - 19s 10ms/step - loss: 0.7532 - accuracy: 0.7855 - val_loss: 0.1744 - val_accuracy: 0.9672
- 第1代第2个染色体的适应度为0.961300
- 此染色体为: [1, 1, 83, 200]
- lstm: (None, 28, 135)
- lstm: (None, 28, 41)
- lstm: (None, 126)
- dense: (None, 47)
- last_dense (None, 10)
- 训练集形状 (60000, 28, 28)
- 1688/1688 [==============================] - 33s 17ms/step - loss: 0.7534 - accuracy: 0.7755 - val_loss: 0.1258 - val_accuracy: 0.9717
- 第1代第3个染色体的适应度为0.967800
- 此染色体为: [3, 1, 135, 41, 126, 47]
- lstm: (None, 28, 247)
- lstm: (None, 28, 82)
- lstm: (None, 71)
- dense: (None, 190)
- dense: (None, 161)
- dense: (None, 124)
- last_dense (None, 10)
- 训练集形状 (60000, 28, 28)
-
欢迎点赞 收藏 加关注
参考链接:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。