「過去問の丸暗記」問題
モデルを作ったら、それが本当に役に立つかを確かめる必要があります。では、学習に使ったデータで予測させて、正解率を測ればよいでしょうか。
これは、学校のテストに例えると分かりやすくなります。ある生徒が、過去問の答えを丸暗記したとします。本番のテストが過去問とまったく同じ問題なら、この生徒は100点を取れます。しかし、それで「この生徒は内容を理解している」と言えるでしょうか。問題が少し変わっただけで、まったく解けないかもしれません。
モデルも同じです。学習に使ったデータで評価すると、モデルが「理解」しているのか「丸暗記」しているのかを区別できません。 前のレッスンで見たように、深い決定木は学習データをほぼ完璧に覚え込めるので、この方法では正解率がほぼ100%になってしまいます。
本当に知りたいのは、まだ見たことのない新しいデータに対して、どれくらい正しく予測できるか です。実際にモデルを使う場面では、予測の対象は必ず「学習のときには無かったデータ」だからです。
そこで、手元のデータを最初に2つに分けます。
- 訓練データ: モデルの学習(
fit)に使うデータ
- テストデータ: 学習には一切使わず、最後の評価だけに使うデータ
テストデータは、いわば「モデルがまだ見たことのない本番のテスト」の役割を果たします。
train_test_splitでデータを分ける
scikit-learnでは、train_test_splitという関数でデータを訓練用とテスト用に分けられます。
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=0
)
model = DecisionTreeClassifier(max_depth=3, random_state=0)
model.fit(X_train, y_train) # 学習には訓練データだけを使う
model.score(X_test, y_test) # テストデータで正解率を測る
1つずつ見ていきましょう。
test_size=0.25: 全体の25%をテストデータにする、という意味です。100件なら、75件が訓練データ、25件がテストデータになります
- 分割はランダムに行われます。
random_state=0のように数値を指定すると、何度実行しても同じ分け方になります。結果を他の人が再現できるように、実務でも指定しておくのが一般的です
- 戻り値は
X_train, X_test, y_train, y_test の順で4つ返ってきます。順番を間違えやすいので注意しましょう
- 分類モデルの
scoreは、正解率(予測が当たった割合)を返します
大事なのは、fitには必ず訓練データを、評価には必ずテストデータを渡す という役割分担を守ることです。
データリーク:テストの答えが漏れていないか
データを分けても、気づかないうちにテストデータの情報が学習に紛れ込んでしまうことがあります。これを データリーク(情報漏洩) と呼びます。リークが起こると、テストの正解率は高く出るのに、実際に使うと全然当たらないモデルが出来上がってしまいます。
よくある例を3つ挙げます。
- 分割する前に、データ全体を使って前処理をしてしまう
例えば、欠損値を「全データの平均値」で埋めてから分割すると、その平均値にはテストデータの情報が含まれています。前処理に使う値は、訓練データだけから計算するのが原則です
- 同じ人・同じものが、訓練とテストの両方に入ってしまう
1人の患者から何枚も撮った画像を、ランダムに分けると、同じ患者の画像が両方に入ります。モデルは「その人の特徴」を覚えるだけで高得点を取れてしまいます
- テストデータの結果を見ながら、何度もモデルを調整してしまう
テストデータで正解率を見ては
max_depthを変えて…と繰り返すと、テストデータに合わせた「過去問対策」になってしまいます。調整用には、訓練データの中からさらに「検証データ」を分けて使います
振り返り
- 学習に使ったデータで評価しても、本当の実力は分からない
- データは訓練データとテストデータに分け、テストデータは最後の評価だけに使う
- テストデータの情報が学習に紛れ込む「データリーク」に注意する