python深度学习之GA(遗传算法)优化LSTM神经⽹络
上次优化完bp神经⽹络后,发现⽤matlab优化⾼级的神经⽹络太慢了,于是⽤tensorflow继续学习GA优化部分。
1.项⽬概述
本⽂采⽤的是python编程,使⽤的数据集是mnist⼿写数据集,该数据集主要是对0-9的⼿写数字体识别,虽然说图像识别⽅⾯⽤CNN识别率较⾼,但这⾥LSTM也可以获取较⾼的准确率。
2.优化参数
本⽂优化的是LSTM的层数参数和各层神经元参数,其中包含了lstm层和Dense层,其中我们规定了神经⽹络的层数不超过3层,每层的神经元个数在[32,256]之间。
3.注意事项
1.本⽂的遗传算法编码并⾮2进制编码,⽽是由各个参数组成的⼀维数组。
2.在遗传算法交叉、变异操作中,关于神经⽹络层数的参数不进⾏交叉,只对神经⽹神经元个数进⾏交叉、变异。
3.⽂件为两部分每⼀部分为lstm的部分,⼀部分为ga部分
4.项⽬如下(代码已详细注释,不懂的可在评论区询问)
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.ist.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=ularizers.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)
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 =s((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 =s((lstm_dense_layers,)))
lstm_dense_dropout_name =s((lstm_dense_layers,)))
lstm_dense_batch_name =s((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_modelpile(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
#不显⽰警告信息
#设置遗传算法的参数
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
# 定义变异函数
random pythondef mutate(child):
#变异操作也只是针对后6位参数
#变异操作也只是针对后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 = py()
#遍历pop中的每⼀个种,进⾏交叉,变异,遗传操作
for parent in pop:
child = crossover(parent,pop_copy)
child = mutate(child)
parent = child
5.运⾏结果如下
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)
速度还是挺慢的,还有待优化。
参考⽂献:
链接: .