Paraméter optimalizálás — Keresztvalidáció és GridSearch technika idősorok esetén
Az idősorokban rejlő szabályrendszert (trend, szezonalitás, ciklusok, véletlen tényezők) egy matematikai modellel szeretnék megragadni. Ez a modell statisztikai keretrendszerében vizsgálódva nem képes oly módon tanulni, mint egy gépi tanulást megvalósító algoritmus. A gépi tanulás itt nagyon is emberi folyamat marad, tehát a szabályrendszer paramétereit nekünk kell megtalálni. Ez legalább annyira kreatív, mint matematikailag alátámasztható folyamat. Felismerünk egy mintát, amit képesek vagyunk extrapolálni, akár “kézzel” meghúzni az előrejelzést. Ugyanakkor a paraméterek megadása nagyon is egzakt kell hogy maradjon, amit ha elhibázunk végletesen rossz előrejelzést adhatunk. Nézzünk egy lehetséges példát a rossz paraméter választásra:
A béta értéket, tehát a trendet szabályzó paramétert állítsuk 0.7-re. Ezzel a megoldással a közelmúlt változásait extrém értékben felülsúlyozzuk. A kódsort futtatva azonnal látható, hogy a trend tökéletesen ellentétes irányt fog felvenni, mint az elvárt.
A megoldás egy, maximum két paraméter esetében lehet a szimpla próbálkozás. De mi a helyzet egy komplexebb adatsort leíró modell esetében, ahol 5–6 paraméter kombinációja adja a legjobb eredményt? Ehhez kísérleteinket automatizálnunk kell. Ugorjunk egy kicsit előre és az ES modell minden, statsmodels csomagban elérhető paraméterét élesítsük.
Alkalmazott modell megismerése
ES technika paraméterei a kódban kifejezve a következők. Fontos, mielőtt bármit állítanánk ismerjük meg annak működését, a várható hatásokat.
# Modell létrehozása
model = sm.tsa.ExponentialSmoothing(
df["passengers"],
trend='add',
seasonal='mul',
seasonal_periods=12,
damped_trend=False # Csillapított trend engedélyezése
)
# Modell illesztése
fitted_model = model.fit(
smoothing_level=0.187,
smoothing_trend=0.7,
smoothing_seasonal=0.108,
damping_slope=0.9,
remove_bias = True, # ha trend add és season mul
optimized=False
)
trend
Ez a paraméter meghatározza, hogy milyen típusú trendkomponens legyen a modellben.
seasonal
A seasonal paraméter meghatározza a szezonális komponens típusát. Lehet multiplikatív vagy additív.
seasonal_periods
Ez a paraméter állítja be, hogy hány időperiódusonként ismétlődik a szezonális minta.
damped_trend
A damped_trend engedélyezése vagy tiltása határozza meg, hogy a trendkomponens az idő előrehaladtával csillapodjon-e vagy sem. Ebben az esetben a False érték azt jelenti, hogy a trend nem csillapodik, hanem konzisztensen tovább nő vagy csökken.
smoothing_level (α)
A simítási szint, ami meghatározza az adatok aktuális értékének súlyát az előző előrejelzés frissítésekor.
smoothing_trend (β)
A simítási trend, ami szabályozza a trendkomponenst.
smoothing_seasonal (γ)
A simítási szezonális paraméter határozza meg, milyen mértékben frissül a szezonális komponens a korábbi szezonok alapján.
damping_slope (φ)
Ez a paraméter szabályozza, hogy mennyire csillapodjon a trend, ha a damped_trend engedélyezve van. Itt a 0.9 érték azt jelenti, hogy a trend csökkenő mértékben változik, ami enyhén csillapított trendet eredményez, bár a damped_trend=False esetében ez a paraméter nem aktív.
remove_bias
A torzítás eltávolítása (remove_bias=True) azt jelenti, hogy a modell megkísérli korrigálni a rendszeres hibákat az illeszkedés során. (additív trend és multiplikatív szezonalitás esetén hasznos)
Fogaljuk össze egy grafikonon azokat a tényezőket amiket változtathatunk a modellépítés és későbbi finomhangolás érdekében. Ezek gyűjteménye és értelmezési tartománya lesz a hangoló alkalmazásunk egyik fő összetevője, a paraméter háló, vagy parameter grid.
Most hogy elméletben megalkottunk egy könyvtárat, nézzük meg hogyan illeszkednek az optimalizáló gép további elemei:
- Adatok beolvasása
- Statisztikai modell induló paramétereinek inicializálása
- Paraméter háló (lehetséges értékek) beállítása
- Paraméterkombinációk vezérlése
- Keresztvalidáció — a kombinációk vizsgálata tanuló és teszt halmazokon
- Kiértékelés átlagolás módszertannal
- For ciklus futtatása amíg a legkedvezőbb érték előáll
- Kilépés a for ciklusból, minden kombináció tesztelése után; legjobb metrika és az azt előállító paraméter kombináció mentése
- Modell illesztése új paraméterekkel, predikció elkészítése
Vizsgáljuk meg a főbb elemeket különös tekintettel a ciklusban működő elemekre.
Keresztvalidáció
Az első eszköz ami szükséges egy automatizált kísérletsorozat elvégzésére a keresztvalidáció technikája. Alapesetben egyszerű validáció során adatainkat két részre osztjuk, egy tanuló és egy teszt halmazra. A tanuló halmazon végezzük a modell hangolását, a teszt halmazon pedig megtörténik a kiértékelés. Megfelelő mennyiségű adathalmaz esetén, a teszt halmazt tovább oszthatjuk két részre. Ennek a tovább osztott halmaznak az egyik része fog szolgálni a betanított modell hiperparamétereinek finomhangolására, a fennmaradó pedig a végső tesztelésre. Hátránya a technikának, hogy csak a szerencsén múlik milyen a két halmaz összetétele. Ez a módszer félreviheti az elemzést és túlilleszthetjük a modellt.
Ennek a megoldásnak az eggyel hatékonyabb változata, amikor a két halmaz véletlenszerűen kerül kiválasztásra. Ekkor az adatok sokkal heterogénebbek, egyenletesebben oszlanak el a vizsgálat alá vont adatpontok.
Ennél még hatékonyabb technika, a keresztvalidáció. Alapgondolata, hogy vegyünk több kísérletet, ahol a teszt és tréning halmazra bontást ismételjük. Az ábrán egy hat-hajtásos keresztvalidáció történik. Hat kísérletet és kiértékelést végzünk. Minden kísérletben a teljes halmaz egyhatoda validációs/kiértékelő csoport, míg a fennmaradó nagyobb halmaz a modell betanítására szolgál. Amint ez megtörténik, kiértékeljük a modell eredményét a validációs halmazokon, majd vesszük a választott hibametrikánk átlagát. Ezzel megkaptuk a modell teljesítményét.
További információt szerezhetünk, ha minden iterációban a teszt és validációs halmaz eredményét összehasonlítjuk. Ideális esetben ezeknek közel kell esniük egymáshoz.
Idősorok esetén keresztvalidáció alkalmazásakor óvatosan kell eljárni. Nem boríthatjuk fel az időbeli sorrendet, tehát a tanuló halmaz indexeinek mindig kisebbnek kell lenniük, mint a validációsnak. Ezt mutatja be a következő ábra:
Ezt a funkciót az sklearn gépi tanulási csomag, TimeSeriesSplit osztályával fogjuk elérni, mely képes ezt, az idősorokra vonatkozó megszorítást biztosítani.
Hibametrika
A tesztelési technikán túl szükségünk lesz egy mérési célra (KPI), vagyis egy hibamaterikára amivel a különböző paraméterkombinációk összehasonlíthatóvá vállnak. Ez a hibametrika fog működni a paraméterkombináció keresztvalidálásakor, illetve eggyel magasabb szinten, mikor kiválasztjuk a legjobb átlagos eredményt adó kombinációt is.
A példában mean_absolut_error mutatót fogjuk használni az optimalizáció során.
Bővebben a metrikákról egy korábbi bejegyzésemben:
Kombinációk keresése — Grid és Random Search
A gépi tanulás világából vett technika jól használható statisztikai idősorok esetében is. Mindkét módszer jól működik, de különböző megközelítéseket alkalmaznak a legjobb paraméterkombinációk megtalálására, és más-más helyzetekben lehet előnyös az alkalmazásuk.
A Grid Search, vagy rácskeresés egy alapos brute force jellegű módszer, amely minden lehetséges kombinációt kipróbál a megadott hiperparaméter-értéktartományokon belül. Egy előre definiált “rácsot” hoz létre a paraméterértékek minden lehetséges kombinációjával, és kiszámítja a modell teljesítményét minden egyes kombináció esetében.
Ha a két paraméterrel dolgozunk, egy 2D-ben kifeszített rácsot tudunk elképzelni. Három paraméter esetén már ennek megfelelő dimenziószámban kell megtalálni az optimumot. Ennél magasabb paraméterszám esetén tovább kell növelnünk a dimenziókat, amit matematikailag az sklearn GridSearch funkciója képes megvalósítani. A lenti ábrán egy döntési fa 3 dimenziós paraméterrácsa látszik:
Előnyök:
- Alapos: minden lehetséges kombinációt értékel.
- Egyszerű implementáció és használat.
- Garantáltan megtalálja a rácsban definiált legjobb kombinációt.
Hátrányok:
- Időigényes és számítási kapacitást igényel, különösen nagy paramétertartományok és/vagy sok paraméter esetén.
- Nem skálázódik jól nagy adatkészletekkel vagy sok hiperparaméterrel.
A Random Search, vagy véletlenszerű keresés, véletlenszerűen választ ki kombinációkat a paraméterek lehetséges értéktartományából egy előre meghatározott számú iterációra. Nem vizsgálja meg minden lehetséges kombinációt, hanem véletlenszerű mintavételezést alkalmaz.
Előnyök:
- Gyorsabb, mint a Grid Search, különösen nagy paramétertartományok és sok hiperparaméter esetén.
- Jobban skálázódik nagy adatkészletekre és bonyolultabb modellekre.
- Meglepően jó eredményeket hozhat, mivel nem korlátozódik egy szabályos rácsra, és így kiszélesítheti a keresési területet.
Hátrányok:
- Nincs garantált fedettség, mint a Grid Search-nél, így lehetséges, hogy nem találja meg az optimális kombinációt.
- Az eredmények kiszámíthatatlanabbak lehetnek, mivel nagyban függenek a véletlen mintavételezéstől.
Nézzük a résztechnikákat elmélet és gyakorlat együtt:
ES + ParameterGrid
Mivel a sklearn és a statsmodel ExponentialSmoothing modellje külön csomag, az egyszerű ParameterGrid osztály kerül meghívásra. most a TimeSeriesSplit biztosítja a keresztvalidációs eljárást idősorok esetére. A kód, az elméleti bevezetőben meghatározottak szerint működik. Fontos: A túlillesztés elkerülése érdekében a lehetséges paraméter változókat tudatosan kontrolláljuk. Pl.: trend esetén nem érdemes 0.5 fölé engedni beállítási lehetőséget, mivel ilyenkor a régmúlt adatainak már szinte semmi hatása nem lesz a predikcióra.
import numpy as np
import pandas as pd
import statsmodels.api as sm
import matplotlib.pyplot as plt
from sklearn.model_selection import ParameterGrid, TimeSeriesSplit
def cross_validate_es_with_mae(data, param_grid, n_splits=5):
best_score = float('inf')
best_params = None
# TimeSeriesSplit inicializálása
tscv = TimeSeriesSplit(n_splits=n_splits)
for params in ParameterGrid(param_grid):
mae_scores = []
for train_index, test_index in tscv.split(data):
train, test = data.iloc[train_index], data.iloc[test_index]
# Modell létrehozása a kiválasztott paraméterekkel
model = sm.tsa.ExponentialSmoothing(
train['passengers'],
trend=params['trend'],
seasonal=params['seasonal'],
seasonal_periods=params['seasonal_periods'],
damped_trend=params['damped_trend']
).fit(
smoothing_level=params['smoothing_level'],
smoothing_trend=params['smoothing_trend'],
smoothing_seasonal=params['smoothing_seasonal'],
damping_slope=params['damping_slope'],
optimized=False
)
# Predikciók készítése a teszthalmazra
predictions = model.forecast(len(test))
# MAE számítása a teszthalmazra
mae = np.mean(np.abs(predictions - test['passengers']))
mae_scores.append(mae)
# Átlagos MAE számítása a különböző felosztásokon
avg_mae = np.mean(mae_scores)
# Legjobb paraméterek és pontszám frissítése, ha szükséges
if avg_mae < best_score:
best_score = avg_mae
best_params = params
return best_params, best_score
# Paraméter rács és adatok megadása
param_grid = {
'smoothing_level': np.arange(0.1, 0.6, 0.1),
'smoothing_trend': np.arange(0.1, 0.6, 0.1),
'smoothing_seasonal': np.arange(0.1, 0.6, 0.1),
'damping_slope': np.arange(0.1, 0.6, 0.1),
'trend': ['add', 'mul'],
'seasonal': ['add', 'mul'],
'seasonal_periods': [12],
'damped_trend': [True, False]
}
# Optimalizálás és eredmények az MAE alapján
best_params, best_mae = cross_validate_es_with_mae(df, param_grid, n_splits=5)
print("Best Params:", best_params)
print("Best MAE:", best_mae)
Futtatás eredménye, a javasolt paraméterekkel, és az ezzel elérhető legjobb MAE átlaggal.
Best Params: {'damped_trend': False, 'damping_slope': 0.1, 'seasonal': 'mul', 'seasonal_periods': 12, 'smoothing_level': 0.30000000000000004, 'smoothing_seasonal': 0.30000000000000004, 'smoothing_trend': 0.30000000000000004, 'trend': 'add'}
Best MAE: 17.039069809793027
ES + Random Search
Mivel az sklearn nem tartalmazza a statsamodel ES modelljét, randomizált kereséshez komoly átalakításra van szükség. Az alábbi kód a fentiek AI által átalakított verziója:
import numpy as np
import pandas as pd
from sklearn.base import BaseEstimator, RegressorMixin
from sklearn.model_selection import TimeSeriesSplit, RandomizedSearchCV
from sklearn.metrics import make_scorer, mean_absolute_error
import statsmodels.api as sm
from scipy.stats import uniform
class ExponentialSmoothingAdapter(BaseEstimator, RegressorMixin):
def __init__(self, trend=None, seasonal=None, seasonal_periods=None, damped_trend=False,
smoothing_level=None, smoothing_trend=None, smoothing_seasonal=None, damping_slope=None):
self.trend = trend
self.seasonal = seasonal
self.seasonal_periods = seasonal_periods
self.damped_trend = damped_trend
self.smoothing_level = smoothing_level
self.smoothing_trend = smoothing_trend
self.smoothing_seasonal = smoothing_seasonal
self.damping_slope = damping_slope
self.model_ = None
def fit(self, X, y):
self.model_ = sm.tsa.ExponentialSmoothing(
y,
trend=self.trend,
seasonal=self.seasonal,
seasonal_periods=self.seasonal_periods,
damped_trend=self.damped_trend
).fit(
smoothing_level=self.smoothing_level,
smoothing_trend=self.smoothing_trend,
smoothing_seasonal=self.smoothing_seasonal,
damping_slope=self.damping_slope,
optimized=False
)
return self
def predict(self, X):
return self.model_.forecast(len(X))
# Paraméter rács
param_dist = {
'smoothing_level': uniform(0.1, 0.5),
'smoothing_trend': uniform(0.1, 0.5),
'smoothing_seasonal': uniform(0.1, 0.5),
'damping_slope': uniform(0.1, 0.5),
'trend': ['add', 'mul'],
'seasonal': ['add', 'mul'],
'seasonal_periods': [12],
'damped_trend': [True, False]
}
# Adatok és RandomizedSearchCV
data = df
X = np.arange(len(data))
y = data['passengers']
tscv = TimeSeriesSplit(n_splits=5)
random_search = RandomizedSearchCV(
estimator=ExponentialSmoothingAdapter(),
param_distributions=param_dist,
n_iter=100, # A véletlenszerű kiválasztások száma
cv=tscv,
scoring='neg_mean_absolute_error',
n_jobs=-1,
verbose=1,
random_state=30 # Véletlenszám-generátor állapota a reprodukálhatóság érdekében
)
random_search.fit(X, y)
print("Best Params:", random_search.best_params_)
print("Best Score:", -random_search.best_score_) # A negatív MAE-t pozitívra konvertáljuk
És az eredmény.
Fitting 5 folds for each of 100 candidates, totalling 500 fits
Best Params: {'damped_trend': False, 'damping_slope': 0.33889487606768864, 'seasonal': 'mul', 'seasonal_periods': 12, 'smoothing_level': 0.29834624219235306, 'smoothing_seasonal': 0.13490199168699377, 'smoothing_trend': 0.28913128423438483, 'trend': 'mul'}
Best Score: 19.647149009454225
Végezetül futtassuk a modellt, a javasolt paraméterek alapján. Az illesztett és predikált görbe futása egyértelműen jelzi hogy jó irányba haladunk.
Zárszó
A paraméter optimalizáció különösen az ML modellek egyik legfontosabb, de előadások során kicsit elhanyagolt része. Látható, hogy komolyabb átalakításokkal, de statisztikai modellek esetén is használható a logika és az azt megvalósító sklearn ökoszisztéma, bár kétségtelen egy ML modell esetén kivitelezése egyszerűbb.
Boldog optimalizálást mindenkinek :)
Kapcsolat
🚀Ha értékesnek találod a leírtakat, és szükségét látod egy hasonló elemzés elkészítésének, vagy adatelemzői munkakörbe keresel hosszútávon szakembert, kérlek keress meg alábbi Linkedin profilon:
(24) Zoltán Mészáros | LinkedIn