9.1 Darbas su sekomis

Iki šiol daugiausia dėmesio skyrėme modeliams, kurių įvestis sudarė vienas požymių vektorius x∈Rd\mathbf{x} \in \mathbb{R}^d. Pagrindinis perspektyvos pasikeitimas kuriant modelius, gebančius apdoroti sekas, yra tas, kad dabar sutelkiame dėmesį į įvestis, sudarytas iš sutvarkyto požymių vektorių sąrašo x1,…,xT\mathbf{x}_1, \dots, \mathbf{x}_T, kur kiekvienas požymių vektorius xt\mathbf{x}_t indeksuojamas laiko žingsniu t∈Z+t \in \mathbb{Z}^+ ir yra erdvėje Rd\mathbb{R}^d.

Kai kuriuos duomenų rinkinius sudaro viena milžiniška seka. Pavyzdžiui, apsvarstykite itin ilgus jutiklių rodmenų srautus, kurie gali būti prieinami klimato mokslininkams. Tokiais atvejais mokymo duomenų rinkinius galime sukurti atsitiktinai imdami iš anksto nustatyto ilgio posekius. Dažniau mūsų duomenys pateikiami kaip sekų rinkinys. Panagrinėkite šiuos pavyzdžius: (i) dokumentų rinkinys, kurių kiekvienas pavaizduotas kaip sava žodžių seka ir turi savo ilgį TiT_i; (ii) pacientų buvimo ligoninėje sekos atvaizdas, kur kiekvieną buvimą sudaro keli įvykiai, o sekos ilgis maždaug priklauso nuo buvimo trukmės.

Anksčiau, dirbdami su atskiromis įvestimis, darėme prielaidą, kad jos buvo atrinktos nepriklausomai iš to paties pagrindinio skirstinio P(X)P(X). Nors vis dar darome prielaidą, kad ištisos sekos (pvz., ištisi dokumentai ar pacientų trajektorijos) atrenkamos nepriklausomai, negalime daryti prielaidos, kad duomenys, gaunami kiekvienu laiko žingsniu, yra nepriklausomi vienas nuo kito. Pavyzdžiui, žodžiai, kurie tikėtina pasirodys vėliau dokumente, labai priklauso nuo žodžių, pasirodžiusių anksčiau dokumente. Vaistai, kuriuos pacientas greičiausiai gaus 10-ąją buvimo ligoninėje dieną, labai priklauso nuo to, kas įvyko per ankstesnes devynias dienas.

Tai neturėtų stebinti. Jei netikėtume, kad sekos elementai yra susiję, nė nesivargintume jų modeliuoti kaip sekos. Apsvarstykite automatinio užbaigimo funkcijų, populiarių paieškos įrankiuose ir šiuolaikiniuose el. pašto klientuose, naudingumą. Jos naudingos būtent todėl, kad dažnai įmanoma nuspėti (netobulai, bet geriau nei atsitiktinai), kokie gali būti tikėtini sekos tęsiniai, turint pradinį priešdėlį. Daugeliui sekų modelių nereikalaujame sekų nepriklausomumo ar net stacionarumo. Vietoj to reikalaujame tik, kad pačios sekos būtų atrenkamos iš tam tikro fiksuoto pagrindinio skirstinio ištisoms sekoms.

Šis lankstus požiūris leidžia tokius reiškinius kaip (i) dokumentai, kurių pradžia gerokai skiriasi nuo pabaigos; arba (ii) paciento būklė, besikeičianti link pasveikimo arba link mirties buvimo ligoninėje metu; arba (iii) klientų skonis, nuspėjamai besikeičiantis nuolat sąveikaujant su rekomendavimo sistema.

Kartais norime prognozuoti fiksuotą tikslą yy, turėdami nuosekliai struktūrizuotą įvestį (pvz., nuotaikų klasifikavimas pagal filmo apžvalgą). Kitais atvejais norime prognozuoti nuosekliai struktūrizuotą tikslą (y1,…,yTy_1, \ldots, y_T), turėdami fiksuotą įvestį (pvz., vaizdų aprašų generavimas). Dar kitais atvejais mūsų tikslas yra prognozuoti nuosekliai struktūrizuotus tikslus pagal nuosekliai struktūrizuotas įvestis (pvz., mašininis vertimas ar vaizdo įrašų aprašų generavimas). Tokios sekos į seką užduotys turi dvi formas: (i) sulygiuotos: kai įvestis kiekvienu laiko žingsniu sulygiuota su atitinkamu tikslu (pvz., kalbos dalių žymėjimas); (ii) nesulygiuotos: kai įvestis ir tikslas nebūtinai rodo atitikimą žingsnis į žingsnį (pvz., mašininis vertimas).

Prieš rūpindamiesi bet kokių tikslų apdorojimu, galime spręsti paprasčiausią uždavinį: neprižiūrimąjį tankio modeliavimą (dar vadinamą sekų modeliavimu). Čia, turint sekų rinkinį, mūsų tikslas yra įvertinti tikimybių masės funkciją, kuri parodo, kiek tikėtina matyti bet kurią duotą seką, t. y. p(x1,…,xT)p(\mathbf{x}_1, \ldots, \mathbf{x}_T).

%matplotlib inline
from d2l import mxnet as d2l
from mxnet import autograd, np, npx, gluon, init
from mxnet.gluon import nn
npx.set_np()
%matplotlib inline
from d2l import torch as d2l
import torch
from torch import nn
%matplotlib inline
from d2l import tensorflow as d2l
import tensorflow as tf
%matplotlib inline
from d2l import jax as d2l
import jax
from jax import numpy as jnp
import numpy as np

9.1.1 Autoregresiniai modeliai

Prieš pristatydami specializuotus neuroninius tinklus, skirtus nuosekliai struktūrizuotiems duomenims apdoroti, pažvelkime į tikrus sekų duomenis ir susikurkime keletą pagrindinių intuicijų bei statistinių įrankių. Visų pirma sutelksime dėmesį į akcijų kainų duomenis iš FTSE 100 indekso (9.1.1 pav.). Kiekvienu laiko žingsniu t∈Z+t \in \mathbb{Z}^+ stebime indekso kainą xtx_t tuo metu.

FTSE 100 indeksas per maždaug 30 metų.

9.1.1 pav. FTSE 100 indeksas per maždaug 30 metų.

Tarkime, kad prekiautojas nori sudaryti trumpalaikius sandorius, strategiškai įeidamas į indeksą ar iš jo išeidamas, priklausomai nuo to, ar mano, kad jis kils ar kris kitu laiko žingsniu. Neturint jokių kitų požymių (naujienų, finansinės atskaitomybės duomenų ir pan.), vienintelis prieinamas signalas kitai reikšmei prognozuoti yra iki šiol buvusių kainų istorija. Todėl prekiautojui įdomu žinoti tikimybių skirstinį

P(xt∣xt−1,…,x1)P(x_t \mid x_{t-1}, \ldots, x_1)

kainų, kurias indeksas gali įgyti kitu laiko žingsniu. Nors visą skirstinį tolydžiai kintančio atsitiktinio dydžio įvertinti gali būti sunku, prekiautojas mielai sutelktų dėmesį į kelias pagrindines skirstinio statistikas, ypač į laukiamąją reikšmę ir dispersiją. Viena paprasta sąlyginės vidurkio įvertinimo strategija

E[(xt∣xt−1,…,x1)],\mathbb{E}[(x_t \mid x_{t-1}, \ldots, x_1)],

būtų pritaikyti tiesinės regresijos modelį (prisiminkite 3.1 skyrelis). Tokie modeliai, regresuojantys signalo reikšmę pagal ankstesnes to paties signalo reikšmes, natūraliai vadinami autoregresiniais modeliais. Yra tik viena didelė problema: įvesčių skaičius xt−1,…,x1x_{t-1}, \ldots, x_1 kinta priklausomai nuo tt. Kitaip tariant, įvesčių skaičius didėja didėjant sutiktų duomenų kiekiui. Todėl jei norime savo istorinius duomenis traktuoti kaip mokymo rinkinį, lieka problema, kad kiekvienas pavyzdys turi skirtingą požymių skaičių. Didelė dalis to, kas bus aptarta šiame skyriuje, sukasi apie technikas, skirtas įveikti šiuos iššūkius sprendžiant tokius autoregresinio modeliavimo uždavinius, kur dominantis objektas yra P(xt∣xt−1,…,x1)P(x_t \mid x_{t-1}, \ldots, x_1) arba kokia nors šio skirstinio statistika (statistikos).

Kelios strategijos kartojasi dažnai. Pirma, galime manyti, kad nors ilgos sekos xt−1,…,x1x_{t-1}, \ldots, x_1 yra prieinamos, gali būti nebūtina žvelgti taip toli atgal į istoriją, prognozuojant artimą ateitį. Šiuo atveju galime tenkintis sąlygojimu pagal tam tikrą τ\tau ilgio langą ir naudoti tik stebėjimus xt−1,…,xt−τx_{t-1}, \ldots, x_{t-\tau}. Tiesioginė nauda ta, kad dabar argumentų skaičius visada yra toks pat, bent jau kai t>τt > \tau. Tai leidžia apmokyti bet kokį tiesinį modelį ar gilųjį tinklą, kuriam reikia fiksuoto ilgio vektorių kaip įvesčių. Antra, galime kurti modelius, kurie palaiko tam tikrą ankstesnių stebėjimų santrauką hth_t (žr. 9.1.2 pav.) ir tuo pat metu atnaujina hth_t be prognozės x^t\hat{x}_t. Tai veda prie modelių, kurie vertina ne tik xtx_t pagal x^t=P(xt∣ht)\hat{x}_t = P(x_t \mid h_{t}), bet ir atnaujinimus pavidalo ht=g(ht−1,xt−1)h_t = g(h_{t-1}, x_{t-1}). Kadangi hth_t niekada nestebimas, šie modeliai taip pat vadinami latentiniais autoregresiniais modeliais.

Latentinis autoregresinis modelis.

9.1.2 pav. Latentinis autoregresinis modelis.

Norint sukonstruoti mokymo duomenis iš istorinių duomenų, paprastai pavyzdžiai kuriami atsitiktinai imant langus. Apskritai nesitikime, kad laikas sustos. Tačiau dažnai darome prielaidą, kad nors konkrečios xtx_t reikšmės gali keistis, dinamika, pagal kurią generuojamas kiekvienas vėlesnis stebėjimas, žinant ankstesnius stebėjimus, nesikeičia. Statistikai dinamiką, kuri nesikeičia, vadina stacionaria.

9.1.2 Sekų modeliai

Kartais, ypač dirbant su kalba, norime įvertinti visos sekos jungtinę tikimybę. Tai įprasta užduotis dirbant su sekomis, sudarytomis iš diskrečių teksto vienetų, pavyzdžiui, žodžių. Apskritai šios įvertintos funkcijos vadinamos sekų modeliais, o natūraliosios kalbos duomenims – kalbos modeliais. Sekų modeliavimo sritį taip stipriai skatino natūraliosios kalbos apdorojimas, kad sekų modelius dažnai vadiname „kalbos modeliais“, net kai dirbame su ne kalbos duomenimis. Kalbos modeliai pasirodo naudingi įvairiais tikslais. Kartais norime įvertinti sakinių tikėtinumą. Pavyzdžiui, galime norėti palyginti dviejų kandidatų, sugeneruotų mašininio vertimo sistemos ar kalbos atpažinimo sistemos, natūralumą. Tačiau kalbos modeliavimas suteikia mums ne tik gebėjimą įvertinti tikėtinumą, bet ir gebėjimą imti imtis sekų ir netgi optimizuoti pagal labiausiai tikėtinas sekas.

Nors kalbos modeliavimas iš pirmo žvilgsnio gali neatrodyti kaip autoregresinis uždavinys, kalbos modeliavimą galime suvesti į autoregresinę prognozę, išskaidydami sekos jungtinį tankį p(x1,…,xT)p(x_1, \ldots, x_T) į sąlyginių tankių sandaugą iš kairės į dešinę, taikydami tikimybių grandinės taisyklę:

P(x1,…,xT)=P(x1)∏t=2TP(xt∣xt−1,…,x1).P(x_1, \ldots, x_T) = P(x_1) \prod_{t=2}^T P(x_t \mid x_{t-1}, \ldots, x_1).

Atkreipkite dėmesį, kad jei dirbame su diskrečiais signalais, tokiais kaip žodžiai, autoregresinis modelis turi būti tikimybinis klasifikatorius, išvedantis pilną tikimybių skirstinį žodyno žodžiams, kurie ateis toliau, žinant kairiojo konteksto informaciją.

9.1.2.1 Markovo modeliai

Dabar tarkime, kad norime pritaikyti aukščiau minėtą strategiją, kai sąlygojame tik pagal τ\tau ankstesnių laiko žingsnių, t. y. xt−1,…,xt−τx_{t-1}, \ldots, x_{t-\tau}, o ne visą sekos istoriją xt−1,…,x1x_{t-1}, \ldots, x_1. Kai tik galime atmesti istoriją, einančią už ankstesnių τ\tau žingsnių, neprarasdami prognozavimo galios, sakome, kad seka tenkina Markovo sąlygą, t. y. kad ateitis yra sąlyginai nepriklausoma nuo praeities, žinant neseną istoriją. Kai τ=1\tau = 1, sakome, kad duomenis apibūdina pirmosios eilės Markovo modelis, o kai τ=k\tau = k, sakome, kad duomenis apibūdina kk-osios eilės Markovo modelis. Kai galioja pirmosios eilės Markovo sąlyga (τ=1\tau = 1), mūsų jungtinės tikimybės faktorizacija tampa kiekvieno žodžio tikimybių, žinant ankstesnį žodį, sandauga:

P(x1,…,xT)=P(x1)∏t=2TP(xt∣xt−1).P(x_1, \ldots, x_T) = P(x_1) \prod_{t=2}^T P(x_t \mid x_{t-1}).

Dažnai pravartu dirbti su modeliais, kurie veikia taip, tarsi Markovo sąlyga būtų tenkinama, net kai žinome, kad tai tik apytiksliai tiesa. Su tikrais tekstiniais dokumentais vis gauname informacijos, įtraukdami vis daugiau kairiojo konteksto. Tačiau šie laimėjimai sparčiai mažėja. Todėl kartais darome kompromisą, išvengdami skaičiavimo ir statistinių sunkumų apmokydami modelius, kurių pagrįstumas priklauso nuo kk-osios eilės Markovo sąlygos. Net šiandieniniai milžiniški RNN ir transformeriais pagrįsti kalbos modeliai retai įtraukia daugiau nei tūkstančius žodžių konteksto.

Su diskrečiais duomenimis tikras Markovo modelis tiesiog skaičiuoja, kiek kartų kiekvienas žodis pasirodė kiekviename kontekste, taip gaudamas santykinio dažnio įvertį P(xt∣xt−1)P(x_t \mid x_{t-1}). Kai tik duomenys įgyja tik diskrečias reikšmes (kaip kalboje), labiausiai tikėtiną žodžių seką galima efektyviai apskaičiuoti naudojant dinaminį programavimą.

9.1.2.2 Dekodavimo tvarka

Galbūt stebitės, kodėl teksto sekos P(x1,…,xT)P(x_1, \ldots, x_T) faktorizaciją pavaizdavome kaip sąlyginių tikimybių grandinę iš kairės į dešinę. Kodėl ne iš dešinės į kairę ar kita, iš pažiūros atsitiktine tvarka? Iš principo nėra nieko blogo išvyniojant P(x1,…,xT)P(x_1, \ldots, x_T) atvirkštine tvarka. Rezultatas yra galiojanti faktorizacija:

P(x1,…,xT)=P(xT)∏t=T−11P(xt∣xt+1,…,xT).P(x_1, \ldots, x_T) = P(x_T) \prod_{t=T-1}^1 P(x_t \mid x_{t+1}, \ldots, x_T).

Tačiau yra daug priežasčių, kodėl teksto faktorizavimas ta pačia kryptimi, kuria jį skaitome (iš kairės į dešinę daugumai kalbų, bet iš dešinės į kairę arabų ir hebrajų kalboms), yra pageidautinas kalbos modeliavimo užduočiai. Pirma, tai tiesiog mums natūralesnė mąstymo kryptis. Juk visi kasdien skaitome tekstą, ir šį procesą lemia mūsų gebėjimas numatyti, kurie žodžiai ir frazės greičiausiai ateis toliau. Pagalvokite, kiek kartų esate pabaigę kito žmogaus sakinį. Taigi, net jei neturėtume jokios kitos priežasties teikti pirmenybę tokiems tvarkingiems dekodavimams, jie būtų naudingi bent jau todėl, kad turime geresnes intuicijas, kas turėtų būti tikėtina, prognozuojant šia tvarka.

Antra, faktorizuodami iš eilės, galime priskirti tikimybes savavališkai ilgoms sekoms naudodami tą patį kalbos modelį. Norėdami tikimybę žingsniams nuo 11 iki tt paversti tikimybe, besitęsiančia iki žodžio t+1t+1, tiesiog padauginame iš papildomo teksto vieneto sąlyginės tikimybės, žinant ankstesnius: P(xt+1,…,x1)=P(xt,…,x1)⋅P(xt+1∣xt,…,x1)P(x_{t+1}, \ldots, x_1) = P(x_{t}, \ldots, x_1) \cdot P(x_{t+1} \mid x_{t}, \ldots, x_1).

Trečia, turime stipresnius prognozavimo modelius gretimiems žodžiams prognozuoti nei žodžiams savavališkose kitose vietose. Nors visos faktorizavimo tvarkos yra galiojančios, jos nebūtinai visos atitinka vienodai lengvus prognozavimo modeliavimo uždavinius. Tai teisinga ne tik kalbai, bet ir kitokiems duomenims, pvz., kai duomenys yra priežastiškai struktūrizuoti. Pavyzdžiui, manome, kad ateities įvykiai negali paveikti praeities. Todėl, jei pakeisime xtx_t, galbūt galėsime paveikti, kas vyks su xt+1x_{t+1} toliau, bet ne atvirkščiai. Tai yra, jei pakeisime xtx_t, praeities įvykių skirstinys nepasikeis. Kai kuriuose kontekstuose dėl to lengviau prognozuoti P(xt+1∣xt)P(x_{t+1} \mid x_t) nei P(xt∣xt+1)P(x_t \mid x_{t+1}). Pavyzdžiui, kai kuriais atvejais galime rasti xt+1=f(xt)+ϵx_{t+1} = f(x_t) + \epsilon su tam tikru adityviuoju triukšmu ϵ\epsilon, o atvirkščiai tai nėra tiesa (Hoyer ir kt., 2009). Tai puiki žinia, nes paprastai mus domina būtent tiesioginės krypties vertinimas. Daugiau šia tema pateikia (Peters ir kt., 2017) knyga. Mes vos paliečiame jos paviršių.

9.1.3 Mokymas

Prieš sutelkdami dėmesį į tekstinius duomenis, pirmiausia išbandykime tai su tolydžių reikšmių sintetiniais duomenimis.

(Čia mūsų 1000 sintetinių duomenų sekos trigonometrinę funkciją sin, pritaikytą 0,01 karto didesniam laiko žingsniui. Kad uždavinys būtų šiek tiek įdomesnis, kiekvieną imtį sugadiname adityviuoju triukšmu.) Iš šios sekos išskiriame mokymo pavyzdžius, kurių kiekvieną sudaro požymiai ir žymuo.

class Data(d2l.DataModule):
    def __init__(self, batch_size=16, T=1000, num_train=600, tau=4):
        self.save_hyperparameters()
        self.time = d2l.arange(1, T + 1, dtype=d2l.float32)
        if tab.selected('mxnet', 'pytorch'):
            self.x = d2l.sin(0.01 * self.time) + d2l.randn(T) * 0.2
        if tab.selected('tensorflow'):
            self.x = d2l.sin(0.01 * self.time) + d2l.normal([T]) * 0.2
        if tab.selected('jax'):
            key = d2l.get_key()
            self.x = d2l.sin(0.01 * self.time) + jax.random.normal(key,
                                                                   [T]) * 0.2
data = Data()
d2l.plot(data.time, data.x, 'time', 'x', xlim=[1, 1000], figsize=(6, 3))

Pradėkime nuo modelio, kuris veikia taip, tarsi duomenys tenkintų τ\tau-osios eilės Markovo sąlygą, ir todėl prognozuoja xtx_t naudodamas tik ankstesnius τ\tau stebėjimų. Taigi kiekvienam laiko žingsniui turime pavyzdį su žymeniu y=xty = x_t ir požymiais xt=[xt−τ,…,xt−1]\mathbf{x}_t = [x_{t-\tau}, \ldots, x_{t-1}]. Įžvalgus skaitytojas galėjo pastebėti, kad taip gaunama 1000−τ1000-\tau pavyzdžių, nes trūksta pakankamos istorijos y1,…,yτy_1, \ldots, y_\tau. Nors galėtume pirmąsias τ\tau sekų papildyti nuliais, paprastumo dėlei kol kas jų atsisakome. Gautame duomenų rinkinyje yra T−τT - \tau pavyzdžių, kur kiekviena modelio įvestis turi sekos ilgį τ\tau. (Sukuriame duomenų iteratorių pirmiems 600 pavyzdžių), apimantiems vieną sin funkcijos periodą.

@d2l.add_to_class(Data)
def get_dataloader(self, train):
    features = [self.x[i : self.T-self.tau+i] for i in range(self.tau)]
    self.features = d2l.stack(features, 1)
    self.labels = d2l.reshape(self.x[self.tau:], (-1, 1))
    i = slice(0, self.num_train) if train else slice(self.num_train, None)
    return self.get_tensorloader([self.features, self.labels], train, i)

Šiame pavyzdyje mūsų modelis bus standartinė tiesinė regresija.

model = d2l.LinearRegression(lr=0.01)
trainer = d2l.Trainer(max_epochs=5)
trainer.fit(model, data)

9.1.4 Prognozavimas

Kad įvertintume savo modelį, pirmiausia patikriname, kaip gerai jis prognozuoja vieną žingsnį į priekį.

onestep_preds = d2l.numpy(model(data.features))
d2l.plot(data.time[data.tau:], [data.labels, onestep_preds], 'time', 'x',
         legend=['labels', '1-step preds'], figsize=(6, 3))
onestep_preds = model.apply({'params': trainer.state.params}, data.features)
d2l.plot(data.time[data.tau:], [data.labels, onestep_preds], 'time', 'x',
         legend=['labels', '1-step preds'], figsize=(6, 3))

Šios prognozės atrodo geros, net pabaigoje, ties t=1000t=1000.

Bet ką daryti, jei stebėjome sekos duomenis tik iki 604 laiko žingsnio (n_train + tau) ir norime daryti prognozes keletą žingsnių į ateitį? Deja, negalime tiesiogiai apskaičiuoti vieno žingsnio į priekį prognozės 609 laiko žingsniui, nes nežinome atitinkamų įvesčių, matę tik iki x604x_{604}. Šią problemą galime spręsti įstatydami savo ankstesnes prognozes kaip įvestis į modelį vėlesnėms prognozėms daryti, projektuodami į priekį, po vieną žingsnį, kol pasieksime norimą laiko žingsnį:

x^605=f(x601,x602,x603,x604),x^606=f(x602,x603,x604,x^605),x^607=f(x603,x604,x^605,x^606),x^608=f(x604,x^605,x^606,x^607),x^609=f(x^605,x^606,x^607,x^608),⋮\begin{aligned} \hat{x}_{605} &= f(x_{601}, x_{602}, x_{603}, x_{604}), \\ \hat{x}_{606} &= f(x_{602}, x_{603}, x_{604}, \hat{x}_{605}), \\ \hat{x}_{607} &= f(x_{603}, x_{604}, \hat{x}_{605}, \hat{x}_{606}),\\ \hat{x}_{608} &= f(x_{604}, \hat{x}_{605}, \hat{x}_{606}, \hat{x}_{607}),\\ \hat{x}_{609} &= f(\hat{x}_{605}, \hat{x}_{606}, \hat{x}_{607}, \hat{x}_{608}),\\ &\vdots\end{aligned}

Apskritai stebėtai sekai x1,…,xtx_1, \ldots, x_t jos prognozuojama išvestis x^t+k\hat{x}_{t+k} laiko žingsniu t+kt+k vadinama kk-žingsnių į priekį prognoze. Kadangi stebėjome iki x604x_{604}, jos kk-žingsnių į priekį prognozė yra x^604+k\hat{x}_{604+k}. Kitaip tariant, turėsime toliau naudoti savo pačių prognozes, kad darytume kelių žingsnių į priekį prognozes. Pažiūrėkime, kaip tai pavyksta.

multistep_preds = d2l.zeros(data.T)
multistep_preds[:] = data.x
for i in range(data.num_train + data.tau, data.T):
    multistep_preds[i] = model(
        d2l.reshape(multistep_preds[i-data.tau : i], (1, -1)))
multistep_preds = d2l.numpy(multistep_preds)
multistep_preds = tf.Variable(d2l.zeros(data.T))
multistep_preds[:].assign(data.x)
for i in range(data.num_train + data.tau, data.T):
    multistep_preds[i].assign(d2l.reshape(model(
        d2l.reshape(multistep_preds[i-data.tau : i], (1, -1))), ()))
multistep_preds = d2l.zeros(data.T)
multistep_preds = multistep_preds.at[:].set(data.x)
for i in range(data.num_train + data.tau, data.T):
    pred = model.apply({'params': trainer.state.params},
                       d2l.reshape(multistep_preds[i-data.tau : i], (1, -1)))
    multistep_preds = multistep_preds.at[i].set(pred.item())
d2l.plot([data.time[data.tau:], data.time[data.num_train+data.tau:]],
         [onestep_preds, multistep_preds[data.num_train+data.tau:]], 'time',
         'x', legend=['1-step preds', 'multistep preds'], figsize=(6, 3))

Deja, šiuo atveju patiriame spektakuliarią nesėkmę. Prognozės gana greitai po kelių žingsnių nusilpsta iki konstantos. Kodėl algoritmas veikė daug prasčiau, prognozuodamas toliau į ateitį? Galiausiai tai lemia tai, kad klaidos kaupiasi. Tarkime, kad po 1 žingsnio turime tam tikrą paklaidą ϵ1=ϵˉ\epsilon_1 = \bar\epsilon. Dabar 2 žingsnio įvestis sutrikdoma ϵ1\epsilon_1, todėl patiriame tam tikrą paklaidą, kurios eilė ϵ2=ϵˉ+cϵ1\epsilon_2 = \bar\epsilon + c \epsilon_1 su tam tikra konstanta cc, ir taip toliau. Prognozės gali sparčiai nukrypti nuo tikrųjų stebėjimų. Galbūt jau esate susipažinę su šiuo įprastu reiškiniu. Pavyzdžiui, orų prognozės artimiausioms 24 valandoms paprastai būna gana tikslios, bet toliau tikslumas sparčiai mažėja. Metodus, kaip tai pagerinti, aptarsime visame šiame skyriuje ir už jo ribų.

Panagrinėkime išsamiau sunkumus kk-žingsnių į priekį prognozėse, apskaičiuodami prognozes visai sekai, kai k=1,4,16,64k = 1, 4, 16, 64.

def k_step_pred(k):
    features = []
    for i in range(data.tau):
        features.append(data.x[i : i+data.T-data.tau-k+1])
    # The (i+tau)-th element stores the (i+1)-step-ahead predictions
    for i in range(k):
        preds = model(d2l.stack(features[i : i+data.tau], 1))
        features.append(d2l.reshape(preds, -1))
    return features[data.tau:]
def k_step_pred(k):
    features = []
    for i in range(data.tau):
        features.append(data.x[i : i+data.T-data.tau-k+1])
    # The (i+tau)-th element stores the (i+1)-step-ahead predictions
    for i in range(k):
        preds = model.apply({'params': trainer.state.params},
                            d2l.stack(features[i : i+data.tau], 1))
        features.append(d2l.reshape(preds, -1))
    return features[data.tau:]
steps = (1, 4, 16, 64)
preds = k_step_pred(steps[-1])
d2l.plot(data.time[data.tau+steps[-1]-1:],
         [d2l.numpy(preds[k-1]) for k in steps], 'time', 'x',
         legend=[f'{k}-step preds' for k in steps], figsize=(6, 3))

Tai aiškiai iliustruoja, kaip kinta prognozės kokybė, kai bandome prognozuoti toliau į ateitį. Nors 4 žingsnių į priekį prognozės vis dar atrodo geros, bet kas už jų yra beveik nenaudinga.

9.1.5 Santrauka

Tarp interpoliacijos ir ekstrapoliacijos sudėtingumo yra nemažas skirtumas. Todėl, jei turite seką, visada gerbkite duomenų laiko tvarką apmokydami, t. y. niekada neapmokykite naudodami ateities duomenis. Turint tokius duomenis, sekų modeliams vertinti reikia specializuotų statistinių įrankių. Du populiarūs pasirinkimai yra autoregresiniai modeliai ir latentinių kintamųjų autoregresiniai modeliai. Priežastiniams modeliams (pvz., laikas eina pirmyn) tiesioginės krypties vertinimas paprastai yra daug lengvesnis nei atvirkštinės krypties. Stebėtai sekai iki laiko žingsnio tt jos prognozuojama išvestis laiko žingsniu t+kt+k yra kk-žingsnių į priekį prognozė. Prognozuojant toliau laike, didinant kk, klaidos kaupiasi, o prognozės kokybė prastėja, dažnai dramatiškai.

9.1.6 Pratimai

  1. Patobulinkite šio skyrelio eksperimento modelį.
    1. Įtraukite daugiau nei ankstesnius keturis stebėjimus? Kiek jų iš tikrųjų reikia?
    2. Kiek ankstesnių stebėjimų reikėtų, jei nebūtų triukšmo? Užuomina: sin⁡\sin ir cos⁡\cos galite užrašyti kaip diferencialinę lygtį.
    3. Ar galite įtraukti senesnius stebėjimus, išlaikydami bendrą požymių skaičių pastovų? Ar tai pagerina tikslumą? Kodėl?
    4. Pakeiskite neuroninio tinklo architektūrą ir įvertinkite našumą. Naują modelį galite apmokyti per daugiau epochų. Ką pastebite?
  2. Investuotojas nori rasti gerą vertybinį popierių pirkti. Jis žiūri į ankstesnes grąžas, kad nuspręstų, kuris greičiausiai pasiseks. Kas gali nutikti blogai su šia strategija?
  3. Ar priežastiškumas taip pat taikomas tekstui? Kiek?
  4. Pateikite pavyzdį, kada latentinio autoregresinio modelio gali prireikti duomenų dinamikai užfiksuoti.

Diskusijos

Diskusijos

Diskusijos

Diskusijos