2クラス分類モデルの評価基準 その2


AIって結局何なのかよく分からないので、とりあえず100日間勉強してみた Day36


経緯についてはこちらをご参照ください。



■本日の進捗

●適合率-再現率カーブを理解

■はじめに

引き続き「Pythonではじめる機械学習(オライリー・ジャパン)」で学んでいきます。

前回学んだ適合率と再現率を用いて不確実性を考慮に入れた閾値の決定を学んでいきたいと思います。

■不確実性を考慮したより良いモデルを作る

まずは前回作った疑似医療スクリーニング用データセットを正規化してロジスティック回帰に学習させたモデルを振り返ります。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score, confusion_matrix, ConfusionMatrixDisplay, classification_report

n_samples = 500
n_features = 30

X, y = make_classification(n_samples=n_samples, n_features=n_features, 
                           n_informative=5, n_redundant=10, n_repeated=5,
                           n_clusters_per_class=1, class_sep=2, flip_y=0.1, 
                           random_state=8)

feature_names = [f"Feature{i}" for i in range(1, n_features+1)]

df = pd.DataFrame(X, columns=feature_names)
df['target'] = y

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

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

model = LogisticRegression(random_state=8)
model.fit(X_train_scaled, y_train)

y_pred = model.predict(X_test_scaled)

accuracy = accuracy_score(y_test, y_pred)
print('Accuracy: {:.2f}'.format(accuracy))

report = classification_report(y_test, y_pred, target_names=['Class 0', 'Class 1'])
print(report)

まずは公平性のために交差検証を適用してみます。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import classification_report

n_samples = 500
n_features = 30

X, y = make_classification(n_samples=n_samples, n_features=n_features, 
                           n_informative=5, n_redundant=10, n_repeated=5,
                           n_clusters_per_class=1, class_sep=2, flip_y=0.1, 
                           random_state=8)

feature_names = [f"Feature{i}" for i in range(1, n_features+1)]

df = pd.DataFrame(X, columns=feature_names)
df['target'] = y

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

model = LogisticRegression(random_state=8)

scores = cross_val_score(model, X_scaled, y, cv=5, scoring='accuracy')

print('mean cv score: {:.2f}'.format(np.mean(scores)))

model.fit(X_scaled, y)
y_pred = model.predict(X_scaled)
report = classification_report(y, y_pred, target_names=['Class 0', 'Class 1'])
print(report)

これが現時点のこのモデルの汎化性能と言えます。

今このモデルに必要なのはAccuracyではなく陽性の再現率(Recall)なので、predict_probaを用いて陰性の再現率を犠牲に陽性の再現率を向上させてみます。

import numpy as np
import pandas as pd
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_predict
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import classification_report

n_samples = 500
n_features = 30

X, y = make_classification(n_samples=n_samples, n_features=n_features, 
                           n_informative=5, n_redundant=10, n_repeated=5,
                           n_clusters_per_class=1, class_sep=2, flip_y=0.1, 
                           random_state=8)

feature_names = [f"Feature{i}" for i in range(1, n_features+1)]

df = pd.DataFrame(X, columns=feature_names)
df['target'] = y

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

model = LogisticRegression(random_state=8)

y_prob = cross_val_predict(model, X_scaled, y, cv=5, method='predict_proba')

threshold = 0.05
y_pred_custom = (y_prob[:, 1] >= threshold).astype(int)

report = classification_report(y, y_pred_custom, target_names=['Class 0', 'Class 1'])
print(report)



■適合率-再現率カーブ

適合率-再現率カーブ(Precision-Recall Curve)とは、分類モデルにおける適合率と再現率のバランスを可視化するためのものです。

先程のモデルは陽性の再現率を2%上げるのに陰性の再現率を38%犠牲にしました。アプリケーションによってはこの2%がクリティカルな場合もあると思うので、一概に悪いことではないのですが、やはり効率が良いとは言えないように思えます。

このモデルで一番おいしいスイートスポットはどこなのでしょうか。また、陽性再現率を上げるためにどこまで妥協できるでしょうか。こういった調整を行うのに良い指標となる表現方法が適合率-再現率カーブで、precision_recall_curveクラスで簡単に実装することが可能です。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_predict
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import classification_report, precision_recall_curve, average_precision_score

n_samples = 500
n_features = 30

X, y = make_classification(n_samples=n_samples, n_features=n_features, 
                           n_informative=5, n_redundant=10, n_repeated=5,
                           n_clusters_per_class=1, class_sep=2, flip_y=0.1, 
                           random_state=8)

feature_names = [f"Feature{i}" for i in range(1, n_features+1)]

df = pd.DataFrame(X, columns=feature_names)
df['target'] = y

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

model = LogisticRegression(random_state=8)

y_prob = cross_val_predict(model, X_scaled, y, cv=5, method='predict_proba')

threshold = 0.05
y_pred_custom = (y_prob[:, 1] >= threshold).astype(int)

report = classification_report(y, y_pred_custom, target_names=['Class 0', 'Class 1'])
print(report)

precision, recall, _ = precision_recall_curve(y, y_prob[:, 1])
average_precision = average_precision_score(y, y_prob[:, 1])

plt.figure(figsize=(8, 6))
plt.plot(precision, recall, marker='.', label='Precision-Recall curve')
plt.title('Precision-Recall Curve')
plt.xlabel('Precision')
plt.ylabel('Recall')
plt.grid()
plt.legend()
plt.show()

再現率を1.0まで上げると適合率が0.5まで下がり、再現率0.96辺りが最もバランスが良いということが分かります。

■おわりに

あくまで訓練データの中での話ではありますが、再現率を1.0にするのに適合率も0.5は保っていることは、すべての入力データを陽性と返すモデルと比べれば遥かに有用で、スクリーニングのみに用いるのであれば仕事を大幅に減らせるとも考えられます。

必要なf-値は目的に沿って決める必要がありますが、適合率-再現率カーブは良い指標になりそうです。

■参考文献

  1. Andreas C. Muller, Sarah Guido. Pythonではじめる機械学習. 中田 秀基 訳. オライリー・ジャパン. 2017. 392p.
  2. ChatGPT. 4o mini. OpenAI. 2024. https://chatgpt.com/
  3. API Reference. scikit-learn.org. https://scikit-learn.org/stable/api/index.html


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です