はじめに
遺伝的アルゴリズム(Genetic Algorithm, GA)は、生物の進化メカニズムに着想を得たメタヒューリスティクス最適化手法です。解の集団(個体群)に対して選択・交叉・突然変異を繰り返し適用することで、目的関数の最適解を探索します。
GAは勾配情報を必要としないブラックボックス最適化手法であり、多峰性関数や離散最適化問題にも適用可能です。
アルゴリズムの概要
GAの基本的な流れは以下のとおりです。
- 初期化: \(N\) 個の個体をランダムに生成
- 適応度評価: 各個体の目的関数値を計算
- 選択: 適応度の高い個体を次世代の親として選択
- 交叉: 親のペアから子個体を生成
- 突然変異: 一定確率で個体の遺伝子を変化させる
- 世代交代: 新しい個体群で次世代を構成
- ステップ2-6を収束まで繰り返す
選択(Selection)
トーナメント選択
集団から \(k\) 個体をランダムに選び、その中で最も適応度の高い個体を親として選択します。\(k\) をトーナメントサイズと呼び、大きいほど選択圧が高くなります。
\[P(\text{最良個体が選択される}) = 1 - \left(1 - \frac{1}{N}\right)^k \tag{1}\]ルーレット選択
適応度に比例した確率で個体を選択します。個体 \(i\) の選択確率は次のとおりです。
\[P(i) = \frac{f(x_i)}{\sum_{j=1}^{N} f(x_j)} \tag{2}\]ここで \(f(x_i)\) は個体 \(i\) の適応度です。最小化問題の場合は適応度の変換(例: \(f' = f_{\max} - f\))が必要です。
交叉(Crossover)
BLX-\(\alpha\) 交叉(実数値最適化向け)
連続値最適化では、BLX-\(\alpha\) 交叉がよく使われます。親 \(p_1, p_2\) から子を生成する際、各次元 \(d\) について次のように計算します。
\[c_d = U\left[\min(p_{1,d}, p_{2,d}) - \alpha \cdot I_d, \; \max(p_{1,d}, p_{2,d}) + \alpha \cdot I_d\right] \tag{3}\]ここで \(I_d = |p_{1,d} - p_{2,d}|\)、\(U[a, b]\) は一様分布、\(\alpha\) は典型的に 0.5 です。
突然変異(Mutation)
ガウス突然変異
連続値最適化では、ガウスノイズを加える突然変異が一般的です。
\[x'_d = x_d + \mathcal{N}(0, \sigma^2) \tag{4}\]\(\sigma\) は突然変異の強度を制御します。世代が進むにつれて \(\sigma\) を減少させる適応的突然変異も有効です。
Python実装:Rastrigin関数の最適化
Rastrigin関数は多峰性のベンチマーク関数で、大域的最小値は \(f(\mathbf{0}) = 0\) です。
\[f(\mathbf{x}) = 10n + \sum_{i=1}^{n} \left[x_i^2 - 10\cos(2\pi x_i)\right] \tag{5}\]import numpy as np
import matplotlib.pyplot as plt
def rastrigin(x):
"""Rastrigin関数"""
return 10 * len(x) + np.sum(x**2 - 10 * np.cos(2 * np.pi * x), axis=-1)
class GeneticAlgorithm:
def __init__(self, dim, pop_size=100, bounds=(-5.12, 5.12),
crossover_rate=0.8, mutation_rate=0.1, mutation_sigma=0.5,
tournament_size=3):
self.dim = dim
self.pop_size = pop_size
self.bounds = bounds
self.crossover_rate = crossover_rate
self.mutation_rate = mutation_rate
self.mutation_sigma = mutation_sigma
self.tournament_size = tournament_size
def initialize(self):
"""ランダムな初期集団を生成"""
low, high = self.bounds
return np.random.uniform(low, high, (self.pop_size, self.dim))
def tournament_selection(self, population, fitness):
"""トーナメント選択"""
indices = np.random.randint(0, self.pop_size, size=self.tournament_size)
best = indices[np.argmin(fitness[indices])]
return population[best]
def blx_alpha_crossover(self, parent1, parent2, alpha=0.5):
"""BLX-α交叉"""
low = np.minimum(parent1, parent2) - alpha * np.abs(parent1 - parent2)
high = np.maximum(parent1, parent2) + alpha * np.abs(parent1 - parent2)
child = np.random.uniform(low, high)
return np.clip(child, *self.bounds)
def gaussian_mutation(self, individual):
"""ガウス突然変異"""
mask = np.random.random(self.dim) < self.mutation_rate
noise = np.random.normal(0, self.mutation_sigma, self.dim)
individual[mask] += noise[mask]
return np.clip(individual, *self.bounds)
def run(self, objective, max_generations=200):
"""GAの実行"""
population = self.initialize()
best_history = []
for gen in range(max_generations):
fitness = objective(population)
best_idx = np.argmin(fitness)
best_history.append(fitness[best_idx])
# 次世代の生成
new_population = [population[best_idx].copy()] # エリート保存
while len(new_population) < self.pop_size:
p1 = self.tournament_selection(population, fitness)
p2 = self.tournament_selection(population, fitness)
if np.random.random() < self.crossover_rate:
child = self.blx_alpha_crossover(p1, p2)
else:
child = p1.copy()
child = self.gaussian_mutation(child)
new_population.append(child)
population = np.array(new_population[:self.pop_size])
fitness = objective(population)
best_idx = np.argmin(fitness)
return population[best_idx], fitness[best_idx], best_history
# --- 実行 ---
np.random.seed(42)
ga = GeneticAlgorithm(dim=10, pop_size=200, max_generations=300)
best_solution, best_fitness, history = ga.run(rastrigin, max_generations=300)
print(f"最良解の適応度: {best_fitness:.6f}")
print(f"最良解: {best_solution[:3]}... (最初の3次元)")
# --- 収束曲線のプロット ---
plt.figure(figsize=(10, 5))
plt.plot(history)
plt.xlabel('Generation')
plt.ylabel('Best Fitness')
plt.title('GA Convergence on Rastrigin Function (10D)')
plt.yscale('log')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
他の最適化手法との比較
| 手法 | 集団ベース | 勾配不要 | 離散問題 | 特徴 |
|---|---|---|---|---|
| GA | Yes | Yes | Yes | 交叉による探索空間の組み合わせ的探索 |
| CEM | Yes | Yes | 困難 | 確率分布の反復更新 |
| PSO | Yes | Yes | 困難 | 速度ベースの粒子移動 |
| SA | No | Yes | Yes | 温度による確率的受容 |
関連記事
- クロスエントロピー法:モンテカルロ最適化の実践的手法 - CEMはGAと同じくブラックボックス最適化手法ですが、確率分布の更新による探索を行います。
- 粒子群最適化(PSO)のPython実装 - 群知能に基づく別のメタヒューリスティクス手法を解説しています。
- ベイズ最適化の基礎とPython実装 - ガウス過程による代理モデルを用いた効率的な最適化手法を解説しています。
- MPPI(Model Predictive Path Integral)の数理 - サンプリングベースの制御最適化手法を解説しています。
参考文献
- Goldberg, D. E. (1989). Genetic Algorithms in Search, Optimization, and Machine Learning. Addison-Wesley.
- Eshelman, L. J., & Schaffer, J. D. (1993). “Real-coded genetic algorithms and interval-schemata”. Foundations of Genetic Algorithms, 2, 187-202.
- Back, T. (1996). Evolutionary Algorithms in Theory and Practice. Oxford University Press.