使用Python创建遗传算法: (Creating Genetic Algorithms With Python:)
介绍: (Introduction:)
Everyone knows about neural networks and Gradient Descent, but much less are familiar with unsupervised machine learning algorithms. Today I am here to introduce Genetic Algorithms and their implementation in Python, and hopefully, get you to understand more about genetic algorithms as well.
每个人都对神经网络和梯度下降有所了解,但对无监督机器学习算法的了解却很少。 今天,我在这里向您介绍遗传算法及其在Python中的实现,并希望您也能进一步了解遗传算法。
遗传算法: (Genetic Algorithms:)
Genetic Algorithms, like other machine learning algorithms, are trying to optimize a set of hyper-parameters to make predictions of a neural network as accurate as possible.
像其他机器学习算法一样,遗传算法也在尝试优化一组超参数,以使神经网络的预测尽可能准确。
Gradient Descent and other optimization algorithms usually use partial derivatives to find the general direction to minimize the loss function and thus improve the accuracy of the neural network.
梯度下降和其他优化算法通常使用偏导数来找到一般方向,以使损失函数最小化,从而提高神经网络的准确性。
Genetic Algorithms, on the other hand, mimic evolution to optimize the network. Here how it works:
另一方面,遗传算法模仿进化来优化网络。 运作方式如下:
- An agent is generated. It contains a set of weights that are compatible with the neural network. 代理已生成。 它包含一组与神经网络兼容的权重。
- The agent’s fitness value is calculated by comparing predictions made with the set of weights and the true y-values. 通过将权重集和真实y值进行比较,可以得出代理商的适应度值。
- Repeat this many times until you have a “population” of agents 重复多次,直到有大量的代理商
- Sort the population by their fitness, with the agents with the best fitness at the top of the list. 按适合程度对总体进行排序,最适合的代理商位于列表顶部。
- “Breed” the top agents together. 一起“育种”顶级代理商。
- Repeat until a satisfactory loss value has been reached. 重复直到达到令人满意的损耗值。
建立遗传算法: (Building a Genetic Algorithm:)
Disclaimer: This code has been adapted from the code here to make it compatible to optimize neural networks.
免责声明:此代码已从此处的代码改编,以使其兼容以优化神经网络。
步骤1 | 先决条件: (Step 1| Prerequisites:)
import random
import numpy as npdef sigmoid(x):
return 1/(1+np.exp(-x))
These are the most basic dependencies for the project. To make the learning process better, you can add more activation functions for your suitable datasets.
这些是项目的最基本依赖项。 为了使学习过程更好,您可以为合适的数据集添加更多激活函数。
步骤2 | 代理蓝图: (Step 2| The Agent Blueprint:)
class Agent:
def __init__(self,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 __str__(self):
return 'Loss: ' + str(self.fitness[0])
Each agent has its own set of weights, generated randomly with the architecture of the neural network setup. The argument network is a list that looks like so:
每个代理都有自己的权重集,这些权重是通过神经网络设置的体系结构随机生成的。 参数网络是一个看起来像这样的列表:
network = [[3,10,sigmoid],[None,1,sigmoid]]
Each term in the list is describing a layer in the Neural Network. The first term in each nested list is the number of input neurons for the layer, the second term is the output and the third the activation function to be applied for the layer.
列表中的每个术语都描述了神经网络中的一层。 每个嵌套列表中的第一项是该层的输入神经元数量,第二项是输出,第三项是要应用于该层的激活函数。
步骤3 | 生成代理: (Step 3| Generating Agents:)
def generate_agents(population, network):
return [Agent(network) for _ in range(population)]
This is used to generate the agents, as detailed in step 1 of the methodology to run a genetic algorithm.
这用于生成代理,如方法的步骤1所述,用于运行遗传算法。
步骤4 | 计算健身度: (Step 4| Calculate Fitness:)
def fitness(agents,X,y):
for agent in agents:
yhat = agent.neural_network.propagate(X)
cost = (yhat - y)**2
agent.fitness = sum(cost)
return agents
This fitness function is essentially calculating the MSE value for each agent, by using the prediction and the true y-values.
该适应度函数实际上是通过使用预测和真实y值来计算每个代理的MSE值。
步骤5 | 代理商选择: (Step 5| Agent Selection:)
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
This code essentially sorts the agents by their fitness value, from the lowest value to the highest. Keep in mind that the lower the fitness value, the better. It then shortens the list of agents to only the top 20% of the agents.
此代码实质上是根据代理的适用性值(从最低值到最高值)对它们进行排序。 请记住,健身值越低越好。 然后,它将代理列表缩短到仅排名前20%的代理。
Step 6| Agent Crossover:
步骤6 | 代理交叉:
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.randint(0,len(genes1)-1)child1_genes = np.array(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
This is the most complex part of the program. In the crossover process two random parents are chosen from within the selected agents. The nested list of weights are reduced to one long list. A splitting point is randomly picked. This determines how much of each parent’s weights the child will be composed of. This child’s weights is then reformatted and the child is added to the list of agents.
这是程序中最复杂的部分。 在交叉过程中,从所选代理中选择了两个随机父代。 嵌套的权重列表减少为一个长列表。 随机选择一个分割点。 这决定了孩子组成每个父母的权重。 然后重新格式化该孩子的权重,并将该孩子添加到代理列表中。
步骤7 | 突变: (Step 7| Mutation:)
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 = []
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
Genetic Algorithms are technically “blind” as they have no insight to how the surface plotted by the loss function looks like. This means it is very easy for the genetic algorithm to get stuck at a certain point. Mutation allows for slight, random, infrequent changes to stir the pot and get the system out of local minimas.
遗传算法在技术上是“盲目的”,因为它们无法了解损失函数绘制的表面的外观。 这意味着遗传算法很容易卡在某个点上。 变异允许轻微,随机,不频繁的变化来搅拌锅,使系统脱离局部最小值。
步骤8 | 放在一起: (Step 8| Putting it all together:)
import random
import numpy as np
from IPython.display import clear_outputdef sigmoid(x):
return 1/(1+np.exp(-x))class genetic_algorithm:
def execute(pop_size,generations,threshold,X,y,network):
class Agent:
def __init__(self,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 __str__(self):
return 'Loss: ' + str(self.fitness[0])
def generate_agents(population, network):
return [Agent(network) for _ in range(population)]
def fitness(agents,X,y):
for agent in agents:
yhat = agent.neural_network.propagate(X)
cost = (yhat - y)**2
agent.fitness = sum(cost)
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
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.randint(0,len(genes1)-1)child1_genes = np.array(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
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 = []
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,X,y)
agents = selection(agents)
agents = crossover(agents,network,pop_size)
agents = mutation(agents)
agents = fitness(agents,X,y)
if any(agent.fitness < threshold for agent in agents):
print('Threshold met at generation '+str(i)+' !')
if i % 100:
clear_output()
return agents[0]
return agents
X = np.array([[0, 0, 1], [1, 1, 1], [1, 0, 1], [0, 1, 1]])
y = np.array([[0, 1, 1, 0]]).T
network = [[3,10,sigmoid],[None,1,sigmoid]]
ga = genetic_algorithm
agent = ga.execute(100,5000,0.1,X,y,network)
weights = agent.neural_network.weights
agent.neural_network.propagate(X)
Wrap everything up within an execute function, customize arguments and you have got yourself a genetic algorithm. The execute function will return the last agent, which is presumably the best agent to be created.
将所有内容包装在执行函数中,自定义参数,您便拥有了遗传算法。 execute函数将返回最后一个代理,它可能是要创建的最佳代理。
最后的想法: (Final Thoughts:)
Apart from optimizing Neural Networks, genetic algorithms have a unique use that other optimizers don’t have. This is for datasets that don’t have y-values that use non-traditional loss functions to evaluate the accuracy of a model.
除了优化神经网络外,遗传算法还有其他优化器没有的独特用途。 这适用于不具有y值且使用非传统损失函数来评估模型准确性的数据集。
Thank you for reading! I hope you learnt something.
感谢您的阅读! 我希望你学到了一些东西。
翻译自: https://medium.com/analytics-vidhya/creating-genetic-algorithms-with-python-187d79f27c0a