使用 PyTorch 实现遗传算法

文摘   2024-08-24 15:51   山东  
点击上方“进修编程”,选择“星标公众号

超级无敌干货,第一时间送达!!!

优化算法能够解决复杂的问题,遗传算法就是优化算法。遗传算法可以轻松地与PyTorch集成,以解决各种优化任务。下面我们将了解如何使用 PyTorch 实现遗传算法。

遗传算法

遗传算法(GA)是受自然启发选择的优化技术。它们从一组问题的潜在解决方案开始,并在多代中不断改进它们。遗传算法模仿自然选择来解决问题。它们从一组解决方案开始,评估它们的优劣,然后混合和变异解决方案以创建新的解决方案。这个过程持续进行,在多次迭代中改进解决方案,找到直到令人满意的解决方案。GA适用于传统方法难以解决的复杂问题。

算法:

  • 总体:从一组问题的随机解决方案开始。

  • 适应度函数:每个使用适应度函数评估每个解决方案的优劣,该函数简化解决方案解决问题的水平。

  • 选择:从人群中选择最佳解决方案作为下一代解决方案的父母。

  • 交叉:将父母结合起来创建新的解决方案(后代)。

  • 变异:偶尔引入随机变化以保持解决方案的多样性。

  • 重复:持续这个过程很多代,不断改进的解决方案。

使用 PyTorch 实现遗传算法

我使用PyTorch实现了一个简单的遗传算法(GA)来在这里优化神经网络的超参数。创建一个代表不同超参数集的本体个体群体,通过训练和评估神经网络来评估它们的适应程度,执行选择、交叉和变异操作,并迭代固定数量的代数。因此,我们遵循以下步骤:

步骤1:导入必要的库

我们首先导入必要的库:

import torchimport torch.nn as nnimport torch.optim as optimfrom torchvision import datasets, transformsfrom torch.utils.data import DataLoader

第2步:定义神经网络

CNN 类使用 PyTorch 的 nn.Module 定义了图层神经网络架构。它由两个图层层(conv1 和 conv2)以及后续的最大池化操作和两个连接层(fc1 和 fc2)组成。

class CNN(nn.Module):    def __init__(self):        super(CNN, self).__init__()        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1)        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)        self.fc1 = nn.Linear(64 * 7 * 7, 128)        self.fc2 = nn.Linear(128, 10)
def forward(self, x): x = torch.relu(self.conv1(x)) x = torch.max_pool2d(x, kernel_size=2, stride=2) x = torch.relu(self.conv2(x)) x = torch.max_pool2d(x, kernel_size=2, stride=2) x = x.view(-1, 64 * 7 * 7) x = torch.relu(self.fc1(x)) x = self.fc2(x) return torch.log_softmax(x, dim=1)

步骤3:计算适应度

该函数计算给定模型(个体)在测试数据集上的准确率。它在数据集上对模型进行固定数量的训练周期(本例中为5个周期),然后在测试数据集上评估其准确率。

def compute_fitness(model, train_loader, test_loader):    criterion = nn.NLLLoss()    optimizer = optim.Adam(model.parameters())
model.train() for epoch in range(5): for data, target in train_loader: optimizer.zero_grad() output = model(data) loss = criterion(output, target) loss.backward() optimizer.step()
model.eval() correct = 0 total = 0 with torch.no_grad(): for data, target in test_loader: output = model(data) _, predicted = torch.max(output.data, 1) total += target.size(0) correct += (predicted == target).sum().item()
accuracy = correct / total return accuracy

步骤 4:提供遗传算法参数

  • 人口规模(population_size):人口中的个体数量。

  • mutation_rate:每个参数的突变概率。

  • num_ Generations:遗传算法的代数。

# Genetic algorithm parameterspopulation_size = 10mutation_rate = 0.1num_generations = 5

步骤5:种群初始化函数

该函数使用指定数量的 CNN 模型初始化种群。

# Initialize genetic algorithm parametersdef initialize_population():    population = []    for _ in range(population_size):        model = CNN()        population.append(model)    return population

步骤6:交叉算子(crossover):

交叉算子将两个父模型的遗传信息结合起来,产生两个子模型。它通过交换父模型层面层之间的权重来实现单点交叉。

# Crossover operator: Single-point crossoverdef crossover(parent1, parent2):    child1 = CNN()    child2 = CNN()    child1.conv1.weight.data = torch.cat((parent1.conv1.weight.data[:16], parent2.conv1.weight.data[16:]), dim=0)    child2.conv1.weight.data = torch.cat((parent2.conv1.weight.data[:16], parent1.conv1.weight.data[16:]), dim=0)    return child1, child2

步骤7:变异变异(mutate):

变异算子以一定的概率(mutation_rate)对模型的参数进行随机扰动。它会给模型的参数添加高斯噪声。

# Mutation operator: Random mutationdef mutate(model):    for param in model.parameters():        if torch.rand(1).item() < mutation_rate:            param.data += torch.randn_like(param.data) * 0.1  # Adding Gaussian noise with std=0.1    return model

步骤8:加载数据集

使用torchvision加载MNIST数据集。它由手写数字图像和相应的标签组成。

# Load MNIST datasettransform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])train_dataset = datasets.MNIST(root='./data', train=True, transform=transform, download=True)train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)test_dataset = datasets.MNIST(root='./data', train=False, transform=transform, download=True)test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)

步骤9:遗传算法

遗传算法在多代中迭代,评估种群中每个个体(模型)的适应度,为下一代选择表现最好的个体,并应用交叉和变异算子来生成新的个体。

# Genetic algorithmpopulation = initialize_population()for generation in range(num_generations):    print("Generation:", generation + 1)    best_accuracy = 0    best_individual = None
# Compute fitness for each individual for individual in population: fitness = compute_fitness(individual, train_loader, test_loader) if fitness > best_accuracy: best_accuracy = fitness best_individual = individual
print("Best accuracy in generation", generation + 1, ":", best_accuracy) print("Best individual:", best_individual)
next_generation = []
# Select top individuals for next generation selected_individuals = population[:population_size // 2]
# Crossover and mutation for i in range(0, len(selected_individuals), 2): parent1 = selected_individuals[i] parent2 = selected_individuals[i + 1] child1, child2 = crossover(parent1, parent2) child1 = mutate(child1) child2 = mutate(child2) next_generation.extend([child1, child2])
population = next_generation

完整代码

import torchimport torch.nn as nnimport torch.optim as optimfrom torchvision import datasets, transformsfrom torch.utils.data import DataLoader
# Define the neural network architecture (CNN)class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1) self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1) self.fc1 = nn.Linear(64 * 7 * 7, 128) self.fc2 = nn.Linear(128, 10)
def forward(self, x): x = torch.relu(self.conv1(x)) x = torch.max_pool2d(x, kernel_size=2, stride=2) x = torch.relu(self.conv2(x)) x = torch.max_pool2d(x, kernel_size=2, stride=2) x = x.view(-1, 64 * 7 * 7) x = torch.relu(self.fc1(x)) x = self.fc2(x) return torch.log_softmax(x, dim=1)
# Function to compute fitness (accuracy) of an individual (hyperparameters)def compute_fitness(model, train_loader, test_loader): criterion = nn.NLLLoss() optimizer = optim.Adam(model.parameters())
model.train() for epoch in range(5): for data, target in train_loader: optimizer.zero_grad() output = model(data) loss = criterion(output, target) loss.backward() optimizer.step()
model.eval() correct = 0 total = 0 with torch.no_grad(): for data, target in test_loader: output = model(data) _, predicted = torch.max(output.data, 1) total += target.size(0) correct += (predicted == target).sum().item()
accuracy = correct / total return accuracy
# Genetic algorithm parameterspopulation_size = 10mutation_rate = 0.1num_generations = 5
# Initialize genetic algorithm parametersdef initialize_population(): population = [] for _ in range(population_size): model = CNN() population.append(model) return population
# Crossover operator: Single-point crossoverdef crossover(parent1, parent2): child1 = CNN() child2 = CNN() child1.conv1.weight.data = torch.cat((parent1.conv1.weight.data[:16], parent2.conv1.weight.data[16:]), dim=0) child2.conv1.weight.data = torch.cat((parent2.conv1.weight.data[:16], parent1.conv1.weight.data[16:]), dim=0) return child1, child2
# Mutation operator: Random mutationdef mutate(model): for param in model.parameters(): if torch.rand(1).item() < mutation_rate: param.data += torch.randn_like(param.data) * 0.1 # Adding Gaussian noise with std=0.1 return model
# Load MNIST datasettransform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])train_dataset = datasets.MNIST(root='./data', train=True, transform=transform, download=True)train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)test_dataset = datasets.MNIST(root='./data', train=False, transform=transform, download=True)test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)
# Genetic algorithmpopulation = initialize_population()for generation in range(num_generations): print("Generation:", generation + 1) best_accuracy = 0 best_individual = None
# Compute fitness for each individual for individual in population: fitness = compute_fitness(individual, train_loader, test_loader) if fitness > best_accuracy: best_accuracy = fitness best_individual = individual
print("Best accuracy in generation", generation + 1, ":", best_accuracy) print("Best individual:", best_individual)
next_generation = []
# Select top individuals for next generation selected_individuals = population[:population_size // 2]
# Crossover and mutation for i in range(0, len(selected_individuals), 2): parent1 = selected_individuals[i] parent2 = selected_individuals[i + 1] child1, child2 = crossover(parent1, parent2) child1 = mutate(child1) child2 = mutate(child2) next_generation.extend([child1, child2])
population = next_generation
# Print final populationprint("Final population:")for individual in population:    print("Individual:", individual)

Output:

Generation: 1Best accuracy in generation 1 : 0.912Best individual: CNN(  (conv1): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))  (conv2): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))  (fc1): Linear(in_features=3136, out_features=128, bias=True)  (fc2): Linear(in_features=128, out_features=10, bias=True))...
Generation: 5Best accuracy in generation 5 : 0.935Best individual: CNN( (conv1): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (conv2): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (fc1): Linear(in_features=3136, out_features=128, bias=True) (fc2): Linear(in_features=128, out_features=10, bias=True))
Final population:Individual: CNN( (conv1): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (conv2): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (fc1): Linear(in_features=3136, out_features=128, bias=True) (fc2): Linear(in_features=128, out_features=10, bias=True))...

输出解释

  • 对于每一代,代码都会打印代号(代:X)。

  • 它还显示了该代中实现的最佳准确度(X 代中的最佳准确度:Y)。

  • 另外,它还展示了该代中最佳单个CNN模型的架构(最佳单个:...)。

  • 处理完所有代数后,代码将打印最终进化的 CNN 模型群体。

  • 最终人口中的每个个体都代表一个印有其架构的CNN模型。

结论

总之,将 PyTorch 与遗传算法或优化方法相结合,为解决机器学习及其他领域的复杂问题提供了一种强大的方法。通过利用 PyTorch 的灵活性和遗传算法的探索能力,从业者可以有效地优化神经网络架构、参数超参数、防御攻击、有效分配资源和自动化机器学习过程。PyTorch 与遗传算法之间的这种合作有助于推动创新并解决各个领域面临的优化问题。

python、matlab程序设计找我哦

—  —


进修编程
提升编程技能,学习编程技巧