Entity Embeddings を使ってタイタニック号生存者を予測する深層学習モデルを作る

<p>
  Entity Embeddingsという深層学習の手法があります。深層学習がよく使われる画像分析や音声分析などのデータとは違う、<strong>カテゴリ変数</strong>や<strong>順序変数</strong>の特徴量を学習する時に使います。
</p>
<p>
  Entity Embeddingsが広く知られるようになったきっかけは、Kaggleの<a href="https://www.kaggle.com/c/rossmann-store-sales" target="_blank" rel="noopener noreferrer">Rossmann Store Sales</a>コンペでした。１位と２位のチームがドメイン知識をフル活用したアプローチをしたのに対し、この手法を活用したチームはドメイン知識の無い中なんと３位に入賞しました。コンペの説明と、使われた手法については、３位のNeokami Incの<a href="http://blog.kaggle.com/2016/01/22/rossmann-store-sales-winners-interview-3rd-place-cheng-gui/" target="_blank" rel="noopener noreferrer">インタビュー記事</a>、<a href="https://github.com/entron/entity-embedding-rossmann" target="_blank" rel="noopener noreferrer">使われたソースコード</a>、コンペ後に発表した手法に関する<a href="https://arxiv.org/abs/1604.06737" target="_blank" rel="noopener noreferrer">論文</a>などで学ぶことができます。
</p>

<p>
  <a href="https://www.kaggle.com/c/titanic" target="_blank" rel="noopener noreferrer">タイタニック号生存者予測コンペ</a>のサンプルデータに対し、このEntity Embeddingsを実装するにはどうすれば良いのでしょうか。
</p>

<h3>0. 環境構築</h3>
<p>
  環境構築に私は<a href="https://pipenv.readthedocs.io/en/latest/" target="_blank" rel="noopener noreferrer">Pipenv</a>を使っています。venv、conda、pip+virtualenv、pyenvなど、他のツールを使っている方はそれを使ってください。今回はPipenvを使った書き方のみ紹介します。
</p>
<p>
  <a href="https://www.python.org/downloads/release/python-367/" target="_blank" rel="noopener noreferrer">Python3.6</a>を使います。今回使う<a href="https://www.tensorflow.org/versions/r1.12/api_docs/python/tf" target="_blank" rel="noopener noreferrer">Tensorflow (v1.12)</a>がPython3.7以降と互換性が無いため（執筆時）です。また、データ読み込み、前処理、表示などのために<a href="https://pandas.pydata.org/" target="_blank" rel="noopener noreferrer">pandas</a>もインストールします。
</p>

```shell
pipenv --python 3.6
pipenv install tensorflow pandas
```

<p>
  <a href="https://jupyter.org/" target="_blank" rel="noopener noreferrer">Jupyter Notebook</a> で実行するときは、インストールして起動しましょう。
</p>
```shell
pipenv install jupyter
pipenv run jupyter notebook
```


<h3>1. データを読み込む</h3>
<p>
  使うデータはタイタニックコンペのデータです。<a href="https://www.kaggle.com/c/titanic/data" target="_blank" rel="noopener noreferrer">リンク</a>からデータをダウンロードして、解凍しておきます。
</p>
```python
import pandas as pd

train = pd.read_csv("./train.csv")
test = pd.read_csv("./test.csv")

train.head(2)
```


<table>
  <thead>
    <tr>
      <th></th>
      <th>PassengerId</th>
      <th>Survived</th>
      <th>Pclass</th>
      <th>Name</th>
      <th>Sex</th>
      <th>Age</th>
      <th>SibSp</th>
      <th>Parch</th>
      <th>Ticket</th>
      <th>Fare</th>
      <th>Cabin</th>
      <th>Embarked</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>1</td>
      <td>0</td>
      <td>3</td>
      <td>Braund, Mr. Owen Harris</td>
      <td>male</td>
      <td>22.0</td>
      <td>1</td>
      <td>0</td>
      <td>A/5 21171</td>
      <td>7.2500</td>
      <td>NaN</td>
      <td>S</td>
    </tr>
    <tr>
      <th>1</th>
      <td>2</td>
      <td>1</td>
      <td>1</td>
      <td>Cumings, Mrs. John Bradley (Florence Briggs Th...</td>
      <td>female</td>
      <td>38.0</td>
      <td>1</td>
      <td>0</td>
      <td>PC 17599</td>
      <td>71.2833</td>
      <td>C85</td>
      <td>C</td>
    </tr>
  </tbody>
</table>

<p>各項目の説明は<a href="https://www.kaggle.com/c/titanic/data" target="_blank" rel="noopener noreferrer">コンペページ</a>で説明がされています。</p>

<h3>2. データの前処理</h3>
<ul>
  <li>Null値に適当な値を代入</li>
  <li>文字列データは、カテゴリ変数（数字）に変換する</li>
  <li>データから新しい特徴を見つけ、データを増やす</li>
</ul>
<p>
  今回は比較的簡単な処理のみをしています。詳細については割愛しますが、python + pandas ではこんなに少ない行数で様々な処理を実行できます。
</p>

```python
data = pd.concat([train.drop(columns="Survived"), test]).reset_index(drop=True)

data["Pclass"] -= 1
data["FamSize"] = data["SibSp"] + data["Parch"]
data["IsAlone"] = data["FamSize"] == 0
data["Embarked"].fillna(data["Embarked"].mode()[0], inplace=True)
data["Age"].fillna(data["Age"].median(), inplace=True)
data["Fare"].fillna(data["Fare"].median(), inplace=True)
data.drop(columns=["Cabin", "Ticket", "Name"], inplace=True)
for c in ["Sex", "Embarked"]: data[c] = data[c].astype("category").cat.codes
for c in ["Pclass", "Sex", "Age", "SibSp", "Parch", "Embarked", "FamSize", "IsAlone"]: data[c] = data[c].astype(int)

train_X = data.join(train["Survived"], how="inner").drop(columns=["PassengerId", "Survived"])
train_Y = train[["Survived"]]
test_X = data.loc[data["PassengerId"].isin(test["PassengerId"])]
```

<p>全て数字のデータができました。</p>

```python
train_X.head(2)
```

<table>
  <thead>
    <tr>
      <th></th>
      <th>Pclass</th>
      <th>Sex</th>
      <th>Age</th>
      <th>SibSp</th>
      <th>Parch</th>
      <th>Fare</th>
      <th>Embarked</th>
      <th>FamSize</th>
      <th>IsAlone</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>2</td>
      <td>1</td>
      <td>22</td>
      <td>1</td>
      <td>0</td>
      <td>7.2500</td>
      <td>2</td>
      <td>1</td>
      <td>0</td>
    </tr>
    <tr>
      <th>1</th>
      <td>0</td>
      <td>0</td>
      <td>38</td>
      <td>1</td>
      <td>0</td>
      <td>71.2833</td>
      <td>0</td>
      <td>1</td>
      <td>0</td>
    </tr>
  </tbody>
</table>

```python
test_Y.head(2)
```

<table>
  <thead>
    <tr>
      <th></th>
      <th>Survived</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0</td>
    </tr>
    <tr>
      <th>1</th>
      <td>1</td>
    </tr>
  </tbody>
</table>

<h3>3. Entity Embeddingを用いた深層学習モデルを作る</h3>
<p>
  Embeddingを作る対象は、「カテゴリ変数」と「順序変数」です。前処理後のタイタニックデータの中では、「Fare」をのぞいて全てが当てはまります。
</p>
<p>
  今回は<a href="https://www.tensorflow.org/guide/keras" target="_blank" rel="noopener noreferrer">Tensorflowの中のKeras API</a>を使います。<a href="https://www.tensorflow.org/guide/keras#functional_api" target="_blank" rel="noopener noreferrer">Functional API</a>という記法を使って以下のように書きます。
</p>

```python
from tensorflow.keras.layers import Input, Embedding, Flatten

x = Input(shape=(1,))                         # 入力層。
x = Embedding("カテゴリ数", "埋め込み次元数")(x)  # 入力されるカテゴリ数と埋め込み次元数をそれぞれ指定する。
x = Flatten()(x)                              # 次ステップで組み合わせるときのために形を整える。
```

<p>上記で１ブロックとなり、これを<strong>各項目ごと</strong>に作ります。残りのステップは、</p>
<ol>
  <li>埋め込みブロックの出力を全て組み合わせて（Concatenate層）、</li>
  <li>何層か<strong>全結合</strong>（Dense層）<strong>+ReLU層</strong>（Dense層の引数で指定）を足し、</li>
  <li>二値分類（生存したか、していないか）のため、最後を<strong>Sigmoid層</strong>（Dense層の引数で指定）にして完成。</li>
</ol>

<hr/>
<p>「Pclass」のEmbeddingブロックを作る過程で詳しく説明します。</p>

<h4>イ. まずは、<a href="https://www.tensorflow.org/api_docs/python/tf/keras/layers/Input" target="_blank" rel="noopener noreferrer">Input</a>層を使って入力層を作ります。</h4>

```python
from tensorflow.keras.layers import Input
input_Pclass = Input(shape=(1,), name="input_Pclass")
```

<p>name引数を使って、層に名前をつけておきます（後ほど使用します）。</p>
<p>尚、入力する形（shape引数）が (1,) なのは、データ１単位あたり必ず１つの数字のみが入力されるためです。つまり、タイタニック乗客一人に対して</p>
`Pclass = [1]`
<p>このようなデータの単位を持っているからです。例えば、</p>
`data = [0, 1, 3]`
<p>上記が単位あたりのデータの場合、入力する形は (3,)。</p>
`data = [[0, 2], [3, 1], [4, 0]]`
<p>この場合は (3, 2) です。</p>

<h4>ロ. 次に、<a href="https://www.tensorflow.org/api_docs/python/tf/keras/layers/Embedding" target="_blank" rel="noopener noreferrer">Embedding</a>層を使い、Embeddingベクトルを割り当てます。  </h4>

```python
from tensorflow.keras.layers import Embedding
embed_Pclass = Embedding(3, 2, name="embed_Pclass")(input_Pclass)
```

<p>
  Pclassのカテゴリ数は、「0 か 1 か 2」 の「3つ」。尚、<strong>埋め込み次元数はハイパーパラメーターのため、正解が無く、試行錯誤する必要があります</strong>。
</p>
<p>
  埋め込み次元数で推奨される大きさは、<a href="https://developers.googleblog.com/2017/11/introducing-tensorflow-feature-columns.html" target="_blank" rel="noopener noreferrer">Googleのチュートリアル</a>では<strong>「カテゴリー数**0.25」</strong>、大元の論文（<a href="https://arxiv.org/pdf/1604.06737.pdf" target="_blank" rel="noopener noreferrer">セクションV. 5段落目</a>）では、<strong>「カテゴリ数〜カテゴリ数-1の間の数字で試しながら決める」</strong>などと書かれており、まだベストプラクティスが決まっていない状態です。今回は、3つのPclassのカテゴリ変数に対して、大きさ「２」のベクトルをそれぞれ割り当てます。
</p>

<h4>ハ. 最後に、<a href="https://www.tensorflow.org/api_docs/python/tf/keras/layers/Flatten" target="_blank" rel="noopener noreferrer">Flatten</a>層を使って、後ほど他のEmbeddingブロックと結合するときのために形を整えます。</h4>

```python
from tensorflow.keras.layers import Flatten
flatt_Pclass = Flatten(name="flatt_Pclass")(embed_Pclass)
```

<hr/>

<p>以上で１ブロック完成です。以下が全ての項目でこの操作をしたコードです。まず、各項目の最小値、最大値、ユニークな値の数を調べ、それを元に埋め込み次元数を決めていきます。</p>

```python
# データの各項目の「最小値」、「最大値」、「ユニークな値数」を表示
data.agg(["min", "max", "nunique"]).T
```

<table>
  <thead>
    <tr>
      <th></th>
      <th>min</th>
      <th>max</th>
      <th>nunique</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>PassengerId</th>
      <td>1.0</td>
      <td>1309.0000</td>
      <td>1309.0</td>
    </tr>
    <tr>
      <th>Pclass</th>
      <td>0.0</td>
      <td>2.0000</td>
      <td>3.0</td>
    </tr>
    <tr>
      <th>Sex</th>
      <td>0.0</td>
      <td>1.0000</td>
      <td>2.0</td>
    </tr>
    <tr>
      <th>Age</th>
      <td>0.0</td>
      <td>80.0000</td>
      <td>73.0</td>
    </tr>
    <tr>
      <th>SibSp</th>
      <td>0.0</td>
      <td>8.0000</td>
      <td>7.0</td>
    </tr>
    <tr>
      <th>Parch</th>
      <td>0.0</td>
      <td>9.0000</td>
      <td>8.0</td>
    </tr>
    <tr>
      <th>Fare</th>
      <td>0.0</td>
      <td>512.3292</td>
      <td>281.0</td>
    </tr>
    <tr>
      <th>Embarked</th>
      <td>0.0</td>
      <td>2.0000</td>
      <td>3.0</td>
    </tr>
    <tr>
      <th>FamSize</th>
      <td>0.0</td>
      <td>10.0000</td>
      <td>9.0</td>
    </tr>
    <tr>
      <th>IsAlone</th>
      <td>0.0</td>
      <td>1.0000</td>
      <td>2.0</td>
    </tr>
  </tbody>
</table>

```python
# 上記のデータを元に各カテゴリ数や埋め込み次元を決めてブロックを作っていく
from tensorflow.keras.layers import Input, Embedding, Flatten

input_Pclass = Input(shape=(1,), name="input_Pclass")
embed_Pclass = Embedding(3, 2, name="embed_Pclass")(input_Pclass)
flatt_Pclass = Flatten(name="flatt_Pclass")(embed_Pclass)

input_Sex = Input(shape=(1,), name="input_Sex")
embed_Sex = Embedding(2, 4, name="embed_Sex")(input_Sex)
flatt_Sex = Flatten(name="flatt_Sex")(embed_Sex)

input_Age = Input(shape=(1,), name="input_Age")
embed_Age = Embedding(81, 15, name="embed_Age")(input_Age)
flatt_Age = Flatten(name="flatt_Age")(embed_Age)

input_SibSp = Input(shape=(1,), name="input_SibSp")
embed_SibSp = Embedding(9, 7, name="embed_SibSp")(input_SibSp)
flatt_SibSp = Flatten(name="flatt_SibSp")(embed_SibSp)

input_Parch = Input(shape=(1,), name="input_Parch")
embed_Parch = Embedding(10, 8, name="embed_Parch")(input_Parch)
flatt_Parch = Flatten(name="flatt_Parch")(embed_Parch)

input_Embarked = Input(shape=(1,), name="input_Embarked")
embed_Embarked = Embedding(4, 3, name="embed_Embarked")(input_Embarked)
flatt_Embarked = Flatten(name="flatt_Embarked")(embed_Embarked)

input_FamSize = Input(shape=(1,), name="input_FamSize")
embed_FamSize = Embedding(11, 6, name="embed_FamSize")(input_FamSize)
flatt_FamSize = Flatten(name="flatt_FamSize")(embed_FamSize)

input_IsAlone = Input(shape=(1,), name="input_IsAlone")
embed_IsAlone = Embedding(2, 4, name="embed_IsAlone")(input_IsAlone)
flatt_IsAlone = Flatten(name="flatt_IsAlone")(embed_IsAlone)
```

<p>Fareだけは連続値のため、別の処理をします。今回は全結合層（<a href="https://www.tensorflow.org/api_docs/python/tf/keras/layers/Dense"target="_blank" rel="noopener noreferrer">Dense</a>）のみです。全結合層は形が他のEmbeddingブロックの出力層と一緒になるため整える必要はありません。</p>

```python
from tensorflow.keras.layers import Dense
input_Fare = Input(shape=(1,), name="input_Fare")
dense_Fare = Dense(30, name="fc_Fare")(input_Fare)
```

<p>全ての層を<a href="https://www.tensorflow.org/api_docs/python/tf/keras/layers/Concatenate"target="_blank" rel="noopener noreferrer">Concatenate</a>層を使って一つの層にします。「flatt_」で始まる層と、FareのDense層を全てまとめます。</p>

```python
from tensorflow.keras.layers import Concatenate

concat_layers= [
    flatt_Pclass,
    flatt_Sex,
    flatt_Age,
    flatt_SibSp,
    flatt_Parch,
    flatt_Embarked,
    flatt_FamSize,
    flatt_IsAlone,
    dense_Fare,
]

x = Concatenate()(concat_layers)
```

<p><a href="https://www.tensorflow.org/api_docs/python/tf/keras/layers/Activation" target="_blank" rel="noopener noreferrer">Activation</a>層を使って活性化関数（ReLU）を適用してから、全結合＋ReLUのブロックを３つ繋げてみます。全結合層のニューロンの数（第一引数）は適当に選び、トレーニングをしながらチューニングします。</p>

```python
from tensorflow.keras.layers import Activation

x = Activation("relu")(x)
x = Dense(96, activation="relu")(x)
x = Dense(48, activation="relu")(x)
x = Dense(24, activation="relu")(x)
```

<p>最終出力は、二値分類のため、ニューロンを1つだけ持った全結合層にSigmoid層を組み合わせたブロックです。</p>

```python
output_layer = Dense(1, activation="sigmoid")(x)
```

<p>ここまでで作った層を全てまとめて、モデルを定義します。<a href="https://www.tensorflow.org/api_docs/python/tf/keras/models/Model" target="_blank" rel="noopener noreferrer">Model</a>クラスに入力層と出力層を渡すと自動で中身の層を繋げてくれます。以下のように入力します。</p>

```python
from tensorflow.keras.models import Model

input_layers = [
    input_Pclass,
    input_Sex,
    input_Age,
    input_SibSp,
    input_Parch,
    input_Embarked,
    input_FamSize,
    input_IsAlone,
    input_Fare
]

model = Model(input_layers, output_layer)
```

<p>これを図にすると以下のようになります。（このコードを実行するには、実行環境に<a href="https://www.graphviz.org/" target="_blank" rel="noopener noreferrer">Graphviz</a>がインストールされている必要があります。<a href="https://github.com/XifengGuo/CapsNet-Keras/issues/7" target="_blank" rel="noopener noreferrer">参考リンク</a>）</p>

```python
from tensorflow.keras.utils import plot_model
plot_model(model, to_file='model.png')
```

![model.png](https://qiita-image-store.s3.amazonaws.com/0/221631/2c327594-bae5-4136-0f36-c8e7edb9d9f5.png)


<h3>4. 作成したモデルで学習</h3>
<p>学習の際には、いくつかの変数を手動で設定する必要があります。</p>
<ul>
  <li>損失関数は二値分類のため「binary-crossentropy」</li>
  <li>勾配法は「Adam」</li>
  <li>モデルのパフォーマンスは「accuracy」</li>
</ul>

<p>Modelクラスの<a href="https://www.tensorflow.org/api_docs/python/tf/keras/models/Model#compile" target="_blank" rel="noopener noreferrer">compile</a>関数で全て設定できます。</p>

```python
model.compile(loss="binary_crossentropy", optimizer="adam", metrics=["accuracy"])
```

<p>トレーニングにはModelクラスの<a href="https://www.tensorflow.org/api_docs/python/tf/keras/models/Model#fit" target="_blank" rel="noopener noreferrer">fit</a>関数を使います。今回引数に渡す変数について：</p>

* x: 入力データ。pythonのdictionary型を使うことで、各Input層でつけていた名前を使い項目ごとのデータを指定することができます。尚、入力の形を各項目で(batch_size, 1)としたいため、PandasのDataFrame項目の選択の仕方を工夫してます。


```python
X = {
    "input_Pclass": train_X[["Pclass"]],
    "input_Sex": train_X[["Sex"]],
    "input_Age": train_X[["Age"]],
    "input_SibSp": train_X[["SibSp"]],
    "input_Parch": train_X[["Parch"]],
    "input_Embarked": train_X[["Embarked"]],
    "input_FamSize": train_X[["FamSize"]],
    "input_IsAlone": train_X[["IsAlone"]],
    "input_Fare": train_X[["Fare"]],
}
```

* y: 目的変数。つまり<strong>生存したかしていないか</strong>のデータ項目。これはDataFrame型で、入力の形が(batch_size, 1)となるためそのまま使います。

```python
train_Y.head(2)
```


<table>
      <thead>
        <tr>
          <th></th>
          <th>Survived</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th>0</th>
          <td>0</td>
        </tr>
        <tr>
          <th>1</th>
          <td>1</td>
        </tr>
      </tbody>
</table>


* validation_split: 機械学習では、モデルが「過学習」を起こしていないかモニタリングするために、データを<strong>「トレーニング用」と「検証用」</strong>に分けます。fit関数では、ここに分割する割合を入れると自動で分割してくれます。2割のデータをテスト用に分割して分けておく場合：

```python
validation_split = 0.2
```

* epochs: 全データを一周学習することを「1 epoch（エポック）学習する」と言います。何epoch学習するか入力します。

```python
epochs = 100
```

<p>
  1~4の変数をfit関数に引数として渡します。その他はデフォルトを使います。尚、fit関数からは「History」オブジェクトというものが返ります（詳細は後述）。
</p>

```python
history = model.fit(x=x, y=train_Y, validation_split=validation_split, epochs=epochs)
```

<p>出力：</p>

```bash

Train on 712 samples, validate on 179 samples
Epoch 1/100
712/712 [==============================] - 1s 2ms/step - loss: 0.9792 - acc: 0.5281 - val_loss: 0.6805 - val_acc: 0.3855
Epoch 2/100
.....
```

<h3>5. 可視化</h3>
<p>
  Historyオブジェクトには、トレーニング経過の記録（history）や、モデル（model）、トレーニングに用いたハイパーパラメーター（params）などが格納されています。これを用いて、トレーニング経過の記録を可視化します。
</p>
<p>
  History のうち、history を pandas の DataFrame にして、<a href="https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.plot.html" target="_blank" rel="noopener noreferrer">plot</a>関数でプロットしてみます。この関数は裏で<a href="https://matplotlib.org/" target="_blank" rel="noopener noreferrer">matplotlib</a> を使っているため、使うにはmatplotlibをインストールした上で、Jupyter Notebook で使う際にはマジックコマンド（%matplotlib inline）を実行します。
</p>

```bash
pipenv install matplotlib
```

```python
%matplotlib inline
results = pd.DataFrame(history.history)
results.head(2)
```


<table>
  <thead>
    <tr>
      <th></th>
      <th>val_loss</th>
      <th>val_acc</th>
      <th>loss</th>
      <th>acc</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0.680474</td>
      <td>0.385475</td>
      <td>0.979209</td>
      <td>0.528090</td>
    </tr>
    <tr>
      <th>1</th>
      <td>0.656121</td>
      <td>0.687151</td>
      <td>0.696029</td>
      <td>0.558989</td>
    </tr>
  </tbody>
</table>

<p>損失関数のプロット。過学習気味のようです。</p>

```python
results[["loss", "val_loss"]].plot()
```

![loss_x_val_loss.png](https://qiita-image-store.s3.amazonaws.com/0/221631/9ee02b3c-0f7a-4f8b-55d6-292a0801ce76.png)


<p>精度のプロット。二値分類で検証データに対しておよそ８割の精度が出ているようです。</p>

```python
results[["acc", "val_acc"]].plot()
```

![acc_x_val_acc.png](https://qiita-image-store.s3.amazonaws.com/0/221631/77bdac57-e330-da6c-16af-2f5dc196a701.png)


<p>
  また、埋め込み層を可視化することもできます。方法は、<a href="http://projector.tensorflow.org/" target="_blank" rel="noopener noreferrer">Embedding Projector プロジェクトページ</a>に書いてあります。
</p>

<h3>おわりに</h3>
<p>Entity Embeddingを使うと、カテゴリ変数がメインのSQLテーブルやエクセルなどのデータから学習する深層学習モデルを作ることができます。</p>
<p>ただし、この手法が必ず良い結果になるとは限りません。実際上記のモデルのKaggleでのsubmission scoreはおよそ0.7で、非常に悪いです。一方で、<a href="https://scikit-learn.org/stable/" target="_blank" rel="noopener noreferrer">scikit-learn</a>の<a href="https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestRegressor.html" target="_blank" rel="noopener noreferrer">RandomForestRegressor</a>と<a href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html" target="_blank" rel="noopener noreferrer">GridSearchCV</a>を使った簡単な方法でおよそ0.8強の精度（上位10%ライン）が出ます。</p>
<p>Entity Embeddingがもっとも光るのは、以下のケースでは無いかと思っています。</p>
<ul>
  <li>データが膨大すぎて、決定木などのナイーブな方法ではモデルが大きくなり過ぎる等対処ができないとき。</li>
  <li>データに関するドメイン知識が無いことから、Feature Engineeringがあまりできないとき。</li>
</ul>
<p>
  Kaggleのコンペは構造化データを扱うコンペが多いため、Entity Embeddingを使った解法を作ることができる場合が多いと思います。しかし、ランダムフォレストやSVMなどの方が精度が出る可能性もあることを認識しておくのは大事だと思います。
</p>
<p>
  もし上記が間違っている、他のパターンの使い道がある、などあればぜひ教えてください:bow_tone1:
</p>