CatBoostで始める不確実性予測


機械学習を用いた予測で「将来の売上がいくらになりそうか」「どのくらい需要が増減するか」を見積もるのは一般的になってきました。しかし、ビジネスの現場では「その予測がどれくらい外れる可能性があるのか」を把握することも非常に重要です。これを「不確実性(Uncertainty)」と呼び、リスクのある意思決定を行うときに役立ちます。

この記事では、CatBoostを使ってこの不確実性を推定してみます。
具体的には、

  1. 通常の回帰予測(RMSEを最小化)

  2. Quantile回帰を用いた上限・下限予測

を行い、「予測値 ± どれくらいブレそうか?」を見積もる流れをデモします。

データの準備

デモのデータセットには、Kaggleの「Bike Sharing Demand」データセットを利用しています。Bike Sharing Demand は、ワシントンD.C.の自転車貸し出しサービスの利用者数(需要)を予測するタスクです。日付・天候・気温などの特徴量から、各時間帯のレンタル数(count)を予測します。

環境はGoogle Colabです。
Kaggleからデータセットをダウンロードしてtrain.csvを配置しておきます。

必要ライブラリのインストール

!pip install catboost

CatBoostを利用するため、インストールしておきます。

データの読み込み

import pandas as pd
import numpy as np
from catboost import CatBoostRegressor, Pool

train_df = pd.read_csv('train.csv')

train_df.head()

ダウンロードしておいたデータセットの読み込みを行います。
特徴量としては、季節(season)、天気(weather)、気温(temp)、湿度(humidity)、風速(windspeed)といった列があります。
レンタルされた自転車の台数(count)列が予測対象になります。

簡単な前処理

train_df['datetime'] = pd.to_datetime(train_df['datetime'])
train_df['year'] = train_df['datetime'].dt.year
train_df['month'] = train_df['datetime'].dt.month
train_df['day'] = train_df['datetime'].dt.day
train_df['hour'] = train_df['datetime'].dt.hour

features = [
    'season',
    'holiday',
    'workingday',
    'weather',
    'temp',
    'atemp',
    'humidity',
    'windspeed',
    'casual',
    'registered',
    'year',
    'month',
    'day',
    'hour'
]

target = 'count'

X = train_df[features]
y = train_df[target]

学習前に簡単なデータの前処理を行っています。時間に関する特徴量をいくつか抜き出し、利用する特徴量と、予測対象の列を定義しています。

CatBoostの学習用データセット準備

categorical_features_indices = [0, 1, 2, 3] 

train_pool = Pool(
    data=X,
    label=y,
    cat_features=categorical_features_indices
)

ここでは季節(season)、天気(weather)等をカテゴリ扱いするためcat_featuresとして定義し、CatBoostの学習用データセットを準備します。

モデルの構築

model_rmse = CatBoostRegressor(
    loss_function='RMSE',
    iterations=1000,
    depth=6,
    verbose=False,
    random_seed=42
)
model_rmse.fit(train_pool)
model_median = CatBoostRegressor(
    loss_function='Quantile:alpha=0.5',
    iterations=1000,
    depth=6,
    verbose=False,
    random_seed=42
)
model_median.fit(train_pool)
model_lower = CatBoostRegressor(
    loss_function='Quantile:alpha=0.05',
    iterations=1000,
    depth=6,
    verbose=False,
    random_seed=42
)
model_lower.fit(train_pool)
model_upper = CatBoostRegressor(
    loss_function='Quantile:alpha=0.95',
    iterations=1000,
    depth=6,
    verbose=False,
    random_seed=42
)
model_upper.fit(train_pool)

ここで4つのモデルを構築しました。

  • model_rmse: 平均的な予測(RMSE最小化)が欲しい

  • model_median: 中央付近の堅実な予測

  • model_lower (lower 5%): 下ぶれリスクの見積もり

  • model_upper (upper 95%): 上ぶれリスクの見積もり

一般的な回帰モデルで予測した値の他に、中央値付近、下限、上限を推定することで、今回のような、需要予測のタスクにおいて「過剰在庫/品切れリスク」を判断しやすくなります。

予測結果の確認

pred_rmse = model_rmse.predict(X)
pred_lower = model_lower.predict(X)
pred_median = model_median.predict(X)
pred_upper = model_upper.predict(X)

results_df = pd.DataFrame({
    'rmse': pred_rmse,
    'lower_5%': pred_lower,
    'median': pred_median,
    'upper_95%': pred_upper,
    'actual': y
})

results_df.head(10)

実際に予測結果を見てみます。

  • rmse: 一般的な回帰モデル(RMSE最小化)で予測した値

  • lower_5%: モデルが考える「最低限このくらいは出るだろう」という推定(5%点)

  • median: 中央値(50%点)

  • upper_95%: 「最高これくらいにはなるかも」という推定(95%点)

  • actual: 実測のcount

となっています。

※ このサンプルはデモ目的です。実運用では検証用データを別に用意し、RMSEやカバー率(下限~上限に実測値がどれほど収まるか)を評価しましょう。

まとめ

この記事では、CatBoostを使って、不確実性(予測区間)を簡単に導出する方法をご紹介しました。

  • RMSEモデル: いわゆる普通の回帰タスク

  • Quantile回帰: αの値を変えて複数モデルを学習することで、下限~上限のブレ幅を推定

この予測区間をもとに、在庫管理やシフト計画などでどの程度余裕を持たせるべきか検討しやすくなります。特にloss_function='Quantile:alpha=〇〇'を指定するだけで分位点の回帰ができるのは、CatBoostならではのお手軽さです。