はじめての階層型タスクネットワーク
ご無沙汰しております。ちょびえです。前回の私の記事は「Electronでゲームの設定ツール作ってみた」でしたね。
あれからElectronは幾度かversion upしておりますが、安定して使えているので程よくつかえるツール環境だなぁ、なんて思ってたりします。
はてさて、今日はAIの話。はじめての階層化型タスクネットワークということで、Hierarchical Task Network略してHTNについて書いてみたいと思います。
本記事ではArtificial Intelligenceの中でもプランニング(意思決定)に使われる仕組みのお話です。
階層型タスクネットワークの概念(ざっくり)
階層化型タスクネットワークはざっくりいうと既知の問題解決のための行動の定義を領域ごとに細分化したグループを用い、特定問題を解決するため集合を定義します。
そこから条件にあったプランを返していく、というのが大まかな流れです。
Web系の人にはChefのBookやRecipeだったり、ワークフローエンジンみたいなもんだと思ってもらえれば、うーん、そこまで大きくは間違ってないかと。
あくまで既知の問題をどうやって解決する、というルールベースのプランニングAIです。
HTNを構成する要素
ページ下部の参考にあるExploring HTN Planners through Example:Troy Humphreysの説明を使い、HTNを構成する要素について書いてみます。
- World: Blackboardみたいなもん。計画をすすめる上で変更されていきます。
- Domain: 特定領域の知識表現を束ねたもの。CompoundTask(この場合RootTaskと呼びます)の集合を持ちます。
- CompoundTask: Methodの集合をもちます。
- Method: CompoundTaskやPrimtiveTaskの集合を持ちます。
- PrimitveTask: Operatorの集合を持ちます。
- Operator: 実際の処理を行う単一のActionです。
設計思想によって多少変わったりはしますが大まかにはこのような分割です。
タスクの集合を条件に照らし合わせてアクションプランを探していくのは階層型プランニングと同様ですが、最終結果のプランをPlanner制約によって順序を「いい感じ」に変更していくというのがHTNの特色みたいです。
各種ノードには前提条件や事後効果などをもたせるといろいろ塩梅が良いです。
大まかなプランニングの流れとしてはPlannerは上位ノードから条件にマッチしたノードの事前効果をWorldに適用しつつ深さ優先探索していき最後のノードまで処理を行ってマッチしたプランを返します。各ノードを通る際にWorldの状態とタスクの事前条件を判定して通過できれば事後効果をWorldに適用していきシミュレーションを進めます。
基本的にはプランニングをするだけなので、実行するのは別の処理になります。
(遅延評価などを入れたい場合はタスクなどにそういった情報を仕込んでいったりします)
という感じで、ゲームAIにはよくある構成なので実装に関してとっつきやすいと思います。
タスクの再順列化
階層型プランニングの一番の理解が難しい所がタスクの再順列化(ReOrder)です。
おそらく殆どのゲーム向け実装ではきっと簡略化されていると思います。そんな都合もあり、解釈が難しい(すくなくともぼくには)
(というかゲームで使われるAIという都合上、AIデザイナが指定した順序で動いてくれたほうが都合が良いという気が。)
再順列化については、実際のゲームで考えるよりも、馴染みのある事象、そうですね、今回は味噌汁づくりで考えてみたいと思います。
それでは、味噌汁を作る上での作業単位で洗い出しを行います。
色々作り方に突っ込みどころはあるかと思いますが、説明という事でご容赦いただければ。
Methodは一つのみとして省略しています。
テキストで前提条件、事後効果も合わせて書いてみます。(今回の味噌汁づくりでは、場合分けなどはないので、あまり効果はありませんが)
調理ドメイン☆味噌汁をつくる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
#鍋準備 鍋をだす: 前提条件:なし 事後効果:鍋が料理台にある ガスコンロに鍋を載せる 前提条件:鍋が料理台にある 事後効果:ガスコンロに鍋がある 計量カップを料理台にだす 前提条件:なし 事後効果:計量カップが料理台にある 水を測る 前提条件:計量カップが料理台にある 事後効果:計量カップに材料分水が入っている 水を鍋に入れる 前提条件:ガスコンロに鍋がある && 計量カップに材料分水が入っている 事後効果:鍋に水が入っている #豆腐準備 豆腐1パックを冷蔵庫から料理台に出す: 前提条件:豆腐1パックが冷蔵庫にあること 事後効果:豆腐1パックが料理台にある 豆腐1パックから豆腐を取り出してまな板に載せる: 前提条件:豆腐1パックが料理台にある 事後効果: まな板の上に豆腐がある 豆腐を切る: 前提条件まな板の上に豆腐がある 事後効果:食べやすいサイズの豆腐群がある #ネギ準備 ネギを冷蔵庫から料理台に出す 前提条件:ネギ(万能ねぎがいいなぁ)が冷蔵庫にあること 事後効果:ネギが料理台にある ネギを洗う 前提条件:ネギが料理台にある 事後効果:ネギがきれい ネギを切る 前提条件:ネギがきれい 事後効果:ネギが食べやすい #だし 簡易だしを準備する 前提条件:冷暗所に簡易だしがある 事後効果:簡易だしが料理台にある 簡易だしを材料分測る 前提条件:簡易だしが料理台にある 事後効果:簡易だしが材料分取り分けられている 簡易だしを入れる 前提条件:簡易だしが材料分取り分けられている && 鍋に水が入っている 事後効果:鍋に簡易だしが入っている #火 火をつける 前提条件:ガスコンロに鍋がある && 鍋に水が入っている 事後効果:火がついている #投入 豆腐を鍋にいれる: 前提条件:鍋に水が入っている && 食べやすいサイズの豆腐群がある 事後効果:鍋に豆腐がいる ネギを鍋に入れる 前提条件:鍋に味噌が溶かれていてる && 良い温度に温め終わった 事後効果:鍋にネギがいる #沸騰 沸騰させる 前提条件:ガスコンロに鍋がある && 鍋に水が入っている && 火がついている 事後効果:水の温度が100度になる #味噌準備 味噌を冷蔵庫から料理台に出す 前提条件: 味噌が冷蔵庫にある 事後効果:味噌が料理台にある 味噌を材料分測る 前提条件:味噌が料理台にある 事後効果:味噌が材料分取り分けられている #味噌投入 味噌を入れるために火を止める 前提条件:ガスコンロに鍋がある && 鍋に水が入っている && 火がついている 事後効果:火が消える 味噌を溶く 前提条件:味噌が材料分取り分けられている && 材料に火が通っている 事後効果:鍋に味噌が溶かれていてる #しあげ 温める 前提条件:ガスコンロに鍋がある && 鍋に水が入っている && 火が消える 事後効果:火がつく 沸騰前に火を止める 前提条件:ガスコンロに鍋がある && 鍋に水が入っている && 火が付いている 事後効果:火がきえる && 適温になる && 鍋に水が入っている 器に味噌汁をよそう 前提条件:火がきえる && 適温になる && 鍋に水が入っている && 鍋に簡易だしが入っている && 鍋に味噌が溶かれていてる && 鍋に豆腐がいる && 鍋にネギがいる 事後効果:椀に味噌汁がはいっている |
と、まぁこんなところでしょうか。
前提条件はタスクを実行するための制約。
(豆腐の準備ができていないのに鍋にいれられないのでそういった制約をつけていきます)
事後効果はタスクをやったことで発生する効果としておきます。
(豆腐切ったら豆腐準備オッケー、みたいな)
今回は特に場合分けがないので素直にタスクを出会った順でもっていけばプランの完成です。
(雑に場合分け増やすなら時間がなければインスタント味噌汁にするタスクを追加、時間があればほどよい味噌汁にする、みたいな感じになるかと)
図の左から順番にやっていけばたしかに味噌汁作れそうですね。
とはいえ、この順序は味噌汁を作れるだけであって工程にはまだ最適化の余地がありそうです。沸騰待つ間にまったりしているのも時間の無駄です。
それではタスクの再順列化を考えてみましょう。
今回は人の手で最順列化をやるので多少あいまいではありますが、味噌汁づくりの経験をかみしつつ、前提条件が満たせるように適当に組み替えてみました。
再順列の流れは基本的には前提条件を満たしているものをやれるならやってしまう、と思っておけばよいと思います。(もちろん再順列制約、並列化制約によって変わります)
このような順序の仕組みを半順序プランニングと呼ぶそうです。
(こういう再順列するには、作業に必要とされる時間や効用の評価、作業中暇を持て余すか、といったメタ情報が必要になりますがHTN自体とはまた話がちょっと変わってくるので割愛します)
出来上がったプランを見る限りでは火つけてる間に具材の準備をしているので全体としての時間は減っていそうな気がしますね。
こんな具合にプランナーの制約や実装によって再順列化の詳細は変わってきます。
何をどの程度解きたいかでヒントの量を調整したり、メタデータを調整してプロダクトにあったものを作っていけばいいかとおもいます。
まとめ
人間は経験と環境から工程を最適化できますが、どうしても中~大規模の工程最適化は難しくなってしまいます。階層型タスクネットワークが使えるとこういった処理が半機械的にできるのが良いと思います。
HTN自体は階層型FSMともみなせるので、BehaviourTreeとやれることは似通っていますが、どちらも得手不得手あるので目的にあった方を使うのが良いです。
(今回の例のような味噌汁づくりの工程を再順列する、みたいなのはBehaviourTree無理ですしね。)
ぼく自身もまだ階層型タスクネットワークは全て理解しきれていないので間違っているところなどがあればご指摘いただけると幸いです。
(自分たちのゲーム向けに簡略/都合の良い解釈してつくるのであってるか、といわれるとそんなに自信なかったり。まーまー動いてくれればおっけー!)
今回はHTN Plannerが得意とするビルド系のプランニングで解説しましたが勿論キャラクターAIなどでも有用で目的を持った行動をさせやすくなります。
とまぁ、AIの意思決定手法は数あれど、解決したい問題に対して違うアプローチで取り組むことによってもっと簡単に量産や問題解決が出来ることがあります。
一つの手法にこだわらず、このアルゴリズムであればこういった問題を解決できる!なんて考えていくのは楽しいですね。
おわりに、AI情報をわかりやすく資料として残してくださった皆様に感謝を申し上げます。
ではでは、たのしいゲーム開発を
参考
- 迷路を解くAIを使ってゲームを作る (ゲームAI連続セミナー第3回付録I):三宅 陽一郎
- GDC における海外のゲーム関連技術についての調査:三宅 陽一郎
- 人工知能の作り方 ――「おもしろい」ゲームAIはいかにして動くのか:三宅 陽一郎
- 「00年代から10年代へゲームAIとプロシージャル手法の進化」:三宅 陽一郎
- プレイヤーに反応するだけのAIはもう古い!ゲームAIへのプランニング技術の導入:長谷 洋平
- Exploring HTN Planners through Example:Troy Humphreys