Lag-tunnused: eilne ilm kui sisend homsele

Ajarea ennustamisel ei ole ĂŒks pĂ€ev iseseisev nĂ€ide. Lag-tunnus tĂ€hendab, et anname mudelile sisendiks möödunud pĂ€evade vÀÀrtused. Lohista allpool pĂ€eva ja muuda lagide arvu ning vaata, milline treeningrida tekib. Andmed: Stockholmi pĂ€eva keskmine temperatuur 2000–2009.

sisend: pĂ€evad t, t−1, 
, t−k+1 sihtmuutuja: pĂ€ev t+1 mudel ei nĂ€e

Sellest aknast tekib ĂŒks treeningrida

Miks ĂŒldse nihutada?

Tavalises tabelis on iga rida eraldi nÀide ja mudel Ôpib seost sama rea veergude vahel. Kui tahame ennustada homset temperatuuri, peab samal real olema kaks asja: see, mida me tÀna teame (tunnused), ja see, mis juhtub homme (sihtmuutuja).

Pandas teeb selle shift() abil. shift(1) lĂŒkkab veergu ĂŒhe rea vĂ”rra allapoole, nii et iga rea juurde jĂ”uab eelmise pĂ€eva vÀÀrtus. shift(-1) lĂŒkkab ĂŒlespoole ja toob jĂ€rgmise pĂ€eva vÀÀrtuse. Esimest kasutame tunnuste, teist sihtmuutuja jaoks.

Iga uus lag lisab tunnuste komplekti veel ĂŒhe pĂ€eva tagasi. Kui algul oli 94 tunnust ja vĂ”tame 10 lagi, on sisendis 940 veergu.

Kuhu suunas shift nihutab

Oletame, et andmestikus on ainult need 12 pÀeva. Vali suund ja vii kursor (vÔi fookus) mÔnele lahtrile: tume raam nÀitab, kust temperatuuri veerust see vÀÀrtus pÀrit on.

tunnus, teada pĂ€eva lĂ”puks sihtmuutuja (homne) tunnus tulevikust — leke NaN

Mitu lagi vÔtta?

Rohkem lage ei tĂ€henda automaatselt paremat mudelit. Vasakul on nĂ€ha, kui tugevalt korreleerub homne Stockholmi temperatuur sama vĂ”i naaberlinna temperatuuriga k pĂ€eva tagasi. Korrelatsioon langeb aeglaselt, kuid see on suuresti aastaaegade mĂ”ju: talvel on kĂŒlm nii tĂ€na kui ka kahe nĂ€dala pĂ€rast. Uut infot lisab iga jĂ€rgmine pĂ€ev aga ĂŒha vĂ€hem.

Paremal on lineaarse mudeli (Ridge) viga testandmetel, kui sisendiks on kĂ”igi 18 linna keskmine temperatuur ja k lagi. Parim tulemus tuleb 2–3 lagiga, edasi lisanduvad peamiselt mĂŒra ja veerud.

Korrelatsioon homse Stockholmiga

Testviga (MAE, °C) lagide arvu jÀrgi

Kriipsjoon: naiivne prognoos „homme on sama mis tĂ€na“.

Andmeleke: kui mudel nÀeb vastust ette

Andmeleke tekib siis, kui mudel saab treenimisel vĂ”i testimisel infot, mida pĂ€ris ennustamise hetkel veel ei ole. Testandmetel paistab selline mudel suurepĂ€rane, pĂ€riselus on ta kasutu. Ajaridades tekib leke eriti kergesti, sest iga rida on seotud naaberpĂ€evadega ja ĂŒks vale mĂ€rk koodis toob tuleviku sisendisse.

Kuldne kĂŒsimus iga tunnuse kohta: kas see vÀÀrtus on teada hetkel, mil prognoos tehakse? Kui ennustame homset, vĂ”ib kasutada kĂ”ike, mis on teada tĂ€nase pĂ€eva lĂ”puks, ja mitte midagi rohkemat.

TĂŒĂŒpilised lekked ajaridades

1. Vale nihke suund

Tunnusele tehakse shift(-i), nii et reale jÔuavad hilisemate pÀevade vÀÀrtused. Veerunimi (_lag1, _eile) ei muuda midagi, loeb ainult mÀrk.

# vale: tunnus tulevikust
X.shift(-i)
# Ôige: tunnus minevikust
X.shift(i)

Reegel: miinusega nihe ainult sihtmuutujale.

2. SihtpÀeva mÔÔtmised sisendis

Kui ennustame pÀeva keskmist temperatuuri, on sama pÀeva maksimum ja miinimum sisuliselt vastus. Need tohivad sisendis olla ainult varasematest pÀevadest.

# sihtmuutuja: temp_mean pÀeval t+1
# vale: temp_max pÀevast t+1
# Ă”ige: temp_max pĂ€evadest t, t−1, 


3. Segatud jagamine

train_test_split segab vaikimisi read. Siis on treeningus pÀevi, mis on testpÀevadest hilisemad, ja mudel Ôpib tulevikust. Sama kehtib tavalise KFold-i kohta.

# vale
train_test_split(X, y, test_size=0.3)
# Ôige
train_test_split(X, y, test_size=0.3, shuffle=False)
TimeSeriesSplit(n_splits=5)

4. Statistika kogu andmetelt

Skaleerimine, tunnuste valik korrelatsiooni jÀrgi vÔi puuduvate vÀÀrtuste tÀitmine keskmisega kasutavad testperioodi infot, kui need tehakse enne jagamist.

# vale
scaler.fit_transform(X)        # enne jagamist
# Ôige
scaler.fit(X_train)
X_train_s = scaler.transform(X_train)
X_test_s  = scaler.transform(X_test)

5. Tulevikku vaatavad aknad ja tÀitmine

MÔned pandase kÀsud kasutavad hilisemaid ridu: keskel paiknev libisev aken, tagantpoolt tÀitmine ja interpoleerimine.

# vale: kasutavad hilisemaid pÀevi
s.rolling(7, center=True).mean()
s.bfill()
s.interpolate()
# Ôige: ainult minevik
s.rolling(7).mean()
s.ffill()

Kuidas leket Àra tunda

  • Tulemus on liiga hea: jĂ€rgmise pĂ€eva ilmaprognoosi RÂČ 0,99 on kahtlane.
  • VĂ”rdle naiivse prognoosiga „homme on sama mis tĂ€na“. Kui mudel vĂ”idab seda kordades, otsi leket.
  • Üks tunnus domineerib tunnuste olulisuses.
  • Vaata ĂŒht rida kĂ€sitsi: kas kĂ”ik tunnused on pĂ€eva t lĂ”puks teada?

Kas mudel on parem kui naiivne prognoos?

Ajarea mudelit tasub alati vÔrrelda kÔige lihtsama prognoosiga: homme on sama mis tÀna. Alloleval joonisel on kolm lekketa lÀhenemist samadel testandmetel.

940 tunnusega nÀrvivÔrk jÀÀb alla isegi naiivsele prognoosile, sest 2500 treeningrea kohta on tunnuseid liiga palju. VÀiksem ja lihtsam lag-komplekt vÔidab.

Õige kood

Tee tunnused ja sihtmuutuja ĂŒhte tabelisse enne dropna()-d. Siis kaovad NaN-iga read korraga mĂ”lemast ja read jÀÀvad omavahel joondatuks, ridu ei pea kĂ€sitsi lĂ”ikama.

lags = 3
cols = [c for c in df.columns if c.endswith("_temp_mean")]

# Tunnused: tĂ€na (lag0), eile (lag1), ĂŒleeile (lag2) — shift(+i) vaatab minevikku
X_lagged = pd.concat(
    [df[cols].shift(i).add_suffix(f"_lag{i}") for i in range(lags)],
    axis=1,
)

# Sihtmuutuja: homne temperatuur — shift(-1) vaatab tulevikku
data = X_lagged.assign(target=df["STOCKHOLM_temp_mean"].shift(-1)).dropna()

X_l, y_l = data.drop(columns="target"), data["target"]

# AjajÀrjestuses jagamine, ilma segamiseta
split = int(len(data) * 0.7)
X_train, X_test = X_l.iloc[:split], X_l.iloc[split:]
y_train, y_test = y_l.iloc[:split], y_l.iloc[split:]

Kontrolli alati ĂŒht rida kĂ€sitsi: vĂ”ta suvaline kuupĂ€ev, vaata, et _lag0 veerud on selle pĂ€eva vÀÀrtused, _lag1 eelmise pĂ€eva omad ja target jĂ€rgmise pĂ€eva temperatuur.

Kontrolli ennast