【Python】 粒子群最適化法による最適化
粒子群最適化法による大域的最適化を行うクラスを実装します。粒子群最適化法は
- ハイパーパラメータが少ないので扱いやすい
- 単峰性が強い関数の最適化に向いている
といった特徴を持っています。最適化問題は慣例として"目隠しした登山家が山の頂上をどうにかして目指す"ことに例えられます。この慣例に従えば、粒子群最適化法は"目隠しした沢山の登山家を山の様々な場所に派遣し、各々が自分が今までに見つけた最も高い標高と、他の登山家が今までに見つけた最も高い標高を頼りに移動先を決めて頂上を目指す"ようなイメージの手法です。自分が持っている情報だけでなく、他の人の情報も使って移動先を決めるため、局所解から抜け出せる余地を持っています。粒子群最適化法では登山家を粒子として表現するのですが、まずは粒子をParticleというクラスで表現します。
import sys import numpy as np class Particle(object): """粒子群最適化法の粒子を表すクラス""" def __init__(self, f, position, velocity, maxs=None, mins=None): """ コンストラクタ @param f 最適化を行う関数 @param position 粒子の初期位置 @param velocity 粒子の初期速度 @param maxs 粒子が移動可能な位置最大値 @param mins 粒子が移動可能な位置最小値 """ self.__f = f self.__my_best_position = position self.__my_best_score = f(position) self.__my_position = position self.__my_velocity = velocity self.__maxs = maxs self.__mins = mins self.__WEIGHT_V = 0.8 self.__WEIGHT_ME = 1.0 self.__WEIGHT_US = 1.0 @property def score(self): return self.__my_best_score @property def position(self): return np.array(self.__my_best_position) def move(self, best_position): """ 粒子を移動させるメソッド @param best_position 関数の最小値を与える粒子の位置 """ # 粒子の位置を更新する self.__my_position += self.__my_velocity # 範囲外に出た粒子は範囲内に収める if (self.__maxs is not None): max_out_of_range_index = self.__my_position > self.__maxs self.__my_position[max_out_of_range_index] = self.__maxs[max_out_of_range_index] if (self.__mins is not None): min_out_of_range_index = self.__my_position < self.__mins self.__my_position[min_out_of_range_index] = self.__mins[min_out_of_range_index] # 評価値を計算する score = self.__f(self.__my_position) # 最良解を更新する if (score < self.__my_best_score): self.__my_best_position = self.__my_position self.__my_best_score = score # 速度を更新する item1 = self.__WEIGHT_V*self.__my_velocity random_me = np.random.rand(self.__my_position.size) item2 = self.__WEIGHT_ME*random_me*(self.__my_best_position - self.__my_position) random_us = np.random.rand(self.__my_position.size) item3 = self.__WEIGHT_US*random_us*(best_position - self.__my_position) self.__my_velocity = item1 + item2 + item3 # 範囲外に出た粒子の速度は0とする if (self.__maxs is not None): self.__my_velocity[max_out_of_range_index] = 0 if (self.__mins is not None): self.__my_velocity[min_out_of_range_index] = 0
次に、粒子を制御するクラスをParticleSwarmOptimizationとして作成します。このクラスは単に粒子群から最適解に最も近い粒子の位置を探し、粒子の位置を更新しているだけです。
import sys import Particle class ParticleSwarmOptimization(object): """粒子群最適化法により最適化を行うクラス""" def __init__(self, particles): """ コンストラクタ @param particles 粒子のリスト """ self.__particles = particles self.__best_score = sys.float_info.max for particle in self.__particles: if (particle.score < self.__best_score): self.__best_score = particle.score self.__best_position = particle.position @property def best_score(self): return self.__best_score @property def best_position(self): return self.__best_position def position(self, index): return [particle.position[index] for particle in self.__particles] def update(self): """ 粒子の位置を更新するメソッド """ for particle in self.__particles: particle.move(self.__best_position) for particle in self.__particles: if (particle.score < self.__best_score): self.__best_score = particle.score self.__best_position = particle.position
作成したParticleクラスとParticleSwarmOptimizationクラスを利用してのRosenbrock関数の最適化を行います。
import numpy as np import matplotlib.pyplot as plt from ParticleSwarmOptimization import ParticleSwarmOptimization from Particle import Particle if __name__ == '__main__': # 最適化する関数 def f(x): return 100.0*(x[1]-x[0]**2)**2 + (1.0-x[0])**2 # 変数が取り得る最大値 maxs = np.array([2.048, 2.048]) # 変数が取り得る最小値 mins = np.array([-2.048, -2.048]) # ばらまく粒子の個数 PARTICLE_COUNT = 100 # 粒子を作成する particles = [] for i in range(PARTICLE_COUNT): position = (np.random.rand(2) - 0.5)*4.0 velocity = (np.random.rand(2) - 0.5)/10.0 particle = Particle(f, position, velocity, maxs, mins) particles.append(particle) # ParticleSwarmOptimizationクラスの生成 pso = ParticleSwarmOptimization(particles) # 計算回数 ITERATION = 100 # 計算開始 for i in range(ITERATION): pso.update() if i == 0 or i == 9 or i == 49 or i == 99: plt.figure() plt.xlim([-2.048, 2.048]) plt.ylim([-2.048, 2.048]) plt.grid() plt.title('i = ' + str(i)) plt.scatter(pso.position(0), pso.position(1)) plt.show()
i=0, i=9, i=49, i=99の時の粒子の様子を図示します。計算が進むにつれて、ばらばらに散らばっていた粒子が最適解であるに集まる様子を確認できます。
- 作者: 古川正志,渡辺美知子,木下正博,鈴木育男,山本雅人,川上敬
- 出版社/メーカー: コロナ社
- 発売日: 2011/12/06
- メディア: 単行本
- この商品を含むブログ (1件) を見る