推論時の入力に未知の欠損値がある場合のlightgbmの挙動の確認

はじめに

以前にlightgbmは入力に欠損値があってもうまく学習してくれるという記事を書いたのですが、 これは学習時に欠損が存在している場合の話でした。

現実の問題を考えると、学習時とそのモデルを使った推論時では時系列の違いや環境変化の影響 により取得していたデータが欠落したりするケースもあると思います。 このように学習データには存在しなかったが、推論時の入力で欠損が入ってしまうパターンが発生した時にモデルがどのような挙動をとるかが気になりましたので、 調査・実験をしました。

参考文献

こちらでは、学習時と推論時でカテゴリカル変数列が異なる(推論時に未知のカテゴリが発生する)場合について書かれており、 カテゴリ変数であることを明示しておけばis, is notで判定されるため基本的には問題ないとしていました。

When zero_as_missing=false (default), the unshown values in sparse matrices (and LightSVM) are treated as zeros.

公式によると上記のように書いてあるので、欠損は0と扱われるっぽい?

実験

sklearnにあるボストンの住宅価格のデータを用い、 欠損なしで学習させたモデルに数値型の変数に欠損がある入力を使って推論した場合の挙動を確認してみます。

このデータセットには欠損はなく、カラムはすべて数値型になります。

boston = load_boston()
X=pd.DataFrame(boston.data, columns=boston.feature_names)
y=boston.target

X.head()

f:id:rmizutaa:20191206141655p:plain

X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.33, random_state=42)

params={
        'objective': 'regression',
        'random_state' : 1,
        "metric": "rmse",
        }

dtrain = lgb.Dataset(X_train, label=y_train)
dvalid = lgb.Dataset(X_valid, label=y_valid)
bst = lgb.train(params, dtrain, num_boost_round=1000,valid_sets=[dtrain, dvalid],early_stopping_rounds=50,verbose_eval=100)

#AGEをすべて0にした入力データ作成
X_valid_zero=X_valid.copy()
X_valid_zero["AGE"]=0

#AGEを全て欠損にした入力データ作成
X_valid_nan=X_valid.copy()
X_valid_nan["AGE"]=np.nan

checkdf=pd.DataFrame()
checkdf["normal"]=bst.predict(X_valid)
checkdf["zero"]=bst.predict(X_valid_zero)
checkdf["nan"]=bst.predict(X_valid_nan)

checkdf.head()

f:id:rmizutaa:20191206141712p:plain

すべて0としたケースとすべて欠損としたケースで予測値が一致しました。 一応他の変数についても同様のことを行いましたが挙動は同じでした。 学習時に欠損がなかった場合、予測時に投入するデータの欠損は0として扱われるようです。

全文コードは以下になります。

github.com

まとめ

欠損のないデータでモデルを作成した場合、欠損のあるデータを予測時に投入すると lightgbmはその欠損は0として扱うことが確認できました。 lightgbmだとこういうケースでもエラーにならずに出力してしまうので注意が必要ですね。 対応策としては、モデルの再作成や推論時の入力データの欠損値補完が良いかなと思いました。