概要
最近のKaggleのコンペティションでは、コンペ終了直前に上位者が激強Kernelや激強DiscussionでLeaderboardを荒らすことがたまにあります。
Kernelが公開されてからなるべく早く反応できるよう、Line Notifyを利用して、通知システムを作ろうと思いました。今回はu++さんのKaggle APIとLINE APIを用いたKernelの新規投稿を通知する仕組みの構築を参考に、システムを構築しました。
通知例
全体の流れ
- Kaggle APIを使ってKernelのリストを取得する
- 取得したKernelのリストをGoogleスプレッドシートに保存する
- 新しいKernelが増えている場合はLine Notifyで通知を行う
この記事では、コードを部分的に紹介するので、全てのコードが欲しい場合はGitHubから取っていってください。
Kaggle API
今回はKaggleが公式で提供しているKaggle APIを使用します。使い方はGitHubのREADMEに詳しく書いてあります。
https://github.com/Kaggle/kaggle-api
pip install kaggle
でインストールできます。
APIを使用するには、更にAPI Tokenを設定する必要があります。
Kaggleの自分のプロフィールページのAccountにあるCreate New API Token
を押すとjsonファイルがダウンロードされます。
ダウンロードされたjsonファイルには、自分のユーザ名とトークンが記述されています。このjsonファイルを~/.kaggle
というディレクトリを作って入れるだけでAPIが使えるようになります。
今回使用するのは、Kernelsに関するAPIです。例えば、santanderのコンペのカーネルを新しい順に表示したい場合、以下のようなコマンドを入力します。
$ kaggle kernels list --competition santander-customer-transaction-prediction --sort-by 'dateCreated'
ref title author lastRunTime totalVotes
------------------------------------------------------------ -------------------------------------------------- ------------------ ------------------- ----------
kanugeete/5-fold-classification-on-a-simple-neural-network 5-fold Classification on a Simple Neural network kanugeete 2019-04-09 18:54:34 0
darbin/logit-for-200-pca-features Logit for 200 PCA features Ilya Khristoforov 2019-04-09 06:01:56 4
tasnimfatima/lgbm-with-no-augmentation LGBM with no Augmentation Tasnim 2019-04-09 05:55:02 1
mhviraf/what-a-decision-tree-cannot-see What a decision tree cannot see? mhviraf 2019-04-09 06:43:10 52
elcaiseri/simple-gnb-and-pipeline-model-with-eda Simple gnb And pipeline Model With EDA Kassem 2019-04-08 23:18:18 6
manishks/kernelee24d12c85 kernelee24d12c85 manish kumar singh 2019-04-08 13:16:40 0
sandeepkumar121995/santander-customer-transaction-prediction Santander Customer Transaction Prediction Sandeep Kumar 2019-04-08 11:19:28 5
subodanirodrigo/xgboost-for-santander-customer-transaction XGBoost for Santander Customer Transaction Subodani Rodrigo 2019-04-08 09:59:12 0
leonyang/blending-all blending_all leonyang 2019-04-09 02:04:12 0
danmoller/funny-pattern-with-non-perfectly-round-numbers Funny pattern with non-perfectly round numbers Daniel Möller 2019-04-08 08:34:07 5
kmezhoud/a-simple-xgboost-application A simple xgboost application Karim Mezhoud 2019-04-09 19:06:45 8
vikasmalhotra08/feature-binning-using-bayesian-blocks Feature Binning using Bayesian Blocks Vikas Malhotra 2019-04-07 16:17:25 8
pranaydugar/dense-net-2-gaussian-difference-features Dense Net 2 (Gaussian Difference Features) PranayDugar 2019-04-07 11:48:45 1
rsaund/sant-model-check sant_model_check RSaund 2019-04-07 11:10:02 0
fuhang/weighted-kernel-naive-bayes-with-lasso-elimination Weighted Kernel Naive Bayes with Lasso Elimination Horizon@AI酱 2019-04-08 10:41:07 56
stevexyu/kfold-convolutional-neural-network Kfold Convolutional Neural Network Stephen 2019-04-09 15:02:48 21
lzsraja/kernal-fe-gp-lgbm-keras Kernal_fe(GP)+LGBM+Keras Zhusong Liu 2019-04-09 18:56:53 0
maheshak04/simpl Simpl MaheshKulkarni 2019-04-07 10:53:22 0
yoheiii/bayesian-optimization-for-santandar Bayesian Optimization for Santandar YOHEI 2019-04-07 06:28:57 7
dmacgirr/naive-bayes-normalized Naive Bayes - normalized Dan MacGirr 2019-04-06 15:31:40 0
これをいい感じに整形します。ぱっと見スペース2つとかでsplitすれば良さそうなので、こんな感じで整形します。とりあえず、記事のurlとタイトルだけ保存します。
import subprocess
proc = subprocess.run("kaggle kernels list --competition santander-customer-transaction-prediction --sort-by 'dateCreated'", shell=True,stdout = subprocess.PIPE, stderr = subprocess.PIPE)
s = proc.stdout.decode("utf8")
kernels = []
rows = s.split("\n")
# スペース2つでlist化、先頭/末尾のスペースを削除
for i in range(len(rows )):
row = rows[i].split(" ")
row = [x.strip(" ") for x in row if x]
kernels.append(row)
# ヘッダとフッタを除外
kernels = kernels[2:-1]
# 使用するパラメータ
use_params = ["url", "title"]
# 古い順にする
kernels = kernels[::-1]
# dictに変える
tmp = [{} for i in range(len(kernels))]
for i, kernel in enumerate(kernels):
for j, param in enumerate(use_params):
tmp[i][param] = "https://kaggle.com/" + kernel[j] if param == "url" else kernel[j]
kernels = tmp
print(kernels)
結果を見るといい感じに分けることができています。
[{'url': 'https://kaggle.com/dmacgirr/naive-bayes-normalized',
'title': 'Naive Bayes - normalized'},
{'url': 'https://kaggle.com/yoheiii/bayesian-optimization-for-santandar',
'title': 'Bayesian Optimization for Santandar'},
{'url': 'https://kaggle.com/maheshak04/simpl', 'title': 'Simpl'},
{'url': 'https://kaggle.com/lzsraja/kernal-fe-gp-lgbm-keras',
'title': 'Kernal_fe(GP)+LGBM+Keras'},
{'url': 'https://kaggle.com/stevexyu/kfold-convolutional-neural-network',
'title': 'Kfold Convolutional Neural Network'},
{'url': 'https://kaggle.com/fuhang/weighted-kernel-naive-bayes-with-lasso-elimination',
'title': 'Weighted Kernel Naive Bayes with Lasso Elimination'},
{'url': 'https://kaggle.com/rsaund/sant-model-check',
'title': 'sant_model_check'},
{'url': 'https://kaggle.com/pranaydugar/dense-net-2-gaussian-difference-features',
'title': 'Dense Net 2 (Gaussian Difference Features)'},
{'url': 'https://kaggle.com/vikasmalhotra08/feature-binning-using-bayesian-blocks',
'title': 'Feature Binning using Bayesian Blocks'},
{'url': 'https://kaggle.com/kmezhoud/a-simple-xgboost-application',
'title': 'A simple xgboost application'},
{'url': 'https://kaggle.com/danmoller/funny-pattern-with-non-perfectly-round-numbers',
'title': 'Funny pattern with non-perfectly round numbers'},
{'url': 'https://kaggle.com/leonyang/blending-all', 'title': 'blending_all'},
{'url': 'https://kaggle.com/subodanirodrigo/xgboost-for-santander-customer-transaction',
'title': 'XGBoost for Santander Customer Transaction'},
{'url': 'https://kaggle.com/sandeepkumar121995/santander-customer-transaction-prediction',
'title': 'Santander Customer Transaction Prediction'},
{'url': 'https://kaggle.com/manishks/kernelee24d12c85',
'title': 'kernelee24d12c85'},
{'url': 'https://kaggle.com/elcaiseri/simple-gnb-and-pipeline-model-with-eda',
'title': 'Simple gnb And pipeline Model With EDA'},
{'url': 'https://kaggle.com/mhviraf/what-a-decision-tree-cannot-see',
'title': 'What a decision tree cannot see?'},
{'url': 'https://kaggle.com/tasnimfatima/lgbm-with-no-augmentation',
'title': 'LGBM with no Augmentation'},
{'url': 'https://kaggle.com/darbin/logit-for-200-pca-features',
'title': 'Logit for 200 PCA features'},
{'url': 'https://kaggle.com/kanugeete/5-fold-classification-on-a-simple-neural-network',
'title': '5-fold Classification on a Simple Neural network'}]
次に取得したカーネルのデータをGoogleスプレッドシートに保存します。
Googleスプレッドシートに保存する
PythonでGoogleスプレッドシートをいじる場合、この記事がわかりやすいと思います。
https://qiita.com/akabei/items/0eac37cb852ad476c6b9
同じような手順でやってみます。
プロジェクト作成
コンソールからプロジェクトを作成します。
https://console.developers.google.com/cloud-resource-manager
プロジェクト名は適当にkaggle-api-notify
にしておきます。
プロジェクトにAPIを追加する
左上のナビゲーションメニューから、APIとサービス
に移動し、Google DriveとGoogle SheetsのAPIを有効にします。
サービスアカウントキー作成
APIとサービス
から認証情報
に移動し、認証情報を作成、サービスアカウントキーを選択します。
サービスアカウント名を適当に決め、役割をProject
の編集者
にします。
作成のボタンを押すと、アカウントの情報が記載されたjsonファイルがダウンロードされます。
このファイルで必要なのは、client_email
の項目です。今回の場合、kaggle-api-account@kaggle-api-notify.iam.gserviceaccount.com
のようなアカウント名@プロジェクト名.iam.gserviceaccount.com
の形式で保存されます。
{
"type": "service_account",
"project_id": "kaggle-api-notify",
"private_key_id": "XXXXXX",
"private_key": "-----BEGIN PRIVATE KEY-----...,
"client_email": "kaggle-api-account@kaggle-api-notify.iam.gserviceaccount.com",
...
}
読み込んだデータを保存しておく、スプレッドシートを作成します。
jsonファイルから取得したclient_email
をスプレッドシートの右上の共有
から編集者として追加します。
これでスプレッドシートの設定は完了です。
スプレッドシートをpythonで操作する場合、gspread
をインストールする必要があります。
gspreadのリファレンス
https://gspread.readthedocs.io/en/latest/
今回使用したのは、以下のメソッドです。
メソッド | 効果 |
---|---|
get_all_values | 全ての値をlist形式でもらえる |
update_acell | 単一セルの編集 |
update_cells | 複数セルの編集 |
注:update_cells
の引数では、編集する領域を設定し、セル単位で値を入れないといけないので少し面倒
get_all_values
で保存してあるデータを読み込み、今Kaggle APIで入手した最新の投稿と保存されている最新のKernelが違う場合、スプレッドシートの更新とLineの通知を入れるようにしました。
Line Notifyで通知
Line Notifyというサービスを使用すると、プログラムからLineを簡単に送ることができます。
上のサイトからLineアカウントでログインし、右上のマイページから、アクセストークンを発行することができます。
トークンを発行する
ボタンを押すと、トークン名(通知の際に表示される名前)と通知を入れたいトークルームを選択することができるので、個人用のトークルームを設定します。チームで参加している場合、チームのトークルームを作成し、通知を入れるといいかもしれません。
pythonファイルからは以下のように呼び出します。YOUR TOKEN
の部分は発行したトークンを入力します。
https://upura.hatenablog.com/entry/kaggle-notification より引用
def line(Me):
line_notify_token = 'YOUR TOKEN'
line_notify_api = 'https://notify-api.line.me/api/notify'
message = '\n' + Me
payload = {'message': message}
headers = {'Authorization': 'Bearer ' + line_notify_token}
line_notify = requests.post(line_notify_api, data=payload, headers=headers)
Kaggleの通知をする場合、line()
の引数に1.で取得したカーネルのタイトルとURLを入力し、通知を行います。
運用
常に通知を受け取れるようにするには、上記の処理をpython等で書いて、n分間隔で実行する必要があります。
私は5分間隔でチェックするようにしましたが、コンペ終盤になると1分間隔とかにした方が良いのかもしれません。
常に起動している必要があるので、Raspberry Piのシェル上で実行するようにしました。以下のように実行しました。
#!/bin/bash
while true
do
python3 kaggle_notify.py
sleep 300
done
この記事の中で一番面倒だった作業がRaspberry Pi上でPythonの環境構築をする部分でした。詳しくはまたの機会に。
Discussionについて
今のKaggleでは、Discussionの投稿やコメントが削除できるので、一部の人だけが有効な情報を手に入れることがあります。今(2019年4月)の段階では、Kaggle APIからDiscussionを取得できないので、
https://www.kaggle.com/forums/130117/topics.json?sortBy=recent&group=all&page=1&pageSize=20&category=all
のようなリクエストを飛ばし、json形式で取得するしかありません。しかし、Kaggleの利用規約の4章h,i節あたりがグレーな気がしています。(Kaggle APIを叩いて情報収拾するのもグレーな気がしますが...)
Kaggle APIにDiscussionを取得する機能が追加されるか、上記のようなリクエストを飛ばして良いのがわかったらDiscussionの通知/保存機能も検討したいと思います。
GitHub
いろいろ整理中なのでしばしおまちを...
https://github.com/reo11/Kaggle-notify
とりあえず作りました。READMEとこの記事を読めば使えると思います。
使い辛かったり、バグがあったらプルリクお願いします。