情報系の手考ノート

数学とか情報系の技術とか調べたり勉強したりしてメモしていきます.

研究用のプログラムでやっておくといいこと

修士の研究を通じて,研究用のプログラム作成時に意識したりやっておくと良いことというのが,自分の中である程度確立されてきました. この記事ではそのノウハウのようなものを共有することを目的として,なにをすれば良いのか,どうしてそれをすると良いのか,という点について書いていきます.

これから研究を始める学部生あたりが対象読者になると思われます. もしかしたら修士学生でも役に立つかもしれません.

また私の研究がニューラルネットワークに関するものだったため,主にニューラルネットワークの研究を行う場合のものになります. 他の研究の場合でも使える知見はあるかもしれないですが,あまり期待しないでもらえると助かります.

拡張性の高いプログラムを心掛けよう

研究で使うプログラムは頻繁に修正されます. 手法を改良したとき,新たに実験を行うとき,とにかくいろんな時に修正されます. 常に仕様が変わり続けるわけです. そのため拡張性が高いプログラムを意識することが重要になります. 3層のネットワークを4層にする,というような状況で層数をハードコーディングしないという工夫をするだけで,簡単に層数を変えて実験できたりします. 拡張性を意識しようとすると,最初にプログラムを書くときが大変にはなります. ですが一度作ったプログラムが拡張しやすければ,後々の修正・拡張に手間がかからなくなるので,研究の期間が長ければ長いほど有益な結果になります. まぁある程度限度というものはありますが….

ログめっちゃ残せ

ログは無理のない範囲でめちゃくちゃ残しておきましょう. 標準出力にログを吐いて,リダイレクトでテキストに保存する程度で十分です. もっと管理できるにこしたことはないですが,そこまでやる必要はあまりないです. ただ生で保存するととんでもないデータ量になるので,適宜圧縮するなりしておくと良いです.

なんでこんなことを言うのかというと,実験の考察等に非常に有用だからです. 最近のニューラルネットワークの研究はアホみたいに時間がかかることがざらにあります. ものによっては1試行で1週間とか平気で使います. そんな実験で想定外の結果が出たときに,原因の考察に必要な情報が多いほうが良いに決まってます. 無いよりはあったほうがいいです. 必要な情報が無いから再度結果を取り直しなんてことになったら目も当てられません. 原因調査のために数ヶ月要するなんてのは,1年も研究できない状態では絶望的な状況です.

そのためには必要そうな情報はできるだけ出力しておく必要があります. 例えば損失関数の値です. 実験内容にもよりますが,最低各エポックごとの値を出力しておいたほうがいいでしょう. そうすれば,例えば NaN が出たとしても原因となるエポック数が特定できます. 損失関数が複数の項から構成されるなら,各項での値も出しておくと尚良いです. どの項で NaN が出たのか,というのがわかります. それだけでも考えられることが多いです.

あと重要なのは使ったパラメータを表示しておくことです. エポック数とか学習率とかです. 実行時にこれを表示して保存しておけば,その実験結果がどんな条件のものかわかります. 人間簡単に忘れたりするので,こういう情報はしっかり出力しておくと良いです. 実際私も過去の自分に幾度も救われています.

他にも必要そうな情報があれば無理のない範囲でしっかり保存しておくといいでしょう. 未来の自分を助けることができます.

乱数は固定しよう

乱数を固定できればリファクタリングやプログラムの修正を行った後に,動作確認をするのが格段に楽になります. さらに実験の再現性もある程度担保できます. やらない手は無いです. PyTorch を使うならとりあえず下記コードを最初に実行しておけばいいかと思われます.

# Python の乱数
random.seed(seed)

# Numpy の乱数
np.random.seed(seed)

# PyTorch の乱数等
torch.manual_seed(seed)
torch.backends.cudnn.benchmark = False
torch.backends.cudnn.deterministic = True

上記コード実行後に実行するプログラムは seed の値ごとに動作が固定されるはずです. これによってプログラム修正後に,前のプログラムと同じ動作をするか確認するという作業が可能になります. 同じ seed で同じ結果が出るなら同じプログラムと見做せます. 研究では実験結果の正当性が重要になるので,プログラム修正後の動作確認ができるのは非常に重要です. 丁寧にログを出しておけば,どこでプログラムの動作が変わったかを調べるのも簡単になります.

というか動作確認はちゃんとしましょう. ソフトウェアの開発で単体テストなんかが重要視されているのに,研究では重要ではないなんてことはあるわけがありません. できるならちゃんとテストをすべきではあります. とはいってもそんな余裕が無いかもしれません. そんな場合でも seed を固定できるようにしておけば,同じ動作をすることは保証できます.

後で述べますが,この seed の値をコマンドライン引数で指定できるようにしておくと尚便利になります.

コマンドライン引数で動作の切り替えをできると便利

実験をするとき様々な条件で比較をするというのは当たり前に行われることだと思います. 例えばエポック数を変えるとか,学習率を変えるとか,バッチサイズを変えるとかいくらでも条件はあります. いわゆるハイパーパラメータというやつです. このハイパーパラメータを変えて実験を行うとき,毎回プログラムを変えるのは非効率的です. それにどのパラメータで実験した結果かもわかりにくかったりします.

このようなパラメータをコマンドライン引数で指定できると,プログラムを変えずエポック数や学習率といったパラメータを実行時に指定して実験ができるようになります. 例えば以下のような感じです.

> python3 main.py --epoch 10 --lr 0.1
...
> python3 main.py --epoch 30 --lr 0.01
...

Python なら標準でコマンドライン引数をパースするパッケージがあるので,それを使えば大して難しいことでもありません. コストパフォーマスは間違いなく良いです. 特に修士に進むつもりの学部生のように,ある程度の期間研究をするなら尚更コストパフォーマンスは良くなります.

実験の実行やデータの集計や整形はスクリプト化すると便利

上で述べたようなコマンドライン引数による実験条件の指定などをできるようにしておくと,もっと便利にできるようになります. それは実験のスクリプト化です. 実験をする時に,シェルスクリプトを書いてそれを実行するだけにしておくというものです. これは複数試行実験する場合に真価を発揮します. シェルスクリプトにコメントとして,いつどんな目的でどういう考えに基づいて実験したのかを書いておけば,あとでその実験の意図などがわかるようになります. 結局は後々の自分のためです.

またデータの集計なんかもスクリプト化すると楽ができます. 複数試行実験した場合,精度等の平均や標準誤差等を計算することになります. この計算をスクリプト化することで,毎回の実験では実行用のスクリプトを動かすだけでよくなります. 無駄な手間が減って実験に集中できるうえに,人的ミスを減らせます. このような機械的な作業は計算機にやらせてしまうのが効率的です.

機械的な作業といえばデータの可視化などもあります. 精度等の平均をグラフにプロットする,という操作も当然スクリプト化できます. これもやっておくと,大量に実験した結果を機械的に可視化できます. いちいち Excel なり Google Spread Sheet なりを開いて可視化するよりよっぽど効率的です. それに,やはり人的ミスを減らせます.

実験結果から考察等をする時に,人的ミスがまず間違いなく起こります. 人間そんな完璧じゃないです. そんな人的ミスのリスクを減らせるうえに,研究の重要な部分に時間をかけられるようになるという意味で,実験の実行,データの集計,可視化等のスクリプト化はかなり重要です. そしてそのためにも,コマンドライン引数による実験条件の実行時指定をできるようにしておくのは大切と言えます.

git を使って再現性を確保

実験の実行から可視化まで全てをスクリプト化すると,git の有用性が顕著になります. git を使ってきれいにブランチを分けて開発をしろという意味ではなく,commit してバージョン管理をするだけで十分です.

実験用のプログラムは頻繁に更新されます. そんなプログラムを使って実験をすると,実験に使ったプログラムは遠い過去へと消えていきます. git でバージョン管理をしておけば,過去の実験で使ったプログラムがそのまま復元できるようになります. これが一番の強みです. なので研究用途では,最低限実験前に commit するだけで十分です. commit メッセージだって"何月何日の実験"とだけ書いておけば十分です. これを GitHub なり GitLab なりに上げておけば,バックアップにもなって尚良いです. 昔の実験をやり直すような場合には,対応する commit まで戻ればいいです. プログラムを修正していたら動かなくなった,そういう場合には動いていた commit まで戻ればいいです. それだけの用途でも git は十分に便利です.

また実験の実行や可視化のスクリプト化を行っているなら,そのスクリプトも git で管理することができます. そうすれば対応する commit に戻ってスクリプトを実行するだけで,そのときと同じ実験ができるようになります. ここまでやれば実験データを損失した場合にも対処できるようになります. 安心感がとんでもないことになります.

データの保存媒体は卒研や修論の締切に壊れるなんてのは良く聞く話です. 間違ってデータを消してしまうこともあります. git を使って管理しておけばそういう場合への対処も可能になります. やはり未来の自分を救うことができます.

まとめ

以上が私が研究を通じてやっておいてよかったと感じた点等になります. どちらかというと何かあったときの対処がしやすい工夫,という感じの内容が多いですが,それだけ想定外の事態が多発していたということでしょう. 想定外の事態への対処を想定していると,ある程度安心して研究を進められるので,精神的にも効果的だったのかとも思います. これから研究を始める学部生は,思ったよりイレギュラーが多いぞということを気に留めておくと良いかもしれません.

発表資料と発表練習のコツ

自分が思う発表資料作成や発表練習におけるコツだったり意識しておくと良い点について書きます. とは言っても発表のコツなどは調べれば腐るほど出てくるので,特別重要だと私が思う内容や,あまり目にしない内容について言及します. また自分への戒めも込めた内容になります.

追記 (2023/03/24): "数式はうまく使えばわかりやすい"という部分を追記

スライドは読ませない

これはとても重要です. 古事記の天岩戸のあたりにも書いてあります. スライドを使った発表では当然発表者が何かを話します. この発表でメインは発表者の発言であり,スライドはその補助です. スライドにたくさん字を書いて読ませるというスライドを主とした発表ではなく,話している内容の理解を補助するためにスライドを使うというのが適切です. これがスライドを使った発表における大前提です. その結果として重要な情報を視覚的にわかりやすく伝えるようなスライドができあがります. 決して字でぎっちぎちのスライドを読みあげるわけではありません. 字は適宜色や大きさによって強調され,また図を用いた説明が行われます.

そして聴衆がスライドを見るのに意識を割かないように注意する必要があります. 聴衆がスライドに書いてある字を読んでいて話をほとんど聞いていないというのは論外とすら言えます. これでは発表者が話す必要がありません. 適当なところにスライドを貼っておけばいいのですから. そういったことを意識してスライドを作ると,発表時には必然的にスライドに書かれていない文を話すことになります. つまり話す内容を暗記,ないし台本の用意が必要になります. オンラインでの発表では台本を見ながら発表できるかもしれないですが,対面で発表しようとすると台本を見ながらの発表は少し見苦しいところがあります. 見苦しいだけでなく声が小さくなったり,台本を読むことに意識をとられたりすることで発表の質の低下にも繋がります. 発表時間の調整のためというだけでなく,この発表の台本を暗記するためにも発表練習は必要になります.

デザインを意識しろ

前述しましたがスライドはぱっと見てわかりやすい必要があります. その上で発表者が詳しく説明するというのが,わかりやすい発表へと繋がります. となると意識しなければならないのはデザインです. 決して洒落たデザインにしろだとか,オリジナリティあふれるデザインにしろとかいうわけではありません. 最低限人に伝わりやすいデザインを心掛けるべきです.

最低限のラインがどこにあるかというのは難しいところですが,まずは字の大きさでしょう. 適度に見やすい大きさにする必要があります. 小さすぎれば見にくく,大きすぎればスライドに必要な情報が収まらなくなります.

また字や図の配置も重要です. ぐっちゃぐちゃに並んでいるより,意味を持たせて整列させている方が聴衆は理解が早くなります. あとは使う色を背景の白,字の黒,強調する用の赤,少し目立たせたい時用の青や緑,くらいにして作るだけで格段に見やすいスライドになります.

人に物を伝えるためにスライドを作るなら,最低限のデザインの知識は持っておくと良いです. そういうレベルのデザインであれば,知識さえあればなんとかなります. 才能なんてものはいらないので軽くネットで調べて,見やすいスライドを心掛けでください.

字は減らせ,図を入れろ

これは皆言ってます. 古事記にも書いてあります,天地開闢のあたりに. 字が多いスライドより図を使っているスライドのほうが見やすいというだけの話です. できるだけ字ばっかりのスライドにならないようにしましょう. 上でも述べましたが文章がいっぱい書いてあるなら,発表する必要がなくなります.

まずはスライドに文を書くのをやめることから始めるだけでも変わってきます. "○○である"と書かなくても"○○"と書くだけで十分です. なにせ発表するときにちゃんと説明するはずですから.

スライドを縮小してみると,ある程度字が多すぎないか確認することができます. 適宜やってみると良いと思います.

聴衆は想像以上にアホ

ものすごく誇張した過激な表現をしましたが,これは人に物事を説明するとき全般に言えることです. もちろん本気でアホだと言っているわけではなく,それくらいの気持で丁寧な説明を心掛けるべきという話です. 当然発表する以上は"聞いて下さってありがとうございます"という気持ちを忘れてはいけません. 見下すような気持ちでいると,結構その意識が透けて聴衆に伝わってしまうものです. どんなにスライドが良くてもそれではちゃんと聞いてもらえません. しかし説明するときは過剰なくらい説明した方が親切です. というか過剰なくらい説明しないと伝わってないことの方が多いです.

基本的に発表は発表者から聴衆への1方向のコミュニケーションになります. 発表中に聴衆がどこかで理解に詰まってしまうと,その箇所の理解をしようとし,結果として話が聞かれなかったりします. これはどうしようもないです. 理解できない箇所があるとその後の内容がわからなくなったりするわけですから,ちゃんと聞こうとするなら誰だってそうします.

こういった問題に対処するには発表内容に冗長性を持たせる必要があります. 一度説明した用語だからもう説明しなくて言いだろう,という考え方では伝わりにくい発表になりかねません. 同じことを数回言う,表現を変えて同じことを説明する,そんな工夫をするだけでずっとわかりやすい発表になります. ただし,あまりにもたくさん同じことを言うようではくどくなって,これもまた話を聞いてもらえない要因になります. 客観的に発表内容を吟味し,くどく無い程度に冗長性のある発表をする必要があります. このためには発表練習を誰かに聞いてもらうのが良いでしょう.

数式はうまく使えばわかりやすい

理論の説明や損失関数等で数式を使う必要が出てくることはあるでしょう. ただし数式を使う場合はうまく使う必要があります. 簡単な数式であれば説明の補助として非常に有用です. 例えば意味が明らかな変数同士の和をとる分には聴衆も十分に理解できるでしょう. しかし式中の変数の意味が不明瞭であったり,大量に変数が存在して混乱しやすいような場合には話が変わってきます. 他にも式の意味が明快でない場合も同様です. このような場合には聴衆はよくわからない式を見せられたことになります. その式が関連分野でほとんど常識レベルのものであれば良いですが,そうでない場合はしっかりと説明しないと理解してはもらえません. 逆に数式の意味するところを正しく十分に伝えきれている場合は,その後の説明が非常に楽になる場合もあります. もしある程度難しい数式を見せる場合には,かなり多めに説明する尺を取った方がいいと思います.

以上のことから基本的にスライドに数式は使わない方針にするのが良いと思います. ただし手法の説明にどうしても必要であるような場合には,説明に十分な時間を割きつつ記載したりすると良いでしょう. また聴衆が一目で理解できるような場合は図と同じように使って問題無いでしょう.

音声合成ソフトが便利

ある程度長い発表になると発表練習や時間の調整ですら手間になります. 20分の発表では時間の調整のために最低でも20分かかります. 1度発表してみて短いからもう1回やってみる. これだけで40分必要です. そんなときに音声合成ソフトウェアを使うと時間の節約と発表の質の向上に繋がります.

音声合成ソフトでは入力されたテキストから音声を生成します. このとき話速,すなわち話す速さが設定できることが多いです. また生成される音声は,当然音声ファイルになります. 発表内容を全て入力し,聞きやすい話速に設定して生成された音声データの長さ,つまり時間を見れば発表しなくてもどれだけ発表に時間がかかるかを知ることができます. これで発表の時間調整に必要な時間を節約できます.

さらに話速を適切に設定すれば,早口になってわかりにくい発表になることを予防できます. 台本とスライドができたら,あとは生成した音声を聞きながら発表練習をするだけで,聞きやすい速さで話しながら時間ぴったりで発表できるようになります. これクッソ便利です. 特に私は早口になりがちなので非常に助かりました. 全人類使った方が良いと思います. なんならこの記事はこれを書きたくて書いたまであります.

さてこの発表練習用に使うと便利と述べた音声合成ソフトですが,2023年現在恐しいほど大量にあります. 少し前までは無料で使えるものは少なかったものの,機械学習の発展によって無料のソフトが大量に出てきています. 最古参と言える,ゆっくり音声等として有名な AquesTalk,機械学習によって近年現れた VOICEVOX や COEIROINK,CoeFont,TALQu 等です. なんとこれらは無料のソフトなので,ダウンロードなりアカウント登録なりすればすぐに使えるようになります. 有料なものであれば VOICEROID,A.I.VOICE,ガイノイド Talk,CeVIO Creative Studio,CeVIO AI 等もっと選択肢は増えてきます.

こんなにたくさんあるわけですが,金に糸目をつけないというのであれば CeVIO Creative Studio か CeVIO AI を強く勧めます. この2つは UI がほとんど同じで,CeVIO Creative Studio は 7,000 円程度,CeVIO AI は 17,000 円程度かかります(セールとかしていればもう少し安くはなるし,ちょっと売り方が特殊なのでもし買うなら気をつけてください). 発表練習のためだけにしてはかなり高いですが,この2つは時間の調整が異常なまでに楽です. CeVIO AI の画面は下図のようになっていて,各セリフの終了時間がわかるようになっています(文面をぼかしたら画面中ぼかしになっちゃった…)

CeVIO AI の画面例.画面中央に縦に並んでいるのが各セリフの終了時間
設定を変えれば各セリフの時間もわかるようにできます. おかげでスライドごとの時間すら測れちゃいます. 時間調整が恐しく楽になります.

CeVIO Creative Studio なら30日間限定の無料体験版があるので,それを使えば1度の発表の準備くらいは十分できるかもしれません.

お金をかけたくないというのであれば A.I.VOICE に無料で使えるものがあるので,それを使うのが良いと思います. 栗田まろんというキャラクタの声に限り A.I.VOICE を無料で使えます. 開発元の株式会社エーアイと,声の元となったニコニコ代表の栗田穣崇氏に絶大の感謝を持って使わせてもらいましょう. A.I.VOICE であれば音声の書き出しを行わずとも再生時間が確認できます. A.I.VOICE の画面は下図のようになっていて,入力した内容全体の再生時間を確認することができます.

A.I.VOICE の画面例.
これでも十分発表練習に使うという用途として十分便利だと思います.

これら以外のソフトは(私が知る範囲では)再生時間を確認するためには音声を書き出す必要があるので手間がかかります. とはいっても話しながら発表練習する場合と比べて,合成された音声で台本の調整をする方が早口になりにくく便利ではあると思います.

その他より質を上げるには

以下はそんなに気にする必要が無いけど,気にする余裕があるなら気にしておくと良いだろう,そんな点になります.

色覚障害者を意識する

カラーユニバーサルデザインというやつです. 別にこんなの気にしなくても,色覚障害者以外からしてみれば関係のないものになります. ただ気にしておくと自己肯定感が上がるので,やってみてもいいかもしれません.

黒は濃すぎないほうがいい

字に使う黒は 0x000000 でなく 0x333333 のような濃い灰色くらいにしておくと良いというものです. 理由はよく知らないですが,このほうが良いらしいです. なんか見た目がそれっぽくなるので自己肯定感が上がります.

まとめ

思いつく限りでコツのようなものを書いてきましたが,個人的に言いたいのは音声合成ソフトは便利だぞという話です. 他の話は適当にググれば出てくるし,先輩なり指導教員なりが教えてくれることだと思います. この記事を通して伝えたいのは,(状況に応じて)使えるものは使えということかなと思います.

我が研究室の後輩へ向けた遺言

これはなに

今年度で修士課程を修了し,学部時からお世話になってきた研究室から離れることとなりました. で,問題なのは私しか持っていない知識や技術があったり,研究で培ったノウハウ等を共有する仕組みがなかったりするという点です. いかんせん私が変わり者(学部時代は Archlinux を普段使いし,隙あらば他大学の数学の講義資料を読み漁る情報工学部生)なせいで,普通の学生との知識の隔りがすごいことになっています. 私が常識と思ってたことは常識じゃなく,他の人が常識と思ってることを私が知らないとか結構ざらにあります. 結果として今年度で研究室で Linux 関連の知識保有者が1人いなくなっちゃったり,数学わかる勢が消えたりすることになります.

ということで,とりあえず自分のブログを使って後輩へ向けた遺言でも残そうかなというのがこの記事です(このブログの存在を全員に伝え損ねたので,私のホームディレクトリ見た人しかここ辿り着けなさそうですけど). 基本的にはこの記事からいろんなページに飛んでくれという意図で立ち上げられたページです.

2023年3月のうちはちょくちょく更新が入ります. それ以降も気が向けば更新されます.

はじめに

README は読んでくれ. そこには"私を読んで"って書いてあるんだから,まず読んでくれ. 研究室の私のホームディレクトリを始めとした各ディレクトリ下に置いてあるはずだから. 他の先輩にも README を置いてくださっている方もいて,どのディレクトリにどんなファイルがあるか書いてくださってます. ちゃんと読もうね.

本ブログの各ページへのリンク

このブログ内にある記事へのリンクです. ある程度我が研究室の後輩に有用そうな物を並べておきます.

その他参考

このブログ以外で参考になったりするんじゃない?っていう他所様のページへのリンクです.

自分流・英語論文を読むコツ

修士の研究を通じてそれなりに英語論文を読んできた私ですが,さすがに学部から修士にかけて論文を読んでいるとある程度ノウハウのようなものがたまってきたかなと感じています. というかニューラルネットワーク関係の研究だと日本語の論文なんてほとんど見ないので,実質的に英語論文を読む選択肢しかないです. おかげで日常会話で使う英単語は知らないのに,専門用語はやたら知ってる変なやつになりました.

また最近知人が英語論文を読むことになったものの,どう読んでいいかわからないということを言っていました. たしかになにも知らずに読み始めるのは大変だろうと思うと同時に,英語クソザコマンである私が培った読み方のコツのようなものも,メモしておけば誰かの役にはたつだろうとも思いました. そこでこの記事では,私が培った英語論文の読み方のコツ?のようなものを書き連ねていきます.

内容の方向性

この記事では,1つの論文をどうやって読めばいいかという点にのみ言及します. 研究する課程で論文を読むなら,その分野の動向などを良く知る必要があるわけですが,私はその手の調査がアホみたいに苦手なので言及しません. 言及してマサカリを投げられたくないので.

どちらかというと,始めて英語論文を読むけど英語が苦手だという人が,どういう点に気をつけて読むと良いか,というような内容になります. ザ・入門向けという感じです.

タイトルはちゃんと読め

第一にこれです. タイトルはちゃんと訳して意味を頭に入れておきましょう. 論文を読みながらタイトルの意味を考えるようにすると理解が深まります. なんなら理解の助けになったりもします.

論文のタイトルは著者らが自分の研究を短かく,そして適当に要約したものです. それをよく読まずに本文に目を通すのは愚の骨頂と言えるでしょう. 私はせっかちなので最初の頃はふわっと読んで,本文を読むときはほとんど頭から抜けおちていましたが,あとからタイトルを見て理解が深まったことが多々ありました. 当時ちゃんとタイトルを頭に入れつつ本文を読んでいたならば,無駄に悩む箇所が減って,結果的にもうちょっと早く論文を読めていたと思います.

Abstract や Conclusion を先に読むとわかりやすい

これはタイトルを読めという話と近い内容です. 論文にはほぼ必ず Conclusion,もしくはそれに類する節や章があります. 論文内のどこにあるかは分野によってまちまちですが Conclusion の名の通り,論文のまとめが書かれています. この節には,著者らがどういう背景の下でどういう実験を行い,どんな結果が得られたのかが要約されています. 極端な話論文の全部と言っていいです. この Conclusion について補足しているのが他の節や章だとすら言っていいかもしれません. ここを読んでから,どんな結果だったんだろうと実験の章を見にいってもいいし,提案手法のアプローチは具体的にどうなっているんだろうと対応する章を見にいってもいいです.

そして全体像が掴めていれば,慣れない英文もいくらか,というよりかなり読みやすくなります. 人間,文章を読むときは文脈が大切です. 日本語を読むときだって,なんの話をしているかわからずに読むのと,これからどんな話がくるか予測できている状態で読むのとでは理解のしやすさが段違いです. 日本語ですらそうなら,慣れない英語では尚重要です. そんな全体像を掴むという作業が Conclusion を読めば一発でできるというわけです. 読まない手はないでしょう.

さらにほとんどの論文には Abstract というものがあります. これは Conclusion をもっと要約したようなものだと思えばいいでしょう. Conclusion と同じ理由で Abstract を読むことは非常に重要です. さらに Abstract は Conclusion より短かいことがほとんどです. つまり英語苦手ニキネキからすれば,読みやすい上に全体の流れを掴めるという意味で非常に価値があります.

というか論文を探すときは普通,Abstract を読むと思うので書くだけ無駄かもしれない内容です. ただ教員から読めと渡された論文なんかは,つい Abstract を読まないなんてこともあるかもしれません. そういうときにも焦らず Abstract を読むことで,結果的に無駄に悩まずに論文読解を進められることでしょう.

図表のキャプションは結構意味がある

私には以外だったのですが,論文内にある図表のキャプションはかなり重要です. 昔レポート課題などで図表を書いたとき,キャプションは"○○の図"くらいしか書いてませんでした. ですがちゃんとした論文のキャプションは違います. その図表の見方くらいは書いてあります. 見ればその図表からある程度実験内容を推測したり,軽く考察をできたりもします. 私のような底辺学生が書くレポートとは話が違うわけです.

上でも述べましたが,文章の全体像を掴むというのは,素早くかつ無駄に悩まずに論文を読む上で非常に重要になります. 図表のキャプションは,文の全体像を掴むことに一役かってくれます. スライドを作るときにも意識することでしょうが,文を読むより視覚的にわかる図の方がぱっと理解できたりすることが多いです( こんな文だらけの記事を書いているやつが言うことじゃないですが…). その意味でも図表のキャプションを読み,直感的に内容を理解しておくことでその実験や手法の理解の助けとなることが多いです.

私は無駄にせっかちだったのと自分がキャプションをちゃんと書いていなかった経験から,最初はキャプションを軽視していましたが,後でキャプションを読んで"なんだそういうことか"となったり,"この理解であってたんだ"となったことはたくさんありました. 英語を読むのが面倒だなんて理由で,キャプションを読まずに飛ばすにはあまりに勿体ない,というのが現在の私の考えです.

わからない箇所は無理せず飛ばせ

そのままです. わからない箇所を無理に読む必要はありません. 英語の論文を読んでいて,特定の文や段落の意味を理解できない場合は大きく2つの場合に分類できます. 1つは英文の訳し方に問題がある場合です. これは翻訳にでもかければ簡単に解消されます. 1つも翻訳ツールでなく,複数の翻訳ツールを使うことでこの問題はより発生しにくくなります(これはアンサンブル学習と近い理屈です).

問題はもう1つ,そもそも意味を理解できる状態じゃないという場合です. 論文を読むためには,どうやったってある程度高度な知識を要求されます. 読者の持つ前提知識が足りなければ,どんなに正しく訳したところで理解できるわけがありません. そんな状況で無理に理解しようとそこで立ちどまるのは時間の無駄でしかないでしょう. しかしその文が重要な箇所であったりすると,理解を諦めたまま放置するわけにもいかなくなります. ただその箇所の理解は1度全体を読んだ後でも十分で,最初に完全に理解する必要はありません. というかだいたい数回読み直すはめになります. ある程度前提知識があれば,全体を軽く読んでからわからなかった箇所を読み直すと簡単に意味がわかったりします. 無駄に同じところで詰まらずに,とりあえず先に進んでしまうというのが効率良く読みすすめるコツになります.

ただそもそもその論文を読むための前提知識が足りない場合があります. そういう場合はその論文だけでなんとかするのは無理です. 人に聞くなり他の文献を漁るなりして前提知識を付けるしかありません. この場合,引用している参考文献を漁ってみると欲しい情報が書いてあったりするので,そこは意識しておくといいかもしれません.

論文は読みながらまとめておこう

これは読むコツとは少し違うかもしれないですが,書いておきます. 論文を読みながら自分で要約をしておくことで理解が深まり,かつ後の自分のためにもなります.

授業とかでも言えることですが,自分で要約をするのとただ聞いたり読んだりするのでは理解度が全く違います. 雑に要約するだけでも記憶に残りやすくなるので,やらないよりよっぽど良いなというのが個人的な感想です. ただしっかりと要約をしようとすると,自分が理解できていない箇所が浮き彫りになったりするのでより理解が深まります.

また研究をしていれば学部,修士に関わらず論文を書くことになるでしょう. そしてよほど先進的な研究でもなければ,論文には先行研究を書くことになります. そのときに自分でまとめていれば,そのコピペで最低限体裁の整った先行研究の章が完成します. あとはちょっと整形するだけです. これは後の自分を大いに助けてくれることになるでしょう. 実際私は非常に助けられました. また,まとめるという行為を日常的に行うことで,文章を書くのに慣れることができるというのも利点と言えるでしょう. TeX をあまり使っていなくて,論文では TeX を使う必要があるような場合は TeX の練習にもなります. そんな感じで論文を読みながらその手法等についてまとめるのは,理解を助けるという意味のみならず,未来の自分のためにも役に立ちます.

まとめ

これらが私が学部から修士での研究生活で身についた,論文を読む上で気にしている点です. ほかにもあるかもしれないですがまず思いつくのはこのへんです. 私のように英語が苦手だったり,無駄にせっかちだったりする人はこの記事の内容を意識して読むと,ちょっとは楽に読めるんじゃないでしょうか. そんな人の助けになれる記事になっていればうれしいです.

そして自分なりに"こうやると読みやすい!"ということに気づいたら即共有しましょう! 少なくとも1人くらいの助けにはなると思うし,なんなら私もそういう知識を知りたい…! みんな軽率に知識を共有しろ…

GAN の損失関数と二値交差エントロピー

概要

先日,GAN の学習時に妙な実装を目にしました. 実際にその実装が一般的なのかは知らないですが,導出過程が謎であったため自分なりに行間を埋めるという作業を行いました. これで正しい!と言いきれる結果にはなっていませんが,とりあえずのメモという形で記録に残したく思い,この記事を書くに至りました.

以下行うのは,GAN の学習を二値交差エントロピーの最小化のみによって実装する場合の正当性についての考察になります. また,GAN について私自身が厳密に理解できているわけではないため,GAN の説明は省略します (検索するなり arXiv を読むなりでなんとでも調べられますし...).

GAN の損失関数の変型

ほとんどの場合 GAN は画像の生成に利用されているため,Discriminator の入力と Generator の出力は画像として考えます. これは本質的に問題でなく,呼称の上で便利だという理由で仮定しています.

まずいくつか必要な変数等を定義します. 学習に使う画像のデータセットSとすることとし,このSから定まる

\begin{aligned}
P_\mathrm{data}(s) = \frac{|s \cap S|}{|S|}
\end{aligned}

を学習時に考える経験分布とします(経験分布ってこういうことだと解釈してるんですがあってるんですかねぇ...分布だから部分集合を始域とする関数だと思うんですけどねぇ...). また Generator に入力されるノイズの事前分布を

\begin{aligned}
P_z
\end{aligned}

とおきます. そして画像 xを入力としたときの Discriminator の出力を

\begin{aligned}
x \in \text{画像の集合},\quad D(x) \in (0, 1)
\end{aligned}

とし,ノイズ zを入力としたときの Generator の出力を

\begin{aligned}
G(z) \in \text{画像の集合}
\end{aligned}

とします. ここで Discriminator の出力の意味は D(x) = 1のとき xは本物であり, D(x) = 0のとき xは偽物であるとしています.

これらの定義の下で確率変数 X,\; Z

\begin{aligned}
X \sim P_\mathrm{data},\quad Z \sim P_z
\end{aligned}

とすると GAN の学習は

\begin{aligned}
\min_G \max_D \left\{ \mathbb{E}\left[ \log D(X) \right] + \mathbb{E}\left[ \log (1 - D(G(Z))) \right] \right\}
\end{aligned}

と表現することができます. この式の意味は検索すればいくらでも出てきますし,論文だってあるので気にしないものとします.

実際に学習する際には,いくつかのデータによって Discriminator を少し学習し,次に Generator を少し学習するというような手順を収束するまで繰り返すという方法が一般的に用いられます. ここで,まず Discriminator の学習に注目し式を考えると,Generator である Gについての最小化は無視することになるため

\begin{aligned}
&\max_D \left\{ \mathbb{E}\left[ \log D(X) \right] + \mathbb{E}\left[ \log (1 - D(G(Z))) \right] \right\} \\
= &\min_D \left\{ \mathbb{E}\left[ - \log D(X) \right] + \mathbb{E}\left[ - \log (1 - D(G(Z))) \right] \right\}
\end{aligned}

と変形でき,単純な最小化として表されます. 同様に Generator の学習は,式中に Gが表れる項が 1つしかないことを考えると

\begin{aligned}
\min_G \mathbb{E}\left[ \log (1 - D(G(Z))) \right]
\end{aligned}

と変形でき,Discriminator と同様に単純な最小化として表されます. ここで少し無理やりではありますが

\begin{aligned}
&\min_G \mathbb{E}\left[ \log (1 - D(G(Z))) \right] \\
= &\min_G \mathbb{E}\left[ - \log D(G(Z)) \right]
\end{aligned}

が成立します. どちらも D(G(Z)) = 0のときが最小となるため,最小値という意味では同値です.

以上より Discriminator の学習は

\begin{aligned}
\min_D \left\{ \mathbb{E}\left[ - \log D(X) \right] + \mathbb{E}\left[ - \log (1 - D(G(Z))) \right] \right\}
\end{aligned}

と表現可能であり,Generator の学習は

\begin{aligned}
\min_G \mathbb{E}\left[ - \log D(G(Z)) \right]
\end{aligned}

と表現できることがわかります. いずれも特定の式の最小化であるため

\begin{aligned}
&L_\mathrm{D} = \mathbb{E}\left[ - \log D(X) \right] + \mathbb{E}\left[ - \log (1 - D(G(Z))) \right] \\
&L_\mathrm{G} = \mathbb{E}\left[ - \log D(G(Z)) \right]
\end{aligned}

とすれば L_ Dは Discriminator の損失関数, L_ Gは Generator の損失関数と解釈できます. 特に Discriminator の損失関数と解釈した L_ Dは 2 つの項から構成され,新たに

\begin{aligned}
L_\mathrm{real} = \mathbb{E}\left[ - \log D(X) \right] \\
L_\mathrm{fake} = \mathbb{E}\left[ - \log (1 - D(G(Z))) \right]
\end{aligned}

とすれば

\begin{aligned}
L_\mathrm{D} = L_\mathrm{real} +L_\mathrm{fake}
\end{aligned}

であり, L_ \mathrm{real}は Discriminator が本物の画像を正しく識別できるかを示す項, L_ \mathrm{fake}は Discriminator が偽物の画像を正しく識別できるかを示す項であると言えます.

GAN の損失関数と二値交差クロスエントロピー

以上の議論から

\begin{gathered}
L_\mathrm{real} = \mathbb{E}\left[ - \log D(X) \right], \quad
L_\mathrm{fake} = \mathbb{E}\left[ - \log (1 - D(G(Z))) \right], \\
L_\mathrm{miss} = \mathbb{E}\left[ - \log D(G(Z)) \right]
\end{gathered}

とすると,Discriminator の損失関数 L_\mathrm{D}と Generator の損失関数 L_\mathrm{G}は,それぞれ

\begin{aligned}
L_\mathrm{D} = L_\mathrm{real} +L_\mathrm{fake}, \quad
L_\mathrm{G} = L_\mathrm{miss}
\end{aligned}

と表すことができます.

ここで二値交差エントロピー

\begin{aligned}
\mathrm{CE}(q, x) = - q \log x - (1 - q) \log (1 - x)
\end{aligned}

とすると,

\begin{aligned}
L_\mathrm{real} = \mathbb{E}\left[ \mathrm{CE}(1, D(X)) \right], \\
L_\mathrm{fake} = \mathbb{E}\left[ \mathrm{CE}(0, D(G(Z))) \right], \\
L_\mathrm{miss} = \mathbb{E}\left[ \mathrm{CE}(1, D(G(Z))) \right]
\end{aligned}

が明かに成り立ちます. よって GAN の損失関数は二値クロスエントロピーによって表現することができるということを示せました.

まとめ

本記事では GAN の学習を示す式を変形することで Discriminator と Generator の損失関数を二値クロスエントロピーによって表現しました. Discriminator については純粋に等価な変形であり,特に違和感は無く自然なものと言えます. 対して Generator の変形は最小値が等しいという意味での変形であり,通常ニューラルネットワークの学習に用いられる勾配降下法の観点からは等価な式とは言えません. その点ではこの変型に納得できているわけでは無いのですが,他にこの実装の正当性について見当がついていません. ということで,この結果をもってひとまずの決着ということにしておきます.

より納得できる考察が思いつくなりすれば,この記事の更新等を行うつもりです.

TeX のパッケージ読み込み順序

TeX でパッケージの読み込みにおいて互換性や依存の関係で読み込む順序に制限がある場合が多々存在します. 例えば hyperref パッケージの後に読み込まれるべき cleveref パッケージ等があります. この順序に従ってパッケージを読み込もうとすると,同じようなパッケージを一箇所で読み込むといったことが困難になります. そこで,パッケージの読み込みを遅延させるマクロを作成しました. 実際は TeX 言語の部分で何か作ってみたいという思いが半分くらいありますが.

定義

パッケージ A の後にパッケージ B をオプション C で読み込むという状況を考えます. この場合に以下のようにするとして設計します.

\RequirePackageAfter<A>[C]{B}

RequirePackage を別のマクロで置き換え,パッケージ A を読み込もうとした時に,B も読み込むことで実現するという方針をとります. 最終的に定義は以下のようになりました. 基本的な実装は [2] で使われているものを元にしています.

\RequirePackage{letltxmacro}
\RequirePackage{xstring}
\def\grenewRequirePackage{\grenewcommand\RequirePackage[2][]}
\def\RequirePackageAfter<#1>[#2]#3{%
  \expandafter\LetLtxMacro\csname RequirePackage@#3@After@#1\endcsname\RequirePackage%
  {%
    \edef\overrideRequirePackage##1##2{%
      \noexpand\IfStrEq{##2}{#1}{%
        \noexpand\LetLtxMacro\noexpand\RequirePackage\expandafter\noexpand\csname RequirePackage@#3@After@#1\endcsname%
        \noexpand\RequirePackage[##1]{#1}%
        \noexpand\RequirePackage[#2]{#3}%
      }%
    }%
  }%
}

参考文献

[1] macros - \global\renewcommand equivalent of \global\def - TeX - LaTeX Stack Exchange

[2] LaTeX でパッケージが衝突したときは(2) - Acetaminophen’s diary

自分用latexチートシート

論文とか自分用のまとめとかでちょくちょくlatexを使うのですが、いかんせん使いづらいところが大く感じます。 ですが数式をよく書くのでその点においてlatexが非常に優秀なのもたしかです(というかtexがあるから他のソフトウェアが表われない...?)。 そのため他にいいソフトないかなぁといいつつ結局latexを使っています。 しかし自分でいれたパッケージの使い方を忘れたりすることが多く作業効率が芳しくないので、とりあえずよく使い方忘れて困るやつ(あと忘れて困りそうなやつ)を自分用にまとめました。

自分用のテンプレートリポジトリ GitHub - muushi97/templates

過去にいろんなサイトを巡って構築された自分なりのまとめなので参考にしたサイトとか忘れちゃってる部分が多々あります。

(使用例とかをしっかり書くべきな気もしましたが、それを言ってるといつまでも書き終わらないと思うのでとりあえず公開します。適宜更新していくつもりです。)

パッケージ

here

figure環境やteble環境の位置指定にHを指定することで環境を書いた位置に強制的に画像や表を表示できるようになるパッケージ。

url

\url{http://example.com}といった形でURLを書くことができるようになるパッケージ。

listings, jlistings

ソースコードを埋めこめるようになるパッケージ。jlistingsをいれることで日本語にも対応できる。 texファイル内に直接ソースコードを書く場合とファイルからソースコードを読み込む場合に対応しています。

texファイル内に直接ソースコードを書く場合

\begin{lstlisting}[caption=キャプション,label=ラベル]
   ソースコード
\end{lstlisting}

外部ファイルからソースコードを読み込む場合

\lstinputlisting[caption=キャプション,label=ラベル]{ファイル名}

listingの設定

\lstsetコマンドをプリアンブルに書くことで設定を変更できる。(要追記)

otf

パッケージを読み込むことで otf フォントを使用可能にする。

fontenc

フォントエンコーディングを変更する。 例えばオプションにT1を設定すれば、フォントエンコーディングをT1に変更できる。

lmodern

欧文フォントのデフォルトを Latin Modern に変える。

subcaption

同一のfigure環境内に複数の図を表示する場合に各図に個別でキャプションをつけられるようにする。 パッケージを読み込むときに他のパッケージとの競合が発生するため以下のように一度別変数に退避させて読み込むと競合を避けられる。

\makeatletter
\let\MYcaption\@makecaption
\makeatother
\usepackage{subcaption}
\captionsetup{compatibility=false}
\makeatletter
\let\@makecaption\MYcaption
\makeatother

amsmath, amssymb

AMSが開発した数式用のパッケージ。

diffcoef

微分が楽に書けるようになるパッケージ。 以下の記事で知りました。 例とかも乗ってます。

diffcoeff パッケージのすゝめ - Qiita

newtxmath, newtxtext

フォントの読み込む。

bm

太字化の\bmコマンドを読み込む。

ascmac

文章を角が丸い四角い枠で囲えるscreen環境他が使用可能になるパッケージ。

hyperref

ハイパーリンク付きのpdfを出力可能になるパッケージ。

  • \href{url}{text}でurlをリンク先としてtextをリンク文字列とする。
  • \hypertarget{name}{text}でnameを使ってtextにリンクを貼れるようにする。
  • \hyperlink{name}{text}でnameと定義されたリンク先にtextを関連付ける。

pxjahyper

hyperref使用時に日本語のタイトル等が文字化けが発生する場合があり、それの防止用。

cleveref

相互参照の時に参照先の環境(図、表、数式等)にあわせて参照をできるようになるパッケージ。 ラベルは通常通りに設定すれば良く、参照用のコマンドが提供される。 \refコマンドでなく\crefコマンドを用いると 図に対して設定されたfig:hogeラベルを参照しようと以下のように参照するとする。

\cref{fig:hoge}

すると

fig.1

のように表示される。

プリアンブルに

\crefname{環境名}{単数形}{複数形}

と書くことでどのように表示されるかを設定できる。 例えば

\crefname{figure}{}{}

とすれば上記のfig:hogeを参照したコードは

図1

と表示される。

autonum

mathtoolsパッケージでshowonlyrefsをtrueにしたときと同じように、参照した数式のみに式番号を付けるようになるパッケージ。

mathtools

amsmathパッケージ同時使用することでより多くの数式関連の機能が利用可能になるパッケージ。

siunitx

SIに従った単位表記をサポートするパッケージ。 以下のような形で書くことでSIに従った記述で表示される。

\SI{}{単位}

マクロ

\newcommand{\fig}[5]{\begin{figure}[#4]\begin{center}\includegraphics[#5]{#1}\caption{#2}\label{fig:#3}\end{center}\end{figure}}

図をいれるときに、いちいちセンタリングとかめんどくさくて書いたマクロです。 キャプションがtest、ラベルがfig:a、横幅1cmでその場所にimages/hoge.pngを表示するときは以下のように書きます。

\fig{images/hoge.png}{test}{a}{H}{width=1cm}

\newenvironment{tab}[4]{\begin{table}[#1]\centering \caption{#2}\label{tab:#4}\begin{tabular}{#3}}{\end{tabular}\end{table}}

図と同じ理由で作ったマクロです。 キャプションがhoge、ラベルがtab:aでその場所に表示するときは以下のように書きます。

\begin{tab}{H}{hoge}{|l|l|l|}{a} \hline
  衝突パラメータ & シミュレーションで得た屈折角 & 屈折角の理論値 \\ \hline
  $0.9  \cdot  0.00464918$ & 0.000009384343336 & 0.000009437262261 \\
  $1.0  \cdot  0.00464918$ & 0.000008441030730 & 0.000008493536035 \\
  $1.25 \cdot  0.00464918$ & 0.000006743259391 & 0.000006794828828 \\
  $1.5  \cdot  0.00464918$ & 0.000005611538341 & 0.000005662357357 \\
  $2.0  \cdot  0.00464918$ & 0.000004197223722 & 0.000004246768018 \\ \hline
\end{tab}

集合

自然数、整数、有理数、実数、複素数それぞれの集合

\newcommand{\N}{\mathbb{N}}
\newcommand{\Z}{\mathbb{Z}}
\newcommand{\R}{\mathbb{R}}
\newcommand{\Q}{\mathbb{Q}}
\newcommand{\C}{\mathbb{C}}

自然数集合等を中抜き文字で表わすときに、わざわざ\mathbb{N}と書かなくても\Nと書くだけですむようになるマクロ。 自然数集合、整数集合、有理数集合、実数集合、複素数集合用を定義。

集合

\def\Set#1{\Setdef#1\Setdef}
\def\Setdef#1|#2\Setdef{\left\{#1\,\;\mathstrut\vrule\,\;#2\right\}}%

昔どこかで見かけたマクロです。 数式環境内で集合を書くときに以下のように書けば波括弧や区切りの縦棒の長さが自動で調整されて表示されます。

\Set{ x \in P | x \geq p }

数式

自分でまとめるより別の方が書かれてるQiita記事のほうがよっぽど丁寧な気がしたので、リンクだけ貼っておきます。

amsmathの数式環境まとめ - Qiita

おわりに

今後便利なものを見つけたりしたら追記していくつもりです。