LoginSignup
5
4

More than 1 year has passed since last update.

DVCでデータ分析実務を想定したKaggle環境を作ろう!

Posted at

この記事はKaggle Advent Calender 12/04の記事として書かれています。

Kaggleはデータ分析者の登竜門としても使われことが多いかと思います。実際自分もその一人でした。
しかし、実際にデータ分析実務をして気づきます「あれ実務、Kaggleと全然ちがくね」。

何が違うかというと、分析ゴリゴリというよりもデータ生成周りが賢くなること、それが機動的に行えることが大事なのです。

今回はこのことを意識した分析環境づくりを僕おすすめのDVCで作って見たいと思います。データ分析用のツールは他にKedroやMLFlowなどのツールがありますが、Kedroは複雑で覚えること多いですし、MLFlowはPythonのコード修正が必要だったり、そもそも実験の管理自体はハイパラチューニングとしてPython内部でやれる(どっちかというと学習器のつなげ方などのコードレベルでの変化をトラッキングしたい)。
何も題材もないと不便なので、KaggleのTitanicを題材にしてやってみようと思います。

githubレポジトリ

DVCとは?

DVC(Data Version Control)はGit管理下でデータ分析をするためのライブラリです。
主な機能はgitでラージファイルを扱えるようにさせる点にあります。この点はGit LFSにそっくりで、やり方もハッシュをGitに管理させるので同じです。
ただし、DVCはデータ分析用にデータパイプライン形成、実験管理などの機能も持っています。しかも基本はdvc.yamlをいじるだけで、pythonのコード自体はいつもどおりで構わないです(というかPythonである必要もない)。

また、dvcはgitの機能に似せて作っているのでgitに慣れていれば学習コストは低くすみます。

DVC導入

導入方法は簡単です。以下のようにコードを実行するだけ(事前にgitレポジトリは作成してください)

$ pip install dvc
$ dvc init

すると以下のファイル・フォルダが作成されます。

  • .dvc: データの実体を保存するためのキャッシュフォルダ
  • .dvcignore: dvcで管理したくないファイルがあるときに明示するファイル。.gitignoreに相当します。

増えたファイルはgit管理下に置きます。

$ git add .dvc/* .gitignore
$ git commit -m "dvc init"

データ追加

KaggleからTitanic号のデータ以下の用に追加します。

- data
    - raw
        - train.csv
        - test.csv
        - gender_submission.csv

このデータをDVCに追加します。

dvc add data/raw/train.csv data/raw/test.csv data/raw/gender_submission.csv
# フォルダごと追加することも可能
# dvc add data/raw

実行すると[データ名].dvcというファイルと.gitignoreが生成されます。.dvcの方はデータのハッシュのファイルで、gitignoreでデータの実体のファイルをgit管理下から除外してくれています。

$ git add data/raw/.gitignore  data/raw/*
$ git commit -m "dvc add raw data"

今回は省きますが、dvcではS3やGCSなどのクラウドのストレージ、共有ドライブなどよく使うファイル共有先をリモートレポジトリとして登録でき、リモートレポジトリにデータをバックアップすることができます。
詳しくは公式サイトを参考にしてください。

データパイプライン構築

DVCを使うとデータパイプラインを簡単に作ることができます。
加工するためのコードにはsrc/proc.pyを使います。
パイプラインの構築方法には

  1. dvc runを使う
  2. dvc.yamlの編集

の2つの方法があります。
ここではより汎用的なdvc.yamlを編集する方法をとります。

ここでは学習するための加工済みデータを用意して、学習・推論するパイプラインを作ります。コードにはsrc/proc.pysrc/train.pyを使います。

src/proc.py
import sys
from pathlib import Path
import pandas as pd

ROOT = Path(__file__).resolve().parent.parent
RAW_DIR = ROOT/'data'/'raw'
OUT_DIR = ROOT/'data'/'out'

if __name__ == '__main__':
    train_csv = Path(RAW_DIR/'train.csv')
    test_csv = Path(RAW_DIR/'test.csv')
    train_df = pd.read_csv(train_csv)
    test_df = pd.read_csv(test_csv)
    test_df['Survived'] = -1
    train_df['is_Test'] = 0
    test_df['is_Test'] = 1

    concat_df = pd.concat([train_df, test_df])
    concat_df = pd.get_dummies(concat_df , dummy_na  = False, drop_first = False,columns=['Sex', 'Ticket', 'Cabin', 'Embarked'])
    concat_df['Age'] = concat_df['Age'].fillna(train_df['Age'].mean())
    concat_df['Fare'] = concat_df['Fare'].fillna(train_df['Fare'].mean())
    del concat_df['Name']

    concat_df.to_pickle(OUT_DIR/'preprocess.pkl')
src/train.py
import sys
from pathlib import Path
import yaml

import pandas as pd
from sklearn import tree
from sklearn.model_selection import train_test_split

ROOT = Path(__file__).resolve().parent.parent
OUT_DIR = ROOT/'data'/'out'

if __name__ == '__main__':
    df = pd.read_pickle(OUT_DIR/'preprocess.pkl')

    train_valid = df.query('is_Test == 0').copy()
    test = df.query('is_Test == 1').copy()

    train, valid = train_test_split(train_valid, test_size=0.2, shuffle=True, random_state=0)

    train_X = train.drop(columns=['Survived', 'is_Test'])
    train_y = train['Survived']
    valid_X = valid.drop(columns=['Survived', 'is_Test'])
    valid_y = valid['Survived']
    test_X = test.drop(columns=['Survived', 'is_Test'])

    clf = tree.DecisionTreeClassifier().fit(train_X, train_y)
    accuracy = (clf.predict(valid_X) == valid_y).mean().tolist()
    obj = {'accuracy': accuracy}
    with open('data/out/summary.yaml', 'w') as f:
        yaml.dump(obj, f, encoding='utf-8')

    test['Survived'] = clf.predict(test_X)
    test[['PassengerId', 'Survived']].to_csv('data/out/gender_submission.csv', index=None)

次に、プロジェクト直下にdvc.yamlを作ります。

dvc.yml
stages:
  data_proc:
    cmd: python src/proc.py
    deps:
    - data/raw/test.csv
    - data/raw/train.csv
    - src/proc.py
    outs:
    - data/out/preprocess.pkl
   
  train:
    cmd: python src/train.py
    deps:
    - data/out/preprocess.pkl
    - src/train.py
    outs:
    - data/out/gender_submission.csv
    metrics:
    - data/out/summary.yaml:
        cache: false

このyamlの意味は

  • data_proc/train: プロセスの名前です。任意の名称をつけることができます
  • cmd: このプロセス自体を示すコマンドです。
  • deps: プロセスが依存するデータ・ソースコード
  • outs: プロセスが生成するデータ
  • metics: outs同様プロセスが生成するデータですが、dvc metricsで集計する対象となります。
    • cache: dvcで追っかける必要がないくらい容量が小さいファイルであることを明示する

dvc.yamlが書けたら

$ dvc repro
$ git add -A
$ git add commit -m "dvc repro 1"

を実行します。するとcmdで指定したコードが順に実行されます。

dvcがすごいのはここからで、train.pyを書き換えます。
例えば、シード値を書き換えます。

train.py
# 修正前L18: 
train, valid = train_test_split(train_valid, test_size=0.2, shuffle=True, random_state=0)
# 修正後:
train, valid = train_test_split(train_valid, test_size=0.2, shuffle=True, random_state=1000)

コードの書き換えが起こったので、データの再出力が必要です。
しかし、書き換えたのはtrain.pyだけなのでプロセスdata_procは再度処理される必要はありません。なので、dvc reproを実行すると

$ dvc repro
'data\raw\test.csv.dvc' didn't change, skipping
'data\raw\train.csv.dvc' didn't change, skipping
Stage 'data_proc' didn't change, skipping
Running stage 'train':
> python src/train.py
Updating lock file 'dvc.lock'

このように、data_procは実行されずtrainだけが実行されます。

また、metricsを指定したので

$ dvc metrics diff
Path                   Metric    HEAD     workspace    Change
data\out\summary.yaml  accuracy  0.86592  0.8324       -0.03352

このように、train.pyを書き換えたことでaccuracyがどのように変わったかが確認できます。

まとめ

  1. dvcの導入はpip install dvcで簡単
  2. dvc.yamlを書くだけでデータプロセスが作れる
  3. dvc reproを唱えるだけで、よしなにデータ再生成ができる
5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4