Skip to content

🏆 Tiêu chuẩn — Basic Forecasting ​

Các tiêu chuẩn giúp bạn đánh giá forecast model đúng cách, tránh overfitting, và communicate uncertainty — Time Series Cross-Validation cho evaluation, Forecast Accuracy Metrics cho so sánh, Prediction Intervals cho uncertainty.

Tổng quan tiêu chuẩn buổi 16 ​

Buổi 16 chuyển từ A/B Testing (Buổi 15) sang Basic Forecasting — dự báo tương lai bằng dữ liệu quá khứ. Forecast tốt không chỉ cần model tốt — cần evaluation nghiêm ngặt, metrics phù hợp, và communication uncertainty:

  1. Time Series Cross-Validation — Expanding/Sliding window evaluation → đánh giá model stability, tránh overfitting
  2. Forecast Accuracy Metrics — MAE, MAPE, RMSE, MASE → chọn metric đúng cho đúng context
  3. Prediction Intervals — Quantify uncertainty → hỗ trợ business decision-making

📋 Danh sách tiêu chuẩn liên quan ​

#Tiêu chuẩnTổ chức / Tác giảÁp dụng cho Buổi 16
1Time Series Cross-ValidationHyndman & Athanasopoulos (FPP3)Evaluation strategy cho time series — không random split
2Forecast Accuracy MetricsM-Competition (Makridakis)Standard metrics cho forecast benchmarking
3Prediction IntervalsNIST, Chatfield (1993)Uncertainty quantification cho forecast output

1️⃣ Time Series Cross-Validation — Đánh giá đúng cách ​

Giới thiệu ​

Time Series Cross-Validation (TSCV) — phương pháp đánh giá forecast model bằng cách tạo nhiều train/test splits theo thứ tự thời gian. Khác biệt cơ bản với k-fold CV thông thường: KHÔNG random shuffle — tương lai luôn phải nằm sau quá khứ.

TSCV phổ biến trong Forecasting: Principles and Practice (Hyndman & Athanasopoulos) và được sử dụng trong Makridakis Competitions (M3, M4, M5) — benchmark lớn nhất thế giới cho forecasting methods.

Tại sao KHÔNG dùng Random Split cho Time Series? ​

AspectRandom Split (k-Fold CV)Time Series CV
Data leakage❌ Future data leak vào train → overfit✅ Train luôn trước Test
Temporal order❌ Phá vỡ thứ tự thời gian✅ Bảo tồn temporal order
Real-world simulation❌ Không giống production✅ Simulate đúng: train past → predict future
Autocorrelation❌ Ignore dependency✅ Data contiguous, autocorrelation preserved

2 Strategies: Expanding Window vs Sliding Window ​

EXPANDING WINDOW (Anchored):
━━━━━━━━━━━━━━━━━━━━━━━━━━
Fold 1: [Train: 1-12 ] → Test: [13-15]
Fold 2: [Train: 1-15 ] → Test: [16-18]
Fold 3: [Train: 1-18 ] → Test: [19-21]
Fold 4: [Train: 1-21 ] → Test: [22-24]
Fold 5: [Train: 1-24 ] → Test: [25-27]

→ Train set GROWS mỗi fold → more data = potentially better model
→ Phù hợp khi data cũ vẫn relevant

SLIDING WINDOW (Rolling):
━━━━━━━━━━━━━━━━━━━━━━━━
Fold 1: [Train: 1-12 ] → Test: [13-15]
Fold 2: [Train: 4-15 ] → Test: [16-18]
Fold 3: [Train: 7-18 ] → Test: [19-21]
Fold 4: [Train: 10-21] → Test: [22-24]
Fold 5: [Train: 13-24] → Test: [25-27]

→ Train set FIXED size → recent data weighted equally
→ Phù hợp khi data cũ không còn relevant (concept drift)

Implementation trong Python ​

python
from statsmodels.tsa.holtwinters import ExponentialSmoothing
import numpy as np
import pandas as pd

def ts_cross_validate(series, min_train_size, forecast_horizon, step=1,
                       method='expanding'):
    """
    Time Series Cross-Validation.

    Parameters:
        series: pd.Series with DatetimeIndex
        min_train_size: minimum training observations
        forecast_horizon: number of periods to forecast
        step: step size between folds
        method: 'expanding' or 'sliding'

    Returns:
        dict with fold results
    """
    results = []
    n = len(series)

    for i in range(min_train_size, n - forecast_horizon + 1, step):
        if method == 'expanding':
            train = series[:i]
        else:  # sliding
            train = series[max(0, i - min_train_size):i]

        test = series[i:i + forecast_horizon]

        # Fit model
        model = ExponentialSmoothing(
            train, trend='add', seasonal='add', seasonal_periods=12
        ).fit(optimized=True)

        forecast = model.forecast(len(test))

        # Calculate metrics
        mae = np.mean(np.abs(test.values - forecast.values))
        mape = np.mean(np.abs((test.values - forecast.values) / test.values)) * 100

        results.append({
            'fold': len(results) + 1,
            'train_size': len(train),
            'test_start': test.index[0],
            'MAE': mae,
            'MAPE': mape
        })

    return pd.DataFrame(results)

# Usage
# cv_results = ts_cross_validate(ts, min_train_size=24,
#                                  forecast_horizon=6, step=3)
# print(f"Mean CV MAPE: {cv_results['MAPE'].mean():.2f}%")
# print(f"Std CV MAPE:  {cv_results['MAPE'].std():.2f}%")

Checklist Time Series CV ​

✅ KHÔNG BAO GIỜ random shuffle time series data
✅ Train set luôn TRƯỚC test set theo thời gian
✅ Minimum train size ≥ 2 seasonal cycles (24 months cho monthly)
✅ Forecast horizon = business-relevant horizon (6 months, 1 quarter)
✅ Report MEAN và STD of metrics across folds (không chỉ 1 fold)
✅ So sánh expanding vs sliding → chọn phù hợp
✅ Nếu MAPE vary nhiều giữa folds → model không stable → investigate

Ưu & nhược điểm ​

Ưu điểmNhược điểm
✅ Simulate real forecasting scenario❌ Computationally expensive (nhiều folds)
✅ Detect overfitting — model tốt trên 1 period nhưng kém trên khác❌ Cần đủ data (≥ 3 seasonal cycles)
✅ Report không chỉ accuracy mà cả stability (std across folds)❌ Sliding window bỏ data cũ → potential information loss
✅ Industry standard — Makridakis Competitions dùng TSCV❌ Không handle structural breaks tốt

2️⃣ Forecast Accuracy Metrics — Chọn metric đúng ​

Giới thiệu ​

Makridakis Competitions (M-Competitions) — series forecasting competitions bắt đầu từ 1982 (M1) bởi Spyros Makridakis, giáo sư INSEAD. M4 (2018) và M5 (2020) là benchmark lớn nhất — hàng trăm teams, hàng nghìn time series. M-Competitions standardize cách đánh giá forecast accuracy và establish best practices cho metrics.

Bảng Metrics ​

MetricCông thứcScale-dependent?Symmetric?Handle Yt=0?
MAE1n∑|et|✅ Có✅✅
RMSE1n∑et2✅ Có✅✅
MAPE100n∑|etYt|❌ Không (%)❌ Không❌ Undefined
sMAPE200n∑|et||Yt|+|Y^t|❌ Không (%)⚠️ Gần✅
MASEMAEMAEnaïve❌ Không (ratio)✅✅

Trong đó et=Yt−Y^t (forecast error).

Khi nào dùng metric nào? ​

MetricDùng khiKHÔNG dùng khi
MAESo sánh models cùng dataset, cùng scaleSo sánh CROSS datasets (scale khác)
RMSEPenalize outlier errors (large errors costly)Errors đều quan trọng như nhau
MAPECommunicate với business (% dễ hiểu)Yt có thể = 0 (intermittent demand)
MASESo sánh ACROSS datasets, benchmark vs naïveDataset quá ngắn cho naïve baseline

MAPE — Phổ biến nhưng có cạm bẫy ​

MAPE là metric phổ biến nhất trong business forecasting vì dễ hiểu — "forecast sai trung bình 8%". Tuy nhiên:

Cạm bẫy 1: Asymmetric

  • Yt=100, Y^t=150 → error = |100−150|100 = 50%
  • Yt=150, Y^t=100 → error = |150−100|150 = 33%
  • Cùng absolute error (50), nhưng MAPE khác nhau! MAPE penalize over-forecast nhiều hơn under-forecast.

Cạm bẫy 2: Undefined khi Yt=0

  • Sản phẩm bán 0 units 1 ngày → MAPE = division by zero
  • Intermittent demand (spare parts, luxury items) → MAPE không dùng được

Cạm bẫy 3: Infinity khi Yt rất nhỏ

  • Yt=1, Y^t=3 → MAPE = 200% — misleading!

MASE (Mean Absolute Scaled Error) — proposed bởi Rob Hyndman (2006), metric chính được dùng trong M4 competition.

MASE=MAE1n−m∑t=m+1n|Yt−Yt−m|

Trong đó m = seasonal period. Mẫu số = MAE của seasonal naïve forecast.

MASEÝ nghĩa
< 1Model tốt hơn seasonal naïve (good!)
= 1Model ngang naïve (bad — model vô nghĩa)
> 1Model kém hơn naïve (very bad — delete model)
python
def mase(actual, forecast, seasonal_period=12):
    """Calculate MASE — Mean Absolute Scaled Error."""
    n = len(actual)
    mae_model = np.mean(np.abs(actual - forecast))

    # Seasonal naïve MAE (in-sample)
    naive_errors = np.abs(np.diff(actual.values, n=seasonal_period))
    mae_naive = np.mean(naive_errors)

    return mae_model / mae_naive

# MASE < 1 → model tốt hơn seasonal naïve
# MASE > 1 → bỏ model, dùng naïve còn hơn

Business Reporting — Metric nào report cho ai? ​

AudienceMetricLý do
CEO / CFOMAPEDễ hiểu: "Forecast sai 8%"
DA teamMAE + RMSE + MASETechnical comparison giữa models
Supply ChainMAE (units)"Forecast sai ±500 units" → actionable
Academic / CompetitionMASEScale-free, benchmark vs naïve

3️⃣ Prediction Intervals — Quantify Uncertainty ​

Giới thiệu ​

Prediction Intervals (PI) — khoảng tin cậy cho forecast. Thay vì nói "revenue tháng sau = 1,500 triệu VND", nói "revenue tháng sau = 1,500 triệu VND, 95% PI = [1,300 — 1,700]".

PI được formalize bởi Chris Chatfield (1993) và áp dụng rộng rãi trong NIST (National Institute of Standards and Technology) guidelines. Trong business forecasting, PI là tiêu chuẩn bắt buộc — point forecast alone = misleading.

Point Forecast vs Prediction Interval ​

AspectPoint ForecastPrediction Interval
Output1 con sốRange [lower, upper]
Uncertainty❌ Ẩn✅ Hiển thị rõ ràng
Decision-making⚠️ Over-confident✅ Informed decisions
Communication"Revenue = 1,500M""Revenue = 1,300M — 1,700M (95%)"
Business usageSingle planning scenarioBest/Base/Worst planning scenarios

Tính Prediction Interval cho Holt-Winters ​

Prediction interval RỘNG dần theo forecast horizon — càng xa tương lai, càng không chắc chắn:

Forecast Horizon vs Prediction Interval Width:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Month 1: ████████████░░░░░░░░░░░░░░░░░  (narrow — confident)
Month 2: ███████████░░░░░░░░░░░░░░░░░░░
Month 3: ██████████░░░░░░░░░░░░░░░░░░░░
Month 4: █████████░░░░░░░░░░░░░░░░░░░░░
Month 5: ████████░░░░░░░░░░░░░░░░░░░░░░
Month 6: ███████░░░░░░░░░░░░░░░░░░░░░░░  (wide — uncertain)

Legend: █ = forecast range, ░ = outside range
python
from statsmodels.tsa.holtwinters import ExponentialSmoothing

# Fit model
model = ExponentialSmoothing(
    ts, trend='add', seasonal='add', seasonal_periods=12
).fit(optimized=True)

# Simulation-based prediction intervals
simulations = model.simulate(
    nsimulations=6,       # 6 months ahead
    repetitions=1000,     # 1000 Monte Carlo paths
    error='mul',          # Multiplicative error
    random_errors='bootstrap'  # Bootstrap from residuals
)

# Calculate intervals
pi_80 = {
    'lower': simulations.quantile(0.10, axis=1),
    'upper': simulations.quantile(0.90, axis=1)
}
pi_95 = {
    'lower': simulations.quantile(0.025, axis=1),
    'upper': simulations.quantile(0.975, axis=1)
}

print("Prediction Intervals:")
print(f"  80% PI: [{pi_80['lower'].iloc[0]:.0f}, {pi_80['upper'].iloc[0]:.0f}]")
print(f"  95% PI: [{pi_95['lower'].iloc[0]:.0f}, {pi_95['upper'].iloc[0]:.0f}]")

Business Applications của Prediction Intervals ​

LevelBusiness UseVí dụ
Lower bound (P10)Pessimistic scenario — cash flow planning"Worst case revenue ≥ 1,300M"
Point forecast (P50)Base case — budgeting"Expected revenue = 1,500M"
Upper bound (P90)Optimistic scenario — capacity planning"Best case revenue ≤ 1,700M"

Checklist Prediction Intervals ​

✅ LUÔN report prediction interval cùng point forecast
✅ Dùng 80% PI cho business planning (phổ biến hơn 95%)
✅ Dùng 95% PI cho risk scenarios và stress testing
✅ Nhắc stakeholders: PI rộng dần theo horizon — xa = ít chắc chắn
✅ Nếu PI quá rộng → model không đủ tốt hoặc data quá noisy
✅ Dùng simulation-based PI (bootstrap) cho non-normal errors
✅ Validate PI: Coverage test — 95% PI nên chứa actual ≈ 95% thời gian

PI Coverage Test ​

python
def pi_coverage_test(actuals, lower_bounds, upper_bounds, target_coverage=0.95):
    """Test if prediction intervals achieve target coverage."""
    covered = sum((a >= l) and (a <= u)
                  for a, l, u in zip(actuals, lower_bounds, upper_bounds))
    actual_coverage = covered / len(actuals)

    print(f"Target coverage: {target_coverage:.0%}")
    print(f"Actual coverage: {actual_coverage:.0%}")
    print(f"Status: {'✅ OK' if abs(actual_coverage - target_coverage) < 0.05 else '❌ Review needed'}")

    return actual_coverage

Tổng kết — Khi nào dùng tiêu chuẩn nào? ​

Tiêu chuẩnÁp dụng khiOutput
Time Series CVEvaluate model trước khi deployMean + Std MAPE across folds
Forecast MetricsSo sánh models, report cho stakeholdersMAE, MAPE, RMSE, MASE
Prediction IntervalsCommunicate forecast uncertaintyPoint forecast + 80%/95% PI

Workflow tích hợp 3 tiêu chuẩn ​

📊 FORECASTING BEST PRACTICES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Step 1: BUILD — Fit multiple models (Naïve, SMA, ETS, HW)
Step 2: EVALUATE — Time Series CV (expanding window, ≥ 5 folds)
Step 3: COMPARE — MAE/MAPE/RMSE per model, chọn best
Step 4: VALIDATE — MASE < 1 (beat naïve?)
Step 5: FORECAST — Point forecast + 95% PI
Step 6: REPORT — MAPE (business), MASE (technical), PI chart
Step 7: MONITOR — Track actual vs forecast monthly
Step 8: RE-TRAIN — Update model khi có data mới

💡 Best Practices Summary

  • Evaluation: Dùng Time Series CV, KHÔNG random split
  • Metrics: Report MAPE cho business, MASE cho technical benchmark
  • Uncertainty: Forecast = point + interval. Never point alone.
  • Monitoring: Track forecast accuracy continuously. MAPE drift > 5pp → investigate.