SIGNATE Quest② ターゲティングモデルの作成~提出データ作成まで


前書き

前回の続きですが、SIGNATE Questがあまりにもわかりやすかったので、有料版を契約しました。
UdemyでSIGNATEが作成した動画もわかりやすかったですが、SIGNATE Questは体系化されていて、さらにわかりやすかったです。

学習用データと評価用データの分割

# pandasのインポート
import pandas as pd

# データの読み込み
df = pd.read_csv('data.csv', index_col='id')

# データのダミー変数化
df = pd.get_dummies(df)

# 説明変数をdata_Xに、目的変数をdata_yに代入
data_X = df.drop('y', axis=1)
data_y = df['y']

# train_test_splitのインポート
from sklearn.model_selection import train_test_split

# 学習データと評価データにデータを分割
train_X, test_X, train_y, test_y = train_test_split(data_X, data_y, test_size=0.25, random_state=0)

# 学習用データの説明変数の行数の表示
print( train_X.shape[0])

# 評価用データの説明変数の行数の表示
print( test_X.shape[0])

評価関数

AUCの使用方法

# roc_auc_scoreのインポート
from sklearn.metrics import roc_auc_score

# AUCの計算結果の表示
# 評価関数を使うには、引数に実測値が代入されている変数と予測値が代入されている変数を与えることで計算ができます。
roc_auc_score(実測値, 予測値)
print( roc_auc_score([0,0,1], [0,1,1]) )

モデル

# pandasのインポート
import pandas as pd

# データの読み込み
df = pd.read_csv('data.csv', index_col='id')

# データのダミー変数化
df = pd.get_dummies(df)

# 説明変数をdata_Xに、目的変数をdata_yに代入
data_X, data_y = df.drop('y', axis=1), df['y']

# train_test_splitのインポート
from sklearn.model_selection import train_test_split

# 学習データと評価データにデータを分割
train_X, test_X, train_y, test_y = train_test_split(data_X, data_y, test_size=0.25, random_state=0)

# 決定木モデルのインポート
from sklearn.tree import DecisionTreeClassifier as DT

# 決定木モデルの準備
tree = DT(max_depth = 2, random_state = 0)

# 決定木モデルの学習
tree.fit(train_X, train_y)

# 重要度の表示
print( tree.feature_importances_ )

# 重要度に名前を付けて表示
print( pd.Series(tree.feature_importances_, index=train_X.columns) )

# 評価用データの予測
pred_y1 = tree.predict_proba(test_X)[:,1]

# 実測値test_y,予測値pred_y1を使ってAUCを計算
auc1 = roc_auc_score(test_y,pred_y1)

# 評価結果の表示
print( auc1 )

ROC曲線の描画

# AUCの計算
from sklearn.metrics import roc_auc_score
auc1 = roc_auc_score(test_y, pred_y1)

# roc_curveのインポート
from sklearn.metrics import roc_curve

# 実測値test_yと予測値pred_y1を使って偽陽性率、真陽性率、閾値の計算
fpr, tpr, thresholds = roc_curve(test_y, pred_y1)

# ラベル名の作成
roc_label = 'ROC(AUC={:.2}, max_depth=2)'.format(auc1)

# ROC曲線の作成
plt.plot(fpr, tpr, label=roc_label)

# 対角線の作成
plt.plot([0, 1], [0, 1], color='black', linestyle='dashed')

# グラフにタイトルを追加
plt.title("ROC")

# グラフのx軸に名前を追加
plt.xlabel('FPR')

# グラフのy軸に名前を追加
plt.ylabel('TPR')

# x軸の表示範囲の指定
plt.xlim(0, 1)

# y軸の表示範囲の指定
plt.ylim(0, 1)

# 凡例の表示
plt.legend()

# グラフを表示
plt.show()

決定木の描画

# 決定木のモデル(tree)構築
from sklearn.tree import DecisionTreeClassifier as DT
tree = DT(max_depth = 2, random_state = 0)
tree.fit(train_X, train_y)

# 決定木描画ライブラリのインポート
from sklearn.tree import export_graphviz

# 決定木グラフの出力
export_graphviz(tree, out_file="tree.dot", feature_names=train_X.columns, class_names=["0","1"], filled=True, rounded=True)

# 決定木グラフの表示
from matplotlib import pyplot as plt
from PIL import Image
import pydotplus
import io

g = pydotplus.graph_from_dot_file(path="tree.dot")
gg = g.create_png()
img = io.BytesIO(gg)
img2 = Image.open(img)
plt.figure(figsize=(img2.width/100, img2.height/100), dpi=100)
plt.imshow(img2)
plt.axis("off")
plt.show()

予測精度改善の為の施策例

パラメータチューニング

グリッドサーチ

# 決定木モデルのインポート
from sklearn.tree import DecisionTreeClassifier as DT

# グリッドサーチのインポート
from sklearn.model_selection import GridSearchCV 

# 決定木モデルの準備
tree = DT(random_state=0)

# パラメータの準備
parameters  = {'max_depth':[2,3,4,5,6,7,8,9,10]}

# グリッドサーチの設定
gcv = GridSearchCV(tree, parameters , cv=5, scoring='roc_auc', return_train_score=True)

# グリッドサーチの実行
gcv.fit(train_X, train_y)

# 評価スコアの取り出し
train_score = gcv.cv_results_ ['mean_train_score']
test_score = gcv.cv_results_ ['mean_test_score']

# matplotlib.pyplotを省略名pltとしてインポート 
import matplotlib.pyplot as plt

# 学習に用いたデータを使って評価したスコアの描画
plt.plot([2,3,4,5,6,7,8,9,10], train_score, label="train_score")

# 学習には用いなかったデータを使って評価したスコアの描画
plt.plot([2,3,4,5,6,7,8,9,10], test_score, label="test_score")

# グラフにタイトルを追加
plt.title('train_score vs test_score')

# グラフのx軸に名前を追加
plt.xlabel('max_depth')

# グラフのy軸に名前を追加
plt.ylabel('AUC')

# 凡例の表示
plt.legend()

# グラフの表示
plt.show()

学習に使用したデータでAUCが上がり続け、学習に使用しなかったデータでAUCが下がるのは、典型的な過学習の現象

最適パラメータモデルによる予測・評価

# 最適なパラメータの表示
print( gcv.best_params_ )

# 最適なパラメータで学習したモデルの取得
best_model = gcv.best_estimator_ 

# 評価用データの予測
pred_y3 = best_model.predict_proba(test_X)[:,1]

# AUCの計算
auc3 = roc_auc_score(test_y, pred_y3)

# AUCの表示
print ( auc3 )
結果

{'max_depth': 6}
0.8631100075115532

ちなみに

print(best_model) とすると下記の結果が返ってくる
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=6,
max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, presort=False, random_state=0,
splitter='best')

ROC曲線の描画 3本のROC曲線を描き、比較

# matplotlib.pyplotのインポート
from matplotlib import pyplot as plt

# roc_curveのインポート
from sklearn.metrics import roc_curve

# 偽陽性率、真陽性率、閾値の計算
# なお、予測結果は以下の変数に代入されているものとします。
# pred_y1:max_depth=2の場合の予測結果
# pred_y2:max_depth=10の場合の予測結果
# pred_y3:max_depth=6の場合の予測結果
# また、それぞれの戻り値を代入する変数は以下とします。
# fpr1,tpr1,thresholds1:max_depth=2の場合の偽陽性率、真陽性率、閾値
# fpr2,tpr2,thresholds2:max_depth=10の場合の偽陽性率、真陽性率、閾値
# fpr3,tpr3,thresholds3:max_depth=6の場合の偽陽性率、真陽性率、閾値
fpr1, tpr1, thresholds1 = roc_curve(test_y, pred_y1)
fpr2, tpr2, thresholds2 = roc_curve(test_y, pred_y2)
fpr3, tpr3, thresholds3 = roc_curve(test_y, pred_y3)

# ラベル名の作成
# なお、それぞれの戻り値を代入する変数は以下とします。
# roc_label1:max_depth=2の場合のラベル名
# roc_label2:max_depth=10の場合のラベル名
# roc_label3:max_depth=6の場合のラベル名
roc_label1 = 'ROC(AUC={:.2}, max_depth=2)'.format(auc1)
roc_label2 = 'ROC(AUC={:.2}, max_depth=10)'.format(auc2)
roc_label3 = 'ROC(AUC={:.2}, max_depth=6)'.format(auc3)

# ROC曲線の作成
plt.plot(fpr1, tpr1, label=roc_label1.format(auc1))
plt.plot(fpr2, tpr2, label=roc_label2.format(auc2))
plt.plot(fpr3, tpr3, label=roc_label3.format(auc3))

# 対角線の作成
plt.plot([0, 1], [0, 1], color='black', linestyle='dashed')

# グラフにタイトルを追加
plt.title("ROC")

# グラフのx軸に名前を追加
plt.xlabel('FPR')

# グラフのy軸に名前を追加
plt.ylabel('TPR')

# x軸の表示範囲の指定
plt.xlim(0, 1)

# y軸の表示範囲の指定
plt.ylim(0, 1)

# 凡例の表示
plt.legend()

# グラフを表示
plt.show()

再確認 AUCとはROC曲線の面積を表す

max_depth=6のROC曲線が最も面積が大きいことを表している。

アタックリストの作成

最適なパラメータで学習した決定木モデルを使い、評価用データに割り当てられた顧客に対する定期預金キャンペーンの申込率の予測結果を算出し、それを元にアタックリストを作成してみましょう。
```

申込率を含む顧客リストの作成

customer_list = pd.DataFrame(index=test_X.index, data={"cvr":pred_y3})

期待できる収益の計算

customer_list["return"] = 2000 * customer_list["cvr"]

期待できるROIの計算

customer_list["ROI"] = customer_list["return"] / 300 * 100

ROIで降順に並べ替え

sorted_customer_list = customer_list.sort_values("ROI", ascending=False)

ROIが100%以上の顧客idを切り出したアタックリストの作成

attack_list = sorted_customer_list[sorted_customer_list["ROI"] >= 100]

アタックリストの行数・列数の表示

print( attack_list.shape )

アタックリストの先頭5行の表示

print( attack_list.head() )
```

今回のクエストでは分類問題をテーマに、データの準備から決定木モデルの作成、そしてグリッドサーチによるパラメータチューニングまでを学びました。この分析の流れは基本的には全てのデータ分析において共通する非常に重要なプロセスとなります