遗传算法与深度学习实战(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)------卷积自编码器详解与实现

相关推荐
文慧的科技江湖10 分钟前
图文结合 - 光伏系统产品设计PRD文档 -(慧哥)慧知开源充电桩平台
人工智能·开源·储能·训练·光伏·推理
白熊18814 分钟前
【计算机视觉】CV实战项目 - 基于YOLOv5与DeepSORT的智能交通监控系统:原理、实战与优化
人工智能·yolo·计算机视觉
别摸我的婴儿肥19 分钟前
从0开始LLM-注意力机制-2
深度学习
gis收藏家26 分钟前
几何编码:启用矢量模式地理空间机器学习
人工智能·机器学习
不吃酸的柠檬31 分钟前
MATLAB 中的图形绘制
人工智能·机器学习·matlab
Ai工具分享1 小时前
6 种AI实用的方法,快速修复模糊照片
人工智能
dundunmm1 小时前
【每天一个知识点】如何解决大模型幻觉(hallucination)问题?
人工智能·数据挖掘·大模型
勤奋的小懒猪1 小时前
Halcon应用:相机标定之应用
图像处理·人工智能·计算机视觉
白熊1881 小时前
【计算机视觉】CV实战项目- 深度解析FaceAI:一款全能的人脸检测与图像处理工具库
图像处理·人工智能·计算机视觉
量子位1 小时前
实测免费 DeepResearch!轻量版深夜上线,基于 o4-mini,速度更快 / 重视脉络梳理
人工智能·openai