PythonベースのWebアプリにHeadless Chrome(Chromium)やHeadless Firefoxを組み込みSeleniumでこれらを制御しようとしたとき、細かいことにいろいろとハマってしまいました。公式サンプルあるいはQiita記事などを含め、1つの例で自分の手元の環境でちゃんと動くようなものがまとまっているものがなかったので、知見をまとめました。
HerokuあるいはDokkuのような環境、すなわちUbuntuベースでDockerベースであり、計算リソースが比較的貧弱な環境を想定していますが、開発用にMacでも確認しています。
Python
Chainerの学習ログをSlackに流すchainer-slack-reportを作った
さて、Chainerで学習してるとその進捗をSlackで見られるようにしたいですよね。
そこで、ChainerのPrintReport(標準出力への学習ログレポートExtension)を拡張して同じ内容をSlackに送るExtensionをさくっと作りました。
belltailjp/chainer_slack_report
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
Chainerで書いたNNとダミー入力があるとき、そのNNのforward passの理論的な計算量・メモリ転送量を計算するchainer_computational_costを作りました。
ChainerのFunction Hookをベースにしているため、NNの定義コードに手を入れる必要は一切ありません。
cupyで2次元ガウス窓を生成する
さて、Pythonを使って2次元のガウス窓を生成したくなりました。
1次元でのscipy.signal.gaussianの多次元版にあたるものです。
Dashにchainerのドキュメントを登録する
さて、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
tqdmをムリヤリprocess safeっぽくするdirty hack
さて、世界70億人のPythonプログラマが手放せないのが、tqdmというプログレスバーを簡単に表示できるようにしてくれるライブラリです。
しかしながらtqdmは(当然と言えば当然ですが)複数のプロセスから1つのプログレスバーを管理することができるような設計にはなっていません。
たとえば高速化のためにmultiprocessingを用いて並列処理をしながらプログレスバーを出したいとき、tqdmはそのままでは使えないことになります。
例えば、下記のようなコードで単にtqdmのインスタンスを子プロセスに渡して中でupdateをすると…
import tqdm import multiprocessing def worker(pbar): pbar.update() ar = [i for i in range(100)] with tqdm.tqdm(ar) as pbar: jobs = [] for t in ar: job = multiprocessing.Process(target=worker, args=(pbar,)) job.start() jobs.append(job) for job in jobs: job.join()
実行結果はこんな感じ。
% python usage.py 1%|▉ | 1/100 [00:00<00:25, 3.89it/s]
こんなふうに、100%まで行ってくれません。
端的に説明すると、100個forkされた内部カウント0の状態をそれぞれ+1するのが100回走るだけ、だからです。
じゃあ内部カウントが常によろしく増えるために、内部カウントを無視して共有メモリで常に正しくカウントしてればいいよね、っていう単純な発想による汚いハックをしてみました。
pythonでlistを(なるべく)均等分割するスニペット
さて、Pythonでリストをあるk個のリストへなるべく均等に分割したい…というようなことが頻繁ではないけどたまーに欲しくなって、その都度何分かかけて書いてる(でときどきバグ作り込む)気がするのでスニペットとしてメモ。
やりたいこと。
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
…というような長さ12の配列を5分割する。
[ [0, 1], [2, 3], [4, 5, 6], [7, 8], [9, 10, 11] ]
何のことはありません。
def split_array(ar, n_group): for i_chunk in range(n_group): yield ar[i_chunk * len(ar) // n_group:(i_chunk + 1) * len(ar) // n_group]
Python2なら割り算のところは//でなく/でおk。
リストを受け取ればリストを返すし、rangeを受け取ればrangeを返します。
> [list(r) for r in split_array(range(12), 5)] [ [0, 1], [2, 3], [4, 5, 6], [7, 8], [9, 10, 11] ]
1982798万番煎じぐらいだとおもう。
第29回関東CV勉強会でSelective Search for Object Recognitionを紹介してきました
さて、仕事がらComputer Vision的な活動はぜんぜんできていなかったこの1年でしたが、年明けたぐらいから気持ち的にも余裕ができてきたので、ちょいちょいと遊んでいました。
そして先日開催された関東CV勉強会で、かなりひさしぶりに外で専門分野のお話をさせていただく機会をいただき、検出界隈では有名と思われる手法Selective Search(*1)(*2)について紹介してきたので、簡単に振り返ってみたいと思います。
勉強会情報
-
テーマ: 有名論文を読む会
- 有名だけど今までちゃんと読んだことがない、日本語情報もあまりない、そんな論文を読んでみよう
- 日時: 2015年5月30日
- 場所: 東京大学(本郷)
- イベント: 第29回 コンピュータビジョン勉強会@関東
- Tweetまとめ: 2015/05/30 CV勉強会@関東「有名論文読み会」ツイートまとめ
- 各発表者の資料等まとめ: 2015/05/30 コンピュータビジョン勉強会@関東「有名論文読み会」 – takminの書きっぱなし備忘録
ndarrayとPySideのQImageとの相互変換
さてnumpyと愉快な仲間たちにおいて画像はndarrayとして表現されていて、それを表示するのに使うmatplotlibはちょっとしたGUI機能(widgetsモジュール)をもっています。
ただこれコード的にも絵的にもクセが強くごく単純なデモ以上のことは難しいので、何かしたいときはQtのPythonラッパーであるPySideを使っています。
んで、そのPySideで画像はQImageで扱われるので、裏っかわでndarrayで処理をしているならそれをQImageとの間で相互変換できる必要があります。
そのやり方はこちら。
(参考: python – QImage to Numpy Array using PySide – Stack Overflow)
画素毎にループしてコピーするとかイケてないことしなくて済むから簡単だね!
QImageに持ってきてしまえばQPixmapにしてQLabelに突っ込んで表示するなりQPainterでお絵かきするなり、自由です。
2行N列のndarrayを一発で(1行目≦2行目)にするnumpy芸
Numpyにて、行同士の大小関係を強制したい、つまり「2行N列のndarrayがあるんだけど、各列ごとに、常に1行目の値が2行目よりも小さい値を取る必要がある」「そうでない列の値を強制的に上下交換させたい」という状況に遭遇しました。
生ループ回してひとつひとつチェックしては…とやってもいいんですが、それをPython側でやっていてはパフォーマンス上どうかなということで、Numpyのインデクス芸・スライス芸・ブロードキャスト芸あたりを駆使してやってみました。
もちろん、N行2列のndarrayの列間の大小関係を統一するのも同様に可能。
インデクシングなどの詳細の説明はここではしません。
日本語であればこのあたりがわかりやすそう(まだ見てない)。
性能やいかに?
Benchmarker.pyを使って、実行時間を比べてみました。
比較対象は、まぁ何も考えずに書くとそうなりますわな、というような雰囲気のコード(だとおもう)。
#ところで標準の時間計測モジュールtimeitが最高に嫌いなんだけどあれなんであんなふうになってしまったのかな。。。
こちらが実行結果。
proposedが本記事で紹介した黒魔術、conventionalがシンプルな方法。
## real (total = user + sys) col-proposed 3.6327 3.6200 2.8100 0.8100 row-proposed 3.6977 3.6900 2.8700 0.8200 col-conventional 49.9127 49.8600 49.7600 0.1000 row-conventional 51.3075 51.2600 51.1600 0.1000
だいたい14倍高速ですね。
メモリ上では行オーダーで格納される都合上、row-*とcol-*の間で速度差が出るのかなと思いましたが、たかだか2行/2列なので、関係ない模様(σが未知なのでなんともいえませんが。。)。
Pythonの高速化の鉄則は『生Python書くな』に尽きますが、それが改めて示された形になりましたとさ。
ただこのようなコード、後から読んだり他人に読ませたりするのはとても困難なので、あまり多用しないようにしましょう。