読むとGPAが上がるブログ(仮)

GPA芸人が気の赴くままに何かを書くブログ

キー配列を変えるお話(続)

はじめに

どうも. GPA芸人です. もう更新することはないかもと思っていた当ブログですが,まさかの1年3ヶ月ぶりの更新となります. 一応 前の記事 の続き?というか続報というか,になるかと思います.

なぜブログを更新することにしたかと言うと……

REALFORCE R3 発売!!!!!!!!

皆さんが待ちわびていた,あのREALFORCEの新機種が出ます!!

pc.watch.impress.co.jp

私も静電容量無接点式のキーボードはHHKBとREALFORCEを使ったことがあるわけですが,現在はREALFORCEを使っています. というより,荷重30gに慣れてしまって,もう45gのキーボードが重すぎて押せません. 意味の分からないことを言ってると思われるかもしれませんが,30g愛好家の方ならこの感覚が分かるかと思います. HHKBは30gモデルがない気がするので,実質REALFORCEしか選択肢がないです.

キー配列を変えるお話の続き

実は,前の記事 を書いていたときとは作業環境が大幅に変わりました. 特に重要なのは,使うエディタをJetBrainsのIDEからVimに変えたところです. メインで使うOSをWindowsからUbuntuに変更した際にエディタをVimに移行したわけですが(ちょうどVimの練習を始めた時期だった),これが前回のキー配列変更と大幅に相性が悪かったのです. つまり,各キーに役割が割り当てられているVimは,キー配列を変更すると全ての機能を発動させる位置が移動し,現実的に使うのが難しくなっていたのです.

もちろん移行した状態で練習をすればいいわけですが,これではサーバー等の別の環境でVimを扱うことが事実上不可能になります. さらに,キー配列はアルファベットの出現頻度を基に決定したものなので,Vimの機能の使う頻度が考慮されません. 従って,当初はvimrcに大量のマッピングを定義して,QWERTY配列のときと同様の操作感でVimを使えるようにしました. しかし,これではメンテナンスが面倒だし,その他自分でマッピングを定義する際などに不都合・面倒が生じていました.

1年以上キー配列を変えた感想

その他,1年以上使って感じた,前の記事で書いた「問題点」への感想は以下の通りです.

他の環境での作業が厳しい

定期的にQWERTYに触っていればキー配列バイリンガルになるので大丈夫でした. 私はインターンで使うマシン(ノートPC・付属のキーボードを使っている)が普通にQWERTYなのですが,そちらは普通に使えています. 私の感覚では,手がキーボード依存で配列を覚えていて,手首をキーボード前に接地すると手がその配列にあったモードになる,という感じです.

習得するまでのタイピング速度が大変なことになる

標題の通りです. 慣れれば特に問題ないです.

QWERTY配列でのタイピング速度が落ちる

標題の通りです. ただ使う頻度と速度に相関があるので,ある程度日常的に使うために速くないと困る,という場合にはそれ相応に速くなるので問題ないです.

ショートカットキーがやばい

標題の通りです. Vimの件と同様ですね.

ゲームがやばい

相変わらずやばいです. 私はゲーム用にWindows機をキープしていますが,Windows機は配列を変えたままなので,この問題に直面します. 実際キーコンフィグがないWASD移動ゲームとかをやる際は,移動が不可能になるのでわざわざレジストリをバックアップしている元々のものに書き換えてやっています. 恐らくWindows11にした段階でキーマップはQWERTYに戻すと思います.

ちなみに仕組みはよく分からないのですが,確かSkyrimをプレイした際は元のQWERTY配列でゲームができました(元のWASDで移動ができた).

キー配列変更(改)

以上の理由から,Ubuntu移行と同時に,アルファベット・数字部分はQWERTYをそのまま使うことにしました. ついでにそれ以外の部分の割り当ても見直しました. 現状のキーマップは以下のようになっています.

f:id:gpageinin:20211026032154p:plain

(スペース以外の空白部分は,何に割り当てるか現在考えているところです)

主な変更点は以下の通りです.

US配列をJIS配列にした

USにする設定が面倒だった(やり方を調べてない)ためです. 無変換・変換などのキーを使える状態のままUS配列に変更できることが判明したら多分USにします.

Ctrlのワンショットモディファイアを増やした

無変換・変換キーを両方ともにCtrlとして使えるようにしました. 左右対称な形でCtrlを配置することで,左右どちら側のキーをCtrlで修飾する場合も押しやすくなりました. Shiftも(長いスペースキーに割り当てているので)左右の手で押せることから,これの拡張と考えられます. 今回の変更で最も気に入っている部分です.

なお,単押しに対する割り当てですが,Vimmerなので,無変換をEscに,変換をEnterに割り当てています. よくEscが遠くて押しにくいのでCtrl-[を使おう!とか書いてる記事を見ますが,Escを使う機会の多さに対してCtrl-[はあまりにも押しにくすぎると私は思います. この変更は全Vimmerにオススメします.

BackSpace・Deleteを移動した

BackSpaceをEnterキーに,Deleteを左Ctrlキーに,それぞれ移動しました. EnterもCtrlも親指部分に移動したため元の位置では使わないので,対称な位置にあって元の場所よりは押しやすいこれらの場所に割り当てました. 私はDeleteも普通に使うので対称性を意識してこの配置にしましたが,Delete使わない派の方は他の適当な場所にBackSpaceを割り当ててもいいかもしれません. いずれにせよ,BackSpaceはまあまあ使う割にゴミみたいな位置に左遷されているので,少しマシな場所に置いてあげるといいと思います. 絶対にtypoしないなら必要ないでしょうが.

ちなみにIME-ON・OFFと上下を入れ替えるかどうかは今でも悩んでます.

Qと同時押しで矢印になるようにした

下で紹介する xkeysnail導入記事 に書いてあったものです. ほとんど使わないアルファベットQのキーを修飾キーとして,Vimライクに

  • Q+h:←
  • Q+j:↓
  • Q+k:↑
  • Q+l:→

とします. ワンショットモディファイアとして,単押しした場合は普通にQが出るようにします.

この記事を読んだときに感銘を受け,そのまま使わせてもらいました. めちゃくちゃ便利でとても気に入っています. Vimmerじゃないと非直感的かもしれませんが,オススメです.

キー配列変更のやり方(Linux

私はUbuntu 20.04 LTSを使っていますが,xkeysnailというツールを使っています. インストールは こちらの記事 の通りやったらできました. 多分sudoが必要です.

まとめ

まだまだ改良の余地があると思われますが,とりあえず現段階でのキー配列を紹介しました.

REALFORCE R3はキーマップを自由に変えられるらしいので,少なくとも前の記事で紹介したアルファベット部分の変更は間違いなくこの機能で楽にできるようになると思います. 皆さんも是非買いましょう.

ミリシタの3周年イベントを走った記録と感想

デレステを始めてから10ヶ月、ミリシタを始めてから半年ほど経つわけですが、ついに初めてのイベランをしたので、その間の記録です。

日記

6/29(午前)

研究とか仕事で忙しかったので、先輩Pにイベントのことを色々聞きつつ、100位を狙うか1000位を狙うか考えたり、その後の戦略や動きを考えたり、いくら課金するかを考えたりしていました。

せっかくやるなら割とガチで走りたいタイプの人間なので、結局100位を狙うことにしました。 (10位はミリシタを始めたのがかなり遅いハンデと、研究と仕事と授業にどうしても時間を割かねばならず、現実問題として不可能だと判断しました。)

また誰で走るかですが、今年は色々考えた末に紬で走ることにしました。 ボーダーが沼なのは自明だったのですがまあそんなのは些細なことですね。

6/29(午後)-6/30

オススメ曲のライブ(仕事ではなく)をスタミナが溢れない程度に回していました。 ライブの方がちょっとスタミナ効率が高いのと、13日間をフルスピードで走り続けるのは100位狙いでは不必要ではないかと思ったので、研究や仕事が忙しかったのもあり、そこそこで回しました。 終わってみてから考えるとここでちゃんと回しておけば良かったとは思いました。

あと某コンビニでiTunesカード10%還元イベントをやっていたので、とりあえず3万円分買いました。

7/1-7/2

7/1あたりに先輩Pがかなり速いペースで走っているのを見て、危機感を覚えます。 そこで、空いている時間は基本的に全て走ることにしました。

この2日間はイベ曲はほぼ回さず(ミッションのために1曲やるくらい)、オススメ曲を(仕事で)ひたすら回してましたね。 というのも、イベントに参加するのが初めてのために後半のボーダーの動き方が全く分からず、イベント最終盤に最高速度を出せるようにするために道標をある程度まとまった個数保持しておいた方が良いのかなと思ったためです。

ちなみに走った感想ですが、

  • イベント曲を回していないのに順位が上がっていく(7/2終了時点で200位ちょっと下くらい)
  • 100位ボーダーとの差は開いていく

ということです。 これは、200-400位程度の人は自分と同じようにイベント曲を回しておらず(土日に回す予定なのでしょう)、100位↑の人はイベント曲もある程度回していることを意味していると考えました。

7/3

ほぼイベント曲のみ回しました。

この日のオススメ曲はどれも曲の長さが長く、いつか回すことになるイベント曲はオススメ曲が長い日に回すのが効率が良いと考えたためです。

順位の変動は、150位まではすんなり上がったのですが、そこからの順位の伸びがかなり鈍りました。 100位--150位あたりに恐らく大きな壁(毎日かなりの時間をコンスタントに走り続けているかどうか)があるのだと思います。

7/4

イベント曲をメインに回しました。 前日と同様、オススメ曲が全て長かったためです。 ただ、これは正直想定外で、前日に道標をほとんど使ってしまったので全く足りなかったので途中オススメ曲で補充するなどしました。

ソロの2MIXで回していたのですが、しばらくイベント曲ばっかりやったのでこの日が終わる頃にはほぼ譜面を覚えましたね。 画面を見ずとも片手で普通にクリアできるようになりました。

この日でちょうど道標を使い切って、24:00にちょうど100位に乗りました。

また、ここからイベ最終日までの計画(リフレッシュをいつ取るか)を立てました。 立てた結果次の日に35時間稼働を求められることが判明して絶望したりしました。

7/5

この日もオススメ曲が前日とほぼ同じ長さだったのもあり、道標を補充しつつ可能な限りイベ曲を回しました。 また、イベントも折り返しということで色々と考え直しました。 考えた結果、仕事を3倍ではなく2倍(スタミナ60)で回すことに決定したのと、本当に理論値に近い速度は出さなくてもいいか(食事等で発生する数分-数十分程度のロスは許容する)、という気持ちを得ました。 前者は3倍だと仕事の回数が減ってオートライブパスが足りなくなったからです(60個だといい感じにオートライブパスが貯まるのでオススメ曲をオートでプレイできる)。 後者は100位なら理論値は必要ないか……?と思ったからです。

あと、この日あたりになると片手タイピングだったり、イベ曲を回しながら講義動画を見るだったり、オートを回している2分ちょいを有効活用して課題を解くだったりといった作業に流石に慣れてきて、最低限の活動はこなせるようになってきましたね。

7/6

翌日に仕事がある都合上そこにリフレッシュを入れなければならず、調整した結果この日のリフレッシュ時間が23:00-07:59になってしまいました。 というわけで、前述した通りこの日は当初寝ずに走り続ける予定だったのですが、さすがに色々と悪影響が出ますし、全速力をやめたというのもあり、6時間ほど睡眠を取りました(4時間の予定だった)。

また、この日は久しぶりに短めの曲がオススメに来たので、オススメ曲をメインに回しました。 この日から「最終日に全速力でイベント曲を回し続けるのに必要十分な量の道標が最終日のリフレッシュ明けに溜まっている」ことを目標に、道標の貯金と消費のバランスを考えるようにしました。 とは言ってもオススメ曲が分からない以上不確定要素が多すぎるので、気持ち程度ですが。 ただ、平日はオススメ曲だけ走っても100位との差をつけることができるというのは感じました。

あと、ジュエルの消費がヤバそうであることが判明したので、大量に走ったことによって開放された大量のコミュやブログ、衣装や覚醒コミュ等からジュエルを数時間かけて回収しました。 80連分くらいありました。

7/7

この日は仕事があるのと前日からのリフレッシュ継続があったためにあまりできませんでしたが、オススメ曲が長かったので回せるだけイベント曲を回しました。 仕事前に100位ボーダーと20万pt差を付けたのですが、仕事が終わったら差が10万ptくらいになってて恐ろしかったですね。

7/8

最短にかなり近い曲がオススメ曲に来たので、オススメ曲を中心に回しました。 計画としては、この日で15万くらい道標を溜めて、次の日とその次の日でバランスよく回して最終日に道標18万を用意する感じです。 18万は、最終日14時間(ブースト消費等の諸作業に1時間を見積もり)でイベ曲を回し続けられる数から最終日のブーストで溜められる数を引いた分です。

実際にちょうど15万くらい残るようにして、余った分はイベント曲を回しました。

7/9

前日に続いてほぼ最短曲が出てきてしまったので、当初の予定を変更してこの日も道標を貯めることにしました。 最後の2日はブースト曲を除いてイベント曲のみを走れるように、36万道標を目標に貯めるようにしました。

ただ、途中でジュエルがなくなったため、少し残っていた覚醒等を全て回収した上で追加で1万円入れました(予算オーバー)が、それも(最後の2日でブーストで使う分を別にとっておいて)なくなってしまったので、36万を溜めきれずにイベ曲を回しました。 最終的に道標は28.5万くらいしか残らなかったのですが、翌日に全力でイベント曲のみを回し、最終日を迎えるときに十分な差がついていたら追加で課金しなくて済む、という算段です。

ちなみにこの日で所持している全てのカード(もちろん全レアリティ)のカードの覚醒が終わりました。

7/10

前日の計画通り、ブーストの10曲以外はイベ曲のみを回しました。 ただ後で考えたら、オススメ曲が全て長かったというのもあり、ブーストは20曲まとめて最終日にやった方が良かったですね。

この日1日(15時間)で60.5万ptくらい稼ぎましたね。 この日が終わった段階で(明日自分がちゃんと起きて残りの道標8万くらいを使い切れば)逃げ切れるはずの差をボーダーにつけることに成功したので、かなり安心しました。

7/11

長かったイベントもついに最終日です。 ちゃんと起きてブースト消費・最後の道標消費ランニングを20:30頃に完了し、残り時間は知り合いのPと通話しながらボーダーを眺めつつ過ごしました。

結果

  • イベントpt(紬):3,579,636pt
  • 紬ptランキング:47位
  • イベントptランキング:1359位

完走した感想・まとめ

最初のイベランということで、複数の有識者達からあらゆる知識を授かったとはいえ、勝手や相場が全く分からないというのが結構大変でした。 例えばボーダーの伸び方を知らないので予測することができないのが困難の1つですね。 分からないので、結局ボーダーとかに関係なく自分が最高効率だろうと考えた走り方を貫き通すしかなかったです。

後は、あらゆるもの(お金・課題・研究・健康等)を捨てるしかなかったのがやはり大変でしたね。 実際、最終日に道標を使い切った後は放置されていた課題等に全力で取り組まざるを得なくなりました。 前述した通りイベント生活で「ながら作業」にかなり慣れてしまったせいで、イベントを走りながら課題等は解けるので丸々残っていたわけではないのですが、それでも片手しか使えないので出来ないことも多々あり、それらをやる生活をイベント後1週間弱ほど送りました。

精神面も結構キツい時が何回かはありました。 私はこういった何かを延々と続ける系の作業は割と得意なのですが、それでも厳しい時は厳しいです。 特に、ジュエル必要量を計算した結果として予定課金額を超える課金がほぼ確定したときは100位を目指し続けるかかなり悩みました。 半分くらいイベント期間が残っていてギリギリ後戻り(1000位狙いにする)もできたのでかなり考えたのですが、最後までやりきるというのが重要である(上に自分が苦手としている)と思っているので、心を奮い立たせて走り切ることにしました。 後は自分はかなり恵まれた環境に置かれている(イベント期間中に提出しなくてはいけないものの数がたまたま少なかったり、仕事の出勤日に割と自由がきいたので仕事の一部をイベント期間後にずらすことができた)というのがあって、自分よりはるかに厳しい環境で戦い続けているPの姿を見ていたので、ここで自分が逃げるわけにはいかない、と思ったのも走り続けられた理由の1つです。

さて、最後にまとめですが、

イベントはしばらく走りません。

流石に疲れたのと、お金も時間もないのでしばらくは走らないというか走れないと思います。

全世界のミリシタPの皆さん、2週間お疲れ様でした。

走ったデータ

付録として、覚えている限りのデータを参考として載せておきます。

走り方

  • ブーストは全てオススメ曲(チケット450)で消費
    • オススメ曲に使うと道標数とイベントptの両方に2倍がかかるからです(イベント曲に使うとイベントptにしか2倍がかからないのでスタミナ効率で損をしています)。
    • 最初の1日か2日は考察が甘く、イベント曲にブーストを使っていました。
  • オススメ曲は4曲の中で最短のものを基本オートで回し、イベント曲は基本ソロの2MIX・4倍で回す
    • イベント曲は獲得ptが難易度に依らないはずなので、一番負荷が軽そうなソロの2MIXを使いました。また、2MIXなら譜面を見ずとも片手の2本の指でリズムに合わせて4つ打ちしていればクリアできます。
      • ただ、ずっと4つ打ちするのは疲れるので、ロングノーツは場所を覚えてそこは長押しした方が絶対良いです。私はそうしていました。
    • ちゃんとイベント前に数十くらいのオートライブパスを貯めておくと、仕事でオートライブパスが落ちなかった時や急にオートを使わなくてはいけなくなった時に役立ちます。
    • (オートの数分やイベント曲中の空いてる方の手で日常のタスク等をちゃんとこなす)
  • 仕事は基本60で回す
    • 前述した通り、90だとオートライブパスが足りなくなります。60だとオススメ曲はほぼフルオートでできます。
    • オートライブパスがMAXになったときは都度消費します。
  • 最短のオススメ曲が2:10より短い日は基本全力でオススメ曲を回して道標を貯め、そうでない日はイベント曲をひたすら回す
    • これが理想ですが、実際にはオススメ曲を回し続けるにはかなりのジュエルが必要で厳しかったので数時間ほどイベント曲を回すこともありました。
    • また、道標がなくなってしまうこともあり、オススメ曲で補充しなくてはならないこともありました。
    • 2:10という閾値には、一考の余地があると思います。
  • ちゃんと毎日デイリーミッションを全てこなす
    • 20ドリンクがもらえるためです。
  • リフレッシュは可能ならAM0時直後を含む時間帯にセット
    • 日付が変わるとログインボーナス等の処理があって時間がちょっと消えるので、AM0時直後の時間帯はリフレッシュに使った方が若干効率がいいです。
    • ただ誤差なので、自分の生活様式やモチベーション管理に合わせた方が結局良いですね(私も9時起床は自信がないので2時-11時で取ることが一番多かった)。

各種数値

イベント開始前

  • プロデューサーLv:多分90か100くらい
  • プロデューサーランク:B
  • ジュエル:多分10000くらい
  • アイテム所持数
    • 10・20・30ドリンク:各90個くらい
    • MAXドリンク:40個くらい
    • オートライブパス:15枚くらい

イベント終了直後

  • プロデューサーLv:207
  • プロデューサーランク:SS
  • ジュエル:1465
  • アイテム所持数:全部0

その他

  • 私の端末(第6世代iPad)でイベント曲1周にかかる時間(「本番」ボタンを押してから次に「本番」ボタンを押すまでの時間):最短で2分53秒くらい
  • 課金総額:7万円(ただしうち3000円は10%還元キャンペーンで帰ってきたので実際に使ったのは67000円)
  • イベント期間中に覚醒コミュ・ブログ等から無料で得たジュエル:23000くらい
  • プレイできなかった時間:平均で1-1.5時間くらい/日(外れ値(7/6など)を除く)
  • イベント曲プレイ数:1243回
    • ソロ2M:1035回
    • ソロ2M+:1回
    • ユニット6M:1回
    • ユニットMM:206回

キー配列を変えるお話

はじめに

突然ですが、皆さんのほとんどが使っているであろうQWERTY配列に疑問を抱いたことはありますか? 恐らく、99%以上の方はそんなことに疑問を覚えることもなくPCと共に育ってきたと思います。 私もそうでした。

ただ、私の場合、タイピングの練習をしているときに、「もっと良い配置にすればもっと良い記録が出るのでは?」とふと思いました。 そこで、どういった配置がいいのかを考えてみることにしました。

実際、ちょっと考えてみれば、これ(QWERTY配列)が最適な配列ではないことはすぐに分かります。 例えば、キーの中で最もタイピングしやすいのは当然両人差し指にある「F」と「J」です。 しかし、この2つってそんな使いますか?使いませんよね??? 当然、よく使う文字は打ちやすい場所にあるのが理想だと思うのですが、「F」や「J」は日本語でも英語でもそんなに使う文字ではないと思います。

既存のキー配列(QWERTY以外)

既に存在する有名なキー配列を調べたところ、DvorakやColemak、Maltronなどがありました。 また、配列とは違いますが、かな入力を使う、というのも考えました。

ただ、DvorakやColemakは英語入力を目的にしているということで、今回は採用を見送りました。 Maltronは情報が少なくてよく分からなかったのですが、いわゆるエルゴノミックキーボード(左右で分割されてるやつ、これみたいなやつ)を想定しているっぽく、使えないかなと思いました。 また、かな入力は英語を入力するのが厳しくなる可能性があるような気がしたのでやめました。

結局、有名所がなさそうだったので自分で考えるか、となった次第です。

私が使っているキー配列紹介

さて、ここで、私が使っている配列を紹介したいと思います。 ちなみに私のキーボードはREALFORCE TKL SA / R2TLSA-JP3-IVです。

f:id:gpageinin:20200426222433p:plain

ちなみに、この配列で使ってみて不満点があったら変えようと思って使い始めたのですが、変えるのが面倒で結局このままなので、ベストとは言い難いところはあります。 これについては後で書きます。

特徴

先にも述べた通り、私は日本人なので、日本語入力をターゲットにして作りました。 ローマ字入力を想定しています。 英語入力も勿論するので、一応そちらも考慮はしているのですが、日本語入力に重きを置いている感じです。

具体的には、(主に)母音を左手、子音を右手で打つように配置しています。 これは、タイピングは可能な限り左右の手で交互に打つのがスピードや疲労等の面で優れているので、母音と子音が基本的に交互に来る日本語の特性を考慮したためです。 また、母音を左手に配置しているのは、種類が多い子音を利き手で処理しようと思ったからです(私は右利きです)。

また、配置を見れば分かる通り、Shiftを同時に押したときの動作はUS配列のものを使っています。 これは個人的にそっちの方が好きというだけです。

さらに、ワンショットモディファイアという機能を導入しています。 これについては後述します。

あと、存在すると害をもたらす「CapsLock」と「Insert」については、消し去っています。

配置の決め方

多く打つ文字は打ちやすい場所に配置する、というのが根本的な考え方になります。

アルファベットキーの配置

キーのランク付け

まず、中3列のキーを以下のように(個人的な)打ちやすさでランク付けしました。 打ちやすい順に、

(打ちやすい)赤>橙>黄>緑>水色>紫(打ちにくい)

となっています。

ちなみに、タイピングにおいて小指を酷使するとよくない(痛めやすい)ので、小指は若干打ちにくいバイアスをかけています。 ただ、今回は意図的に小指の負担を小さく見積もった(本当にキツかったら変えようと思ったため)ので、もう少し小指の使用を重く見積もった方がいいかもしれません。 ここらへんは完全に使う人に依存しますね。 なぜ負担を小さく見積もったかというと、先に述べた私が使っているキーボードは荷重(キーを入力するのに必要な力)30gと非常に軽いためです。

f:id:gpageinin:20200426222436p:plain

配置

このサイトローマ字入力、英語入力における各アルファベットの出現頻度が載っています。 これを見ながら、出現頻度が高い順に配置していきます。 一応、以下の点を考慮しながら配置していたような気がします(配置を考えたのが1年以上前で一部しか覚えていない)。

  • 右手で子音、左手で母音になるように
    • ただし、場所の都合上、「F」や「L」、「J」などのごく稀に使う(使い方の半分程度は他のキーで代替できる)子音のキーは左手側に配置
  • 英語の出現頻度も一応参考にする
    • 例えば「K」は、日本語では非常に良く出てくる(頻度8位、子音では3位)が、英語ではほとんど出てこない(22位、子音では17位)ため、若干ランクの落ちる右手小指に配置
  • 二重母音(「AI」みたいなやつ)の中でもよく出てくるものに関しては、同じ指で打たないで済むように配置
  • 「Y」は拗音(「KYO」とか)入力のキモなので、拗音の入力がしやすい場所に配置
    • 単純に子音なのでというのと、私の利き手である右手で2回入力する方が楽なので、右手側に配置

あと、日本語でも英語でもほとんどお目にかかれない(頻度:日本語で0%、英語で0.08%)「Q」は左遷しました。

数字・記号キーの配置

正直、頻度とか調べながら配置を考えていたら疲れてしまって、こちらはあまり工夫していません。 しかし、アルファベットを配置した隙間に埋めていくことになるので、数字をここに埋めていくとバラバラになってしまって意味が分からなくなってしまいます。 なので、数字は大体そのままです。

ただ、「1」だけは1段下げています。 元々の場所があまりにも打ちづらいためです。

また、似ている感じのキーは近くに配置するようにしました。 「'」と「`」、「/」と「\」ですね。 本当は「,」と「.」も近くに配置したかったのですが、場所がなく、この配置になりました。

その他のキーの配置

これは後にも述べますが、アルファベットや数字、記号のキー配列を入れ替えるのは非常に強い副作用を伴うので、万人にオススメできるものではありません。 しかし、今から述べるキーの配置変更は、そこまで副作用が強くなく、キー配置変更の入門?としても有力だと思います。 ちなみに先に述べた「1」を1段下ろすのもチョイ変えにオススメです。 QWERTY配列だとちょうど「Q」を追いやることができます。

Ctrl

これは全人類やる義務がある有名なやつですが、Ctrlを中段最左に置きます。 大体CapsLockとかいうゴミになってるところですね。

BackSpace

入力を消す機会ってそこそこあると思うんですが、その割にBackSpaceの場所ってめちゃくちゃ遠いですよね。 大体の人は遠いのに見なくても押せるくらいには押してると思うんです。

なので、思い切って近くに持ってきました。 入力ミスを減らせば押す機会も減るということで、自戒を込めて割と低い緑ランク(しかも水色と迷った)の場所に配置しました。 また、Vimの1文字削除「x」の場所であり、役割的に似ていた、というのも配置理由の1つです。

Delete

これは完全に人によると思うのですが、私はDeleteをそこそこ使う人です。 ほぼBackSpaceと同列に扱っています。 そこで、BackSpaceの隣に配置しました。

Shift・Enter

これは後に述べますが、代替キーを使うので元の場所には不要です。 一応、僻地に残しておいてあります(それぞれ、元々Escだった場所とBackSpaceだった場所)。

IME-ON・IME-OFF

逆に元々Shiftだったところに置いてある謎のこれらですが、その実態は存在が忘れ去られている「変換」キー・「無変換」キーです。 私はIMEの設定で、「変換」を押すと無条件でひらがな入力モード、「無変換」を押すと無条件で直接入力モード(半角アルファベットが入力されるやつ)になるように設定しています。

Macユーザー以外だと通常これらは「半角/全角」で切り替えていると思いますが、これは今どっちにいるかを知る(覚えている)必要が生じます。

「半角/全角」→何か日本語を入力→アルファベットのまま入力されてる→BackSpace連打からの「半角/全角」

という流れを今まで何度繰り返してきたことか……、という話です。

というわけで、この設定はかなりオススメです。 今回は場所も変えていますが、一般的な「無変換」と「変換」の位置でも相当な効果が出ます。 IMEの設定を変えるだけでできます。

今回は場所も変えられるので、一等地(親指で押せるので)に置くにはもったいないので、Shiftの位置に置いています。

Esc

すぐ前でも述べましたが、親指で押せる場所(Space・変換・無変換)はタイピングにおいては一等地です。 ほぼ手を動かさずに押せるというのと、親指は基本的に暇してるからです。 これをうまく使えば単純計算で効率が1.25倍になりますよね。

この3つの一等地のうち、今回は左側にEscを配置しました。 これは完全に私がVimmerであることに由来します。 ご存知の通り、VimにおいてEscは超重要なキーであるため、ここに配置しました。 逆に、Vimmerでない方、もしくはCtrl-[派の方は、何か超使うキーとかを置いてくれればいいと思います。

Space

これは場所を変えていませんが、左下にも配置してあります。 後に述べますが、Spaceを長押しして空白を入れまくるやつが都合上元々のSpaceでできず、それのみこちらでやる、というためのものです。 まあこれをすることはほとんどないので、僻地に配置してあります。

ワンショットモディファイア

ワンショットモディファイアとは、あるキーに対して、他のキーと同時に押したときと、そのキー単独を押したときで別々の役割を持たせることです。 今回の配置では、以下の2箇所でこれを導入しています。

  • 元々Spaceだった場所(単独押し:Space、同時押し:Shift)
  • 元々「変換」だった場所(単独押し:Enter、同時押し:Ctrl)

ShiftとCtrlは修飾キー(他のキーと同時押しすることが前提のキー)の中でも群を抜いて押す機会の多いキーであるのは議論の余地がないと思います。 そこで、これらを一等地に置くことを考えます。 しかし、Spaceは(私の場合Escも)超絶押すキーなので、移動させるのが厳しいです。 そこで、これらのキーにワンショットモディファイアで2つの機能を持たせることにより、これを解決します。

あとは一等地3つのうちどこに配置するかを決めます。 ShiftもCtrlも両手どちらでも押せることが非常に望ましいわけですが、Ctrlは左手側には存在します(元々CapsLockだった場所)。 そこで、これを「変換」だった場所に配置します。 また、Spaceは横に長いために両手どちらの親指でも押せるので、ShiftをSpaceの場所に割り当てます。

そして、「変換」だった場所の単独押し機能が空いているので、ここにEnterを割り当てます。 Enterも当然しょっちゅう押すキーですから、お誂え向きなわけです。

ちなみに、Escの部分もワンショットモディファイアにしないのかという話ですが、特に割り当てたいキーがないのと、後に説明する都合により単独キーとしています。

キー配置変更の実現

さて、ここまで長々と書き連ねてきたわけですが、ではこれをどうやって実現するのかという話になります。 私はこれをWindows時代に実現したため、Windowsでの方法しか調べていません。 従って、Windowsでの方法のみ紹介します。 MacではKarabiner?とかいうのでできる、というのは調べていて見た記憶があります。 Linuxは分かりません。

あとお約束で一応言っておきますが、レジストリを書き換えるので自己責任でお願いします。 ソフトウェアで変えれば安全ですが、ソフトウェアを起動していないといけないのと、何かしら不具合というか変換が効かなくなることが多そうな気がしたので低レベルで変えます。

US配列にする

先に述べた通り、この配列はUS配列が元になっています。 ただ、普通に設定からUS配列にすると、「変換」や「無変換」など、一部のキーが使えません(当然US配列には存在しないため)。 ちなみに、この理由(「変換」や「無変換」がなく、キーの数が少なくてカスタマイズ性が下がる。しかも一等地のキーがない)があって私はUSキーボードは使いません。

話がそれました。 まあ今回は変換や無変換はIMEの設定で機能をつけているので、他のキーにすればいい話ですが、今回は別の方法でどうにかします。 「AXキーボード」という聞いたこともない代物を利用します。

AXキーボードとは、昔々に使われていたのか?というよく分からないキーボードで、日本版US配列、みたいな感じの配列をしているキーボードらしいです。 要は、Shiftと同時押ししたときに出てくる文字の部分だけUS配列で、あとはJIS配列みたいなやつです。 なので、かな入力もできます。

このサイトに従うと、キーボードをAXにすることができます。

スキャンコードのマッピング

あとは、このキーをどう変えていくか、という話になりますが、Change Keyみたいなソフトがあるので、これで入れ替えることができます。

ただ、これでどうなるかは私は動作確認していません。 もしかしたらこれでできるかもしれないので、できたら以下の説明は不要となります。

キーボードからの入力は、「スキャンコード」というキー固有の値としてキーボードから送られます。 このスキャンコードをCPUが受け取り、処理して文字として扱っています。 Change Keyみたいなソフトはいくつかありますが、全てこのスキャンコードの処理に干渉することでキーの入れ替えを実現しています。

ところで、このスキャンコードには、キーを押した瞬間に送られる「メイクコード」と、キーを離した瞬間に送られる「ブレイクコード」があります。 この2種類があるのでキーの長押しとかを処理できるのです(と私は思っています)。 私が確認した限り、こういう系のソフトは全て「メイクコード」しか処理していません。 後に述べますが、ワンショットモディファイアの設定ではブレイクコードに干渉するような処理を入れているので、ブレイクコードを処理していないこれらのソフトで動くかどうかは分かりません。

ソフトがないので、自力で書き換えるしかありません。 このサイトを参考に、レジストリを書き換えます。 このときに使うスキャンコードは、以下のサイトが参考になると思います。

また、ワンショットモディファイアキーに関しては、「他キーと同時押しした時の機能」を割り当てます。

さらに、Ctrl・Win・Alt・Shiftは左・右を区別します。 まあどちらでもいいわけですが、見た目通りの配置にしておけばいいかと思います。 ただ、AXキーボードは右Altに別の機能(「半角/全角」っぽい機能らしい)が割り当てられているので、通常のAltとして使いたい場合は「右Altのスキャンコードを左Altのものにする」という処理を入れる必要があります。 あと何故か私の設定ではCtrlを両方とも左にしていたので、右Ctrlにも別の機能が割り当てられていたのかもしれません。

ワンショットモディファイアの設定

AutoHotKeyというソフトを使います。 使い方の説明は割愛します(こことかを参考にしてください)。 以下のスクリプトを使えば上で説明した機能は実現できると思います。

~RShift up::
    If(A_PriorKey == "RShift")
    {
        Send, {Space}
    }
    Return

~LCtrl up::
    If(A_PriorKey == "LControl")
    {
        Send, {Enter}
    }
    Return

例えば1つ目なら「右Shiftを離したとき、最後に押したキーが右Shiftなら(すなわち、右Shiftを単押ししたときには)Spaceとする」みたいな感じです。 Spaceだった場所を右Shiftに、「変換」だった場所を左Ctrlにしている想定です。

なお、Escをワンショットモディファイアにしていないのは、ここで述べているような実装方法を取っているのが理由です。 すなわち、ワンショットモディファイアにすると、キーを押してから離すまでEscとして認識されず、次のキーとの間隔が短いときにEscが入力されない場合があるからです。 入力の一貫というか、流れのなかで押すことが多いキーなので、これが起きることが多く、単独キーとしました。

ちなみに私の場合、EnterはEscに比べると「そこで一区切り」感があって、その直後のキーを入力するまでの時間が長くなりがちなので、あまり問題になっていません。 Spaceはたまに暴発します。

感じている点

何となく感じている感想を挙げます。

  • 今「1」にしている部分を「0」にしてもいいかもしれない
    • プログラムを書いていると初期化で使うことが多く、今の配列では数字の中で一番押しやすいため
  • 「,」と「.」が押しづらい
    • 結構押すので、もうちょっと押しやすい場所に置いてもいいかもしれない
  • ワンショットモディファイア周りの不具合
    • ワンショットモディファイアの「Enter」を何か別のキーと同時押しする場合(Ctrl-EnterとかShift-Enterとか)に、うまく機能しない
      • ここらへんはAutoHotKeyの設定を詰めればいけるかもしれない
  • なんかキーの変更が効かない場合がある
    • Skyrimをプレイしたときはなぜか元々のキーだと認識されていました。理由はよく分かりません。
  • Linuxでの変更方法が分からん
    • 変更方法が分からない(面倒で調べてない)ので、Ubuntu機ではQWERTYで使っています。なので実質バイリンガルみたいなもんですね。慣れると意外と両方使えます。

問題点

キー配列変更による問題点を挙げます。

他の環境での作業が厳しい

他のキーボードとかマシンとかで作業するときに厳しい気持ちになります。 まあ普通QWERTYなので今まで手が覚えていると思うので、意外となんとかなります。

習得するまでのタイピング速度が大変なことになる

結構練習するか、ある程度の期間使い続けるまではタイピング速度が大変なことになります。

QWERTY配列でのタイピング速度が落ちる

当然ですが、QWERTY配列でのタイピング速度は落ちます。 私の場合、e-typingでのスコアが450くらいだったのが、現在は400くらいになっています。 変えた配列でのスコアは現在では350くらいです。 まあ配列を変えたあたりからタイピング練習をしなくなった、というのもあると思いますが。

ショートカットキーがやばい

ショートカットキーが変更できないツールだと、かなり厳しくなります。 通常は位置で覚えていると思うので、特に最初のうちは別のショートカットを暴発させまくります。 この点は、ソフトウェア的入れ替えをすれば一応なんとかなります(先に述べたAutoHotKeyなどでも、スクリプト起動時のみ変更を適用する、といったことができる)。

ゲームがやばい

configがないゲームだと詰みます。 WASDで移動とか言われても移動で死ぬほど苦しむことになります。

まとめ

キー配列の変更について色々書きました。 まあ私のキー配列変更はあまりに過激なので全くオススメしませんが、キー配列を変えること自体はかなり面白い部分があると思うので、参考とかインスピレーションとかになればと思います。 細かいところを少しChangeKey等で簡単に自分好みに変えてみる、というのを是非やってみてはいかがでしょうか。

デレステmaster+譜面の個人的メモ

個人的難所のメモ(練習に使う)

随時更新

Cool

双翼の独奏歌

Lv30

  • 4フリックの連続
  • 3-4、始点2ズレ、左右からの二重フリック
  • 右からの4階段の連続

美に入り彩を穿つ

Lv30

  • 右からの5フリック
  • 右手で8分ジグザグ
  • 右からの超長ジグザグ(x軸移動)
  • 左からのジグザグ反射+反射後に逆向き二重フリック
  • 4-4、始点1ズレ、1拍ズレ、右からの二重フリック

Trinity Field

Lv30

  • 同時2押しの8分乱れ打ち
  • 横幅が短く平行移動していく16分階段
  • 右からの5フリック
  • 右手でロング、左手でビート刻むやつ
  • 2フリックの連続(x軸移動)
  • 4-5、始点1ズレ、左からの二重フリック

ガールズ・イン・ザ・フロンティア

Lv30

  • 横幅が長い16分階段
  • 横幅が短く平行移動していく16分階段
  • 右手16分ジグザグ
  • 両手同時楔ジグザグ
  • 左からの5-5反射フリック

バベル

Lv31

  • 左右中央の8分縦連
  • 左右で別(難)テンポのロング・タップ
  • 発狂(タップのみ、部分的に24か32分)
  • 16分で片手は移動なし片手は階段
  • 左右で16分ジグザグ
  • 移動ロング+終わり際フリックの連続
  • 右からの3-5、始点2ズレの二重フリック

数学を厭わない強化学習(その3:方策勾配定理)

\usepackage{amsmath,amssymb}
\usepackage{pifont}

\usepackage{bigints}
\usepackage{bm}
\usepackage{siunitx}

\newcommand{\hs}[1]{\hspace{#1zw}}
\newcommand{\vs}[1]{\vspace{#1zh}}
\newcommand{\hsh}{\hs{0.5}}
\newcommand{\vsh}{\vs{0.5}}

\newcommand{\eref}[1]{\text{式\eqref{eq:#1}}}

\newcommand{\Nset}{\mathbf{N}}
\newcommand{\Zset}{\mathbf{Z}}
\newcommand{\Qset}{\mathbf{Q}}
\newcommand{\Rset}{\mathbf{R}}
\newcommand{\Cset}{\mathbf{C}}

\DeclareMathOperator*{\argmax}{\mathrm{arg\,max}}
\DeclareMathOperator*{\argmin}{\mathrm{arg\,min}}

\mathchardef\ordinarycolon\mathcode`\:
\mathcode`\:=\string"8000
\begingroup \catcode`\:=\active
  \gdef:{\mathrel{\mathop\ordinarycolon}}
\endgroup

\newcommand{\cond}[2]{#1\,|\,#2}

\newcommand{\expct}{\mathbb{E}}
\newcommand{\expctpi}{\mathbb{E}_{\pi}}

\newcommand{\Scal}{\mathcal{S}}
\newcommand{\Scalp}{\mathcal{S}^+}
\newcommand{\Acal}{\mathcal{A}}
\newcommand{\Rcal}{\mathcal{R}}

\newcommand{\sums}{\sum_s}
\newcommand{\sumsp}{\sum_{s'}}
\newcommand{\suma}{\sum_a}
\newcommand{\sumap}{\sum_{a'}}
\newcommand{\sumr}{\sum_r}

\newcommand{\pias}{\pi(\cond{a}s)}
\newcommand{\piapsp}{\pi(\cond{a'}{s'})}
\newcommand{\pitheta}{\pi_{\bm\theta}}
\newcommand{\pithetaas}{\pi_{\bm\theta}(\cond{a}s)}
\newcommand{\Ppi}{P_{\pi}}
\newcommand{\Ppin}{P_{\pi}^{~n}}
\newcommand{\Pcond}[2]{P(\cond{#1}{#2})}
\newcommand{\Ppicond}[2]{\Ppi(\cond{#1}{#2})}
\newcommand{\Ppincond}[2]{\Ppin(\cond{#1}{#2})}
\newcommand{\Psrsa}{\Pcond{s', r}{s, a}}
\newcommand{\Pssa}{\Pcond{s'}{s, a}}
\newcommand{\Prsa}{\Pcond{r}{s, a}}
\newcommand{\Ppisrs}{\Ppicond{s', r}s}
\newcommand{\Ppiss}{\Ppicond{s'}s}
\newcommand{\Ppirs}{\Ppicond{r}s}
\newcommand{\Ppinss}{\Ppincond{s'}s}
\newcommand{\Dpigamma}{D_{\pi}^{\,\gamma}}

\newcommand{\follow}{\sim}
\newcommand{\followpis}{\follow\pi(\cond{\cdot}s)}
\newcommand{\followpisp}{\follow\pi(\cond{\cdot}s')}
\newcommand{\twofollowPsa}{\follow\Pcond{\cdot, \cdot}{s, a}}
\newcommand{\onefollowPsa}{\follow\Pcond{\cdot}{s, a}}
\newcommand{\twofollowPpis}{\follow\Ppicond{\cdot, \cdot}s}
\newcommand{\onefollowPpis}{\follow\Ppicond{\cdot}s}

\newcommand{\Rsa}{R(s, a)}
\newcommand{\Rpis}{R_{\pi}(s)}

\newcommand{\prd}[1]{\bar{#1}}
\newcommand{\prds}{\prd{s}}
\newcommand{\prda}{\prd{a}}

\newcommand{\Vpi}{V_{\pi}}
\newcommand{\Vpis}{\Vpi(s)}
\newcommand{\Vpisp}{\Vpi(s')}
\newcommand{\Qpi}{Q_{\pi}}
\newcommand{\Qpisa}{\Qpi(s, a)}
\newcommand{\Qpispap}{\Qpi(s', a')}

\newcommand{\Vpihat}{\hat{V}_{\pi}}
\newcommand{\Vpihats}{\hat{V}_{\pi}(s)}
\newcommand{\Qpihat}{\hat{Q}_{\pi}}
\newcommand{\Qpihatsa}{\hat{Q}_{\pi}(s, a)}

\newcommand{\QED}{\blacksquare}

\newcommand{\Jtheta}{J_{\bm\theta}}
\newcommand{\df}{\bm\nabla}
\newcommand{\gpi}{g_{\pi}}
\newcommand{\gpisa}{\gpi(s, a)}
\newcommand{\gpispap}{\gpi(s', a')}

前回の続きです。

前回まではValue-basedな手法の基礎を扱ったので、今回はPolicy-basedな手法の基礎である、方策勾配定理の証明をしていきます。

方策勾配定理

準備

方策 $\pitheta の「良さ」 $\Jtheta を、

\Jtheta:=\expct\bigl[\cond{\Vpi(s_0)}{s_0\follow\iota(\cdot)}\bigr]=\sum_{s_0}\iota(s_0)\Vpi(s_0) \label{eq:J}

で定義します。 式から分かるように、$s_0\in\Scal は開始状態です。 状態価値は(方策に従った際の)割引報酬和の期待値なので、開始状態における状態価値を最大化する(ような方策を獲得する)のが強化学習の目的と言えるからです。

さて、方策勾配定理は、$\bm\nabla_{\bm\theta}\Jtheta を与える定理です。 これが分かれば、勾配法によって方策を改善することができるため、重要な定理です。

なお、以下では $\bm\nabla_{\bm\theta} は単に $\df と表記します。 また、新たな記号 $\gpisa を、

\gpisa:=\Qpisa\df\pias \label{eq:g}

と定義します。

さらに、微分操作が入っているので、ここでは $\Vpis や $\Qpisa、$\Jtheta 等は全て $\bm\theta微分可能であるとしておきます。

定理の主張

\df\Jtheta=\sums\Dpigamma(s)\suma\gpisa \label{eq:thm}

証明

補題

まず、補題

\df\Vpis=\sumsp\sum_{n=0}^{\infty}\gamma^n\Ppinss\suma\gpi(s', a) \label{eq:lem}

を示します。

まず、$\df\Vpis を以下のように変形していきます。

\df\Vpis &=\df\suma\pias\Qpisa\hs1\text{(状態価値と行動価値の関係式その1)} \\
              &=\suma\bigl\{\Qpisa\df\pias+\pias\df\Qpisa\bigr\}\hs1\text{(積の微分)} \\
              &=\suma\gpisa+\suma\pias\df\Qpisa\hs1\text{($\eref{g}$)} \\
              &=\suma\gpisa+\suma\pias\df\left\{\Rsa+\gamma\sumsp\Pssa\Vpisp\right\}\hs1\text{(状態価値と行動価値の関係式その2)} \\
              &=\suma\gpisa+\suma\pias\sumsp\gamma\Pssa\df\Vpisp\hs1\text{($\Rsa$、$\Pssa$ は $\bm\theta$ に依存しない)} \\
              &=\suma\gpisa+\sumsp\gamma\suma\pias\Pssa\df\Vpisp\hs1\text{(総和を取る順序を交換)} \\
              &=\suma\gpisa+\sumsp\gamma\Ppiss\df\Vpisp\hs1\text{(定義)}

結局、得られた式は

\df\Vpis=\suma\gpisa+\sumsp\gamma\Ppiss\df\Vpisp \label{eq:rec-1}

となりますが、これは、$\df\Vpis の漸化式であると見なすことができます。 そこで、この式の右辺の $\df\Vpisp に対して同様の変形を行い、さらに変形していきます。

\df\Vpis &=\suma\gpisa+\sumsp\gamma\Ppiss\left\{\sumap\gpispap+\sum_{s''}\gamma\Ppi(\cond{s''}{s'})\df\Vpi(s'')\right\} \\
              &=\suma\gpisa+\sumsp\gamma\Ppiss\sumap\gpispap+\sum_{s''}\gamma^2\sumsp\Ppi(\cond{s''}{s'})\Ppiss\df\Vpi(s'')\hs1\text{(総和を取る順序を交換)} \\
              &=\sumsp\gamma^0P_{\pi}^{~0}(\cond{s'}s)\suma\gpi(s', a)+\sumsp\gamma^1P_{\pi}^{~1}(\cond{s'}s)\suma\gpi(s', a)+\sum_{s''}\gamma^2P_{\pi}^{~2}(\cond{s''}s)\df\Vpi(s'')\hs1\text{(定義・走る文字変更)}

以上の変形の過程と結果の形から、「『$\df\Vpis』に対して $\eref{rec-1} を $n 回適用した式の最右辺は、以下に示す $\eref{rec-n} となる」という予想を立てることができます(最後の項の走る文字も変更しました)。 これは数学的帰納法を用いれば簡単に証明できるので、ここでは正しいということにします。 数式を書くの結構辛いので許してください。

\df\Vpis=\sum_{k=0}^{n-1}\sumsp\gamma^kP_{\pi}^{~k}(\cond{s'}s)\suma\gpi(s', a)+\sumsp\gamma^n\Ppinss\df\Vpisp \label{eq:rec-n}

この式の右辺の最初の2種類の総和(走る文字が $k と $s')を交換してから $n\to\infty の極限を取ることで、補題 $\eref{lem}$ が証明されます。 但し、以下に注意します。

  • $\gamma\in[0, 1](定義)
  • $\Ppinss\in[0, 1](確率なので)
  • $\df\Vpisp は有限値($\Vpisp微分可能であるという仮定)
  • 十分大きな $n に対し、$\Ppinss は $s'\in\Scal ならば十分小さな値を取る
    • これが満たされない(永遠に行動し続けられ得る)場合は、$T の定義の部分で述べたように、$\gamma\in[0, 1) となっている

証明本体

ここまで来れば簡単です。 $\eref{lem} を $\eref{J} に適用して、$\df\Jtheta を計算します。

\df\Jtheta &=\sum_{s_0}\iota(s_0)\df\Vpi(s_0)\hs1\text{($\iota(s_0)$ は $\bm\theta$ に依存しない)} \\
                &=\sum_{s_0}\iota(s_0)\sums\sum_{n=0}^{\infty}\gamma^n\Ppin(\cond{s}{s_0})\suma\gpi(s, a)\hs1\text{($\eref{lem}$)} \\
                &=\sums\left\{\sum_{s_0}\iota(s_0)\sum_{n=0}^{\infty}\gamma^n\Ppin(\cond{s}{s_0})\right\}\suma\gpi(s, a)\hs1\text{(総和を取る順序を交換)} \\
                &=\sums\Dpigamma(s)\suma\gpisa\hs1\text{(定義)}\hs1\QED

補足

$\df\pias=\pias\df\ln\pias を利用して、さらに変形を続ける場合もあります。

\df\Jtheta &=\sums\Dpigamma(s)\suma\Qpisa\df\pias \\
                 &=\sums\Dpigamma(s)\suma\Qpisa\pias\df\ln\pias \\
                 &=\sums\Dpigamma(s)\expct\bigl[\cond{\Qpisa\df\ln\pias}{a\followpis}\bigr]

さらに $\gamma=1 の場合には、$\Dpigamma(s) は確率分布となるので、上の変形の2行目から以下のように変形できます。

\df\Jtheta &=\sums\Dpigamma(s)\suma\Qpisa\pias\df\ln\pias \\
                 &=\expct\bigl[\cond{\Qpisa\df\ln\pias}{s\follow\Dpigamma(\cdot), a\followpis}\bigr]

まとめ

今回は方策勾配定理の証明をしました。 次回扱う内容は未定です。

数学を厭わない強化学習(その2:Bellman方程式など(続))

\usepackage{amsmath,amssymb}
\usepackage{pifont}

\usepackage{bigints}
\usepackage{bm}
\usepackage{siunitx}

\newcommand{\hs}[1]{\hspace{#1zw}}
\newcommand{\vs}[1]{\vspace{#1zh}}
\newcommand{\hsh}{\hs{0.5}}
\newcommand{\vsh}{\vs{0.5}}

\newcommand{\eref}[1]{\text{式\eqref{eq:#1}}}

\newcommand{\Nset}{\mathbf{N}}
\newcommand{\Zset}{\mathbf{Z}}
\newcommand{\Qset}{\mathbf{Q}}
\newcommand{\Rset}{\mathbf{R}}
\newcommand{\Cset}{\mathbf{C}}

\DeclareMathOperator*{\argmax}{\mathrm{arg\,max}}
\DeclareMathOperator*{\argmin}{\mathrm{arg\,min}}

\mathchardef\ordinarycolon\mathcode`\:
\mathcode`\:=\string"8000
\begingroup \catcode`\:=\active
  \gdef:{\mathrel{\mathop\ordinarycolon}}
\endgroup

\newcommand{\cond}[2]{#1\,|\,#2}

\newcommand{\expct}{\mathbb{E}}
\newcommand{\expctpi}{\mathbb{E}_{\pi}}

\newcommand{\Scal}{\mathcal{S}}
\newcommand{\Scalp}{\mathcal{S}^+}
\newcommand{\Acal}{\mathcal{A}}
\newcommand{\Rcal}{\mathcal{R}}

\newcommand{\sums}{\sum_s}
\newcommand{\sumsp}{\sum_{s'}}
\newcommand{\suma}{\sum_a}
\newcommand{\sumap}{\sum_{a'}}
\newcommand{\sumr}{\sum_r}

\newcommand{\pias}{\pi(\cond{a}s)}
\newcommand{\piapsp}{\pi(\cond{a'}{s'})}
\newcommand{\pitheta}{\pi_{\bm\theta}}
\newcommand{\pithetaas}{\pi_{\bm\theta}(\cond{a}s)}
\newcommand{\Ppi}{P_{\pi}}
\newcommand{\Ppin}{P_{\pi}^{~n}}
\newcommand{\Pcond}[2]{P(\cond{#1}{#2})}
\newcommand{\Ppicond}[2]{\Ppi(\cond{#1}{#2})}
\newcommand{\Ppincond}[2]{\Ppin(\cond{#1}{#2})}
\newcommand{\Psrsa}{\Pcond{s', r}{s, a}}
\newcommand{\Pssa}{\Pcond{s'}{s, a}}
\newcommand{\Prsa}{\Pcond{r}{s, a}}
\newcommand{\Ppisrs}{\Ppicond{s', r}s}
\newcommand{\Ppiss}{\Ppicond{s'}s}
\newcommand{\Ppirs}{\Ppicond{r}s}
\newcommand{\Ppinss}{\Ppincond{s'}s}
\newcommand{\Dpigamma}{D_{\pi}^{\,\gamma}}

\newcommand{\follow}{\sim}
\newcommand{\followpis}{\follow\pi(\cond{\cdot}s)}
\newcommand{\followpisp}{\follow\pi(\cond{\cdot}s')}
\newcommand{\twofollowPsa}{\follow\Pcond{\cdot, \cdot}{s, a}}
\newcommand{\onefollowPsa}{\follow\Pcond{\cdot}{s, a}}
\newcommand{\twofollowPpis}{\follow\Ppicond{\cdot, \cdot}s}
\newcommand{\onefollowPpis}{\follow\Ppicond{\cdot}s}

\newcommand{\Rsa}{R(s, a)}
\newcommand{\Rpis}{R_{\pi}(s)}

\newcommand{\prd}[1]{\bar{#1}}
\newcommand{\prds}{\prd{s}}
\newcommand{\prda}{\prd{a}}

\newcommand{\Vpi}{V_{\pi}}
\newcommand{\Vpis}{\Vpi(s)}
\newcommand{\Vpisp}{\Vpi(s')}
\newcommand{\Qpi}{Q_{\pi}}
\newcommand{\Qpisa}{\Qpi(s, a)}
\newcommand{\Qpispap}{\Qpi(s', a')}

\newcommand{\Vpihat}{\hat{V}_{\pi}}
\newcommand{\Vpihats}{\hat{V}_{\pi}(s)}
\newcommand{\Qpihat}{\hat{Q}_{\pi}}
\newcommand{\Qpihatsa}{\hat{Q}_{\pi}(s, a)}

\newcommand{\QED}{\blacksquare}

前回の続きです。

前回は5つの式を扱いました(下に再掲)。 このうち $\eref{C} 以外は、サンプリングした変数が含まれていて、方程式としてはかなり怪しい感じになっています。 というか、そのままでは普通にダメです。 確率的な変数が式に入っているのにイコールが成り立つ方がおかしいですからね。 (前回は、イメージを掴みやすくするために、なるべく簡単な形の式にしたかった、という目的があり、敢えて不正確な形で終わらせました。)

というわけで今回は、サンプリング処理を取り除いた、完全な形の式を紹介します。

C_t &=r_{t+1}+\gamma C_{t+1} \label{eq:C} \\
\Vpis &=\Rpis+\gamma\Vpisp & \text{($s'\onefollowPpis$)} \label{eq:V} \\
\Vpis &=\Qpisa & \text{($a\followpis$)} \label{eq:VQ} \\
\Qpisa &=\Rsa+\gamma\Vpisp & \text{($s'\onefollowPsa$)} \label{eq:QV} \\
\Qpisa &=\Rsa+\gamma\Qpispap & \text{($s'\onefollowPsa$、$a'\followpisp$)} \label{eq:Q}

方針

前述した通り、問題なのは確率変数が(そのまま)方程式に含まれていることです。 例えば $\eref{V} で言えば、$s' をサンプリングしているため、$\Vpisp そのものが確率変数になっています。 そこで、$\Vpisp をそのまま使うのではなく、$\Vpisp の期待値 $\expct\bigl[\Vpisp\bigr] を用いた式にすることで、サンプリングという処理を包括した、正確な式にすることができます。

その後は、期待値の定義に則って、具体的な確率と総和を用いた形式に変形するだけです。

この方針で、記事の最初に示した式からスタートして、4つの式を書き換えていきます。

状態価値に対するBellman方程式

\Vpis &=\Rpis+\gamma\Vpisp\hs1\text{($s'\onefollowPpis$)}  \notag \\
         &=\Rpis+\gamma\expct\bigl[\cond{\Vpisp}{s'\onefollowPpis}\bigr] \notag \\
         &=\Rpis+\gamma\suma\pias\sumsp\Pssa\Vpisp

状態価値と行動価値の関係式その1

\Vpis &=\Qpisa\hs1\text{($a\followpis$)} \notag \\
         &=\expct\bigl[\cond{\Qpisa}{a\followpis}\bigr] \notag \\
         &=\suma\pias\Qpisa

状態価値と行動価値の関係式その2

\Qpisa &=\Rsa+\gamma\Vpisp\hs1\text{($s'\onefollowPsa$)} \notag \\
            &=\Rsa+\gamma\expct\bigl[\cond{\Vpisp}{s'\onefollowPsa}\bigr] \notag \\
            &=\Rsa+\gamma\sumsp\Pssa\Vpisp

行動価値に対するBellman方程式

\Qpisa &=\Rsa+\gamma\Qpispap\hs1\text{($s'\onefollowPsa$、$a'\followpisp$)} \notag \\
            &=\Rsa+\gamma\expct\bigl[\cond{\Qpispap}{s'\onefollowPsa, a'\followpisp}\bigr] \notag \\
            &=\Rsa+\gamma\sumsp\Pssa\sumap\piapsp\Qpispap

まとめ

今回は正確な形の、ちゃんとした「方程式」が得られました。 得られた4つの式を下にまとめて再掲します。

\Vpis &=\Rpis+\gamma\suma\pias\sumsp\Pssa\Vpisp \\
\Vpis &=\suma\pias\Qpisa \\
\Qpisa &=\Rsa+\gamma\sumsp\Pssa\Vpisp \\
\Qpisa &=\Rsa+\gamma\sumsp\Pssa\sumap\piapsp\Qpispap

次回は方策勾配定理を扱います。

数学を厭わない強化学習(その1:Bellman方程式など)

\usepackage{amsmath,amssymb}
\usepackage{pifont}

\usepackage{bigints}
\usepackage{bm}
\usepackage{siunitx}

\newcommand{\hs}[1]{\hspace{#1zw}}
\newcommand{\vs}[1]{\vspace{#1zh}}
\newcommand{\hsh}{\hs{0.5}}
\newcommand{\vsh}{\vs{0.5}}

\newcommand{\eref}[1]{\text{式\eqref{eq:#1}}}

\newcommand{\Nset}{\mathbf{N}}
\newcommand{\Zset}{\mathbf{Z}}
\newcommand{\Qset}{\mathbf{Q}}
\newcommand{\Rset}{\mathbf{R}}
\newcommand{\Cset}{\mathbf{C}}

\DeclareMathOperator*{\argmax}{\mathrm{arg\,max}}
\DeclareMathOperator*{\argmin}{\mathrm{arg\,min}}

\mathchardef\ordinarycolon\mathcode`\:
\mathcode`\:=\string"8000
\begingroup \catcode`\:=\active
  \gdef:{\mathrel{\mathop\ordinarycolon}}
\endgroup

\newcommand{\cond}[2]{#1\,|\,#2}

\newcommand{\expct}{\mathbb{E}}
\newcommand{\expctpi}{\mathbb{E}_{\pi}}

\newcommand{\Scal}{\mathcal{S}}
\newcommand{\Scalp}{\mathcal{S}^+}
\newcommand{\Acal}{\mathcal{A}}
\newcommand{\Rcal}{\mathcal{R}}

\newcommand{\sums}{\sum_s}
\newcommand{\sumsp}{\sum_{s'}}
\newcommand{\suma}{\sum_a}
\newcommand{\sumap}{\sum_{a'}}
\newcommand{\sumr}{\sum_r}

\newcommand{\pias}{\pi(\cond{a}s)}
\newcommand{\piapsp}{\pi(\cond{a'}{s'})}
\newcommand{\pitheta}{\pi_{\bm\theta}}
\newcommand{\pithetaas}{\pi_{\bm\theta}(\cond{a}s)}
\newcommand{\Ppi}{P_{\pi}}
\newcommand{\Ppin}{P_{\pi}^{~n}}
\newcommand{\Pcond}[2]{P(\cond{#1}{#2})}
\newcommand{\Ppicond}[2]{\Ppi(\cond{#1}{#2})}
\newcommand{\Ppincond}[2]{\Ppin(\cond{#1}{#2})}
\newcommand{\Psrsa}{\Pcond{s', r}{s, a}}
\newcommand{\Pssa}{\Pcond{s'}{s, a}}
\newcommand{\Prsa}{\Pcond{r}{s, a}}
\newcommand{\Ppisrs}{\Ppicond{s', r}s}
\newcommand{\Ppiss}{\Ppicond{s'}s}
\newcommand{\Ppirs}{\Ppicond{r}s}
\newcommand{\Ppinss}{\Ppincond{s'}s}
\newcommand{\Dpigamma}{D_{\pi}^{\,\gamma}}

\newcommand{\follow}{\sim}
\newcommand{\followpis}{\follow\pi(\cond{\cdot}s)}
\newcommand{\followpisp}{\follow\pi(\cond{\cdot}s')}
\newcommand{\twofollowPsa}{\follow\Pcond{\cdot, \cdot}{s, a}}
\newcommand{\onefollowPsa}{\follow\Pcond{\cdot}{s, a}}
\newcommand{\twofollowPpis}{\follow\Ppicond{\cdot, \cdot}s}
\newcommand{\onefollowPpis}{\follow\Ppicond{\cdot}s}

\newcommand{\Rsa}{R(s, a)}
\newcommand{\Rpis}{R_{\pi}(s)}

\newcommand{\prd}[1]{\bar{#1}}
\newcommand{\prds}{\prd{s}}
\newcommand{\prda}{\prd{a}}

\newcommand{\Vpi}{V_{\pi}}
\newcommand{\Vpis}{\Vpi(s)}
\newcommand{\Vpisp}{\Vpi(s')}
\newcommand{\Qpi}{Q_{\pi}}
\newcommand{\Qpisa}{\Qpi(s, a)}
\newcommand{\Qpispap}{\Qpi(s', a')}

\newcommand{\Vpihat}{\hat{V}_{\pi}}
\newcommand{\Vpihats}{\hat{V}_{\pi}(s)}
\newcommand{\Qpihat}{\hat{Q}_{\pi}}
\newcommand{\Qpihatsa}{\hat{Q}_{\pi}(s, a)}

\newcommand{\QED}{\blacksquare}

前回の続きです。

今回はValue-basedな手法の基礎となるBellman方程式など、状態価値 $\Vpis と行動価値 $\Qpisa が絡む式4つをメインに扱います。 イメージを掴みやすくするため、見やすい形で表現することにしたので、式の右辺が若干怪しい表現になってますけど、そこは許してください。 (期待値が絡むところなので式が見にくくなってしまうのと、説明が長くなってしまうためです。 正確なバージョンは次回扱います。)

割引報酬和が満たす漸化式

まず、後に証明で用いる重要な漸化式を証明します。 証明は簡単ですが、その意義は重要です。

C_t=r_{t+1}+\gamma C_{t+1} \label{eq:C}

イメージ

割引報酬和は「終端状態に到達するまでに得られた報酬たちを、それぞれに時間による重みをつけて総和を取る」という演算でした。 なので、総和のうち、最初にもらえる報酬を総和の外に出した、というだけです。 文章だと逆に分かりづらいですが、証明を見れば明らかです。

証明

C_t &=\sum_{t'=t+1}^T\gamma^{t'-t-1}r_{t'}\hs1\text{(定義)} \\
       &=r_{t+1}+\sum_{t'=t+2}^T\gamma^{t'-t-1}r_{t'} \\
       &=r_{t+1}+\gamma\sum_{t'=(t+1)+1}^T\gamma^{t'-(t+1)-1}r_{t'} \\
       &=r_{t+1}+\gamma C_{t+1}\hs1\text{(定義)}\hs1\QED

簡単な関係式でしたが、この考え方は以下で説明する4つの式全てに通じるものなので、重要です。 というか、Bellman方程式に関しては、形がこれとほぼ同じです。 まあ状態価値とか行動価値はそもそも(条件は色々ついているものの)割引報酬和そのものですから、当たり前と言えば当たり前なのですが。

状態価値に対するBellman方程式

状態価値 $\Vpis に対するBellman方程式は、以下の通りです。

\Vpis=\Rpis+\gamma\Vpisp\hs1\text{($s'\onefollowPpis$)} \label{eq:V}

証明

\Vpis &=\expctpi[\cond{C_t}{s_t=s}]\hs1\text{(定義)} \\
         &=\expctpi\bigl[\cond{r_{t+1}+\gamma C_{t+1}}{r_{t+1}, s_{t+1}\twofollowPpis}\bigr]\hs1\text{(\eref{C}・$\pi$ に従う条件を明示)} \\
         &=\expctpi\bigl[\cond{r_{t+1}}{r_{t+1}\onefollowPpis}\bigr]+\gamma\expctpi\bigl[\cond{C_{t+1}}{s_{t+1}\onefollowPpis}\bigr]\hs1\text{(和の期待値は期待値の和)} \\
         &=\Rpis+\gamma\Vpisp\hs1\text{($s'\onefollowPpis$)}\hs1\text{(定義)}\hs1\QED

状態価値と行動価値の関係式その1

状態価値と行動価値にも強い関係があります。 繰り返しになりますが、両方とも実質同じものを指してるので当たり前なのですが。

\Vpis=\Qpisa\hs1\text{($a\followpis$)} \label{eq:VQ}

証明

\Vpis &=\expctpi[\cond{C_t}{s_t=s}]\hs1\text{(定義)} \\
         &=\expctpi\bigl[\cond{C_t}{s_t=s, a_t\followpis}\bigr]\hs1\text{($\pi$ に従う条件を明示)} \\
         &=\Qpisa\hs1\text{($a\followpis$)}\hs1\text{(定義)}\hs1\QED

状態価値と行動価値の関係式その2

先程とは逆に、$\Qpisa を変形していきます。

\Qpisa=\Rsa+\gamma\Vpisp\hs1\text{($s'\onefollowPsa$)} \label{eq:QV}

証明

\Qpisa &=\expctpi[\cond{C_t}{s_t=s, a_t=a}]\hs1\text{(定義)} \\
            &=\expctpi\bigl[\cond{r_{t+1}+\gamma C_{t+1}}{r_{t+1}, s_{t+1}\twofollowPsa}\bigr]\hs1\text{(\eref{C}・$\pi$ に従う条件を明示)} \\
            &=\expctpi\bigl[\cond{r_{t+1}}{r_{t+1}\onefollowPsa}\bigr]+\gamma\expctpi\bigl[\cond{C_{t+1}}{s_{t+1}\onefollowPsa}\bigr]\hs1\text{(和の期待値は期待値の和)} \\
            &=\Rsa+\gamma\Vpisp\hs1\text{($s'\onefollowPsa$)}\hs1\text{(定義)}\hs1\QED

行動価値に対するBellman方程式

状態価値 $\Qpisa に対するBellman方程式は、以下の通りです。

\Qpisa=\Rsa+\gamma\Qpispap\hs1\text{($s'\onefollowPsa$、$a'\followpisp$)} \label{eq:Q}

証明

\Qpisa &=\Rsa+\gamma\Vpisp\hs1\text{($s'\onefollowPsa$)}\hs1\text{(\eref{QV})} \\
            &=\Rsa+\gamma\Qpispap\hs1\text{($s'\onefollowPsa$、$a'\followpisp$)}\hs1\text{(\eref{VQ})}\hs1\QED

まとめ

5本ほど式と証明を紹介しました。 下にまとめて再掲します。

C_t &=r_{t+1}+\gamma C_{t+1} \\
\Vpis &=\Rpis+\gamma\Vpisp & \text{($s'\onefollowPpis$)} \\
\Vpis &=\Qpisa & \text{($a\followpis$)} \\
\Qpisa &=\Rsa+\gamma\Vpisp & \text{($s'\onefollowPsa$)} \\
\Qpisa &=\Rsa+\gamma\Qpispap & \text{($s'\onefollowPsa$、$a'\followpisp$)}

まあ証明は割とどうでもよくて、イメージを掴むことが大事だと思います。 具体的には、

  • 割引報酬和の漸化式($\eref{C}
  • 状態価値は(方策に従った時の)割引報酬和
  • 行動価値は(最初の行動以外は方策に従った時の)割引報酬和

この3つを抑えて適切に使えば、$\eref{C} 以外の4つの式は全て妥当なものに見えてくると思います。

それでは、今回はここまでです。 次回はサンプリングが入らないちゃんとした形のBellman方程式などを扱います。