利用遗传算法优化GANs
GANs是在训练阶段最需要计算的密集型模型之一,因为它相当于同时训练两个神经网络。对于我的普通电脑来说,把gan训练到收敛是非常困难的。
遗传算法是根据大自然中生物体进化规律而设计提出的,是根据大自然中生物体进化规律而设计提出的。是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法。
在本片文章中,我们尝试使用遗传算法来对训练GANs进行优化,我们的训练模型是生成手写数字。
什么是遗传算法?
遗传算法是一种学习算法,它利用交叉两个好的神经网络的权值的思想,从而得到一个更好的神经网络。
遗传算法如此有效的原因是没有直接的优化算法,所以允许有极端变化的结果的可能性。此外,他们通常会提出非常有趣的解决方案,这些方案通常会对问题提供有价值的见解。
它们如何工作?
生成一组随机权重。 这是第一个代理的神经网络。 在代理上执行了一组测试。 代理会根据测试获得分数。 重复几次以创建种群。选择种群的前10%以进行交叉。 从最高的10%中选择两个随机的父母,他们的权重是交叉的。 每次发生交叉时,发生突变的可能性都很小:这是一个随机值,不会受到父母的影响。
这个过程会慢慢地优化代理的性能,因为代理会慢慢地适应环境。
优点
计算不密集:没有线性代数计算要完成。唯一必要的机器学习计算是通过神经网络的正向传递。因此,与深度神经网络相比,系统要求非常广泛。
适应性强:可以改编并插入许多不同的测试和方法来操纵遗传算法的灵活性。可以通过使代理传播生成器网络并使用鉴别器作为测试,在遗传算法内创建GAN。这是一个至关重要的好处,这使我相信将来遗传算法的使用将更加广泛。
可解释的:对于普通的神经网络,该算法的学习模式是不可解释的。对于遗传算法,很容易理解为什么会发生某些事情:例如,当给遗传算法提供Tic-Tac-Toe环境时,某些可识别的策略就会慢慢发展。这是一个很大的好处,因为使用机器学习就是使用技术来帮助我们了解重要事项。
缺点
可能需要很长时间:某些的交叉和变异可能会对程序的准确性造成负面影响,从而使程序收敛或达到某个损失阈值的速度变慢。。
代码
在对遗传算法有了一个一些的了解后,我现在可以向你展示这个程序:
import randomimport numpy as npfrom IPython.display import clear_outputfrom keras.layers import Reshapefrom keras.layers import Flattenfrom keras.layers import Conv2Dfrom keras.layers import Conv2DTransposefrom keras.layers import LeakyReLUfrom keras.layers import Dropout,Densefrom keras.optimizers import Adamfrom keras.models import Sequentialfrom keras.datasets.mnist import load_data(trainX, trainy), (testX, testy) = load_data()
这里我们使用Keras进行鉴别器部分的构建,但是遗传算法中的神经网络是以numpy为基础进行构建的。
class g enetic_algorithm: def execute(pop_size,generations,threshold,network): class Agent: def __init__(self,network):
这是' geneticalgorithm'类的创建,其中包含与遗传算法有关的所有功能以及其应如何发挥作用。 主要功能是它以popsize,generations,threshold,network作为参数。 pop_size是生成的种群的大小,generation是时代的术语,threshold是损失值的阈值。 X和y用于标记数据的遗传算法。 对于没有数据或未标记数据的问题,可以删除X和y的所有实例。
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
适应度函数是此遗传算法的独特部分:
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
(鉴别器的神经网络将在以后定义。 该模型将根据之前加载的MNIST数据集进行训练。 该模型采用卷积网络的形式,以返回二进制结果)
此功能模仿了进化中的选择理论:最佳选择生存,而其他选择则死亡。 在这种情况下,它们的数据将被遗忘并且不再使用。
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. 压平父母的权重
1. 产生两个分裂点
1. 使用拆分点作为索引来设置两个子代理的权重
def mutation(agents): for agent in agents: if random.uniform(0.0, 1.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 = 28latent_size = 100model = Sequential()model.add(Conv2D(64, (3,3), strides=(2, 2), 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=(2, 2), 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_algorithmagent = ga.execute(1000,1000,90,network)(trainX, trainy), (testX, testy) = load_data()weights = agent.neural_network.weights
这就是卷积网络实现的鉴别器。Imagesize是MNIST图像的大小,latentsize用于确保
这将执行整个遗传算法。 对于网络变量,每个嵌套列表都包含输入神经元编号,输出神经元编号和激活函数。 execute函数返回最佳代理。
总结
显然,遗传算法的收敛速度不会像基于梯度的算法那样快,但计算工作在较长一段时间内分散,使它在计算机上不那么密集!