Slide 1

Slide 1 text

データサイエンスレガシーコードに 立ち向かう Repro Tech Meetup #7 2019/04/04 @shiumachi

Slide 2

Slide 2 text

自己紹介 @shiumachi ルミノソジャパン合同会社勤務

Slide 3

Slide 3 text

会社紹介 (仕事用スライドを参照)

Slide 4

Slide 4 text

今の仕事で得た知見 今までの自分の認識: 機械学習をやるには大量にデータ集めて前処理して特徴エ ンジニアリングしてモデル作って検証して……が終わってからそのモデル使って 何かをし始める 今の自分の認識: 「さいきょうのモデル」がある前提でその先の仕事をする世界 がある

Slide 5

Slide 5 text

データサイエンスレガシーコード 「レガシーコードとは、単にテストのないコードです」 ――レガシーコード改善ガイド、序文、翔泳社 データサイエンスのコードはテストがないコードである (ことが多い) しかしこれらのコードを別のアプリやサービスに組み込 んだり、保守をしなければいけないケースは増えてくる このようなコードを個人的にデータサイエンスレガシー コードと読んでいる 古いけどいい本なのでみんな買おう

Slide 6

Slide 6 text

仕様化テスト 手動チェックしているテストがあるとする - 自分が正しいと認識している手動チェッ クの結果をコピーする - 手動チェックの手順をそのままテストに 落とし込む 「バグを見つけることは重要ですが、直近の目 標は変更をより確実に行うための役立つテスト 環境を作ることです」――レガシーコード改善 ガイド、p.200 この本では、この手法を仕様化テストと呼んで いる DON'T > result = すごいデータサイエンス関数(データ) > print(result) 期待する結果か目視で確認 DO def test_すごい関数(): result = すごいデータサイエンス関数(データ) assert 期待する結果 == result

Slide 7

Slide 7 text

pytest Pythonのテストフレームワーク テスト駆動Python(翔泳社、2018)を読んで勉強 するのがおすすめ 以下の3つのプラグインと一緒に入れる $ pip install pytest pytest-cov pytest-randomly pytest-mock pytest使うならセットで買うべし

Slide 8

Slide 8 text

pytest-cov テストカバレッジを計測する $ pytest --cov=source_dir --cov-report=html 実行後、 htmlcov というディレクトリができる ので、 $ open htmlcov/index.html (MacOSの場合) あるいはWebブラウザで上記ファイルを開く と、カバレッジを可視化することができる 詳細は「テスト駆動Python」7.2 参照 https://github.com/python-attrs/attrs/blob/master/src/attr/_mak e.py

Slide 9

Slide 9 text

pytest-mock モックオブジェクト(あるオブジェクトを擬装す るオブジェクト)を使うためのツール テストコードを書く際に依存関係を分離するの に使う レガシーコードにおけるモックの考え方につい ては「レガシーコード改善ガイド」3、5章、 pytest-mockの使い方については「テスト駆動 python」7.3をそれぞれ参照のこと https://github.com/ansible/molecule/blob/master/test/unit/conft est.py

Slide 10

Slide 10 text

pytest-randomly テスト実行時にランダムな順序で実施する 暗黙的にテストの実行順序を仮定したテストが混じって ないかを確認可能 デフォルトはランダムシード。シード値を固定するには 以下のように実行。 $ pytest --randomly-seed=1234 (1234はシード値) 最後に実行したシード値をもう一度使うことも可能。 $ pytest --randomly-seed=last https://pypi.org/project/pytest-randomly/

Slide 11

Slide 11 text

scripttest コマンド実行をそのままテストに落とし込むための、機 能テストツール $ pip install scripttest pipなどのテストに使われている Jupyter等で手動チェックをしている手順をまるごとそ のままテストにできる コードが複雑すぎて容易に単体テストを作成できない、 しかし短期間で大きく変更しなければならない、という 状況ではかなり便利 ないよりマシという程度なのであくまで一時しのぎと認 識すべき import pytest import scripttest @pytest.fixture def env(): env = scripttest.TestFileEnvironment("./test-output") return env def test_func(env): result = env.run("target.py") assert result.returncode == 0

Slide 12

Slide 12 text

scripttestの基本 env = scripttest.TestFileEnvironment("./test-output") 引数の文字列は、ファイル出力時のルートディ レクトリかつ実行時のルートディレクトリ。 内容が保持されることを保証しないので、絶対 に既存のファイルを含むディレクトリを指定し ないこと ターゲットスクリプト実行時にはこのディレク トリからの相対パスで指定する。 TestFileEnvironment.run(コマンド実行文字列) オプション expect_stderr = True デフォルトだとstderrに出力されると実行失敗 扱いとなるが、このオプションで無効化可能 レガシーコードだとstderrにWARNING出しな がら実行しているとかがザラにある

Slide 13

Slide 13 text

env.run()の実行結果 response = env.run(コマンド)を実行したとする response.returncode コマンドのリターンコードを含む assert response.returncode == 0 は基本。 files_created / files_deleted / files_updated それぞれ、作成・削除・更新されたファイル、 ディレクトリの一覧を {ファイル名: FoundFile, ディレクトリ名: FoundDir} という辞書で返す

Slide 14

Slide 14 text

flake8 言わずとしれた、コードフォーマットチェッ カー兼静的解析ツール レガシーコードではコードのフォーマットがバ ラバラという事態は頻繁に発生する とりあえず以下のコマンドを叩くこと $ pip install flake8 $ flake8 target.py コードのフォーマットが統一されないまま、テ ストなしであれこれ変更したコードを動作確認 なしでそのまま渡されるケースに対処する場合 フォーマット直してる余裕はないので、静的解 析によるエラー検出だけに集中する $ flake8 --select=F target.py

Slide 15

Slide 15 text

プロファイリング 自分の手でいじって実行している場合、多少の 遅さは気にしないというデータサイエンティス トは多い 保守の観点からは非常に深刻な問題となる - テストの実行に時間がかかる - よって改修やバグ修正にも時間がかかる - それにより様々な業務に影響が出る 明らかに不必要に遅いコードは直すこと 過度な最適化はしないこと!! 今日紹介するツール cProfile : 標準プロファイラ gprof2dot: cProfileの結果をグラフで可視化 おまけ: tqdm 処理の進捗を可視化 直感的なボトルネックの探索だけでなく、実行 待ちストレスの軽減にもなる

Slide 16

Slide 16 text

cProfile import cProfile cProfile.run('main()', '/tmp/test.prof') 関数は文字列で渡すことに注意。 第二引数はオプションで、ファイル出力するた めのもの。 バイナリファイルであることに注意。 プロファイルができたらipython等を開いて中身 を確認する。 import pstats p = pstats.Stats('test.prof') p.sort_stats('cumulative').print_stats(50)

Slide 17

Slide 17 text

gprof2dot プロファイルを可視化する $ pip install gprof2dot $ gprof2dot -f pstats test.prof | dot -Tpng -o output.png 要graphviz。Mac なら brew install graphviz で インストール可能 https://github.com/jrfonseca/gprof2dot

Slide 18

Slide 18 text

tqdm プロファイリングとは違うが、おまけ的に紹介 簡単に処理の進捗を可視化可能 進捗の可視化により重い処理のストレス低減に 有効 遅いのをごまかすのに使う https://github.com/jrfonseca/gprof2dot

Slide 19

Slide 19 text

まとめ 今日紹介した話 - pytestプラグイン - scripttest - flake8 - cProfile - gprof2dot - tqdm 頑張ってレガシーコードに立ち向かおう 「レガシーコード改善ガイド」と「テスト駆動 python」は買おう 〜おしまい〜 @shiumachi

Slide 20

Slide 20 text

補足

Slide 21

Slide 21 text

Q. カバレッジはどの程度気にしているのか? A. いきなりプロダクションレベルの品質でテストを書くのは不可能なので、以 下のように優先度をつけてテストを書いています。 - 正常系1つ (最低限のテストでの保護) - 経験上明らかに頻出すると予想される異常系 - 実際に遭遇したエラー ここでいうテストは、品質管理というよりも「確認作業の自動化」くらいの意味 にとらえておけばいいです。 あくまで一時しのぎなので、本当に品質管理しようと思ったら書き直すことも検 討すべきでしょう。

Slide 22

Slide 22 text

Q. 型チェックなどはやっているのか? A. mypy / attrs / cattrs を使ってデータクラスの型チェックをやろうとしていま す。(一部始めたところ)