第一回日本最強プログラマー学生選手権決勝

結果

f:id:tokumini:20190930120028p:plain

 A, Bの2完で94位。レートで見たらとても低い方だと思っていたんだけど、コンテスト前の順位表を見ていたら思っていたよりもドベ付近というわけでもなかったっぽい? まぁそれにしても100位以内は運が良かった(A問題がひねったものだったので普通の実力順とも少し違う結果になった気もする)。

A - Equal Weight

 何も考えずA問題から解き始める。しかしこれが異様に難しい。 A, Bそれぞれから差分が等しくなるようなペアを見つければ良いのかと思い、差分 d 10 ^ 6未満だから全探索できて、 O(\log{N})などで「 N要素の配列の中に差分が dとなるペアがあるか判定」ができればそれを A, Bそれぞれに適用して解けそうだという方向性で考えていた。しかし全然そんなことできそうにないし、だいたい2つ配列があってそれぞれに同じ判定問題を適用して答えを出すというのはあまり解法として美しくないなと感じていた。それなら問題を少し変えて配列一つに対して判定問題を解かせるような形にしていそうだなというAtCoderに対する信頼感。そういうことを考えつつさっぱり解法はわからなかったので、順位表を見て異様に解いた人が多いB問題へ移動。

B - Reachability

 問題文がややこしくて理解するのに時間がかかったが、理解できてしまえばやるだけ。しかし合っていると確信して出したコードがWA。めちゃくちゃ焦りながらデバッグし始めようとしたところ、CLionの環境としてVisual C++を指定していたせいかデバッグモードが使えないことが判明し、急遽Visual Studioに戻るという手間も発生して焦りが最高潮に。普段ほとんどノートパソコンで競技プログラミングをすることがないので環境の整備が全然できていなかった。せめて一日前のABCで確認しておくべきだった……。

 assertを仕込みつつ何度か提出したがどこが間違っているかわからないまま1時間ちょいが経過して、0完を覚悟しだした。死んだ目をしながらコードを眺めていたらzと書くべきところでxと書いているところがあってそこを直したらAC。一つ通せて、まぁ最低限はできたかとかなりホッとした。

 提出

A - Equal Weight

 再びA問題に戻ってくる。が、やっぱりわからなくて特に考察が進む感じもないまま数十分が経過した。残り1時間になったら一度お手洗いに行こうと少し前から決めていて、そこまでさっぱり考察が進まなかったので予定通り席を立った。

 少し歩いて気分を切り替えられたのが功を奏したのか、戻ってすぐに「これ NMが大きい場合と小さい場合で違う解法で解けるんじゃね?」という思考に至る。小さい場合は愚直に O(NM)のループを回せば良いし、大きい場合は鳩ノ巣論法からいってどこかでダブるからそれを利用して解けそうというところに気づくと、300点問題ということも加味してかなり手ごたえがある方針だと感じた。その後も思考が混乱していてやや時間もかかったが、本当に愚直なループを書けば良いだけというところまで考察が進んで上手く解けた。想定解だという自信もあったし気持ちよかった。

 提出

C - Maximize Minimum

 A問題を解いた時点で残り40分を切っていたのでもうこれ以上解けないだろうなとは思ったが、この集中力で問題に取り組める時間も貴重なので次の問題へ。C問題とF問題が同じくらいのAC数だったので、どちらも読むだけ読んで解けそうな可能性を少しだけ感じたこっちへ注力。しかし取り組んでみればどういうものが最適解になるかすらわからなくて完全にお手上げという感じだった。

 後日、解説PDFfuppy0716氏のコードを参考に(ほぼ丸写しで)通してみた。考察の一つ一つやその実装が自分にとって全く不可能な難易度とは思わないが、それらを慎重に繋いでいって全体の解法にまで構成していくのをコンテスト時間中にやるのは相当難しそうだと思う。訓練を重ねていくしかない。

 提出

感想など

  • プラチナスポンサーと謳われているYahooの影が薄かったのはやや気になった。電通の会場提供パワーが強かったのだろうか
  • トークセッションも面白かったが、わりと深刻に就活に対する興味がないなぁというつらい現実を確認する時間でもあった
  • 個人的に講評というものが好きなので簡単な各問講評があったら嬉しかったか。半分以上の問題を読んですらいない人間が講評を求めるのも変な気はするが……(よくわからん問題に対してすごい方法で「こうすると解けます」という話をほえーと思いながら聞くのが好き)
  • 懇親会での食事が豪華で良かった
  • 人々強いし自分ももう少しレート上げたいと思った

AtCoder Beginner Contest 142

結果

順位   145th / 5235
パフォーマンス   2116
レーティング  1893 → 1917(+24)

 ノーペナ全完ができたので良かった。E問題が簡単めかと思ったけど意外と解いた人が多くなくて順位もそこそこ。次回は上手くいけば最高レート(1925)更新なるかという戦いに。

A - Odds of Oddness

 なんかむずかったのでビビりながら解いた。

 提出

B - Roller Coaster

 A問題より考える要素がなくて楽。

 提出

C - Go to School

 構造体を作ってソートする。解説PDFを見たらもっと賢くやってて、これは思いつきたかったか。

 提出

D - Disjoint Set of Common Divisors

 ボーっと考えていたらなんとなくGCD(A, B)の素因数の種類数 + 1でいけそうという勘が芽生えてそれを提出したらAC。ちゃんと証明? したわけではないけどそれなりに確信度は高かった。

 提出

E - Get Everything

 最初はフローに帰着させそうとかいくらか考えたけど、制約をよく見たら Nがめちゃくちゃ小さいことに気がついて全てを察した。しかしbitDPの実装に苦労してしまう弱者っぷり。コメントで「これは買う方、こっちは買わない方の遷移」というのを書かないと脳が混乱してしまう。

 提出

F - Pure

 最小ループっぽいものを見つければ良さそうだというのはなんとなくわかる。ループを見つけて途中に辺があったらそれで小さいループに変更していく方針は実装の仕方がわからなくて断念。いろいろ考えたが、最終的には各点について「自分から始まり自分で終わるような最小ループを見つける」ことができれば、それらの中で一番小さいものを選べば良いという方針に絞って解いた。最初は幅優先探索で実装しようと思ったが上手くできなくて(お前本当に青コーダーか?)、ダイクストラのライブラリを貼ってAC。ダイクストラ、速い、強い。

 提出

対抗形の学習が不十分

結論

 現状の学習方法で得たパラメータは対抗形が苦手であり、学習局面として対抗形ほとんど出現していない。

背景

 自己対局による強化学習だと学習局面が偏ってしまうのではないかという指摘は多々ある。Miacisについてはどうも対抗形で上手く指せていないように感じていた。今回はそれを検証する実験を行った。

実験1

 対抗形の局面から技巧2(深さ10・定跡オフ)との対局を先手後手それぞれ250局行い、勝率を計測した。採用した初期局面は人気抜群の四間飛車を知る。まずは美濃囲いを作ってみよう!【はじめての戦法入門-第2回】|将棋コラム|日本将棋連盟 第2図である(下図)。

f:id:tokumini:20190927095814p:plain

 sfen

position startpos moves 7g7f 3c3d 6g6f 8c8d 2h6h 8d8e 8h7g 7a6b 7i7h 5c5d 5i4h 5a4b 4h3h 4b3b 3h2h 6a5b 3i3h 6b5c 6i5h 7c7d 1g1f

 参考値として初期局面から対局させた場合の勝率も含め、Miacis側から見た勝率は次のようになった。

条件 Miacis側から見た勝率 技巧2(深さ10)に対するレート
平手初期局面(Miacis先手) 39.8% -71.9
対抗形(Miacis先手・振飛車側) 13.2% -327.2
平手初期局面(Miacis後手) 46.8% -22.3
対抗形(Miacis後手・居飛車側) 12.2% -342.9

 局面を対抗形に固定するだけでレートが300近く下がり、居飛車振飛車のどちらを持つのも苦手ということがわかった。対局を眺めて感じた印象としては

  • Miacisが居飛車を持つ場合…全て急戦。囲いは△3一金のelmo囲いや、△4二金直、△4二銀などバリエーションはいくらかあったが、戦いが始まる前に1,2筋まで玉を囲っていく対局は見た限り一回もなかった。
  • Miacisが振飛車を持つ場合…自分から美濃囲いを崩して片矢倉にしたがる。囲いの金銀の連結が悪い。下段飛車を好み、▲6九飛〜▲2九飛のような活用をすることも(右玉風?)。とにかくまったく振飛車らしい指し方ではない。

実験2

 実際に学習データに対抗形が現れているかを(かなり荒っぽく)確認してみた。具体的には平手初期局面から10手までの間に▲6八飛車または△4二飛車という符号が出現している棋譜を抽出した。

 学習中に適当な間隔で保存した全19,961局中、条件に合うものは32局だった。そのうち30局は最初の300局の中に集中しており、つまり学習があまり進んでおらず方策にランダム性が高いうちに生成された棋譜であった。いくらか内容を見てみたが、まともな対局内容ではなかった。

 残りの2局もそれぞれIDが2143、6608と両方とも前半1/3に入るものであり、内容は

  • 棋譜2134(下図)…ややそれっぽい駒組にはなったが結局ただの下段飛車でしかない。左図の▲5八玉も、序盤に一度▲4八玉と行ったところをわざわざ戻してきたものであり、玉を囲うという構想がなさそう。
f:id:tokumini:20190927170519p:plainf:id:tokumini:20190927170523p:plain
  • 棋譜6608(下図)…初手▲6八飛車から5手目に▲2八飛車と戻してただの相居飛車

f:id:tokumini:20190927170546p:plain

 惨憺たる結果である。

所感

 注目すべきは振飛車側を持つことも苦手だということだと思われる。もし振飛車側を持つことが苦手でなければ、自己対局中に低い確率でも対抗形が出現すればその局面の振飛車側の評価が高くなり、その後選ばれやすくなる。しかし振飛車が苦手だと根本的に悪い局面だと判断してそこへ到達しなくなってしまう。その結果まったく対抗形が出現しなくなる。

 個人的な印象としては、「囲い」そのもの、あるいは「固く囲って攻め駒をさばいて勝つ」という作戦・方針が学習できていないと感じる。これを学習する難しさは、ある対局中で方針を一貫させなければならないところにあるのではないか。つまり指し手のランダム性によりたまたま囲いができても、囲いを維持しようという方針がないとすぐに囲いを崩して(そのとき局所解的に)良いと思っている形に戻してしまうのではないかと考えている。もちろんランダム性によってたまたま囲いを維持してたまたま攻め駒をさばけて勝つという体験に辿りつくことができればそこを起点に学習していけるのかもしれないが……。

 いずれにせよ、強化学習的文脈で扱うならば(強化学習の意味での)探索が不十分だという問題として捉えるべきだろう。AlphaZeroの膨大な学習時間も結局は十分な探索を行うためにかかる時間なのではないかと思う部分もあり、湯水のように計算資源を使えるわけでもない一介の学生としては探索の改善によって学習高速化ができないかと夢想するところであるが、それを解決する手段はありやなしや。

AtCoder Grand Contest 038

結果

順位   514th / 2032
パフォーマンス   1663
レーティング  1916 → 1893(-23)

 ペナルティが重たくのしかかる。再びmerom氏にレート抜き返されてしまった。

A - 01 Matrix

 さっぱりわからなくてやばかった。58分かけて8WAの後に通すことができたけどこれは致命傷。

 最終的には

  1.  H行のうち、0が A個である行が i行、 W - A個である行が H - i行とする。同じように列に関しても0が B個となる列が j列として i, jを全探索する
  2. 行の数式から考えて1を立てる数と、列の数式から考えて1を立てる数が一致するi, jを見つける。
  3. 各列が必要とする1の数をstd::priority_queueに詰めていく
  4. 各行が必要とする1の数だけ上で作ったpriority_queueから取り出していき、そこへ1を書き込んで(各列が必要とする1の数を一つ減らして)priority_queueに詰め直す

 というやり方で解いた。3以降でこういうことをやらずに左上から貪欲に詰めていくと

5 5 2 2

でダメになりWAを連発したので七面倒臭いことをやらなければいけなかった。

 解説PDFを見て、これはどう思いつけば良いのかさっぱりわからない。あまりにもきついA問題だった。

 提出

B - Sorting a Segment

 これは見てすぐ端の関係だけ考えれば良さそうという感じでセグメント木を二つ持って、あとはソートしてもなにも起きない場合を考えて無理やり通した。A問題よりよっぽど簡単。

 提出

C - LCMs

 コンテスト中はさっぱりわからなかった。解説を見て、頭が良すぎる! という印象。しかし値の上限が小さめであることには気づいて良かったな。普通に考えて解けるわけない問題なんだからそういうところに目が行かないのはちょっと感覚が悪かった。

 解説の内容をコードに翻訳するような形で通したけど、約数列挙を O(\sqrt{n})でやると余裕でTLEだし、強い人たちの会話

を見て「なるほどstd::vectorに突っ込んで行けばよいのか」と思ってやったらそれもTLEだし、直接 wを構築しているときに上手いことやるということが必要だった。こういうの本番中に解ける気がしない。

 提出

AtCoder Beginner Contest 141

結果

順位   111th / 5166
パフォーマンス   2214
レーティング  1877 → 1916(+39)

A - Weather Prediction

 頭が悪い実装方法でタイプミスが怖かったが、そのときはCLionが指摘してくれるだろうと信じてやった。

 提出

B - Tap Dance

 こういうの条件間違えそうで怖い。

 提出

C - Attack Survival

 全員即 Q点減点させて各問題では正解者のみに1点増やせば良いというのを、感覚でそういう感じというノリで実装。C問題ならそれで合う。

 提出

D - Powerful Discount Tickets

 基本的にはstd::priority_queueに全部貯めて、「1個取り出して2で割って入れる」という操作を Y回やれば良いんだろうということにはすぐ気づくが、具体的にどうするかでやや悩んだ。

 最初は毎回2で割って切り捨てていくと値がズレそうだと思って、doubleでは精度がダメそうだなぁとか、std::pairで初期値と割った回数を保持するとかでは順番の計算が大変かとかを考えた。が、なんか少し試していくと割って切り捨てを繰り返しても最終的な値が変わらなさそうだということがわかってきたので無証明のままそれで実装してAC。解説PDFには証明があるが、あーまーそうなのねという感じであまりピンとは来なかった。

 提出

E - Who Says a Pun?

 Zアルゴリズムで殴って勝ち。文字列アルゴリズムは去年のJAG合宿で多少ライブラリを整備したのでそれが活きたわけだけど、これで早解きできてパフォーマンス高いというのもなんだかなぁという気持ち。

 提出

F - Xor Sum 3

 大きい方の桁から決定していくんだろうとか、そもそも1が立っている数が奇数ならどうしようもなく1が立ち、偶数なら1, 1で分けたくなるとかそのへんのほぼ自明なところはわかったけどそれ以降さっぱり進まず。掃き出し方なんですか。はぁ。動画を見てなんとなく雰囲気を察して実装。これは思いつけない。

 提出

適当にやった実験の結果

 以下全てEloレートは全て技巧2(深さ10)と1手0.25秒で500局対戦した結果から推定したもの。

ディリクレノイズなし

 行動選択を価値のソフトマックス関数にしたので、ある意味Policyに対する依存性が弱まり、ディリクレノイズを抜いても良いのではないかと思ったので試してみた。

f:id:tokumini:20190915155916p:plainf:id:tokumini:20190915155929p:plainf:id:tokumini:20190915155938p:plain

 Policy損失はあまり下がらなかったが対局させてみると性能が落ちている感じもしなくて謎。

リプレイバッファサイズ4倍

 リプレイバッファサイズは今まで適当に決めた1Mでやっていて、AlphaZeroは局面数換算するともっと大きいので大きくして実験してみた。

f:id:tokumini:20190915160030p:plainf:id:tokumini:20190915160033p:plainf:id:tokumini:20190915160036p:plain

 ほとんど差はないが、まぁ大きくしておいた方が少しだけ良いのかなという気がする。

スリープ時間2倍

 学習スレッドのスリープ時間を2倍にすれば実質生成速度を2倍にするのと一緒なので基本的にはこれで性能が伸びるはずだと期待してやった。

f:id:tokumini:20190915160144p:plainf:id:tokumini:20190915160146p:plainf:id:tokumini:20190915160149p:plain

 1.3Mステップのベースラインは上ブレっぽいのでおそらくスリープ時間2倍の方が全体的には良いと思われる。単純に学習時間が2倍になるので結構つらいが、余裕があるならやっておきたい設定ではある。あるいはAWSとかでGPU8枚インスタンスとかを使ってみるというのもあり。

バッチサイズ2倍

 なんとなくやった。

f:id:tokumini:20190831120655p:plainf:id:tokumini:20190831120711p:plainf:id:tokumini:20190831120715p:plain

 バッチサイズを2倍にするとステップあたりの学習速度が2倍になるので、横軸をステップ数で並べるとめちゃくちゃ性能が良くなったように見える。しかし今は学習スレッドのスリープ時間をバッチサイズに応じて調整しているので、バッチサイズを2倍にするとスリープ時間が2倍になる。横軸を学習時間にすると

f:id:tokumini:20190831120719p:plainf:id:tokumini:20190831120722p:plainf:id:tokumini:20190831120725p:plain

 ほとんど変わらない。バッチサイズを大きくした方がGPUで並列計算ができる分、学習部分がやや高速になるが、結局スリープ時間の方が遥かに大きいので全体としてはあまり変わらない。DeepMindみたいに生成側を十分な数用意できるなら学習部分がボトルネックになるのでバッチサイズを4096と大きくする意味があるのだろうが、GPUが少ない環境で回すならどっちでも良さそう。

DeepMDP: Learning Continuous Latent Space Models for Representation Learningを読んだ際のメモ

 本当にただのメモだし意味もわからないままに式を写しているだけなので注意。特に数学の専門用語など誤訳も多そう。力のある人は自分で読んで。

出典

 ICML2019に採択

概要

  • 状態表現空間の中でのMDPとしてDeepMDPを定式化
  • 報酬予測と次状態表現予測について適切に損失を設定しそれらを最小化すると、状態空間としての質と環境モデルとしての質を理論的に保証可能
  • Atariゲームにおいて補助タスクとして組み込むことでモデルフリー手法強化学習のパフォーマンスが改善

f:id:tokumini:20190914162648p:plain

2. 背景:DeepMDPの定義

2.1 マルコフ決定過程(Markov Decision Process : MDP)

マルコフ決定過程\mathcal{M} = \langle \mathcal{S}, \mathcal{A}, \mathcal{R}, \mathcal{P}, \gamma \rangle

  • \Pi : 定常方策\piの集合
  • V^{\pi}(s) : 状態sから方策\piに従って得られる期待収益
  • Q^{\pi}(s, a) : 状態sで行動aを取った以降方策\piに従って得られる期待収益
  • \mathcal{P}_{\bar{\pi}} : 行動によらない遷移確率
  • \mathcal{P}_{\bar{\pi}}(s'|s) = \sum_{a\in\mathcal{A}} \mathcal{P}(s'|s, a)\pi(a|s)
  • \mathcal{R}_{\bar{\pi}} : 行動によらない報酬
  • \pi^{*} : \mathcal{M}における最適方策
  • \varepsilon_{\pi}(s) : 方策\piの下での状態の定常分布
  • \varepsilon_{\pi}(s, a) = \varepsilon(s)\pi(a|s)

2.2 隠れ空間モデル

 次の二つを損失とする。

$$ L_{\bar{\mathcal{R}}}(s, a) = |\mathcal{R}(s, a) - \bar{\mathcal{R}}\left(\phi(s), a \right)| $$

$$ L_{\bar{\mathcal{P}}}(s, a) = \mathcal{D}\left(\phi\mathcal{P}(\cdot|s, a), \bar{\mathcal{P}}(\cdot|\phi(s), a) \right) $$

2.3 Wasserstein距離

 W_d(P,Q)を空間dにおける二つの分布P, QのWasserstein距離とする。これはPからQへ輸送する最小コストと一致する。ここである粒子をxからyへと移動させるコストは距離関数d(x, y)で測られるとする。


 定義1. 距離空間\langle \chi, d \rangleにおけるP, Q間のWasserstein-1距離Wは $$ W_d(P, Q) = \inf_{\lambda \in \Gamma(P, Q)} \int_{\chi \times \chi} d(x,y)\lambda(x,y) dxdy $$ である。ここで\Gamma(P,Q)PQの全てのカップリングの集合である。


 距離関数dに曖昧性がないとするとWを簡単に表現することができる。Monge-Kantorovich dualityより双対形式として $$ W_d(P, Q) = \sup_{f\in\mathcal{F}_d} | \mathbb{E}_{x \sim P} f(x) - \mathbb{E}_{y \sim Q} f(y) | $$ と表せる。ここで\mathcal{F}_dは、距離関数dにおける1-リプシッツ関数の集合\mathcal{F}_d = \{ f: |f(x) - f(y)| \le d(x,y) \}である。

2.4 価値関数のリプシッツノルム

 方策\bar{\pi} \in \bar{\Pi}に対する価値関数がリプシッツであるとき、この方策をLipschitz-valuedな方策と書くことにする。


 定義2. \bar{\mathcal{M}} d_{\bar{\mathcal{S}}}を距離関数とするDeepMDPとする。方策\bar{\pi} \in \bar{\Pi}K_{\bar{V}}-Lipshitz-valuedであるとは、全ての状態\bar{s}_1, \bar{s}_2 \in \mathcal{S}について $$ |\bar{V}^{\bar{\pi}}(\bar{s}_1) - \bar{V}^{\bar{\pi}}(\bar{s}_2)| \le K_{\bar{V}} d_{\bar{\mathcal{S}}}(\bar{s}_1, \bar{s}_2) $$ であり、全ての行動a \in \mathcal{A}について $$ |\bar{Q}^{\bar{\pi}}(\bar{s}_1, a) - \bar{Q}^{\bar{\pi}}(\bar{s}_2, a)| \le K_{\bar{V}} d_{\bar{\mathcal{S}}}(\bar{s}_1, \bar{s}_2) $$


 定義3. \bar{\mathcal{M}}d_{\bar{\mathcal{S}}}を距離関数とするDeepMDPとする。二つの状態 \bar{s_1}, \bar{s_2}について収益、遷移分布の距離がそれぞれK_{\bar{\mathcal{R}}}, K_{\bar{\mathcal{P}}}で抑えられるとき、(K_{\bar{\mathcal{R}}}, K_{\bar{\mathcal{P}}})-リプシッツであると言う。

$$ |\mathcal{R}(\bar{s_1}, a) - \bar{\mathcal{R}}\left(\bar{s_2}, a \right)| \le K_{\bar{\mathcal{R}}} d_{\bar{\mathcal{S}}}(\bar{s_1}, \bar{s_2}) $$ $$ W \left (\phi \mathcal{P}(\cdot | \bar{s_1}, a), \bar{\mathcal{P}}(\cdot|\bar{s_2}, a) \right) \le K_{\bar{\mathcal{P}}} d_{\bar{\mathcal{S}}}(\bar{s_1}, \bar{s_2}) $$


 前提1. 遷移関数のリプシッツ制約K_{\bar{\mathcal{P}}}\frac{1}{\gamma}未満であるとする。

 厳しめの制約ではあるが、この前提は十分条件であるし、エピソードが有限時間内に終わるときは不要。

 補題1. \bar{\mathcal{M}}(K_{\bar{\mathcal{R}}}, K_{\bar{\mathcal{P}}})-リプシッツであるとする。このとき以下が成り立つ。

  1. 最適方策は\frac{K_{\bar{\mathcal{R}}}}{1 - \gamma \bar{\mathcal{P}}}-リプシッツである。
  2. 全ての K_{\bar{\mathcal{P}}} \le \frac{1}{\gamma}である方策は\frac{K_{\bar{\mathcal{R}}}}{1 - \gamma \bar{\mathcal{P}_{\bar{\pi}}}}-リプシッツである。

3. Global DeepMDP Bounds

 行動空間、状態空間全てについてのWasserstein距離を利用した損失を考えたときに成り立つ諸性質。よくわからなかったが良い性質が成り立つのだろうなと思った。

4.Local DeepMDP Bounds

 実際の問題設定では行動空間、状態空間を網羅できる場合は少ない。期待値でも諸性質が同様に成り立つことを示すことでサンプリングによる近似が可能であることを証明。よくわからなかったが良い性質が成り立つのだろうなと思った。

5. 双模倣(Bisimulation)

5.1 双模倣関係


 定義4. MDPMについて、状態同士についての同値関係Bは、Bについて同値である全ての状態s_1, s_2 \in \mathcal{S}と全ての行動a \in \mathcal{A}次が成り立つとき双模倣的関係であると言う。

$$ R(s_1, a) = R(s_2, a) $$ $$ \mathcal{P}(G|s_1, a) = \mathcal{P}(G|s_2, a), \forall G \in \mathcal{S} / B $$ ここで\mathcal{S} / Bは関係Bの下で分割された\mathcal{S}を示し、\mathcal{P}(G|s, a) = \sum_{s'\in G}\mathcal{P}(s'|s, a)となる。


 双模倣関係は一意ではない。たとえば=は常に双模倣関係になる。特に関心があるのは\mathcal{S} / Bが少ない要素数となるような関係Bを見つけること(上の意味で"同じ"状態をたくさんまとめられる関係をこそ見つけたい)。

 双模倣関係の意味的な解釈は

  1. 全ての行動に対して同じ即時報酬がある
  2. 次の状態に対する分布が類似

 たとえばAtariゲームだとオブジェクトの色が違うだけというような状態がまとめられると嬉しい。

5.2 双模倣距離

 双模倣関係は二つの状態に関して同一かそうでないかの2値で分けてしまう。少しでも報酬が異なると完全に異なると判定されるのは不便であり、もう少し緩く類似性を評価したい。既存研究としてWasserstein距離を用いて双模倣距離を導入した例がある(Fern 2004)。

 導入する疑似距離は「d(x, y) = 0 \Leftrightarrow x = y」以外の距離の公理を満たす必要がある。そうであればd(x, y) = 0 ⇒ x B yとして同値関係の定義に用いることができる。Wasserstein距離はそれを満たしているので疑似距離として用いるのも自然である。こうすると異なる(x, y)でも、輸送コストが0であるならば同値と判断することがでいる。


 定義5. MをMDPとし、空間\mathcal{S}においてZを疑似距離集合とする。つまり d(s_1, s_2) \in [0, \infty) \, for \, d \in Z。オペレータF:Z \to Zを次のように定義する。

$$ F_d(s_1, s_2) = \max_{a}(1 - \gamma)|\mathcal{R}(s, a) - \bar{\mathcal{R}}\left(\phi(s), a \right)| + \gamma\mathcal{D}\left(\phi\mathcal{P}(\cdot|s, a), \bar{\mathcal{P}}(\cdot|\phi(s), a) \right) $$

 このとき

  1. オペレータF\tilde{d}を固定点とする縮約となる
  2. \tilde{d}カーネルは最大双模倣関係になる。

 双模倣距離の利点は、最適価値関数における2状態の価値の差が上から抑えられる点にある。

$$ |V^{*}(s_1) - V^{*}(s_2)| \le \frac{\tilde{d}(s_1, s_2)}{1-\gamma} $$

 このような双模倣距離を用いた既存研究もあるが、計算コストがかかるため深層学習との組み合わせは行われてきていない。

5.3 DeepMDPとの連結

 Wasserstein距離を用いて大域的なDeepMDP損失によって学習された表現\phiは、双模倣距離に関連付けることができる。

 定理3. 双模倣距離の上限はWasserstein距離と損失とリプシッツ係数から計算できる。(式略)

5.4 \bar{\Pi}の特徴

 双模倣関係を持つ二つの状態について同じ方策を示す方策の集合について考える。DeepMDPでの方策が損失を大域損失を最小化しているとき、だいたい双模倣関係内の方策と一致するようなものが得られる。

 定理4. 大域の損失値を用いて方策の差分の絶対値が抑えられる(式略)

6. Wassersteinを超えて

 補題2,3などにおいて確率距離の選び方はWasserstein距離に限らず他のものも可能。関数のノルムを介して定義される最大平均不一致(MMD)距離族に一般化する。値の差の境界におけるLipschitzノルムの役割は、Wasserstein距離を使用した結果出てくるものであり、違う距離を使えば違う上限が得られる。

 たとえば非常に非Lipschitzの遷移がある環境では、小さいLipschitzノルムを持つ関数で正確なDeepMDPを学ぶことは不可能かもしれない。よって距離関数の選び方を工夫することによってより良い近似ができるかもしれない。またWasserstein距離は計算コストが大きいのでもっと軽いコストの距離で十分ならそれも利点になる。しかしなんだかんだ議論した結果としてはWasserstein距離であることが重要だったりする? あまりよくわからなかった。

7. 表現学習における関連研究

 略

8. 評価実験

 大域最適化から期待値最適化に落とさないと深層学習に適用できないが、課題が2つある。

  1. Wassersteinの最小化→既存研究としてはWasserstein GANやQuantile regressionを用いた手法がある。推定にバイアスが生じるが、決定的な遷移を持つ環境を考えることで回避する。
  2. リプシッツ定数 K_{\bar{\mathcal{R}}}, K_{\bar{\mathcal{P}}}の制御→Wasserstein GANの改良版の考え方を採用

8.1 ドーナツ世界の実験

 円から小さい円をくり抜いた帯状のトラックを車で走るタスク。時計回りに走ると報酬が得られ、中心に近いほど速度が上がる。グレースケール化した32×32ピクセルの画像が入力として与えられ、表現として2次元に落とし込む。

  1. 上から実際の位置(白い点)
  2. Autoencoderでの白い点と近い表現を得る位置のヒートマップ
  3. DeepMDPの表現について白い点と近い表現を得る位置のヒートマップ

を描画した結果(画像の左半分)。

f:id:tokumini:20190914155749p:plain

 AutoencoderよりもDeepMDPの方がより位置情報を明確に取れている。また画像右側は少し難しいタスクとして、最初に車が4つのトラックのどれかにランダムに配置されるような場合の結果。より顕著に差が出ている。

 損失推移は

f:id:tokumini:20190914160429p:plain

 報酬損失と遷移損失は相反することもあるので両方下がるとは限らない。スケールが違うので大きい方を優先して下げるということになっている。

8.2 Atari2600実験

 C51をベースに、DeepMDPの損失を補助タスクとして入れることで性能向上。

f:id:tokumini:20190914160550p:plain

コード

 著者の実装が公開されている。

 結局は決定的環境でやっているので予測と実際の遷移したものとのノルムを考えるだけだと思ったが、コードを読むとなんか違う。謎。

AtCoder Beginner Contest 140

結果

順位   196th / 5446
パフォーマンス   2058
レーティング  1855 → 1877(+22)

 E問題まで素早く解けたのに結局F問題を解けなくてそこまで伸びきらなかった。残念。

A - Password

 A問題のページを開いておくのを忘れていてやや時間がかかってしまった。

 提出

B - Buffet

 問題文が頭の中に入ってこなくて少し焦った。B問題にしては難しめ?

 提出

C - Maximal Value

 すぐにはわからなくて紙とペンを使って考えた。 A_iが影響し得るのは B_i B_{i + 1}に対してなので、つまりその両方を見て小さい方までは大きくできる。番兵入れようかなとか少し思ったけど両端だけ特別扱いして書いた。

 提出

D - Face Produces Unhappiness

 1回の操作で幸福である人数が増えるのがそもそも回転させる両端の二人が限界で、それは同じ向きになっている切れ目を探せば良いという考えから、具体的に回数を求めようとしたら本当に切れ目の数を数えれば良いというだけなことがわかって上手く解けた。

 提出

E - Second Sum

 まぁなんとなくこういうのは「各数字が何回2番目になるか」ということを数えたくなる。そうなれば大きい方から数字の場所を見ていくというのは自然。そして自分より大きい数字を自分より右側で一つだけ含むか、自分より左側で一つだけ含むかという場合分けをすればあとは流れで実装していくうちに煮詰まっていくという感じ。最初は番兵を識別するために位置だけじゃなくて数字も必要かなと思って{ 位置, 数字 }のペアをstd::setに詰めていっていたんだけど、実装をいろいろ変えているうちに使わなかった。

 提出

F - Many Slimes

 一番大きいものに二番目以降のものを大きい順に N個くっつけて、その次は二番目のものにまたそれ以降のものを大きい順に適切な数くっつけて……と貪欲にやっていくことがいけそうと思い実装してWAでずっと困っているうちに終わった。これは考え方が間違っていて

3
5 4 3 2 1 1 1 1

をNoと判断してしまう。こういう後ろの方で同じ数がたくさんある場合は5から1を生やすようなパスが必要なので。今日解き直していてもこれがずっとわからなくて詰まってしまったし、間違っている理由がわかった後も正しい答えの実装がわからなくてかなり時間がかかった。感覚的に合ってそうと思ったらすぐ実装し始めてしまい、それでハマるとどれだけ時間があっても無理。解法の証明のしないやり方の悪い面が出た。

 提出

AtCoder Beginner Contest 139

結果

順位   653rd / 5899
パフォーマンス   1568
レーティング  1883 → 1855(-28)

 5完遅解きではこんなもん。もうmerom氏に抜かれそう。

A - Tenki

 for使っちゃった。

 提出

B - Power Socket

 A問題じゃん。

 提出

C - Lower

 えー、やるだけと思ったらforを抜けるタイミングで答えを更新していなくて1WA。こういう端から見ていって特定条件のとき値がリセットされてそのときに答えを更新するっていうものについて、ループ最後まで条件が来なくてループ終わった後に取り直すという記述を忘れがち。リセットタイミングだけじゃなくて常に答え更新で良いのか。そうか。

 提出

D - ModSum

 勘でこうだろって思った。サンプル2がそうっぽいもん。無証明。

 提出

E - League

 愚直で通るんじゃないかという気がして書いてみたが当然TLE。(一応頑張れば通るらしいが本番中そこに注力する決断はできない)

 なんか考えていたらdp[i][j] := i番目の人がj回目に対戦する相手と対戦し終える最小日数というのが見えて実装し始めるが、遷移がよくわからなくて混乱しまくった。最終的にはメモ化再帰で実装してループは再帰回数で無理やり判定するというようなことをして通した。

 DAGに帰着できそうという発想だったので想定解からそこまで離れているわけでもないとは思うが、明確にグラフに落として考えられないと実装が綺麗にならない。ついでに答えを取得する部分でmaxを取り忘れていて余計に3WA出した。注意力に欠けていた。

 提出

F - Engines

 残り28分くらいで突入したけどなんもわからず、直前のchokudai contestの印象が残っていたので適当に焼きなましっぽいものを書いて投げたけどまぁそれは通るわけがない。

 解説PDFを読んで、まぁこれは全然気づいていなかったなという感じの方針だった。AtCoderがあんまり幾何出さないのもあってあんまり幾何の経験値がないな。

 角度をyの正負で場合分けしてacosで求めようとしたらなんかWA。std::atan2を使えば何も考えずできたしAC。めっちゃ便利。最初引数をx, yの順番で与えていたらCLionが警告を出してくれて、それを検知できるの賢いなぁって思った。

f:id:tokumini:20190902084937p:plain

 提出

第一回日本最強プログラマー学生選手権-予選-

結果

順位   413th / 3534
パフォーマンス   1867
レーティング  1885 → 1883(-2)

 まぁだいだいレート通りのパフォーマンスなのでこんなもんだなと。

A - Takahashi Calendar

  d_1 \ge 2 d_{10} \ge 2という条件を見落としていて「は?」ってなったりしながら解いていた。

 提出

B - Kleene Inversion

 配列 Aの中で A_iより左にある A_iより大きいものは \frac{K(K+1)}{2}回数えられて、右側にある \frac{K(K-1)}{2}回数えられるとわかるので数えていく。なんか妙な実装をしてしまい、計算量が O(N \max A)になった。 \max Aが大きかったら考え直しで面倒だったな。

 提出

C - Cell Inversion

 左側から伸びている閉じ待ちの線をここでキャッチするかここでまた開くかみたいなイメージを思い浮かべるとわりとすぐわかった。これは完全に競技プログラミングAtCoder)に過剰適応した感じの考察の仕方な気もする。

 提出

D - Classified

 根本的に \left\lfloor \frac{N + 1}{2} \right\rfloorレベル必要なのかなと誤解していて全然ダメ。 N = 6のときにレベル3までで構成できなかったのが頭悪すぎだった。

 解説放送がめっちゃわかりやすかった。

 20行で書けてしまうんだなぁ。全然わからなかった。

 提出