0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

2値分類のしきい値をいじって精度の調整

Last updated at Posted at 2024-04-19

Scikit-LearnやKerasやその他いろいろと最後にシグモイド関数を使って分類する事は2値分類(陽性か陰性)では多いです。
ですが、2値分類において大体の場合しきい値は0.5以上だと1(一般に陽性)で0.5未満だと0(一般に陰性)とすることが初期設定では多いです。
ただ、もちろんそれに合わせて回帰しているのでその考え方自体は間違っていないのですが、時として他の値をしきい値にすることでより汎化する事もあります。
そこで以下の指標を基に実際にロジスティック回帰のしきい値を変えて汎化能力を考察してみましょう。

  • 正解率
  • 再現率
    陽性・陰性のデータが実際に陽性・陰性と判断された割合
  • 適合率
    陽性・陰性と判断されたデータが実際に陽性・陰性だった割合
  • f1
    再現率と適合率の調和平均

コーディング

ライブラリのインポート

ロジスティック回帰を分かりやすくするためここではstatsmodelsを使用します。

from sklearn.metrics import classification_report
import statsmodels.api as sm
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

データの読み込み

ここでは分かりやすく陰性と陽性を扱いたいのでScikit-Learnにあります乳がんのデータセットを使います。あらかじめCSVにしているのでそれを読み込みます(目的変数の名前が当時解析できなかったので「y」としています)。

df = pd.read_csv("breast_cancer.csv")
df.head()

目的変数と説明変数で分ける

ここではstatsmodelsを使用しているため何もしないと切片が出来ないため「add_constatnt」を行いcontantという説明変数を作り「1」で埋めます。
※なお、本当は自由に値を最後に入れてテスト(シミュレーション)をする場合は変数間の相関、つまり多重共線性を考慮しないといけないのでVIFを本当なら計算して因子を選択しましょう。

x = sm.add_constant(df.drop("y", axis=1))
y = df["y"]

モデルの生成と回帰

実際にロジスティック回帰を作成して説明変数をテストデータとして値を出力させます。

model = sm.Logit(y, x).fit_regularized()
y_pred_proba = model.predict(x)
y_pred_proba

するとこのように出力されます。

0      6.312334e-55
1      1.152860e-32
2      2.268552e-41
3      4.608393e-20
4      9.211627e-26
           ...     
564    6.152497e-61
565    1.854253e-35
566    6.885977e-10
567    1.064712e-47
568    1.000000e+00
Length: 569, dtype: float64

しきい値をいじって判別

ここからが本題で、ロジスティック回帰におけるシグモイド関数のしきい値を0.5以外で設定した時にどのように精度が変動するかを見ていきます。

acc = []
rcl0 = []
rcl1 = []
pcs0 = []
pcs1 = []
f10 = []
f11 = []
x = []
for i in range(1, 10):
    y_pred = np.where(y_pred_proba.values >= i/10, 1, 0)
    rep = classification_report(y, y_pred, output_dict=True)
    acc.append(rep["accuracy"])
    rcl0.append(rep["0.0"]["recall"])
    rcl1.append(rep["1.0"]["recall"])
    pcs0.append(rep["0.0"]["precision"])
    pcs1.append(rep["1.0"]["precision"])
    f10.append(rep["0.0"]["f1-score"])
    f11.append(rep["1.0"]["f1-score"])
    x.append(i/10)
plt.plot(x, acc, marker="x", label="accuracy")
plt.plot(x, rcl0, marker="x", label="recall 0")
plt.plot(x, rcl1, marker="x", label="recall 1")
plt.plot(x, pcs0, marker="x", label="precision 0")
plt.plot(x, pcs1, marker="x", label="precision 1")
plt.plot(x, f10, marker="x", label="f1 0")
plt.plot(x, f11, marker="x", label="f1 1")
plt.legend()
plt.show()

これによって以下のような折れ線グラフが出力されます。
(横軸:しきい値、縦軸:精度)
oresenn.png
これらを総合的に見るとしきい値はどの値が適切かを考察できます。
例えばこのグラフから陰性の適合率は値が大きくなるにつれて下がりますが、陽性の適合率は陰性の適合率ほどの変動はありません。
正解率やf1に着目すると0.3と0.4が適切になります。
その他にもいろいろ考察できますが、何が言いたいかというと正解率単体に着目していると予測のバランス(陰性と陽性の正誤の比率)が悪くなったり、陰性と陽性の識別はトレードオフだったり案外f1(F値)が再現率と適合率のバランスにおいてその辺役に立つなどがあります。

いずれにせよ、別に本来のしきい値の0.5でも問題ないんですけどね、本当は。ただ、一度こういう実験をしてみるのも大切かなと思った次第です。

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?