まとめ
- การถดถอยควอนไทล์ประมาณค่ากลาง เปอร์เซ็นไทล์ 10 หรือเปอร์เซ็นไทล์ใดๆ ได้โดยตรง ไม่จำกัดแค่ค่าเฉลี่ย
- การใช้ pinball loss ทำให้โมเดลทนทานต่อ outlier และรับมือกับ noise ที่ไม่สมมาตรได้ดี
- แต่ละควอนไทล์เป็นโมเดลแยกกัน หากรวมควอนไทล์ต่ำและสูงเข้าด้วยกันจะได้แถบคาดการณ์ (prediction interval)
- การทำมาตรฐานและการเลือกพารามิเตอร์การทำให้เป็นระเบียบช่วยให้การหาคำตอบลู่เข้าและมีการทั่วไปที่ดี
ภาพรวมเชิงสัญชาติญาณ #
ต่างจากวิธีกำลังสองน้อยที่สุดที่สนใจค่าเฉลี่ย การถดถอยควอนไทล์ตอบคำถามว่า “มีโอกาสเท่าไรที่ค่าจะต่ำกว่าจุดนี้” จึงเหมาะกับงานที่ต้องการมุมมองหลายระดับ เช่น คาดการณ์ความต้องการในมุมมองแย่ที่สุด/ปานกลาง/ดีที่สุด หรือคำนวณ Value at Risk ในการจัดการความเสี่ยง
สูตรสำคัญ #
ให้ค่าคลาดเคลื่อน \(r = y - \hat{y}\) และควอนไทล์ \(\tau \in (0,1)\) pinball loss นิยามเป็น
$$ L_\tau(r) = \begin{cases} \tau , r & (r \ge 0) \\ (\tau - 1) r & (r < 0) \end{cases} $$
การทำให้ loss นี้ต่ำสุดให้ตัวพยากรณ์ที่สอดคล้องกับควอนไทล์ \(\tau\) เช่น \(\tau = 0.5\) เท่ากับการถดถอยแบบมัธยฐาน ซึ่งมีพฤติกรรมเดียวกับการลดค่าความคลาดเคลื่อนสัมบูรณ์และทนทานต่อ outlier
ทดลองด้วย Python #
โค้ดต่อไปนี้ใช้ QuantileRegressor เพื่อประมาณควอนไทล์ 0.1, 0.5 และ 0.9 พร้อมกับเส้นเชิงเส้นทั่วไป
from __future__ import annotations
import japanize_matplotlib
import matplotlib.pyplot as plt
import numpy as np
from sklearn.linear_model import LinearRegression, QuantileRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
def run_quantile_regression_demo(
taus: tuple[float, ...] = (0.1, 0.5, 0.9),
n_samples: int = 400,
xlabel: str = "input x",
ylabel: str = "output y",
label_observations: str = "observations",
label_mean: str = "mean (OLS)",
label_template: str = "quantile τ={tau}",
title: str | None = None,
) -> dict[float, tuple[float, float]]:
"""Fit quantile regressors alongside OLS and plot the conditional bands.
Args:
taus: Quantile levels to fit (each in (0, 1)).
n_samples: Number of synthetic observations to generate.
xlabel: Label for the x-axis.
ylabel: Label for the y-axis.
label_observations: Legend label for the scatter plot.
label_mean: Legend label for the OLS line.
label_template: Format string for quantile labels.
title: Optional title for the plot.
Returns:
Mapping of quantile level to (min prediction, max prediction).
"""
japanize_matplotlib.japanize()
rng = np.random.default_rng(123)
x_values: np.ndarray = np.linspace(0.0, 10.0, n_samples, dtype=float)
noise: np.ndarray = rng.gamma(shape=2.0, scale=1.0, size=n_samples) - 2.0
y_values: np.ndarray = 1.5 * x_values + 5.0 + noise
X: np.ndarray = x_values[:, np.newaxis]
quantile_models: dict[float, make_pipeline] = {}
for tau in taus:
model = make_pipeline(
StandardScaler(with_mean=True),
QuantileRegressor(alpha=0.001, quantile=float(tau), solver="highs"),
)
model.fit(X, y_values)
quantile_models[tau] = model
ols = LinearRegression()
ols.fit(X, y_values)
grid: np.ndarray = np.linspace(0.0, 10.0, 200, dtype=float)[:, np.newaxis]
preds = {tau: model.predict(grid) for tau, model in quantile_models.items()}
ols_pred: np.ndarray = ols.predict(grid)
fig, ax = plt.subplots(figsize=(10, 5))
ax.scatter(X, y_values, s=15, alpha=0.4, label=label_observations)
color_cycle = plt.rcParams["axes.prop_cycle"].by_key().get("color", ["#1f77b4", "#ff7f0e", "#2ca02c"])
for idx, tau in enumerate(taus):
color = color_cycle[idx % len(color_cycle)]
ax.plot(
grid,
preds[tau],
color=color,
linewidth=2,
label=label_template.format(tau=tau),
)
ax.plot(grid, ols_pred, color="#9467bd", linestyle="--", label=label_mean)
ax.set_xlabel(xlabel)
ax.set_ylabel(ylabel)
if title:
ax.set_title(title)
ax.legend()
fig.tight_layout()
plt.show()
summary: dict[float, tuple[float, float]] = {
tau: (float(pred.min()), float(pred.max())) for tau, pred in preds.items()
}
return summary
summary = run_quantile_regression_demo(
xlabel="อินพุต x",
ylabel="เอาต์พุต y",
label_observations="ข้อมูลที่สังเกต",
label_mean="ค่าเฉลี่ย (OLS)",
label_template="ควอนไทล์ τ={tau}",
title="การถดถอยควอนไทล์และแถบคาดการณ์",
)
for tau, (ymin, ymax) in summary.items():
print(f"τ={tau:.1f}: ค่าพยากรณ์ต่ำสุด {ymin:.2f}, สูงสุด {ymax:.2f}")

วิเคราะห์ผลลัพธ์ #
- แต่ละควอนไทล์ให้เส้นที่ต่างกัน จึงมองเห็นการกระจายตัวด้านบนและด้านล่างได้ชัด
- เมื่อเปรียบเทียบกับ OLS ที่จับค่าเฉลี่ย จะเห็นว่าโมเดลควอนไทล์รับมือกับ noise ที่เอียงด้านเดียวได้ดีกว่า
- การมีหลายควอนไทล์พร้อมกันช่วยสร้างช่วงคาดการณ์เพื่อสนับสนุนการตัดสินใจ เช่น กำหนด buffer สำหรับอุปสงค์
เอกสารอ้างอิง #
- Koenker, R., & Bassett, G. (1978). Regression Quantiles. Econometrica, 46(1), 33 E0.
- Koenker, R. (2005). Quantile Regression. Cambridge University Press.