Help us understand the problem. What is going on with this article?

Kaggleに登録したら次にやること ~ これだけやれば十分闘える!Titanicの先へ行く入門 10 Kernel ~

事業会社でデータサイエンティストをしているu++です。普段ははてなブログ1でKaggleといったデータ分析に関する記事などを定期的に書いています。

Kaggleでは「PetFinder.my Adoption Prediction2というコンペで優勝(チーム)し、「Santander Value Prediction Challenge3というコンペで銀メダルを獲得(個人)しました。「Kaggle Master」と呼ばれる称号4を得ており、Kaggle内ランクは、約11万人中最高229位です5

本記事では「Kaggleに登録したら次にやること」と題して、Kaggleに入門したい方に向けて次のようなコンテンツを掲載します。

  • Kaggleの概要
  • 環境構築不要な「Kernel」の使い方
  • 入門 10 Kernel
    • 1. まずはsubmit! 順位表に載ってみよう
    • 2. 全体像を把握! submitまでの処理の流れを見てみよう
    • 3. ここで差がつく! 仮説に基づいて新しい特徴量を作ってみよう
    • 4. 勾配ブースティングが最強?! いろいろな機械学習アルゴリズムを使ってみよう
    • 5. 機械学習アルゴリズムのお気持ち?! ハイパーパラメータを調整してみよう
    • 6. submitのその前に! 「Cross Validation」の大切さを知ろう
    • 7. 三人寄れば文殊の知恵! アンサンブルを体験しよう
    • 8. Titanicの先へ行く①! 複数テーブルを結合してみよう
    • 9. Titanicの先へ行く②! 画像データに触れてみよう
    • 10. Titanicの先へ行く③! テキストデータに触れてみよう
  • メダルが獲得できる開催中のコンペティションに参加しよう
  • さらなる学びのために

Kaggleの入門というと、チュートリアルとして用意されている「Titanic : Machine Learning from Disaster6が有名です。本記事でも最初はTitanicを題材に話を進めます。しかし個人的には、メダルが獲得できる開催中のコンペティションに参加してこそ、歴戦の猛者たちと切磋琢磨することができ、学びや楽しみも大きいのではないかと感じています。

本記事を経て、少しでも多くの方が開催中のコンテストに参加してくださったら嬉しいです。なお本記事のタイトルは@drkenさんの競技プログラミング「AtCoder」入門記事7に倣いました。本記事執筆の背景はポエム要素を含むので、自身のはてなブログに掲載しました8

Kaggleの概要

Kaggle9とは、主に機械学習モデルを構築するコンペティションのプラットフォームです。

image.png
(DeNAでデータサイエンスチーム(通称Kaggle枠)のマネージャを務める原田さんの資料10より引用)

キスモ取締役(当時)の大越さんの資料11やDeNAの鈴木さんの資料12も、Kaggleとは何かが簡潔にまとめられています。原田さんの資料と合わせて、最初にご覧いただくのがオススメです。

環境構築不要な「Kernel」の使い方

Kernelとは?

Kaggleには、Kernelと呼ばれるブラウザ上の実行環境が用意されています。言語としては現在、Python3とRが対応しています。それぞれScript形式かNotebook形式を選択可能です。

image.png

Kernelには、機械学習モデルの構築に必要なさまざまなパッケージがあらかじめインストールされており、初心者がつまづきやすい環境構築が必要ありません。GPUも使用可能です。一般的なノートパソコン以上の性能が自由に使える環境が整っています。

image.png

本記事では、このKernelを用いたKaggle入門コンテンツを提供します。自分で手を動かしながらKaggleのエッセンスを学べるような10つのKernelを用意しました。言語としてはPython3、形式はNotebookを選択します。

Kernelの使い方

ここでは、Kernelの具体的な使い方を解説します。次の章にある「1. まずはsubmit! 順位表に載ってみよう」のリンクをクリックして、作業してみてください。

リンクを開くと、次のようなKernelのページが開きます。まずは右上の「Fork」をクリックしてください。

※ 2019年6月ごろに「Fork」が「Copy and Edit」に変更されました。また8月時点では「Kernel」という語が「Notebook」に置き換えられつつあります。2020年4月ごろの変更に対応したNotebookの操作動画をYouTubeに公開済です。

image.png

すると、自分で編集できる画面に遷移します。ForkしたKernelは元のKernelとは別物なので、自分の好き勝手に編集して問題ありません。

image.png

Kernelは「Public」「Private」という公開設定があります。現在の状況は右のサイドバー>Settings>Sharingから確認できます。例えば、私のKennelは皆さんに使ってもらうために「Public」になっています。デフォルトは「Private」なので、意図的に操作しない限りは「Public」にはなりません。安心して自由に記述することが可能です。

Kernelはいくつものセルに分割されています。セルには2種類あり、1つは説明文などを記述する「Markdown」、もう1つはPythonのコードを記述する「Code」です。次の図で言うと、上のセルがMarkdown、下のセルがCodeです。

image.png

セルは自由に追加・削除ができます。新規追加は上の画像で出ている「+Code」「+Markdown」から可能で、移動・削除はセルを選択した状態でメニューバーの「Edit」「Insert」から操作できます。

Codeセル内でSHIFT+ENTERすると、個々の単位でプログラムを実行できます。段階的にプログラムを処理できるので、個々のセルで何が起きているかが理解しやすいかと思います。

Kernelの操作方法やショートカットなどは、Jupyter Notebookとほぼ同様です。さらなる便利な使い方の詳細が知りたい場合は「Jupyter Notebook 使い方」などで調べると良いでしょう。

入門 10 Kernel

ここでは、自分で手を動かしながらKaggleのエッセンスを学べるような10つのKernelを掲載します。

本記事内では理論的な面を解説し、各テーマに対応するKernelで実践していただく構成になっています。本記事を読むだけでも理解できるような構成にしていますが、ぜひKernelをForkしてご自身で操作してみてください。

1. まずはsubmit! 順位表に載ってみよう

このKernelでは、Kaggleでの提出(submit)の方法を学びます。

Kaggleでは、いくつかの方法で自分が作成した機械学習モデルの予測結果を提出可能です(Kernel経由でしか提出できないコンペティションも存在します)。

  • Kernel経由
  • csvファイルを直接アップロード
  • Kaggle API13を利用

今回は、Kernel経由で提出してみましょう。

このKernelにはいろいろなセルが含まれていますが、一旦は何も考えずに右上の「COMMIT」をクリックしてみてください。

次のような画面が立ち上がり、Kernel全体が実行されます。

image.png

実行が終わったら「Open Version」をクリックしましょう。

image.png

実行されたKernelの情報が表示されています。KernelはCOMMITするごとに、自動的にバージョンが管理されます。

image.png

左の「Output」タブを押すと、このバージョンのKernelでの予測結果が「submission.csv」というファイルで保存されています。

image.png

「Submit to Competition」を押すと、このファイルが提出されます。スコアが計算され、今回の場合は「0.66507」という値が算出されています。

image.png

無事にスコアが付いたので、順位表にも自分のアカウントが登場しました。

image.png

このKernelでは、Kernel経由で提出する方法を学びました。「Output」タブから「submission.csv」をダウンロードすることも可能なので、csvファイルを直接アップロードする方法も試してみてください。

なおKaggle APIを利用して提出する方法に興味があれば、こちらのブログ14などをご覧ください。

2. 全体像を把握! submitまでの処理の流れを見てみよう

このKernelでは、前回は一旦無視したKernelの処理の流れを具体的に見ていきます。ぜひ、実際に一番上からセルを実行しながら読み進めてみてください。

具体的な処理の流れは、次のようになっています。

  1. パッケージの読み込み
  2. データの読み込み
  3. 特徴量エンジニアリング
  4. 機械学習アルゴリズムの学習・予測
  5. 提出(submit)

パッケージの読み込み

import numpy as np
import pandas as pd

まずは、以降の処理で利用する「パッケージ」の読み込み(import)をします。パッケージを読み込むことで、標準では搭載されていない便利な機能を拡張して利用できます。

例えば次のセルで読み込む「numpy」15は数値計算に秀でたパッケージで、「pandas」16はTitanicのようなテーブル形式のデータを扱いやすいパッケージです。

パッケージの読み込みはどこで実施しても構いません(Script形式の場合は、冒頭が望ましいです)。

データの読み込み

ここでは、Kaggleから提供されたデータを読み込みます。

まずはどういうデータが用意されているかを確認しましょう。詳細はKaggleのコンペティションのページの「Data」タブ17に記載されています。

train = pd.read_csv('../input/titanic/train.csv')
test = pd.read_csv('../input/titanic/test.csv')
gender_submission = pd.read_csv('../input/titanic/gender_submission.csv')
  • 「gender_submission.csv」は、提出のサンプルです。このファイルで提出ファイルの形式を確認できます。仮の予測として、女性のみが生存する(Survivedが1)という値が設定されています。
  • 「train.csv」は機械学習の訓練用のデータです。これらのデータについてはTitanic号の乗客の性別・年齢などの属性情報と、その乗客に対応する生存したか否かの情報(Survived)が格納されています。
  • 「test.csv」は、予測を実施するデータです。これらのデータについてはTitanic号の乗客の性別・年齢などの属性情報のみが格納されており、訓練用データの情報を基に予測値を算出することになります。
    • 「train.csv」と比較すると、Survivedという列が存在しないと分かります。(この列があったら全て正解できてしまうので当然ですね)

これらは、Kaggleから提供された大元のデータです。

image.png

例えばNameSexなどは文字列で格納されており、そのままでは機械学習アルゴリズムの入力にすることはできません。機械学習アルゴリズムが扱える数値の形式に変換していく必要があります。

NaNというのは、データの欠損です。こうした欠損値は、一部の機械学習アルゴリズムではそのまま扱うこともできますが、平均値や中央値など代表的な値で穴埋めする場合もあります。

特徴量エンジニアリング

次のような処理を「特徴量エンジニアリング」と呼びます。

  • 読み込んだデータを機械学習アルゴリズムが扱える形に変換
  • 既存のデータから、機械学習アルゴリズムが予測する上で有用な新しい特徴量を作成

前者について、例えばSexmalefemaleをそれぞれ0と1に変換します。欠損を穴埋めする処理も行います。具体的な処理内容については、Kernelを参照ください。

後者については、次のKernelで詳しく掘り下げていきます。

大元のデータから次のようなデータを作成するイメージになっています。

feature_engineering.png

特徴量エンジニアリング後のデータを、より一般化して表現したのが、次の図です。

image.png

大元のデータから特徴量エンジニアリングを経て、X_trainy_trainX_testというデータの塊を作ります。大雑把に表現すると、X_trainy_trainの対応関係を学習し、X_testに対応する(隠された)y_testの値を当てるという枠組みになっています。なおこのような仕組みを、機械学習の中でも特に「教師あり学習」と呼びます。

機械学習アルゴリズムの学習・予測

用意した特徴量と予測の対象のペアから、機械学習アルゴリズムを用いて予測器を学習させましょう。

image.png

from sklearn.linear_model import LogisticRegression


clf = LogisticRegression(penalty='l2', solver='sag', random_state=0)
clf.fit(X_train, y_train)

ここでは「ロジスティック回帰」という機械学習アルゴリズムを利用します。

機械学習アルゴリズムの振る舞いはハイパーパラメータという値で制御されます。LogisticRegression の括弧内の値が該当します。ハイパーパラメータの調整方法については「5. 機械学習アルゴリズムのお気持ち?! ハイパーパラメータを調整してみよう」で詳しくみていきます。

学習を終えると、予測値が未知の特徴量(X_test)を与えて予測させることができます。

y_pred = clf.predict(X_test)

y_predには0か1の予測値が格納されています。

提出(submit)

最後に、Kernel経由で提出するために予測値を提出ファイルの形式に整えます。

sub = pd.DataFrame(pd.read_csv('../input/titanic/test.csv')['PassengerId'])
sub['Survived'] = list(map(int, y_pred))
sub.to_csv('submission.csv', index=False)

Kaggleの運営側はy_testの中身を把握しているので、y_testy_predが比較され、自分の提出した予測値の性能がスコアとして返ってくる仕組みになっています。

このKernelでは、提出に向けたKaggleでの処理の流れを追いました。

3. ここで差がつく! 仮説に基づいて新しい特徴量を作ってみよう

1つ目のKernelでは、Kernelを用いてKaggleのコンペティションに提出して順位表に載る方法、2つ目のKernelでは全体の処理の流れを解説しました。

3〜7つ目のKernelでは、既存のKernelに手を加えていきながら、スコアを上げていく方法を学んでいきます。ここで紹介する方法は、メダルが獲得できる開催中のコンペティションにも、ある程度は汎用的に使えるものだと思っています。「このような方法でスコアを上げているんだな〜」と知り、自分でコンペティションに参加していく際の道標になればと考えています。

このKernelでは、特徴量エンジニアリングの部分で、スコアの向上を体験してみましょう。

さっそく特徴量エンジニアリングの解説を始めようかと思いますが、その前に前提的な話として、Kaggleに取り組む上で欠かせない「再現性」の話をします。

再現性の大切さ

「再現性がある」とは、何度実行しても同じ結果が得られることです。Kaggleで言うと、同一のスコアが得られると言い換えても良いでしょう。

再現性がないと、実行ごとに異なるスコアが得られてしまいます。今後、特徴量エンジニアリングなどでスコアの向上を試みても、予測モデルが改善されたか否かを正しく判断できなくなる問題が生じます。

実は、2つ目のKernelには再現性がありません。その原因は、Ageという特徴量の欠損値を埋める際の乱数です。ここでは平均と標準偏差を考慮した乱数で欠損値を穴埋めしているのですが、この乱数は実行ごとに値が変わるようになってしまっています。

age_avg = data['Age'].mean()
age_std = data['Age'].std()
data['Age'].fillna(np.random.randint(age_avg - age_std, age_avg + age_std), inplace=True)

再現性を確保するためには、例えば次のような方法が考えられます。

  1. そもそも乱数を用いる部分を削除する
  2. 乱数のseedを与えて実行結果を固定する

Ageについては、そもそも乱数を用いるよりも、欠損していないデータの中央値を与えた方が筋の良い補完ができそうです。今回は中央値で補完するようにコードを改変します。

data['Age'].fillna(data['Age'].median(), inplace=True)

機械学習アルゴリズムの大半は乱数を利用するので、再現性を担保するためにはseedを設定しておかなければなりません。2つ目のKernelを振り返ると、機械学習アルゴリズムのロジスティック回帰のハイパーパラメータとしてrandom_state=0を与え、seedを固定していました。

clf = LogisticRegression(penalty='l2', solver='sag', random_state=0)

このようにKaggleを進めていく際には、きちんと再現性が取れていることを随時確認していきましょう。なお、GPUを利用する場合など、どうしても再現性が担保できない場合もあります。またseedを固定して比較しても、あくまでそのseedの条件下での結果に過ぎない点には注意が必要です。seedを変えると異なる結果が得られる可能性もあります。

参考までに、KaggleのKernelでは以下のように全ての乱数のseedを固定する関数が使われることもあります18

def seed_everything(seed=1234):
    random.seed(seed)
    os.environ['PYTHONHASHSEED'] = str(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)
    torch.backends.cudnn.deterministic = True

仮説から新しい特徴量を作る

ここでは、仮説と可視化から新しい特徴量を作る過程についてまとめます19。予測精度に寄与する新しい特徴量を作成するに当たっては、仮説と可視化を繰り返すサイクルが大切だと思っています。

image.png

  • 予測精度に寄与しそうな仮説を立てる
  • 可視化を実施する
    • 予測精度に寄与する仮説を見つけるため
    • 仮説が正しいかを検証するため

個々人や問題によって、どちらが起点になるかが変わってきます。

ケース1)ドメイン知識がある場合

例えば自分が詳しい、つまりドメイン知識を持っている分野の問題に取り組む場合、最初から仮説がいくつかあると思います。その場合は仮説を検証するような可視化を実施し、本当に予測精度に寄与するかを確認します。その可視化の結果によっては、改めて仮説を立てることになるかもしれません。

ケース2)ドメイン知識がない場合

ドメイン知識がない場合は、まずは仮説を立てるための探索的なデータ分析を実施することになるでしょう。いろいろな軸でデータを眺め、予測精度に寄与しそうな仮説を立てるのが目的になります。

ここでは、実際に新しい特徴量を作っていきましょう。例として探索的なデータ分析を実施した結果、ぼんやりと「一緒に乗船した家族の人数が多い方が、生存率が低そうだ」という仮説が得られた状況を考えます。

仮説が得られたので、次はこの仮説を検証するための可視化に移ります。FamilySizeという新しい行を作り、その大きさごとに生存したか否かを棒グラフにしました。

  • Survived == 0: 死亡
  • Survived == 1: 生存
import seaborn as sns


data['FamilySize'] = data['Parch'] + data['SibSp'] + 1
train['FamilySize'] = data['FamilySize'][:len(train)]
test['FamilySize'] = data['FamilySize'][len(train):]
sns.countplot(x='FamilySize', data=train, hue='Survived')

image.png

ここでFamilySizeが5以上の場合、死亡が生存を上回っており、生存率が低いことが分かります。

「一緒に乗船した家族の人数が多い方が、生存率が低そうだ」という(ぼんやりとした)仮説が、可視化を通じて「FamilySizeが5以上の場合、生存率が低いので、この特徴量は予測精度に寄与しそうだ」という確信を持った仮説に変わりました。

今回のFamilySizeのように仮説に基づいたいろいろな集計軸で分析をして可視化してみて、予測精度に寄与しそうな集計軸か否かを確認するというのは、有効なアプローチだと思います。

更に今回、可視化を通じて、それまで持っていなかった仮説(情報)を得ることもできました。

FamilySizeが1の人が圧倒的に多く、かつ生存率が低いということです。

このFamilySizeが1であるという特徴量も予測精度に寄与しそうなので、下記のように新しくIsAloneという特徴量を作成しました。

data['IsAlone'] = 0
data.loc[data['FamilySize'] == 1, 'IsAlone'] = 1
train['IsAlone'] = data['IsAlone'][:len(train)]
test['IsAlone'] = data['IsAlone'][len(train):]

このように仮説と可視化を繰り返すことで、既存のデータから機械学習アルゴリズムが予測する上で有用な新しい特徴量を探索していきましょう。

作成した特徴量が「有用」だったかを判断するには、例えば次の4パターンで学習した結果を提出する方法があります。提出した際のスコアを見ることで、特徴量の有用性をある程度確認可能です。

  • FamilySizeIsAloneを加えた場合
  • FamilySizeのみを加えた場合
  • IsAloneのみを加えた場合
  • FamilySizeIsAloneを加えていない場合

ここで「ある程度」としている理由については「6. submitのその前に! 「Cross Validation」の大切さを知ろう」で学びます。

特徴量エンジニアリングの技法を学ぶ

特徴量エンジニアリングを実施していく中で、一般にどのようなやり方があり得るのかの技法を学んでおくことは重要です20

4. 勾配ブースティングが最強?! いろいろな機械学習アルゴリズムを使ってみよう

これまでは機械学習アルゴリズムとして、ロジスティック回帰を採用していました。

このKernelでは、いろいろな機械学習アルゴリズムを使ってみましょう。これまでロジスティック回帰を使っていた部分を差し替えて学習・予測を実行してみたいと思います。

ロジスティック回帰の実装に利用していたsklearnというパッケージは入出力のインタフェースが統一されており、手軽に機械学習アルゴリズムを変更できます。実際にいくつか試してみましょう。

また最近のKaggleのコンペティションで上位陣が利用している機械学習アルゴリズムとしては、勾配ブースティングやニューラルネットワークが挙げられます。これらはロジスティック回帰に比べて表現力が高く、高性能に予測できる可能性を秘めています。特に上位陣での採用率が高いのは「LightGBM24という勾配ブースティングのパッケージです。sklearnと同様のインターフェイスも用意されていますが、ここではPython-package Introduction25に記載の方式で実装します。

sklearn

まずはsklearn内で機械学習アルゴリズムを変更していきましょう。これまではロジスティック回帰を使ってきました。

from sklearn.linear_model import LogisticRegression


clf = LogisticRegression(penalty='l2', solver='sag', random_state=0)

sklearnでは、clfで宣言するモデルを切り替えるだけで機械学習アルゴリズムを差し替えられます。例えば、ランダムフォレスト26と呼ばれる機械学習アルゴリズムを使ってみましょう。

from sklearn.ensemble import RandomForestClassifier


clf = RandomForestClassifier(n_estimators=100, max_depth=2, random_state=0)

あとはロジスティック回帰の場合と同様に学習・予測が実行可能です。

clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)

ランダムフォレストでの予測結果を提出してみると、私の環境では0.77990というロジスティック回帰の時よりも格段に良いスコアが出ました。

Screen Shot 2020-02-12 at 10.32.52.png

sklearnには非常に多くの種類の機械学習アルゴリズムが実装されている27ので、ぜひいろいろな機械学習アルゴリズムを試してみてください。

LightGBM

続いて、LightGBMを使います。sklearnとの差異もあり、いくつか下準備が必要です。

  1. 学習用・検証用にデータセットを分割する
  2. カテゴリ変数をリスト形式で宣言する

LightGBMは大量の決定木を作成しながら学習を進めます。そのため、学習に利用したデータセットなどにのみ過剰に適合してしまい、本来の目的である未知の値に対する性能が劣化してしまう「過学習」という現象に陥りがちです。そこで学習に利用しない検証用のデータに対する性能を見ながら学習を打ち切る「early stopping」を利用するのが一般的となっています。

ここでは、X_trainX_train(学習用)とX_valid(検証用)に分割します。

from sklearn.model_selection import train_test_split


X_train, X_valid, y_train, y_valid = train_test_split(X_train, y_train, test_size=0.3, random_state=0, stratify=y_train)

LightGBMでは、カテゴリ変数に対して特別な処理28を自動的に実行してくれます。次のように、何をカテゴリ変数として扱ってほしいか明示的にLightGBMに教えてあげましょう。

categorical_features = ['Embarked', 'Pclass', 'Sex']

下準備も終わったところで、LightGBMで学習・予測を実施します。

import lightgbm as lgb


lgb_train = lgb.Dataset(X_train, y_train, categorical_feature=categorical_features)
lgb_eval = lgb.Dataset(X_valid, y_valid, reference=lgb_train, categorical_feature=categorical_features)

params = {
    'objective': 'binary'
}

model = lgb.train(params, lgb_train,
                  valid_sets=[lgb_train, lgb_eval],
                  verbose_eval=10,
                  num_boost_round=1000,
                  early_stopping_rounds=10)

y_pred = model.predict(X_test, num_iteration=model.best_iteration)

次のような実行ログと共に、学習が進行します。

Training until validation scores don't improve for 10 rounds.
[10]    training's binary_logloss: 0.425241 valid_1's binary_logloss: 0.478975
[20]    training's binary_logloss: 0.344972 valid_1's binary_logloss: 0.444039
[30]    training's binary_logloss: 0.301357 valid_1's binary_logloss: 0.436304
[40]    training's binary_logloss: 0.265535 valid_1's binary_logloss: 0.438139
Early stopping, best iteration is:
[38]    training's binary_logloss: 0.271328 valid_1's binary_logloss: 0.435633
y_pred[:10]
array([0.0320592 , 0.34308916, 0.09903007, 0.05723199, 0.39919906,
       0.22299318, 0.55036246, 0.0908458 , 0.78109016, 0.01881392])

今回のLightGBMの設定では、出力結果は1になる予測値になります。今回はしきい値を決め打って、0.5を上回っていれば1と予測したと見なして、提出してみます。

y_pred = (y_pred > 0.5).astype(int)
y_pred[:10]
array([0, 0, 0, 0, 0, 0, 1, 0, 1, 0])

LightGBMでの予測結果を提出してみると、私の環境では0.75598というスコアが出ました。ランダムフォレスト同様、ロジスティック回帰の時のスコアよりも向上しているのが分かります。

このように利用する機械学習アルゴリズム次第で、Kaggleのスコアを向上させることが可能です。

その他

勾配ブースティング系では今回紹介したLightGBM以外に、数年前から人気の根強い「XGBoost29や、主流とは言い難いですが徐々に頭角を現している「CatBoost30などがあります。

また「PyTorch31TensorFlow32などのパッケージを用いてニューラルネットワークを実装する場合もあります。

5. 機械学習アルゴリズムのお気持ち?! ハイパーパラメータを調整してみよう

先にも説明したように、機械学習アルゴリズムの振る舞いはハイパーパラメータという値で制御されます。もちろん、ハイパーパラメータの値次第で予測結果は変わり得ます。

ハイパーパラメータの調整は、主に2種類の方法があります。

  • 手動で調整
  • チューニングツールを使う

後者としては、Grid search33, Bayesian Optimization34, Hyperopt35, Optuna36など、いくつかのツールがあります。

このKernelでは、最初に手動でハイパーパラメータを調整し、機械学習アルゴリズムの振る舞いが異なることを確認します。その後、Optunaを用いたチューニングを実施します。

最近のKaggleのコンペティションでは、データサイズが大きいため上記のツールでのハイパーパラメータ調整が現実的な時間で終わらない問題もあります。また一般に、ハイパーパラメータでのスコアの上がり幅は特徴量エンジニアリングで良い特徴量を見つけた場合に劣るので、あまり時間をかけずに手動で微調整をする場合も少なくないように感じます。

手動で調整

ここでは、LightGBMの精度の向上を試みます。これまではobjectiveのみを指定していました。明示的に指定しない場合は、defaultの値37が自動的に定義されます。

params = {
    'objective': 'binary'
}

公式documentationの「Parameters Tuning38に従って、手動で調整を行っていきましょう。いくつかのユースケース別に、ハイパーパラメータ調整のTipsが記載されています。

今回は、精度を高めるのが目的なので「For Better Accuracy39を参照します。

  • Use large max_bin (may be slower)
  • Use small learning_rate with large num_iterations
  • Use large num_leaves (may cause over-fitting)
  • Use bigger training data
  • Try dart
  • 1つ目は「大きめのmax_binを使え」です。defaultの値は255なので、ここでは300にしてみます。
  • 2つ目は「小さめのlearning_rateを使え」です。defaultの値は0.1なので、ここでは0.05にしてみます。
  • 3つ目は「大きめのnum_leavesを使え」です。defaultの値は31なので、ここでは40にしてみます。

手動で調整するにせよ、チューニングツールを使うにせよ、機械学習アルゴリズムをブラックボックス的に利用するのではなく、ハイパーパラメータを正しく理解することが非常に大切です。

ハイパーパラメータの説明については、英語ですが公式のdocumentation40で確認するのが確実です。

日本語の記事だと、例えば「勾配ブースティングで大事なパラメータの気持ち41に、LightGBMなどの勾配ブースティングの主要なハイパーパラメータ解説が記載されています。LightGBMのコミッタの大元さんの発表資料も、開発者の視点でハイパーパラメータの方法が紹介されており参考になります。

params = {
    'objective': 'binary',
    'max_bin': 300,
    'learning_rate': 0.05,
    'num_leaves': 40
}

lgb_train = lgb.Dataset(X_train, y_train, categorical_feature=categorical_features)
lgb_eval = lgb.Dataset(X_valid, y_valid,
                       reference=lgb_train,
                       categorical_feature=categorical_features)

model = lgb.train(params, lgb_train, valid_sets=[lgb_train, lgb_eval],
                  verbose_eval=10,
                  num_boost_round=1000,
                  early_stopping_rounds=10)

y_pred = model.predict(X_test, num_iteration=model.best_iteration)
Training until validation scores don't improve for 10 rounds.
[10]    training's binary_logloss: 0.505699 valid_1's binary_logloss: 0.532106
[20]    training's binary_logloss: 0.427825 valid_1's binary_logloss: 0.482279
[30]    training's binary_logloss: 0.377242 valid_1's binary_logloss: 0.456641
[40]    training's binary_logloss: 0.345424 valid_1's binary_logloss: 0.447083
[50]    training's binary_logloss: 0.323113 valid_1's binary_logloss: 0.440407
[60]    training's binary_logloss: 0.302727 valid_1's binary_logloss: 0.434527
[70]    training's binary_logloss: 0.285597 valid_1's binary_logloss: 0.434932
Early stopping, best iteration is:
[66]    training's binary_logloss: 0.293072 valid_1's binary_logloss: 0.433251

y_predはハイパーパラメータ変更前と異なる値を取ります。出力ログにも変化があり、最終的なvalid_1's binary_loglossが 0.433251 と、変更前よりも小さい値になっています。binary_loglossは損失なので、小さい方が望ましいです。

LightGBMでの予測結果を提出してみると、私の環境では0.77033というスコアが出ました。ハイパーパラメータ変更前の0.75598に比べて、スコアが向上しています。

スクリーンショット 2019-08-24 12.02.13.png

Optunaを使う

ここまで手動でハイパーパラメータを調整してきましたが、次のような感情が芽生えている方もいるのではないでしょうか。

  • 「大きめ」「小さめ」といっても、具体的にどの値にすればよいのか分からない
  • 各パラメータの組み合わせ方もいくつかあり、逐一設定・実行して性能を検証するのは煩わしい

そのような課題を解決してくれるのが、ハイパーパラメータのチューニングツールです。今回はOptunaを使っていきます。

Optunaを使うに当たっては、あらかじめ次の関数のtrial.suggest_int()のように、探索範囲を定義します。 指定方法は公式documentationのTrial42で確認できます。

ここでは、意図的にlearning_rateの調整を実施していません。テーブルデータをLightGBMで扱う場合、一般にlearning_rateが低いほど高い性能が得られるためです。そのため探索範囲には含めず、必要であれば後に手動で低い値に変更することにします。

# KaggleのKernelに搭載済
import optuna
from sklearn.metrics import log_loss


def objective(trial):
    params = {
        'objective': 'binary',
        'max_bin': trial.suggest_int('max_bin', 255, 500),
        'learning_rate': 0.05,
        'num_leaves': trial.suggest_int('num_leaves', 32, 128),
    }

    lgb_train = lgb.Dataset(X_train, y_train,
                            categorical_feature=categorical_features)
    lgb_eval = lgb.Dataset(X_valid, y_valid,
                           reference=lgb_train,
                           categorical_feature=categorical_features)

    model = lgb.train(params, lgb_train,
                      valid_sets=[lgb_train, lgb_eval],
                      verbose_eval=10,
                      num_boost_round=1000,
                      early_stopping_rounds=10)

    y_pred_valid = model.predict(X_valid, num_iteration=model.best_iteration)
    score = log_loss(y_valid, y_pred_valid)
    return score

n_trialsは試行回数です。ここでは計算を短くするため、40回程度にしておきます。乱数も固定しておきます43

study = optuna.create_study(sampler=optuna.samplers.RandomSampler(seed=0))
study.optimize(objective, n_trials=40)
study.best_params
{'max_bin': 427, 'num_leaves': 79}

指定した範囲内で試行回数だけ探索した結果得られた最良のハイパーパラメータが表示されています。こちらで改めて予測し直して、提出してみると、私の環境では0.77033というスコアが出ました。偶然手動での調整と同じスコアになっています。探索範囲や試行回数を変えれば、より良いスコアが出るかもしれません。

スクリーンショット 2019-08-25 1.27.10.png

6. submitのその前に! 「Cross Validation」の大切さを知ろう

3〜5つ目のKernelでは、特徴量エンジニアリング・機械学習アルゴリズム・ハイパーパラメータの面で、スコアを上げていく方法を学びました。

このKernelでは、機械学習モデルの性能を見積もる「validation」について解説します。

提出時のスコアで検証してはいけないのか?

これまではモデルの性能について、主に実際にKaggleに提出した際のスコアで確認していました。しかし、この方法には次のような問題点があります44

  • 提出回数に制限がある
  • public LBで良いスコアが出ても、一部のデータのみに過学習した結果の可能性がある

Kaggleのコンペティションには1日の提出回数に制限があり、スコアが上がる保証もないのに気軽に提出するのは得策ではありません。1日の提出回数分しか試行錯誤ができない状況にも陥ってしまいます。

またメダルが獲得できるコンペティションでは、y_testの一部データのみがpublic LBに利用されておりスコアを随時確認できますが、最終順位は残りのprivate LBのデータに対する性能で決定します。

image.png

public LBで良いスコアが出ても、public LBのデータのみに過学習した結果の可能性があり、必ずしもprivate LBでの性能に寄与するかは分かりません。

public LBにどのようなデータが使われている分からないという問題もあります。極端な例ですが、Titanicのような二値分類の問題で0のラベルが付いたデータのみがpublic LBに使われている可能性を考えましょう。この場合、仮にpublic LBで高いスコアを出すモデルが作成できても、そのモデルは1のラベルを当てる性能がどれだけあるか確認できていません。

以上の問題を踏まえて、Kaggleではvalidationスコアを通じて自分のモデルの性能を測るのが一般的です。ここでのvalidationは「モデルの汎化性能の検証」を意味します。汎化性能とは「未知のデータに対する性能」のことです。「過学習をしていないかの確認」というような意味合いでもあります。

ホールドアウト検証

実は既に、LightGBMを利用する段階で「ホールドアウト検証」と呼ばれる一種のvalidationを実施していました。次の図のように、学習用データセットを分割した上でLightGBMを学習させていたことを思い出してください。

image.png

この検証用データセットは、自分で学習用データセットから切り出しているため、目的変数を含めて全容を把握できています。全体像の見えていないpublic LBのスコアに比べて、信頼性の高いスコアを得られる可能性を秘めています。

検証用データセットに対する性能、すなわちvalidationスコアは、提出することなく手元で確認可能です。自分の気の済むだけ試行錯誤を回し、良いスコアを得た場合に実際にKaggleに提出するような運用が可能です。

交差検証(Cross Validation)

さて、ここで「Cross Validation」を実行すると、ホールドアウト検証の例よりも、更に汎用的に性能を確認できます。交差検証(Cross Validation)とは、次の図のように複数回に(図では3回)わたって異なる方法でデータセットを分割し、それぞれでホールドアウト検証を実行する方法です。そのスコアの平均を確認することで、1回のホールドアウト検証で生じうる偏りに対する懸念を弱めることができます。

image.png

train_test_split を複数回書いて実装することもできそうですが、より便利なパッケージが用意されています。n_splitsは分割数で、ここではデータセットを5つに分けます。

from sklearn.model_selection import KFold


kf = KFold(n_splits=5, shuffle=True, random_state=0)

コードの全容は以下に示します。

from sklearn.model_selection import KFold


y_preds = []
models = []
oof_train = np.zeros((len(X_train),))
cv = KFold(n_splits=5, shuffle=True, random_state=0)

categorical_features = ['Embarked', 'Pclass', 'Sex']

params = {
    'objective': 'binary',
    'max_bin': 300,
    'learning_rate': 0.05,
    'num_leaves': 40
}

for fold_id, (train_index, valid_index) in enumerate(cv.split(X_train)):
    X_tr = X_train.loc[train_index, :]
    X_val = X_train.loc[valid_index, :]
    y_tr = y_train[train_index]
    y_val = y_train[valid_index]

    lgb_train = lgb.Dataset(X_tr, y_tr,
                            categorical_feature=categorical_features)
    lgb_eval = lgb.Dataset(X_val, y_val,
                           reference=lgb_train,
                           categorical_feature=categorical_features)

    model = lgb.train(params, lgb_train,
                      valid_sets=[lgb_train, lgb_eval],
                      verbose_eval=10,
                      num_boost_round=1000,
                      early_stopping_rounds=10)

    oof_train[valid_index] = model.predict(X_val, num_iteration=model.best_iteration)
    y_pred = model.predict(X_test, num_iteration=model.best_iteration)

    y_preds.append(y_pred)
    models.append(model)

Cross Validationを実施した際は、全体の平均をvalidationスコアと見なすことが多いです。このスコアのことを「CVスコア」、省略して単に「CV」とも呼びます。

scores = [
    m.best_score['valid_1']['binary_logloss'] for m in models
]
score = sum(scores) / len(scores)
print('===CV scores===')
print(scores)
print(score)
===CV scores===
[0.3691161193267495, 0.4491122965802196, 0.3833384988458873, 0.43712149656630833, 0.43469994547894103]
0.41467767135962114

Cross Validationには、学習用データセットを無駄にしないという利点もあります。1回のみのホールドアウト検証では検証用データセットに該当する部分を学習に利用できていないですが、Cross Validationでは複数の分割を実施するので、全体としては与えられたデータセットをもれなく学習に利用できています。

この予測値を提出すると、私の環境で0.76555というスコアが出ました。ホールドアウト検証の時よりも悪いスコアになってしまいました。その原因の一つは、データセットの分割方法だと推察されます。最後に、この部分を掘り下げて解説していきます。

スクリーンショット 2019-08-26 14.03.05.png

データセットの分割方法

データセットの分割に当たっては、データセットや課題設定の特徴を意識するのが大切です。

ここまで使っていたKFoldは、特にデータセットや課題設定の特徴を考慮することなくデータセットを分割します。例えば、学習用・検証用データセット内のy==1の割合を見てみると次のようになりました。fold: 2, 4などで顕著に割合が異なっていると分かります。

from sklearn.model_selection import KFold


cv = KFold(n_splits=5, shuffle=True, random_state=0)
for fold_id, (train_index, valid_index) in enumerate(cv.split(X_train)):
    X_tr = X_train.loc[train_index, :]
    X_val = X_train.loc[valid_index, :]
    y_tr = y_train[train_index]
    y_val = y_train[valid_index]

    print(f'fold: {fold_id}')
    print(f'y_tr y==1 rate: {sum(y_tr)/len(y_tr)}')
    print(f'y_val y==1 rate: {sum(y_val)/len(y_val)}')
fold: 0
y_tr y==1 rate: 0.38342696629213485
y_val y==1 rate: 0.3854748603351955
fold: 1
y_tr y==1 rate: 0.3856942496493689
y_val y==1 rate: 0.37640449438202245
fold: 2
y_tr y==1 rate: 0.39831697054698456
y_val y==1 rate: 0.3258426966292135
fold: 3
y_tr y==1 rate: 0.3856942496493689
y_val y==1 rate: 0.37640449438202245
fold: 4
y_tr y==1 rate: 0.36605890603085556
y_val y==1 rate: 0.4550561797752809

繰り返しになりますが、Kaggleの目的は未知のデータセットであるLBに対する性能を高めることです。未知のデータセットにおけるy==1の割合は誰にも正確には分からないですが、既存のデータセットである学習用データセットと同様の割合だと近似するのが一般的です。つまり、データセットはy==1の割合を保つように分割するのが理想的です。

y==1の割合が均等でない場合、y==1を重要視したり逆に軽視したりと、機械学習アルゴリズムの学習がうまくいかない傾向にあります。このような状況では適切に特徴を学習できず、未知のデータセットに対する性能が劣化してしまう可能性があります。KFoldを用いた場合にスコアが悪化した原因もここにあると考えられます。

ちなみに以前にtrain_test_splitを利用した際には、stratifyという引数でy_trainを指定することで、割合を保ったままデータセットを2つに分割していました。

from sklearn.model_selection import train_test_split


X_train, X_valid, y_train, y_valid = train_test_split(X_train, y_train, test_size=0.3, random_state=0, stratify=y_train)

割合を保ったままCross Validationを実施するためにはStratifiedKFoldを使います。学習用・検証用データセット内のy==1の割合が可能な範囲で均一に保たれています。

from sklearn.model_selection import StratifiedKFold


cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=0)
for fold_id, (train_index, valid_index) in enumerate(cv.split(X_train, y_train)):
    X_tr = X_train.loc[train_index, :]
    X_val = X_train.loc[valid_index, :]
    y_tr = y_train[train_index]
    y_val = y_train[valid_index]

    print(f'fold: {fold_id}')
    print(f'y_tr y==1 rate: {sum(y_tr)/len(y_tr)}')
    print(f'y_val y==1 rate: {sum(y_val)/len(y_val)}')
fold: 0
y_tr y==1 rate: 0.38342696629213485
y_val y==1 rate: 0.3854748603351955
fold: 1
y_tr y==1 rate: 0.38342696629213485
y_val y==1 rate: 0.3854748603351955
fold: 2
y_tr y==1 rate: 0.38429172510518933
y_val y==1 rate: 0.38202247191011235
fold: 3
y_tr y==1 rate: 0.38429172510518933
y_val y==1 rate: 0.38202247191011235
fold: 4
y_tr y==1 rate: 0.38375350140056025
y_val y==1 rate: 0.384180790960452

この分割を用いて学習・予測を実行したところ、私の環境で0.77511というスコアが出ました。KFoldの時よりも、ホールドアウト検証の時よりも良いスコアが出ています。

スクリーンショット 2019-08-26 14.02.44.png

分割の際に気を付けたいことは、目的変数の割合以外にも、以下のような点があります。

  • データセット内に時系列性がないか
  • データセット内にグループが存在しないか

詳細は「validationの切り方いろいろ(sklearnの関数まとめ)45に譲りますが、前者については時系列性を意識したデータセットの分割が必要です。ここでの未知のデータセットとは時系列的に未来のデータが該当します。つまりは、学習に使うデータセットが検証用・評価用のデータセットよりも過去になるような分割が望ましいです。

後者の場合は、同一のグループ内の場合は予測が比較的容易である点に注意しましょう。仮に未知のデータセットには既存のデータセットと同一のグループが存在しないと予想される場合、データセットの分割の際にも同一のグループが分割されないように気を付ける必要があります。

7. 三人寄れば文殊の知恵! アンサンブルを体験しよう

ここでは、機械学習における「アンサンブル」について解説します。アンサンブルとは、複数の機械学習モデルを組み合わせることで精度の高い予測値を獲得する手法です。

アンサンブルはKaggleなどのコンペにおける最後の一押しとして、大きな成果を発揮する場合があります。近年は多くのチームで、取り組みの深さは違えど至極当然に用いられる手法になっている印象です。

時には「実際の業務には役に立たない」と批判される対象にもなるものですが、個人的には「良くも悪くも機械学習コンペの特徴」と言えるものの一つだと思っています。

最初に、簡単な例を通じてアンサンブルの考え方を学びましょう。次いで、このKernelの中で、実際にアンサンブルの効果を確認します。

三人寄れば文殊の知恵

アンサンブルについては「Kaggle Ensembling Guide46と呼ばれる有名な記事が存在します。ここでは「Kaggle Ensembling Guide」の冒頭からの具体例を引用して、アンサンブルの考え方を学びましょう。

10個のyに対して、0か1の二値分類を考えます。

つまりは

(y_0, y_1, ... , y_9)=(0, 1, ... , 0)

のような予想をする問題です。簡単のため、以後は右辺の括弧内だけを取り出して次のように表現したいと思います。次の例は $y_4$ と$y_9$ のみを1と予想した」という意味です。

0000100001

さて、ここで正解が全て1であるような問題を考えます。

1111111111

この問題に対して、モデルA・B・Cがそれぞれ次のような予測をしたとしましょう。

モデルA = 80% accuracy

1111111100

モデルB = 70% accuracy

0111011101

モデルC = 60% accuracy

1000101111

単純に一番良いモデルを選んだ場合、モデルAを採用すれば80%の正解率を得られる状況です。しかし、ここでアンサンブルを使うと80%以上の正解率を出すモデルを得ることができます。

今回使うアンサンブルは、非常に単純な「多数決」の技法です。 $y_0, y_1, ... , y_9$ のそれぞれで各モデルの予測を確認し、多数決で最終的な予測結果を導出します。

例えば $y_0$ について、モデルAとモデルCは1を、モデルBは0を予測しています。そのため、最終的な予測結果は1とします。

同様に考えていくと、下記が最終的な予測結果です。

最終的な予測結果 = 90% accuracy

1111111101

なんと、基にした全てのモデルよりも高い正解率を得ることができました。

数式的な説明は「Kaggle Ensembling Guide」に記載があるので割愛しますが、それぞれのモデルの良い部分・悪い部分を補完し合い、全体として良い予測結果が得られています。

Titanicでの実験

このKernelでは、これまで作ってきたKernelの延長線上で実際にアンサンブルの効果を確認します。

ここでは「提出ファイルによるアンサンブル」を試してみましょう。これまで作成したランダムフォレストとLightGBMによる提出ファイルを利用します。

それぞれ提出スコアは、0.77511、0.77033、0.77990です。

import pandas as pd


sub_lgbm_sk = pd.read_csv('../input/submit-files/submission_lightgbm_skfold.csv')
sub_lgbm_ho = pd.read_csv('../input/submit-files/submission_lightgbm_holdout.csv')
sub_rf = pd.read_csv('../input/submit-files/submission_randomforest.csv')

「Kaggle Ensembling Guide」と同様に、多数決で予測値を決定します。3ファイルの予測値部分を合計し、合計が2以上の場合は全体としての予測値を1とします。

sub = pd.DataFrame(pd.read_csv('../input/titanic/test.csv')['PassengerId'])
sub['Survived'] = sub_lgbm_sk['Survived'] + sub_lgbm_ho['Survived'] + sub_rf['Survived']
sub['Survived'] = (sub['Survived'] >= 2).astype(int)
sub.to_csv('submission_lightgbm_ensemble.csv', index=False)

この予測値を提出すると、私の環境で0.78468という過去最高のスコアが得られました。

スクリーンショット 2019-08-26 17.12.59.png

「Kaggle Ensembling Guide」には今回体験したような「提出ファイルによるアンサンブル」だけではなく、より高度な「Stacked Generalization (Stacking)」「Blending」といった様々な技法が紹介されています。より深くアンサンブルを勉強したい場合は、ご一読をオススメします。

8. Titanicの先へ行く①! 複数テーブルを結合してみよう

Titanicでは訓練用データが「train.csv」という1つのcsvファイルにまとまっていますが、複数ファイルが用意されている場合も多いです。

例えば「Home Credit Default Risk47というコンペティションでは、次の図のような関係を持つ複数のファイルが提供されました。

image.png
(画像はHome Credit Default Riskの「Data」タブから引用)

個人的には、こういったコンペティションに直面した際のデータの扱い方が分からず、Titanicの次へ行く障壁の1つになっているのではないかと考えています。

このKernelでは、複数のファイルを結合して機械学習アルゴリズムに入力するためのデータを用意する方法を扱います。

題材にするのは先に紹介した「Home Credit Default Risk」コンペです。このコンペでは、融資の申込に対して、それぞれが債務不履行になるか否かを予測します。application_{train/test}.csvに含まれる申込1件に対して、過去の申込履歴などの関連情報を含むcsvファイルが複数存在するデータ構造で、どのように有益な情報を取り出すかが問われるコンペとなりました48

Home Credit Default Risk

ここでは、公開Kernel「Introduction to Manual Feature Engineering49の冒頭の例を引用します。

まずは主ファイルであるapplication_train.csvを読み込みます。各行が1つの融資の申込を表し、TARGETが予測の対象です。

application_train = pd.read_csv('../input/home-credit-default-risk/application_train.csv')
application_train.head()

スクリーンショット 2019-08-27 2.56.48.png

次いで、サブファイルに当たるbureau.csvを読み込みます。bureau.csvには、他の金融機関から提供された過去の申込履歴が記録されています。

図に示された通り、SK_ID_CURRで紐づく関係性です。過去の履歴なので、application_trainの1行に対して、bureauの複数行が対応する可能性があります。

bureau = pd.read_csv('../input/home-credit-default-risk/bureau.csv')
bureau.head()

スクリーンショット 2019-08-27 2.59.13.png

仮に1:1の関係ならば、純粋にデータを結合してしまえば良いです。ここでは1:Nの関係なので、N側のデータセットを何かしらの方法で集約して情報量を落とす必要があります。

「何かしらの方法で集約」の部分は、「3. ここで差がつく! 仮説に基づいて新しい特徴量を作ってみよう」で説明したような特徴量エンジニアリングの力が試されます。思考停止でさまざまな集約パターンを試す場合もありますが、今回のデータセットのようにある程度カラム数も多い場合には、意味のある仮説を立てるのが望ましいと思います。

ここでは「過去の申込回数」が有効な特徴量になるという仮説を得たとします。この特徴量は、次のようにbureauから作成可能です。SK_ID_CURRごとに、回数が集計されています。

previous_loan_counts = bureau.groupby('SK_ID_CURR', as_index=False)['SK_ID_BUREAU'].count().rename(columns={'SK_ID_BUREAU': 'previous_loan_counts'})
previous_loan_counts.head()

スクリーンショット 2019-08-27 3.36.39.png

あとは、application_trainにSK_ID_CURRをキーにして結合するだけです。

application_train = pd.merge(application_train, previous_loan_counts, on='SK_ID_CURR', how='left')

この際how='left'という引数が与えられている点に注目です。SQLを使ったことのある方はイメージしやすいかと思いますが、引数として与えられているデータセットのうち、左側のファイルを軸にデータセットを結合していくという意味合いです。右側のデータセットに該当する値が含まれていない場合は、欠損値になります。

仮にこの引数を指定しないと、両者に含まれるSK_ID_CURRのデータセットのみが返ります50。ここでprevious_loan_countsには申込が0回のSK_ID_CURRは含まれていないので、データセットの欠落が発生する可能性があります。

application_trainの行数が減るのは学習用データセットの量を保つ観点から望ましくありません。application_test.csvを扱う場合は、予測すべきデータセットが欠落する事態を招いてしまいます。

この欠損値は、意味合いを考えると0で補完するのが適切です。

application_train['previous_loan_counts'].fillna(0, inplace=True)
application_train.head()

スクリーンショット 2019-08-26 18.55.23.png

今回は単純な仮説を基に特徴量を作成しましたが、例えば「直近の申込履歴を重視して集計したい」など、さまざまな観点でデータセットを集約できます。このような考え方で、複数のファイルを結合して機械学習アルゴリズムに入力するためのデータを用意することが可能となります。

9. Titanicの先へ行く②! 画像データに触れてみよう

Kaggleのコンペティションで扱うデータは、大きく分けて次の3種類があります。

  • テーブルデータ
  • 画像データ
  • テキストデータ

Titanicのデータはテーブルデータに該当しますが、他のコンペティションでは画像データやテキストデータを扱う場合も多いです。

PetFinder.my Adoption Prediction」のように、3種類全てのデータを扱うコンペティションも存在します。テーブルデータとしてはペットの犬種や年齢、画像データとしてはペットの写真、テキストデータとしてはペットの説明文といった情報が提供されました。

image.png

9〜10つ目のKernelでは、それぞれ画像とテキストデータを扱う方法を解説します。とはいえ、近年に急速に発展を遂げる画像認識・自然言語処理分野を網羅的に紹介するのは現実的ではありません。

本記事では画像とテキストデータを扱うコンペティションの概要を述べ、これまで学んできたテーブルデータを扱うコンペティションと比較して、共通する・異なる部分を簡潔に説明します。今後ご自身で取り組んでいく上での最初の取っ掛かりになればと考えています。

画像を扱うコンペティションの概要

ここでは、画像を扱うコンペティションの概要を説明します。略して「画像コンペ」などと表現されることも多いです。

画像コンペでは、画像認識分野で一般的な課題が出題されることが多いです51。具体的には、分類・検出・セグメンテーションなどが挙げられます。その他には「Adversarial Example」52「Generative Adversarial Network(GAN)」53など具体的な技術に特化したコンペティションも開催されています。

分類

分類の問題では、与えられた画像に対して、適切なラベルを推定します。出力は最も可能性の高いラベルの場合もあれば、ラベルと確率の場合もあります。

classification.png

(画像は「CS231n: Convolutional Neural Networks for Visual Recognition54講義資料55から引用)

検出

検出の問題では、与えられた画像の中にある物体の場所・ラベルを推定します。どの粒度で何を検出すべきかは問題設定次第です。例えば下図では「DOG」「CAT」を出力していますが、犬の目や鼻の部分を抽出すべき場合もあるかと思います。

detection.png

(画像は「CS231n: Convolutional Neural Networks for Visual Recognition」の講義資料から引用)

セグメンテーション

セグメンテーションの問題では、与えられた画像をいくつかの領域に塗り分けます。出力画像の見た目から「塗り絵」などと呼ばれることもあります。

segmantation.png

(画像は「CS231n: Convolutional Neural Networks for Visual Recognition」の講義資料から引用)

Adversarial Example

2017年には「Neural Information Processing Systems (NeurIPS)」56という著名な国際会議に併設される形で、Adversarial Exampleを題材にしたコンペティションが開催されました57。画像コンペは、国際学会に併設されて開催されることも多い印象です。

Adversarial Exampleは、入力に対し人間が検知できない微量な変更を加えることで機械学習アルゴリズムの出力を大きく狂わせてしまう現象のことです。このコンペティションでは、出力を狂わせる「攻撃側」と、出力を狂わされにくい機械学習アルゴリズムを作る「防御側」の2部門が設けられました。より具体的な詳細については、4位に入賞したPreferred Networksのブログ58をご参照ください。

adversarial.png

(画像は「Explaining and Harnessing Adversarial Examples59から引用)

GAN

2019年には、新たにGANを題材にしたコンペティションも開催されました60。GANは画像の生成器と識別器という2種類のニューラルネットワークを組み合わせて精度の高い画像を生成する技術です。GANを用いて、いかに精度の高い画像を生成できるかを競う問題設定になっていました。

image.png

(画像は「An intuitive introduction to Generative Adversarial Networks (GANs)61から引用)

GANを用いずに評価指標を不当に最適化しようとする試みがはびこるなど、GANを題材にする難しさも露呈したコンペティションとなりました62

テーブルデータと共通する・異なる部分

ここでは、これまで学んできたテーブルデータを扱うコンペティションと比較して、共通する・異なる部分を簡潔に説明します。

まず前提として機械学習の教師あり学習の枠組みである限り、学習用データセットの特徴量・目的変数の対応関係を機械学習アルゴリズムで学習して未知のデータセットに対する汎化性能を得るという部分は共通です。教師あり学習の枠組みについては「2. 全体像を把握! 提出までの処理の流れを見てみよう」で解説しました。

テーブルデータ同様、画像も数値データの集合に過ぎない点に考えると類似点が分かりやすいかもしれません。

以降、Kernelでは、PyTorchの提供するチュートリアル「TRAINING A CLASSIFIER63に沿って画像データを扱っていきます。

import torch
import torchvision


trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
                                          shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
                                         shuffle=False, num_workers=2)

まずはデータセットをダウンロードします。Kernelを利用する場合は、右サイドバーの「Internet」をOnにしておく必要があります。今回利用するのは「CIFAR1064と呼ばれる、10種類のラベルが付与された画像分類の著名なデータセットです。

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

internet.png

画像を可視化した結果を以下に示します。

import matplotlib.pyplot as plt
import numpy as np


# functions to show an image
def imshow(img):
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()


# get some random training images
dataiter = iter(trainloader)
images, labels = dataiter.next()

# show images
imshow(torchvision.utils.make_grid(images))
# print labels
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))

cifar10.png

batch_size=4にしているためimagesには、4枚の画像データが配列形式で含まれています。それぞれの画像は縦横32ピクセル×32ピクセルのRGB値(3種類)のデータになっています。

images.shape
torch.Size([4, 3, 32, 32])

この画像1枚の数値データが、Titanicでのテーブル1行に対応しています。

images[0]
tensor([[[ 0.0824,  0.0510,  0.0510,  ...,  0.0745,  0.0745,  0.0902],
         [ 0.1137,  0.0745,  0.0588,  ...,  0.0980,  0.0980,  0.0980],
         [ 0.1294,  0.0902,  0.0745,  ...,  0.1137,  0.1059,  0.0980],
         ...,
         [-0.5686, -0.6000, -0.6157,  ..., -0.6549, -0.6549, -0.6314],
         [-0.5765, -0.6078, -0.6314,  ..., -0.6863, -0.7020, -0.6941],
         [-0.6000, -0.6235, -0.6549,  ..., -0.6941, -0.7020, -0.7098]],

        [[ 0.1137,  0.1059,  0.1216,  ...,  0.1294,  0.1294,  0.1216],
         [ 0.1373,  0.1137,  0.1216,  ...,  0.1373,  0.1373,  0.1294],
         [ 0.1373,  0.1137,  0.1216,  ...,  0.1373,  0.1294,  0.1216],
         ...,
         [-0.4745, -0.4824, -0.4902,  ..., -0.5294, -0.5373, -0.5137],
         [-0.4902, -0.4902, -0.4902,  ..., -0.5451, -0.5608, -0.5529],
         [-0.5059, -0.5059, -0.5137,  ..., -0.5529, -0.5608, -0.5686]],

        [[ 0.2078,  0.1922,  0.2000,  ...,  0.2157,  0.2157,  0.2157],
         [ 0.2157,  0.1843,  0.1922,  ...,  0.2078,  0.2157,  0.2078],
         [ 0.2078,  0.1843,  0.1843,  ...,  0.2078,  0.2000,  0.1922],
         ...,
         [-0.4510, -0.4745, -0.4824,  ..., -0.5294, -0.5294, -0.4980],
         [-0.4745, -0.4824, -0.4902,  ..., -0.5451, -0.5608, -0.5529],
         [-0.4902, -0.5059, -0.5137,  ..., -0.5529, -0.5608, -0.5686]]])

テーブルデータと画像データを扱うに当たって、特に大きく異なるのは特徴量エンジニアリングの部分だと思います。

伝統的な画像認識では「画像のどこに注目し、何を特徴として取り出すか」という手続きが存在していました65。局所特徴量「SIFT66などが有名です。SIFTなどを用いて画像を数値化した上で、ロジスティック回帰などの機械学習アルゴリズムに投入するやり方が採用されていました。このやり方はテーブルデータから特徴量を作っていく過程とも類似していると思います。

ただし近年はニューラルネットワークの台頭に伴い、画像から特徴量を抽出する部分をニューラルネットワークに任せる手法が一般的です。詳細は割愛しますが、画像のデータセットをそのまま入力として利用し、ニューラルネットワークの高い表現力で有用な特徴量を獲得しています。この辺りは歴史も含めて『画像認識』(機械学習プロフェッショナルシリーズ)67の第1章に記載されています。

こういった背景もあり、画像コンペでは特徴量エンジニアリングよりも、ニューラルネットワークの構造設計に注力する面が大きい印象です。「第9回:Kaggleの「画像コンペ」とは--取り組み方と面白さを読み解く68では、論文を読みながら研究動向を追うなど、DeNAの矢野さんの画像コンペへの取り組み方がまとめられています。画像コンペの取り組み方については、DeNAのphalanxさんの資料69も参考になります。

他にも、データセットのサイズの違いは大きいです。例えばTitanicの「train.csv」は61KBですが、画像コンペのデータセットは10GBを超えることも珍しくありません。また機械学習アルゴリズムにも層の深いニューラルネットワーク(ディープラーニング)を利用する場合が多く、計算量が多いです。計算資源としてGPUが必須になる場合が多いでしょう。

このKernelでは、PyTorchのチュートリアルの冒頭部分からコードを流用しました。チュートリアルの続きでは、GPUを用いてディープラーニングの一種「Convolutional Neural Network (CNN)」70を学習し、予測を実行しています。興味があれば、最初の取っ掛かりとして取り組むのも良いかと思います。

10. Titanicの先へ行く③! テキストデータに触れてみよう

ここでは、テキストデータを扱う方法を解説します。

テキストを扱うコンペティションの概要

まず、テキストを扱うコンペティションの概要を説明します。自然言語処理(Natural Language Processing)が題材なので「NLPコンペ」などと表現されることも多いです。

自然言語処理の課題としては、一般に分類・文生成・質問応答などが挙げられます。Kaggleではデータセットや採点の都合か、分類や回帰に帰着した出題が多い印象です。最近の例で言うと「Quora Insincere Questions Classification71Jigsaw Unintended Bias in Toxicity Classification72は、文章の有害度合いを予測するコンペティションでした。

テーブルデータと共通する・異なる部分

ここでは、これまで学んできたテーブルデータを扱うコンペティションと比較して、共通する・異なる部分を簡潔に説明します。

前章でも述べた通り、NLPコンペでも機械学習の教師あり学習の枠組みである限り、大まかな考え方は変わりません。学習用データセットの特徴量・目的変数の対応関係を機械学習アルゴリズムで学習して、未知のデータセットに対する汎化性能を得るのが目的となります。

一つ大きく異なるのは、文章はそのままでは機械学習アルゴリズムで扱えないという点です。コンピュータで表示している以上は英語・日本語のテキストデータも大元を辿れば数値データですが、テーブルデータ・画像データのように特徴量という意味のあるベクトルとして見なす必要があります。

以降、Kernelでは、テキストデータを扱っていきます。サンプルデータとしては、次の3文を利用します。

import pandas as pd


df = pd.DataFrame({'text': ['I like kaggle very much',
                            'I do not like kaggle',
                            'I do really love machine learning']})

これらの文は、このままでは機械学習アルゴリズムの入力として扱えません。文の特徴を保持したまま何かしらの方法でベクトルに変換する必要があります。

Bag of Words

単純なアイディアとして、文で登場した単語の回数を数える方法があります。この方法は「Bag of Words」と呼ばれます。

from sklearn.feature_extraction.text import CountVectorizer


vectorizer = CountVectorizer(token_pattern=u'(?u)\\b\\w+\\b')
bag = vectorizer.fit_transform(df['text'])
bag.toarray()
array([[0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1],
       [1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0],
       [1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0]], dtype=int64)

arrayの中には3つの要素が存在し、それぞれが文に対応しています。1つ目の要素は[0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1]です。これは、indexの1, 2, 4, 7, 10に値する単語が文に含まれているという意味です。

各indexに対応している単語は、次のように確認できます。例えばindexが1になっている「i」は、全ての文章で1のフラグが立っています(indexが0始まりであることに注意してください)。

vectorizer.vocabulary_
{'i': 1, 'like': 4, 'kaggle': 2, 'very': 10, 'much': 7, 'do': 0, 'not': 8, 'really': 9, 'love': 5, 'machine': 6, 'learning': 3}

Bag of Wordsは単純で分かりやすい手法ですが、次のような弱点があります。

  1. 単語の珍しさを表現できていない
  2. 単語同士の近さを考慮できていない
  3. 文中の単語の順番に関する情報を捨ててしまっている

1点目について、文の特徴を捉えたいという目的ならば、「I」などの一般に文に多く登場する単語よりも「Kaggle」「machine learning」などの特徴的な単語を重要視したいです。2点目について、ここでは「like」と「love」を完全に別の単語として扱っており、この2つの単語が似ているという情報は使えていません。3点目について、文を単語に分割して考えることで文の意味を正確に捉えるのは難しくなっています。

TF-IDF

先に述べた1点目の問題に対処したのが、登場する単語の珍しさを考慮した「TF-IDF」と呼ばれる手法です。「Term Frequency」(単語の登場頻度)を数えるだけではなく、「Inverse Document Frequency」(ドキュメント内での登場頻度の逆数)を掛け合わせています。

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer


vectorizer = CountVectorizer(token_pattern=u'(?u)\\b\\w+\\b')
transformer = TfidfTransformer()

tf = vectorizer.fit_transform(df['text'])
tfidf = transformer.fit_transform(tf)
# tfidfが計算された結果の表示
print(tfidf.toarray())
[[0.         0.31544415 0.40619178 0.         0.40619178 0.
  0.         0.53409337 0.         0.         0.53409337]
 [0.43306685 0.33631504 0.43306685 0.         0.43306685 0.
  0.         0.         0.56943086 0.         0.        ]
 [0.34261996 0.26607496 0.         0.45050407 0.         0.45050407
  0.45050407 0.         0.         0.45050407 0.        ]]

Bag of Wordsと同様に、arrayの中には3つの要素が存在し、それぞれが文に対応しています。

1文目をBag of WordsとTF-IDFで比較してみましょう。今回、各indexに対応している単語は両者で変わらないので、0より大きい値を含むindexは同じです。

  • Bag of Words: [0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1]
  • TF-IDF: [0., 0.31544415, 0.40619178, 0., 0.40619178, 0., 0., 0.53409337, 0., 0., 0.53409337]

違うのは、Bag of Wordsでは0か1のみの値を取るのに対して、TF-IDFでは0-1の値を取っている点です。

TF-IDFでは「I」は0.31544415、「kaggle」と「like」は0.40619178、「very」と「much」は0.53409337の値が割り当てられています。「I」は3文全てに登場している一方で、「very」と「much」は1文目にのみ存在しており、単語の珍しさに応じて大きな値になっていると読み取れます。

Word2vec

先に、Bag of Wordsの2つ目の問題点として「単語同士の近さを考慮できていない」と述べました。このような単語同士の意味の近さを捉えたベクトル化の手法として「Word2vec」などが挙げられます。

具体的な仕組みについては「絵で理解するWord2vecの仕組み73や「word2vec(Skip-Gram Model)の仕組みを恐らく日本一簡潔にまとめてみたつもり74が詳しいです。

from gensim.models import word2vec


sentences = [d.split() for d in df['text']]
model = word2vec.Word2Vec(sentences, size=10, min_count=1, window=2, seed=7)

以上で学習が完了しました。学習に用いた単語は、次のようにしてベクトル形式に変換できます。

model.wv['like']
array([-0.01043484, -0.03806506,  0.01846329,  0.04698185,  0.02265111,
       -0.0275427 ,  0.00458471,  0.04774009,  0.01365959,  0.01941545],
      dtype=float32)

次のようにして、学習に用いた単語の中から似ている単語を抽出できます。今回はデータセットが3文しかないので十分に単語の意味の近さを学習できていないようです。

model.wv.most_similar('like')
[('really', 0.3932609558105469),
 ('do', 0.34805530309677124),
 ('very', 0.29682281613349915),
 ('machine', 0.20769622921943665),
 ('learning', 0.08932216465473175),
 ('love', -0.035492151975631714),
 ('not', -0.13548487424850464),
 ('I', -0.2518322765827179),
 ('much', -0.40533819794654846),
 ('kaggle', -0.44660162925720215)]

このようなアイディアで単語をベクトル化することで、文を機械学習アルゴリズムに入力として利用可能です。具体的には、次のような方法が挙げられます。

  1. 文に登場する単語のベクトルの平均を取る
  2. 文に登場する単語ベクトルの各要素の最大値を取る
  3. 各単語の時系列データとして扱う

1つ目は非常に単純なやり方です。今回の場合は5つの単語の平均を計算します。

df['text'][0].split()
['I', 'like', 'kaggle', 'very', 'much']
import numpy as np


wordvec = np.array([model.wv[word] for word in df['text'][0].split()])
wordvec
array([[ 0.03103545, -0.01161594, -0.04156914,  0.0151331 , -0.02015941,
         0.02498668,  0.01226169, -0.01423238,  0.0299348 , -0.0235391 ],
       [-0.01043484, -0.03806506,  0.01846329,  0.04698185,  0.02265111,
        -0.0275427 ,  0.00458471,  0.04774009,  0.01365959,  0.01941545],
       [ 0.00562139,  0.04261161,  0.01942341,  0.02058475, -0.04178216,
         0.0483778 ,  0.02867676, -0.03482581,  0.00596862,  0.01260627],
       [-0.00546305,  0.04037713, -0.02587517,  0.02301916,  0.03183642,
        -0.0372007 ,  0.03839479,  0.01596523,  0.02796198,  0.01038733],
       [-0.01727871,  0.03896596, -0.01460331, -0.01620135,  0.01536224,
         0.02102943,  0.00892776,  0.00372602,  0.02321487, -0.01123929]],
      dtype=float32)
np.mean(wordvec, axis=0)
array([ 0.00069605,  0.01445474, -0.00883218,  0.0179035 ,  0.00158164,
        0.0059301 ,  0.01856914,  0.00367463,  0.02014797,  0.00152613],
      dtype=float32)

2つ目の方法では、平均ではなく各要素の最大値を計算します。この手法は「SWEM-max75などと呼ばれています。

np.max(wordvec, axis=0)
array([0.03103545, 0.04261161, 0.01942341, 0.04698185, 0.03183642,
       0.0483778 , 0.03839479, 0.04774009, 0.0299348 , 0.01941545],
      dtype=float32)

3つ目の方法はwordvecのベクトルをそのまま時系列の入力として扱います。Bag of Wordsの3つ目の問題点として挙げた「文中の単語の順番に関する情報を捨ててしまっている」の部分に対応する手法となっています。

ここまで、機械学習の教師あり学習の枠組みの中で、文を機械学習アルゴリズムの入力として扱うためのいくつかの手法を紹介しました。

最近のKaggleのNLPコンペでは機械学習アルゴリズムとして、時系列性を扱える「Recurrent Neural Network(RNN)」などのニューラルネットワークが頻繁に使われています。文をベクトル化して機械学習アルゴリズムで予測する一連の流れは「Approaching (Almost) Any NLP Problem on Kaggle76と題された優れたKernelで解説されています。

直近のNLPコンペである「Jigsaw Unintended Bias in Toxicity Classification」では汎用言語表現モデルの「Bert77を用いた解法が目立ちました。このコンペでは終了1週間前に公開された「XLNet78という機械学習アルゴリズムも利用されました。テーブルデータを扱うコンペと比較して、NLPコンペでは画像コンペ同様、研究の最新動向が積極的に使われている印象があります。

メダルが獲得できる開催中のコンペティションに参加しよう

入門 10 Kernelで学んだこと

  • 1つ目のKernelでは、Kernelを用いてKaggleのコンペティションに提出して順位表に載る方法、2つ目のKernelでは全体の処理の流れを解説しました。
  • 3〜7つ目のKernelでは、既存のKernelに手を加えていきながら、スコアを上げていく方法を学びました。
  • 8〜10つ目のKernelでは、Titanicでは扱わない複数テーブルを結合する方法、画像・テキストデータを扱う方法を紹介しました。

タイトルにもある通り「これだけやれば十分戦える!」と言える内容になっているかと思います。ぜひ次は、メダルが獲得できる開催中のコンペティションに参加してみてください。

開催中のコンペティションを確認する

開催中のコンペティションの一覧は、KaggleのCompetitions79というページの「Active Competitions」で確認できます。それぞれのページでは具体的なコンペティションの内容が記載されています。

ただし、Titanicと同様のチュートリアル的なコンペティションなど、開催中でもメダルが獲得できないコンペティションもあるので注意が必要です。Kaggleでメダルが獲得できるコンペティションか否かは、トップページの「Tiers」部分の表記で確認できます80

例えば、メダルが獲得できるコンペティションでは、次のような表記です。

image.png

一方で、Titanicなどのメダルが付与されないコンペティションでは、次のような表記になっています。

image.png

なお、Kaggleのコンペティションには、いくつかの分類81があります。一概には言えませんが、ある程度この分類でもメダル獲得の可否が判断できます。

  • Common Competition Types
    • Featured
    • Research
    • Getting Started
    • Playground
  • Other Competition Types
    • Recruitment
    • Annual
    • Limited Participation

こちらの分類の日本語訳は、こちら82で解説されています。

初心者にオススメの戦い方

ここでは参加するコンペティションが決まった後、初心者にオススメの戦い方を書いておきます。tkm2261さん流のKaggle入門方法83にも、似たような内容が記載されています。

  1. ベースラインとなるKernelを探す
  2. 本記事の内容を参考に、ベースラインを改善する

ベースラインとなるKernelを探す

今回は「Santander Customer Transaction Prediction84を例に解説します。

まず「Kernels」タブに行き、右のプルダウンで「Most Votes」を選びましょう。

image.png

すると、参加者に多く「いいね」された順にKernelが表示されます。

image.png

0.9, 0.901 など数字の数字は、そのKernel経由で提出されスコアが付いていることを意味します。稀に、csvファイルでのアンサンブルのみを実施しているKernelが上位に来ているのは注意が必要です。きちんと、Kaggleから提供された大元のデータから提出までの一連の流れを実行しているKernelをベースラインとすると良いでしょう。

最上位のKernelはタイトルに「EDA」と入っています。これは「Explanatory Data Analysis」、日本語だと「探索的データ分析」を意味します。これらのKernelは、提供されたデータの特徴などを解説してくれており、ベースラインとして適していると思います。

本記事の内容を参考に、ベースラインを改善する

ベースラインのKernelが決まったら、本記事の入門 10 Kernelと同様に、KernelをForkして自分で手を動かしてみてください。

例えば特徴量エンジニアリングで新しい特徴量を追加したり、機械学習アルゴリズムを差し替えたり、ハイパーパラメータを調整したり、アンサンブルをしてみたり、いろいろな手数があるかと思います。自分のアイディアでスコアが増減する経験を楽しんでみてください。

コンペ中に取り組む手順について、自分なりの方法は以前にブログに記載しています71

できれば、自分が取り組んだコンペティションには終了日まで継続して取り組んでみてほしいです。Twitterのタイムラインなどで一喜一憂したり、終了後に共有される上位者の解法から多くのことを学んだり、Kaggleの醍醐味が味わえるかと思っています。

その意味では、締切2週間前くらいのコンペティションに参加するのも1つの手だと思います(コンペティションの参加は締切1週間前に締め切られる点には注意してください)。最終的な成績を決める際に使われる自分の提出は忘れずに選ぶようにしましょう85

さらなる学びのために

最後に、さらなる学びに向けたリンクをいくつか掲載します。

Kaggleで勝つデータ分析の技術

データ分析コンペのテーブルデータコンペに注力して書かれた「教科書」です86。体系的かつ網羅的に知見がまとまっています。技法のみならず筆者および関係者の実体験に基づいた集合知も言語化されている点も魅力です。GitHubでソースコードも公開されています。

kagger-ja slack

主に日本人のKagglerが集まっているSlackのワークスペースです87。質問が飛び交うチャンネルやコンペティションの解法を共有するチャンネルなどが活発で、参加して閲覧しているだけでも多くの知見が得られると思います。メールアドレスをフォームに入力するだけで、誰でも参加可能です。

過去ログはこちら88で公開されています。

kaggler-ja wiki

kagger-ja slackで話題になった内容などを体系的にまとめたページです89。次のようなコンテンツがまとめられています。

  • kaggle初心者ガイド
  • なんでもkaggle関連リンク
  • よくある質問
  • 過去コンペ情報

Kaggle Tokyo Meetupの動画・資料

Kaggle Tokyo Meetupは、Kagglerが一堂に会すイベントです。上位に入賞した方が解法をプレゼンしたり、多種多様なLTがあったり、学びの多いイベントです。

過去6回909192939495開催されており、特に第4回は動画96も公開されているのでオススメです。

tkm2261さんのKaggle入門動画

次のコンテンツが動画で用意されています97

  • Kaggleについて
  • Porto Seguroコンペについて
  • GCP立ち上げ(アカウント作ると付いてくる$300クーポン使用)
  • Bitbucketでgitリポジトリ作成
  • GCPの使い方
  • Ubuntuセットアップ
  • Anacondaセットアップ
  • Pythonコーディング (Pandas, scikit-learn, xgboost)
  • Gitの使い方
  • loggerの使い方
  • ロジスティック回帰
  • Cross Validation解説
  • Grid Search解説
  • xgboost解説
  • one hot encoding解説
  • Kaggleの提出方法
  • おまけ: 私の過去コンペのコードの解説

twitter Kaggle リスト

私が日々見ているKagglerを集めたtwitterのリストです98。Kaggleに取り組む方々の日常から、さまざまな刺激を受けています。

Weekly Kaggle News

日本語で、Kaggleをはじめとするデータ分析コンペティションに関する話題を取り扱っているニューズレターです99。週次で、毎週金曜ごろに更新されています。

おわりに

本記事では「Kaggleに登録したら次にやること」と題して、Kaggleに入門したい方に向けた情報を掲載しました。少しでも多くの方のお役に立てていれば幸いです。


  1. https://upura.hatenablog.com 

  2. https://www.kaggle.com/c/petfinder-adoption-prediction 

  3. https://www.kaggle.com/c/santander-value-prediction-challenge 

  4. https://www.kaggle.com/progression/ 

  5. https://www.kaggle.com/sishihara 

  6. https://www.kaggle.com/c/titanic 

  7. https://qiita.com/drken/items/fd4e5e3630d0f5859067 

  8. https://upura.hatenablog.com/entry/2019/04/04/221252 

  9. https://www.kaggle.com/ 

  10. https://www.slideshare.net/HaradaKei/devsumi-2018summer 

  11. https://www.slideshare.net/ssuserafaae8/for-manabiya 

  12. https://speakerdeck.com/amaotone/what-is-kaggle 

  13. https://github.com/Kaggle/kaggle-api 

  14. http://www.currypurin.com/entry/2018/kaggle-api 

  15. https://numpy.org/ 

  16. https://pandas.pydata.org/ 

  17. https://www.kaggle.com/c/titanic/data 

  18. https://www.kaggle.com/bminixhofer/deterministic-neural-networks-using-pytorch 

  19. https://upura.hatenablog.com/entry/2018/10/25/154655 

  20. https://upura.hatenablog.com/entry/2019/02/22/221125 

  21. https://www.amazon.co.jp/dp/4873118689 

  22. https://www.slideshare.net/mlm_kansai/kaggle-138546659 

  23. https://naotaka1128.hatenadiary.jp/entry/kaggle-compe-tips 

  24. https://lightgbm.readthedocs.io/en/latest/ 

  25. https://lightgbm.readthedocs.io/en/latest/Python-Intro.html 

  26. https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html 

  27. https://scikit-learn.org/stable/supervised_learning.html 

  28. https://tebasakisan.hatenadiary.com/entry/2019/01/27/222102 

  29. https://xgboost.readthedocs.io/en/latest/ 

  30. https://catboost.ai/ 

  31. https://pytorch.org/ 

  32. https://www.tensorflow.org/ 

  33. https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html 

  34. https://github.com/fmfn/BayesianOptimization 

  35. https://github.com/hyperopt/hyperopt 

  36. https://research.preferred.jp/2018/12/optuna-release/ 

  37. https://lightgbm.readthedocs.io/en/latest/Parameters.html 

  38. https://lightgbm.readthedocs.io/en/latest/Parameters-Tuning.html 

  39. https://lightgbm.readthedocs.io/en/latest/Parameters-Tuning.html#for-better-accuracy 

  40. https://lightgbm.readthedocs.io/en/latest/Parameters.html 

  41. https://nykergoto.hatenablog.jp/entry/2019/03/29/%E5%8B%BE%E9%85%8D%E3%83%96%E3%83%BC%E3%82%B9%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%81%A7%E5%A4%A7%E4%BA%8B%E3%81%AA%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%81%AE%E6%B0%97%E6%8C%81%E3%81%A1 

  42. https://optuna.readthedocs.io/en/latest/reference/trial.html 

  43. https://qiita.com/phorizon20/items/1b795beb202c2dc378ed 

  44. https://upura.hatenablog.com/entry/2018/12/04/205200 

  45. https://upura.hatenablog.com/entry/2018/12/04/224436 

  46. https://mlwave.com/kaggle-ensembling-guide/ 

  47. https://www.kaggle.com/c/home-credit-default-risk 

  48. https://amalog.hateblo.jp/entry/kaggle-home-credit 

  49. https://www.kaggle.com/willkoehrsen/introduction-to-manual-feature-engineering 

  50. https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.merge.html 

  51. https://japan.zdnet.com/article/35140207/ 

  52. https://arxiv.org/abs/1312.6199 

  53. https://arxiv.org/abs/1406.2661 

  54. http://cs231n.stanford.edu/ 

  55. http://cs231n.stanford.edu/slides/2018/cs231n_2018_lecture11.pdf 

  56. https://nips.cc/ 

  57. https://www.kaggle.com/c/nips-2017-non-targeted-adversarial-attack/ 

  58. https://research.preferred.jp/2018/04/nips17-adversarial-learning-competition/ 

  59. https://arxiv.org/abs/1412.6572 

  60. https://www.kaggle.com/c/generative-dog-images 

  61. https://speakerdeck.com/hirune924/generative-dog-images 

  62. https://speakerdeck.com/hirune924/generative-dog-images 

  63. https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html 

  64. https://www.cs.toronto.edu/~kriz/cifar.html 

  65. https://www.kspub.co.jp/book/detail/1529120.html 

  66. https://www.robots.ox.ac.uk/~vgg/research/affine/det_eval_files/lowe_ijcv2004.pdf 

  67. https://www.kspub.co.jp/book/detail/1529120.html 

  68. https://japan.zdnet.com/article/35140207/ 

  69. https://speakerdeck.com/phalanx/imet-7th-place-solution-and-my-approach-to-image-data-competition?slide=30 

  70. https://www.deeplearningbook.org/front_matter.pdf 

  71. https://www.kaggle.com/c/quora-insincere-questions-classification/ 

  72. https://www.kaggle.com/c/jigsaw-unintended-bias-in-toxicity-classification 

  73. https://qiita.com/Hironsan/items/11b388575a058dc8a46a 

  74. https://www.randpy.tokyo/entry/word2vec_skip_gram_model 

  75. https://arxiv.org/abs/1805.09843 

  76. https://www.kaggle.com/abhishek/approaching-almost-any-nlp-problem-on-kaggle 

  77. https://arxiv.org/abs/1810.04805 

  78. https://arxiv.org/abs/1906.08237 

  79. https://www.kaggle.com/competitions 

  80. https://upura.hatenablog.com/entry/2019/03/16/200000 

  81. https://www.kaggle.com/docs/competitions 

  82. https://www.kokokocococo555.com/entry/2018/08/29/223655 

  83. http://kaggler-ja-wiki.herokuapp.com/kaggle%E5%88%9D%E5%BF%83%E8%80%85%E3%82%AC%E3%82%A4%E3%83%89#%E3%82%AA%E3%83%AC%E3%82%AA%E3%83%ACKaggle%E5%85%A5%E9%96%80%E6%96%B9%E6%B3%95 

  84. https://www.kaggle.com/c/santander-customer-transaction-prediction 

  85. https://upura.hatenablog.com/entry/2019/04/06/114802 

  86. https://gihyo.jp/book/2019/978-4-297-10843-4 

  87. https://kaggler-ja.herokuapp.com/ 

  88. https://kaggler-ja-slack-archive.appspot.com/ 

  89. http://kaggler-ja-wiki.herokuapp.com/ 

  90. https://atnd.org/events/74953 

  91. https://techplay.jp/event/613561 

  92. http://yutori-datascience.hatenablog.com/entry/2017/10/29/205433 

  93. https://connpass.com/event/82458/presentation/ 

  94. https://connpass.com/event/105298/presentation/ 

  95. https://connpass.com/event/132935/presentation/ 

  96. https://www.youtube.com/watch?v=VMjnhGW2MgU&list=PLkBjLQIGEjJlciM9lEz1AsuZZ8lDgyxDu 

  97. http://yutori-datascience.hatenablog.com/entry/2017/10/24/215647 

  98. https://twitter.com/upura0/lists/kaggle1 

  99. https://www.getrevue.co/profile/upura 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした