The jonki

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

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

Touch Bar使ってないならApple Scriptで色々しようぜ

あなたのTouch Bar,ちゃんと使ってますか?私は全く使いきれず,買ったことをずっと後悔していました.しかし最近BetterTouchToolを利用することで,簡単に自分好みにカスタムできることができることを知ったので共有しておきます.今の私のTouch Barの様子はこちらです.なんかごちゃごちゃしてますね.
f:id:jonki:20180407095924p:plain


今回は,作例紹介ということで,BetterTouchToolと組み合わせて主にApple ScriptというMac OSスクリプトを動かします.BetterTouchToolは有料ソフトですが,ショートカットのアプリ毎のカスタムなどが気軽にできるので,すでに購入済みの方も多いのではないかと思います.作例は全てGithubに載せてあります.またApple ScriptはBetterTouchTool上でもテストできますが,実行がやや面倒なのでMac純正のScriptEditor(多分もともとインストールされている)を使うのがおすすめです.また今回はChromeを使っている前提のプログラムが多いですが,Safariなどでも一部変更すればできるはずです.またApple Scriptでなくても,Shell Scriptなどでも良いようです.

github.com

ButtonとWidget

BetterTouchToolの[TouchBar]タブを開くと,右下に[+TouchBar Button]と[+Widget]が見えると思います.私はこの2つを利用しています.基本的に,前者はボタンを押した時にイベントが発行するタイプで,後者は,バックグラウンドで動いており,実行結果に合わせてアイコン部にメッセージを動的表示できたりします.以下の作例では前者をButton,後者をWidgetと便宜上呼びます.Actionとして,[Run Apple Script]を選択するとApple Scriptのコードを貼れる画面があるのでそこに色々書く感じです.
f:id:jonki:20180407101224p:plain
f:id:jonki:20180407104704p:plain

作例(Button編)

選択しているテキストとその開いているタブのURLを合わせてコピーする(copy_with_url.scpt)

Chrome上でページ内のテキストを選択した状態でこのボタンを押すと,"テキスト<改行>URL"という形でクリップボードにコピーされます.選択テキストをCmd+cでコピーした後に(選択テキストには直接アクセスできない模様),現在のURLを取得してクリップボードに連結して上書きする形で実現します.delayを少し入れないとクリップボードへの反映が間に合わないようです.

tell application "System Events"
	keystroke "c" using command down
	delay 0.2
	tell application "Google Chrome"
		set the clipboard to (the clipboard) & return & (URL of active tab of front window as text)
	end tell
end tell
選択しているテキストをGoogle翻訳にかけて,日本語か英語に翻訳する

Chrome上でテキストを選択している状態でこのボタンを押すと,そのテキストをGoogle翻訳にかけたページを開きます.先ほどのURLコピーと同様の手順で,一度Cmd+cでテキストをコピーしてから,Clipboardにアクセスしています.また日英翻訳と割り切っているので,ユニコード値で簡単に日本語から英語なのか,英語から日本語なのかを決めています.テキストができたらあとはChromeで翻訳のURLを開きます.

tell application "System Events"
	keystroke "c" using command down
	delay 0.2
	tell application "Google Chrome"
		set text_ids to id of (the clipboard)
		set is_eng to true
		repeat with tid in text_ids
			if tid ≥ 256 then
				set is_eng to false
			end if
		end repeat
		
		if is_eng then
			open location "https://translate.google.com/#en/ja/" & (the clipboard)
		else
			open location "https://translate.google.com/#ja/en/" & (the clipboard)
		end if
		
		activate
	end tell
end tell
現在の日付を出力する(insert_today_str.scpt)

そのまんまです.押すと"2018/4/6"みたいな感じでキー出力が行えます.日付のフォーマットは適宜お好みで.

tell application "System Events"
	set {year:y, month:m, day:d} to (current date)
	set customDate to y & "/" & (m as number) & "/" & d as text
	keystroke customDate
end tell
Google Play Musicの再生停止を行う(google_play_music_play_pause_toggle.scpt)

Apple ScriptではJavaScriptも実行できます.下記の例では,Google Chromeの各ウインドウ,各タブに対して,Google Play Musicを開いているタブを探し,見つかったところで,任意のJavaScriptを実行しています.再生停止ボタンがあるのでそれをクリックするJavaScriptを実行しています(結構怖いですね!).Google Play MusicはWebアプリなので,MacBookのメディア再生停止ボタンが効きませんが,これを利用することでそれを代用できます.この機能を実行する時,Chromeがフォアグラウンドである必要もないので結構便利です.

tell application "Google Chrome"
	repeat with w in windows
		set i to 1
		repeat with t in tabs of w
			if URL of t starts with "https://play.google.com" then
				execute t javascript "document.getElementById('player-bar-play-pause').click()"
				return
			end if
			set i to i + 1
		end repeat
	end repeat
end tell
キーアクションを出力する

これはApple Scriptを使わず,BetterTouchToolのみで,[Custom Keyboard Shortcut]を使っています.私の環境ではグローバルショートカットキーでToDoアプリが立ち上がるのですが,覚えられないので使ってる感じです.この他にもPredefined Actionにはアプリ起動など色々便利な定義済みコマンドがあるので見てみるのも面白いです.
f:id:jonki:20180407102143p:plain

作例(Widget編)

Google Play Musicで現在再生中の曲を表示する(google_play_music_show_current_track.scpt)

先ほどの音楽の再生停止の要領でJavaScriptを実行します.returnで文字列を返すとそのテキストがTouch Barに表示されます.また,Google Play Musicを開いていないのであれば,空文字を返してそもそも表示しないようにもできます.ちなみにSpotifyとかでも似たようなことはもちろんできます

tell application "Google Chrome"
	repeat with w in windows
		set i to 1
		repeat with t in tabs of w
			if URL of t starts with "https://play.google.com" then
				set song_title to execute t javascript "document.getElementById('currently-playing-title').getAttribute('title');"
				return song_title
			end if
			set i to i + 1
		end repeat
	end repeat
	return ""
end tell
特定のURLを開いた時に,メッセージを表示する(show_msg_in_specific_url.scpt)

例えば私は,TwitterFacebook見過ぎなのでこんな感じで我に返るようなメッセージを表示しています.アイコンはBetterTouchToolからpngなど設定できます.ネタです.


tell application "Google Chrome"
	set currentTabUrl to URL of active tab of first window
	if currentTabUrl contains "twitter.com" then
		return "またTwitter見てるの?"
	else
		return ""
	end if
end tell

まとめ

今回の私の作例をそのまま使う人は少ないと思いますが,何か作りたくなってきませんか?Apple Scriptは初めて触ったのですが,色々なコードは転がっているのでコピペで結構いけます.皆さんもなんか書いてTouch Bar活用しましょう.