Asosiy qismga o'tish

RNN, LSTM va vaqt qatorlari

35-bob 14 kod bloki 4442 so'z

Sahna: ustunlar tartibi muhim bo'lganda

Shu paytgacha har bir qator mustaqil edi. Uy ijarasida 5-qatorni 500-qator bilan almashtirsak, hech narsa o'zgarmaydi.

Endi boshqa ma'lumot turi: vaqt qatori. Toshkentda o'rtacha ijara narxi, oyma-oy.

Bu yerda tartib — ma'lumotning o'zi. Aralashtirsangiz, ma'lumot yo'qoladi.

python28 qator
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
from torch.utils.data import TensorDataset, DataLoader
from sklearn.linear_model import LinearRegression
from sklearn.neighbors import KNeighborsRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error
from sklearn.model_selection import train_test_split

torch.set_num_threads(4)

r = np.random.default_rng(7)
OYLAR = 400
oy = np.arange(OYLAR)
qator = (420 + 0.9 * oy + 38 * np.sin(2 * np.pi * oy / 12) + r.normal(0, 14, OYLAR)).round(2)
seriya = pd.Series(qator, index=pd.date_range("1993-01-01", periods=OYLAR, freq="MS"))

print(f"Qator uzunligi : {OYLAR} oy")
print(f"Diapazon       : {qator.min():.1f} .. {qator.max():.1f}")
print(f"Birinchi yil   : {qator[:12].mean():.1f}")
print(f"Oxirgi yil     : {qator[-12:].mean():.1f}")
print()
print("Ichida uch narsa bor: TREND (o'sish), SEZONLIK (12 oylik to'lqin), SHOVQIN.")
Chiqish
Qator uzunligi : 400 oy
Diapazon       : 379.3 .. 827.1
Birinchi yil   : 423.6
Oxirgi yil     : 769.7

Ichida uch narsa bor: TREND (o'sish), SEZONLIK (12 oylik to'lqin), SHOVQIN.
python9 qator
rasm, (a1, a2) = plt.subplots(2, 1, figsize=(12, 6.5))
a1.plot(seriya.index, seriya.values, lw=1.1)
a1.set_title("To'liq qator: trend + sezonlik")
a1.set_ylabel("narx")
a2.plot(seriya.index[-48:], seriya.values[-48:], marker="o", ms=3.5, lw=1.2)
a2.set_title("Oxirgi 48 oy — sezonlik yaqindan ko'rinadi")
a2.set_ylabel("narx")
plt.tight_layout()
fig("qator.png", caption="Vaqt qatori: o'sish tendentsiyasi ustiga 12 oylik mavsumiy to'lqin qo'yilgan.")

Vaqt qatori: o'sish tendentsiyasi ustiga 12 oylik mavsumiy to'lqin qo'yilgan.

Vaqt qatori: o'sish tendentsiyasi ustiga 12 oylik mavsumiy to'lqin qo'yilgan.

Lag ustunlari: qatorni jadvalga aylantirish

ML modellari jadval kutadi. Vaqt qatorini jadvalga aylantirish usuli — lag oyna: oxirgi L oyga qarab keyingi oyni aytamiz.

python11 qator
L = 12
X = np.stack([qator[i:i + L] for i in range(OYLAR - L)])
y = qator[L:]

print(f"Oyna uzunligi L = {L}")
print(f"X shakli: {X.shape}   y shakli: {y.shape}")
print()
print("Muhim kuzatish — qo'shni oynalar bir-biriga juda o'xshaydi:")
print(f"  oyna 0: {X[0][:5]} ...")
print(f"  oyna 1: {X[1][:5]} ...")
print(f"  umumiy qiymatlar soni: {len(set(X[0]) & set(X[1]))} / {L}")
Chiqish
Oyna uzunligi L = 12
X shakli: (388, 12)   y shakli: (388,)

Muhim kuzatish — qo'shni oynalar bir-biriga juda o'xshaydi:
  oyna 0: [420.02 444.08 450.87 448.23 450.14] ...
  oyna 1: [444.08 450.87 448.23 450.14 429.62] ...
  umumiy qiymatlar soni: 11 / 12

Split: bu yerda tasodifiy bo'linish YOLG'ON gapiradi

5-bobda aytganmiz: split to'g'ri bo'lmasa, hamma metrika ma'nosiz. Vaqt qatorida bu xato boshqa shaklda keladi.

To'g'ri yo'l — xronologik split: avval o'qit, keyingi davrda sinab ko'r. Chunki amalda siz kelasi oyni bashorat qilasiz, o'tgan oyni emas.

Buni o'lchab ko'ramiz.

python12 qator
kesish = int(len(X) * 0.8)
Xtr, Xte, ytr, yte = X[:kesish], X[kesish:], y[:kesish], y[kesish:]
Xa, Xb, ya, yb = train_test_split(X, y, test_size=0.2, random_state=42)

print(f"Xronologik: train {len(Xtr)} oy -> test {len(Xte)} oy (keyingi davr)")
print(f"Tasodifiy : train {len(Xa)} oy -> test {len(Xb)} oy (vaqt bo'ylab aralash)")
print()
print(f"Train target diapazoni (xronologik): {ytr.min():.1f} .. {ytr.max():.1f}")
print(f"Test  target diapazoni (xronologik): {yte.min():.1f} .. {yte.max():.1f}")
print()
print(f"Test ning {float((yte > ytr.max()).mean()):.1%} qismi train dagi ENG KATTA qiymatdan yuqori.")
print("Ya'ni model EKSTRAPOLYATSIYA qilishi kerak — ko'rmagan diapazonga chiqishi kerak.")
Chiqish
Xronologik: train 310 oy -> test 78 oy (keyingi davr)
Tasodifiy : train 310 oy -> test 78 oy (vaqt bo'ylab aralash)

Train target diapazoni (xronologik): 379.3 .. 743.0
Test  target diapazoni (xronologik): 670.2 .. 827.1

Test ning 51.3% qismi train dagi ENG KATTA qiymatdan yuqori.
Ya'ni model EKSTRAPOLYATSIYA qilishi kerak — ko'rmagan diapazonga chiqishi kerak.
python12 qator
modellar = [
    ("linear regression", lambda: LinearRegression()),
    ("KNN (k=3)", lambda: KNeighborsRegressor(3)),
    ("random forest", lambda: RandomForestRegressor(200, random_state=0)),
]
jadval = []
for nom, yasa in modellar:
    xron = mean_absolute_error(yte, yasa().fit(Xtr, ytr).predict(Xte))
    tasod = mean_absolute_error(yb, yasa().fit(Xa, ya).predict(Xb))
    jadval.append({"model": nom, "xronologik MAE": round(xron, 2),
                   "tasodifiy MAE": round(tasod, 2), "farq": round(tasod - xron, 2)})
show(pd.DataFrame(jadval))
modelxronologik MAEtasodifiy MAEfarq
linear regression12.3411.54-0.8
KNN (k=3)43.914.22-29.68
random forest33.3313.95-19.38

Jadvalga qarang. Tasodifiy split hamma model uchun "yaxshi" natija beradi. Xronologik split esa haqiqatni aytadi: KNN va random forest bu vazifada ishlamaydi.

Daraxt va KNN EKSTRAPOLYATSIYA QILA OLMAYDI

Men bu bo'limni "qo'shni oynalar bir xil, shuning uchun tasodifiy split leakage beradi" degan taxmin bilan boshlagandim. O'lchov buni rad etdi — sabab boshqa.

Random forest bashorati — train dagi target qiymatlarning o'rtachasi. KNN bashorati — eng yaqin qo'shnilarning o'rtachasi. Ikkisi ham train dagi eng katta qiymatdan yuqoriga chiqa olmaydi. Matematik jihatdan mumkin emas.

Qatorda trend bor, ya'ni kelajak qiymatlari o'tmishdan yuqori. Shuning uchun bu modellar tuzilishiga ko'ra xato qiladi.

Linear regression esa ekstrapolyatsiya qila oladi (u chiziq, chiziqni davom ettirish mumkin) — shuning uchun uning natijasi ikki splitda ham deyarli bir xil.

Tasodifiy split buni yashiradi: test nuqtalari train nuqtalari orasiga aralashib ketadi, hech qanday ekstrapolyatsiya kerak bo'lmaydi. Siz 13-14 MAE ko'rasiz va modelni ishlab chiqarishga chiqarasiz. Amalda esa 33-44 MAE olasiz.

Qoida: vaqt qatorida split har doim xronologik. Va trendli qatorda daraxt modellarini ishlatmoqchi bo'lsangiz, avval trendni ayiring (differensiyalash).

python11 qator
m_knn = KNeighborsRegressor(3).fit(Xtr, ytr)
m_lin = LinearRegression().fit(Xtr, ytr)
rasm, ax = plt.subplots(figsize=(12, 4.2))
ind = np.arange(len(yte))
ax.plot(ind, yte, "k-", lw=2, label="haqiqiy")
ax.plot(ind, m_knn.predict(Xte), "--", lw=1.6, label="KNN bashorati")
ax.plot(ind, m_lin.predict(Xte), "--", lw=1.6, label="linear regression bashorati")
ax.axhline(ytr.max(), color="red", ls=":", lw=1.4, label=f"train dagi maksimum ({ytr.max():.0f})")
ax.set_title("KNN qizil chizidan yuqoriga CHIQA OLMAYDI")
ax.set_xlabel("test oyi"); ax.set_ylabel("narx"); ax.legend(fontsize=9)
fig("ekstrapolyatsiya.png", caption="KNN bashorati train dagi maksimumga urilib qoladi. Linear regression trendni davom ettiradi.")

KNN bashorati train dagi maksimumga urilib qoladi. Linear regression trendni davom ettiradi.

KNN bashorati train dagi maksimumga urilib qoladi. Linear regression trendni davom ettiradi.

Baza: bashorat uchun eng oddiy raqib

6-bobdagi qoida bu yerda ham ishlaydi — avval baza. Vaqt qatorida ikkita klassik baza bor.

python9 qator
bazalar = {
    "naive (oxirgi qiymat)": Xte[:, -1],
    "sezonli naive (12 oy oldin)": Xte[:, 0],
}
natija = [{"usul": k, "MAE": round(mean_absolute_error(yte, v), 2)} for k, v in bazalar.items()]
natija.append({"usul": "linear regression (12 lag)",
               "MAE": round(mean_absolute_error(yte, m_lin.predict(Xte)), 2)})
show(pd.DataFrame(natija))
print("Har qanday murakkab model SHU raqamlardan past MAE bermasa — keraksiz.")
Chiqish
Har qanday murakkab model SHU raqamlardan past MAE bermasa — keraksiz.
usulMAE
naive (oxirgi qiymat)19.47
sezonli naive (12 oy oldin)18.25
linear regression (12 lag)12.34

RNN: xotirali tarmoq

Endi ketma-ketlik uchun maxsus arxitektura. Oddiy tarmoq butun oynani birdan ko'radi. RNN esa qadamba-qadam yuradi va o'zida yashirin holat (hidden state) saqlaydi.

python16 qator
diagramma(
    {
        "x1\n1-oy": (0.2, 2.5, 1.4, 0.7, "manba"),
        "x2\n2-oy": (2.1, 2.5, 1.4, 0.7, "manba"),
        "x3\n3-oy": (4.0, 2.5, 1.4, 0.7, "manba"),
        "h1": (0.2, 1.3, 1.4, 0.7, "qayta"),
        "h2": (2.1, 1.3, 1.4, 0.7, "qayta"),
        "h3": (4.0, 1.3, 1.4, 0.7, "qayta"),
        "javob": (4.0, 0.1, 1.4, 0.7, "xizmat"),
    },
    [("x1\n1-oy", "h1"), ("x2\n2-oy", "h2"), ("x3\n3-oy", "h3"),
     ("h1", "h2", "holat"), ("h2", "h3", "holat"), ("h3", "javob")],
    nom="rnn_sxema.png",
    izoh="RNN qadamba-qadam yuradi. Yashirin holat o'ngga uzatiladi, "
         "oxirgi holat javob beradi. Birinchi oyning ta'siri shu zanjir orqali o'tishi kerak.",
)

RNN qadamba-qadam yuradi. Yashirin holat o'ngga uzatiladi, oxirgi holat javob beradi. Birinchi oyning ta'siri shu zanjir orqali o'tishi kerak.

RNN qadamba-qadam yuradi. Yashirin holat o'ngga uzatiladi, oxirgi holat javob beradi. Birinchi oyning ta'siri shu zanjir orqali o'tishi kerak.

python8 qator
h = 32
rnn = nn.RNN(1, h, batch_first=True)
lstm = nn.LSTM(1, h, batch_first=True)
print(f"nn.RNN(1, {h})  parametrlar: {sum(p.numel() for p in rnn.parameters())}")
print(f"nn.LSTM(1, {h}) parametrlar: {sum(p.numel() for p in lstm.parameters())}  (4 barobar — 4 ta darvoza)")
print()
print("LSTM ning 4 darvozasi: unutish, kirish, chiqish va yacheyka yangilanishi.")
print("Nazariy g'oya: darvozalar 'nimani eslab qolish'ni o'rganadi.")
Chiqish
nn.RNN(1, 32)  parametrlar: 1120
nn.LSTM(1, 32) parametrlar: 4480  (4 barobar — 4 ta darvoza)

LSTM ning 4 darvozasi: unutish, kirish, chiqish va yacheyka yangilanishi.
Nazariy g'oya: darvozalar 'nimani eslab qolish'ni o'rganadi.

Sinov: uzoq masofali xotira

Nazariya aytadi: oddiy RNN uzoq ketma-ketlikni eslab qolmaydi, LSTM esa qoladi. O'lchaymiz.

Vazifa ataylab eng sof shaklda: ketma-ketlikning faqat birinchi elementi javobni belgilaydi. Qolgani shovqin. Model T qadam oxirigacha shu bitta faktni eslab turishi kerak.

python41 qator
def xotira_vazifasi(n, T, seed=0):
    rr = np.random.default_rng(seed)
    Xs = rr.normal(0, 1, (n, T, 1)).astype(np.float32)
    return torch.tensor(Xs), torch.tensor((Xs[:, 0, 0] > 0).astype(np.int64))

class Ketma(nn.Module):
    def __init__(self, turi, h=32):
        super().__init__()
        self.rnn = nn.RNN(1, h, batch_first=True) if turi == "rnn" else nn.LSTM(1, h, batch_first=True)
        self.fc = nn.Linear(h, 2)
    def forward(self, x):
        chiqish, _ = self.rnn(x)
        return self.fc(chiqish[:, -1])

def ketma_oqit(turi, T, seed, epoxalar=60, n=2000):
    torch.manual_seed(seed)
    Xtr_, ytr_ = xotira_vazifasi(n, T, 1)
    Xte_, yte_ = xotira_vazifasi(600, T, 2)
    m = Ketma(turi)
    opt = torch.optim.Adam(m.parameters(), lr=0.005)
    krit = nn.CrossEntropyLoss()
    dl = DataLoader(TensorDataset(Xtr_, ytr_), batch_size=64, shuffle=True,
                    generator=torch.Generator().manual_seed(seed))
    for _ in range(epoxalar):
        m.train()
        for xb, yb_ in dl:
            opt.zero_grad(); krit(m(xb), yb_).backward(); opt.step()
    m.eval()
    with torch.no_grad():
        return float((m(Xte_).argmax(1) == yte_).float().mean())

print("Tasodifiy javob = 0.50 aniqlik. Har konfiguratsiya 3 SEED bilan.")
qatorlar = []
for T in [5, 20, 60]:
    for turi in ["rnn", "lstm"]:
        aniq = [ketma_oqit(turi, T, s) for s in (0, 1, 2)]
        qatorlar.append({"uzunlik T": T, "model": turi,
                         "seedlar": str([round(a, 3) for a in aniq]),
                         "o'rtacha": round(float(np.mean(aniq)), 4),
                         "eng yomon": round(min(aniq), 4)})
show(pd.DataFrame(qatorlar))
Chiqish
Tasodifiy javob = 0.50 aniqlik. Har konfiguratsiya 3 SEED bilan.
uzunlik Tmodelseedlaro'rtachaeng yomon
5rnn[0.997, 0.995, 0.998]0.9970.995
5lstm[0.998, 0.997, 0.998]0.9980.997
20rnn[0.933, 0.942, 0.838]0.9040.838
20lstm[0.813, 0.493, 0.997]0.7680.493
60rnn[0.847, 0.98, 0.98]0.9360.847
60lstm[0.542, 0.97, 0.517]0.6760.517

Jadvalni diqqat bilan o'qing. Men bu bo'limga "LSTM uzoq ketma-ketlikda ustun keladi" deb yozmoqchi edim. O'lchov boshqa narsa ko'rsatdi.

Bitta seed bilan chiqarilgan xulosa — xulosa emas

LSTM natijalari ikki qutbli: ba'zi seedlarda vazifani deyarli to'liq yechadi, ba'zilarida tasodifiy javob darajasida qoladi.

Bu — sig'im muammosi emas, optimizatsiya muammosi. Arxitektura yecha oladi (buni muvaffaqiyatli seedlar isbotlaydi), lekin o'qitish har doim shu yechimga yetib bormaydi.

Amaliy xulosa ikkita:

  1. Neyron tarmoq natijasini bitta seed bilan o'lchash — o'lchamaslik bilan teng. 31-bobda TensorFlow takrorlanmasligini ko'rgandik; bu — shu muammoning jiddiyroq shakli. Kamida 3 seed, va eng yomon natijani ham yozing.
  2. "Kitobda X yaxshi deyilgan" — bu sizning ma'lumotingizda va sizning o'qitish budjetingizda X yaxshi degani emas.

Men klassik tuzatishni ham sinab ko'rdim: LSTM ning unutish darvozasi bias ini 1 ga qo'yish (adabiyotda tavsiya etiladi). Bu konfiguratsiyada yordam bermadi — natija yaxshilanmadi. Shuning uchun uni kitobga "yechim" sifatida yozmadim.

Nega uzoq masofa qiyin: gradientni o'lchaymiz

30-bobda backpropagation ni ko'rganmiz. Ketma-ketlikda gradient vaqt bo'ylab orqaga qaytadi — va yo'lda kichrayadi.

python12 qator
print("Kirishning har qadami bo'yicha gradient kattaligi (T=60, o'qitishdan oldin):")
print()
for turi in ["rnn", "lstm"]:
    torch.manual_seed(0)
    Xg, yg = xotira_vazifasi(256, 60, 1)
    Xg.requires_grad_(True)
    m = Ketma(turi)
    nn.CrossEntropyLoss()(m(Xg), yg).backward()
    g = Xg.grad.abs().mean(dim=(0, 2))
    print(f"  {turi.upper():>5}: qadam 0 -> {g[0]:.3e} | qadam 30 -> {g[30]:.3e} | "
          f"qadam 59 -> {g[59]:.3e}")
    print(f"         qadam 59 / qadam 0 nisbati: {g[59] / g[0]:.2e}")
Chiqish
Kirishning har qadami bo'yicha gradient kattaligi (T=60, o'qitishdan oldin):

    RNN: qadam 0 -> 2.427e-17 | qadam 30 -> 7.964e-11 | qadam 59 -> 1.129e-05
         qadam 59 / qadam 0 nisbati: 4.65e+11
   LSTM: qadam 0 -> 2.503e-17 | qadam 30 -> 4.343e-11 | qadam 59 -> 2.982e-05
         qadam 59 / qadam 0 nisbati: 1.19e+12

Mana muammoning ildizi: javobni belgilaydigan birinchi qadamga yetib boradigan gradient oxirgi qadamdagidan ko'p marta kichik. Model "birinchi element muhim" degan xulosani chiqarish uchun juda kuchsiz signal oladi.

Shuning uchun amalda uzoq kontekst uchun RNN ham, LSTM ham emas — attention ishlatiladi. U har qadamga to'g'ridan-to'g'ri yo'l beradi. Keyingi bob shu haqda.

LSTM bashorat uchun foydalimi?

Endi asosiy savol: bizning ijara qatorida LSTM linear regression dan ustun keladimi?

python42 qator
def lstm_bashorat(seed, epoxalar, differensiya=False):
    torch.manual_seed(seed)
    if differensiya:
        Xa, Xb = np.diff(Xtr, axis=1), np.diff(Xte, axis=1)
        ya, baza = ytr - Xtr[:, -1], Xte[:, -1]
    else:
        Xa, Xb, ya, baza = Xtr, Xte, ytr, 0.0
    mu_, sd_ = Xa.mean(), Xa.std()
    ymu, ysd = ya.mean(), ya.std()
    yadro = nn.LSTM(1, 32, batch_first=True)
    boshi = nn.Linear(32, 1)
    opt = torch.optim.Adam(nn.ModuleList([yadro, boshi]).parameters(), lr=0.01)
    xt = torch.tensor(((Xa - mu_) / sd_).astype(np.float32)).unsqueeze(-1)
    yt = torch.tensor(((ya - ymu) / ysd).astype(np.float32)).unsqueeze(-1)
    dl = DataLoader(TensorDataset(xt, yt), batch_size=32, shuffle=True,
                    generator=torch.Generator().manual_seed(seed))
    for _ in range(epoxalar):
        for xb, yb_ in dl:
            opt.zero_grad()
            o, _ = yadro(xb)
            nn.MSELoss()(boshi(o[:, -1]), yb_).backward()
            opt.step()
    with torch.no_grad():
        o, _ = yadro(torch.tensor(((Xb - mu_) / sd_).astype(np.float32)).unsqueeze(-1))
        return boshi(o[:, -1]).squeeze(-1).numpy() * ysd + ymu + baza

tajribalar = [
    ("LSTM, xom qator, 120 epoxa", dict(epoxalar=120)),
    ("LSTM, xom qator, 400 epoxa", dict(epoxalar=400)),
    ("LSTM, DIFFERENSIYALANGAN, 400 epoxa", dict(epoxalar=400, differensiya=True)),
]
oxirgi = [
    {"usul": "naive (oxirgi qiymat)", "MAE": round(mean_absolute_error(yte, Xte[:, -1]), 2),
     "seedlar": "-"},
    {"usul": "linear regression (12 lag)",
     "MAE": round(mean_absolute_error(yte, m_lin.predict(Xte)), 2), "seedlar": "-"},
]
for nom, kw in tajribalar:
    xs_ = [mean_absolute_error(yte, lstm_bashorat(s, **kw)) for s in (0, 1, 2)]
    oxirgi.append({"usul": nom, "MAE": round(float(np.mean(xs_)), 2),
                   "seedlar": str([round(v, 1) for v in xs_])})
show(pd.DataFrame(oxirgi))
usulMAEseedlar
naive (oxirgi qiymat)19.47-
linear regression (12 lag)12.34-
LSTM, xom qator, 120 epoxa30.36[40.9, 21.5, 28.7]
LSTM, xom qator, 400 epoxa30.73[25.9, 33.7, 32.7]
LSTM, DIFFERENSIYALANGAN, 400 epoxa17.87[19.8, 15.6, 18.2]

Jadvaldan uchta xulosa chiqadi, va ikkinchisi meni to'xtatdi.

Birinchi: xom qatorda LSTM naive bazadan ham yomon ishlaydi.

Ikkinchi: epoxalarni 120 dan 400 ga oshirish yordam bermadi. Men avval "LSTM ga kam o'qitish berdim, taqqoslash adolatsiz" deb o'yladim va budjetni uch barobar oshirdim. Natija o'zgarmadi. Demak muammo o'qitish budjetida emas — tuzilishda.

Uchinchi: qatorni differensiyalagandan keyin LSTM sezilarli yaxshilandi.

Neyron tarmoq ham trendni ekstrapolyatsiya qila olmaydi

Yuqorida KNN va random forest train maksimumidan yuqoriga chiqa olmasligini ko'rdik. LSTM uchun ham xuddi shu muammo bor, faqat sabab boshqa: biz kirishni train statistikasi bilan normallashtiramiz ((x - mu) / sd). Test qiymatlari train diapazonidan tashqarida bo'lgani uchun model hech qachon ko'rmagan kattalikdagi kirish oladi.

Yechim — target ni o'zgarish sifatida qo'yish: "keyingi oy narxi" emas, "keyingi oy narxi oxirgi oydan qancha farq qiladi". O'zgarishlar trendsiz va barqaror diapazonda bo'ladi.

Kodda shu: ya = ytr - Xtr[:, -1] va bashoratga baza = Xte[:, -1] qaytarib qo'shiladi.

Bu — vaqt qatori bilan ishlashda birinchi qilinadigan amal, model tanlashdan oldin. Statistikada u differensiyalash deb ataladi.

Vaqt qatorida avval klassik usullar

Bu kitobda yana bir marta bir xil naqsh: murakkab model oddiy modeldan ustun kelmadi. Differensiyalashdan keyin ham LSTM 12 ta lag ustiga qurilgan oddiy linear regression dan past qoldi.

Vaqt qatori uchun amaliy tartib: 1. naive va sezonli naive — baza 2. differensiyalash — trendni ayirish 3. lag ustunlari + linear regression / Ridge — ko'pincha shu yetarli 4. ARIMA, Prophet, ETS — statistik usullar (bu kitob qamrovidan tashqari, lekin mavjud) 5. gradient boosting — differensiyalangan qatorda 6. LSTM / Transformer — ko'p qator va ko'p ma'lumot bo'lganda

LSTM ni birinchi urinish qilib tanlash — eng ko'p uchraydigan xato.

Qanday qilib to'g'ri cross-validation

9-bobdagi KFold vaqt qatoriga yaramaydi: u kelajakni train ga, o'tmishni test ga qo'yadi.

python14 qator
from sklearn.model_selection import TimeSeriesSplit

tss = TimeSeriesSplit(n_splits=5)
print("TimeSeriesSplit — train oynasi o'sib boradi, test har doim keyin keladi:")
print()
print(f"{'burma':>6} {'train':>14} {'test':>14}")
print("-" * 38)
for i, (tr, te) in enumerate(tss.split(X), 1):
    print(f"{i:>6} {f'0..{tr[-1]}':>14} {f'{te[0]}..{te[-1]}':>14}")
print()
xs = [mean_absolute_error(y[te], LinearRegression().fit(X[tr], y[tr]).predict(X[te]))
      for tr, te in tss.split(X)]
print(f"MAE har burmada : {[round(v, 2) for v in xs]}")
print(f"O'rtacha        : {np.mean(xs):.2f}  (+/- {np.std(xs):.2f})")
Chiqish
TimeSeriesSplit — train oynasi o'sib boradi, test har doim keyin keladi:

 burma          train           test
--------------------------------------
     1          0..67        68..131
     2         0..131       132..195
     3         0..195       196..259
     4         0..259       260..323
     5         0..323       324..387

MAE har burmada : [13.75, 11.11, 12.28, 11.82, 12.23]
O'rtacha        : 12.24  (+/- 0.86)

Bugun nima o'rgandik

python12 qator
show(pd.DataFrame([
    {"Tushuncha": "Lag oyna", "Bitta jumla": "Vaqt qatorini jadvalga aylantirish usuli"},
    {"Tushuncha": "Xronologik split", "Bitta jumla": "Majburiy — tasodifiy split natijani yaxshi ko'rsatadi"},
    {"Tushuncha": "Ekstrapolyatsiya", "Bitta jumla": "Daraxt va KNN train maksimumidan yuqoriga chiqa olmaydi"},
    {"Tushuncha": "Differensiyalash", "Bitta jumla": "Trendni ayirish — model tanlashdan OLDIN qilinadi"},
    {"Tushuncha": "TimeSeriesSplit", "Bitta jumla": "Vaqt qatori uchun KFold o'rnini bosadi"},
    {"Tushuncha": "Vaqt qatori bazasi", "Bitta jumla": "naive va sezonli naive — birinchi raqib"},
    {"Tushuncha": "RNN", "Bitta jumla": "Qadamba-qadam yuradi, yashirin holat saqlaydi"},
    {"Tushuncha": "LSTM darvozalari", "Bitta jumla": "4 barobar parametr; nazariyada uzoq xotira"},
    {"Tushuncha": "Seed tarqoqligi", "Bitta jumla": "O'lchandi: bitta seed yetarli emas, eng yomonini yozing"},
    {"Tushuncha": "Vanishing gradient", "Bitta jumla": "O'lchandi: birinchi qadamga signal deyarli yetmaydi"},
]))
TushunchaBitta jumla
Lag oynaVaqt qatorini jadvalga aylantirish usuli
Xronologik splitMajburiy — tasodifiy split natijani yaxshi ko'rsatadi
EkstrapolyatsiyaDaraxt va KNN train maksimumidan yuqoriga chiqa olmaydi
DifferensiyalashTrendni ayirish — model tanlashdan OLDIN qilinadi
TimeSeriesSplitVaqt qatori uchun KFold o'rnini bosadi
Vaqt qatori bazasinaive va sezonli naive — birinchi raqib
RNNQadamba-qadam yuradi, yashirin holat saqlaydi
LSTM darvozalari4 barobar parametr; nazariyada uzoq xotira
Seed tarqoqligiO'lchandi: bitta seed yetarli emas, eng yomonini yozing
Vanishing gradientO'lchandi: birinchi qadamga signal deyarli yetmaydi

Keyingi bobda shu muammoning hozirgi yechimini ko'ramiz: attention. U ketma-ketlik bo'ylab yurmaydi — hamma qadamga bir vaqtda qaraydi.

Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar