The jonki

呼ばれて飛び出てじょじょじょじょーんき

pudbで機械学習開発を加速させる

皆さん,python機械学習のコードを書くときに,どのような環境で実装してますか?私は師匠もおらず,自分なりにいろいろ試していたところpudbに落ち着きました.pudbはデバッガーでpdbにUIが付いたようなものになります.pdbC++でいうgdbみたいなもんですが,まぁガッツリ使うのは辛いです.pudbは下記の画像のようにターミナル上でグラフィカルにデバッグをできます. pudbを使うとどのようなことができるのか,この記事ではgifアニメーションをもとに紹介します.

f:id:jonki:20180816043137p:plain:w380

なぜpudbが便利か,AtomVS Codeは使わないの?

動作例の説明の前に,簡単にpudbに落ち着いた理由を説明しておきます. 私の環境だと,GPUが載ったマシンが手元になく(AWS,大学,会社),リモート(SSH経由)で開発する必要がありました.マシンが物理的に近くにあるときは,そのマシン上でVS Codeを動かす分には特に不満はありませんでしたが,リモートでの開発方法がまったく分かりませんでした.どうやらリモートプロセスもアタッチできるようなのですが,ネットワーク設定等,面倒な雰囲気があり試していません. VNCリモートデスクトップではラグが気になり,AWSでは普通GUI付きのインスタンスなんて準備していない状況です. コーディングに関してはvimでいけるものの,printデバッグとか流石に辛いよね,と探して見つけたのがpudbです.逆にリモートでも開発できるデバッガがあるのか,周りの人はどうしてるのか気になってます.

インストールと設定

pip install pudbで入ります.起動は,pudb3 hoge.pyという形で,普通のpythonを実行する形で問題ありません. 初回起動時あるいはctrl-pにより,設定画面がでます.私はここではshellをipythonに,themeをmonokai-256に変えています.ここはお好みで.

カーソル移動

j, kで上下1行ずつ移動.ctrl-dctrl-uでまとまって上下移動.vimと同じですね. こちらはカーソルを動かしているだけで,プログラムは1行目から動いて(実行して)いません.

https://media.giphy.com/media/fQVgVsDClz88mV2vYU/giphy.gif

ステップ移動

nでステップオーバー,sでステップイン(その関数に入り込む),fでその関数内の最後に飛びます.また動画にはないですが,cがcontinueのcでプログラム開始になります.ブレークポイントがない限り最後まで実行しようとします.

https://media.giphy.com/media/69BXk4CVNGPADpML8d/giphy.gif

ファイルを開く

mでファイルを検索し,開くことができます.built-inでない自分で作ったプログラムなどはimportした後でないと検索に出てこないので注意.

https://media.giphy.com/media/MSR1LemjpXBSLilkyE/giphy.gif

ブレークポイント

カーソル行のあたっているところに,bブレークポイントを貼れます.cキーでcontinueなので,次のブレークポイントまで飛びます.この際,プログラム終了前であればctrl-cにより,プログラムを一旦止めることができます.中断処理は次に説明します.(ブレークポイントがない場合,終了まで実行しようとする).またブレークポイントはpudbを終了しても保存されていますので,毎回貼り直す必要はありません.shift-bで右下のブレークポイントエリアに飛べるので,不要なブレークポイントdで消せます.またはすでにブレークポイントが貼ってあるところで,再度bでも消せます.

https://media.giphy.com/media/SIufogiT8bVMQWYTq9/giphy.gif

重い処理の中断

ctrl-cにより,プログラムを一旦停止させることができます.重い処理をしているときに途中結果などを確認するときに便利です.またこの動画ではshift-sによりStackエリアにフォーカスをあて,ファイルを移動しています.カーソルキーで目的のファイルを選択してEnterすることで,該当箇所を開くことができます.

https://media.giphy.com/media/wJ4GzGFy2IgCLossRg/giphy.gif

エラー時のデバッグ

エラーが発生すると,ポストモーテムモードになります.eによりエラーを確認できます.エラー発生箇所で,該当データなどをコマンドラインエリア上で確認したりしてエラー原因を分析します.またshift-sによりスタックエリアに飛べるので,エラーの前のコード部分に飛んでエラー分析,といったこともできます.ちなみにエリアからフォーカスを外すにはctrl-xで外せます.ショートカットがうまく動かないときは試してみてください.

https://media.giphy.com/media/7YDcddngrycxQEC5jL/giphy.gif

現在のコンソールへの出力を確認する

oにより,現在のコンソールへの出力を確認できます.Enterで戻ります.

https://media.giphy.com/media/WNlePpYFMmwZmbvs5W/giphy.gif

shellを利用する

プログラムの途中で,データの中を細かく確認したいときがあります.そのときは画面左下のコマンドラインエリアに切り替えるか(ctrl-x)か,!でshellモードに切り替えてipythonを使うこともできます.ipythonが使えるのでかなり自由にデータを確認したり,簡単なコードを実行できて便利です.エラー発生時に最も威力を発揮するモードです.ipythonから戻るにはctrl-dで戻れます.

https://media.giphy.com/media/1wn4zRyj4zhKagSPNQ/giphy.gif

再描画する

ctrl-lで崩れた表示を再描画します.

https://media.giphy.com/media/3fiCw9o5hcqFZaFZVD/giphy.gif

ショートカットを確認する

?を押せば,ショートカットの一覧が確認できます

リスタートor終了する

qにより,デバッグのリスタートや終了のメニューが選べます.

https://media.giphy.com/media/2tKz4G7BT2kc5icINP/giphy.gif

気をつけておくこと

pudbはデバッガなので,デバッガを起動しないときよりもpythonの実行速度は落ちます(体感的に半分ぐらい).そのため,大きめの辞書を一から構成する,などの処理をpudbの処理中にやるのはあまり向いていません.辞書は予めpickleなどにしておき,それを読み込むようにすることで,時間短縮ができたりします.

まとめ

前回のログTipsに続き,今回はデバッガの話をしてみました.pudbについては多くの記事を見かけるものの,まだまだ知名度は高くない気もしていて,もっと実践開発の知見が貯まればと思い書いてみました.私も自己流なのでまだまだ知らないことが多いと思いますので,こんな使い方も便利だよ,というのがありました是非コメントください.

www.jonki.net

Fitbitのアプリ開発環境がイケてる件について

f:id:jonki:20180718122139p:plain

愛用していたPebbleが買収されたので,買収元のFitbitからVersaを買ってみました.スマートウォッチ使いたい,というよりは開発環境が楽しそうなので買ってみたというのが本音です.Pebble時代はWeb上でアプリを開発できて,とてもイケてる開発環境だったのですが,C言語でUIを書くというのがとにかく大変でした.こちらの記事はPebbleでPomodoroを書いた時のもの.
www.jonki.net

VersaではWeb上での開発環境そのままに,JavaScriptCSSSVGが使えちゃいます.またコンパニオンアプリとのメッセージングやOAuthなどのライブラリは提供されているため,必要最低限のコーディングだけで良いのが魅力的です.またWin/Macのどちらにも実機シミュレータがあり,使い勝手がよいです.トップ画面は今回作ったClockfaceです.アプリは時計アプリであるClockfaceと一般アプリに大別されるようです.

この記事では,具体的な実装方法ではなく,今回のClockfaceを作る上での準備内容,参考にしたページ,ハマったところなどを書いていきます.参考リンクに貼った公式ページがよく出来ていて基本的にコピペで作れます.またコードは下記に公開しています.
https://github.com/jojonki/fitbit-image-clock

環境セットアップ

Getting Startedに従って,Fitbitのアカウントセットアップ及び,Fitbit OS Simulatorをインストールして下さい.開発は,Fitbit Studio上で行います.プロジェクトのテンプレートとしていくつか用意されているので,最初はそれを眺めるのが良いと思います.

  • fitbit studio

いわゆるIDEです.ブラウザベースなのですぐに使えます.ここからシミュレータや実機にアプリをインストールしたり,公開用のアプリバイナリを生成できたりします.またログの出力もここで確認できます.
f:id:jonki:20180718124554p:plain

  • fitbit Simulator

見たまんまシミュレータです.物理ボタンや画面タッチをマウスで行うエミュレートもできます.
f:id:jonki:20180718122815p:plain

プロジェクト構成

構成はとても簡易なものでわかりやすいです.app/上には実機上で動くjsが配置されます.companion/上にはスマホのコンパニオンアプリで動くjsが配置されます.companionアプリ上では外部リソース(http上の画像とか)を取得してもらったりします.resources/には画像やレイアウト情報を配置します.index.guiはいわゆるindex.html的なものでsvgによるDOMを配置します.cssはその名の通りスタイルシートです.
f:id:jonki:20180718123058p:plain:w300

実機インストール

実機にインストールする場合には,PhonesとDevicesの中からシミュレータでなく,それぞれスマホ(私の場合Sony F5321)及び実機(Versa)を選択する必要があります.コンパニオンアプリからVersaを選択し,[開発者向けメニュー]->[開発者用ブリッジ接続]をオンにします.次にVersa本体のメニューから[Settings]->[Developer Bridge]をタッチし,Connected to ..となるようにします.たぶんPC/Macスマホと同じWiFiにいる必要があります.接続できたらfitbit studio上のRunボタンを押すことでインストールできます.(下記の画像はシミュレータを選択しているものなので注意)
うまくつながらないときはfitbit studio上でプロジェクトを開き直したり,コンパニオンアプリのブリッジ接続トグルを入れ直したりして下さい.
f:id:jonki:20180718123613p:plain:w300

アプリの公開

Fitbit Gallery App Managerでアプリプロファイルを作ります.fitbit studio上でDownloadのメニューからpublishを選択すると.fbaファイルがダウンロードできるので,それをアプリ公開ページに添付することで公開できます.ただ実際にはレビューリクエストを経てからなので,ストアに並ぶまでには時間がかかります(私もいま申請中).アプリインストール用のリンクが生成されるので,それをモバイルから開くことで,限定公開によるインストールも行うことができます.そのため一般公開せずに限定的なリリース(テストとか)もできます.

ハマりどころ

  • テキストが一部削られて表示されている

New element text larger than buffer! Consider increasing text-length to avoid truncation. とstudio上で出ていませんか?CSSでtext-lengthを指定して,文字数の上限を指定しましょう.メモリが少ないのでケチケチ削りましょう.

  • Simulator上で表示される画像が実機で表示されない

画像の解像度が大きかったり,pngが8bitじゃなかったりすると起きるかも?参考.
Solved: Clockface with images not working - Fitbit Community

  • 実機あるいはコンパニオンアプリに繋がらない

studio上でプロジェクト開き直す,PC/Macスマホが同じWiFiにいるか確認する,WiFi変えてみる.同じWiFiにいても認証が必要なケースとかだと繋がらない場合があるかも..

ここが辛い

  • CSS変更によるリアルタイムでのプレビューができず,毎回インストール(5秒ぐらい)が必要.そのためデザインをシミュレータ上で試行錯誤するのは結構面倒.
  • 実機上で画像転送すると遅い.シミュレータでは1秒かかってないような画像取得が実機上だと10秒ほどかかっていた.シミュレータは実機のパフォーマンス制約を再現しきれていない気がする.
  • コードのGit連携できない..zipでExportはできるが...
  • fitbit studioでタブが開けないので,ファイルの切り替えが面倒.

参考リンク

SDK Guidesを見ながら開発することになると思います.大体の情報はここに載っています.

  • SVG Guide, SVGCSSについて.UI周り全般.
  • Animations Guide, 非力だが簡単なアニメーションもできるっぽい.
  • File Transfer Guide, 画像の送受信のサンプル.本体からは直接は画像をダウンロードできないため,予めアップしておくかcompanion経由でアクセスする必要がある
  • Messaging Guide, 本体とコンパニオンでのWebsocketのメッセージのやり取り.天気のAPIを叩く例もある.
  • User-activity API, ユーザーアクティビティ系(歩数取得とか)
  • Power API, バッテリ,充電状態の取得

まとめ

雑多な感じですがまとめてみました.JavaScriptベースなので非常に開発が楽です.また開発環境もシミュレータのみインストールが必要ですが,基本的にはブラウザベースで敷居が非常に低いです.とても今時な開発環境なのでエンジニアの方は触ってみるととても勉強になると思います.また, fitbitを持っていなくてもシミュレータ上で開発できるので,ぜひ皆さんも何か作ってみましょう.

機械学習における実践ログTips

機械学習(深層学習)の開発では,一般的なプログラミングでの開発とは異なり,実行から結果の確認までのフィードバックまでの時間がとても長いです.機械学習初心者の私はその違いをあまり深く認識しておらず,当初はその特性の違いで困っていました.失敗をしていく上で,このやり方でログをまとめると便利だなと思うTipsが溜まったので共有したいと思います.師匠や長年機械学習をやってきたわけではないので,何を当たり前な..みたいなことを思う方もいらっしゃるかもしれませんが,あしからず.機械学習系のブログは理論解説が多く,実践的な開発ノウハウが少ない気がするので,これを機にこういうエントリが増えたらと思います.こういうやり方も便利だよ,というのがありましたら是非コメント欄にてフィードバック頂けると幸いです.

なお今回の話は,ポッドキャストのrebuild.fmでのhigeponさんの回に深く同意する内容になっています.
Rebuild: 208: Oculus Go On The Go (higepon)


ロガーの準備

pythonを前提にする場合は,私はいつも下記のような形で使ってます.ファイル名は基本的にタイムスタンプにしていて,必要に応じて実行時引数でログファイル名を指定しています.

import logging
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--log', type=str, help='Specify a logger\'s output filename')


date_label = datetime.datetime.now().strftime('%Y%m%d-%H%M%S')
log_fname = args.log if args.log is not None else 'logs/{}.log'.format(date_label)
logging.basicConfig(level=logging.DEBUG,
                    filename=log_fname,
                    format='%(asctime)s %(message)s',
                    datefmt='%Y%m%d-%H%M%S')
console = logging.StreamHandler()
console.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(message)s')
console.setFormatter(formatter)
logging.getLogger('').addHandler(console)
log = logging.getLogger(__name__)

log.info('log file: {}'.format(log_fname))
引数一覧はログに出力する

深層学習の開発ではいろいろとモデルをカスタムしていく上で,様々な実行時引数を取るようにカスタムする傾向にあると思います.そこで私は実行時には下記のようにして実行時引数(指定しなかった場合はデフォルトの引数も)をログに残しておくようにします.これにより,隠れ層の次元数を引数でしているけど何を指定したっけな..みたいなことがなくなります.

for arg in vars(args):
    log.info('Arg: {}={}'.format(arg, getattr(args, arg)))
20180630-000951 log file: logs/20180630-000951.log
20180630-000951 Arg: batch_size=32
20180630-000951 Arg: n_epochs=25
...
実行時引数を利用してメモを残しておく

実行時引数を利用してメモを取れるようにしておきます.プログラム実行には時間がかかるので,何か目的があって実行したプログラムのはずなのに,なんでこれ動かしたんだっけ..みたいなことが防げます.

parser.add_argument('--note', type=str, help='Note for this running.')
$ python train_model.py --note "OptimizerをSGDからAdamに変更してみた"
Gitのリビジョン番号をログに残す

プログラムは常に更新していると思います.そのため私はこまめにコミットをしているのですが,ログにも実行時のリビジョン番号をしっかりと残しておくと,あとあと自分の結果の再現をするときに便利です.ただ毎度コミットした状態で実行するとは限らないので,先程の"note"ハックとセットで使うと便利です.

import subprocess

rev = subprocess.check_output(['git', 'describe', '--always']).strip().decode('utf-8')
log.info('Git rev: {}'.format(rev))
Git rev: 33c55c9
実行時間は計算しておく

深層学習の実行は時間がかかるので夜中とかに回しとくことが多いです.そのためどれぐらい実行に時間がかかるかを残しておくことは,作業時間の見積もりに対して有用です.

import time

begin_time = time.time()
# ....

# プログラムの最後
log.info('Process time: {:.3f}h'.format((time.time() - begin_time)/3600))
実行完了時のログは整理した情報を出力し,Evernoteなどにコピペできるようにする.

実行完了後,実行結果を要約してレポートを出力するようにします.私はEvernoteを使っていて,この最後のレポートをEvernoteにコピペするようにしています.Evernoteなどのメモソフトにこのようなレポートをコピペしておけば,また後日このコピペ内容からログファイルを見たりするのが容易です.例えば,このパワポで進捗報告した82.476%の性能って,どの段階でで出たんだっけ?みたいなときにEvernoteで検索するとこれがヒットします.もちろんそのためにはEvernoteのノートのタイトルや,メモを残しておいた方が(自分に)親切です.

20180630-022147 -----Final Report. Running mode: 3 ------
20180630-022147 Hoge1 Acc      : 82.476%
20180630-022147 Hoge2 Acc      : 71.46%
20180630-022147 log file: logs/20180630-000951.log
20180630-022147 python train_my_model.py --mode 3 --resume ckpts/hoge1.model --note "MLPのレイヤ数を3に増やしてみる"
20180630-022147 Git rev: 9ac7188
20180630-022147 Process time: 3.5h
- MLPのレイヤ数を3に増やして,性能向上を試みる
// ここに先程のログをコピペ
ログはfilterを通して閲覧する

ログは漏れがあるといけないので基本的に冗長に出力するケースが多いと思います.そのため出力されたログから迅速に必要な情報をフィルターする必要があります.私はpecoを使っています.これはlessなどとパイプで組み合わせて,該当行を高速に表示してくれます.リンク先に動作gifがあるのでご参考に.

less logs/my_log.txt | peco

また私はログファイルがタイムスタンプになっていて探るのが面倒なので,あるディレクトリ下の最新のログファイルをtailとpecoで読み取るスクリプトを利用しています.リアルタイムログ閲覧+pecoにより,決まったログだけを眺めることができて経過を見守れます.

#!/bin/sh
ls -t logs/* | head -1 | xargs tail -f -n+1 | peco


と雑多に書いてみましたがこんな感じです.ログは面倒ですが,人の記憶は適当なものなので,たった数分前の自分がやったことも理解できなかったりします.こういったログTipsが他にも増えたらと思います.
次回は需要があれば,pudbによるリモート開発Tipsをできたらと思います.

あるサブディレクトリ内の最新のファイルを一発で開く方法

大量のログファイルとかがあるときに便利なコマンド.
こんな感じ.サブディレクトリの次に*(アスタリスク)を入れると,相対パスも含めて表示してくれるのがミソ.

ls -t subdir/* | head -1 | xargs less

pecoとか入れてるなら更にパイプでつなげると便利.

ls -t subdir/* | head -1 | xargs less | peco

正規分布間のKLダイバージェンス

皆さん大好きなVariational Auto Encoderですが,目的関数に再構築ロスとKLダイバージェンスによる正則化項を使っています.原論文のAppendix Bで,正規分布間のKLダイバージェンスの導出をしていますが,途中式が省かれていてよく分かりません.ということで勉強がてら自分でも計算してみました.

参考にしたのはこちら様のサイト.一次元の正規分布間と多変量正規分布間のKLダイバージェンスを求めています.多変量正規分布間の計算は,難しくはないですが,式が複雑なのでとりあえず一次を手計算で導出できたらへぇ,っと眺めて最後のところ見れば良いと思います.
sucrose.hatenablog.com


では,肝心のVAE論文のAppendix Bの導出をします.導出はこちらのPDFに書き出したので御覧ください.(すみません,はてなの記事で書いても良かったのですが,sharelatexで書いてしまいました)
github.com