深度学习与遗传算法的碰撞——利用遗传算法优化深度学习网络结构(详解与实现)

在这里插入图片描述

感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的:

① 2000多本Python电子书(主流和经典的书籍应该都有了)

② Python标准库资料(最全中文版)

③ 项目源码(四五十个有趣且经典的练手项目及源码)

④ Python基础入门、爬虫、web开发、大数据分析方面的视频(适合小白学习)

⑤ Python学习路线图(告别不入流的学习)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

前言


近年来,深度学习模型性能取得了飞跃,可以在单个网络中使用大量隐藏层。训练深度学习模型可能会占用大量计算资源,并且通常在图形处理单元(GPU)上进行,同时为了获得最优的模型性能,可能需要网络架构和超参数的反复修改和调整,通常此过程取决于实际问题和网络架构设计人员的经验,而利用遗传算法可以将此过程自动化,同时可以在可接受的时间开销内找到更好的网络架构。专门的深度学习库,例如TensorFlow,能够利用基于GPU的计算平台,本文使用MNIST数据集和Tensorflow构建简单的全连接网络,利用遗传算法优化隐藏层数和每层的节点数。虽然全连接网络是十分基础简单的网络,但是,使用的原理同样适用于更复杂的网络和数据集。

以下是所用库:

  1. tensorflow2.x

  2. deap

  3. matplotlib

优化深度学习分类器的架构


在创建神经网络模型可以执行给定的机器学习任务时,一项关键工作是设计网络体系结构的配置。对于多层感知器,输入层和输出层中的节点数取决于当前问题的特征。因此,要做出的选择是关于隐藏层——有多少层以及每层有多少个节点。可以采用一些经验进行尝试,但是在多数情况下,确定最佳架构可能需要反复试验。

处理网络体系结构参数的一种方法是将它们视为模型的超参数,使用这些超参数构建网络,并将训练后网络的性能作为适应度进行评价。接下来,将使用遗传算法找到隐藏层的最佳组合。

全连接网络

隐藏层配置的染色体表示


由于MLP的体系结构由隐藏层配置决定,在tensorflow.keras中可通过改变Dense层的units参数获得节点数不同的全连接隐藏层:

Dense(units, activation=None,…)

同时,可以通过for来构建所需层数,例如,如果要为MLP配置三个有20个节点的隐藏层,则可以通过以下方式:

self.model = Sequential()

for l in range(3):

self.model.add(layers.Dense(20,activation=‘relu’))

因此,我们需要提出既可以表示层数又可以表示每一层节点数的染色体。

同时,为了能够使用标准遗传算子,使用固定长度的染色体表示形式。使用这种方法时,预先确定最大层数,但为了层数可变,可以在染色体中设置无效位(也可以称为终止参数),使模型构建提前终止。例如,将网络限制为四个隐藏层,则染色体将如下所示:

[ n 1 , n 2 , n 3 , n 4 ] [n_1, n_2, n_3, n_4] [n1​,n2​,n3​,n4​]

其中, n i n_i ni​表示 i i i层中的节点数。

为了控制网络中隐藏层的实际数量,其中一些值可能为零或负数。该值意味着之后不会再有其他层添加到网络:

  1. 染色体[10, 20, -5, 15]表示元组(10, 20),因为-5是无效位。

  2. 染色体[10, 0, -5, 15]表示元组(10, ),因为0是无效位。

  3. 染色体[10, 20, 5, -15]表示元组(10, 20, 5),因为-15是无效位。

  4. 染色体[10, 20, 5, 15]表示元组(10, 20, 5, 15)。

为了保证至少有一个隐藏层,可以强制第一个参数始终大于零。其他层参数可以在零附近分布,以便可以控制它们成为终止参数。

另外,由于染色体中值有限定区间,可以选择使用浮点数列表构建染色体。使用浮点数列表使我们能够使用现有的遗传算子。为了构建网络可以使用round()函数可以将浮点数转换回整数:

  1. 染色体[9.35, 10.71, -2.51, 17.99]可以转化为元组(9, 11)

  2. 染色体[9.35, 10.71, 2.51, -17.99]可以转化为元组(9, 11, 3)

要评估构建的网络结构,需要创建实现这些层的MLP分类器,对其进行训练并进行评估。

评估个体的适应度得分


MLPLayers类封装了MNIST数据集的MLP分类器的构建以及模型准确率的评估。

#导入必要库

import tensorflow as tf

from tensorflow import keras

from tensorflow.keras import layers,Sequential,losses,optimizers,datasets

from math import floor

导入优化器,损失函数模块

from tensorflow.keras import optimizers,losses

MLPLayers类主要包括以下方法

  1. preprocess(self,x,y)用于构建训练数据集的预处理

  2. initDataset(self)用于构建训练数据集

  3. convertParams(self,params)将params的列表转换为可以有效构建模型的元组

  4. getAccuracy(self,params)构建模型,训练,并返回最后一个epoch的验证准确率,用于适应度评估。

  5. testLayer(self),使用经验值构建的分类模型,用于和优化得到的网络进行对比

  6. formatParams(self, params)用于格式化输出染色体

class MLPLayers:

def init(self):

self.initDataset()

def preprocess(self,x,y):

x = tf.reshape(x, [-1])

return x,y

def initDataset(self):

(self.X_train,self.y_train),(self.X_test,self.y_test) = datasets.mnist.load_data()

self.X_train = tf.convert_to_tensor(self.X_train,dtype=tf.float32) / 255.

self.X_test = tf.convert_to_tensor(self.X_test,dtype=tf.float32) / 255.

self.y_train = tf.convert_to_tensor(self.y_train,dtype=tf.int32)

self.y_test = tf.convert_to_tensor(self.y_test,dtype=tf.int32)

self.y_train = tf.one_hot(self.y_train,depth=10)

self.y_test = tf.one_hot(self.y_test,depth=10)

self.train_db = tf.data.Dataset.from_tensor_slices((self.X_train,self.y_train))

self.validation_db = tf.data.Dataset.from_tensor_slices((self.X_test,self.y_test))

self.train_db = self.train_db.shuffle(1000).map(self.preprocess).batch(128)

self.validation_db = self.validation_db.shuffle(1000).map(self.preprocess).batch(128)

def convertParams(self,params):

if round(params[1]) <= 0:

hiddenLayerSizes = round(params[0]),

elif round(params[2]) <= 0:

hiddenLayerSizes = (round(params[0]), round(params[1]))

elif round(params[3]) <= 0:

hiddenLayerSizes = (round(params[0]), round(params[1]), round(params[2]))

else:

hiddenLayerSizes = (round(params[0]), round(params[1]), round(params[2]), round(params[3]))

return hiddenLayerSizes

def getAccuracy(self,params):

#将染色体转化为可以有效构建网络的元组

hiddenLayerSizes = self.convertParams(params)

self.model = Sequential()

#构建网络

for l in hiddenLayerSizes:

self.model.add(layers.Dense(l,activation=‘relu’))

self.model.add(layers.Dense(10,activation=‘relu’))

self.model.build(input_shape=(4,28*28))

self.model.summary()

self.model.compile(optimizer=optimizers.Adam(lr=0.01),

loss=losses.CategoricalCrossentropy(from_logits=True),

metrics=[‘accuracy’])

指定训练集为db,验证集为val_db,训练5个epochs,每1个epoch验证一次

history = self.model.fit(self.train_db, epochs=5, validation_data=self.validation_db, validation_freq=1,verbose=2)

#返回最后一个epoch训练后的验证准确率,用于适应度评估

return history.history[‘val_accuracy’][-1]

def testLayer(self):

创建5层的全连接层网络

network = Sequential([layers.Dense(256, activation=‘relu’),

layers.Dense(128, activation=‘relu’),

layers.Dense(64, activation=‘relu’),

layers.Dense(32, activation=‘relu’),

layers.Dense(10)])

network.build(input_shape=(4, 28*28))

network.summary()

采用Adam优化器,学习率为0.01;采用交叉熵损失函数,包含Softmax

network.compile(optimizer=optimizers.Adam(lr=0.01),

loss=losses.CategoricalCrossentropy(from_logits=True),

metrics=[‘accuracy’] # 设置测量指标为准确率

)

指定训练集为db,验证集为val_db,训练5个epochs,每1个epoch验证一次

history = network.fit(self.train_db, epochs=5, validation_data=self.validation_db, validation_freq=1,verbose=2)

#打印结果

print(history.history[‘val_accuracy’][-1])

def formatParams(self, params):

return “‘hidden_layer_sizes’={}”.format(self.convertParams(params))

使用遗传算法优化MLP架构


现在,我们已经有了MLP的体系结构配置,以及确定每种配置的MLP准确率的方法,接下来,创建基于遗传算法的优化程序以对配置进行搜索——隐藏层的数量以及每层中的节点数量——产生最佳分类准确率。

详细的步骤在注释中进行介绍

from deap import base

from deap import creator

from deap import tools

import random

import numpy as np

import matplotlib.pyplot as plt

import seaborn as sns

#创建MlpLayersTest类的实例,用于测试隐藏层架构的各种组合

test = MLPLayers()

首先为代表隐藏层的每个float值设置上下边界。第一个隐藏层的范围为[100,300],而其余的层则从负值开始,增加终止层数的机会:

BOUNDS_LOW = [100,-25,-50,-75]

BOUNDS_HIGH = [300,200,100,50]

NUM_OF_PARAMS = len(BOUNDS_LOW)

#超参数:

POPULATION_SIZE = 50

P_CROSSOVER = 0.9

P_MUTATION = 0.5

MAX_GENERATIONS = 20

HALL_OF_FAME_SIZE = 5

CROWDING_FACTOR = 10.0

toolbox = base.Toolbox()

#定义最大化适用度策略:

creator.create(“FitnessMax”,base.Fitness,weights=(1.0,))

#基于列表创建个体类:

creator.create(“Individual”,list,fitness=creator.FitnessMax)

#由于解由一系列不同区间的浮点值表示,因此我们使用以下循环并为每个区间创建一个单独的toolbox运算符(layer_size_attribute),用于在适当范围内生成随机浮点值:

for i in range(NUM_OF_PARAMS):

#“layer_size_attribute_0”,“layer_size_attribute_1”…

toolbox.register(“layer_size_attribute_”+str(i),

random.uniform,

BOUNDS_LOW[i],

BOUNDS_HIGH[i])

#创建layer_size_attributes元组,其中包含我们刚刚为每个隐藏层创建的单独的浮点数生成器:

layer_size_attributes = ()

for i in range(NUM_OF_PARAMS):

layer_size_attributes = layer_size_attributes + (toolbox.getattribute(“layer_size_attribute_”+str(i)),)

#将此layer_size_attributes元组与DEAP的内置initCycle()运算符结合使用,以创建一个新的individualCreator运算符,该运算符将随机生成的隐藏层值组合起来填充单个实例

toolbox.register(“individualCreator”,tools.initCycle,creator.Individual,layer_size_attributes,n=1)

#定义种群创建运算符:

toolbox.register(“populationCreator”,tools.initRepeat,list,toolbox.individualCreator)

#使用类的getAccuracy()方法进行适应度评估

def classificationAccuracy(individual):

return test.getAccuracy(individual),

toolbox.register(“evaluate”,classificationAccuracy)

#遗传算子定义:对于选择运算符,使用锦标赛大小为2的锦标赛选择,使用专门用于有界浮动列表染色体的交叉和变异运算符,并为它们提供定义的上下限:

toolbox.register(“select”,tools.selTournament,tournsize=2)

toolbox.register(“mate”,tools.cxSimulatedBinaryBounded,low=BOUNDS_LOW,up=BOUNDS_HIGH,eta=CROWDING_FACTOR)

toolbox.register(“mutate”,tools.mutPolynomialBounded,low=BOUNDS_LOW,up=BOUNDS_HIGH,eta=CROWDING_FACTOR,indpb=1.0/NUM_OF_PARAMS)

现在能在网上找到很多很多的学习资源,有免费的也有收费的,当我拿到1套比较全的学习资源之前,我并没着急去看第1节,我而是去审视这套资源是否值得学习,有时候也会去问一些学长的意见,如果可以之后,我会对这套学习资源做1个学习计划,我的学习计划主要包括规划图和学习进度表。

分享给大家这份我薅到的免费视频资料,质量还不错,大家可以跟着学习

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 15
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值