遗传算法与深度学习实战(29)——编码卷积自编码器架构

遗传算法与深度学习实战(29)------编码卷积自编码器架构

    • [0. 前言](#0. 前言)
    • [1. 构建卷积自编码器](#1. 构建卷积自编码器)
    • [2. 构建卷积自编码器基因序列](#2. 构建卷积自编码器基因序列)
    • [3. 解析基因序列构建模型](#3. 解析基因序列构建模型)
    • 小结
    • 系列链接

0. 前言

使用遗传算法 (Genetic Algorithm, GA) 构建自编码器 (AutoEncoder, AE)优化器时,第一步是构建将架构编码为基因序列的模式,借鉴使用遗传算法自动优化卷积神经网络的基因构建思想,并引入 AE 的限制条件。同时,模型通过添加 BatchNormalizationDropout 来改进自编码器模型。

1. 构建卷积自编码器

(1) 导入所需库和数据集:

python 复制代码
import numpy as np
import tensorflow as tf
import numpy as np
import random

from tensorflow.keras.datasets import mnist
from tensorflow.keras import layers, models, Input, Model
from tensorflow.keras.callbacks import EarlyStopping

from IPython import display
from IPython.display import clear_output
import matplotlib.pyplot as plt
from tensorflow.keras.utils import plot_model
plt.gray()

# load dataset
(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.fashion_mnist.load_data()

# split dataset
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype("float32") / 255.0
test_images = test_images.reshape(test_images.shape[0], 28, 28, 1).astype("float32") / 255.0

class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']

import math

def plot_data(num_images, images, labels):
    grid = math.ceil(math.sqrt(num_images))
    plt.figure(figsize=(grid*2,grid*2))
    for i in range(num_images):
        plt.subplot(grid,grid,i+1)
        plt.xticks([])
        plt.yticks([])
        plt.grid(False)     
        plt.imshow(images[i].reshape(28,28))
        plt.xlabel(class_names[labels[i]])      
    plt.show()

plot_data(25, train_images, train_labels)

(2) 构建、编译并训练模型:

python 复制代码
# input layer
input_layer = Input(shape=(28, 28, 1))

# encoding architecture
encoded_layer1 = layers.Conv2D(64, (3, 3), activation='relu', padding='same')(input_layer)
encoded_layer1 = layers.MaxPool2D( (2, 2), padding='same')(encoded_layer1)
encoded_layer2 = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(encoded_layer1)
encoded_layer2 = layers.MaxPool2D( (2, 2), padding='same')(encoded_layer2)
encoded_layer3 = layers.Conv2D(16, (3, 3), activation='relu', padding='same')(encoded_layer2)
latent_view    = layers.MaxPool2D( (2, 2), padding='same')(encoded_layer3)

#decoding architecture
decoded_layer1 = layers.Conv2D(16, (3, 3), activation='relu', padding='same')(latent_view)
decoded_layer1 = layers.UpSampling2D((2, 2))(decoded_layer1)
decoded_layer2 = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(decoded_layer1)
decoded_layer2 = layers.UpSampling2D((2, 2))(decoded_layer2)
decoded_layer3 = layers.Conv2D(64, (3, 3), activation='relu')(decoded_layer2)
decoded_layer3 = layers.UpSampling2D((2, 2))(decoded_layer3)
#output layer
output_layer   = layers.Conv2D(1, (3, 3), padding='same')(decoded_layer3)

# compile the model
model = Model(input_layer, output_layer)
model.compile(optimizer='adam', loss='mse')
model.summary()
plot_model(model)

history_loss = []
history_val_loss = []

def add_history(history):
    history_loss.append(history.history["loss"])
    history_val_loss.append(history.history["val_loss"])

def reset_history():
    global history_loss
    global history_val_loss
    history_loss = []
    history_val_loss = []
    return []

def plot_results(num_images, images, labels, history):
    add_history(history)
    grid = math.ceil(math.sqrt(num_images))
    plt.figure(figsize=(grid*2,grid*2))
    for i in range(num_images):
        plt.subplot(grid,grid,i+1)
        plt.xticks([])
        plt.yticks([])
        plt.grid(False)     
        plt.imshow(images[i].reshape(28,28))
        plt.xlabel(class_names[labels[i]])      
    plt.show()
    plt.plot(history_loss, label='loss')
    plt.plot(history_val_loss, label='val_loss')
    plt.legend()
plt.show()

EPOCHS = 3
history = reset_history()

for i in range(EPOCHS):
    history = model.fit(train_images, train_images, epochs=1, batch_size=2048, validation_data=(test_images, test_images))
    pred_images = model.predict(test_images[:25])
    clear_output()
plot_results(25, pred_images[:25], test_labels[:25], history)
# input layer
input_layer = Input(shape=(28, 28, 1))

# encoding architecture
x = layers.Conv2D(64, (3, 3), activation='relu', padding='same')(input_layer)
#x = layers.BatchNormalization()(x)
x = layers.MaxPool2D( (2, 2), padding='same')(x)
#x = layers.Dropout(0.5)(x)
x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(x)
#x = layers.BatchNormalization()(x)
x = layers.MaxPool2D( (2, 2), padding='same')(x)
#x = layers.Dropout(0.5)(x)
x = layers.Conv2D(16, (3, 3), activation='relu', padding='same')(x)
#x = layers.BatchNormalization()(x)
x = layers.MaxPool2D( (2, 2), padding='same')(x)
#x = layers.Dropout(0.5)(x)

#decoding architecture
x = layers.Conv2D(16, (3, 3), activation='relu', padding='same')(x)
#x = layers.Dropout(0.5)(x)
x = layers.UpSampling2D((2, 2))(x)
#x = layers.BatchNormalization()(x)
x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(x)
#x = layers.Dropout(0.5)(x)
x = layers.UpSampling2D((2, 2))(x)
#x = layers.BatchNormalization()(x)
x = layers.Conv2D(64, (3, 3), activation='relu')(x)
#x = layers.Dropout(0.5)(x)
x = layers.UpSampling2D((2, 2))(x)
#x = layers.BatchNormalization()(x)
#output layer
output_layer   = layers.Conv2D(1, (3, 3), padding='same')(x)

# compile the model
model = Model(input_layer, output_layer)
model.compile(optimizer='adam', loss='mse')
model.summary()
plot_model(model)

EPOCHS = 10
history = reset_history()

for i in range(EPOCHS):
    history = model.fit(train_images, train_images, epochs=1, batch_size=2048, validation_data=(test_images, test_images))
    pred_images = model.predict(test_images[:25])
    clear_output()
plot_results(25, pred_images[:25], test_labels[:25], history)

2. 构建卷积自编码器基因序列

在基因序列的编码模式考虑卷积/最大池化层和上采样/卷积层,表示编码器卷积层的编码标记定义为 CONV_LAYER,表示解码器的上采样/卷积层的标记定义为 UPCONV_LAYER

(1) 接下来,生成编码器层 (CONV_LAYER) 和解码器层 (UPCONV_LAYER):

python 复制代码
max_layers = 10
max_neurons = 128
min_neurons = 16
max_kernel = 3
min_kernel = 3
max_pool = 2
min_pool = 2

CONV_LAYER = -1
CONV_LAYER_LEN = 4
BN_LAYER = -3
BN_LAYER_LEN = 1
DROPOUT_LAYER = -4
DROPOUT_LAYER_LEN = 2
UPCONV_LAYER = -2
UPCONV_LAYER_LEN = 4
def generate_neurons():
    return random.randint(min_neurons, max_neurons)

def generate_kernel():
    part = []
    part.append(random.randint(min_kernel, max_kernel))
    part.append(random.randint(min_kernel, max_kernel))
    return part

def generate_conv_layer():
    part = [CONV_LAYER] 
    part.append(generate_neurons())
    part.extend(generate_kernel())  
    return part

def generate_upconv_layer():
    part = [UPCONV_LAYER] 
    part.append(generate_neurons())
    part.extend(generate_kernel())  
    return part
编写函数在模型中添加BatchNormalization和dropout。
def generate_bn_layer():
    part = [BN_LAYER] 
    return part

def generate_dropout_layer():
    part = [DROPOUT_LAYER] 
    part.append(random.uniform(0,.5))  
    return part

(2) 实现 create_offspring() 函数创建基因序列,函数分为两个循环:一个用于编码器部分,另一个用于解码器部分。编码器部分遍历网络层,并随机检查是否应该添加另一个卷积层,如果添加了一个卷积层,则继续随机检查是否应该添加批归一化层或 dropout 层。并自动添加了一个最大池化层,以实现自编码器的降维架构:

python 复制代码
def create_offspring():
    ind = []
    layers = 0
    for i in range(max_layers):
        if i==0: #first layer always convolutational
            ind.extend(generate_conv_layer())      
            layers += 1
        elif random.uniform(0,1)<.5:
            #add convolution layer
            ind.extend(generate_conv_layer())
            layers += 1
            if random.uniform(0,1)<.5:
                #add batchnormalization
                ind.extend(generate_bn_layer())
            if random.uniform(0,1) < .5:
                ind.extend(generate_dropout_layer()) 
    for i in range(layers):
        ind.extend(generate_upconv_layer())
        if random.uniform(0,1)<.5:
            #add batchnormalization
            ind.extend(generate_bn_layer())
        if random.uniform(0,1) < .5:
            ind.extend(generate_dropout_layer())
    return ind
        
individual = create_offspring()
print(individual)

解码器部分是编码器的镜像。因此,通过与编码器相同的迭代次数进行循环,添加上采样和卷积层组合。之后,以应用概率检查添加 BatchNormalizationDropout 层。

3. 解析基因序列构建模型

(1) 接下来,通过解析基因序列构建模型。首先循环遍历每个基因,并检查它是否与一个网络层标记匹配。如果匹配,则将相应的层和选项添加到模型中。对于编码器卷积层 (CONV_LAYER),如果输入形状大于 (7, 7),添加一个最大池化层,用于确保模型保持降维架构:

python 复制代码
def padding(gene):
    return "same" if gene == 1 else "valid"

def build_model(individual):
    input_layer = Input(shape=(28, 28, 1))  
    il = len(individual)
    i = 0
    x = input_layer
    while i < il:    
        if individual[i] == CONV_LAYER:      
            pad="same" 
            n = individual[i+1]
            k = (individual[i+2], individual[i+3])
            i += CONV_LAYER_LEN        
            x = layers.Conv2D(n, k, activation='relu', padding=pad)(x) 
            if x.shape[1] > 7:
                x = layers.MaxPool2D( (2, 2), padding='same')(x)

(2) 继续检查标记添加网络层,对于 UPCONV_LAYER 解码器层,检查模型输出是否与输入大小相同:

python 复制代码
        elif individual[i] == BN_LAYER: #add batchnormal layer
            x = layers.BatchNormalization()(x)
            i += BN_LAYER_LEN      
        elif individual[i] == DROPOUT_LAYER: #add dropout layer            
            x = layers.Dropout(individual[i+1])(x) 
            i += DROPOUT_LAYER_LEN
        elif individual[i] == UPCONV_LAYER:
            pad="same"
            n = individual[i+1]
            k = (individual[i+2], individual[i+3])        
            x = layers.Conv2D(n, k, activation='relu', padding=pad)(x)   
            x = layers.UpSampling2D((2, 2))(x)   
            i += CONV_LAYER_LEN   
            if x.shape[1] == (28):
                break #model is complete
        else:
            break

build_model() 函数构建、编译模型并返回模型。在返回之前,通过检查最后一个解码器层的形状来确认模型输出,如果输出尺寸较小,通过添加一个上采样层,将输出大小从 (14, 14) 加倍到 (28, 28)

为了测试 build_model() 函数,创建 100 个随机后代,并评估模型的大小。生成随机的个体基因序列,然后根据这些序列构建相应的模型。在此过程中,跟踪生成的最小和最大模型:

python 复制代码
    if x.shape[1] == 14:
        x = layers.UpSampling2D((2, 2))(x)
        
    output_layer = layers.Conv2D(1, (3, 3), padding='same')(x)
    model = Model(input_layer, output_layer)
    model.compile(optimizer='adam', loss='mse')
    return model

model = build_model(individual) 
model.summary()

(3) 使用最小参数模型进行 10epochs 的训练,输出如下图所示:

python 复制代码
max_model = None
min_model = None
maxp = 0
minp = 10000000

for i in range(100):
    individual = create_offspring() 
    model = build_model(individual)
    p = model.count_params() 
    if p > maxp:
        maxp = p
        max_model = model
    if p < minp:
        minp = p
        min_model = model 
  
max_model.summary(line_length=100)
min_model.summary(line_length=100)

EPOCHS = 10
history = reset_history()

#model = max_model
model = min_model

for i in range(EPOCHS):
    history = model.fit(train_images, train_images, epochs=1, batch_size=2048, validation_data=(test_images, test_images))
    pred_images = model.predict(test_images[:25])
    clear_output()
    plot_results(25, pred_images[:25], test_labels[:25], history)

可以看到,使用 create_offspring()build_model() 随机生成的模型比我们手动优化的自编码器更好,这表明了使用遗传算法优化自编码器模型的有效性。

同样,我们可以测试最大参数模型。可以通过完成以下问题进一步了解网络架构编码:

  • 调用 create_offspring() 函数创建个体列表,打印并比较不同模型
  • 将概率从 0.5 修改为其他值,观察对生成的模型有什么影响

小结

使用卷积层的复杂自编码器可能较难构建,可以使用神经进化构建定义编码器和解码器部分的分层架构。在编码器和解码器中使用卷积层需要额外的上采样层和匹配的层配置,这些配置可以编码成自定义的遗传序列。

系列链接

遗传算法与深度学习实战(1)------进化深度学习
遗传算法与深度学习实战(2)------生命模拟及其应用
遗传算法与深度学习实战(3)------生命模拟与进化论
遗传算法与深度学习实战(4)------遗传算法(Genetic Algorithm)详解与实现
遗传算法与深度学习实战(5)------遗传算法中常用遗传算子
遗传算法与深度学习实战(6)------遗传算法框架DEAP
遗传算法与深度学习实战(7)------DEAP框架初体验
遗传算法与深度学习实战(8)------使用遗传算法解决N皇后问题
遗传算法与深度学习实战(9)------使用遗传算法解决旅行商问题
遗传算法与深度学习实战(10)------使用遗传算法重建图像
遗传算法与深度学习实战(11)------遗传编程详解与实现
遗传算法与深度学习实战(12)------粒子群优化详解与实现
遗传算法与深度学习实战(13)------协同进化详解与实现
遗传算法与深度学习实战(14)------进化策略详解与实现
遗传算法与深度学习实战(15)------差分进化详解与实现
遗传算法与深度学习实战(16)------神经网络超参数优化
遗传算法与深度学习实战(17)------使用随机搜索自动超参数优化
遗传算法与深度学习实战(18)------使用网格搜索自动超参数优化
遗传算法与深度学习实战(19)------使用粒子群优化自动超参数优化
遗传算法与深度学习实战(20)------使用进化策略自动超参数优化
遗传算法与深度学习实战(21)------使用差分搜索自动超参数优化
遗传算法与深度学习实战(22)------使用Numpy构建神经网络
遗传算法与深度学习实战(23)------利用遗传算法优化深度学习模型
遗传算法与深度学习实战(24)------在Keras中应用神经进化优化
遗传算法与深度学习实战(25)------使用Keras构建卷积神经网络
遗传算法与深度学习实战(26)------编码卷积神经网络架构
遗传算法与深度学习实战(27)------进化卷积神经网络
遗传算法与深度学习实战(28)------卷积自编码器详解与实现

相关推荐
陈天伟教授3 分钟前
基于学习的人工智能(4)机器学习基本框架
人工智能·学习·机器学习
studytosky16 分钟前
深度学习理论与实战:MNIST 手写数字分类实战
人工智能·pytorch·python·深度学习·机器学习·分类·matplotlib
做萤石二次开发的哈哈21 分钟前
11月27日直播预告 | 萤石智慧台球厅创新场景化方案分享
大数据·人工智能
AGI前沿25 分钟前
AdamW的继任者?AdamHD让LLM训练提速15%,性能提升4.7%,显存再省30%
人工智能·算法·语言模型·aigc
后端小肥肠44 分钟前
小佛陀漫画怎么做?深扒中老年高互动赛道,用n8n流水线批量打造
人工智能·aigc·agent
是店小二呀44 分钟前
本地绘图工具也能远程协作?Excalidraw+cpolar解决团队跨网画图难题
人工智能
i爱校对1 小时前
爱校对团队服务全新升级
人工智能
KL132881526931 小时前
AI 介绍的东西大概率是不会错的,包括这款酷铂达 VGS耳机
人工智能
vigel19901 小时前
人工智能的7大应用领域
人工智能
哥布林学者1 小时前
吴恩达深度学习课程三: 结构化机器学习项目 第一周:机器学习策略(二)数据集设置
深度学习·ai