TensorFlow 2.0 Alpha : Beginner Tutorials : Estimator :- TensorFlow 2.0 でブースティング木モデルをどのように訓練するか (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/03/2019
* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Beginner Tutorials – Estimator の以下のページを翻訳した上で
適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
Estimator :- TensorFlow 2.0 でブースティング木モデルをどのように訓練するか
このチュートリアルは tf.estimator API で決定木を使用する勾配ブースティング木を訓練する end-to-end なウォークスルーです。ブースティング木モデルは回帰と分類の両者のための最も一般的で効果的な機械学習アプローチの一つです。それは幾つか (10s, 100s あるいは 1000s さえも考えます) の木モデルからの予測を連結するアンサンブル・テクニックです。
ブースト木モデルは多くの機械学習実践者により人気があります、何故ならばそれらは最小限のハイパーパラメータ調整で素晴らしいパフォーマンスを獲得できるからです。
タイタニック・データセットをロードする
タイタニック・データセットを使用します、そこでは (寧ろ憂鬱な) 目標は性別、年齢、クラス, etc. のような特質が与えられたときに乗客の生存を予測することです。
from __future__ import absolute_import, division, print_function
import numpy as np
import pandas as pd
from IPython.display import clear_output
# Load dataset.
dftrain = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/train.csv')
dfeval = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/eval.csv')
y_train = dftrain.pop('survived')
y_eval = dfeval.pop('survived')
!pip install -q tensorflow==2.0.0-alpha0 import tensorflow as tf tf.random.set_seed(123)
データセットは訓練セットと評価セットから成ります :
- dftrain と y_train は訓練セットです — モデルが学習するために使用するデータです。
 - モデルは評価セット, dfeval と y_eval に対してテストされます。
 
訓練のために次の特徴を使用します :
| Feature Name | Description | 
| sex | 乗客の性別 | 
| age | 乗客の年齢 | 
| n_siblings_spouses | # 乗船した兄弟とパートナー | 
| parch | # of 乗船した両親と子供 | 
| fare | 乗客が支払った運賃 | 
| class | 船の乗客のクラス | 
| deck | 乗客がどのデッキ上にいたか | 
| embark_town | 乗客がどの町から乗船したか | 
| alone | 乗客が一人であるか否か | 
データを調査する
最初にデータの幾つかをプレビューして訓練セット上の簡易統計 (= summary statistics) を作成します。
dftrain.head()
| sex | age | n_siblings_spouses | parch | fare | class | deck | embark_town | alone | |
| 0 | male | 22.0 | 1 | 0 | 7.2500 | Third | unknown | Southampton | n | 
| 1 | female | 38.0 | 1 | 0 | 71.2833 | First | C | Cherbourg | n | 
| 2 | female | 26.0 | 0 | 0 | 7.9250 | Third | unknown | Southampton | y | 
| 3 | female | 35.0 | 1 | 0 | 53.1000 | First | C | Southampton | n | 
| 4 | male | 28.0 | 0 | 0 | 8.4583 | Third | unknown | Queenstown | y | 
dftrain.describe()
| age | n_siblings_spouses | parch | fare | |
| count | 627.000000 | 627.000000 | 627.000000 | 627.000000 | 
| mean | 29.631308 | 0.545455 | 0.379585 | 34.385399 | 
| std | 12.511818 | 1.151090 | 0.792999 | 54.597730 | 
| min | 0.750000 | 0.000000 | 0.000000 | 0.000000 | 
| 25% | 23.000000 | 0.000000 | 0.000000 | 7.895800 | 
| 50% | 28.000000 | 0.000000 | 0.000000 | 15.045800 | 
| 75% | 35.000000 | 1.000000 | 0.000000 | 31.387500 | 
| max | 80.000000 | 8.000000 | 5.000000 | 512.329200 | 
訓練と評価セットにはそれぞれ 627 と 264 サンプルがあります。
dftrain.shape[0], dfeval.shape[0]
(627, 264)
乗客の大多数は 20 代と 30 代です。
dftrain.age.hist(bins=20);

乗船した男性の乗客は女性の乗客のおよそ 2 倍いました。
dftrain.sex.value_counts().plot(kind='barh');

乗客の多数は “third” クラスでした。
dftrain['class'].value_counts().plot(kind='barh');

殆どの乗客は Southampton から乗船しました。
dftrain['embark_town'].value_counts().plot(kind='barh');

女性は男性に比較して遥かに高い生存のチャンスを持ちます。これは明らかにモデルのための予測的な特徴となるでしょう。
pd.concat([dftrain, y_train], axis=1).groupby('sex').survived.mean().plot(kind='barh').set_xlabel('% survive');

feature column と入力関数を作成する
勾配ブースティング estimator は numeric と categorical features の両者を利用できます。feature columns は総ての TensorFlow estimator とともに動作してそれらの目的はモデリングのために使用される特徴を定義することです。更にそれらは one-hot-エンコーディング、正規化そして bucketization のようなある特徴エンジニアリング機能を提供します。このチュートリアルでは、CATEGORICAL_COLUMNS のフィールドは categorical columns から one-hot-エンコードされた columns (indicator column) へ変形されます :
fc = tf.feature_column
CATEGORICAL_COLUMNS = ['sex', 'n_siblings_spouses', 'parch', 'class', 'deck', 
                       'embark_town', 'alone']
NUMERIC_COLUMNS = ['age', 'fare']
  
def one_hot_cat_column(feature_name, vocab):
  return tf.feature_column.indicator_column(
      tf.feature_column.categorical_column_with_vocabulary_list(feature_name,
                                                 vocab))
feature_columns = []
for feature_name in CATEGORICAL_COLUMNS:
  # Need to one-hot encode categorical features.
  vocabulary = dftrain[feature_name].unique()
  feature_columns.append(one_hot_cat_column(feature_name, vocabulary))
  
for feature_name in NUMERIC_COLUMNS:
  feature_columns.append(tf.feature_column.numeric_column(feature_name,
                                           dtype=tf.float32))
feature column が生成する変形を見ることができます。例えば、ここに単一のサンプル上で indicator_column を使用するときの出力があります :
example = dict(dftrain.head(1))
class_fc = tf.feature_column.indicator_column(tf.feature_column.categorical_column_with_vocabulary_list('class', ('First', 'Second', 'Third')))
print('Feature value: "{}"'.format(example['class'].iloc[0]))
print('One-hot encoded: ', tf.keras.layers.DenseFeatures([class_fc])(example).numpy())
Feature value: "Third" One-hot encoded: [[ 0. 0. 1.]]
更に、総ての feature column 変形も一緒に見ることができます :
tf.keras.layers.DenseFeatures(feature_columns)(example).numpy()
array([[ 22.  ,   1.  ,   0.  ,   1.  ,   0.  ,   0.  ,   1.  ,   0.  ,
          0.  ,   0.  ,   0.  ,   0.  ,   0.  ,   0.  ,   1.  ,   0.  ,
          0.  ,   0.  ,   7.25,   1.  ,   0.  ,   0.  ,   0.  ,   0.  ,
          0.  ,   0.  ,   1.  ,   0.  ,   0.  ,   0.  ,   0.  ,   0.  ,
          1.  ,   0.  ]], dtype=float32)
次に入力関数を作成する必要があります。これらは訓練と推論の両者に対してデータがどのようにモデルに読まれるかを指定します。Pandas から直接データを読み込むために tf.data API の from_tensor_slices メソッドを使用します。これはより小さい、in-メモリなデータセットに対して適しています。より巨大なデータセットについては、メモリに収まらないデータセットを処理できるように tf.data API は (csv を含む) 様々な種類のファイルフォーマットをサポートします。
# Use entire batch since this is such a small dataset.
NUM_EXAMPLES = len(y_train)
def make_input_fn(X, y, n_epochs=None, shuffle=True):
  def input_fn():
    dataset = tf.data.Dataset.from_tensor_slices((dict(X), y))
    if shuffle:
      dataset = dataset.shuffle(NUM_EXAMPLES)
    # For training, cycle thru dataset as many times as need (n_epochs=None).    
    dataset = dataset.repeat(n_epochs)
    # In memory training doesn't use batching.
    dataset = dataset.batch(NUM_EXAMPLES)
    return dataset
  return input_fn
# Training and evaluation input functions.
train_input_fn = make_input_fn(dftrain, y_train)
eval_input_fn = make_input_fn(dfeval, y_eval, shuffle=False, n_epochs=1)
モデルを訓練して評価する
下では次のステップを行ないます :
- features とハイパーパラメータを指定して、モデルを初期化します。
 - train_input_fn を使用してモデルに訓練データを供給して train 関数を使用してモデルを訓練します。
 - 評価セット — この例では dfeval DataFrame を使用してモデル・パフォーマンスを評価します。予測が y_eval 配列からのラベルにマッチするか検証します。
 
ブースティング木モデルを訓練する前に、最初に線形分類器 (ロジスティック回帰モデル) を訓練しましょう。ベンチマークを確証するためにより単純なモデルで始めるのがベストプラクティスです。
linear_est = tf.estimator.LinearClassifier(feature_columns) # Train model. linear_est.train(train_input_fn, max_steps=100) # Evaluation. result = linear_est.evaluate(eval_input_fn) clear_output() print(pd.Series(result))
accuracy 0.765152 accuracy_baseline 0.625000 auc 0.832844 auc_precision_recall 0.789631 average_loss 0.478908 global_step 100.000000 label/mean 0.375000 loss 0.478908 precision 0.703297 prediction/mean 0.350790 recall 0.646465 dtype: float64
次に、ブーティング木モデルを訓練しましょう。ブースティング木については、回帰 (BoostedTreesRegressor) と分類 (BoostedTreesClassifier) がサポートされます。目標はクラス – 生存か非生存かを推測することですから、BoostedTreesClassifier を使用します。
# Since data fits into memory, use entire dataset per layer. It will be faster.
# Above one batch is defined as the entire dataset. 
n_batches = 1
est = tf.estimator.BoostedTreesClassifier(feature_columns,
                                          n_batches_per_layer=n_batches)
# The model will stop training once the specified number of trees is built, not 
# based on the number of steps.
est.train(train_input_fn, max_steps=100)
# Eval.
result = est.evaluate(eval_input_fn)
clear_output()
print(pd.Series(result))
accuracy 0.829545 accuracy_baseline 0.625000 auc 0.872788 auc_precision_recall 0.857807 average_loss 0.411839 global_step 100.000000 label/mean 0.375000 loss 0.411839 precision 0.793478 prediction/mean 0.381942 recall 0.737374 dtype: float64
今では評価セットからの乗客について予測を行なうために訓練モデルを使用できます。TensorFlow モデルはサンプルのバッチやコレクションの上で同時に予測を行なうために最適化されています。前に、eval_input_fn は評価セット全体を使用して定義されています。
pred_dicts = list(est.predict(eval_input_fn)) probs = pd.Series([pred['probabilities'][1] for pred in pred_dicts]) probs.plot(kind='hist', bins=20, title='predicted probabilities');

最後に結果の ROC (receiver operating characteristic) もまた見ることができます、これは真陽性率 (= true positive rate) と偽陽性率 (= false positive rate) 間のトレードオフのより良い考えを与えます。
from sklearn.metrics import roc_curve
from matplotlib import pyplot as plt
fpr, tpr, _ = roc_curve(y_eval, probs)
plt.plot(fpr, tpr)
plt.title('ROC curve')
plt.xlabel('false positive rate')
plt.ylabel('true positive rate')
plt.xlim(0,)
plt.ylim(0,);

以上