LoginSignup
77
62

More than 3 years have passed since last update.

Kaggleでのソースコード管理の手助け

Last updated at Posted at 2019-12-26

この記事は「Gitでコード管理しているのに、KaggleのNotebook提出用に別のコードを用意するのが面倒だ」と感じてる人向けの記事となっております。また、Notebookで提出するというコンペであるCodeCompetitionを対象として話を進めてゆきます。CodeCompetitionについては後述します。

はじめに

再掲しますが「Gitでコード管理しているのに、KaggleのNotebook提出用に別のコードを用意するのが面倒だ」。この不満、心当たりがある方が多いのではないでしょうか。

今回書く記事はその不満を解消できる方法を発見しましたので共有し、その方法について解説するというのが趣旨になります。

また記事内容ですが主にこちらのKaggle Kernel - lopuhin/imet-2019-submissionの内容を参考に書いております。

前置きのために記事ではCodeCompetitionとはから説明していますが本題から読みたい方は#git等で管理しているファイル構造をそのまま使うから読んで下さい。

CodeCompetitionとは

冒頭で触れたCodeCompetitionについて説明させてください。

Kaggleのドキュメントで説明されている内容を引用します。

Some competitions are code competitions. In these competitions all submissions are made from inside of a Kaggle Notebook, and it is not possible to upload submissions to the Competition directly.

下記はそれを私が意訳したものです。
CSVなどのファイルを直接アップロードするのではなく、Notebookを利用してそこにファイル提出するまでのソースコードを書きそれを提出するという形式のコンペを指します。

開催中(2019/12/25現在)のコンペ8種の内(Featured/Researchのみカウント)6種類がCodeCompetitionであるのでおそらく参加されている方も多いと思います。

[参考]CodeCompetition例

赤枠で囲ったCode Competitionというタグが付けられたコンペがCodeCompetitionになります。

code-competitionn.PNG

CodeCompetitionでの提出方法

Kaggle内でコードを書こうとするとScriptsNotebooksという2種類の記述する手段があり、CodeCompetitionでは2つの形式のどちらかに提出ファイルを生成するまでのコードを書きそれを提出する形になります。

Scripts

このScriptのみにコードを書いてゆきます。

kaggle_script.PNG

Notebooks

いわゆるJupter Notebooks、Scripts同様これのみにコードを書いてゆきます。

kaggle_notebook.PNG

CodeCompetitionの不満点

デバッグやテストのしやすさを考えると、こんな感じで処理毎にモジュール(ファイル)で分けたいという思いがあると思います。(タイタニックコンペに取り組んだ際の一例です)

タイタニックでのファイル構造例
titanic_sample
  │  dataset.py
  │  model.text
  │  predict.py
  │  train.py
  │  utils.py
  │  __init__.py
  │
  ├─resources
     gender_submission.csv
     test.csv
     train.csv

提出時にも同様のファイル構造で提出できればよいのですが上記で触れたようにScriptsかもしくはNotebooksにほぼ提出専用のコードを書くことになっているというのが現状の不満点です。

Git等で管理しているファイル構造をそのまま使う!

パッケージとしてインストールしてしまおう

遅くなりましたが、本題に入ります。

Gitで管理しているコード郡をNotebook環境下でそのまま使えれば、KaggleのNotebook提出用に別のコードを用意する必要なんてないわけです。というわけでNotebook環境下にそれらのコードを用意してしまうというのが今回説明する解決方法になります。

簡単な概要

これ以降手順の説明に入りますが説明が長くなるので簡単にやることを書くと、
KaggleのNotebookにGitで管理しているコードをアップロードして、それらをパッケージとしてインストールしSubmitまでの書かれたコードをコマンドライン上から実行するという流れです。

Submitするまでの手順説明

具体的にどうやるのか手順を説明します。

説明をわかりやすくするためにタイタニックのデータを使って説明します。
また、前処理、モデルの学習等のコードはこちらを参考にさせていただきました。
Kaggle Kernel - sishihara/upura-kaggle-tutorial-04-lightgbm

手順①

Github - lopuhin/kaggle-script-templateをForkします。

手順②

ForkしたリポジトリをTemplateリポジトリにする。この作業は色々なコンペでこのTemplateリポジトリを使い回すためにやっています。
参考に私が作成したTemplateリポジトリのURLを貼っておきます。
Github - wakamezake/kaggle-script-template

手順③

手順②で作成したTemplateリポジトリを使って、コンペ用のリポジトリを作成する。
Github - wakamezake/Titanic_submit_script

手順④

easy_goldディレクトリ(Github - wakamezake/Titanic_submit_scriptだとtitanic_sampleにrenameしてます)にいつもどおりコードを書く。注意点として以下のように最終的にsubmission.csvが出力されるようにコードを書いてください。

script.py
def run(command):
    os.system('export PYTHONPATH=${PYTHONPATH}:/kaggle/working && ' + command)

run('python setup.py develop --install-dir /kaggle/working')
run('python titanic_sample/train.py model.text --test_size 0.3')
run('python titanic_sample/predict.py model.text submission.csv')

そしてタイタニックでの例ですがおそらく以下のようなフォルダ構成になると思います。

タイタニックでのフォルダ構成例
.
│  .gitignore
│  build.py
│  README.rst
│  script_template.py
│  setup.py
│
├─build
│   .keep
│   script.py
│
├─titanic_sample
  │  dataset.py
  │  model.text
  │  predict.py
  │  train.py
  │  utils.py
  │  __init__.py
  │
  ├─resources
       gender_submission.csv
       test.csv
       train.csv

手順⑤

build.pyを実行します。実行後build/script.pyが生成されているのでそれをNotebooksに貼り付けます。その後はCommitボタンを押してsubmitするといういつもの流れです。
貼り付けた例としてこちらのカーネルを用意しました。Kaggle Kernel - wakamezake/titanic-submission-sample

Submitするまでの手順説明は以上になります。

少し解説します

手順をざっと説明してきましたがやったことを解説しますと・・・

手順の中で実行したbuild.pyについて。まずすべてのコードをbase64でエンコードして文字列に変換します、その際エンコードしたコードのファイルパスをkeyにエンコードして文字列をvalueにした辞書を作ります。そしてscript_template.pyというscript.pyを作るためのテンプレートのfile_data部分に辞書の内容を置換します。

script.pyNotebooksに貼り付けたあとエンコードされたコードをデコードして元のファイル構造を保った状態に戻します。そしてpython setup.py develop --install-dir /kaggle/workingを実行してNotebooksの環境にデコードしたコードをパッケージとしてインストールします。

解説は以上です。

build.py

build.py
#!/usr/bin/env python3
import base64
import gzip
import os
from pathlib import Path

is_Windows = True if os.name == "nt" else False


def encode_file(path: Path) -> str:
    compressed = gzip.compress(path.read_bytes(), compresslevel=9)
    return base64.b64encode(compressed).decode('utf-8')


def build_script():
    to_encode = list(Path('titanic_sample').glob('*.py')) + [Path('setup.py')]
    if is_Windows:
        # https://stackoverflow.com/questions/54671385/convert-windowspath-to-posixpath
        file_data = {str(path.as_posix()): encode_file(path) for path in
                     to_encode}
    else:
        file_data = {str(path): encode_file(path) for path in to_encode}
    template = Path('script_template.py').read_text('utf8')
    Path('build/script.py').write_text(
        template.replace('{file_data}', str(file_data)),
        encoding='utf8')


if __name__ == '__main__':
    build_script()

script_template.py

script_template.py
import gzip
import base64
import os
from pathlib import Path
from typing import Dict


# this is base64 encoded source code
file_data: Dict = {file_data} # ←ここに置換しています


for path, encoded in file_data.items():
    print(path)
    path = Path(path)
    path.parent.mkdir(exist_ok=True)
    path.write_bytes(gzip.decompress(base64.b64decode(encoded)))


def run(command):
    os.system('export PYTHONPATH=${PYTHONPATH}:/kaggle/working && ' + command)


run('python setup.py develop --install-dir /kaggle/working')
run('python titanic_sample/main.py')

script.py

script.py
import base64
import gzip
import os
from pathlib import Path
from typing import Dict

# this is base64 encoded source code
file_data: Dict = {
    'titanic_sample/dataset.py': 'H4sIAHQXAl4C/6VVUW/aMBB+R+I/WLzE0VCA7Q0pD6grVbVp7QZvCEUmOahVx7FsB8Gm/fednYQktKs2Na3ks+/7Pp/Pd2avi5woZp8E3xGeq0Jb8ojT4WA4qKeyzNWZMEOkuqwpJjNcwX+VDQd7J2KeBTAto7zIQCQGBKSWF7IRtZpxmVgwNjFKcOs28DzLLZM8TQzLlYCotFyYhvTwLfmyuLv7euvQnxfrRfLj4WFNYh8iDaJowqUq7aTWCAjftxwCwgAJookGU5Q6BROEw8ENs3AoNE+ZSJbAbIleFNwEt/mO6WfIgjEJHlPBjHHWCk7Bdjioond5QmwbyISMvCdKzXGEKHe6V0HoqDGm3OX8ddQBZAY68QhjMHc1xf1lsCdKg9IFnsPQjFkWzocDgp+zN1WgESIES4FugpwJcAfYg7e2Y7KZjskMR0yZw8RrXULYlbhkYBvtuRCS0WAV/BMeT/JCImeK/kKBOcF9gxscZzh+x/Hj7zBixp4V0ErOfVzanviSaWgDkSrKgUna9YVvhLY4dMjdpRwyjjpvcZcs5+K84j+hc7BHptMnnH9o0s13K+Xnsy733ixEISvitHVEokg3r6nHPildVi2nAStTenJbAKJgWVUelja3XwNVhnePXqwY2imx8Irs9AxYWvUhBhFPo0+NlC9lDKGr1RZ+nSPHvMY0Ze938yCmD2CTtBAIHa1KfeRHyEZtQiqFtJAps3TjNxl7aaxPg50fLxk2b9gnXJV/7cSHxoLbqsxl1cnfWO5L/xF7GOQB9L1v6jVPn8E664btuHRtfbmfTBeK9qXGhJ24iWcvKqWfLH+rcwGySlW47aXJe1vnfNvQz0kj4MdNm7Fa4NQHVBG2qCa68IKuNvQvzd+xPe0xGkcmeDZuwnGGX2m27TzY9NRiKqNt3v//2gK8WO/S0/iDVOSJsfi8x9N3SRk8neX7c1yfM+w1Gn2ZvCr5+KTQ6zQi9Q+pvtnNYQcAAA==',
    'titanic_sample/predict.py': 'H4sIAHQXAl4C/31QwWrEIBC9B/IPspcYCNJLL4UU2kPPhR5LGcw6SYUYg06W3b+vRpPdbqEi6Pjec948bWbriEk3zNJ5LIuy0Olp1MM3DZ1h0rNx6CLSO2sYaZKTPoKXZh5RKEnSI7FNZaWC/Nakyi+d0RT1ZaGwZwMShH6LwYk8r5/KgoW1tnes3a2Il8x5XxFe3/KEVGr/hB+MVTjCLOn78B/NLjQvlA39ogeOD72zaj2izm9dHdLippV2HcRIPe3+8w93wyWMQ8PCPgOhp7pJdR3Yt2lt5OBuQ5LRDViHjNDQiVdrPYVQ0uC9HrGNBsQ1iCy6wOxQBdWKiFjoI/Fs5Y7D8+2ZPYjHWkhPlxm5nuhq7bP6WNxJn1BVX0GRBDsqyMLRn/jq5W/aDdOTwnP7JkePdQpS9wxgkgYBWNuyCiDGClDlXFPIZfEDh06KTakCAAA=',
    'titanic_sample/train.py': 'H4sIAHQXAl4C/5VUTY+bMBC9R8p/sHKBSCxN1dtKPqy0Pe+q3dtqZQ0wsG4xINuJklb97x1/AEmWRiocsPzezLzxPCPV0GvLQDcDaIPr1Xolw1Yrm3fbFIqBYW1TOKTWvWJWWuhkKQyoocW8AgsGLYtRjw8vD+Lb09NLxtoeKhFhF201yE4MYN8Zn3nsE9t4JC/NYUMsNHaZREDkuLfCmjVoBSnfK+ysSbf36xWjxzeiKXxsKn+InGePpNtzXg5VNSVJN6qvsPX1N7dod3dep5G/cJMxexqQ19SvzRjpgn1r+S7/EhNQlCE1MY//uExm1KHR7nXnaXNr/kjSo/DfjJ3GxVEcoJWV24mLEiw2vZYltKJGoFxoxqOguYVAqk/r/DFM42PeQP/Xs1CCL5adqyKp+1D0WrrGGjV2JfJJ6G0l/yFkGh4od/q/57xJX/zA0soDJvcsKWQH+pQE+M8Y520Q9YdRzPEhZ8aWNPvGBHVr+OuMTyfydk5FXfRkBbfPP+/OkG6vRNH35C/d77uKwN05jKDbkzC2HwbZNYFjiBQYl6byfcyuUq6T0RzRl1eXKGCTu8M1Mvm0EfDZQdNUaYc424ylp9ldHtu6gzz7GaRTslhtaYQU85p8VQXon1glGUueyxaMcavveEzeLud0cV34tb35LZvHBvi1Pflo06WgJcW3roTXmRs4oPDL1B/q/LPZhhnJmgnRgUIhGOcsEcJNTIgkjizMb736C/g+V6a2BQAA',
    'titanic_sample/utils.py': 'H4sIAHQXAl4C/yXNsQqDMBCA4T2Qdwg4OOnVLqVCB6FFRFFw6SimBD2a5II5C337tnT8v+VHF2hjRVEKKRK1ModYAizI667zBzmwFPYVPTznZbEmQ2c4Ox6KM2hLGtwc2WzwU9gZbczDO+mKkxRDP7VVXXe3Umkiqy4q/fd0H8a26evp2oypQv+d58a/cCMvxQet+8gdkAAAAA==',
    'titanic_sample/__init__.py': 'H4sIAHQXAl4C/wMAAAAAAAAAAAA=',
    'setup.py': 'H4sIAHQXAl4C/0srys9VKE4tKS0oyc/PKVbIzC3ILyqBiPBy8XKBGRq8XApAkJeYm2qrXpJZkpiXmRxfnJhbkJOqrgORK0hMzk5MTy22jUZXEAtUocnLBQBmZqg3aQAAAA=='}

for path, encoded in file_data.items():
    print(path)
    path = Path(path)
    path.parent.mkdir(exist_ok=True)
    path.write_bytes(gzip.decompress(base64.b64decode(encoded)))


def run(command):
    os.system('export PYTHONPATH=${PYTHONPATH}:/kaggle/working && ' + command)


run('python setup.py develop --install-dir /kaggle/working')
run('python titanic_sample/main.py')

参考

77
62
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
77
62