PyInv

プログラミングのメモ、海外投資のメモ

第二回:Ensemble Model - Gradient Boosted Decistion Trees(GBDT)

概要

引き続いてMachine Learning のEnsemble Model。Gradient Boosted Decision Trees

Gradient-Boosted Decision Trees(GBDT)

連続したいくつもの小さいDecision Tree。Decision Treeごとに前回のDecision Treeのエラーを修正していく。一方、Random Forestsは並行したいくつものDecision Treeでそれぞれ独立している。
最も多く使われるMachine Learning Model。
後述のようにLearning Rateが最も大事なパラメータ。

Pros and Cons

Pros
  • 既存のパッケージの中で多くの対象に汎用的に最も高いAccuracyをたたき出す
  • メモリの使用量がそれほど高くなく、計算が速い
  • 特徴量に対して正規化が必要でない
  • Decision Treeと同様に、多種多様な特徴量を加工なく使える

Cons
  • Random Forestsと同様に、モデルの解釈が難しい
  • Learning Rateやその他のパラメータのチューニングが必要
  • 学習のための計算量が大きい
  • Decision Treeと同様にText Classificationのような多次元の特徴量の取り扱いがうまくない


Structure

sklearn.ensemble.GradientBoostingClassifier(loss='deviance', learning_rate=0.1, n_estimators=100, subsample=1.0, criterion='friedman_mse', min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_depth=3, min_impurity_decrease=0.0, min_impurity_split=None, init=None, random_state=None, max_features=None, verbose=0, max_leaf_nodes=None, warm_start=False, presort='deprecated', validation_fraction=0.1, n_iter_no_change=None, tol=0.0001, ccp_alpha=0.0)

from sklearn.ensemble import GradientBoostingClassifier


主なParameters

n_estimatorsとlearning_rateはセットで調整する

Parameters Description
learning_rate 前回のTreeからエラーを修正する
High: More complex model, overfitting
Low: simpler model
(Default = 0.1)
n_estimators 連続させるDecision Treeの個数。
(Default = 100)
max_depth 各Treeの最大深度。通常は小さな値(3-5)
(Default = 3)


Usage

%matplotlib notebook
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_blobs
from adspy_shared_utilities import plot_class_regions_for_classifier_subplot #パッケージ
import matplotlib.pyplot as plt

X_D2, y_D2 = make_blobs(n_samples = 100, n_features = 2,
                       centers = 8, cluster_std = 1.3,
                       random_state = 4)
y_D2 = y_D2 % 2

X_train, X_test, y_train, y_test  = train_test_split(X_D2, y_D2, random_state=0)
fig, subaxes = plt.subplots(1,1,figsize = (6,6))

clf = GradientBoostingClassifier(learning_rate=0.01, max_depth=2).fit(X_train, y_train)
title = 'GBDT, complex binary dataset, default settings'
plot_class_regions_for_classifier_subplot(clf, X_train, y_train, X_test, y_test, title, subaxes)

plt.show()



f:id:singapp:20200221021527p:plain

第一回:Ensemble Model - Random Forestsとはなんぞや

Ensemble Model

たくさんのMahcine Learning Model をたばねたもの。ひとつひとつにのOverfittingのような問題があっても、全体としては安定。
サンプルはBootstrapによって集められる。

Random Forests

Decision Tree をいくつも束ねたもの。それぞれのTreeではRandom_stateによって異なるモデルが生成される。
singapp.hatenablog.com


Regression: それぞれのtreeのaverage
Classification: それぞれのtreeが返すprobabilityのaverage, 最後に最も高いprobabilityで予想する

Pros and Cons

Pros

  • Normalization やParameterのチューニングが必要ない
  • 簡単で幅広い対象に対してすごくよいパフォーマンス
  • 並列処理化が簡単

Cons

  • できたモデルの解釈が困難
  • text classififationのような高次の処理が得意でない


Structure

  • Classification: RandomForestClassifier
sklearn.ensemble.RandomForestClassifier(n_estimators=100, criterion='gini', max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features='auto', max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, bootstrap=True, oob_score=False, n_jobs=None, random_state=None, verbose=0, warm_start=False, class_weight=None, ccp_alpha=0.0, max_samples=None)

  • Regression: RansomForestRegressor
sklearn.ensemble.RandomForestRegressor(n_estimators=100, criterion='mse', max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features='auto', max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, bootstrap=True, oob_score=False, n_jobs=None, random_state=None, verbose=0, warm_start=False, ccp_alpha=0.0, max_samples=None)

Parmeters

decistion treeとだいたい同じだがmax_featuresが一番大事

Parameter Description
max_features i.e.
set as 1: ランダムに選ばれた特徴量が一つだけ使われる→more complex
set as close to the number of features: 通常のDecision Treeと同じようになる
(Default = 'auto')
n_estimators ensembleで何個のTreeを使うか
(Default = 10)
max_depth 各Treeの深さ
(Default = 'None')
n_jobs 何個のCPU coreを学習中に使うか
random_state 再現性のために毎回同じ特定の値を使うこと

Usage

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_classification, make_blobs
import matplotlib.pyplot as plt

X_D2, y_D2 = make_blobs(n_samples = 100, n_features = 2,
                       centers = 8, cluster_std = 1.3,
                       random_state = 4)

X_train, X_test, y_train, y_test =train_test_split(X_D2, y_D2, random_state=0)
fig , subaxes=plt.subplots(1,1, figsize=(6,6))

clf = RandomForestClassifier().fit(X_train, y_train)
title = 'Random Forest Classifier, complex binary dataset, default settings'

plot_class_regions_for_classifier_subplot(clf, X_train, y_train, X_test,
                                         y_test, title, subaxes)

plt.show()


f:id:singapp:20200219172936p:plain

続Python Machine Laerning Models

モデルの書き忘れ.

Logistic Regression, Decision Tree

singapp.hatenablog.com

Logistic Regression

非線形回帰の一種。 各説明変数を入れると0から1間の値、つまり確率分布を返す。
これを使ってBinary Classificationを行う。(ある確率以上は1という判断)

Structure

sklearn.linear_model.LogisticRegression(penalty='l2', dual=False, tol=0.0001, C=1.0, fit_intercept=True, intercept_scaling=1, class_weight=None, random_state=None, solver='lbfgs', max_iter=100, multi_class='auto', verbose=0, warm_start=False, n_jobs=None, l1_ratio=None)

from sklearn.linear_model import LogisticRegression


Parameters

Parameter Discription
Penalty 説明変数自体の大きさによりモデル内での重みが変わってしまうので、正規化する必要がある。ここで説明変数の標準化の手法を選択 ['l1' or 'l2']
L1は絶対値をとり、小さい説明変数への重みがゼロになる。
(Default as l2)
C 正則化の速さ(強さ)を指定する。RidgeやLassoのαと逆向き。
Lower: more regularization, simpler model
Larger: less regulalization, more complex model -> overfitting
(Default as 1.0)
random_state random generatorのseed
毎回同じのを使うとよい

Sample

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X_fruits, y_fruits, random_state=0)
clf = LogisticRegression(C=this_C).fit(X_train, y_train)


f:id:singapp:20200219134353p:plain


Decision Tree

条件分岐によってグループを分割していく。一回の分岐で一つのグループをきれいに抽出できるとスコアが高い(不純度)。

Structure

sklearn.tree.DecisionTreeClassifier(criterion='gini', splitter='best', max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=None, random_state=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, class_weight=None, presort='deprecated', ccp_alpha=0.0)

from sklearn.tree import DecisionTreeClassifier


主なParameters

Parameter Discription
Criterion 分割基準、'gini' or 'entropy'
(Default = 'gini')
max_depth Treeの最大深度
多すぎるとoverfitting
(Default = None)
min_samples_split 分岐する際の最低サンプル量
(Default = 2)
min_samples_leaf Leaf nodeでの最低サンプル量
(Default = 1)

ほかのは
scikit-learn.org


Sample

from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split


iris = load_iris()

X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state = 3)
clf = DecisionTreeClassifier(max_depth = 3,min_samples_leaf = 8,
                            random_state = 0).fit(X_train, y_train)

Visualization

sklearn.tree.export_graphviz(decision_tree, out_file=None, max_depth=None, feature_names=None, class_names=None, label='all', filled=False, leaves_parallel=False, impurity=True, node_ids=False, proportion=False, rotate=False, rounded=False, special_characters=False, precision=3)

import pydotplus
from IPython.display import Image
from graphviz import Digraph
from sklearn.externals.six import StringIO

dot_data = StringIO()
tree.export_graphviz(clf, out_file=dot_data,feature_names=train_X.columns, max_depth=3)
graph = pydotplus.graph_from_dot_data(dot_data.getvalue())
graph.write_pdf("graph.pdf")
Image(graph.create_png())


f:id:singapp:20200219143745p:plain

Feature importance

どの変数が重要だったかを返すメソッド

clf.feature_importances_)

その他のLinear Classifier - Naive Bayes Classifiers

その他のLinear ClassifierとしてNaive Bayes Classifiersがある

Naive Bayes Classifiers

特徴量間で独立となる確率分布を用いて事後確率が最大となるクラスに分類する。単純で高速なため、他のモデルを評価するbaseとして使われる。
一般的な分析対象の特徴量間では通常はなんらかの関係性が見いだされるため、モデルのあてはまりはよくない。Text Classificationに使われる。

確率分布によって3種類用意されている

  1. Bernoulli Naive Bayes
  2. Multinomial Naive Bayes
  3. Gaussian Naive Bayes


Bernoulli Naive Bayes

ベルヌーイ分布を仮定する。つまり2項分布。

Multinomial Naive Bayes

多項分布

Gaussian Naive Bayes

正規分布

Strucuture

sklearn.naive_bayes.GaussianNB(priors=None, var_smoothing=1e-09

from sklearn.naive_bayes import GaussianNB

Modelの選び方 - k分割交差検証(k-fold Cross Validation by Validation Curve)とグリッドサーチ(Grid Search)

どのようにModelを選ぶか

  • Validation Curveを使って仮定した選んだModelに対して、サンプルのとり方の影響を排除した評価値をだす
  • よさげなModelについてGrid Searchにより最適なparemeterを導出
  • 最適なparameterでもう一回Validation Curveを使ってModel間の評価をする


Validation Curveを使う

下記で取り上げたようにValidation Curveを使うことで、サンプルのとり方による影響を排除したModelの評価値(Accuracy, Precision等)を導出できる。ホールドアウト法(train_test_split)と比べて小さいデータの時に有効
singapp.hatenablog.com


Structure
sklearn.model_selection.cross_val_score(estimator,
                                        X,
                                        y=None,
                                        groups=None,
                                        scoring=None,
                                        cv=None,
                                        n_jobs=None,
                                        verbose=0, 
                                        fit_params=None,
                                        pre_dispatch='2*n_jobs',
                                        error_score=nan)

from sklearn.model_selection import validation_curve



主なParameters

Parameters Description
scoring 評価値を選択
accuracy, roc_aucとか
sklearn.metricsの関数を選択できる
cv データセットの分割個数

Usage
  1. Model のオブジェクトを作成(SampleではDecisionTree)
  2. Grcross_val_scoreオブジェクトを作成し、Modelを学習データとともに渡す
  3. 各思考のscoreを取得
  4. 「平均+/-標準偏差」をこのModelの評価値として用いる


Sample
from sklearn.datasets import load_breast_cancer
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score

cancer = load_breast_cancer()
cn_data = pd.DataFrame(cancer.data, columns=cancer.feature_names)
cn_target= pd.Series(cancer.target)

tree = DecisionTreeClassifier(criterion='gini', max_depth=5, random_state=0)
scores = cross_val_score(tree, cn_data, cn_target, cv=5)

print('Cross Validation Scores: {}'.format(scores))
print('Cross Validation Mean: {:.3f}'.format(scores.mean()))
print('Cross Validation Standard Deviation: {:.3f}'.format(scores.std()))

print('Cross Validation Score Range: {:.3f}+/-{:.3f}'.format(scores.mean(), scores.std()))


f:id:singapp:20200918131427p:plain

Furthermore

scoringに複数の指標を同時に使いたいときはcross_val_scoreのかわりにcross_validateを使う

from sklearn.model_selection import cross_validate


選択可能なscoring parameter一覧はこちら


指定したモデルの最適パラメータをいろんな評価値で計算する。

Structure
sklearn.model_selection.GridSearchCV(estimator,
                                     param_grid,
                                     scoring=None,
                                     n_jobs=None,
                                     iid='deprecated',
                                     refit=True,
                                     cv=None,
                                     verbose=0,
                                     pre_dispatch='2*n_jobs',
                                     error_score=nan,
                                     return_train_score=False)

from sklearn.model_selection import GridSearchCV


主なParameters

Parameters Description
estimator 評価するモデル
SVCとか
param_grid 探索するパラメータ
scoring 評価値を選択
accuracy, roc_aucとか
cv 試行回数




Usage
  1. GridSearchCVオブジェクトを作成
  2. GridSearchCV.fit()に学習データを渡す
  3. 最もよいパラメータを取得 best_estimator_

Sample
from sklearn.svm import SVC
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import roc_auc_score
from sklearn.datasets import load_digits

dataset = load_digits()
X, y = dataset.data, dataset.target == 1
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

clf = SVC(kernel='rbf')
grid_values = {'gamma': [0.001, 0.01, 0.05, 0.1, 1, 10, 100]}

# default metric to optimize over grid parameters: accuracy
grid_clf_acc = GridSearchCV(clf, param_grid = grid_values)
grid_clf_acc.fit(X_train, y_train)
y_decision_fn_scores_acc = grid_clf_acc.decision_function(X_test) 

print('Grid best parameter (max. accuracy): ', grid_clf_acc.best_params_)
print('Grid best score (accuracy): ', grid_clf_acc.best_score_)

# alternative metric to optimize over grid parameters: AUC
grid_clf_auc = GridSearchCV(clf, param_grid = grid_values, scoring = 'roc_auc')
grid_clf_auc.fit(X_train, y_train)
y_decision_fn_scores_auc = grid_clf_auc.decision_function(X_test) 

print('Test set AUC: ', roc_auc_score(y_test, y_decision_fn_scores_auc))
print('Grid best parameter (max. AUC): ', grid_clf_auc.best_params_)
print('Grid best score (AUC): ', grid_clf_auc.best_score_)



f:id:singapp:20200918131606p:plain

続続続Modelの評価 - ROC曲線とAUC

予測確率を用いたClassification Modelの評価

Confusion MatrixではPredicted Label (予測ラベル as 0 or 1)を用いてモデルを評価した。しかし、Predict Probability (予測確率)→Predicted Labelへと写すthreshold(閾値)が事前には決められるとは限らない。そこで、Predict Probabilityそのものからモデルを評価する場合にROC曲線とAUCが役に立つ

Confusion matrixはこちら
singapp.hatenablog.com


Receiver Operating Characteristic (ROC)

False Positive RateとTrue Positive Rateのthreshold =閾値(0-1)ごとの組み合わせ。

  • False Positive Rate: Precisionの余事象→ 横軸

      \displaystyle{FPR=\frac{FP}{(FP+TN)}}

  • True Positive Rate: Recall→ 縦軸

      \displaystyle{TPR=\frac{TP}{(FN+TP)}}

Structure
from sklearn.metrics import roc_curve
fpr_lr, tpr_lr, _ = roc_curve(y_test, y_score_lr)


Output
  • fpr_lr: False Positive Rate
  • tpr_lr: True Positive Rate


グラフが左上に近いほうが良い結果(つまり右下が広い=AUCが大きい)
f:id:singapp:20200218155401p:plain

Sample
from sklearn.svm import SVC
from sklearn.datasets import load_breast_cancer
from sklearn.metrics import roc_curve

cancer =  load_breast_cancer()
X = pd.DataFrame(cancer.data, columns=cancer.feature_names)
y = pd.Series(cancer.target)


X_train, X_test, y_train, y_test = train_test_split(X,y, random_state=0)
clf = SVC(kernel='rbf', gamma=0.001, C=1, probability=True) #probability = True としないと学習結果に予測確率が含まれない
clf.fit(X_train,y_train) 

y_score_lr = clf.predict_proba(X_test)[:,1]
fpr_lr, tpr_lr, _ = roc_curve(y_test, y_score_lr) #threshold は使わないので'_'に取得

#graph plot
plt.figure(figsize=(10,6))
plt.plot(fpr_lr, tpr_lr, color ='red', label = 'ROC curve')
plt.plot([0,1], [0,1], color = 'black', linestyle ='--')

plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])

plt.xlabel('False positive rate')
plt.ylabel('True positive rate')
plt.title('Receiver Operating Characteristic')

plt.legend(loc='best')
plt.show()

f:id:singapp:20200918172454p:plain

Area Under Curve (AUC)

ROCの下の面積

Structure
from sklearn.metrics import auc
sklearn.metrics.auc(x, y)
Sample
from sklearn.metrics import auc
auc = auc(fpr_lr, tpr_lr)
print ('AUC:{:.3f}'.format(auc))

f:id:singapp:20200918172916p:plain


またはROCと一緒にグラフへ

#ROCのコードの一部を以下のように変更
plt.plot(fpr_lr, tpr_lr, color ='red', label = 'ROC curve (AUC = %.3f)' % auc)

f:id:singapp:20200918173132p:plain

続続Modelの評価 - Precision-Recall Curve (RecallとPrecisionの可視化)

RecallとPrecisioinの可視化


前準備としてDecision Function

SVC等で推定された境界平面からそのアウトプットへの距離。例えばX_testをインプットした時のアウトプット(y_predicted)がどれだけモデルの境界から遠いかを算定する。似たものとしてpredict_proba(0-1にスケーリングしたもの)がある。
(自分にはもう 限界なのでもっとすごい人の説明を参照求む)

Structure

Sklearnモデルのmethodとして

y_scores_lr = lr.fit(X_train, y_train).decision_function(X_test)


Precision-recall curves

decision functionのであらわされる値を閾値(threshold)としてRecallとPrecisionの組み合わせを返す

Structure
precision, recall, thresholds = precision_recall_curve(y_test, y_scores_lr)
from sklearn.metrics import precision_recall_curve

f:id:singapp:20200218122106p:plain

全体をグラフとして可視化する以外に、precision, recall, thresholdの各値をターゲットに他の2つの値を導出
例えば、thresholdsのうち最小なもののインデックスを取り出して

closest_zero = np.argmin(np.abs(thresholds))

それに対応するprecisionを取り出す

closest_zero_p = precision[closest_zero]