Ml-tips

【LightGBM】分位点回帰で予測区間を出す

はじめに

普通の回帰は「だいたいこのくらい」という1点を予測します。しかし実務では、「予測はこのくらいだが、上振れ・下振れはどこまであり得るか」という**幅(不確実性)**を知りたい場面があります。

LightGBMはobjective="quantile"分位点回帰ができます。alphaで指定した分位点(例:alpha=0.9なら「9割がこの値以下」という値)を予測するもので、複数のalphaで学習すれば予測区間が作れます。

この記事では、予測区間の作り方と、その区間が実際に当たっているか(カバレッジ)を確認します。

分位点回帰で予測区間を作る

ノイズの大きさがxとともに増えるデータを用意します(xが大きいほど散らばる)。alpha0.05 / 0.5 / 0.95にした3つのモデルを学習し、0.05〜0.95を90%予測区間とします。

import numpy as np
import lightgbm as lgb
import matplotlib.pyplot as plt

rng = np.random.RandomState(0)
n = 5000
x = rng.uniform(0, 10, n)
noise_std = 0.2 + 0.25 * x                       # x が大きいほどノイズ大
y = np.sin(x) + 0.3 * x + rng.randn(n) * noise_std
X = x.reshape(-1, 1)
tr = np.arange(n) < 4000                          # train/test split

params = dict(num_leaves=15, learning_rate=0.05, min_child_samples=50, verbose=-1)

# 分位点ごとに別々のモデルを学習する
models = {a: lgb.train(dict(params, objective="quantile", alpha=a),
                       lgb.Dataset(X[tr], y[tr]), num_boost_round=300)
          for a in [0.05, 0.5, 0.95]}

grid = np.linspace(0, 10, 500).reshape(-1, 1)
q05, q50, q95 = (models[a].predict(grid) for a in [0.05, 0.5, 0.95])

plt.scatter(x[~tr], y[~tr], s=6, alpha=0.15, color="gray", label="test data")
plt.fill_between(grid.ravel(), q05, q95, alpha=0.25, color="#1f77b4",
                 label="90% interval (alpha=0.05 - 0.95)")
plt.plot(grid, q50, color="#1f77b4", lw=2, label="median (alpha=0.5)")
plt.xlabel("x"); plt.ylabel("y"); plt.legend(); plt.show()

xが大きいほど区間の幅が広がり、データの散らばりが大きくなる様子をそのまま捉えています。真ん中の線はalpha=0.5中央値の予測です(平均ではありません)。

区間は当たっているか(カバレッジ)

予測区間は「作れる」だけでなく「当たっている」ことが大事です。テストデータの真値が[q05, q95]に入る割合(カバレッジ)を測ります。90%区間なら理想は0.90です。

pred = {a: models[a].predict(X[~tr]) for a in [0.05, 0.5, 0.95]}
yte = y[~tr]

inside = (yte >= pred[0.05]) & (yte <= pred[0.95])
print("90%区間のカバレッジ:", round(float(inside.mean()), 3), "(理想 0.90)")

for a in [0.05, 0.5, 0.95]:
    emp = float((yte <= pred[a]).mean())
    print(f"  alpha={a}: 真値がその予測以下の割合={emp:.3f} (理想 {a})")
90%区間のカバレッジ: 0.881 (理想 0.90)
  alpha=0.05: 真値がその予測以下の割合=0.067 (理想 0.05)
  alpha=0.5: 真値がその予測以下の割合=0.537 (理想 0.5)
  alpha=0.95: 真値がその予測以下の割合=0.948 (理想 0.95)

90%区間に実際に88.1%が入り、ほぼ狙い通りです。各分位点の実測割合も、指定したalphaに近い値になっています。有限データなので理想値とぴったりにはなりませんが、区間が意味のある形で機能していることが確認できます。

注意点

  • 分位点ごとに別モデルが必要です。区間の上下+中央値なら3回学習します。多くの分位点が欲しいときは学習コストが増えます。
  • **分位点交差(quantile crossing)**が起こり得ます。分位点を独立に学習するため、場所によってq05 > q95のように上下が逆転することがあります(今回のデータでは0件でしたが、一般には起こります)。気になる場合は交差を後処理で補正します。
  • ここで得られる区間はデータのばらつき(予測分布の広がり)を表すもので、モデルパラメータの信頼区間とは別物です。
  • alpha=0.5は平均ではなく中央値を最適化します。外れ値の影響を受けにくいのが特徴です。

まとめ

  • LightGBMはobjective="quantile"alphaで分位点回帰ができる
  • 複数のalpha(例:0.05 / 0.5 / 0.95)を学習すれば予測区間が作れる
  • 区間はカバレッジ(真値が入る割合)で検証できる。今回は90%区間に88.1%が入った
  • 分位点ごとに別学習が必要/分位点交差に注意

参考リンク