Slide 1

Slide 1 text

Workflow, Serialization & Docker for Kaggle smly confirm on 2016-03-05 at Kaggle Tokyo Meetup #1

Slide 2

Slide 2 text

About :-) 研究開発する Software Engineer ICPC → ICFP contest → KDD Cup → Kaggle と趣味を変えてきた. 最近は Game AI (+ Deep Reinforcement Learning) に興味. (as of 2016-03-05)

Slide 3

Slide 3 text

Agenda ソフトウェア・エンジニアリングをとことん活⽤して 分析コンテストを攻略する話をします 1.  Workflow (Reproducible Research) 2.  Serialization (bloscpack & HDF5) 3.  Docker (DockerMachine, alpine-kaggle & fireplug)

Slide 4

Slide 4 text

Agenda ソフトウェア・エンジニアリングをとことん活⽤して 分析コンテストを攻略する話をします 1.  Workflow (Reproducible Research) 2.  Serialization (bloscpack & HDF5) 3.  Docker (DockerMachine, alpine-kaggle & fireplug)

Slide 5

Slide 5 text

Well-designed Workflow ▶  実験結果の再現性を確保&プロセスの効率化 ▶  “Patterns for Research in Machine Learning” –  http://arkitus.com/patterns-for-research-in-machine-learning/ –  Always, always use version control. –  Separate code from data. –  Separate input data, working data and output data. –  Modify input data with care. –  Save everything to disk frequently. –  Separate options from parameters. –  Do not use global variables. –  Record the options used to generate each run of the algorithm. –  Make it easy to sweep options. –  Make it easy to execute only portions of the code.

Slide 6

Slide 6 text

Directory Structure: Data ~/kaggle/telstra:$ tree . ├── data │ ├── input │ │ ├── train.csv │ │ ├── test.csv │ │ └── cv_id.csv │ ├── working │ └── output ├── trunk │ └── table_schema.xlsx ├── Dockerfile ├── base.py ├── v1.py └── feat.py ▶  データとコードを分離 ▶  ⼊⼒,出⼒,中間データの分離 ▶  交差確認のインデックスファイル ▶  その他ファイルは trunk

Slide 7

Slide 7 text

Directory Structure: Code ~/kaggle/telstra:$ tree . ├── data │ ├── input │ │ ├── train.csv │ │ ├── test.csv │ │ └── cv_id.csv │ ├── working │ └── output ├── trunk │ └── table_schema.xlsx ├── Dockerfile ├── base.py ├── v1.py └── feat.py ▶  base.py には各⼊⼒ファイルの ファイルパスを定義した定数や 予測モデルの基底クラスなど ▶  v1.py で基底クラスを継承して 個別の予測モデルを定義 ▶  feat.py には特徴量の定義

Slide 8

Slide 8 text

Make it easy to execute only portions of the code 特徴量抽出・予測モデル作成・交差確認それぞれの部分をわけて実⾏ できるようにする.並⾏実⾏の役に⽴つ # 交差確認の Fold0 の prediction を “data/output/v1_val_fold0” に保存 $ python v1.py -v -f0 # 交差確認の Fold1 の prediction を “data/output/v1_val_fold1” に保存 $ python v1.py -v -f1 # 交差確認の Fold2 の prediction を “data/output/v1_val_fold2” に保存 $ python v1.py -v -f2 # training data 全体で学習して prediction を “data/output/v1_test” に保存 $ python v1.py -t

Slide 9

Slide 9 text

Base Class of Predictive Model (base.py) 同じコードを繰り返し書くことメンテナンスの観点から避けるべき 以下のパラメーターを渡せば学習・交差確認・予測などをすべてよろ しくやってくれるような基底クラスを作成する ▶  Serialize した特徴量のファイルリスト ▶  学習器(sklearn の BaseEstimator インスタンス) ▶  予測モデルのパラメータ 基底クラスを継承してパラメータを渡せば, それだけで新しいモデルができる状態にする.

Slide 10

Slide 10 text

Demo1 from base import BaseModelV4, XGBClassifier FEATURE_LIST = ["feat.group14.blp”] PARAMS = { 'n_estimators': 700, 'subsample': 0.8, 'colsample_bytree': 0.7, 'max_depth': 7, 'eta': 0.03, 'objective': 'multi:softprobʼ, 'eval_metric': 'mlogloss', 'num_class': 3, 'seed': 32, } class Modelv60(BaseModelV4): def build_model(self): return XGBClassifier(**self.params) if __name__ == '__main__': m = Modelv60(name="v60”, flist=FEATURE_LIST, params=PARAMS) m.run() ▶  base.py の基底クラスを継承して パラメータと特徴量だけ異なる 新しいモデルを作成する例

Slide 11

Slide 11 text

Demo2 def percentile_trafo(X): X2 = [] for i in range(X.shape[1]): X2.append((rankdata(X[:, i]) / X.shape[0]).reshape((-1, 1))) return np.hstack(X2) class Modelv142(BaseModelV4): def build_model(self): return ETC(**self.params) def load_X(self): X = super(Modelv142, self).load_X() return np.hstack([ X, percentile_trafo(X), ]) ▶  特徴量の前処理をした⾏列と 前処理をしなかった⾏列を ⽔平⽅向に stack したモデル

Slide 12

Slide 12 text

Agenda ソフトウェア・エンジニアリングをとことん活⽤して 分析コンテストを攻略する話をします 1.  Workflow (Reproducible Research) 2.  Serialization (bloscpack & HDF5) 3.  Docker (DockerMachine, alpine-kaggle & fireplug)

Slide 13

Slide 13 text

Serialization 中間データ(多次元⾏列)をどのように保存するか ▶  かなり昔やってたこと ▶  全部 pickle … 筋が悪い.実⾏時間もファイルサイズもでかい ▶  昔やってたこと ▶  密⾏列: numpy.io.save … まあまあ妥当 ▶  疎⾏列: scipy.io.savemat … でかい.限界ある. ▶  今やってること ▶  密⾏列: bloscpack ▶  疎⾏列: HDF5 + complib=blosc 全部 HDF5 + complib=blosc で良いかもしれない.

Slide 14

Slide 14 text

Bloscpack Processor cache にデータを転送するようデザインされているので memcpy call より⾼速.SIMD や multi-thread を使⽤ 最新版 python-blosc 1.2.7 は multi-threading に問題があり Downgrade して 1.2.5 を利⽤すれば問題ない

Slide 15

Slide 15 text

MATLAB Format for Sparse Matrix ▶  ⾏列サイズに上限があるので⼤きな疎⾏列には使えません –  4GB 以上の⼤きな⾏列を保存することができない –  N-gram feature を扱う場合,問題となり得る –  圧縮すると保存が遅い –  圧縮しないとファイルサイズ⼤きい

Slide 16

Slide 16 text

HDF5 Store for Sparse Matrix ▶  疎⾏列を複数のベクトルで圧縮表現 ▶  ベクトルも blosc アルゴリズムで圧縮して HDF5 に保存する # 疎⾏列を複数の数値列で圧縮表現して HDF5 に保存する def savemat(X, filepath): X = ss.csc_matrix(X) with tables.open_file(filepath, 'w') as f: filters = tables.Filters(complevel=5, complib='blosc') out_data = f.create_earray( f.root, 'data', tables.Float32Atom(), shape=(0,), filters=filters) out_indices = f.create_earray( f.root, 'indices', tables.Int32Atom(), shape=(0,), filters=filters) out_indptr = f.create_earray( f.root, 'indptr', tables.Int32Atom(), shape=(0,), filters=filters) out_shape = f.create_earray( f.root, 'shape', tables.Int32Atom(), shape=(0,), filters=filters) out_data.append(X.data) out_indices.append(X.indices) out_indptr.append(X.indptr) out_shape.append(np.array([X.shape[0], X.shape[1]]))

Slide 17

Slide 17 text

Benchmark (Dense Matrix) 実験設定1: ランダムな密⾏列 np.random.random((1e7, 30)) 圧縮の難しい密⾏列で np.savez_compressed は無駄

Slide 18

Slide 18 text

Benchmark (Dense Matrix) 実験設定2: Cervical Cancer Screening の⼀部変数を保存 無圧縮と同等あるいはそれ以上の速さで圧縮

Slide 19

Slide 19 text

Benchmark (Dense Matrix) 実験設定3: Springleaf contest の変数 VAR_0002 を保存 圧縮できる密⾏列で np.savez はディスクの無駄

Slide 20

Slide 20 text

Agenda ソフトウェア・エンジニアリングをとことん活⽤して 分析コンテストを攻略する話をします 1.  Workflow (Reproducible Research) 2.  Serialization (bloscpack & HDF4) 3.  Docker (DockerMachine, alpine-kaggle & FirePlug)

Slide 21

Slide 21 text

Amazon AWS ▶  S3 + EC2 spot instance の組 み合わせは財布に優しい ▶  しかし GPU instance は半年前 から価格が暴⾛している ▶  素直に On-Demand instance を借りたほうが良い場合もある

Slide 22

Slide 22 text

それではたくさんのサーバーを使ったときの事例を⾒てみましょう image source: h.ps://twi.er.com/ultrais.er/status/554280474042847233

Slide 23

Slide 23 text

Use Case for Cervical Cancer Screening (1) S3 … 同⼀リージョン間の転送料は無料なので優しい

Slide 24

Slide 24 text

Use Case for Cervical Cancer Screening (2) EC2 … spot も使い過ぎると厳しい (でも m4.10xlarge を 2000 時間で 1k USD は安い)

Slide 25

Slide 25 text

Simple Way to utilize AWS on Cross Validation # 3fold CV の各 fold を 3 台の EC2 instance で並⾏して計算 dev$ ssh –i kaggle-ws.pem \ ubuntu@aws01 \ "cd /home/ubuntu/ws; bash run.sh python v1.py -v -f0” dev$ ssh –i kaggle-ws.pem \ ubuntu@aws02 \ "cd /home/ubuntu/ws; bash run.sh python v1.py -v -f1” dev$ ssh –i kaggle-ws.pem \ ubuntu@aws03 \ "cd /home/ubuntu/ws; bash run.sh python v1.py -v -f2" # run.sh git pull aws s3 sync s3://mybucket/cervical data $* aws s3 sync data s3://mybucket/cervical サーバーの台数が 10 台とか増えると管理がつらくなる ⼤量のイメージ, スナップショット, ボリューム… 単純化したい…

Slide 26

Slide 26 text

使い捨ての精神で実⾏環境を⾒直す

Slide 27

Slide 27 text

smly/alpine-kaggle Kaggle のコンテストのために作ったコンパクトなイメージ Alpine Linux に miniconda と XGBoost 0.47 を同梱 サイズは 317MB (Virtual Size=994.7 MB).Docker Hub で⾃動ビルド # 動作確認 dev$ docker pull smly/alpine-kaggle Using default tag: latest latest: Pulling from smly/alpine-kaggle (snip) # 実⾏ dev$ docker run --rm -i smly/alpine-kaggle \ python -c "import xgboost; print(xgboost.__version__)” 0.4

Slide 28

Slide 28 text

Docker Machine a # スポットリクエストで Docker Host を AWS 上に作成する (c3.8xlarge を 5 USD で⼊札) $ docker-machine create \ --driver amazonec2 \ --amazonec2-vpc-id vpc-43537026 \ --amazonec2-subnet-id subnet-9cfbaeeb \ --amazonec2-region us-west-2 \ --amazonec2-zone a \ --amazonec2-instance-type c3.8xlarge \ --amazonec2-root-size 20 \ --amazonec2-ami ami-16b1a077 \ --amazonec2-security-group kaggle-srck \ --amazonec2-request-spot-instance \ --amazonec2-spot-price 5 aws01 Running pre-create checks... Creating machine... (aws01) Launching instance... (aws01) Waiting for spot instance... (aws01) Created spot instance request %v sir-0396hlmq Waiting for machine to be running, this may take a few minutes... Detecting operating system of created instance... Waiting for SSH to be available... (snip) Docker is up and running! To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env aws01

Slide 29

Slide 29 text

Remote Docker Host をどのように使いこなすか

Slide 30

Slide 30 text

FirePlug: A Plug to Remote Docker Hosts # あらかじめ docker host を⽤意しておく. $ docker-machine ls NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS aws01 - amazonec2 Running tcp://52.36.102.243:2376 v1.9.1 aws02 - amazonec2 Running tcp://52.36.102.244:2376 v1.9.1 # ⼿元の分析環境でスクリプト beat_the_benchmark.py を実⾏する $ python beat_the_benchmark.py [0] train-auc:0.820364 [1] train-auc:0.813448 [2] train-auc:0.827770 [3] train-auc:0.828806 # AWS 上の Docker host 上でカレントディレクトリのスクリプト beat_the_benchmark.py を実⾏ $ fp python beat_the_beanchmark.py Run python beat_the_benchmark.py on aws01... Sending build context to Docker daemon 18.43 kB (snip) [0] train-auc:0.820364 [1] train-auc:0.813448 “fp” を先頭に付けるだけで,ローカル環境のコードを Docker Host で実⾏ https://github.com/smly/fireplug

Slide 31

Slide 31 text

Workflow on FirePlug FirePlug はコマンド実⾏ごとに Build → Sync → Run → Sync する コンテナは使い捨て.イメージも(基本的に)再利⽤しない Client PC AWS 2. rsync 4. rsync 1. docker build 3. docker run Docker Host Container fs mount

Slide 32

Slide 32 text

Building Step on FirePlug ベースとなる Image から⼿元のファイルを追加した仮想イメージを作成 Remote Docker Host でビルドするための転送サイズは⼩さい Base Image (smly/alpine-kaggle) One-time Image c7df017aaeeb (987.4 MB) 68fbf78112b5 (18.43 kB) Client PC (current directory)

Slide 33

Slide 33 text

コンテナではなく Docker Host 側のファイルシステムに Sync する コンテナを捨てても Sync したファイルは Docker Host で保持される Sync 対象は以下の2つから選択: ▶  (Default) クライアント環境から rsync ▶  Docker Host から S3 に aws s3 sync 転送量が増える場合は S3 が便利 (同⼀ AZ 内の転送が課⾦対象外なので) Sync Step on FirePlug 1. docker build 3. docker run Docker Host Container fs mount

Slide 34

Slide 34 text

Sync Variation / --nosync, --insync, --outsync 実⾏環境(Docker Host)へのファイル転送はオプションで制御 (default) Build → Sync (download) → Run → Sync (upload) nosync Build → Sync (download) → Run → Sync (upload) insync Build → Sync (download) → Run → Sync (upload) outsync Build → Sync (download) → Run → Sync (upload) Download … From Client PC (or S3) to Docker Host Upload … From Docker Host to Client PC (or S3)

Slide 35

Slide 35 text

Initial Configuration / fp --init 初期設定コマンド # 初期設定(空欄の場合はデフォルト値) # S3 bucket の設定がなければ rsync でクライアントPC のデータディレクトリと同期する $ fp –init [1/4] Enter your project name: telstra [2/4] Enter your s3 bucket name (Default: None): [3/4] Enter data path on local client (Default: data): [4/4] Enter your base docker image (Default: smly/alpine-kaggle): # プログラム実⾏時に毎回ビルドされるイメージの Dockerfile # マウントポイント /data に telstra ディレクトリを作成 # working directory (/root) 直下に同期対象となるディレクトリ /root/data へ symlink を張っている # COPY でクライントPCのファイルをイメージに組み込んでいる(不要なものは .dockerignore で指定) $ cat Dockerfile FROM smly/alpine-kaggle RUN mkdir -p /data/telstra && ln -s /data/telstra /root/data COPY ./ /root/ WORKDIR /root ビルドされるimage 名,S3 上のディレクトリ名 デフォルトは rsync (S3 使わない) ローカル環境上のデータ同期対象 Base Image // を作り, 作業ディレクトリ上のデータ同期先に synlink

Slide 36

Slide 36 text

Future work ▶  Documentation ▶  Background 実⾏できるようにする ▶  実⾏中のタスクとサーバーリソースの可視化 (Web UI) ▶  Docker Image の Private Registory 対応 ▶  LibFM, RGF, dboost などの各種アルゴリズム対応 image ▶  R ⽤ image ▶  Portability (Windows support) ▶  Compatibility (Docker API Level) ▶  分散アルゴリズム対応,例えば XGBoost (Wormhole) Contributor 募集中です!

Slide 37

Slide 37 text

余談 •  Kaggle は Kaggle Script で Docker を使ってる.しかし Kaggle/python のイメージサイズは Debian ベースで 6GB. •  ⼀⽅ smly/alpine-kaggle は Alpine ベースで 317MB. Docker Host の増減を頻繁に⾏う場合に有利. •  DataRobot も内部のシステムでスケールさせるために Docker を 使っていると中の⼈が教えてくれた.