We implemented temperature parallel simulated annealing in Golang and applied it to a traveling salesman problem.
Golangで並行シミュレーテッドアニーリング2019/10/28(Mon)Go Conference Tokyo 2019 AutumnFuture Architect Tsuji Daishiro
View Slide
Who are you?辻 大志郎(つじ だいしろう) @d_tutuz渋谷区役所(~2014/9)Future Architect(2014/10~)✓ 所属Technology Innovation Group競技プログラミング部2
Future Architectの紹介3
本日伝えたいこと✓ シミュレーテッドアニーリングの並行化アプローチを伝えたい✓ Golangの並行化機構を用いてシンプルに実装できることを伝えたい※本資料は終了後公開します 4
What is シミュレーテッドアニーリング?✓ 最適化問題を解くアルゴリズム• 与えられた制約条件のもとで、評価値を最大または最小にする最適解を求める問題✓ 特定の問題に依存しない、メタヒューリスティクスな手法✓ 焼きなまし法、とも言われたり• 金属工学の焼きなましの原理に似ていることから5
シミュレーテッドアニーリング(SA)の特徴✓ 大域的最適解を探索することが可能• 局所探索法+温度による確率的な改悪遷移✓ 十分な時間をかければ、最適解への到達可能性の理論的保証あり• ただし計算時間の観点から非現実的なので、実用的には近似解を計算評価値状態良悪大域的最適解局所的最適解局所的最適解大域的最適解を探索したい6
局所探索法✓ 山登り法✓ ちょっと改善することを試行し続ける方法• 適当な探索解 x に対して、x の近傍内に x よりもよい解 x′ があれば x′ を解とすることを反復する手法• 評価値が悪化しない評価値状態良悪7
局所探索法✓ 山登り法✓ ちょっと改善することを試行し続ける方法• 適当な探索解 x に対して、x の近傍内に x よりもよい解 x′ があれば x′ を解とすることを反復する手法• 評価値が悪化しない(局所解に収束)評価値状態良悪大域的最適解局所的最適解局所的最適解に収束し、大域的最適解に到達できない8
SAの特徴✓ 大域的最適解を探索することが可能• 局所探索法+温度による確率的な改悪遷移✓ 十分な時間をかければ、最適解に到達可能性の理論的保証あり• ただし計算時間の観点から非現実的なので、実用的には近似解を計算評価値状態良悪大域的最適解局所的最適解温度パラメータを用いた改悪遷移を認めることでより良好な結果を得る可能性がある9
SAの遷移の条件✓ ある解から近傍の解の評価値の差分:Δe✓ 温度パラメータ:T評価値が改良する(Δe<0)場合は、必ず改良する近傍に遷移する(山登り法と同じ)評価値が改悪になる場合でも、一定の確率 exp(-Δe/T) で遷移する改悪遷移の確率は、高温の場合は低温と比較してより改悪な近傍に遷移する可能性が高い1000.20.40.60.81-5 -4.5 -4 -3.5 -3 -2.5 -2 -1.5 -1 -0.5 0【参考】exp(x)のグラフ
SAの特徴✓ exp(-Δe/T) により低温時には大きな改悪方向には遷移しない可能性が高い• 近傍の最適解に収束していく✓ 高温時は大きく評価値が遷移する可能性が高いので、局所的最適解から抜け出しやすい評価値状態良悪大域的最適解局所的最適解温度パラメータが非常に重要11
SAの課題✓ 温度パラメータのチューニングが困難• 温度をどのようなスケジュールで冷却するか✓ 良質な結果を得るために必要な計算時間が膨大12
SAの課題に対するアプローチ✓ 温度パラメータのチューニングが困難• 温度をどのようなスケジュールで冷却するか✓ 良質な結果を得るために必要な計算時間が膨大 並行化して効率化する研究が存在Golangを用いて、シンプルに実装できるのではないか?13
SAの並行化のアプローチ✓ いろいろな既存研究がある✓ Parallel Independent Annealing(PIA)• 従来の逐次SAを並列化して最良の結果を解とする✓ Temperature Parallel Simulated Annealing(TPSA)• 温度で並列化してSAを実行する14
SAの並行化のアプローチ✓ いろいろな既存研究がある✓ Parallel Independent Annealing(PIA)• 従来の逐次SAを並列化して最良の結果を解とする✓ Temperature Parallel Simulated Annealing(TPSA)• 温度で並列化してSAを実行するTPSAをGolangで実装して検証15
TPSA✓ 相異なる一定の温度をもつプロセスが、一定回数のシミュレーテッドアニーリングを実施✓ 交換周期ごとに、隣接する温度のプロセス間で解を一定の確率で交換• 解を交換するタイミングのみ、各プロセス間で同期※8温度で並列SAするイメージ図16
TPSAのプロセス間の解の交換条件✓ 隣接するプロセスの温度差:ΔT (=T’-T) (T’✓ 隣接するプロセスの評価値の差:ΔE (=E’-E) (※評価値(E’,E)は値が小さいほうが良好な解)高温プロセスが低温プロセスと比較して・・・良好な評価値を保持している場合 (ΔT*ΔE<0) は、隣接プロセス間で解を交換評価値が悪い解を保持している場合でも、一定の確率で交換17
巡回セールスマン問題✓ みんな大好き巡回セールスマン問題(TSP)• 各頂点を1回だけ経由するときの巡回路のコストを最短にする問題• 巡回路の組み合わせの数は O(N!) なので全経路を探索することは不可能• 15!=1307674368000• …18
巡回セールスマン問題の近傍✓ 2-opt• TSPで最も基本的な近傍• ある巡回路上において、適当な2辺を交換する手法12 3 47 6 512 3 47 6 5※2辺を交換して評価値が改善する例もとの巡回路: (1, 2, 6, 5, 4, 3, 7)交換後の巡回路: (1, 2, 3, 4, 5, 6, 7)19
TPSAの実装✓ SAを実施する各プロセスは goroutine で温度数分まで非同期処理として並行化✓ 解交換は sync.WaitGroup を用いて待ち合わせすることで同期処理20
TPSAの実装func (t *TPSA) Solve() error {iteration := 0for iteration < t.MaxIteration {wg := &sync.WaitGroup{}for i := 0; i < t.Thread; i++ {wg.Add(1)//各温度プロセスが並行してSAを実行go func(i int) {defer wg.Done()t.sa(i)}(i)}wg.Wait()// 隣接プロセス間の解交換は逐次処理t.exchangeSolutions(iteration)iteration++}}✓ SAを実施する各プロセスは goroutine で温度数分まで非同期処理として並行化✓ 解交換は sync.WaitGroup を用いて待ち合わせすることで同期処理21https://github.com/d-tsuji/tpsa
TSPを解くデモTime!✓ TPSAで巡回セールスマン問題を解いているデモ• 温度[0~100)を均等に16分割した各温度のプロセスのうち、高/中/低温として3プロセスをサンプルとして表示• ビジュアライザ作成にあたっては、 gonum/plot とWebSocketを活用22
実験データ✓ 実験データはTSPLIBを使用• 同じアニーリング回数でSAとTPSAを比較するとTPSAで良好な結果を得たberlin52 krod100 ch13023パラメータ 値最低温度 0最高温度 100交換周期 32温度数(ゴルーチン数) 16総アニーリング回数 51,200※実験時の各種パラメータ
実験結果(評価値)✓ 評価値の実験結果• SAと比較してTPSAで良好な結果を得ていることが確認できたberlin52(最適解7,542)krod100(最適解21,294)ch130(最適解6,110)24平均値 最良値 最悪値SA 7,840 7,544 8,238TPSA 7,544 7,544 7,544平均値 最良値 最悪値SA 6,347 6,185 6,482TPSA 6,171 6,129 6,253平均値 最良値 最悪値SA 22,197 21,662 22,795TPSA 21,294 21,294 21,294
実験結果(実行時間)✓ 実行時間のベンチマーク結果• コア数に比例して実行時間がスケールされていることを確認できた25496251177135 137 123 11998.601002003004005006001 2 3 4 5 6 7 8実行時間(ns/op)並列数並列数ごとの実行時間(ns/op)※実験環境プロセッサ:Intel Core i7-8650U Processor 4コア/8スレッドメモリ:16GB※サンプルデータberlin52
【参考】実験時にハマったこと✓ 実験当初、並列実行したときにスケールしなかった• なぜか並列数をあげると実行時間が増加する...265478211158 11581098 1118 1128 114702004006008001000120014001 2 3 4 5 6 7 8実行時間(ns/op)並列数並列数ごとの実行時間(ns/op)※実験環境プロセッサ:Intel Core i7-8650U Processor 4コア/8スレッドメモリ:16GB※サンプルデータberlin52
【参考】実験時にハマったこと✓ pprofでプロファイリング結果を解析したら、泣けてきた• 実行時間の約7割が rand.Float64() の処理に費やされている27
【参考】実験時にハマったこと✓ 共有ロックを取得することを知らなかった...✓ 並行処理する関数内で rand インスタンスを生成することで対応28/** Top-level convenience functions*/var globalRand = New(&lockedSource{src: NewSource(1).(Source64)})// ...// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0)// from the default Source.func Float64() float64 { return globalRand.Float64() }// ...type lockedSource struct {lk sync.Mutexsrc Source64}※go1.12のmath/rand/rand.goより抜粋
【参考】実験時にハマったこと✓ 対応後のpprofでのプロファイリング解析結果• rand.Float64() の実行時間は全体の1割程度となった29
まとめ✓ TPSAという、温度のプロセスが並行してSAするアプローチで良好な結果を得ることができた✓ Golangの並行機構を使うとシンプルに実装できてよい30
参考文献✓ 小西健三、瀧和男、木村宏一:温度並列シミュレーテッド・アニーリング法とその評価、情報処理学会論文誌、Vol.36 No.4 (1995)✓ 小西健三、屋鋪正史、瀧和男:温度並列シミュレーテッドアニーリング法の巡回セールスマン問題への適用と実験的解析、電子情報通信学会論文誌、Vol.J80-D-I No.2 (1997)31