2018年12月20日

機械学習ライブラリApache MADlibで決定木を使ってKaggleのTitanicを解く

この記事は PostgreSQL Advent Calendar 2018 のDay20の記事です。昨日19日は U_ikki さんによるPostgreSQL 12の新機能の話でした。

以前から興味はあるのだけれどなかなか手を付けられなかったものの中に「Kaggleにチャレンジしてみる」というものがありました。

「趣味はKaggleを少々嗜んでおりまして」とか言ってみたい。

そんなことをずっと考えていたのですが、最近ようやくKaggleデビューしました。

本エントリではPostgreSQLで使える機械学習ライブラリであるApache MADlibを使って、Kaggleの「チュートリアル」と言われているTitanicの問題を解いてみます。

■Kaggle Titanicとは


Titanicは、Kaggle初心者のために準備されているチュートリアルの問題(Competition)のことで、以下のページから参照できます。
簡単に言うと、

「タイタニックで生き残った乗客と亡くなった乗客を記録した訓練用データ(トレーニングデータ)があり、その乗客の属性情報などを元に予測モデルを作成し、予測用データ(テストデータ)に掲載されている乗客が生き残るかどうかを予測し、その予測精度を競う」

というものです。

インターネット上には初心者向けに取り組み方を解説したページもいろいろとあり、私は以下のページを参考にまずは「Python + Jupyter」で実装してみました。
その結果は以下のJupyter Notebookにまとめてあります。
この内容を踏襲して、今回はPostgreSQLとApache MADlibでTitanicの予測を実装してみます。

■訓練用データと予測用データをPostgreSQLにロードする


Titanicの訓練用データと予測用データがCSVファイルで提供されていますので、まずはこれをPostgreSQLにロードします。

ほとんどのカラムが整数、文字列、浮動小数点のいずれかになるかと思います。

最初に訓練用データをロードします。このテーブルのSurvivedカラムが、その乗客が生存したかどうかを示すフラグになります。
titanic=# CREATE TABLE titanic_train (
titanic(#   PassengerId INTEGER,
titanic(#   Survived INTEGER,
titanic(#   Pclass INTEGER,
titanic(#   Name TEXT,
titanic(#   Sex TEXT,
titanic(#   Age DOUBLE PRECISION,
titanic(#   SibSp INTEGER,
titanic(#   Parch INTEGER,
titanic(#   Ticket TEXT,
titanic(#   Fare DOUBLE PRECISION,
titanic(#   Cabin TEXT,
titanic(#   Embarked TEXT
titanic(# );
CREATE TABLE
titanic=# \COPY titanic_train FROM 'train.csv' WITH (FORMAT CSV, DELIMITER ',', HEADER true);
COPY 891
titanic=# \d titanic_train
                  Table "public.titanic_train"
   Column    |       Type       | Collation | Nullable | Default
-------------+------------------+-----------+----------+---------
 passengerid | integer          |           |          |
 survived    | integer          |           |          |
 pclass      | integer          |           |          |
 name        | text             |           |          |
 sex         | text             |           |          |
 age         | double precision |           |          |
 sibsp       | integer          |           |          |
 parch       | integer          |           |          |
 ticket      | text             |           |          |
 fare        | double precision |           |          |
 cabin       | text             |           |          |
 embarked    | text             |           |          |

titanic=# SELECT * FROM titanic_train LIMIT 5;
 passengerid | survived | pclass |                        name                         |  sex   | age | sibsp | parch |      ticket      |  fare   | cabin | embarked
-------------+----------+--------+-----------------------------------------------------+--------+-----+-------+-------+------------------+---------+-------+----------
           1 |        0 |      3 | Braund, Mr. Owen Harris                             | male   |  22 |     1 |     0 | A/5 21171        |    7.25 |       | S
           2 |        1 |      1 | Cumings, Mrs. John Bradley (Florence Briggs Thayer) | female |  38 |     1 |     0 | PC 17599         | 71.2833 | C85   | C
           3 |        1 |      3 | Heikkinen, Miss. Laina                              | female |  26 |     0 |     0 | STON/O2. 3101282 |   7.925 |       | S
           4 |        1 |      1 | Futrelle, Mrs. Jacques Heath (Lily May Peel)        | female |  35 |     1 |     0 | 113803           |    53.1 | C123  | S
           5 |        0 |      3 | Allen, Mr. William Henry                            | male   |  35 |     0 |     0 | 373450           |    8.05 |       | S
(5 rows)

titanic=#

次に予測用データをロードします。こちらは作成した予測モデルを使って予測をするためのデータですので、訓練用データにあったSurvivedカラムはありません。このSurvivedフラグを予測するのが今回の目的になります。
titanic=# CREATE TABLE titanic_test (
titanic(#   PassengerId INTEGER,
titanic(#   Pclass INTEGER,
titanic(#   Name TEXT,
titanic(#   Sex TEXT,
titanic(#   Age DOUBLE PRECISION,
titanic(#   SibSp INTEGER,
titanic(#   Parch INTEGER,
titanic(#   Ticket TEXT,
titanic(#   Fare DOUBLE PRECISION,
titanic(#   Cabin TEXT,
titanic(#   Embarked TEXT
titanic(# );
CREATE TABLE
titanic=# \COPY titanic_test FROM 'test.csv' WITH (FORMAT CSV, DELIMITER ',', HEADER true);
COPY 418
titanic=# \d titanic_test
                   Table "public.titanic_test"
   Column    |       Type       | Collation | Nullable | Default
-------------+------------------+-----------+----------+---------
 passengerid | integer          |           |          |
 pclass      | integer          |           |          |
 name        | text             |           |          |
 sex         | text             |           |          |
 age         | double precision |           |          |
 sibsp       | integer          |           |          |
 parch       | integer          |           |          |
 ticket      | text             |           |          |
 fare        | double precision |           |          |
 cabin       | text             |           |          |
 embarked    | text             |           |          |

titanic=# SELECT * FROM titanic_test LIMIT 5;
 passengerid | pclass |                     name                     |  sex   | age  | sibsp | parch | ticket  |  fare   | cabin | embarked
-------------+--------+----------------------------------------------+--------+------+-------+-------+---------+---------+-------+----------
         892 |      3 | Kelly, Mr. James                             | male   | 34.5 |     0 |     0 | 330911  |  7.8292 |       | Q
         893 |      3 | Wilkes, Mrs. James (Ellen Needs)             | female |   47 |     1 |     0 | 363272  |       7 |       | S
         894 |      2 | Myles, Mr. Thomas Francis                    | male   |   62 |     0 |     0 | 240276  |  9.6875 |       | Q
         895 |      3 | Wirz, Mr. Albert                             | male   |   27 |     0 |     0 | 315154  |  8.6625 |       | S
         896 |      3 | Hirvonen, Mrs. Alexander (Helga E Lindqvist) | female |   22 |     1 |     1 | 3101298 | 12.2875 |       | S
(5 rows)

titanic=#

■訓練用データを使って予測モデルを作成する


ここまで出来たら、MADlibの決定木を使って予測モデルを作成してみます。

Pythonで実行する際に参考にした こちらの入門記事 ではデータの前処理を行っていますが、ここでは敢えて前処理無しでモデルを作成してみます。

MADlibの決定木の使い方は以下から参照できます。
学習用データからモデルを作成するためには tree_train() 関数を使います。

必須の引数は以下の通りです。
  • training_table_name 学習用テーブル名
  • output_table_name 作成したモデルの出力テーブル名
  • id_col_name 学習用データのレコードを特定する識別子のカラム名
  • dependent_variable 従属変数(結果)のカラム名
  • list_of_features 独立変数のカラム名のリスト
それでは、これらの引数を指定して予測モデルを作成します。作成したモデルは titanic_model というテーブルに保存されます。

titanic=# SELECT madlib.tree_train(
titanic(#   'titanic_train',
titanic(#   'titanic_model',
titanic(#   'passengerid',
titanic(#   'survived',
titanic(#   'pclass,sex,age,fare'
titanic(# );
 tree_train
------------

(1 row)

titanic=# \d titanic_model
                    Table "public.titanic_model"
       Column       |     Type      | Collation | Nullable | Default
--------------------+---------------+-----------+----------+---------
 pruning_cp         | integer       |           |          |
 tree               | madlib.bytea8 |           |          |
 cat_levels_in_text | text[]        |           |          |
 cat_n_levels       | integer[]     |           |          |
 tree_depth         | integer       |           |          |

titanic=# 

■作成した予測モデルとテストデータを使って予測をする


次に、今作成した予測モデルと、先ほどロードしておいたテストデータを使って予測を行ってみます。

予測をするには tree_predict() 関数を使います。

必須の引数は
  • tree_model 予測モデルを保存したテーブル名
  • new_data_table テストデータを保存してあるテーブル名
  • output_table 予測結果を出力するテーブル名
となります。

titanic=# SELECT madlib.tree_predict(
titanic(#   'titanic_model',
titanic(#   'titanic_test',
titanic(#   'titanic_predict');
 tree_predict
--------------

(1 row)

titanic=# 

予測結果のテーブルを見てみると、乗客IDと生存したかどうかを予測した出力(estimated_survived)がペアで出力されています。

titanic=# \d titanic_predict
                Table "public.titanic_predict"
       Column       |  Type   | Collation | Nullable | Default
--------------------+---------+-----------+----------+---------
 passengerid        | integer |           |          |
 estimated_survived | integer |           |          |

titanic=# select * from titanic_predict limit 5;
 passengerid | estimated_survived
-------------+--------------------
         892 |                  0
         893 |                  1
         894 |                  0
         895 |                  0
         896 |                  0
(5 rows)

titanic=#

■予測結果をKaggleに投稿して予測精度を確認する


それでは、最後に予測結果をCSVファイルにエクスポートして、Kaggleに投稿して予測精度を確認してみます。

CSVファイルのカラムヘッダが passengerid と survived である必要があるため、カラム名にエイリアスを指定してCSVファイルにエクスポートします。

titanic=# \copy (select passengerid, estimated_survived survived from titanic_predict) to 'titanic_predict.csv' with (format csv, header true)
COPY 418
titanic=#

エクスポートしたCSVファイルをKaggleのTitanicのページの「Submit Predictions」から投稿すると、自分の予測精度と予測精度のランキングが表示されます。


今回の場合だと、予測精度は79%弱、ランキングは3571位(本エントリ執筆時点)だったようです。

なお、今回Pythonで解くときに参考にした scikit-learn を使った入門記事 では、チューニング前の予測精度は71%程度だったようですので、それと比べてもなかなか良い結果が出ているのではないかと思います。

今回は細かなパラメータのチューニングを行いませんでしたが、MADlibのマニュアルを見ていただくと、さまざまなパラメータを指定できることがお分かりいただけるかと思います。興味のある方は、ぜひモデル作成時のパラメータをチューニングして試してみていただければと思います。

■まとめ


以上、簡単にではありましたが、Apache MADlibを使ってTitanicを解く手順をご紹介しました。

データベースの中にあるデータに対して直接機械学習を活用できるというのは、さまざまな可能性があるように思います。

機械学習についてはさまざまな参考書が出ていますし、Pythonで使えるscikit-learnの解説などもたくさんあります。MADlibに興味を持った方は、そういった参考資料をMADlibのマニュアルと読み比べながら、いろいろと試してみていただければと思います。

では。

PostgreSQL Advent Calendar 2018、明日21日の担当は tom-sato さんです。contribモジュールについて書いていただけるようです。お楽しみに!

0 件のコメント:

コメントを投稿