①Elixir＋KerasでKaggleタイタニック予測を解いてみた ～UIでデータサイエンスする前処理／MLプラットフォーム「Esuna」～

（この記事は「Elixir Advent Calendar 2018」の23日目です）

[fukuoka.ex](https://fukuokaex.connpass.com/)代表のpiacereです
ご覧いただいて、ありがとうございます :bow:

fukuoka.ex#11で、αバージョンを初お披露目した「Esuna」は、「データ前処理」「機械学習（ディープラーニング）モデル構築／呼出」「ビジュアライゼーション」を搭載した、データサイエンスプラットフォームです

どの機能も、WebUIで操作でき、誰にでも使えることが特徴です

Elixir／Phoenix＋Vue.js＋Chart.jsで開発しています

モデル構築としては、Kerasのコードを生成することが可能です
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/9c817a06-a059-1f04-5bec-eabd2275bf79.png)
今回、このEsunaの「前処理」と「機械学習」の機能を使い、「Kaggle」のチュートリアル的なコンペである[「Titanic: Machine Learning from Disaster」](https://www.kaggle.com/c/titanic/)、つまり「タイタニック号乗客の生存予測（以降、タイタニック予測と省略）」を解いていく過程を解説したいと思います
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/58cfa81f-f280-a224-2864-4ea2257880ae.png)

なお、[「Phoenix」](https://qiita.com/piacerex/items/b7787580fce5f148242f#phoenix%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)は、ElixirのWebフレームワークです

# はじめに

このコラムは、以下の三部構成になっており、うち「第一部：データに前処理を施す」を本コラムで解説、第二部以降は、後編の別コラムで解説します

<b>第一部：データに前処理を施す
第二部：機械学習（Keras）モデル構築とテスト
第三部：テスト結果をKaggleに提出する</b>

後編は、以下になります
　|> <b>[Elixir＋KerasでKaggleタイタニック予測を解いてみた②](https://qiita.com/piacere_ex/items/771389e1fe66dcf795c5)
　　　[～データサイエンスプラットフォーム「Esuna」はUIでAI・MLを生成～](https://qiita.com/piacere_ex/items/771389e1fe66dcf795c5)</b>

# 第一部：前処理

## 前処理対象のCSVファイルをKaggleからダウンロード

今回は、[Kaggleの「Titanic: Machine Learning from Disaster」からダウンロードする2つのCSV（train.csv、test.csv）](https://www.kaggle.com/c/titanic/data)が対象データとなりますので、手元にダウンロードします
[![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/0ca29552-09e9-535c-d6e8-6aad91f17ab4.png)](https://www.kaggle.com/c/titanic/data)

## 「Data Processing」メニューからCSVをインポート

Esunaは、画面上部の「Data Preprocessing」メニューをクリックすると出てくる画面で、前処理を設定していきますが、最初にやることは、画面上部の「data import」からデータをインポートすることです

Excel／CSV／DB／APIといった、様々なデータタイプが指定できますが、今回は「CSV」を選択した後、「Load」ボタンをクリックして、先ほど入手したtrain.csvを選択します
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/e0b273b4-0049-69e6-4d7c-5e0a1b207851.png)
選択したファイルが、Esuna側にアップロードされ、画面にCSV内容が表示されます
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/428fac12-921d-0efd-81fe-3b2386794554.png)
CSVデータが読み込まれた上部には、データの統計情報（型、バリエーション数、分布グラフ）が表示されるので、どんなデータ傾向かは、一目で分かります

たとえば、乗客の階級を示す「Pclass」は、3種類の整数で構成されていることがEsunaで自動判別されていて、実際にデータを見ると、1（金持ち上層クラス）～3（労働階級下層クラス）の整数のみ存在するデータであることが確認できます

![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/bb62a999-6e31-2900-2a49-f7361b0ac43d.png)

## 前処理①：特徴量にならない情報（列）を削除

前処理の基本は、まず、「特徴量（≒列のこと）となり得ない情報を削除」することが、最も肝心です

AI・MLのリテラシーがあまり無いと、あるあるなネタですが、「データが沢山あれば、望んだ予測ができるだろう」という誤っているけど、何故か盲信されている「幻想」があります

実際は、予測に貢献する特徴量は、ごく限られていることが多く、PCA（主成分分析）やImpurity（不純度）、NMF（非負値行列因子分解）で見ていくと、支配的な特徴量と、そうでないものの差が大きく出ます

ひらたく言えば、「大半の予測は、重要度の高い少量のデータだけで決められる」ということであり、そのためには、あまり貢献しない特徴量を削減（≒次元削減と言います）することが非常に大事です（データが減れば、学習や予測にかかる計算負荷も下がるので、一石二鳥です）

さて、タイタニック予測においては、以下の列が、各理由から削減対象と捉えます

- チケット番号「Ticket」
  - チケット番号そのものは、生存率に直接関与しないため
  - 近い番号の方が、生存率の高い／低い部屋番号にまとまって配置された可能性は考えられるが、仮説の域を出ないので、いったん削除
- 乗客名「Name」
  - 乗客名は、全員が異なるため、特徴量になり得ない
  - 名字が同じで、チケット番号が近い or 部屋が近い等であれば、家族乗船の可能性があり、家族全員がボートに乗れるまで待ったとき生存率が低くなる、といった仮説は考えられるが、トリッキーな処理が必要なため、他の手段を先に試す
- 部屋番号「Cabin」
  - 部屋番号は、生存率にとても高い相関性を持っているはずですが、Cabinは891件中、687件と大量のデータが欠損しているため、使い物にならないと判断し、削除

なお、訓練データ／テストデータのどちらにも存在する、乗客をユニークに識別するID「PassengerId」は、特徴量としては不要ですが、予測データには必要なため、前処理段階では削除しません

また、訓練データのみに存在する、生存／死亡を表す正解データ「Survived」についても、特徴量としては不要ですが、モデル構築の際に使うため、前処理段階では削除しません

それでは、Esunaで、この3列の削除を行います

「Ticket」の見出しをクリックすると、前処理用のプロパティウインドウが開きます
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/41db1b01-7cbb-49c0-6e99-6013e5b0a1d8.png)
列削除するには、「exclude_column」にチェックを入れます
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/5d7fc420-8896-8ffe-1e1e-fb68d8403a8c.png)
同じ要領で、「Name」「Cabin」の「exclude_column」にもチェックを入れていきます

画面上部少し下にある「Processed Data」タブをクリックすると、3列が削除されていることを確認できます
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/78580959-36fe-b5fa-ee18-09126a9d4231.png)

## 前処理②：欠損値を補完

次は、少量の欠損値（≒空欄の値）がある以下2列の欠損値補完を行います

- 年齢「Age」
  - 空欄は、空欄以外の平均値で補完することとします
  - 今回のデータでは、891件中、177件と20%近くを占めるため、平均値に偏ることが予想されます
  - 欠損がある行自体を削除した方が良いかも知れませんが、いったん補完することとします
- 乗船した港「Embarked」
  - S=Southampton（サウサンプトン）：イギリス南部の都市
  - Q=Queenstown（クィーンズタウン）：ニュージーランド南西部の町
  - C=（シェルブール・オクトヴィル）：フランス北西部の都市
  - この3つのうち、最も多い「S」で補完します（対象データ2件なので影響は極小）

それでは、補完していきましょう

Ageの補完には、数値補完を行う「impute_missing_number」を使い、欠損していない全員の年齢の平均値（avg）での補完を設定します
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/c207b918-4de4-41fc-0c99-fed0a0ba919e.png)
Ageが、平均値29.7歳で補完されていることが確認できます
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/328959f3-324f-ae06-3704-bf2be640bd96.png)
Embarkedの補完には、文字列補完を行う「impute_missing_string」を使い、手入力で「S」での補完を設定します
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/0e6cfe34-30a0-0a5b-4570-065ce151e359.png)
PassengerId=62等のEmbarkedは、「S」で補完されたことが確認できます
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/2197d7eb-68ca-5f51-bed0-bd7753ca9cc9.png)

## 前処理③：カテゴリ値を数値に変換

機械学習／ディープラーニングに入力するデータは、基本的に、数値以外は受け付けないため、以下のような、幾つかの文字列バリエーション、つまり「カテゴリ値」で表される列は、数値への変換が必要です

- 性別「Sex」
- 乗船した港「Embarked」

カテゴリ値の数値変換には、「one_hot_encoding」を使います
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/45a3f074-7a6b-9f0f-e7eb-ed65ee23be39.png)
「male」「female」の2つのカテゴリ値だった「Sex」は、この変換により、「0」「1」という数値に変わります
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/51e69787-c31c-980a-98ee-5d7dcaca2ee8.png)
Embarkedも同様に、「one_hot_encoding」します
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/94a86855-41e0-cdb5-c4af-e7d59e6cf1b0.png)
「S」「C」「Q」が、「0」「1」「2」へと変換されました
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/c8b66b83-23c0-c245-0c37-f066deee5881.png)

## 前処理済みデータのCSVエクスポート

ここまでの操作で、特徴量になり得ない3列が削除され、残り9列についても、欠損値が無くなり、数値のみのデータとなったので、前処理済みの訓練データとして、CSVエクスポートします

「Processed Export CSV」をクリックすると、train_processed.csvというファイルが作成されます
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/38db5efa-41f8-66fb-c3e9-03f08de324e5.png)
これで、train.csvの前処理は完了です

同じ前処理を、test.csvに対しても実施したい（≒前処理が訓練データとテストデータで異なると、モデルがうまく動作しないため）ので、画面上部の「data import」からtest.csvをインポートし、「Processed Export CSV」を行い、test_processed.csvというファイルも作成します

## 前処理は、Esunaが生成したElixirコードで実行される

先ほどまで見てきた、前処理後データは、実は裏でEsunaが動的に生成するElixirコードによって、実現されています

ここで生成された、前処理Elixirコードを見てみましょう

```elixir
defmodule Preprocessor do
	def rows( csv_file_path ) do
		csv_file_path
		|> Importer.csv_load_map
		|> Enum.map( &( Map.drop( &1, [ "Cabin", "Name", "Ticket" ] ) ) )
		|> MapList.imputer( [ "Age" ], "", "avg" )
		|> MapList.fill( [ "Embarked" ], "S", "" )
		|> MapList.get_dummies( [ "Embarked", "Sex" ] )
	end
	def columns( csv_file_path ) do
		csv_file_path
		|> Importer.csv_load
		|> List.first
	end
end
```

ImporterモジュールによるCSVファイルのロード／変換や、MapListモジュールによるリストマップの複数列操作と、Elixirのパイプにより、かなり直観的なコードになっています

また、Pythonでpandasによるデータ操作をやったことがあれば、それと似たような操作が、Esunaでは可能なことが分かります

なお、このEsunaで使っているモジュール群は、別途切り出し、scikit-learnやpandasに相当するようなツールキットとして、そのうちOSS公開予定です

# 終わり

第一部は、以上となります

Kaggleのタイタニック予測にて、データサイエンスプラットフォーム「Esuna」を使い、前処理の解説を行いましたが、Esunaでの前処理は、非常にカンタンなUIから操作できることが、実感できたのでは無いかなぁ、と思います

まもなく、OSSとして公開されるEsunaを、楽しみにお待ちください :wink:

<b>[→ Kaggleに参戦した結果も含む、第二部以降は、コチラからご覧になれます](https://qiita.com/piacere_ex/items/771389e1fe66dcf795c5)</b>

# p.s.「いいね」よろしくお願いします

ページ左上の ![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/4d515047-cc48-382e-c2b1-3ad0cc50dbbf.png) や ![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/a4e3da58-70a3-4197-95a2-6a6906650d01.png) のクリックを、どうぞよろしくお願いします:bow:
ここの数字が増えると、書き手としては「ウケている」という感覚が得られ、連載を更に進化させていくモチベーションになりますので、もっとElixirネタを見たいというあなた、私達と一緒に盛り上げてください！:tada: