$30 off During Our Annual Pro Sale. View Details »

実験の再現性と効率化の話(Docker と Serialization 周辺)

@smly
July 05, 2016
3k

実験の再現性と効率化の話(Docker と Serialization 周辺)

2016-07-04 ビッグデータ基盤勉強会での発表資料です.Kaggle Tokyo Meetup #1 の資料から主に Serialization の Blosc まわりの説明を加筆しています.

@smly

July 05, 2016
Tweet

More Decks by @smly

Transcript

  1. 実験の再現性と効率化の話
    (Docker と Serialization 周辺)
    @smly
    2016-07-04 ビッグデータ基盤勉強会@筑波⼤学

    View Slide

  2. Agenda
    データ分析のためのエンジニアリング⾯における⼯夫の話をします.
    1.  Workflow (Reproducible Research)
    2.  Serialization (Blosc)
    3.  Docker (AWS + Docker Machine)
    Disclaimer:
    スパコンや⼤企業レベルのノード数のコンピューターを扱う話ではない.
    個⼈研究者や野良研究者がなるべく安価にマシンリソースを活⽤する話です

    View Slide

  3. Agenda
    データ分析のためのエンジニアリング⾯における⼯夫の話をします.
    1.  Workflow (Reproducible Research)
    2.  Serialization (Blosc)
    3.  Docker (AWS + Docker Machine)

    View Slide

  4. Well-designed Workflow Patterns
    ▶  分析・実験結果を発展させるためには「再現性の確保」や
    「プロセスの効率化」が重要となる.
    ▶  “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.

    View Slide

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

    View Slide

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

    View Slide

  7. Make it easy to execute only portions of the code
    特徴量抽出・予測モデル作成・交差確認それぞれの部分をわけて実⾏
    できるようにする.異なるサーバーで並⾏実⾏する際に役に⽴つ
    # 交差確認の Fold0 の prediction を “data/output/v1_val_fold0” に保存
    [cluster001]$ python model.py -v -f0 -m v1
    # 交差確認の Fold1 の prediction を “data/output/v1_val_fold1” に保存
    [cluster002]$ python model.py -v -f1 -m v1
    # 交差確認の Fold2 の prediction を “data/output/v1_val_fold2” に保存
    [cluster003]$ python model.py -v -f2 -m v1
    # training data 全体で学習して prediction を “data/output/v1_test” に保存
    [cluster004]$ python model.py –t -m v1
    実⾏環境が問題となることも(ソフトウェアバージョンの不⼀致など)
    → 実⾏環境を Docker で仮想化して管理する.後述

    View Slide

  8. Agenda
    データ分析のためのエンジニアリング⾯における⼯夫の話をします.
    1.  Workflow (Reproducible Research)
    2.  Serialization (Blosc)
    3.  Docker (AWS + Docker Machine)

    View Slide

  9. Agenda
    データ分析のためのエンジニアリング⾯における⼯夫の話をします.
    1.  Workflow (Reproducible Research)
    2.  Serialization (Blosc)
    3.  Docker (AWS + Docker Machine)
    予測モデルを作るには「特徴量作成」
    「パラメータチューニング」「前処理」
    「アンサンブルのための個別モデル」な
    どを何度も試⾏錯誤する.
    そのためには中間ファイルを⼤量に書き
    出すためシリアライズは重要な話

    View Slide

  10. Feather
    R, Python のデータフレームオブジェクトを Apache Arrow*1 の
    メモリレイアウトで “ファイル” に保存する補完的なライブラリ
    ▶  R, Python (Pandas) のデータフレームを “ファイル” に保存して
    お互いの環境で読み込むことができる
    ▶  ただし 2016年7⽉現在ではバージョン間の安定性を保証しない.データ圧
    縮を⾏わない.発展途上(あるいは実験的)なライブラリである
    import feather
    import pandas as pd
    import numpy as np
    arr = np.random.randn(10000000) # 10% nulls
    arr[::10] = np.nan
    df = pd.DataFrame({'column_{0}'.format(i):
    arr for i in range(10)})
    feather.write_dataframe(df, 'test.feather')
    library(feather)
    x <- runif(1e7)
    x[sample(1e7, 1e6)] <- NA # 10% NAs
    df <- as.data.frame(replicate(10, x))
    write_feather(df, 'test.feather')
    *1: Spark, Drill, Impala などのインメモリシステムで共通のメモリレイアウトを使うことで,
    システム間のオーバーヘッドを減らすことを⽬指したプロジェクト

    View Slide

  11. Blosc: A BLOcked Shuffler & Compressor
    数値データを lightweight かつ⾼速に serialize するためにデザインさ
    れた meta-compressor.⾼い圧縮率を捨て,速さを重視する
    ▶  Blocking による分割統治を⾏うことでマルチスレッド対応
    ▶  Bitshuffle により数値データに対して⾼速で⾼い圧縮率
    ▶  SIMD を積極的に使う FastLZ ベースの compressor がデフォルト
    Blosc のインメモリデータ構造でデータを扱うことで memcpy() より⾼速
    にデータを処理することができる
    (補⾜:メモリから CPU への転送で CPU Starvation が発⽣する.blocking して並列化できる
    ようにしたり圧縮した状態でデータを保持しておいて,CPU へ転送して展開したほうが,その
    ままの状態で保持して CPU へ転送するよりも速いという話.逸脱するので割愛)

    View Slide

  12. Blocking techniques
    Dataset を個別の block に分割して別々のスレッドで処理する.
    Block ⻑は L1, L2 cache に収まるサイズにデザインされている
    (compression level 6 までは L1 に,6 より上は L2 にフィット)
    Image source:
    h.p://www.slideshare.net/PyData/blosc-sending-data-from-memory-to-cpu-and-back-faster-than-memcpy-by-francesc-alted

    View Slide

  13. Shuffling による圧縮率の向上
    圧縮の前処理として:データストリーム上のバイト単位のデータ順序
    を規則的に⼊れ替えることで圧縮率の向上を図る
    - “A compression scheme for radio data in high performance computing”
    http://arxiv.org/abs/1503.00638
    - “New 'bitshuffle' filter“ http://blosc.org/blog/new-bitshuffle-filter.html
    00
    1
    00 00
    01 00
    23
    00 00
    17 00
    43
    00 00
    2B 00
    56
    00 00
    38
    01 17 2B 38 00 00 00 00 00 00 00 00 00 00 00 00
    Blosc では SIMD (SSE/AVX2) をサポートした bit-level の shuffle
    (Bitshuffle) が実装されている.
    1 byte ⽬のグループ 2 byte ⽬のグループ 3 byte ⽬のグループ 4 byte ⽬のグループ

    View Slide

  14. HDF5 における blosc サポート (ndarray)
    Python ではデータコンテナ HDF5 を管理する PyTables package が
    blosc をサポートしている.ndarray を HDF5 に保存する例:
    # ndarray を HDF5 に保存
    import tables as tb
    import numpy as np
    def save(X, filepath):
    with tb.open_file(filepath, 'w') as f:
    filters = tb.Filters(complevel=5, complib='blosc')
    data_ = f.create_earray(f.root, 'data', tb.Float32Atom(), shape=(0,), filters=filters)
    shape_ = f.create_earray(f.root, 'shape', tb.Int32Atom(), shape=(0,), filters=filters)
    data_.append(X.ravel())
    shape_.append(np.array([X.shape[0], X.shape[1]]))

    View Slide

  15. HDF5 における blosc サポート (ndarray)
    Python ではデータコンテナ HDF5 を管理する PyTables package が
    blosc をサポートしている.ndarray を HDF5 に保存する例:
    # ndarray を HDF5 に保存
    import tables as tb
    import numpy as np
    def load(filepath):
    with tb.open_file(filepath, ‘r') as f:
    l = f.root.shape[0]
    n = f.root.shape[1]
    X = f.root.data[:]
    return X.reshape((l, n))

    View Slide

  16. HDF5 における blosc サポート (pandas)
    Pandas.DataFrame の中間ファイルへの保存にも使うことができる
    (内部的に PyTables 実装を使っている)
    # DataFrame を HDF5 に保存
    import pandas as pd
    IRIS_CSV = 'http://www.ats.ucla.edu/stat/data/binary.csv’
    iris = pd.read_csv(IRIS_CSV)
    # Save & Load
    iris.to_hdf('iris.h5', 'table', complib='blosc’)
    iris = pd.read_hdf('iris.h5', 'table')

    View Slide

  17. 疎⾏列の保存:MATLAB Format
    ▶  SciPy は疎⾏列の保存⽅法として MATLAB Format をサポート
    ▶  しかし⾏列サイズに上限があるので⼤きな疎⾏列には使えない
    –  4GB 以上の⼤きな⾏列を保存することができない
    –  N-gram feature を扱う場合,問題となり得る
    –  圧縮すると保存に時間がかかり,圧縮しないとサイズが⼤きい
    –  何度も繰り返しセーブ・ロードする⽤途には向かない…

    View Slide

  18. 疎⾏列の保存:HDF5 Store
    ▶  疎⾏列を複数のベクトルで圧縮表現
    ▶  ベクトルも blosc アルゴリズムで圧縮して HDF5 に保存する
    # 疎⾏列 CSC Matrix を複数の数値列で圧縮表現して 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]]))

    View Slide

  19. 疎⾏列の保存:HDF5 Store
    ▶  疎⾏列を複数のベクトルで圧縮表現
    ▶  ベクトルも blosc アルゴリズムで圧縮して HDF5 に保存する
    # ロードする
    def loadmat(filepath):
    with tb.open_file(filepath, 'r') as f:
    l = f.root.shape[1]
    n = f.root.shape[0]
    X = ss.csr_matrix(
    (f.root.data[:], f.root.indices[:], f.root.indptr[:]),
    shape=(l, n))
    X = ss.csc_matrix(X.T)
    return X

    View Slide

  20. Benchmark (Dense Matrix)
    実験設定1: ランダムな密⾏列 np.random.random((1e7, 30))
    圧縮の難しい密⾏列で np.savez_compressed は無駄
    無圧縮と同等の時間
    ファイルサイズ 実⾏時間

    View Slide

  21. Benchmark (Dense Matrix)
    実験設定2: 実データ(Cervical Cancer Screening)の⼀部変数
    無圧縮と同等あるいはそれ以上の速さで圧縮
    無圧縮と同等の時間
    ファイルサイズ 実⾏時間

    View Slide

  22. Benchmark (Dense Matrix)
    実験設定3:実データ(Springleaf contest)の変数 VAR_0002 を保存
    圧縮できる密⾏列で np.savez はディスクの無駄
    ファイルサイズ 実⾏時間
    圧縮できる場⾯での
    np.savez は無駄が⼤きい

    View Slide

  23. Agenda
    データ分析のためのエンジニアリング⾯における⼯夫の話をします.
    1.  Workflow (Reproducible Research)
    2.  Serialization (Blosc)
    3.  Docker (AWS + Docker Machine)

    View Slide

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

    View Slide

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

    View Slide

  26. あるデータ分析コンテストでの利⽤例 (1/2)
    S3 は「同⼀リージョン間の転送料は無料」
    そこまで極端な負担にはならない

    View Slide

  27. あるデータ分析コンテストでの利⽤例 (2/2)
    EC2 はスポットインスタンスでコストを抑えることができる.
    (m4.10xlarge を 2000 時間で 1k USD は安い)

    View Slide

  28. 扱うものが増えると管理ができなくなる
    # 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 台とか増えると管理がつらくなる
    ⼤量のイメージ, 中間ファイル,スナップショット, ボリューム…

    View Slide

  29. Docker で実⾏環境を使い捨てる
    KVM などのハイパーバイザー型仮想化技術と異
    なり,Linux コンテナ (LXC) と cgroups のリ
    ソース管理機能を活⽤するツール
    仮想ハードウェアのエミュレーションやゲスト
    OSの起動などのオーバーヘッドがないので,プ
    ロセス実⾏が⾮常に⾼速.Web システム以外に
    分析環境を⼿軽に提供する場⾯でも使われてい

    パッケージ郡をまとめたイメージや NVIDIA の CUDA Toolkit インストール済みイメージなど:
    - ref: “Kaggle R docker image” https://github.com/Kaggle/docker-rstats
    - ref: “Data-Science-Notebook” https://github.com/dnc1994/Data-Science-Notebook
    - ref: “NVIDIA Docker” https://github.com/NVIDIA/nvidia-docker (* Docker ⾃⾝を拡張)

    View Slide

  30. ⽤途ごとに Docker イメージを作成する
    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

    View Slide

  31. コマンド1つで Docker Host を作成
    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

    View Slide

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

    View Slide

  33. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  37. まとめ
    •  分析・実験結果を発展させるためには「再現性の確保」や
    「プロセスの効率化」が重要となる.Workflow を適切に設計する
    ことで実験の⼿戻りを最⼩限に抑えることができる
    •  「中間ファイルを頻繁に書き出す」ためには⾼速なシリアライズが
    可能なツールが必要となる.Blosc による⾼速な圧縮・シリアライ
    ズはこの要件を満たす
    •  Docker による仮想化はクラウドサービスを使う上でも,再現性を
    確保する上でも効果的である.

    View Slide