Tak's Notebook

Kaggle, Machine Learning, Engineering

Kaggle API とシェルスクリプトで Kaggle のデータ周りを簡単に操作したい

はじめに

Kaggle API を使うと便利にアレコレをダウンロードしたりアップロードしたりできますが、コマンドオプションが覚えられなかったり、設定をアレコレ書くのが難しいので 出来る限り シェルスクリプトを使って簡単にしたものを整理しておこうと思い、書きました。

事前準備

Kaggle API が使える前提です。詳細はドキュメントを参照してください。

github.com

ディレクトリ構成

大体以下のようになっている前提で以下のスクリプトを書いています。

.
├── input
│   └── tabular-playground-series-jan-2021
├── kaggle-notebook.py
├── kernel-metadata.json
├── output
│   ├── confusion
│   ├── feature
│   ├── importance
│   ├── log
│   ├── model
│   ├── optuna
│   ├── result
│   └── submission
├── run.py
├── scripts
│   └── kaggle
├── src
│   ├── __init__.py
│   ├── __pycache__
│   ├── data
│   ├── exp
│   ├── fe
│   ├── features
│   ├── kfold.py
│   ├── models
│   ├── tests
│   ├── types.py
│   └── utils
└── transform-kaggle-notebook.sh

コンペデータセットのダウンロード

コンペ名だけベタ書きして実行すると input ディレクトリ以下に $COMPETITION_NAMEディレクトリが新たに生成され、そのディレクトリ以下にデータセットが展開される。

$COMPETITION_NAME が空文字だとエラーを吐くようにしています。

#!/bin/bash
COMPETITION_NAME="tabular-playground-series-jan-2021"  # Fill comepetiton name

COMPETITION_PATH="input/${COMPETITION_NAME}"
if [ -n "$COMPETITION_NAME" ]; then
  echo ${COMPETITION_NAME}
  kaggle competitions download -c ${COMPETITION_NAME} -p .
  mkdir -p ${COMPETITION_PATH}
  unzip ${COMPETITION_NAME}.zip -d ${COMPETITION_PATH}
  rm ${COMPETITION_NAME}.zip
else
  echo "COMPETITION_NAME should be provided"
  exit 1
fi

コンペにサブミット

こちらも事前にコンペ名だけベタ書きしておく必要があります。実験ごとにサブミットファイルが生成されているのと、CVのスコアがログにあることを前提にしています。

#!/bin/bash
COMPETITION_NAME="tabular-playground-series-jan-2021"  # Fill comepetiton name

RUN_NAME=$1
FILEPATH=./output/submission/${RUN_NAME}.csv
MESSAGE=$(cat ./output/log/result.log | grep ${RUN_NAME} | tail -n 1)
echo ${RUN_NAME}
echo filepath: ${FILEPATH}
echo message: ${MESSAGE}

kaggle competitions submit -c ${COMPETITION_NAME} -f ${FILEPATH} -m "${MESSAGE}"

自前データセットをアップロード

Kaggle Dataset にDATASE_DIR 以下をアップロードしようとしています。

kaggle datasets init で dataset-metadata.json を作成し、その json ファイルを書き換えて kaggle datasets create するとデータセットがアップロードされます。

ここでは手元で作っていた特徴量のデータセットをアップロードすることを想定して書いています。json ファイルの書き換えは sed コマンドで無理やり行っています。

ドキュメントを見ると もう少し詳細に説明等を書けるようですが、自前で使う分にはこれで十分そうです。

#!/bin/bash
DATASE_DIR="output/feature"
TITLE="tak-tps-jan2021-feature"
SLUG="tak-tps-jan2021-feature"

kaggle datasets init -p $DATASE_DIR
sed -i "s/INSERT_TITLE_HERE/$TITLE/g" $DATASE_DIR/dataset-metadata.json
sed -i "s/INSERT_SLUG_HERE/$SLUG/g" $DATASE_DIR/dataset-metadata.json
kaggle datasets create -p $DATASE_DIR

github.com

コードをアップロード

Kaggle Code に自身コードをアップロードしようとしています。

コードの場合も kaggle kernel init で kernel-metadata.json が作成され、その json ファイルを書き換えて kaggle kernel push するとコードがアップロードされます(同時に実行もされる)。

タイトルのユニーク性を保ちたいので実験名を引数に取るようにしています。自分の場合は exp00 のような命名規則で書いているので それを引数で要求しています。ベタ書きで書かないといけないところも多く もう少し改善の余地がありそうです。

作成中は sed コマンドのエスケープ周りなどにハマりました。

#!/bin/bash
function sed_kernel_metajson() {
    sed -i "s*${1}*${2}*g" kernel-metadata.json
}

if [ ! $1 == "exp*" ] & [ $# != 1 ]; then
    echo "argument must be string which begins with exp (e.g. exp00), but actual: $1"
    exit 0
fi

TITLE="tak-tps-jan2021-"$1  # Fill expmeriment number
KERNEL_SLUG=${TITLE}
CODE_FILE_PATH="kaggle-notebook.py"
LANGUAGE="python"
KERNEL_TYPE="script"

kaggle kernels init
sed_kernel_metajson "INSERT_KERNEL_SLUG_HERE" $KERNEL_SLUG
sed_kernel_metajson "INSERT_TITLE_HERE" $TITLE
sed_kernel_metajson "INSERT_CODE_FILE_PATH_HERE" $CODE_FILE_PATH
sed_kernel_metajson "Pick one of: {python,r,rmarkdown}" $LANGUAGE
sed_kernel_metajson "Pick one of: {script,notebook}" $KERNEL_TYPE
sed_kernel_metajson '\"enable_gpu\": \"false\",' '\"enable_gpu\": \"true\",'
sed_kernel_metajson '\"enable_internet\": \"false\",' '\"enable_internet\": \"true\",'
sed_kernel_metajson '\"dataset_sources\": \[\],' '\"dataset_sources\": \[\"takaishikawa/tak-tps-jan2021-feature\"\],'
sed_kernel_metajson '\"competition_sources\": \[\],' '\"competition_sources\": \[\"tabular-playground-series-jan-2021\"\],'
kaggle kernels push

github.com

終わりに

改善できる箇所は十分ありそうだし、そもそももっと単純な方法がありそうです。あるいは config とかを置いておくとよしなに書き換えてくれるみたいな機能あったりするのかなと思いました。