データサイエンティスト(仮)

元素粒子論博士。今はデータサイエンティスト(仮)。

Pythonでデータ分析:Catboost

導入

2017年7月に、ロシアのGoogleと言われている(らしい)Yandex社から、Catboostと呼ばれるGradient Boostingの機械学習ライブラリが公開されています。

catboost.yandex

ここ何ヶ月か調整さんになっていて分析から遠ざかりがちになりやすくなっていたのですが、手を動かしたい欲求が我慢できなくなってきたので、いい機会だと思って触ってみることにしました。

インストール

anacondaなどでPythonを導入していることを前提にします。その場合、おそらくpipで一瞬でインストールできます。

$ pip install catboost

使用データセット:Titanic

Titanic号の乗客の情報および生存・死亡のデータを用いて、どの程度の精度のモデルができるかを試してみます。

データセットは、Kaggleのページからダウンロードしました。
Titanic: Machine Learning from Disaster | Kaggle

train.csvとtest.csvの2つを利用します。train.csvはSurvived(生存なら1、死亡なら0)のカラムがあるデータ、test.csvはSurvivedのカラムが無いデータになっています。train.csvを用いてモデルを作成し、test.csvからSurvivedの予測値を出し、Kaggleにsubmitすることで、モデルの精度(今回はAccuracy)を見ます。
train.csvのデータはdf_raw、test.csvのデータはdf_raw_testという名前で読み込んでおきます。

データ加工(笑)

Titanicのデータは、いくつか欠損があるカラムがあるのですが、今回は雑に-999で埋めます。また、乗客の名前などの非構造データも幾つかあるのですが、それらは予めインデックスを保持しておき、モデリングの際によしなにカテゴリカル変数として処理してもらうことにします(Catboostはそういった機能があったので、挙動確認も込めてあえて特徴量加工を頑張らないことにしました)。

# pandasやnumpyはインポート済み
# 欠損値補完
df_raw.fillna(-999, inplace=True)
# 説明変数・目的変数
X = df_raw.drop('Survived', axis = 1)
y = df_raw['Survived']
# カテゴリカル変数として扱うカラム指定
categorical_features_indices = np.where(X.dtypes != np.float)[0]

訓練用と検証(Validation)用にデータを分割しておきます。

rom sklearn.model_selection import train_test_split
X_train, X_validation, y_train, y_validation = train_test_split(X, y, test_size = 0.3)

モデリング

今回はクラス分類なので、catboost.CatBoostClassifierという関数を利用します。

import catboost
mod = catboost.CatBoostClassifier(iterations=5000, 
                                  calc_feature_importance = True, 
                                  use_best_model=True,
                                  eval_metric = 'Accuracy' )
mod.fit(X_train, y_train, 
        cat_features=categorical_features_indices,
        eval_set=(X_validation, y_validation), 
        plot=True)

オプションについては、例えば以下のページにあります。
tech.yandex.com
今回は、CatBoostClassifierにはiterations:何回学習と検証を繰り返すか、calc_feature_importance:変数重要度を計算するか、use_best_model:iterationの中で一番検証用の指標が良いものを選ぶか、eval_metric:評価指標をオプションとして選んでいます。

モデル作成は、sklearnのようにfitメソッドで行います。cat_featuresでカテゴリカル変数として扱うカラムを指定し、eval_setで最初のvalidationに使用するデータを指定します。

実行後は、以下のような図がプロットされます。
f:id:tekenuko:20171013225210p:plain
これは、訓練・検証用データに対してAUCをプロットしたものです。点線が訓練用、普通の線が検証用なので、結構過学習しちゃってます(汗)。検証用データで一番良い場合はaccuracyが0.88程度のようです。

from sklearn.metrics import accuracy_score
y_val_pred = mod.predict(X_validation)
print('accuracy : %.2f' % accuracy_score(y_val_pred, y_validation))

# 出力
accuracy : 0.88

テストデータを用いて、予測してみます。これはsklearnの場合と同様に、predictメソッドで出力できます。ただし、floatとして出て来るので、出力後にintに変換します。こうしておかないと、Kaggleにsubmitしたときにscoreが0になります。

# テストデータはdf_raw_test
df_raw_test.fillna(-999, inplace = True)
y_test = mod.predict(df_raw_test)
est = DataFrame()
test['PassengerId'] = df_raw_test['PassengerId']
test['Survived'] = y_test
# 予測値をintに変換
test['Survived'] = test['Survived'].astype('int')
# 保存
test.to_csv('out/benchmark_catboost.csv', index = False)

この結果をKaggleにsubmitすると....

f:id:tekenuko:20171013230117p:plain

ほぼほぼ何も頑張ってないのにaccuracyが0.8を超えました。ただ、過学習しているモデルなので、より安定して精度を出したかったらパラメータ調整などをおこなって汎化性能を上げる必要があるかと思います。そういった努力は今後の課題にします。

変数重要度

Catboostはfeature_importances_で変数の重要度も出力することができます(ソートはされていない)。ざっくり可視化しておきます。

FI = DataFrame()
FI = FI.append(mod.feature_importances_)
FI.index = X.columns
FI.columns = ['Feature_Importance']

# 可視化
import matplotlib.pyplot as plt
%matplotlib inline
FI.plot(kind='bar')

f:id:tekenuko:20171013230525p:plain

Pclassは乗客の社会的地位、Sexは性別、Ageは年代で、地位の高い女性や子供が優先的に救出されたという歴史的経緯が概ね反映されています。Ticletが効いているのはちょっと難しいですが。

Next Step

大雑把には使い方が分かったので、今後はGrid Searchなどを詰めていって、より使いこなせるようにしていこうと思います。