5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

機械学習(分類問題)

Last updated at Posted at 2021-08-29

#データの作成

from sklearn.model_selection import train_test_split

target_col = ''
exclude_cols = ['', '', '', '']
feature_cols = []
for col in dataset.columns:
    if col not in exclude_cols:
        feature_cols.append(col)
        
X = dataset[feature_cols]
y = dataset[target_col]           

X_train, X_test, y_train, y_test = train_test_split(X , y , test_size=0.3, random_state=1234)

print('X_test Features Shape: ', X_test.shape)
print('y_test Target Shape: ', y_test.shape)
print('X_train Features Shape: ', X_train.shape)
print('y_train Target Shape: ', y_train.shape)

分類問題

ロジスティック回帰

線形回帰によって求めた値をシグモイド関数に適応させ、確率を予測する。

P = \frac{1}{1+e^{-(\beta_0 + \beta_1 X_1 + \cdots + \beta_K X_K)}}

        output.png

重要度(オッズ比)の求め方
回帰係数を基にオッズ比で解釈する。
オッズ比が1に近い → 重要度が低い
オッズ比が1より大きい → 説明変数が大きいほど、事象が起こりやすい
オッズ比が1より小さい → 説明変数が小さいほど、事象が起こりやすい

\begin{align}
p &= \frac{1}{1+e^{-(\beta_0+ \beta_1X)}} \cdots 事象が起こる確率\\
\\

オッズ
\cdots
\frac{p}{1-p} &= \frac{\frac{1}{1+e^{-(\beta_0+ \beta_1X)}}}{1-\frac{1}{1+e^{-(\beta_0+ \beta_1X)}}}
= e^{\beta_0+ \beta_1 X}
= e^{\beta_0} e^{\beta_1 X}\\
\\

X_0 \underset{+1}{\longrightarrow} X_1とする\\

オッズ比 \cdots
\frac{\frac{p_0}{1-p_0}}{\frac{p_1}{1-p_1}}
&=\frac{e^{\beta_0} e^{\beta_1 X_0}}{e^{\beta_0} e^{\beta_1 X_1}}
=e^{\beta_1(X_1 - X_0)}
=e^{\beta_1}

\end{align}
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report, auc, roc_curve

logi = LogisticRegression()
logi = LogisticRegression(penalty = "l1",solver = "liblinear", C=1)  #Lasso
logi = LogisticRegression(penalty = "l2",solver = "liblinear", C=1)  #Ridge

logi.fit(X_train, y_train)
y_pred = logi.predict(X_test)
y_proba = logi.predict_proba(X_test)[:,1]

#混同行列
def show_evaluation_metrics(y_true, y_pred):
    print("Accuracy:")
    print(accuracy_score(y_true, y_pred))
    print()
    print("Report:")
    print(classification_report(y_true, y_pred))
    print("Confusion matrix:")
    print(confusion_matrix(y_true, y_pred))
show_evaluation_metrics(y_test, y_pred)

#AUC
def AUC_and_ROC_curve(y_true, y_proba):
    fpr, tpr, thresholds = roc_curve(y_true, y_proba)
    AUC = auc(fpr, tpr)
    plt.plot(fpr, tpr, label='ROC curve (AUC={:.3f})'.format(AUC))
    plt.legend()
    plt.title('ROC curve')
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.grid(True)
    plt.show()
AUC_and_ROC_curve(y_test, y_proba)

#オッズ比
df_odds = pd.DataFrame({'feature':X.columns, 'odds':np.exp(logi.coef_)[0]})
sns.barplot(x='odds', y='feature', 
            data=df_odds.sort_values('odds', ascending=False))
plt.show()

#パラメーターの調整
from sklearn.model_selection import GridSearchCV

logi = LogisticRegression(penalty = "l1 or l2",solver = "liblinear")
params = {"C": [0.01*i for i in range(1,10,1)]}
gscv = GridSearchCV(logi, 
                    param_grid=params,
                    verbose=1,  #詳細を表示
                    cv=3,  #交差検証の分割数
                    scoring='neg_log_loss',
                    n_jobs=-1  #全てのプロセッサを使用する。
                    )
gscv.fit(X_train, y_train)
print('best params:', gscv.best_params_)
print('best logloss:', gscv.best_score_)

#best_paramsの可視化
gscv_df = pd.DataFrame(gscv.cv_results_['mean_test_score'],
                       index=np.round(params['C'],2))
plt.plot(gscv_df)
plt.xlabel('C')
plt.ylabel('mean_test_score')
plt.grid(True)
plt.show()

決定木

分類問題の場合、ジニ係数を元に値が最小となるように分類する。

output.png

重要度の求め方
ある特徴量で分岐させることでどのくらい値を下げられるかを示す。
回帰問題の場合はMSE、分類問題の場合はジニ係数
(例)分類問題

\begin{align}
 (Pclassの重要度) &= I(Pclass)  \\
                  &= (623*0.474)-((275*0.492)+(348*0.369))  \\
\\
 (Parchの重要度) &= I(Parch)_1 + I(Parch)_2  \\
I(Parch)_1 &= (275*0.492)-((203*0.5)+(72*0.346))

\\
I(Parch)_2 &= (348*0.369)-((272*0.344)+(76*0.441))


\end{align}
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report, auc, roc_curve

dt = DecisionTreeClassifier(random_state=1234, 
                            max_depth=None  #全てのリーフがPureになるまで分岐する
                           )
dt.fit(X_train, y_train)
y_pred = dt.predict(X_test)
y_proba = dt.predict_proba(X_test)[:,1]

#混同行列
def show_evaluation_metrics(y_true, y_pred):
    print("Accuracy:")
    print(accuracy_score(y_true, y_pred))
    print()
    print("Report:")
    print(classification_report(y_true, y_pred))
    print("Confusion matrix:")
    print(confusion_matrix(y_true, y_pred))
show_evaluation_metrics(y_test, y_pred)

#AUC
def AUC_and_ROC_curve(y_true, y_proba):
    fpr, tpr, thresholds = roc_curve(y_true, y_proba)
    AUC = auc(fpr, tpr)
    plt.plot(fpr, tpr, label='ROC curve (AUC={:.3f})'.format(AUC))
    plt.legend()
    plt.title('ROC curve')
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.grid(True)
    plt.show()
AUC_and_ROC_curve(y_test, y_proba)

#特徴量の重要度
df_feature_importance = pd.DataFrame({'feature':X.columns, 'importance':dt.feature_importances_})
sns.barplot(x='importance', y='feature',
            data=df_feature_importance.sort_values('importance', ascending=False))
plt.show()

#決定木の可視化
plot_tree(dt, feature_names=X.columns, filled=True, rounded=True)
plt.show()

#パラメータ調整
from sklearn.model_selection import GridSearchCV

dt = DecisionTreeClassifier(random_state=1234)
params = {"max_depth": [i for i in range(4,10,1)]}
gscv = GridSearchCV(dt, param_grid=params, verbose=1, cv=3,scoring='neg_log_loss', n_jobs=-1)
gscv.fit(X_train, y_train)
print('best params:', gscv.best_params_)
print('best logloss:', gscv.best_score_)

#best_paramsの可視化
gscv_df = pd.DataFrame(gscv.cv_results_['mean_test_score'],
                       index=params['max_depth'])
plt.plot(gscv_df)
plt.xlabel('max_depth')
plt.ylabel('mean_test_score')
plt.grid(True)
plt.show()

ランダムフォレスト

決定木をたくさん作り、多数決を行う。
サンプリングはデータと特徴量両方で行われる。

252B495F-24E3-41C6-8930-7E87D5968901.jpeg

重要度の求め方
OOBのデータを使用。
ある特徴量(列)をシャッフルし、ランダムフォレストの精度の差を求める。

(重要度)=(シャッフル前の精度)-(シャッフル後の精度)

精度が下がる→その特徴量が重要であった。
精度があまり変わらない→その特徴量があまり必要ではない。

FBAC9618-E0D3-40EA-8284-5C94AE7AC3B5.jpeg

from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report, auc, roc_curve

rf = RandomForestClassifier(random_state=1234,
                            n_estimators=100,
                            max_depth=None  #全てのリーフがPureになるまで分岐する
                           )
rf.fit(X_train, y_train)
y_pred = rf.predict(X_test)
y_proba = rf.predict_proba(X_test)[:,1]

#混同行列
def show_evaluation_metrics(y_true, y_pred):
    print("Accuracy:")
    print(accuracy_score(y_true, y_pred))
    print()
    print("Report:")
    print(classification_report(y_true, y_pred))
    print("Confusion matrix:")
    print(confusion_matrix(y_true, y_pred))
show_evaluation_metrics(y_test, y_pred)

#AUC
def AUC_and_ROC_curve(y_true, y_proba):
    fpr, tpr, thresholds = roc_curve(y_true, y_proba)
    AUC = auc(fpr, tpr)
    plt.plot(fpr, tpr, label='ROC curve (AUC={:.3f})'.format(AUC))
    plt.legend()
    plt.title('ROC curve')
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.grid(True)
    plt.show()
AUC_and_ROC_curve(y_test, y_proba)

#特徴量の重要度
df_feature_importance = pd.DataFrame({'feature':X.columns, 'importance':rf.feature_importances_})
sns.barplot(x='importance', y='feature',
            data=df_feature_importance.sort_values('importance', ascending=False))
plt.show()

#パラメータ調整
from sklearn.model_selection import GridSearchCV

rf = RandomForestRegressor(random_state=1234)
params = {"n_estimators":[10*i for i in range(8,12,1)], "max_depth":[5*i for i in range(1,5,1)]}
gscv = GridSearchCV(rf, 
                    param_grid=params,
                    verbose=1,  #詳細を表示
                    cv=3,  #交差検証の分割数
                    scoring='neg_log_loss',
                    n_jobs=-1  #全てのプロセッサを使用する
                    )
gscv.fit(X_train, y_train)
print('best params:', gscv.best_params_)
print('best logloss:', gscv.best_score_)

#グリッドサーチの可視化
gscv_score = []
for i in range(len(gscv.grid_scores_)):
    gscv_score.append(gscv.grid_scores_[i][1])

gscv_df = pd.DataFrame(np.reshape(gscv_score,(len(params['n_estimators']),len(params['max_depth']))),
                       index=gscv.param_grid['n_estimators'],
                       columns=gscv.param_grid['max_depth'])
sns.heatmap(gscv_df, annot=True, fmt='1.2f'))
plt.show()

XGBoost

勾配Boosting木の一種で精度が高く、使いやすい。

勾配Boosting木(Gradient Boosting Decision Tree)
〜特徴〜
・特徴量は数値         → 特徴量がある値より大きいか小さいかで分岐するため
・欠損値を扱うことができる   → 欠損値かどうかで分岐もすることができる
・変数間の相互作用が反映される → 分岐の繰り返しによりできる
・スケーリングする必要がない  → 値の大小関係のみが重要
・カテゴリー変数をone-hot-encodingする必要がない
                → 決定木の分岐の際に、勝手に特徴量を抽出してくれる
・疎行列への対応がサポートされている

〜仕組み〜
目的変数$y_i$と予測値$\hat{y_i}$の差について学習し、逐次的に決定木を作成する。
ハイパーパラメータである決定木の本数分、上記を繰り返す。
最終的に各決定木のウェイト$W_m$の総和が予測値となる。

C8112030-AF9A-40A8-910C-5825268AC099.jpeg

木の作成の仕方
Level-wise tree growth・・・深さ単位で木を成長させる。
CEC13020-2396-4D3E-8197-A06304F5C3AD.jpeg

重要度の求め方
gain → 特徴量によって得られた損失の減少を示し、基本的な手法。
他にも、"weight","cover","total_gain","total_cover"がある。

#sklearnAPI ver.
from xgboost import XGBClassifier
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report, auc, roc_curve

xgb = XGBClassifier(random_state=1234,
                    learning_rate=0.3,  # 学習率
                    max_depth=6,  #木の深さ(最も重要)
                    importance_type='gain'  #特徴量の算出方法
                    )
xgb.fit(X_train, y_train)
y_pred = xgb.predict(X_test)
y_proba = xgb.predict_proba(X_test)[:,1]

#混同行列
def show_evaluation_metrics(y_true, y_pred):
    print("Accuracy:")
    print(accuracy_score(y_true, y_pred))
    print()
    print("Report:")
    print(classification_report(y_true, y_pred))
    print("Confusion matrix:")
    print(confusion_matrix(y_true, y_pred))
show_evaluation_metrics(y_test, y_pred)

#AUC
def AUC_and_ROC_curve(y_true, y_proba):
    fpr, tpr, thresholds = roc_curve(y_true, y_proba)
    AUC = auc(fpr, tpr)
    plt.plot(fpr, tpr, label='ROC curve (AUC={:.3f})'.format(AUC))
    plt.legend()
    plt.title('ROC curve')
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.grid(True)
    plt.show()
AUC_and_ROC_curve(y_test, y_proba)

#特徴量の重要度
df_feature_importance = pd.DataFrame({'feature':X.columns, 'importance':xgb.feature_importances_})
sns.barplot(x='importance', y='feature',
            data=df_feature_importance.sort_values('importance', ascending=False))
plt.show()

#ハイパーパラメータ調整
from sklearn.model_selection import GridSearchCV

xgb =XGBClassifier(random_state=1234)
params = {"learning_rate":[0.05*i for i in range(0,5)],
          "max_depth":[i for i in range(6,10,1)]}
gscv = GridSearchCV(xgb, 
                    param_grid=params,
                    verbose=1,  #詳細を表示
                    cv=3,  #交差検証の分割数
                    scoring='neg_log_loss',
                    n_jobs=-1  #全てのプロセッサを使用する。
                    )
gscv.fit(X_train, y_train)
print('best params:', gscv.best_params_)
print('best logloss:', gscv.best_score_)

#グリッドサーチの可視化
gscv_df = pd.DataFrame(np.reshape(gscv.cv_results_['mean_test_score'],(len(params['learning_rate']),len(params['max_depth']))),
                       index=np.round(gscv.param_grid['learning_rate'],2),
                       columns=gscv.param_grid['max_depth'])
sns.heatmap(gscv_df, annot=True, fmt='1.2f')
plt.show()

LGBM

XGBoostと比較して、高速かつ精度が同等程度と考えられている。

木の作成の仕方
Leaf-wise tree growth・・・葉単位で木を成長させる
CDB0F4F3-03AB-4EB0-A7B5-A490F680FB21.jpeg

重要度の求め方
split → 分岐に使われた回数
他にも、"gain"を使うことができる。

#sklearnAPI ver.
from lightgbm import LGBMClassifier
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report, auc, roc_curve

lgbm = LGBMClassifier(random_state=1234,
                      num_leaves=31,  #葉の数
                      min_child_samples=20,  #各ノードに入る最小データ数
                      importance_type='split'  #特徴量の算出方法
                     )
lgbm.fit(X_train, y_train)
y_pred = lgbm.predict(X_test)
y_proba = lgbm.predict_proba(X_test)[:,1]

#混同行列
def show_evaluation_metrics(y_true, y_pred):
    print("Accuracy:")
    print(accuracy_score(y_true, y_pred))
    print()
    print("Report:")
    print(classification_report(y_true, y_pred))
    print("Confusion matrix:")
    print(confusion_matrix(y_true, y_pred))
show_evaluation_metrics(y_test, y_pred)

#AUC
def AUC_and_ROC_curve(y_true, y_proba):
    fpr, tpr, thresholds = roc_curve(y_true, y_proba)
    AUC = auc(fpr, tpr)
    plt.plot(fpr, tpr, label='ROC curve (AUC={:.3f})'.format(AUC))
    plt.legend()
    plt.title('ROC curve')
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.grid(True)
    plt.show()
AUC_and_ROC_curve(y_test, y_proba)

#特徴量の重要度
df_feature_importance = pd.DataFrame({'feature':X.columns, 'importance':lgbm.feature_importances_})
sns.barplot(x='importance', y='feature',
            data=df_feature_importance.sort_values('importance', ascending=False))
plt.show()

#ハイパーパラメータ調整
from sklearn.model_selection import GridSearchCV

lgbm = LGBMClassifier(random_state=1234)
params = {"num_leaves":[i for i in range(28,34,1)],
          "min_child_samples":[i for i in range(17,23,1)]}
gscv = GridSearchCV(lgbm, 
                    param_grid=params,
                    verbose=1,  #詳細を表示
                    cv=3,  #交差検証の分割数
                    scoring='neg_log_loss',
                    n_jobs=-1  #全てのプロセッサを使用する。
                    )
gscv.fit(X_train, y_train)
print('best params:', gscv.best_params_)
print('best logloss:', gscv.best_score_)

#グリッドサーチの可視化
gscv_df = pd.DataFrame(np.reshape(gscv.cv_results_['mean_test_score'],(len(params['num_leaves']),len(params['min_child_samples']))),
                       index=gscv.param_grid['num_leaves'],
                       columns=gscv.param_grid['min_child_samples'])
sns.heatmap(gscv_df, annot=True, fmt='1.2f')
plt.show()

SVM

from sklearn.svm import SVC
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report, auc, roc_curve

svm = SVC(kernel='rbf', #linear:直線的に分割
          C=1.0,  #正規化パラメータ
          gamma='scale',  #決定境界の複雑さ scale:1/(n_features*X.var()), auto:1/n_features
          probability=True)  #確率推定を有効
svm.fit(X_train, y_train)
y_pred = svm.predict(X_test)
y_proba = svm.predict_proba(X_test)[:,1]

#混同行列
def show_evaluation_metrics(y_true, y_pred):
    print("Accuracy:")
    print(accuracy_score(y_true, y_pred))
    print()
    print("Report:")
    print(classification_report(y_true, y_pred))
    print("Confusion matrix:")
    print(confusion_matrix(y_true, y_pred))
show_evaluation_metrics(y_test, y_pred)

#AUC
def AUC_and_ROC_curve(y_true, y_proba):
    fpr, tpr, thresholds = roc_curve(y_true, y_proba)
    AUC = auc(fpr, tpr)
    plt.plot(fpr, tpr, label='ROC curve (AUC={:.3f})'.format(AUC))
    plt.legend()
    plt.title('ROC curve')
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.grid(True)
    plt.show()
AUC_and_ROC_curve(y_test, y_proba)

#ハイパーパラメータ調整
from sklearn.model_selection import GridSearchCV

svc = SVC(kernel='rbf', random_state=1234)
params = {"gamma":[0.001*i for i in range(0,10,1)]}
gscv = GridSearchCV(svc, 
                    param_grid=params,
                    verbose=1,  #詳細を表示
                    cv=3,  #交差検証の分割数
                    scoring='accuracy',
                    n_jobs=-1  #全てのプロセッサを使用する。
                    )
gscv.fit(X_train, y_train)
print('best params:', gscv.best_params_)
print('best logloss:', gscv.best_score_)

#best_paramsの可視化
gscv_df = pd.DataFrame(gscv.cv_results_['mean_test_score'],
                       index=np.round(params['gamma'], 3))
plt.plot(gscv_df)
plt.xlabel('gamma')
plt.ylabel('accuracy')
plt.grid(True)
plt.show()

損失関数

以下の尤度関数$,f(\lambda)$を用いた**Cross Entropy誤差関数$,E(\lambda)$**を使用する。

\begin{align}
\lambda &= \frac{1}{1+e^{-(\beta_0+ \beta_1 X_1\cdots +\beta_K X_K)}}\\
\lambda_i &= \frac{1}{1+e^{-(\beta_0+ \beta_1 x_{1,i}\cdots +\beta_K x_{K,i})}} \cdots i\,が1と判断される確率  \\
\\

f(\lambda ) &= \displaystyle \prod_{i=1}^{n}\lambda_i^{t_i} 
 \quad(t_i\cdots0\,or\,1) \\


E(\lambda) &= -log\,f(\lambda)\\
           &= -log \displaystyle \prod_{i=1}^{N}\lambda^{t_i}\\
           &=-\displaystyle \sum_{ i = 1 }^{ n } t_i log\lambda\\
\end{align}

他にも『Binary Cross Entropy 誤差関数』を使用することもできる。

\begin{align}

f(\lambda ) &= \displaystyle \prod_{i=1}^{n}\lambda_i^{t_i}(1-\lambda_i)^{(1-t_i)} 
 \quad(t_i\cdots0\,or\,1) \\


E(\lambda) &= -log\,f(\lambda)\\
           &= -log \displaystyle \prod_{i=1}^{N}\lambda^{t_i}(1-\lambda)^{(1-t_i)}\\
           &=-\displaystyle \sum_{ i = 1 }^{ n } (t_i log\lambda + (1-t_i)log(1-\lambda))\\
\end{align}

精度評価指標

混同行列

       予測されたクラス
実際のクラス
Positive Negative
Positive True Positive False Negative
Negative False Positive True Negative
\begin{align}
Accuracy(正解率) \cdots \frac{True Positive + True Negative}{(全データ)}\\
\\
Precision(適合率) \cdots \frac{True Positive}{TruePositive+FalsePositive}\\
\\
Recall(再現率) \cdots \frac{True Positive}{TruePositive+FalseNegative}\\
\\
F1Score \cdots \frac{2*Recall*Precision}{Recall+Precision}  \quad\quad\quad \\ 
\\
FbetaScore \cdots \frac{(1+\beta^2)*Recall*Precision}{\beta^2Recall+Precision}
\end{align}

AUC

閾値を変化させ、ROC曲線を描く。
AUCはその下側の面積となる。
AUCは0.5に近いほどモデルの精度が低く、1.0に近いほどモデルの精度が高いことを示している。

       予測されたクラス
実際のクラス
Positive Negative
Positive True Positive False Negative
Negative False Positive True Negative
(真陽性率)=\frac{True Positive}{True Positive + False Negative}   \\
\\
(偽陽性率)=\frac{False Positive}{False Positive + True Negative}

D5F444EA-55C0-4CAD-9741-580F81BE1368.jpeg

#あとがき
KaggleやSignateのコンペティションに熱中している間に、モデルの内容や指標の解釈を忘れてしまうので、すぐ概要を確認できるようにまとめました。
詳細を突き詰めると、間違えている箇所はあるかもしれませんがご容赦ください。

<参考文献>
・scikit-learnホームページ(https://scikit-learn.org/stable/)
・XGBoostホームページ(https://xgboost.readthedocs.io/en/latest/)
・LightGBMホームページ(https://lightgbm.readthedocs.io/en/latest/)
・Kaggleで勝つデータ分析の技術(技術評論社/著:門脇大輔,阪田隆司,保坂桂佑,平松雄司)

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?