Python ile Zaman Serisi Tahminleme: ARIMA'dan Transformer Modellerine Kapsamlı Rehber

Python'da zaman serisi tahminlemeyi ARIMA'dan Transformer modellerine kadar adım adım öğrenin. StatsForecast, NeuralForecast, Prophet ve daha fazlasıyla pratik kod örnekleri içeren kapsamlı rehber.

Python ile Zaman Serisi Tahminleme: ARIMA'dan Transformer Modellerine

Zaman serisi tahminleme, geçmiş verilere bakıp gelecekteki değerleri öngörmeye çalışma işidir — ve açıkçası, veri biliminin en heyecan verici alanlarından biri. Finans sektöründen üretim planlamasına, enerji yönetiminden perakende satışlara kadar hemen her sektörde karşımıza çıkıyor. Python ekosistemi de bu alanda çalışanlara gerçekten zengin bir araç seti sunuyor.

Bu rehberde klasik istatistiksel yöntemlerden en güncel Transformer tabanlı derin öğrenme modellerine kadar geniş bir yelpazede zaman serisi tahminlemeyi ele alacağız. Haydi başlayalım.

1. Zaman Serisi Nedir ve Neden Önemlidir?

Zaman serisi, belirli zaman aralıklarında sırayla kaydedilen gözlemler bütünüdür. Günlük hisse senedi fiyatları, aylık satış rakamları, saatlik hava sıcaklıkları — bunların hepsi birer zaman serisi. Kulağa basit gelebilir ama iş tahminlemeye gelince, işler karmaşıklaşıyor.

Zaman serisi tahminlemenin önemini şu alanlarda net bir şekilde görebilirsiniz:

  • Finans: Hisse senedi ve döviz kurlarının tahminlenmesi, risk yönetimi
  • Perakende: Talep tahmini, stok optimizasyonu, sezonsal planlama
  • Enerji: Elektrik tüketimi ve üretim tahminleri, şebeke yönetimi
  • Sağlık: Hasta sayısı tahmini, ilaç tedarik planlaması
  • Üretim: Makine arızası tahmini (predictive maintenance), kalite kontrolü
  • İklim Bilimi: Hava durumu ve iklim değişikliği projeksiyonları

Geleneksel makine öğrenmesi yöntemleri veri noktaları arasındaki zamansal bağımlılıkları genelde göz ardı eder. Zaman serisi modelleri ise bu ardışık yapıyı doğrudan modeller. Dolayısıyla doğru model seçimi, tahmin başarısını büyük ölçüde etkiler.

2. Zaman Serisi Verisinin Temel Bileşenleri

Herhangi bir zaman serisi modelini uygulamadan önce verinin yapısını anlamak şart. Bir zaman serisi genellikle dört temel bileşenden oluşur.

2.1 Trend (Eğilim)

Verinin uzun vadeli artış ya da azalış yönelimini ifade eder. Mesela bir şirketin yıllar içinde büyüyen satış rakamları pozitif trende, giderek düşen bir ürünün satışları ise negatif trende örnektir.

2.2 Mevsimsellik (Seasonality)

Belirli periyotlarda tekrarlayan düzenli kalıplardır. Yaz aylarında artan dondurma satışları, yılbaşı döneminde yükselen e-ticaret trafiği — bunlar mevsimselliğin tipik örnekleri. Mevsimsellik günlük, haftalık, aylık veya yıllık döngüler şeklinde ortaya çıkabilir.

2.3 Durağanlık (Stationarity)

Bir zaman serisinin ortalama, varyans ve otokovaryansının zaman içinde değişmemesi durumudur. ARIMA gibi klasik modeller durağan veri üzerinde çalışır; bu nedenle durağan olmayan serileri önce fark alma (differencing) veya logaritmik dönüşüm gibi yöntemlerle durağanlaştırmamız gerekir.

Durağanlık kontrolü için en yaygın yöntem Augmented Dickey-Fuller (ADF) testidir:

import pandas as pd
import numpy as np
from statsmodels.tsa.stattools import adfuller
import matplotlib.pyplot as plt

# Örnek zaman serisi verisi oluşturma
np.random.seed(42)
dates = pd.date_range(start='2020-01-01', periods=365, freq='D')
trend = np.linspace(0, 10, 365)
seasonality = 5 * np.sin(2 * np.pi * np.arange(365) / 365)
noise = np.random.normal(0, 1, 365)
ts = pd.Series(trend + seasonality + noise, index=dates)

# ADF Testi ile durağanlık kontrolü
def adf_test(series, alpha=0.05):
    result = adfuller(series, autolag='AIC')
    print(f'ADF İstatistiği: {result[0]:.4f}')
    print(f'p-değeri: {result[1]:.4f}')
    print(f'Kritik Değerler:')
    for key, value in result[4].items():
        print(f'   {key}: {value:.4f}')
    if result[1] <= alpha:
        print("Seri DURAĞAN (H0 reddedildi)")
    else:
        print("Seri DURAĞAN DEĞİL (H0 reddedilemedi)")

print("Orijinal Seri için ADF Testi:")
adf_test(ts)

# Fark alma ile durağanlaştırma
ts_diff = ts.diff().dropna()
print("\nFark Alınmış Seri için ADF Testi:")
adf_test(ts_diff)

2.4 Veri Ayrıştırma (Decomposition)

Zaman serisini bileşenlerine ayırmak, verinin yapısını görsel olarak anlamak açısından oldukça faydalı. Ben projelerde ilk iş olarak bunu yapıyorum — neyle uğraştığınızı görmek her şeyi kolaylaştırır.

from statsmodels.tsa.seasonal import seasonal_decompose

# Zaman serisini bileşenlerine ayırma
decomposition = seasonal_decompose(ts, model='additive', period=30)

fig, axes = plt.subplots(4, 1, figsize=(12, 10))
decomposition.observed.plot(ax=axes[0], title='Gözlemlenen')
decomposition.trend.plot(ax=axes[1], title='Trend')
decomposition.seasonal.plot(ax=axes[2], title='Mevsimsellik')
decomposition.resid.plot(ax=axes[3], title='Artık (Residual)')
plt.tight_layout()
plt.savefig('decomposition.png', dpi=150)
plt.show()

3. Klasik İstatistiksel Yöntemler: ARIMA ve SARIMA

ARIMA (AutoRegressive Integrated Moving Average), zaman serisi tahminlemesinin adeta temel taşı. Üç parametresi var: p (otoregresif terim sayısı), d (fark alma derecesi) ve q (hareketli ortalama terimi sayısı). Kavramsal olarak biraz korkutucu görünebilir ama pratikte oldukça sezgisel bir model.

3.1 statsmodels ile ARIMA Uygulaması

import pandas as pd
import numpy as np
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
import warnings
warnings.filterwarnings('ignore')

# Gerçekçi satış verisi simülasyonu
np.random.seed(42)
n = 200
dates = pd.date_range(start='2022-01-01', periods=n, freq='W')
sales = (200 + np.cumsum(np.random.randn(n) * 5)
         + 30 * np.sin(2 * np.pi * np.arange(n) / 52))
sales = pd.Series(np.abs(sales), index=dates, name='Satislar')

# ACF ve PACF grafikleri ile p, q parametrelerini belirleme
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
plot_acf(sales.diff().dropna(), lags=40, ax=axes[0],
         title='Otokorelasyon Fonksiyonu (ACF)')
plot_pacf(sales.diff().dropna(), lags=40, ax=axes[1],
          title='Kısmi Otokorelasyon Fonksiyonu (PACF)')
plt.tight_layout()
plt.show()

# ARIMA(2,1,2) modeli
train = sales[:-12]
test = sales[-12:]

model = ARIMA(train, order=(2, 1, 2))
fitted_model = model.fit()
print(fitted_model.summary())

# Tahmin yapma
forecast = fitted_model.get_forecast(steps=12)
forecast_mean = forecast.predicted_mean
conf_int = forecast.conf_int()

# Sonuçları görselleştirme
plt.figure(figsize=(14, 6))
plt.plot(train[-52:], label='Eğitim Verisi', color='blue')
plt.plot(test, label='Gerçek Değerler', color='green')
plt.plot(forecast_mean, label='ARIMA Tahmini', color='red', linestyle='--')
plt.fill_between(conf_int.index,
                 conf_int.iloc[:, 0],
                 conf_int.iloc[:, 1],
                 alpha=0.3, color='red', label='%95 Güven Aralığı')
plt.title('ARIMA(2,1,2) ile Satış Tahmini')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

3.2 Mevsimsel ARIMA (SARIMA)

SARIMA, ARIMA'ya mevsimsel bileşenler (P, D, Q, m) ekler. Eğer verilerinizde yıllık veya dönemsel kalıplar varsa — ki çoğu iş verisinde vardır — SARIMA ilk denemeniz gereken modellerden biri olmalı.

from statsmodels.tsa.statespace.sarimax import SARIMAX

# SARIMA(1,1,1)(1,1,1,52) - Haftalık mevsimsellik
sarima_model = SARIMAX(train,
                       order=(1, 1, 1),
                       seasonal_order=(1, 1, 1, 52),
                       enforce_stationarity=False,
                       enforce_invertibility=False)
sarima_result = sarima_model.fit(disp=False)
print(sarima_result.summary())

sarima_forecast = sarima_result.get_forecast(steps=12)
sarima_pred = sarima_forecast.predicted_mean
sarima_ci = sarima_forecast.conf_int()

3.3 pmdarima ile Auto-ARIMA

p, d, q parametrelerini elle bulmak bazen zahmetli olabiliyor. İşte burada pmdarima devreye giriyor. AIC/BIC kriterleri kullanarak farklı parametre kombinasyonlarını otomatik test eder ve en iyi modeli seçer. Özellikle hızlı prototipleme yaparken çok işe yarıyor.

# pip install pmdarima
import pmdarima as pm

# Auto-ARIMA ile otomatik parametre seçimi
auto_model = pm.auto_arima(
    train,
    start_p=0, start_q=0,
    max_p=5, max_q=5,
    m=52,                    # Mevsimsel periyot (haftalık)
    start_P=0, start_Q=0,
    max_P=2, max_Q=2,
    seasonal=True,
    d=None,                  # Otomatik fark derecesi tespiti
    D=None,                  # Otomatik mevsimsel fark derecesi
    test='adf',              # Durağanlık testi
    information_criterion='aic',
    trace=True,              # Model seçim sürecini göster
    error_action='ignore',
    suppress_warnings=True,
    stepwise=True            # Hızlı stepwise arama
)

print(f"Seçilen Model: ARIMA{auto_model.order}x{auto_model.seasonal_order}")
print(auto_model.summary())

# Tahmin
auto_forecast = auto_model.predict(n_periods=12)

4. Facebook/Meta Prophet Modeli

Prophet, Meta (eski adıyla Facebook) tarafından geliştirilen ve iş verilerine özel tasarlanmış güçlü bir zaman serisi tahmin kütüphanesi. Trend değişim noktaları, mevsimsellik ve tatil etkilerini otomatik olarak modelliyor. Açıkçası, veri bilimcisi olmayan kullanıcılar için bile oldukça erişilebilir bir arayüze sahip — onu bu kadar popüler yapan da biraz bu.

4.1 Prophet'in Temel Özellikleri

  • Kayıp veri ve aykırı değerlere karşı dirençli
  • Otomatik trend değişim noktası tespiti (changepoint detection)
  • Özelleştirilebilir mevsimsellik: günlük, haftalık, yıllık
  • Tatil ve özel gün etkilerinin modellenmesi
  • Bayesian belirsizlik aralıkları
# pip install prophet
from prophet import Prophet
import pandas as pd
import numpy as np

# Prophet için veri hazırlama (ds ve y sütunları zorunlu)
np.random.seed(42)
n = 365 * 3
dates = pd.date_range(start='2021-01-01', periods=n, freq='D')
y_values = (1000
            + np.cumsum(np.random.randn(n) * 10)
            + 200 * np.sin(2 * np.pi * np.arange(n) / 365)
            + 50 * np.sin(2 * np.pi * np.arange(n) / 7))

df_prophet = pd.DataFrame({'ds': dates, 'y': np.abs(y_values)})

# Eğitim ve test setlerine bölme
train_prophet = df_prophet[:-90]
test_prophet = df_prophet[-90:]

# Prophet modeli oluşturma ve eğitme
model_prophet = Prophet(
    changepoint_prior_scale=0.05,  # Trend esnekliği
    seasonality_prior_scale=10,    # Mevsimsellik kuvveti
    holidays_prior_scale=10,       # Tatil etkisi kuvveti
    seasonality_mode='multiplicative',  # Çarpımsal mevsimsellik
    yearly_seasonality=True,
    weekly_seasonality=True,
    daily_seasonality=False
)

# Türkiye'deki ulusal tatilleri ekleme
model_prophet.add_country_holidays(country_name='TR')
model_prophet.fit(train_prophet)

# Gelecek dönemler için tahmin
future = model_prophet.make_future_dataframe(periods=90, freq='D')
forecast_prophet = model_prophet.predict(future)

# Bileşenleri görselleştirme
fig1 = model_prophet.plot(forecast_prophet)
fig1.suptitle('Prophet Tahmini', fontsize=14)

fig2 = model_prophet.plot_components(forecast_prophet)
plt.tight_layout()
plt.show()

# Tahmin sonuçlarını inceleme
print(forecast_prophet[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail(10))

5. Nixtla StatsForecast ile Modern İstatistiksel Yöntemler

StatsForecast, Nixtla tarafından geliştirilen ve istatistiksel zaman serisi modellerini son derece hızlı çalıştırmak için tasarlanmış bir kütüphane. Numba JIT derleme kullanarak Python'da C hızına ulaşıyor ve milyonlarca zaman serisini paralel olarak işleyebiliyor. Kurumsal ölçekli uygulamalar için gerçekten ideal bir araç.

5.1 StatsForecast'in Temel Avantajları

  • Numba tabanlı hız: Vanilla Python'a kıyasla 20-300x daha hızlı çalışır
  • Ölçeklenebilirlik: Milyonlarca zaman serisini paralel işleyebilir
  • Kapsamlı model koleksiyonu: AutoARIMA, ETS, CES, Theta, MSTL ve daha fazlası
  • Dask/Ray entegrasyonu: Dağıtık hesaplama desteği
  • Olasılıksal tahminler: Tahmin aralıkları otomatik olarak hesaplanır
# pip install statsforecast
from statsforecast import StatsForecast
from statsforecast.models import (
    AutoARIMA,
    ETS,
    CES,          # Complex Exponential Smoothing
    AutoTheta,    # Theta yöntemi
    SeasonalNaive,
)
import pandas as pd
import numpy as np

# StatsForecast için uzun format veri hazırlama
np.random.seed(42)
n_series = 5  # 5 farklı ürün/mağaza serisi
n_periods = 104  # 2 yıl haftalık veri

series_list = []
for i in range(n_series):
    dates = pd.date_range(start='2022-01-01', periods=n_periods, freq='W')
    trend = np.linspace(100 * (i + 1), 150 * (i + 1), n_periods)
    seasonal = 20 * np.sin(2 * np.pi * np.arange(n_periods) / 52)
    noise = np.random.randn(n_periods) * 10
    y = trend + seasonal + noise

    df_series = pd.DataFrame({
        'unique_id': f'urun_{i+1}',
        'ds': dates,
        'y': np.abs(y)
    })
    series_list.append(df_series)

df_long = pd.concat(series_list, ignore_index=True)

# StatsForecast nesnesini oluşturma
sf = StatsForecast(
    models=[
        AutoARIMA(season_length=52),     # Otomatik ARIMA
        ETS(season_length=52),           # Üstel Düzeltme
        CES(season_length=52),           # Karmaşık Üstel Düzeltme
        AutoTheta(season_length=52),     # Theta yöntemi
        SeasonalNaive(season_length=52), # Baseline: Mevsimsel Naive
    ],
    freq='W',
    n_jobs=-1  # Tüm CPU çekirdeklerini kullan
)

# Modelleri eğitme ve tahmin yapma
horizon = 12  # 12 hafta ileri tahmin
forecasts = sf.forecast(df=df_long, h=horizon, fitted=True)
print(forecasts.head(20))

# Çapraz doğrulama (Cross-Validation)
cv_results = sf.cross_validation(
    df=df_long,
    h=horizon,
    n_windows=3,  # 3 farklı test penceresi
    step_size=horizon
)
print("\nÇapraz Doğrulama Sonuçları:")
print(cv_results.head())

5.2 Büyük Ölçekli Tahminleme

StatsForecast'in asıl gücü büyük ölçekli veri setlerinde ortaya çıkıyor. Binlerce, hatta on binlerce seriyi dakikalar içinde işleyebilmesi onu endüstriyel kullanım için vazgeçilmez kılıyor. Şu örneğe bir bakın:

# Büyük ölçekli simülasyon: 1000 ürün serisi
import time

n_large = 1000
n_periods = 52
large_series = []

for i in range(n_large):
    dates = pd.date_range(start='2023-01-01', periods=n_periods, freq='W')
    y = (np.random.uniform(50, 500) +
         np.cumsum(np.random.randn(n_periods) * 5) +
         np.random.uniform(10, 50) *
         np.sin(2 * np.pi * np.arange(n_periods) / 52))
    large_series.append(pd.DataFrame({
        'unique_id': f'seri_{i:04d}',
        'ds': dates,
        'y': np.abs(y)
    }))

df_large = pd.concat(large_series, ignore_index=True)
print(f"Toplam satır sayısı: {len(df_large):,}")

# Hız ölçümü
sf_fast = StatsForecast(
    models=[AutoARIMA(season_length=52), ETS(season_length=52)],
    freq='W',
    n_jobs=-1  # Paralel işlem
)

start = time.time()
forecasts_large = sf_fast.forecast(df=df_large, h=8)
elapsed = time.time() - start
print(f"{n_large} seri için tahmin süresi: {elapsed:.2f} saniye")
print(f"Seri başına ortalama süre: {elapsed/n_large*1000:.2f} ms")

6. Derin Öğrenme: NeuralForecast ile Modern Modeller

Şimdi işler gerçekten heyecanlı hale geliyor. NeuralForecast, yine Nixtla'nın derin öğrenme tabanlı zaman serisi tahmin kütüphanesi. 30'dan fazla model barındıran bu kütüphane; N-BEATS, NHITS, Temporal Fusion Transformer (TFT), PatchTST ve iTransformer gibi son teknoloji modelleri kullanıma hazır bir arayüzle sunuyor.

6.1 NeuralForecast'te Öne Çıkan Modeller

Model Tip Güçlü Yön En İyi Kullanım
N-BEATS MLP tabanlı Yorumlanabilir bileşen ayrıştırma Tek değişkenli uzun vadeli tahmin
NHITS MLP tabanlı Çok frekanslı örnekleme Uzun ufuklu tahmin, hızlı eğitim
TFT Attention tabanlı Statik ve dinamik kovaryatlar Çok değişkenli, yorumlanabilir tahmin
PatchTST Transformer Yerel-küresel bağlam yakalama Uzun bağlam pencereli tahmin
iTransformer Transformer Değişken boyutunda attention Çok değişkenli seri tahmini
TimesNet CNN tabanlı 2D yapıya dönüştürme Karmaşık dönemsellik örüntüleri
TimeMixer MLP-Mixer Çok ölçekli karıştırma Genel amaçlı tahmin

6.2 N-BEATS ve NHITS ile Uygulama

N-BEATS ve NHITS, MLP tabanlı olmaları sayesinde eğitimi nispeten hızlıdır ve çoğu senaryoda Transformer modellerine yakın performans gösterirler. Kendi projelerimde genellikle ilk derin öğrenme denememi bu ikisiyle yapıyorum.

# pip install neuralforecast
from neuralforecast import NeuralForecast
from neuralforecast.models import NBEATS, NHITS
from neuralforecast.losses.pytorch import MAE
import pandas as pd
import numpy as np

# NeuralForecast için veri hazırlama
np.random.seed(42)
n = 300
dates = pd.date_range(start='2021-01-01', periods=n, freq='D')

# Gerçekçi e-ticaret satış simülasyonu
trend = np.linspace(1000, 2000, n)
yearly_season = 300 * np.sin(2 * np.pi * np.arange(n) / 365)
weekly_season = 100 * np.sin(2 * np.pi * np.arange(n) / 7)
noise = np.random.randn(n) * 50
y = trend + yearly_season + weekly_season + noise

df_neural = pd.DataFrame({
    'unique_id': 'eticaret',
    'ds': dates,
    'y': np.abs(y)
})

# Eğitim/test ayrımı
horizon = 28
train_neural = df_neural[:-horizon]
test_neural = df_neural[-horizon:]

# NeuralForecast nesnesi - birden fazla modeli aynı anda çalıştırma
nf = NeuralForecast(
    models=[
        NBEATS(
            h=horizon,
            input_size=2 * horizon,
            loss=MAE(),
            max_steps=200,
            scaler_type='robust',
            stack_types=['trend', 'seasonality'],
            n_blocks=[3, 3],
            mlp_units=3 * [[512, 512]]
        ),
        NHITS(
            h=horizon,
            input_size=2 * horizon,
            loss=MAE(),
            max_steps=200,
            scaler_type='robust',
            n_freq_downsample=[7, 1, 1],
            mlp_units=3 * [[512, 512]]
        ),
    ],
    freq='D'
)

# Modeli eğitme
nf.fit(df=train_neural)

# Tahmin yapma
forecasts_neural = nf.predict()
print(forecasts_neural.head(10))

6.3 Temporal Fusion Transformer (TFT) ile Çok Değişkenli Tahmin

Dışsal değişkenler (hava durumu, kampanya bilgisi, tatil günleri vs.) tahmin doğruluğunu ciddi şekilde artırabilir. TFT bu konuda gerçekten parlıyor çünkü hangi dışsal değişkenin ne kadar etkili olduğunu attention mekanizmasıyla gösterebiliyor.

# Covariates (dışsal değişkenler) ile TFT modeli
import pandas as pd
import numpy as np
from neuralforecast import NeuralForecast
from neuralforecast.models import TFT, PatchTST
from neuralforecast.losses.pytorch import MAE

np.random.seed(42)
n = 500
dates = pd.date_range(start='2020-01-01', periods=n, freq='D')

# Ana seri ve dışsal değişkenler
y = (1000 + np.cumsum(np.random.randn(n) * 5)
     + 200 * np.sin(2 * np.pi * np.arange(n) / 365)
     + 50 * np.sin(2 * np.pi * np.arange(n) / 7))

# Dışsal değişkenler (gelecekte de bilinen)
hava_sicakligi = 20 + 15 * np.sin(2 * np.pi * np.arange(n) / 365) + np.random.randn(n) * 2
tatil_gunu = (np.arange(n) % 7 >= 5).astype(float)
kampanya = np.random.binomial(1, 0.1, n)

df_tft = pd.DataFrame({
    'unique_id': 'ana_seri',
    'ds': dates,
    'y': np.abs(y),
    'hava_sicakligi': hava_sicakligi,
    'tatil_gunu': tatil_gunu,
    'kampanya': kampanya
})

horizon = 30
futr_exog_list = ['hava_sicakligi', 'tatil_gunu', 'kampanya']
train_tft = df_tft[:-horizon]

# TFT ve PatchTST modellerini karşılaştırma
nf_advanced = NeuralForecast(
    models=[
        TFT(
            h=horizon,
            input_size=3 * horizon,
            hidden_size=128,
            n_head=4,
            attn_dropout=0.0,
            dropout=0.1,
            loss=MAE(),
            max_steps=300,
            futr_exog_list=futr_exog_list,
            scaler_type='robust'
        ),
        PatchTST(
            h=horizon,
            input_size=5 * horizon,
            patch_len=16,
            stride=8,
            d_model=128,
            n_heads=4,
            loss=MAE(),
            max_steps=300,
            scaler_type='robust'
        )
    ],
    freq='D'
)

nf_advanced.fit(df=train_tft, val_size=horizon)

# Gelecek dışsal değişken değerleri ile tahmin
futr_df = df_tft[-horizon:][['unique_id', 'ds'] + futr_exog_list]
forecasts_advanced = nf_advanced.predict(futr_df=futr_df)
print("TFT ve PatchTST Tahminleri:")
print(forecasts_advanced)

6.4 NeuralForecast ile Çapraz Doğrulama

Derin öğrenme modelleriyle çalışırken çapraz doğrulama yapmayı atlamamak lazım. Tek bir test setine bakıp "harika sonuç" demek yanıltıcı olabilir (bunu zor yoldan öğrendim).

# NeuralForecast cross-validation
from neuralforecast import NeuralForecast
from neuralforecast.models import NHITS
from neuralforecast.losses.pytorch import MAE

nf_cv = NeuralForecast(
    models=[NHITS(h=14, input_size=56, loss=MAE(), max_steps=100)],
    freq='D'
)

# Zaman serisi çapraz doğrulama
cv_df = nf_cv.cross_validation(
    df=df_neural,
    n_windows=3,          # 3 test penceresi
    step_size=14,         # Her pencerede 14 gün ilerleme
    refit=True            # Her pencerede modeli yeniden eğit
)

print("Çapraz Doğrulama Sonuçları:")
print(cv_df.groupby('cutoff')['NHITS'].describe())

7. Model Değerlendirme Metrikleri

Doğru metrikleri kullanmak, tahmin kalitesini nesnel biçimde ölçmek için kritik. Ama burada dikkat edilmesi gereken bir şey var: her metriğin farklı avantajları ve kör noktaları var. Tek bir metriğe güvenmek genellikle iyi bir fikir değil.

import numpy as np
import pandas as pd
from sklearn.metrics import mean_absolute_error, mean_squared_error

def hesapla_metrikler(gercek, tahmin, model_adi="Model"):
    gercek = np.array(gercek)
    tahmin = np.array(tahmin)

    mae = mean_absolute_error(gercek, tahmin)
    mse = mean_squared_error(gercek, tahmin)
    rmse = np.sqrt(mse)
    mape = np.mean(np.abs((gercek - tahmin) / (gercek + 1e-8))) * 100
    smape = np.mean(2 * np.abs(gercek - tahmin) /
                    (np.abs(gercek) + np.abs(tahmin) + 1e-8)) * 100
    rmsse = rmse / (np.mean(np.abs(np.diff(gercek))) + 1e-8)
    ss_res = np.sum((gercek - tahmin) ** 2)
    ss_tot = np.sum((gercek - np.mean(gercek)) ** 2)
    r2 = 1 - (ss_res / (ss_tot + 1e-8))

    return {
        'Model': model_adi,
        'MAE': round(mae, 4),
        'RMSE': round(rmse, 4),
        'MAPE (%)': round(mape, 4),
        'sMAPE (%)': round(smape, 4),
        'RMSSE': round(rmsse, 4),
        'R2': round(r2, 4)
    }

# Modelleri karşılaştırma
np.random.seed(42)
gercek_degerler = np.array([120, 135, 148, 162, 155, 170, 183, 195, 188, 210,
                             225, 218, 235, 248, 260, 275, 265, 280, 295, 310])

arima_tahmin = gercek_degerler + np.random.randn(20) * 8
prophet_tahmin = gercek_degerler + np.random.randn(20) * 6
nbeats_tahmin = gercek_degerler + np.random.randn(20) * 4
nhits_tahmin = gercek_degerler + np.random.randn(20) * 3

sonuclar = []
for model_adi, tahmin in [
    ('ARIMA', arima_tahmin),
    ('Prophet', prophet_tahmin),
    ('N-BEATS', nbeats_tahmin),
    ('NHITS', nhits_tahmin)
]:
    sonuc = hesapla_metrikler(gercek_degerler, tahmin, model_adi)
    sonuclar.append(sonuc)

karsilastirma_df = pd.DataFrame(sonuclar).set_index('Model')
print("MODEL KARŞILAŞTIRMA TABLOSU")
print(karsilastirma_df.to_string())

7.1 Metriklerin Açıklaması

Hangi metriği ne zaman kullanmanız gerektiğini anlamak önemli. İşte kısa bir özet:

Metrik Formül Avantaj Dezavantaj
MAE mean(|y - ŷ|) Yorumlaması kolay, aykırı değere dayanıklı Büyük hataları cezalandırmaz
RMSE sqrt(mean((y - ŷ)²)) Büyük hataları güçlü cezalandırır Aykırı değerlere duyarlı
MAPE mean(|y - ŷ| / |y|) × 100 Yüzde bazlı, ölçekten bağımsız Sıfıra yakın değerlerde hata verir
sMAPE mean(2|y - ŷ| / (|y| + |ŷ|)) × 100 Simetrik, sıfıra daha dayanıklı Yorumlaması daha zor
1 - SS_res/SS_tot Varyansın açıklanan oranını gösterir Zaman serisinde yanıltıcı olabilir

8. Hangi Modeli Ne Zaman Kullanmalı?

Bu soruyu çok duyuyorum ve cevabı her zaman "duruma göre değişir". Model seçimi veri setinin boyutu, yapısı ve tahmin gereksinimlerine bağlı. Ama elimden geldiğince somut bir rehber sunmaya çalışayım.

8.1 Veri Miktarına Göre Model Seçimi

  • Az veri (n < 50): ARIMA, ETS, Exponential Smoothing. Basit modeller az veriyle daha iyi genelleştirir.
  • Orta veri (50 < n < 500): SARIMA, Prophet, StatsForecast modelleri. Mevsimsel kalıplar artık modellenebilir.
  • Çok veri (n > 500): N-BEATS, NHITS, TFT, PatchTST gibi derin öğrenme modelleri. Karmaşık örüntüleri öğrenme kapasitesine sahipler.
  • Çok sayıda seri (N > 1000): StatsForecast (paralel istatistiksel modeller) veya global derin öğrenme modelleri.

8.2 Kullanım Senaryolarına Göre Rehber

Senaryo Önerilen Model Neden
Hızlı prototip ve baseline SeasonalNaive, ETS, AutoARIMA Hızlı eğitim, yorumlanabilir sonuçlar
Tatil/özel gün etkisi var Prophet, TFT Özel gün regressor desteği
Dışsal değişkenler (hava, fiyat) SARIMAX, TFT, NHITS Exogenous variable desteği
Yorumlanabilirlik gerekli N-BEATS, Prophet, TFT Bileşen ayrıştırma yapabilir
Milyonlarca seri StatsForecast (numba) Paralel ve hızlı işlem
Uzun vadeli tahmin (>30 adım) PatchTST, iTransformer, NHITS Uzun bağlam pencereleri
Gerçek zamanlı tahmin ETS, Theta, ARIMA Hızlı güncelleme ve inference

9. Kapsamlı Pipeline: Veri Hazırlamadan Modele

Gerçek dünya projelerinde tek bir model eğitmekle iş bitmez. Veri temizleme, ön işleme, birden fazla model eğitme ve sonuçları birleştirme gibi adımları düzenli bir pipeline içinde toplamak hayat kurtarır. İşte tam olarak bunu yapan bir örnek:

import pandas as pd
import numpy as np
from statsforecast import StatsForecast
from statsforecast.models import AutoARIMA, ETS, AutoTheta
from neuralforecast import NeuralForecast
from neuralforecast.models import NHITS
from neuralforecast.losses.pytorch import MAE
import warnings
warnings.filterwarnings('ignore')

class ZamanSerisiPipeline:
    def __init__(self, horizon, freq='D'):
        self.horizon = horizon
        self.freq = freq
        self.sf = None
        self.nf = None
        self.sonuclar = {}

    def veri_hazirla(self, df):
        df = df.copy()
        gerekli = ['unique_id', 'ds', 'y']
        for s in gerekli:
            if s not in df.columns:
                raise ValueError(f"'{s}' sutunu eksik!")
        df = df.sort_values(['unique_id', 'ds']).reset_index(drop=True)
        df['y'] = df.groupby('unique_id')['y'].transform(
            lambda x: x.ffill().bfill()
        )
        df['y'] = df['y'].clip(lower=0)
        print(f"Veri hazirlandi: {df['unique_id'].nunique()} seri, "
              f"{len(df)} satir")
        return df

    def istatistiksel_modeller_egit(self, df):
        sl = 7 if self.freq == 'D' else 12
        self.sf = StatsForecast(
            models=[AutoARIMA(season_length=sl),
                    ETS(season_length=sl),
                    AutoTheta(season_length=sl)],
            freq=self.freq, n_jobs=-1
        )
        tahminler = self.sf.forecast(df=df, h=self.horizon)
        self.sonuclar['ist'] = tahminler
        print("Istatistiksel modeller egitildi.")
        return tahminler

    def derin_ogrenme_egit(self, df):
        self.nf = NeuralForecast(
            models=[NHITS(h=self.horizon,
                          input_size=3*self.horizon,
                          loss=MAE(), max_steps=150,
                          scaler_type='robust')],
            freq=self.freq
        )
        self.nf.fit(df=df)
        tahminler = self.nf.predict()
        self.sonuclar['dl'] = tahminler
        print("Derin ogrenme modeli egitildi.")
        return tahminler

    def ensemble_tahmin(self):
        ist = self.sonuclar.get('ist')
        dl = self.sonuclar.get('dl')
        if ist is not None and dl is not None:
            birlesik = ist.merge(dl, on=['unique_id', 'ds'], how='inner')
            model_cols = [c for c in birlesik.columns
                          if c not in ['unique_id', 'ds']]
            birlesik['Ensemble'] = birlesik[model_cols].mean(axis=1)
            return birlesik
        return ist if ist is not None else dl

# Pipeline kullanim ornegi
np.random.seed(42)
n = 200
dates = pd.date_range(start='2023-01-01', periods=n, freq='D')
y = (500 + np.cumsum(np.random.randn(n) * 3)
     + 80 * np.sin(2 * np.pi * np.arange(n) / 7)
     + 150 * np.sin(2 * np.pi * np.arange(n) / 365))

df_ornek = pd.DataFrame({
    'unique_id': 'magaza_1', 'ds': dates, 'y': np.abs(y)
})

pipeline = ZamanSerisiPipeline(horizon=14, freq='D')
df_temiz = pipeline.veri_hazirla(df_ornek)
ist_tahminler = pipeline.istatistiksel_modeller_egit(df_temiz)
dl_tahminler = pipeline.derin_ogrenme_egit(df_temiz)
ensemble = pipeline.ensemble_tahmin()
print("\nEnsemble Tahmin Sonuclari:")
print(ensemble[['unique_id', 'ds', 'Ensemble']].tail(14))

10. En İyi Uygulamalar ve İpuçları

10.1 Veri Ön İşleme

Veri ön işleme aşaması çoğu zaman modelleme kadar (hatta bazen daha fazla) önemli. İşte dikkat etmeniz gereken temel noktalar:

  • Ölçekleme (Scaling): Derin öğrenme modelleri için StandardScaler veya RobustScaler kullanın. StatsForecast ve NeuralForecast'in kendi scaler seçenekleri de mevcut.
  • Aykırı Değer Tespiti: IQR yöntemi veya Isolation Forest ile aykırı değerleri tespit edip düzeltin ya da işaretleyin.
  • Eksik Değerler: Forward fill, backward fill veya interpolasyon ile doldurun. Uzun boşluklar varsa dikkatli olun — bazen o kısmı tamamen çıkarmak daha iyidir.
  • Frekans Tutarlılığı: Veri noktalarının eşit aralıklı olduğundan emin olun. Eksik tarihleri tespit edip doldurun.
import pandas as pd
import numpy as np

def on_isleme_pipeline(df, sutun='y'):
    df = df.copy().sort_values('ds')

    # 1. Eksik tarih tespiti ve doldurma
    tam_tarih = pd.date_range(
        start=df['ds'].min(),
        end=df['ds'].max(),
        freq=pd.infer_freq(df['ds'])
    )
    df = df.set_index('ds').reindex(tam_tarih).reset_index()
    df = df.rename(columns={'index': 'ds'})

    # 2. Eksik deger doldurma (interpolasyon)
    df[sutun] = df[sutun].interpolate(method='linear')

    # 3. Aykiri deger tespiti (IQR yontemi)
    Q1 = df[sutun].quantile(0.25)
    Q3 = df[sutun].quantile(0.75)
    IQR = Q3 - Q1
    alt_sinir = Q1 - 3 * IQR
    ust_sinir = Q3 + 3 * IQR

    aykiri_maske = (df[sutun] < alt_sinir) | (df[sutun] > ust_sinir)
    print(f"Tespit edilen aykiri deger sayisi: {aykiri_maske.sum()}")

    # Aykiri degerleri medyan ile degistirme
    df.loc[aykiri_maske, sutun] = df[sutun].rolling(
        window=7, center=True, min_periods=1
    ).median()[aykiri_maske]

    return df

10.2 Zaman Serisi Çapraz Doğrulama Stratejisi

Burada çok önemli bir nokta var: klasik k-fold çapraz doğrulama zaman serisinde uygulanamaz. Neden mi? Çünkü gelecekteki verileri geçmişi tahmin etmek için kullanamazsınız — bu veri sızıntısına (data leakage) yol açar. Bunun yerine Walk-Forward Validation kullanmalısınız:

def walk_forward_validation(model_fn, df, initial_train,
                            horizon, step=None):
    if step is None:
        step = horizon

    tum_sonuclar = []
    n = len(df)

    for start in range(initial_train, n - horizon + 1, step):
        train_slice = df.iloc[:start]
        test_slice = df.iloc[start:start + horizon]
        tahminler = model_fn(train_slice, horizon)
        sonuc = pd.DataFrame({
            'ds': test_slice['ds'].values,
            'gercek': test_slice['y'].values,
            'tahmin': tahminler,
            'pencere': start
        })
        tum_sonuclar.append(sonuc)

    return pd.concat(tum_sonuclar, ignore_index=True)

# Ornek kullanim
def basit_model_fn(train_df, horizon):
    pencere = min(7, len(train_df))
    son_ortalama = train_df['y'].tail(pencere).mean()
    return np.full(horizon, son_ortalama)

cv_sonuclari = walk_forward_validation(
    model_fn=basit_model_fn,
    df=df_ornek,
    initial_train=100,
    horizon=14,
    step=14
)

mae_pencereler = cv_sonuclari.groupby('pencere').apply(
    lambda x: np.mean(np.abs(x['gercek'] - x['tahmin']))
)
print("Pencere bazinda MAE degerleri:")
print(mae_pencereler)
print(f"\nOrtalama MAE: {mae_pencereler.mean():.4f}")

10.3 Olasılıksal Tahmin (Prediction Intervals)

Nokta tahmini (tek bir değer) vermek çoğu zaman yeterli değil. Karar vericiler belirsizliği de görmek ister. Güven aralıklı tahminler bu ihtiyacı karşılar.

from statsforecast import StatsForecast
from statsforecast.models import AutoARIMA

# Guven aralikli tahmin
sf_pi = StatsForecast(
    models=[AutoARIMA(season_length=7)],
    freq='D', n_jobs=1
)
sf_pi.fit(df=df_ornek)

# %80 ve %95 guven araliklari
tahmin_pi = sf_pi.predict(h=14, level=[80, 95])
print("Guven Aralikli Tahminler:")
print(tahmin_pi[['ds', 'AutoARIMA',
                  'AutoARIMA-lo-80', 'AutoARIMA-hi-80',
                  'AutoARIMA-lo-95', 'AutoARIMA-hi-95']].head(7))

11. Sonuç ve Gelecek Yönelimleri

Python ekosistemi, zaman serisi tahminleme için gerçekten zengin bir araç seti sunuyor. Bu rehberde ele aldığımız yöntemleri kısaca özetleyelim:

  1. ARIMA/SARIMA: Yorumlanabilir, az veri gerektiren klasik modeller. statsmodels ve pmdarima ile kolayca uygulanabilir.
  2. Prophet: İş verilerine özel, tatil ve özel gün etkilerini modelleyebilen, kullanımı kolay Bayesian model.
  3. StatsForecast: Numba hızlandırmalı, milyonlarca seriyi paralel işleyebilen, kurumsal ölçekli istatistiksel modeller koleksiyonu.
  4. NeuralForecast: 30+ derin öğrenme modeli barındıran, N-BEATS'ten iTransformer'a uzanan kapsamlı kütüphane.

Gelecekte foundation models (GPT benzeri büyük zaman serisi modelleri), çok modaliteli tahmin ve federated learning gibi yönelimler öne çıkacak gibi görünüyor. Amazon Chronos, Google TimesFM ve Nixtla'nın TimeGPT gibi ön-eğitimli modeller, transfer learning yaklaşımını bu alana taşıyarak az veriyle bile güçlü tahminler yapılmasını mümkün kılıyor.

Hangi modeli seçeceğiniz nihayetinde probleminizin boyutuna, veri miktarına ve hesaplama kaynaklarınıza bağlı. Benim tavsiyem her zaman baseline modellerden (SeasonalNaive, ETS) başlayıp karmaşıklığı kademeli olarak artırmanız. Ve unutmayın — en iyi model, verileriniz üzerinde en iyi performansı gösteren modeldir, mutlaka en karmaşık olan değil.

Faydalı Kaynaklar ve Kütüphane Linkleri

  • statsmodels: Klasik istatistiksel modeller — statsmodels.org
  • pmdarima: Auto-ARIMA implementasyonu — alkaline-ml.com/pmdarima
  • Prophet: Meta'nın tahmin kütüphanesi — facebook.github.io/prophet
  • StatsForecast: Nixtla'nın hızlı istatistiksel modelleri — nixtla.github.io/statsforecast
  • NeuralForecast: Nixtla'nın derin öğrenme modelleri — nixtla.github.io/neuralforecast
  • TimeGPT: Nixtla'nın ön-eğitimli zaman serisi modeli — docs.nixtla.io
Yazar Hakkında Editorial Team

Our team of expert writers and editors.