20.3 Egyszerű előrejelző modellek és baseline

20.3.1 Cél és konkrét helyzet

A Northstar-projekt orders.csv fájlját napi rendelésszámmá aggregálva rövid távú napi előrejelzési feladatot kapunk. Ebben az alfejezetben nem a 7 napot meghaladó kézbesítés bináris célját, hanem egy idősoros döntési kérdést vizsgálunk: a következő 7 nap mindegyikére hány rendelés várható, ha csak a mai napig ismert adatot használhatjuk. A megfigyelési egység egy nap; a célváltozó az aznapi rendelésszám, mértékegysége darab. Az időbélyeg UTC, a gyakoriság 11 nap. A predikció pillanata az utolsó teljesen megfigyelt nap végén, a következő nap 00:00 UTC időpontja, a horizont pedig h=1,…,7h=1,\dots,7 nap.

A labor egy ismert szerkezetű, determinisztikus seedű szintetikus napi sort használ, hogy a várható hiba nagyságrendjét is ellenőrizhessük. A feladat: építsük meg a naiv (persistence) és szezonális naiv baseline-t, illesszünk egy kis exponenciális simítási modellt, majd több időablakon, mindig csak a múltból számolva hasonlítsuk össze őket korrekt hibamértékkel. Az ARIMA-modellt áttekintés szintjén ismerjük meg; a teljes azonosítás és elmélet nem előfeltétel.

20.3.2 Autokorreláció kis példán

Az idősoros modellek alapötlete, hogy a mai érték hasonlíthat a tegnapihoz. Az autokorreláció (autocorrelation) a sorozat önmagával vett, kk nappal eltolt lineáris kapcsolata. Az y1,…,yTy_1,\dots,y_T sorozat kk rendű autokorrelációja:

rk=∑t=k+1T(yt−y‾)(yt−k−y‾)∑t=1T(yt−y‾)2, r_k = \frac{\sum_{t=k+1}^{T}(y_t-\bar y)(y_{t-k}-\bar y)} {\sum_{t=1}^{T}(y_t-\bar y)^2},

ahol yty_t a tt napi érték, y‾\bar y a mintaátlag, TT a megfigyelések száma, kk a késleltetés (lag), napokban. A nevező a teljes varianciaarányos skálázás, a számláló pedig a kk nappal eltolt párok együttes ingadozása. Ha a szomszédos napok együtt mozognak, r1r_1 közel +1+1; ha váltakoznak, közel −1-1.

Kézi kontroll a [10,11,12,13,14,15][10, 11, 12, 13, 14, 15] sorozaton: y‾=12,5\bar y=12{,}5, az eltérések [−2,5,−1,5,−0,5,0,5,1,5,2,5][-2{,}5, -1{,}5, -0{,}5, 0{,}5, 1{,}5, 2{,}5]. A k=1k=1 számláló 3,75+0,75−0,25+0,75+3,75=8,753{,}75+0{,}75-0{,}25+0{,}75+3{,}75=8{,}75, a nevező 17,517{,}5, így r1=0,5r_1=0{,}5. A pozitív érték azt mutatja, hogy a monoton növekvő sorban a tegnapi érték információt hordoz a mairól; ez indokolja a naiv baseline-t.

20.3.3 Egyszerű modellek és jelölések

Naiv modell (persistence). A legjobb tipp a holnapi értékre a mai érték. Ha a predikció origója TT, akkor minden h≥1h\ge 1 horizontra:

ŷT+h|T=yT. \hat y_{T+h|T} = y_T .

Itt ŷT+h|T\hat y_{T+h|T} a TT-ig ismert adatokból készült hh lépéses előrejelzés; mértékegysége megegyezik yy-nal. A modellnek nincs illesztendő paramétere, ezért kiváló baseline.

Szezonális naiv. Heti ciklusnál m=7m=7 nap; a holnapi értéket a múlt heti azonos napra adjuk. Általánosan, ha mm a periódus hossza napokban:

ŷT+h|T=yT+h−m⌈h/m⌉, \hat y_{T+h|T}=y_{T+h-m\lceil h/m\rceil},

ahol ⌈⋅⌉\lceil\cdot\rceil felfelé kerekítés. Például T=7T=7, m=7m=7, h=1h=1 esetén y1y_{1}, h=7h=7 esetén y7y_7, h=8h=8 esetén ismét y1y_1. Gyakorlatban ez az utolsó mm megfigyelés ciklikus ismétlése.

Exponenciális simítás. Az egyszerű exponenciális simítás (simple exponential smoothing, SES) egy szintet tart karban:

lt=αyt+(1−α)lt−1,ŷT+h|T=lT. l_t = \alpha y_t + (1-\alpha) l_{t-1}, \qquad \hat y_{T+h|T}=l_T .

A szint ltl_t mértékegysége megegyezik a célváltozóéval; α\alpha simítási paraméter, dimenziótlan, 0≤α≤10 \le \alpha \le 1. Kis α\alpha simít, nagy α\alpha gyorsan követi a friss megfigyelést; az optimális α\alpha az illesztés során becsülhető. Szezonális adatra a Holt–Winters-változat trendet és periódusonkénti szezonális tagokat is becsül; a statsmodels ExponentialSmoothing osztálya ezt a seasonal="add" és seasonal_periods=7 argumentumokkal adja.

ARIMA áttekintés. Az ARIMA(p,d,q)(p,d,q) család általános keret: pp autoregresszív tag a saját múltat használja, qq mozgóátlag tag a múltbeli hibákat, dd pedig az egyszerű differenciát y′t=yt−yt−1y'_t=y_t-y_{t-1} a trend eltávolítására. AR(1):

yt=c+ϕ1yt−1+εt, y_t = c + \phi_1 y_{t-1} + \varepsilon_t,

MA(1):

yt=c+εt+θ1εt−1, y_t = c + \varepsilon_t + \theta_1 \varepsilon_{t-1},

ahol cc konstans, ϕ1\phi_1 és θ1\theta_1 becsült együtthatók, εt\varepsilon_t véletlen hibatag, naponként azonos szórású, nem autokorrelált. A teljes ARIMA-azonosítás meghaladja ezt az alfejezetet; a laborban az exponenciális simítás a választott kis statisztikai modell, az ARIMA pedig opcionális összehasonlítás.

Reziduális. Az illesztett modell in-sample hibája

et=yt−ŷt|t−1, e_t = y_t - \hat y_{t|t-1},

vagyis a tényleges érték és az egylépéses modell-előrejelzés különbsége. Jó modellnél a reziduális közelítőleg fehér zaj: nulla körüli átlag, közel konstans szórás és nincs jelentős autokorreláció. Autokorrelált maradék arra utal, hogy a modell kihagyott struktúrát; ilyenkor trend- vagy AR/MA-taggal érdemes bővíteni.

20.3.4 Teljes minimális munkapélda

A következő kód szintetikus napi sort készít, majd négy darab 7 napos tesztablakon, mindig csak az origó előtti adatra illesztve értékeli a naiv, a szezonális naiv és a Holt–Winters-modellt. A kimenet szemléltető; a tényleges futási számok a helyi környezettől és a seedtől függenek.

import numpy as np
import pandas as pd
from statsmodels.tsa.holtwinters import ExponentialSmoothing
from sklearn.metrics import mean_absolute_error

rng = np.random.default_rng(20261020)
n = 168
t = np.arange(n)
trend = 0.08 * t
season = 4.0 * np.sin(2 * np.pi * t / 7)
noise = rng.normal(0, 1.8, size=n)
y = 30.0 + trend + season + noise
series = pd.Series(
    y, index=pd.date_range("2025-01-01", periods=n, freq="D")
)

def evaluate_origin(series, origin, horizon=7):
    train = series.iloc[:origin]
    test = series.iloc[origin:origin + horizon]

    # Naiv baseline: az utolsó tanítóérték ismétlése
    naive_pred = np.full(horizon, train.iloc[-1])

    # Szezonális naiv: az utolsó 7 nap ciklikus ismétlése
    seasonal_pred = np.tile(train.iloc[-7:].to_numpy(), (horizon // 7) + 1)[:horizon]

    # Kis statisztikai modell: additív Holt-Winters
    hw_model = ExponentialSmoothing(train, seasonal="add", seasonal_periods=7)
    hw_fit = hw_model.fit(optimized=True)
    hw_pred = hw_fit.forecast(horizon).to_numpy()

    naive_scale = np.mean(np.abs(np.diff(train.to_numpy())))
    result = {
        "naive_mae": mean_absolute_error(test, naive_pred),
        "seasonal_naive_mae": mean_absolute_error(test, seasonal_pred),
        "hw_mae": mean_absolute_error(test, hw_pred),
        "naive_mase": mean_absolute_error(test, naive_pred) / naive_scale,
        "hw_mase": mean_absolute_error(test, hw_pred) / naive_scale,
        "resid": train.to_numpy() - hw_fit.fittedvalues.to_numpy(),
    }
    return result

train_size = 140
horizon = 7
windows = []
for origin in range(train_size, n, horizon):
    windows.append((origin, evaluate_origin(series, origin, horizon)))

rows = []
for origin, r in windows:
    rows.append({
        "window_origin": origin,
        "naive_mae": r["naive_mae"],
        "seasonal_naive_mae": r["seasonal_naive_mae"],
        "hw_mae": r["hw_mae"],
        "naive_mase": r["naive_mase"],
        "hw_mase": r["hw_mase"],
    })
results = pd.DataFrame(rows)
print(results.round(2))

A reziduális diagnosztikát az utolsó ablak tanítórészére így kérhetjük le:

from statsmodels.stats.diagnostic import acorr_ljungbox

ljung = acorr_ljungbox(windows[-1][1]["resid"], lags=[7], return_df=True)
print(ljung)

A szemléltető futtatásban a 7 napos periódus a determinisztikus magban látható; az adatgeneráló képlet 30+0,08t+4sin(2πt/7)+εt30 + 0{,}08t + 4\sin(2\pi t/7) + \varepsilon_t, ahol tt napokban, az amplitúdó és a szint rendelésszámban értendő, εt\varepsilon_t pedig független normális zaj, szórása 1,81{,}8 rendelés.

A szintetikus sorozat determinisztikus magja - Számított függvénypélda, nem mérés; a szimuláció zajt is hozzáad.

20.3.5 Eredmény értelmezése

A MAE (mean absolute error) abszolút hibák átlaga a tesztablakban, rendelésszámban:

MAE=1H∑h=1H|yT+h−ŷT+h|T|, \mathrm{MAE} = \frac{1}{H}\sum_{h=1}^{H}\left| y_{T+h} - \hat y_{T+h|T} \right|,

ahol H=7H=7 a horizont hossza. A MASE (mean absolute scaled error) ezt a naiv baseline in-sample hibájához viszonyítja: a nevező 1T−1∑t=2T|yt−yt−1|\frac{1}{T-1}\sum_{t=2}^{T}|y_t-y_{t-1}| a tanítóablak átlagos napi változása. A MASE dimenziótlan; 11-nél kisebb érték azt jelenti, hogy a modell jobb, mint a naiv, 11-nél nagyobb, hogy rosszabb. A MASE akkor használható érdemben, ha a nevező nem nulla; állandó sorozatnál nem értelmezhető.

Az elméletileg várt mintázat: a heti szezonalitás miatt a seasonal_naive_mae és a hw_mae kisebb, mint a naive_mae; a hw_mase tipikusan 11 alatt van, míg a naiv MASE 11 körül mozog, de a rövid tesztablakok miatt ablakonként ingadozik. A Holt–Winters-modell becsült simítási paraméterei az adott ablakra illeszkednek; a modell nem használhat jövőbeli tesztnapot. A Ljung–Box-teszt pp-értéke 0,050{,}05 felett azt jelzi, hogy a reziduális autokorrelációja nem szignifikáns; ez a modell elfogadhatóságának egyik feltétele, nem önmagában bizonyíték a jó előrejelzésre.

Horizont és kiértékelés: a hétlépéses előrejelzésnél külön kell nevezni a direkt és a rekurzív stratégiát, valamint horizontonként összehasonlítani a baseline modelleket. A h=1 mérőszáma önmagában nem igazolja a h=7 előrejelzés használhatóságát.

20.3.6 Feladatok

  1. Értelmezés. Legyen m=2m=2, a tanítóablak utolsó két értéke [101,103][101, 103], a tesztablak pedig [104,106,105,107][104, 106, 105, 107]. Számítsa ki kézzel a naiv és a szezonális naiv előrejelzést mind a 4 napra, majd adja meg a MAE értékét. Melyik baseline a jobb ebben a rövid ablakban?

  2. Megvalósítás. Futtassa a fenti kódot, majd módosítsa a Holt–Winters-modellt seasonal="mul" értékre. Hasonlítsa össze a két változat ablakonkénti MAE-jét, és indokolja, miért várható az additív változat előnye a használt generáló képlet mellett. A döntéshez ne használja a tesztablakot modellválasztásra; rögzítse előre a kritériumot.

  3. Hibadiagnózis. Egy társ a naiv baseline-t így számolja: pred = pd.Series(test).shift(1). Milyen hibát követ el a fix originű, 7 napos előrejelzésnél? Adjon invariánst, amellyel a helyes naiv előrejelzés ellenőrizhető.

  4. Transzfer. Növelje a zaj szórását az adatgeneráló kódban 1,81{,}8-ról 6,06{,}0-ra, és futtassa újra a négy ablakot. Hogyan változnak a MAE és a MASE értékek? Miért változik kevésbé a MASE, mint a MAE?

20.3.7 Megoldások és indoklás

  1. A naiv előrejelzés mind a 4 napra 103103. A szezonális naiv m=2m=2-vel az utolsó két értéket ismétli: [101,103,101,103][101, 103, 101, 103]. A naiv hibái: |104−103|+|106−103|+|105−103|+|107−103|=1+3+2+4=10|104-103|+|106-103|+|105-103|+|107-103|=1+3+2+4=10, MAE =10/4=2,5=10/4=2{,}5. A szezonális naiv hibái: |104−101|+|106−103|+|105−101|+|107−103|=3+3+4+4=14|104-101|+|106-103|+|105-101|+|107-103|=3+3+4+4=14, MAE =14/4=3,5=14/4=3{,}5. Ebben a rövid ablakban a naiv jobb; ez arra figyelmeztet, hogy a szezonális naiv nem automatikus nyertes.

  2. A multiplikatív változat akkor indokolt, ha a szezonális amplitúdó a szinttel arányosan nő. A generáló képletben a szezonális tag additív konstans amplitúdójú, ezért az additív specifikáció közelebb van az igaz folyamathoz; a multiplikatív változat a kis szintingadozást is arányosítja, és rövid ablakon túlilleszthet. A helyes döntési eljárás előre rögzített, például az utolsó ablak kivételével az összes ablak átlagos MAE-je alapján választunk, a félretett ablak csak jelentésre szolgál.

  3. A shift(1) minden tesztnapra az előző tényleges tesztértéket adja, ami egy rögzített TT origóból nem áll rendelkezésre; ez jövőinformáció, és optimista hibát okozhat. A helyes naiv prediktor konstans: np.full(horizon, train.iloc[-1]). Invariáns: assert (pred == train.iloc[-1]).all() és assert len(pred) == horizon.

  4. Nagyobb zajnál a tényleges értékek ingadozása nő, ezért a MAE minden modellnél nő. A MASE nevezője, a training átlagos napi változása is nő, mert a zaj dominál; így a hányados kevésbé változik. A zaj elnyomja a heti struktúrát, ezért a Holt–Winters és a naiv közötti MASE-különbség csökkenhet; a modell előnye kevésbé lesz megbízható.

20.3.8 Gyakori tévedés ellenpéldával

A leggyakoribb hiba, hogy a bonyolultabb modellt automatikusan jobbnak tekintjük a naiv baseline-nál. Ellenpélda: ha a generáló képletből elhagyjuk a szezonális tagot, tehát season = 0.0, akkor a heti ciklus megszűnik, és a szezonális naiv az utolsó hét zajos értékeit ismétli; a 7 napos teszt MAE-je várhatóan meghaladja a naiv modellét, a Holt–Winters pedig a becsült szezonális tagokkal ugyancsak túlilleszthet. Emlékeztető a korábbi fejezetekből: ahogyan Időhelyes feature és gördülő visszateszt alfejezetben a jövőinformáció kizárása volt a feltétel, itt is minden ablakban csak az origó előtti adat használható; a többszöri modellválasztás pedig a Power, mintanagyság és többszörös próbák alfejezetben tárgyalt peekinghez hasonló torzítást vihet az értékelésbe, ha nincs előre rögzített döntési szabály.

Önellenőrzés: a naiv prediktor minden eleme egyenlő; a forecast hossza egyenlő a horizonttal; a MASE nevező pozitív; a modellillesztés soha nem kapja meg a tesztablakot.

20.3.9 Rövid összefoglaló

A naiv modell a mai értéket ismétli, a szezonális naiv a múlt heti mintát, az exponenciális simítás pedig simított szintből és szezonális tagokból készít 77 napos előrejelzést. A több tesztablakos értékelésben a MAE rendelésszámban méri az abszolút hibát, a MASE a naiv baseline-hoz viszonyít: 11 alatt javulás, 11 felett romlás. A reziduális autokorreláció diagnosztika; ha jelentős, a modell struktúrát hagyott ki. Az ARIMA keretrendszer AR, MA és differencia tagokkal általánosít, de a bonyolultabb modell nem garantál javulást. A következő alkalmazás az V. ellenőrző pont, ahol ezt a baseline- és hibakeretet teljes modellértékelési állításrendszerré kell összekötni.