【深度学习】遗传算法优化GAN

机器学习初学者

共 6346字,需浏览 13分钟

 ·

2021-01-22 11:04

作者 | Victor Sim 

编译 | VK 

来源 | Towards Data Science

GANs是计算量最大的模型之一,因为它相当于同时训练两个神经网络。对于我那台糟糕的便携式计算机来说,训练一个GAN直到收敛是非常困难的。我写了一个通用的遗传算法,可以适应许多不同的问题。我采用这种遗传算法来训练GANs,生成手写数字。

什么是遗传算法

遗传算法是一种学习算法,它利用两个好的神经网络的权值交叉的思想,可以得到一个更好的神经网络。

遗传算法之所以如此有效,是因为没有直接的优化算法,允许有可能产生极其不同的结果。此外,他们通常会提出非常有趣的解决方案,这些解决方案往往能为问题提供有价值的结论。

它们是如何工作的

生成一组随机权重。这是第一个智能体的神经网络。对智能体执行一组测试。智能体收到基于测试的分数。重复此操作几次以创建种群。选择种群的前10%可以进行繁衍。从前10%中随机选择两个父母,他们的权重是交叉的。每次交叉发生时,都有一个很小的突变机会:这是一个随机值,不在父对象的权重中。

随着智能体慢慢适应环境,此过程会慢慢优化智能体的性能。

优缺点:

优点:
  • 计算不密集

不需要进行线性代数计算。唯一需要的机器学习计算是通过神经网络前向传播。因此,与深层神经网络相比,需求非常广泛。

  • 适应性强

人们可以调整和插入许多不同的测试和方法来操纵遗传算法的灵活性。人们可以在遗传算法中创建一个GAN,方法是让智能体作为生成器网络,通过鉴别器进行测试。这是一个关键的好处,它使我相信遗传算法在未来的应用将更加广泛。

  • 可以理解

对于普通神经网络,该算法的学习模式是神秘的。对于遗传算法来说,我们很容易理解为什么会发生一些事情:例如,当一个遗传算法被赋予Tic-Tac-Toe环境时,某些可识别的策略会慢慢发展。这是一个很大的好处,因为使用机器学习是为了使用技术来帮助我们深入了解重要问题。

缺点:
  • 需要很长时间

  • 不好的交叉和突变会对程序的准确性产生负面影响,从而使程序收敛速度减慢或达到某个损失阈值。

代码

既然你对遗传算法及其优点和缺点有了相当全面的了解,我现在就可以向你展示该程序:

import random
import numpy as np
from IPython.display import clear_output
from keras.layers import Reshape
from keras.layers import Flatten
from keras.layers import Conv2D
from keras.layers import Conv2DTranspose
from keras.layers import LeakyReLU
from keras.layers import Dropout,Dense
from keras.optimizers import Adam
from keras.models import Sequential
from keras.datasets.mnist import load_data

(trainX, trainy), (testX, testy) = load_data()

鉴别器需要Keras,但是遗传算法中的神经网络是由下面的代码创建的,在代码中,它是以numpy为基础构建的。

class genetic_algorithm:
        
    def execute(pop_size,generations,threshold,network):
        class Agent:
            def __init__(self,network):

这是“genetic_algorithm”类的创建,它包含了所有与遗传算法有关的函数以及它应该如何工作。主要函数是execute函数,它以pop_size,generations,threshold,network为参数。pop_size是生成的种群的大小,generations是表示epoch的术语,threshold是你满意的损失值。X和y表示标记数据。network是神经网络的结构。

class neural_network:
                    def __init__(self,network):
                        self.weights = []
                        self.activations = []
                        for layer in network:
                            if layer[0] != None:
                                input_size = layer[0]
                            else:
                                input_size = network[network.index(layer)-1][1]
                            output_size = layer[1]
                            activation = layer[2]
                            
                            self.weights.append(np.random.randn(input_size,output_size))
                            self.activations.append(activation)
                    def propagate(self,data):
                        input_data = data
                        for i in range(len(self.weights)):
                            z = np.dot(input_data,self.weights[i])
                            a = self.activations[i](z)
                            input_data = a
                        yhat = a
                        return yhat
                self.neural_network = neural_network(network)
                self.fitness = 0

此脚本描述每个智能体的神经网络的权重初始化和网络传播。

def generate_agents(population, network):
            return [Agent(network) for _ in range(population)]

此函数创建将要测试的第一组智能体。

def fitness(agents):
            for agent in agents:
                dataset_len = 100
                fake = []
                real = []
                y = []
                for i in range(dataset_len//2):
                fake.append(agent.neural_network.propagate(np.random.randn(latent_size)).reshape(28,28))
                    y.append(0)
                    real.append(random.choice(trainX))
                    y.append(1)
                X = fake+real
                X = np.array(X).astype('uint8').reshape(len(X),28,28,1)
                y = np.array(y).astype('uint8')
                model.fit(X,y,verbose = 0)
                
                fake = []
                real = []
                y = []
                for i in range(dataset_len//2):
                    fake.append(agent.neural_network.propagate(np.random.randn(latent_size)).reshape(28,28))
                    y.append(0)
                    real.append(random.choice(trainX))
                    y.append(1)
                X = fake+real
                X = np.array(X).astype('uint8').reshape(len(X),28,28,1)
                y = np.array(y).astype('uint8')
                agent.fitness = model.evaluate(X,y,verbose = 0)[1]*100
            return agents

适应度函数是该遗传算法的独特部分:

稍后将定义鉴别器型神经网络。此模型将基于先前加载的MNIST数据集进行训练。该模型以卷积网络的形式返回结果。

def selection(agents):
            agents = sorted(agents, key=lambda agent: agent.fitness, reverse=False)
            print('\n'.join(map(str, agents)))
            agents = agents[:int(0.2 * len(agents))]
            return agents

这个函数模仿了进化中的选择理论:最好的种群生存,而其他则任由他们死去。

def unflatten(flattened,shapes):
            newarray = []
            index = 0
            for shape in shapes:
                size = np.product(shape)
                newarray.append(flattened[index : index + size].reshape(shape))
                index += size
            return newarray

要执行交叉和变异函数,需要将权重展平,并将其展开为原始形状。

def crossover(agents,network,pop_size):
            offspring = []
            for _ in range((pop_size - len(agents)) // 2):
                parent1 = random.choice(agents)
                parent2 = random.choice(agents)
                child1 = Agent(network)
                child2 = Agent(network)
                
                shapes = [a.shape for a in parent1.neural_network.weights]
                
                genes1 = np.concatenate([a.flatten() for a in parent1.neural_network.weights])
                genes2 = np.concatenate([a.flatten() for a in parent2.neural_network.weights])
                
                split = random.ragendint(0,len(genes1)-1)child1_genes = np.asrray(genes1[0:split].tolist() + genes2[split:].tolist())
                child2_genes = np.array(genes1[0:split].tolist() + genes2[split:].tolist())
                
                child1.neural_network.weights = unflatten(child1_genes,shapes)
                child2.neural_network.weights = unflatten(child2_genes,shapes)
                
                offspring.append(child1)
                offspring.append(child2)
            agents.extend(offspring)
            return agents

交叉函数是程序中最复杂的函数之一。它生成两个新的“子代”智能体,其权重被替换为两个父代的交叉。这是创建权重的过程:

  1. Flatten化父代的权重

  2. 生成两个分割点

  3. 使用分割点作为索引来设置两个子智能体的权重

这是特征交叉的全过程。

def mutation(agents):
            for agent in agents:
                if random.uniform(0.01.0) <= 0.1:
                    weights = agent.neural_network.weights
                    shapes = [a.shape for a in weights]flattened = np.concatenate([a.flatten() for a in weights])
                    randint = random.randint(0,len(flattened)-1)
                    flattened[randint] = np.random.randn()newarray = [a ]
                    indeweights = 0
                    for shape in shapes:
                        size = np.product(shape)
                        newarray.append(flattened[indeweights : indeweights + size].reshape(shape))
                        indeweights += size
                    agent.neural_network.weights = newarray
            return agents

这就是突变函数。展平与交叉函数相同。不是拆分点,而是选择一个随机点,用一个随机值替换。

for i in range(generations):
            print('Generation',str(i),':')
            agents = generate_agents(pop_size,network)
            agents = fitness(agents)
            agents = selection(agents)
            agents = crossover(agents,network,pop_size)
            agents = mutation(agents)
            agents = fitness(agents)
            
            if any(agent.fitness < threshold for agent in agents):
                print('Threshold met at generation '+str(i)+' !')
                
            if i % 100:
                clear_output()
                
        return agents[0]

这是execute函数的最后一部分,它执行已定义的所有函数。

image_size = 28
latent_size = 100

model = Sequential()
model.add(Conv2D(64, (3,3), strides=(22), padding='same', input_shape=(image_size,image_size,1)))
model.add(LeakyReLU(alpha=0.2))
model.add(Dropout(0.4))
model.add(Conv2D(64, (3,3), strides=(22), padding='same'))
model.add(LeakyReLU(alpha=0.2))
model.add(Dropout(0.4))
model.add(Flatten())
model.add(Dense(1, activation='sigmoid'))
opt = Adam(lr=0.0002, beta_1=0.5)
model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])

network = [[latent_size,100,sigmoid],[None,image_size**2,sigmoid]]
ga = genetic_algorithm
agent = ga.execute(1000,1000,90,network)
(trainX, trainy), (testX, testy) = load_data()
weights = agent.neural_network.weights

对于网络变量,每个嵌套列表包含输入神经元数、输出神经元数和激活函数。execute函数返回最佳智能体。

结论

显然,遗传算法不会像基于梯度的算法那样快速收敛,但是计算工作会在较长的时间内进行,这使得它在计算机上的密集度降低了!

感谢你阅读我的文章!


往期精彩回顾





本站知识星球“黄博的机器学习圈子”(92416895)

本站qq群704220115。

加入微信群请扫码:

浏览 9
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报