欠損値の補完でリークする理由:平均値が20から40に変わる教材で確認
欠損値を平均で埋める作業にも、評価データの情報が入り込む可能性があります。機械学習の前処理では、学習用と評価用を分けた後に補完規則を学習するのが基本です。架空の温度の4行で、分ける順番の違いを手計算します。
欠損は0と同じではない
記録されていない値を0にすると、実際に0だった測定値と区別できません。教材の温度なら、センサーが記録しなかったことと0度を測ったことは別です。最初に、空欄、測定不能、対象外などが同じ記号になっていないか確認します。今回扱うのは「数値が欠けている」ことだけで、欠損の理由を推測する処理ではありません。
| 用途 | 温度の教材値 |
|---|---|
| 学習 | 10、欠損、30 |
| 評価 | 80 |
全体の平均で埋めると、評価行が規則を変える
観測された全行の平均は(10+30+80)/3=40です。この40で学習側の欠損を埋めると、評価用の80を見てから学習データを作ったことになります。
学習用だけの平均なら(10+30)/2=20です。学習行は10、20、30となります。将来の入力に欠損があっても、この学習済み規則では20を使います。評価側の分布を見て平均を決め直しません。数字の大小ではなく、規則を決める際に何を参照したかが問題です。
fitとtransformを分けて読む
import numpy as np
from sklearn.impute import SimpleImputer
train = np.array([[10.0], [np.nan], [30.0]])
test = np.array([[80.0], [np.nan]])
imputer = SimpleImputer(strategy="mean")
train_filled = imputer.fit_transform(train)
test_filled = imputer.transform(test)
assert imputer.statistics_[0] == 20.0
assert train_filled[:, 0].tolist() == [10.0, 20.0, 30.0]
assert test_filled[:, 0].tolist() == [80.0, 20.0]
fitは学習行から補完に必要な値を決める段階、transformはその値を使って変換する段階です。SimpleImputerは、平均、中央値などの方法を指定できます。上は平均を学ぶ演習で、どのデータでも平均補完が最良になるという推奨ではありません。[出典1]
交差検証でも「分割ごと」に補完を学ぶ
学習データ全体で補完してから交差検証に入ると、各分割の評価側を先に参照します。各分割の学習側で補完規則を学ぶ順序が必要です。前処理とモデルをPipelineで組み合わせれば、この順序を管理しやすくなります。[出典2]
時系列なら、将来に当たる行を前処理のfitへ含めないことも確認します。利用者ごとの繰り返し記録がある問題では、同じ利用者の行が学習と評価に分かれる影響も別に点検します。
補完を決めるためのチェック表
| 質問 | 残す記録 |
|---|---|
| なぜ欠けているか | 未入力・測定不能・対象外を分けられるか |
| どこでfitしたか | 参照した行IDと期間 |
| 何で埋めたか | 列ごとの方法と学習した統計量 |
| 評価を見て変更したか | 調整用データと最終評価データの分離 |
| 本番でも同じか | 列順、欠損記号、保存した変換器の版 |
| 列全体が欠損したら | 使用ライブラリの扱いと後工程の入力列数 |
欠損の多い列を残すか、補完するか、別の方法を使うかは問題ごとの判断です。「欠損をなくした」ことだけを完成条件にせず、評価の情報が入り込んでいないことと、元の意味を壊していないことを確認してください。