Top View


Author shimao

Kaggleコンペで惨敗したけど、最高に楽しかった話

2019/12/25

2020/1/8追記

ようやく結果が出まして、なんと165位でシルバーメダルを獲得できました⭐ タイトル詐欺みたいになってしまい、申し訳有りません。まさかのPublicな順位から1,200位近くもアップするとは思わず…。

今回は実力よりも運の要素が大きかったとは思いますが、自身初のメダルなので素直に嬉しいです。そして、楽しかった!!

本記事の対象者

  • Kaggle好きな人
  • 機械学習・Kaggle始めようと思っている人、興味がある人

筆者の情報

  • 参考までに軽く自己紹介させてください。
  • Webエンジニア歴8年(Perl -> PHP)、機械学習エンジニア歴2年弱(Python)、Kaggle歴1年くらい。
  • 業務では画像系の深層学習多め。表データは業務ではあんまり触ってない。
  • Kaggleで参加したことがあるコンペは雑多に10個くらい。これまではKernel見て勉強してたまにsubmitもするくらい。上位を目指してコンペに取り組むことは2回目です。

参加まで

目的設定

  • Kaggleに参加して約1年、そろそろメダルほしい。銅メダルを取るつもりで参加してみようと思いました。
  • ご存じない方向けに簡単に説明しますと、Kaggleのコンペティションで上位入賞するとメダルが貰えます(金・銀・銅)。メダルを集めるとExpert、Master、GrandMasterといった称号を貰えます。
    • ちなみにGrandMasterは世界でも200人弱、日本で約10人という超々狭き門です。
    • 銅メダルの取得条件は、(コンペティションにより変わりますが)上位10%入賞です。
    • 詳細な条件はこちらに記載があります。
  • 自由にやりたかったのでソロでの参加です。(チームを組むこともできます)

コンペ選定

  • 経験不足なので色々試してみるしかない。それを踏まえるとあまりデータが大きいコンペは厳しいかも。
  • 12月2週ごろからは、予定無いのでガッツリ取り組めそう。
  • 以上の2点から12月後半締切のASHRAEコンペを選択。
    • ASHRAEコンペは表データなので、画像系・自然言語系のコンペより学習・推論が早そうという安直な考えです。
  • 表データを扱ってみたいという思いもありました。
  • 参加したのは11月上旬くらいだった気がします。

コンペ概要紹介

  • 世界各国にあるビルにおける消費エネルギーを予測するという回帰タスクです。
  • 冷水、電気、温水、蒸気の各メーターでの予測を行います。
  • 1年分のデータを学習して、次の約1.5年分の消費エネルギーを予測します。
    • 学習データが約2000万行、テストデータが約4000万行です。
  • 評価指標はRMSLE(Root Mean Squared Logarithmic Error)です。

コンペ中に取り組んだこと

全体スケジュール・掛けた時間

  • 11月中はローギアな感じ。
    • 福岡Kaggleもくもく会を主催しているので、毎週最低2時間以上の時間を確保。
    • Public NotebookをいくつかForkさせて頂いて、EDA・Feature Engineering・GBDT系のライブラリ(lightGBM・CatBoost)の基本的な使い方を勉強しました。
  • 12月1週は別件でバタバタしてほぼ動けてない…。
    • なぜかAWSの資格試験をこの週に設定してしまってました…。
  • 12月2,3週目でガッツリ目で取り組みました。
    • 平均すると3時間/日くらいかと思います。
    • 締め切り3日前くらいは6時間/日くらい。

リークについて

  • もはや「よくあること」な感がありますが、本コンペも途中でリークが発覚しました。
  • Discussionにて運営側が「Privateなテストデータにはリークの影響が無いようにする」と言っていたので、それを信じて自分はリークは気にせず参加することにしました。
    • 「評価データセットに含まれていなくても、リークデータがモデルの学習に有益なのではないか?」という議論も起きていましたが、「まぁとりあえず気にせずやってみるか。それでどうなるかな。」という気持ちでした。

取り組み詳細

時系列に沿って書いていきます。

1. 最初のSubmit:各メータ毎にLightGBMモデルを作成

  • こちらのPublic NotebookをForkさせていただきました。
  • 各メーターごとに予測するのがシンプルで良さそうだと感じたので、こちらにしました。Vote数が多かったのもあります。
  • 学習データから異常値を除く処理がありましたが、ベースライン的なスコアが知りたくって最初はこの処理をなくしました。
  • 特徴量作成はpandas.DataFrame.rollingを使用した、天候情報の移動平均・最大・最小・標準偏差を追加しています。(後述します)
  • Public Scoreが1.22くらいだったと思います。

2. 独自データセット作成

  • そこそこのデータ量があるため、CSVからのデータの読み込みに多少時間がかかっていました。
    • 全部のCSVを読み込んだときの %%time の実行結果が以下です。
CPU times: user 39.7 s, sys: 10.6 s, total: 50.3 s
Wall time: 49.3 s
  • また、データの前処理(クリーンナップ)にもそこそこ時間掛かっていました。
  • このタイミングでKaggleのノートブックに独自データを追加する方法に気づいたので、データセット作成用のノートブックを先に作ることにしました。
    • ノートブック編集中画面にて「File」->「Add or upload data」->「Kernel Output Files」->「Your Work」で自分のノートブックが表示されるので追加したいデータを出力したノートブックを選択すればOKです。
  • 「CSV -> Feather形式への変更」・「データのクリーンナップ」・「Feature Engineering」の3つを作りました。
  • このタイミングでもう12月に入っていて、Public Kernelに色々な前処理が公開されていたので良さそうだと思った以下を取り入れました。
    • 学習データから不要なデータを除外。
      • 明らかな異常値、電気メータが0になっているデータ(電気を使用しない日はないはず)、メータ毎の典型的な利用時期に一定期間0になっているデータ。
    • 気象情報・ビルのメタ情報の欠損値埋め処理
    • 気象情報を現地時間に変換

3. メータ毎にLightGBMモデルを作るノートブックで再提出

  • 独自データセットを使って学習し直した所、Public Scoreが1.089になりました。
  • この時点で3000人中1500位くらいだった記憶です。

4. 全メータのデータを使うシンプルなLightGBMモデルで実験

  • Public Kernelでメータ毎にデータセット・モデルを分けないシンプルなLightGBMモデルが出てて、スコアも良さそうだったのでこれでやってみた所、Public Scoreが1.089でした。
  • メータ毎にLightGBMモデルを作る場合と同値だったのでこちらの方がシンプルかなと思って、こちらを使うことにしました。
  • この時点で説明変数の数次第でKaggleのノートブックのメモリ(16GB)だと足りなくなっていたので、AWSのEC2に実験環境を移動させました。
    • 最終的にm5a.8xlargeを使ってました。

5. lightGBMのハイパーパラメータチューニング

  • この時点で残り3日くらいだったので、いまのモデルのパイパーパラメータのチューニングに集中することにしました。
  • こちらの記事に書いたのですが、BayesianOptimizationを使いました。
  • これによりPublicScoreが1.081になりました。順位は3500人中1300位くらいだったと思います。

6. Catboostを使ってみる

  • ハイパーパラメータチューニングや特徴量選択をやっても中々スコアが上がらずだったので、最後Catboostに切り替えてみました。
  • 結果、Public Scoreが1.2くらいであまり良くなかったです。(これはハイパラチューニングできてません)

勉強になった処理の抜粋

せっかくなので個人的に勉強になった処理をいくつか紹介させていただきます。

CSV -> Featherへの変換

import pandas as pd

train_df = pd.read_csv('train.csv')
train_df.to_feather('train.feather')
  • CSVだとWall time: 49.3 sだった読み込み時間が、FeatherだとWall time: 13.4 s になりました。
  • こちらの記事によると「Apache Arrowのpythonラッパー」のようです。

欠損している日付のレコードを追加する処理

import datetime
import numpy as np
import pandas as pd

start_date = df['timestamp'].min()
end_date = df['timestamp'].max()
total_hours = int(((end_date - start_date).total_seconds() + 3600) / 3600)
hours_list = [np.datetime64(end_date - datetime.timedelta(hours=x)) for x in range(total_hours)]

exist_hours = np.array(df['timestamp'])
new_rows = pd.DataFrame(np.setdiff1d(hours_list, exist_hours), columns=['timestamp'])

df = pd.concat([df,new_rows])
df = df.reset_index(drop=True)
  • np.setdiff1d()を初めて知りました。これまでは愚直にFor文書いてました。まだ計測はしていないのですが、高速に動作する印象です。

pandas.DataFrame.rollingによるラグ特徴量作成

def add_lag_feature(df, window=24):
    group_df = df.groupby('location_id')
    cols = ['temperature', 'sea_pressure']
    
    rolled = df[cols].rolling(window=window, min_periods=0)
    lag_mean = rolled.mean().reset_index().astype(np.float32)
    lag_max = rolled.max().reset_index().astype(np.float32)
    lag_min = rolled.min().reset_index().astype(np.float32)
    lag_std = rolled.std().reset_index().astype(np.float32)
    for col in cols:
        df[f'{col}_mean_lag{window}'] = lag_mean[col]
        df[f'{col}_max_lag{window}'] = lag_max[col]
        df[f'{col}_min_lag{window}'] = lag_min[col]
        df[f'{col}_std_lag{window}'] = lag_std[col]

add_lag_feature(df, window=72)
  • dfの中身の説明がなく分かりにくいと思いますが、天候情報のラグ特徴量作成の処理です。
    • 天候情報はlocation_id毎に決まっている場合です。

lightGBMにおける時間情報の特徴量化

df["timestamp"] = pd.to_datetime(df["timestamp"])
df["hour"] = df["timestamp"].dt.hour
df["month"] = df["timestamp"].dt.month
df['weekend'] = (df["timestamp"].dt.weekday < 5).astype(int)
df["dayofweek"] = df["timestamp"].dt.dayofweek
  • 単純に知らなかったんですが、時間情報をhour、monthなどの特徴量に分解して使うようです。面白いですね。

終わっての感想

  • ツラツラと書いてて伝わっているか不安なのですがこんな感じのライブ感といいますか、ガヤガヤしている感じがすごく楽しかったです。
    • 締め切り5日前くらいから寝る前に学習回して(4時間とか掛かるので)、起きてSubmitして結果に一喜一憂するみたいな感じでした。
    • ちなみに最終的に使っていたAWSのm5.8xlargeが$1.536/時間で、最初は寝るのが怖かったのですがすぐに慣れました。笑
  • 試行錯誤の末に実験して、結果がすぐ出る環境って中々無いと思います。
  • 更に世界中の人が取り組んでてて、解法も公開されるので刺激を受けるし、スコアを上げるためにできることはいくらでもあるという状況なので、やる気次第でいくらでも成長できそうです。
  • 今回はソロでしたら、気心のしれたメンバーでチームを組んだらもっと楽しいと思います。ソロ銅メダル取れたら次はチームを組んでみようと思います。
    • ちなみに今回の最終結果はまだ出てませんが、上位10%にはどう転んでも入れないと思うので銅メダルは取れてないと思います😭

まとめ

  • メダル取れなくても、Kaggleは楽しいですし、確実に成長できます!
  • もし興味がある人は是非コンペに参加してみてください!

以上です。最後までお付き合いくださり、ありがとうございました!

shimao

shimao

福岡在住。機械学習エンジニア。AWS SAP・MLS。趣味はKaggleで、現在ソロ銀1。福岡でKaggleもくもく会を主催しているので、どなたでもぜひご参加ください。