正解率0.95なのに役に立たない
モデルの評価とは、作ったモデルが新しいデータでどれだけ役に立つかを、数字で確かめることです。正解率だけを見ると、役に立たないモデルを良いと思ってしまうことがあります。
200件の貸出のうち、遅れるのは10件だけとします。「全部期限内」と答えるモデルは、正解率が 0.95 です。けれど遅れる貸出は1件も見つかりません。
このレッスンのゴール
メモ
混同行列から適合率と再現率を計算して、モデルを評価できる。
キーワード
| ことば | いみ |
|---|---|
| テストデータ | 学習に使わず、評価のためだけに取っておくデータ |
| 混同行列 | 予測と本当の答えを、4つの数に分けた表 |
| 正解率 | 予測が当たった割合 |
| 適合率 | 「見つけた」と予測したうち、本当に当たりだった割合 |
| 再現率 | 本当の当たりのうち、見つけられた割合 |
学生が入力するセルは、このプレビューでは空白です。
学習に使っていないデータで測る
学習に使ったデータで測ると、モデルは答えを覚えただけかもしれません。そこで、データの一部をテストデータとして取っておきます。残りで学習し、テストデータで点をつけます。
これなら、初めて見るデータに対する力を測れます。ラボの knn.py は、365日の4分の1にあたる92日をテストデータにしています。
knn.pyは何をするか
knn.py は、300台以上の日を busy、それ未満を quiet と呼びます。気温と雨から、近い日の多数決で busy かを当てるモデルです。
取っておいた92日に点をつけ、正解率と混同行列を表示します。表示を読むのが、このレッスンの練習です。
テストデータで点をつける
docker exec jupyter python3 steps/knn.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
混同行列は4つの数に分ける
混同行列は、予測と本当の答えを組み合わせた4つの数です。行が本当の答え、列が予測です。
busy を見つけたい日とすると、「busy と予測して当たった」が 23日です。「本当は busy なのに quiet と予測した」見逃しが 10日、「quiet なのに busy と予測した」空振りが 7日です。
例: knn.pyの混同行列
| busyと予測 | quietと予測 | |
|---|---|---|
| 本当は busy | 23 | 10 |
| 本当は quiet | 7 | 52 |
学生が入力するセルは、このプレビューでは空白です。
適合率と再現率は別のことを聞く
適合率は、busy と予測した日のうち、本当に busy だった割合です。23 ÷ (23 + 7) で約 0.77 です。
再現率は、本当に busy だった日のうち、見つけられた割合です。23 ÷ (23 + 10) で約 0.70 です。適合率は当てた予測の確かさ、再現率は見逃しの少なさを表します。
例: 3つの数字の比べ方
| 数字 | 式 | この表の値 |
|---|---|---|
| 正解率 | (23 + 52) ÷ 92 | 0.815 |
| 適合率 | 23 ÷ (23 + 7) | 0.77 |
| 再現率 | 23 ÷ (23 + 10) | 0.70 |
学生が入力するセルは、このプレビューでは空白です。
コマンドで2つの数字を求める
次のコマンドは、混同行列の23、7、10から、適合率と再現率を求めます。tp は当てた数、fp は空振り、fn は見逃しです。
答えは「0.77 0.7」です。0.70 の最後の 0 は、Python の表示では省かれます。
適合率と再現率を求める
docker exec jupyter python3 -c 'tp = 23; fp = 7; fn = 10; print(round(tp / (tp + fp), 2), round(tp / (tp + fn), 2))'ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
よくある間違い
注意
正解率が高ければ、良いモデルだと考えがちです。実際は、いつも多いほうを答えるだけでも、0.641 になることがあります。まず手抜きの決まりと比べ、適合率と再現率も見ます。
ノートブックに入力: 2つの数字
import pandas as pd
from sklearn.metrics import precision_score, recall_score
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
hires = pd.read_csv("hires.csv")
label = (hires["hires"] >= 300).map({True: "busy", False: "quiet"})
X = hires[["temp_c", "rain_mm"]]
X_train, X_test, y_train, y_test = train_test_split(X, label, test_size=0.25, random_state=1)
guess = KNeighborsClassifier(n_neighbors=5).fit(X_train, y_train).predict(X_test)
# busy を「見つけたい日」として、2つの数字を出します。
print("適合率:", round(precision_score(y_test, guess, pos_label="busy"), 3))
print("再現率:", round(recall_score(y_test, guess, pos_label="busy"), 3))