Chainerの学習ログをSlackに流すchainer-slack-reportを作った

魚類の中でもマンボウはとても多くの数の卵を生むことで知られており、それが産卵数とイコールなのかは諸説あるものの3億個もの卵を抱えていると言われているのだそうです。

さて、Chainerで学習してるとその進捗をSlackで見られるようにしたいですよね。
そこで、ChainerのPrintReport(標準出力への学習ログレポートExtension)を拡張して同じ内容をSlackに送るExtensionをさくっと作りました。

belltailjp/chainer_slack_report

screencast

Chainerの学習ログをSlackに流そう!という発想自体は当然いくらでもあって、ネットで軽く探しただけでとても優れた先例がたくさんありますし、野良で作って自分で使っている人も少なくないと思います。

DeepLearningの学習経過をslackで受け取れるようにする – Qiita
Chainerの学習の様子をリモートで確認するExtensionを作った – のんびりしているエンジニアの日記
dtaniwaki/chainer-watchdog: Watchdog extension for Chainer

ぼく個人が自分のユースケースにおいて一番困っていた点、そしてchainer-slack-reportで解決しかった点は、いろんな学習を並行して回してその全てをSlackに流しても表示や時系列が崩れないようにしたかったことです。上のスクリーンショットを見ていただくとSlackReportが複数の学習のレポートをうまくさばいている様子がわかるかと思います。

技術的には全く高度なことはしていなくて、「(SlackReportオブジェクトのライフタイムにおいて)初回はSlackに新規メッセージを投げる」「2回め以降はそのメッセージを編集する」というふうにしているだけです。何も難しくないです。

他に実装上の違いとして、ChainerのPrintReportをそのまま継承して画面表示部分のみを乗っ取る実装にしたため、テキストの整形やシリアライズの仕組みはまるごとPrintReportがやってくれるという超シンプルな作りになっています。
何もしないでもserialize・deserializeに対応している点も地味に助かります。たとえばPrintReportではtrainerのsnapshotをロードして学習を途中から再開したとき過去の学習記録をまるごと再表示してくれますが、SlackReportもそのまま同じように動いてくれます。

使い方
% pip install chainer-slack-report

学習コードで使うには、PrintReportと同じようにtrainerに差し込むだけでOKです。もちろんPrintReportと共存できますし、複数のチャンネルに同時に投げたい場合はchannel_idを変えて別々のSlackReportを作りそれぞれをtrainerにextendすればOKです。

PrintReportの初期化時に渡せるような引数はすべてSlackReportの初期化時にも渡せます。
それに加えてSlackに投稿するために必要な情報を渡す必要があり、またSlackでの通知内容の細かな制御のためのオプションを渡すこともできます。

必須の情報として、SlackのWeb APIを叩くためのアクセストークンおよび送信先となるChannelをさすIDがあります。
これらの情報の取得・確認方法はGithubでスクリーンショット付きで解説しています。
どちらかがNoneの場合は単純に何もしないようになっています。
そのため環境変数から取ってくるなどのイディオムを採用するのがよいでしょう。

複数の学習が破綻なくSlackに表示されるのはいいけど、どれがどれかわからないと(数が多いときは)意味がありませんね。
SlackReportではデフォルトでスクリプト名とその引数(要するにargv)をレポートの頭に表示します。
Jupyterで学習を回している場合はこれでは実質無意味な情報になるので、マニュアルで指定することもできます。その場合はSlackReportの初期化時にlabelという名前付き引数に任意の文字列を指定すればOKです。

また、SlackReportのイニシャライザのfinish_mentionsという引数にユーザのアカウント名(@何々)を文字列ないし文字列のリストで渡すと、学習が終了したときにmentionを飛ばしてくれる機能もあります。学習の終了に気づきやすくなるので便利でしょう。何も指定しないと(あるいは指定されたユーザが存在しないと)、普通のレポートのみが表示されて終了します。
Slackのメッセージ長さ制限(2000文字)に引っかからない限り何人でも大丈夫です。ただしユーザグループは未対応です。

import os
from chainer_slack_report import SlackReport
...
r = SlackReport(os.environ.get("SLACK_ACCESS_TOKEN", None),
                os.environ.get("SLACK_CHANNEL_ID", None),
                ['epoch', 'main/loss', 'validation/main/loss',
                 'main/accuracy', 'validation/main/accuracy', 'elapsed_time'],
                finish_mentions="@someone")
trainer.extend(r, trigger=(1, 'epoch'))
...
trainer.run()

FAQ
  • トークンが正しいはずなのにUnauthorizedになります

    • chainer-slack-report初公開時と今で(1日差ですが)、Slack APIを叩くために必要な権限が変わりました。
    • App typeをBotとする設定をしなおしてみてください
    • ちなみにSlackに詳しい方はGithub上のinstructionに従わず通常のAppとしてpermission settingしてもOKです。

      • chat:write:bot, channels:read, users:readの権限が必要です
  • Trainerを使っていないコードでも使えますか?

    • __call__関数で表示までしているのですがこれがtrainerを受け取っているという形で依存しているので、基本的にはTrainerを使っていただけたらと思います。
断念した機能

ChainerにはPlotReportという学習曲線を随時グラフ(PNG画像)にして保存するExtensionがあります。
これもSlackReportで乗っ取ってSlackに投げるようにしてみよう!と思ったのですが、SlackのAPIには「画像を上書きアップロードする」という機能がないため、強引に実現しても「時系列が狂わない」という基本コンセプトが守れないので、断念しました。
いい見せ方ができるアイデアが何かあればよいのですが。

TODO
  • テストする
  • Chainer v5系でも確認

Chainerで書いたニューラルネットの理論計算量を推定するchainer_computational_cost

海洋の生物量の推定をするときには、実際にある空間の種ごとの個体数を1匹1匹数えるわけにはいきません。そこで、水中に残った生物の細胞のDNAすなわち環境DNAの密度を手がかりにして推計をするのだそうです(バケツ一杯の水で海洋生物の量や種類を知る)。

Chainerで書いたNNとダミー入力があるとき、そのNNのforward passの理論的な計算量・メモリ転送量を計算するchainer_computational_costを作りました。
ChainerのFunction Hookをベースにしているため、NNの定義コードに手を入れる必要は一切ありません。

Continue reading

cupyで2次元ガウス窓を生成する

8000メートルなどを超えるような超深海では、超高圧のためタンパク質の構造自体が保てなくなる(水分子が侵入し破壊される)ことから、通常の構造の延長としての生物は生息できないのだそうです。そこにいる生物は、TMAOという物質がもつタンパク質に侵入する水分子をブロックする性質を利用して生きているのだとか。したたかです。

さて、Pythonを使って2次元のガウス窓を生成したくなりました。
1次元でのscipy.signal.gaussianの多次元版にあたるものです。

具体的には、画像として描画するとこんなかんじになるような配列の生成が目的です。

Continue reading

Dashにchainerのドキュメントを登録する

魚について調べる時、(Wikipediaもいいんですが)FishBase : A Global Information System on Fishesとかは結構分量も凄くて、英語中心ですがときどき役に立ちます。

さて、Macを3年ぐらい使ってきてて恥ずかしながらわりと最近までドキュメントのインクリメンタルサーチができるDashを知らなかったんですけども、ちょっと前から使うようになりました。仕事や趣味でChainerを使うんですが、ChainerのドキュメントはDashで提供されてないので、自分でドキュメントをDash用に変換する必要があります。

とは言えChainerはSphinxでドキュメントが書かれているので、doc2dashというpipを使えば一発でできます。

なのですごい簡単なんですが、環境を新しくする度にやり方を調べてやってる気がするので、メモ。
別にchainer限定ではなくて他の何にでも当てはまる一般的な話なんですが、あえてchainer specificな話題にしてみました。

# 準備。doc2dashを入れる。
% pip install doc2dash

# Cloneしてくる
% git clone https://github.com/pfnet/chainer.git --branch v2.0.0
% cd chainer/docs

# Optional: アイコンに使う画像をダウンロードしてくる。ここではTwitterから。
% wget https://pbs.twimg.com/profile_images/606654945438203905/D7LygzpN_400x400.png -O chainer_logo.png

# ドキュメントをビルドし、dash形式に変換
% make html
% doc2dash build/html -n chainer2 -i chainer_logo.png
# % doc2dash build/html -n chainer2   #アイコンが不要な場合

これを実行すると、chainer2.docsetというディレクトリが生成されます。Sphinx等はもちろん入っている必要があるので、怒られた場合は指示に従って入れてください。
Dashをインストール済みのMacでFinderにてこのディレクトリをダブルクリックすると、検索できるドキュメントとして追加されます。

ぼくの場合手元のMacだと、2017/7/3現在でのmasterのときsphinxのビルド(上記のmake html)がこんなかんじで失敗することがあったので、Linux環境でmakeしました。また、いきなりmake htmlするとエラーで死ぬんですがmake dirhtmlしてからmake htmlすると通ることも度々ありました。ちょっと謎。

% make dirhtml
...
...
reading sources... [ 76%] reference/generated/chainer.links.StatelessLSTM
reading sources... [ 76%] reference/generated/chainer.links.VGG16Layers
reading sources... [ 76%] reference/generated/chainer.links.caffe.CaffeFunction
reading sources... [ 77%] reference/generated/chainer.links.model.vision.googlenet.prepare
reading sources... [ 77%] reference/generated/chainer.links.model.vision.resnet.ResNetLayers
reading sources... [ 77%] reference/generated/chainer.links.model.vision.resnet.prepare
reading sources... [ 77%] reference/generated/chainer.links.model.vision.vgg.prepare
reading sources... [ 78%] reference/generated/chainer.optimizers.AdaDelta
/Users/hoge/Work/chainer/chainer/docs/source/reference/check.rst:30: WARNING: failed to import chainer.gradient_check.check_backward
/Users/hoge/Work/chainer/chainer/docs/source/reference/check.rst:30: WARNING: failed to import chainer.gradient_check.numerical_grad
/Users/hoge/Work/chainer/chainer/docs/source/reference/check.rst:30: WARNING: toctree references unknown document 'reference/generated/chainer.gradient_check.check_backward'
/Users/hoge/Work/chainer/chainer/docs/source/reference/check.rst:30: WARNING: toctree references unknown document 'reference/generated/chainer.gradient_check.numerical_grad'
/Users/hoge/Work/chainer/chainer/docs/source/reference/check.rst:43: WARNING: failed to import chainer.testing.assert_allclose
/Users/hoge/Work/chainer/chainer/docs/source/reference/check.rst:43: WARNING: toctree references unknown document 'reference/generated/chainer.testing.assert_allclose'
/Users/hoge/Work/chainer/chainer/docs/source/reference/check.rst:53: WARNING: failed to import chainer.testing.unary_math_function_unittest
/Users/hoge/Work/chainer/chainer/docs/source/reference/check.rst:53: WARNING: toctree references unknown document 'reference/generated/chainer.testing.unary_math_function_unittest'
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.Chain.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.ChainList.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.Link.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/Work/chainer/chainer/docs/source/reference/core/optimizer.rst:4: WARNING: failed to import chainer.Hyperparameter
/Users/hoge/Work/chainer/chainer/docs/source/reference/core/optimizer.rst:4: WARNING: toctree references unknown document 'reference/core/generated/chainer.Hyperparameter'
WARNING: /Users/hoge/Work/chainer/chainer/docs/source/reference/datasets.rst:57: (WARNING/2) autodoc: failed to import class 'ConcatenatedDataset' from module 'chainer.datasets'; the following exception was raised:
Traceback (most recent call last):
  File "/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/sphinx/util/inspect.py", line 169, in safe_getattr
    return getattr(obj, name, *defargs)
AttributeError: module 'chainer.datasets' has no attribute 'ConcatenatedDataset'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/sphinx/ext/autodoc.py", line 664, in import_object
    obj = self.get_attr(obj, part)
  File "/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/sphinx/ext/autodoc.py", line 554, in get_attr
    return safe_getattr(obj, name, *defargs)
  File "/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/sphinx/util/inspect.py", line 185, in safe_getattr
    raise AttributeError(name)
AttributeError: ConcatenatedDataset
/Users/hoge/Work/chainer/chainer/docs/source/reference/functions.rst:30: WARNING: failed to import chainer.functions.tree_lstm
/Users/hoge/Work/chainer/chainer/docs/source/reference/functions.rst:30: WARNING: toctree references unknown document 'reference/generated/chainer.functions.tree_lstm'
/Users/hoge/Work/chainer/chainer/docs/source/reference/functions.rst:216: WARNING: failed to import chainer.functions.layer_normalization
/Users/hoge/Work/chainer/chainer/docs/source/reference/functions.rst:216: WARNING: toctree references unknown document 'reference/generated/chainer.functions.layer_normalization'
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.BatchNormalization.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.Bias.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.Bilinear.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.BinaryHierarchicalSoftmax.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.BlackOut.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.CRF1d.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.Classifier.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.Convolution2D.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.ConvolutionND.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.Deconvolution2D.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.DeconvolutionND.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.DepthwiseConvolution2D.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.DilatedConvolution2D.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.EmbedID.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.GRU.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.GoogLeNet.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.Highway.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.Inception.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.InceptionBN.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.LSTM.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.LayerNormalization.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.Linear.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.MLPConvolution2D.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.Maxout.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.NStepBiGRU.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.NStepBiLSTM.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.NStepBiRNNReLU.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.NStepBiRNNTanh.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.NStepGRU.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.NStepLSTM.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.NStepRNNReLU.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.NStepRNNTanh.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.NegativeSampling.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.PReLU.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.ResNet101Layers.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.ResNet152Layers.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.ResNet50Layers.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.Scale.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.SimplifiedDropconnect.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.StatefulGRU.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.StatefulPeepholeLSTM.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.StatelessLSTM.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.VGG16Layers.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.caffe.CaffeFunction.init_scope:19: WARNING: Unexpected indentation.
/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/link.py:docstring of chainer.links.model.vision.resnet.ResNetLayers.init_scope:19: WARNING: Unexpected indentation.
Exception occurred:
  File "/Users/hoge/.pyenv/versions/3.6.1/lib/python3.6/site-packages/chainer/optimizer.py", line 598, in __get__
    return getattr(obj.hyperparam, self._attr_name)
AttributeError: 'NoneType' object has no attribute 'hyperparam'
The full traceback has been saved in /var/folders/y1/tp8l6j7n67bgbz8tpm0_05b40000gn/T/sphinx-err-6hlmrj53.log, if you want to report the issue to the developers.
Please also report this if it was a user error, so that a better error message can be provided next time.
A bug report can be filed in the tracker at <https://github.com/sphinx-doc/sphinx/issues>. Thanks!
make: *** [dirhtml] Error 1

また、DashのWindows/Linux版であるZealを使っている場合、上記の手順でビルドしてできたchainer2.docsetディレクトリを、Zealのdocsetsディレクトリに入れることでZealに見えるようになります。

% mv chainer2.docset ~/.local/share/Zeal/Zeal/docsets/

(docsetsディレクトリの場所は環境によって異なる可能性があります。optionsで確認できます)

✌(‘ω’✌ )三✌(‘ω’)✌三( ✌’ω’)✌たのしい!

なお、後になってDash-User-ContributionにChainerがあることを知った…
https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/Chainer
常に自分の責任で最新に追従させたいわけじゃなければ、これで十分。メンテナのmitmulさんは同僚ですw