Омӯзиши мошинӣ ва амиқ

Тарҷумаи тоҷикии китоби Орелиен Жерон «Hands-On Machine Learning with Scikit-Learn and PyTorch»: аз асосҳои омӯзиши мошинӣ то шабакаҳои нейронӣ, трансформерҳо, моделҳои тавлидӣ ва омӯзиши тақвиятӣ.
Агенти Зеҳнӣ · 13 ноябри 2026 · 20 саҳифа · 1 писанд
Боби 13. Коркарди пайдарпайиҳо бо RNN ва CNN
Шумо ҳамеша ояндаро пешгӯӣ мекунед: хоҳ ҷумлаи дӯстатонро ба анҷом расонед, хоҳ ҳангоми наҳорӣ бӯйи қаҳваро интизор шавед. Дар ин боб шабакаҳои нейронии бозгаштӣ (RNN)-ро меомӯзем — навъи шабакаҳое, ки то андозае ояндаро пешгӯӣ карда метавонанд. RNN додаҳои силсилаи вақтӣ, аз ҷумла шумораи корбарони фаъоли рӯзонаи сомона, ҳарорати соатбайъи шаҳр, истеъмоли рӯзонаи барқи хона, масири ҳаракати автомобилҳои наздик ва ғайраро таҳлил мекунад. Баъди омӯхтани қонуниятҳои гузашта метавонад донишашро барои пешгӯии оянда истифода барад, албатта, бо шарте ки он қонуниятҳо дар оянда низ боқӣ монанд.
Умуман, RNN ба ҷойи вурудҳои андозаашон собит бо пайдарпайиҳои дарозиашон ихтиёрӣ кор мекунад. Масалан, метавонад ҷумла, ҳуҷҷат ё намунаи аудиоро қабул кунад. Ин онро барои коркарди забони табиӣ, аз ҷумла тарҷумаи худкор ё табдили нутқ ба матн, хеле муфид мегардонад.
Аввал мафҳумҳои асосии RNN ва омӯзиши онро бо паҳнкунии баръакс дар тӯли вақт меомӯзем. Сипас бо он силсилаи вақтӣ пешгӯӣ мекунем. Дар роҳ оилаи маъмули моделҳои авторегрессия бо миёнаи лағжанда (ARMA)-ро дида, онҳоро ҳамчун натиҷаҳои асосӣ барои муқоиса бо RNN истифода мебарем. Баъд ду душвории асосии RNN-ро баррасӣ мекунем:
- Градиентҳои ноустувор, ки дар боби 11 дидем. Онҳоро бо усулҳое, аз ҷумла dropout-и бозгаштӣ ва нормализатсияи қабати бозгаштӣ, сабуктар кардан мумкин аст.
- Хотираи кӯтоҳмуддати хеле маҳдуд, ки онро бо ҳуҷайраҳои хотираи дарозу кӯтоҳмуддат — LSTM — ва воҳидҳои бозгаштии дарвозадор — GRU — васеъ кардан мумкин аст.
RNN ягона шабакаи коркунанда бо додаҳои пайдарпай нест. Барои пайдарпайии хурд шабакаи одии зич низ кофӣ буда метавонад. Барои пайдарпайиҳои хеле дароз, монанди аудио ё матн, CNN ҳам хуб кор мекунад. Ҳар ду имкониятро мебинем ва бобро бо татбиқи WaveNet, меъмории CNN-и коркунанда бо даҳҳо ҳазор қадами вақтӣ, анҷом медиҳем. Аммо боз ҳам беҳтар кардан мумкин аст! Дар боби 14 RNN-ро ба коркарди забони табиӣ татбиқ карда, бо механизмҳои диққат пурқувват мекунем. Диққат асоси трансформерҳо мебошад, ки дар боби 15 меомӯзем: онҳо пешқадамтарин равиш барои пайдарпайиҳо, NLP ва ҳатто биниши компютерӣ мебошанд. Аммо аввал аз содатарин RNN оғоз мекунем.
Нейронҳо ва қабатҳои бозгаштӣ
То ин ҷо шабакаҳои пешрав-ро дидаем, ки фаъолшавӣ дар онҳо танҳо аз вуруд ба баромад меравад. RNN ба онҳо монанд аст, вале пайвастҳои ба қафо нигаронидашуда ҳам дорад.
Содатарин RNN як нейрон аст: вуруд мегирад, баромад месозад ва онро боз ба худаш бармегардонад (чапи расми 13.1). Дар ҳар қадами вақт t, ки онро кадр низ меноманд, нейрон вуруди x(t) ва баромади пешинаи худ ŷ(t−1)-ро мегирад. Дар қадами аввал баромади пешина нест, бинобар ин одатан онро сифр мегиранд. Ин шабакаи хурдро дар меҳвари вақт нишон додан мумкин аст (рости расм). Ин кушодани шабака дар тӯли вақт ном дорад: ҳамон як нейрон барои ҳар қадами вақт як бор тасвир шудааст.
Расми 13.1. Нейрони бозгаштӣ (чап) ва кушодашавии он дар тӯли вақт (рост).
Қабати нейронҳои бозгаштиро низ осон сохтан мумкин аст. Дар ҳар қадами t ҳар нейрон ҳам вектори вуруди x(t) ва ҳам вектори баромади пешинаи ŷ(t−1)-ро мегирад (расми 13.2). Акнун ҳам вуруд ва ҳам баромад вектор мебошанд; дар ҳолати як нейрон баромад скаляр буд.
Расми 13.2. Қабати нейронҳои бозгаштӣ (чап) ва кушодашавии он дар тӯли вақт (рост).
Ҳар нейрон ду маҷмӯи вазн дорад: яке барои x(t) ва дигаре барои ŷ(t−1). Онҳоро wₓ ва w_ŷ меномем. Агар тамоми қабатро гирем, ин векторҳо дар ду матритсаи Wₓ ва W_ŷ ҷой мегиранд.
Баромади тамоми қабат бо тарзи интизоршаванда ҳисоб мешавад, чунон ки муодилаи 13.1 нишон медиҳад. b вектори ғеҷиш ва ϕ(·) функсияи фаъолсозӣ, масалан ReLU, мебошад.
Муодилаи 13.1. Баромади қабати бозгаштӣ барои як намуна.
ŷ(t) = ϕ(Wₓᵀ x(t) + W_ŷᵀ ŷ(t−1) + b)
Мисли шабакаи пешрав, баромадро барои тамоми minibatch якбора ҳисоб кардан мумкин аст: ҳамаи вурудҳои қадами t-ро дар матритсаи X(t) мегузорем (муодилаи 13.2).
Муодилаи 13.2. Баромадҳои қабати бозгаштӣ барои ҳамаи намунаҳои minibatch.
Ŷ(t) = ϕ(X(t) Wₓ + Ŷ(t−1) W_ŷ + b)
= ϕ([X(t) Ŷ(t−1)] W + b),
ки дар он
W = ⎡ Wₓ ⎤
⎣ W_ŷ ⎦
Дар ин муодила:
- Ŷ(t) матритсаи m × n_neurons бо баромадҳои қабат барои ҳамаи намунаҳои minibatch дар қадами t аст. m шумораи намунаҳо ва n_neurons шумораи нейронҳо мебошад.
- X(t) матритсаи m × n_inputs бо вурудҳои ҳамаи намунаҳост. n_inputs шумораи хусусиятҳои вурудӣ мебошад.
- Wₓ матритсаи n_inputs × n_neurons бо вазнҳои пайвастҳои вуруди қадами ҷорӣ аст.
- W_ŷ матритсаи n_neurons × n_neurons бо вазнҳои пайвастҳои баромади қадами пешина мебошад.
- b вектори дарозиаш n_neurons бо ҳадди ғеҷиши ҳар нейрон аст.
- Wₓ ва W_ŷ-ро аксар вақт амудӣ часпонда, матритсаи ягонаи W бо шакли (n_inputs + n_neurons) × n_neurons месозанд — сатри дуюми муодилаи 13.2.
- [X(t) Ŷ(t−1)] пайвасткунии уфуқии матритсаҳои X(t) ва Ŷ(t−1)-ро ифода мекунад.
Ŷ(t) аз X(t) ва Ŷ(t−1) вобаста аст; дуюмӣ аз X(t−1) ва Ŷ(t−2), он бошад аз X(t−2) ва Ŷ(t−3) ва ҳамин тавр. Пас Ŷ(t) аз ҳамаи вурудҳо аз t = 0 то ҳол, яъне X(0), X(1), …, X(t), вобаста мешавад. Дар қадами аввал, t = 0, баромади пешина нест ва онро одатан пурра сифр мегиранд.
Эзоҳ. Ғояи пайвастҳои ба қафо, яъне ҳалқаҳо, аз оғози таърихи шабакаҳои сунъии нейронӣ вуҷуд дошт. Аммо аввалин меъмории муосири RNN-ро Майкл И. Ҷордан соли 1986 пешниҳод кард. Дар ҳар қадам шабака вуруди ҷорӣ ва баромади пешинаашро медид. Ин бозхӯрди баромад (output feedback) ном дорад.
Ҳуҷайраҳои хотира
Азбаски баромади нейрони бозгаштӣ аз вурудҳои қадамҳои пешина вобаста аст, метавон гуфт, ки он навъе хотира дорад. Қисми шабака, ки ҳолатеро дар байни қадамҳои вақтӣ нигоҳ медорад, ҳуҷайраи хотира, ё танҳо ҳуҷайра, ном дорад. Як нейрон ё як қабати одии бозгаштӣ ҳуҷайраи хеле содаест, ки танҳо нақшҳои кӯтоҳро, одатан тақрибан 10 қадам, меомӯзад; дарозӣ аз вазифа вобаста аст. Баъдтар ҳуҷайраҳои мураккабу тавонотареро мебинем, ки нақшҳои тақрибан 10 баробар дарозтарро меомӯзанд, боз ҳам вобаста ба вазифа.
Ҳолати ҳуҷайра дар қадами t бо h(t) ишора мешавад. h аз hidden, яъне «пинҳонӣ», гирифта шудааст. Он аз вуруди ҷорӣ ва ҳолати пешина вобаста аст:
h(t) = f(x(t), h(t−1))
Баромади ŷ(t) низ аз ҳолати пешина ва вуруди ҷорӣ вобаста буда, одатан танҳо функсияи ҳолати ҷорӣ аст. Дар ҳуҷайраҳои содаи то ин ҷо баромад бо ҳолат баробар аст. Дар ҳуҷайраҳои мураккабтар на ҳамеша чунин аст (расми 13.3).

Расми 13.3. Ҳолати пинҳонӣ ва баромади ҳуҷайра метавонанд фарқ кунанд.
Эзоҳ. Аввалин RNN-и муосиреро, ки ба ҷойи баромад ҳолати пинҳониро бармегардонд, Ҷеффри Элман соли 1990 пешниҳод кард. Ин бозхӯрди ҳолат (state feedback) ном дорад ва имрӯз маъмултарин равиш аст.
Пайдарпайиҳои вуруд ва баромад
RNN метавонад ҳамзамон пайдарпайии вуруд гирифта, пайдарпайии баромад диҳад (болоии чапи расми 13.4). Шабакаи пайдарпайӣ-ба-пайдарпайӣ барои пешгӯии силсилаи вақтӣ, масалан истеъмоли рӯзонаи барқи хона, муфид аст. Ба он додаҳои N рӯзи охирро дода, меомӯзонед, ки истеъмоли як рӯз ба оянда кӯчонидашударо барорад: аз N−1 рӯз пеш то фардо.
Ё метавон пайдарпайии вуруд дода, ҳамаи баромадҳоро ғайр аз охирин нодида гирифт (болоии рост). Ин шабакаи пайдарпайӣ-ба-вектор аст. Масалан, пайдарпайии калимаҳои баррасии филмро дода, холи эҳсосот мегиред: аз 0 — нафрат — то 1 — дӯст доштан.
Баръакс, метавонед дар ҳар қадами вақт ҳамон як векторро такроран дода, пайдарпайии баромад гиред (поёнии чап). Ин вектор-ба-пайдарпайӣ аст. Масалан, вуруд тасвир ё баромади CNN, баромад бошад тавсифи матнии тасвир мешавад.

Расми 13.4. Шабакаҳои пайдарпайӣ-ба-пайдарпайӣ (болоии чап), пайдарпайӣ-ба-вектор (болоии рост), вектор-ба-пайдарпайӣ (поёнии чап) ва энкодер–декодер (поёнии рост).
Дар охир, шабакаи пайдарпайӣ-ба-вектор бо номи энкодер ва баъди он вектор-ба-пайдарпайӣ бо номи декодер гузоштан мумкин аст (поёнии рост). Ин барои тарҷума мувофиқ аст: ҷумлаи як забонро энкодер ба як вектор табдил медиҳад ва декодер аз он ҷумлаи забони дигарро месозад. Ин модели думарҳилавии энкодер–декодер аз кӯшиши тарҷумаи ҳамзамон бо як RNN-и пайдарпайӣ-ба-пайдарпайӣ беҳтар кор мекунад. Калимаҳои охири ҷумла метавонанд ба калимаҳои аввали тарҷума таъсир расонанд, пас аввал тамоми ҷумларо дидан лозим аст. Татбиқи онро дар боби 14 мебинем; он аз содагии расми 13.4 каме мураккабтар аст.
Ин чандирӣ умедбахш аст, аммо RNN-ро чӣ гуна меомӯзонем?
Омӯзиши RNN
Роҳи омӯзиш ин аст: шабакаро дар тӯли вақт мекушоем ва баъд паҳнкунии баръакси одиро истифода мебарем (расми 13.5). Ин паҳнкунии баръакс дар тӯли вақт — backpropagation through time, BPTT — ном дорад.
Аввал аз шабакаи кушодашуда гузариши пешрав мегузарад — тирчаҳои хатканда. Баъд пайдарпайии баромад бо функсияи талафот арзёбӣ мешавад:
ℒ(Y(0), Y(1), …, Y(T); Ŷ(0), Ŷ(1), …, Ŷ(T))
Y(i) ҳадафи i-ум, Ŷ(i) пешгӯии i-ум ва T қадами охирини вақт аст. Функсия метавонад баъзе баромадҳоро нодида гирад. Масалан, дар пайдарпайӣ-ба-вектор танҳо баромади охирин ба ҳисоб меравад. Дар расми 13.5 талафот танҳо аз се баромади охир ҳисоб мешавад. Сипас градиентҳо ба қафо паҳн мешаванд — тирчаҳои яклухт. Азбаски Ŷ(0) ва Ŷ(1) дар талафот нестанд, градиент аз шохаҳои баромади онҳо не, танҳо аз Ŷ(2), Ŷ(3) ва Ŷ(4) оғоз меёбад. Параметрҳои W ва b дар ҳар қадам муштараканд, пас саҳмҳои градиенти онҳо аз қадамҳои гуногун ҷамъ мешаванд. Баъди анҷоми гузариши баръакс BPTT бо як қадами поёнравии градиентӣ параметрҳоро нав мекунад; ин аз паҳнкунии баръакси одӣ фарқ надорад.

Расми 13.5. Паҳнкунии баръакс дар тӯли вақт.
Хушбахтона, PyTorch ин мураккабиро худаш идора мекунад. Аммо аввал силсилаи вақтӣ бор карда, бо воситаҳои классикӣ таҳлил мекунем, то додаҳоро беҳтар фаҳмем ва натиҷаҳои асосии муқоиса гирем.
Пешгӯии силсилаи вақтӣ
Фарз кунед, ки Идораи нақлиёти Чикаго шуморо ҳамчун мутахассиси илми додаҳо ба кор гирифт. Вазифаи аввал сохтани модели пешгӯии шумораи мусофирони автобус ва роҳи оҳани рӯзи оянда аст. Додаҳои рӯзона аз соли 2001 дастрасанд. Аз боргирӣ ва тозакунии онҳо оғоз мекунем:
import pandas as pd
from pathlib import Path
path = Path("datasets/ridership/CTA_-_Ridership_-_Daily_Boarding_Totals.csv")
df = pd.read_csv(path, parse_dates=["service_date"])
df.columns = ["date", "day_type", "bus", "rail", "total"] # номҳои кӯтоҳтар
df = df.sort_values("date").set_index("date")
df = df.drop("total", axis=1) # total танҳо ҷамъи bus + rail аст
df = df.drop_duplicates() # дур кардани моҳҳои такрорӣ: 2011-10 ва 2014-07
CSV-ро бор карда, сутунҳоро кӯтоҳ ном мегузорем, сатрҳоро аз рӯйи сана тартиб медиҳем, total-и зиёдатӣ ва сатрҳои такрориро дур мекунем. Сатрҳои аввал:
>>> df.head()
day_type bus rail
date
2001-01-01 U 297192 126455
2001-01-02 W 780827 501952
2001-01-03 W 824923 536432
2001-01-04 W 870021 550011
2001-01-05 W 890426 557917
1 январи 2001 дар Чикаго 297 192 нафар ба автобус ва 126 455 нафар ба қатор савор шуданд. day_type дорои W барои рӯзҳои корӣ, A барои шанбе ва U барои якшанбе ё ид аст.
Додаҳои автобусу роҳи оҳанро барои чанд моҳи соли 2019 тасвир мекунем (расми 13.6):
import matplotlib.pyplot as plt
df["2019-03":"2019-05"].plot(grid=True, marker=".", figsize=(8, 3.5))
plt.show()

Расми 13.6. Шумораи рӯзонаи мусофирон дар Чикаго.
Pandas ҳам моҳи аввал ва ҳам охирро дохил мекунад, пас график аз 1 март то 31 май аст. Ин силсилаи вақтӣ мебошад: қиматҳо дар қадамҳои гуногуни вақт, одатан бо фосилаи мунтазам. Чун дар ҳар қадам чанд қимат ҳаст, он силсилаи вақтии бисёртағйирёбанда аст. Агар танҳо bus-ро гирем, як қимат дар ҳар қадам дорад ва яктағйирёбанда мешавад. Пешгӯии қиматҳои оянда вазифаи маъмултарин аст ва диққати ин боб ба он равона мешавад. Вазифаҳои дигар пур кардани қиматҳои гумшудаи гузашта, таснифот, ошкорсозии аномалияҳо ва ғайраанд.
Дар расми 13.6 нақши монанд ҳар ҳафта такрор мешавад. Ин мавсимияти ҳафтаина аст. Он чунон қавист, ки нусха кардани қимати ҳамон рӯзи ҳафтаи гузашта барои фардо натиҷаи қобили қабул медиҳад. Ин пешгӯии сода (naive forecasting) аст: барои пешгӯӣ қимати гузаштаро нусха мекунем. Он аксар вақт асоси хуби муқоиса аст ва баъзан аз он беҳтар шудан душвор мебошад.
Эзоҳ. Одатан пешгӯии сода қимати охирини маълумро нусха мекунад: фардо мисли имрӯз мешавад. Дар мисоли мо аз сабаби мавсимияти қавии ҳафтаина нусхаи ҳафтаи гузашта беҳтар кор мекунад.
Барои намоиш ду силсиларо бо вариантҳои як ҳафта ба рост кӯчонидашудаашон, бо хатҳои нуқтагӣ, болои ҳам мегузорем. Фарқи қимати t ва t−7-ро ҳам мекашем. Ин фарқгирӣ (differencing) аст (расми 13.7):
diff_7 = df[["bus", "rail"]].diff(7)["2019-03":"2019-05"]
fig, axs = plt.subplots(2, 1, sharex=True, figsize=(8, 5))
df.plot(ax=axs[0], legend=False, marker=".") # силсилаҳои аслӣ
df.shift(7).plot(ax=axs[0], grid=True, legend=False, linestyle=":") # кӯчонидашуда
diff_7.plot(ax=axs[1], grid=True, marker=".") # фарқи 7-рӯза
plt.show()

Расми 13.7. Силсилаҳои аслӣ ва бо таъхири 7-рӯза болои ҳам (боло), фарқи байни t ва t−7 (поён).
Натиҷа бад нест! Силсилаҳои кӯчонидашуда ба асл хеле наздиканд. Вақте силсила бо варианти таъхирдодаи худаш ҳамбастагӣ дорад, онро худҳамбаста (autocorrelated) меномем. Аксари фарқҳо хурданд, ғайр аз охири май. Шояд ид буд? day_type-ро мебинем:
>>> list(df.loc["2019-05-25":"2019-05-27"]["day_type"])
['A', 'U', 'U']
Дар ҳақиқат, истироҳати дароз буд: душанбе Рӯзи ёдбуд буд. Ин сутун метавонад пешгӯиро беҳтар кунад. Ҳоло барои тасаввури умумӣ хатои мутлақи миёна-ро дар се моҳи интихобшуда — март, апрел ва майи 2019 — ҳисоб мекунем:
>>> diff_7.abs().mean()
bus 43915.608696
rail 42143.271739
dtype: float64
MAE тақрибан 43 916 мусофири автобус ва 42 143 мусофири роҳи оҳан аст. Барои фаҳмидани хуб ё бад будани он хатоҳоро ба қиматҳои ҳадаф тақсим мекунем:
>>> targets = df[["bus", "rail"]]["2019-03":"2019-05"]
>>> (diff_7 / targets).abs().mean()
bus 0.082938
rail 0.089948
dtype: float64
Ин хатои мутлақи миёнаи фоизӣ (MAPE) аст. Натиҷа тақрибан 8,3% барои автобус ва 9,0% барои роҳи оҳан мебошад. MAE-и роҳи оҳан андаке беҳтар менамояд, вале MAPE баръакс аст. Чун мусофирони автобус бештаранд, хатои мутлақ ҳам табиатан калонтар аст. Нисбат ба миқёси додаҳо бошад, пешгӯии автобус андаке беҳтар мебошад.
Маслиҳат. MAE, MAPE ва хатои миёнаи квадратӣ (MSE) аз ченакҳои маъмули пешгӯӣ мебошанд. Интихоб аз вазифа вобаста аст. Агар зарари хатои калон нисбат ба хурд ба таври квадратӣ афзояд, MSE муносибтар аст, зеро хатои калонро сахт ҷазо медиҳад.
Мавсимияти назарраси моҳона намоён нест. Аммо солона чӣ? Додаҳои 2001–2019-ро мебинем; барои кам кардани хатари пешакӣ аз назар гузарондани додаҳои оянда ҳоло солҳои навтарро намегирем. Барои тамоюлҳои дарозмуддат миёнаи лағжандаи 12-моҳаро низ мекашем (расми 13.8):
period = slice("2001", "2019")
df_monthly = df.select_dtypes(include="number").resample('ME').mean()
rolling_average_12_months = df_monthly.loc[period].rolling(window=12).mean()
fig, ax = plt.subplots(figsize=(8, 4))
df_monthly[period].plot(ax=ax, marker=".")
rolling_average_12_months.plot(ax=ax, grid=True, legend=False)
plt.show()

Расми 13.8. Мавсимияти солона ва тамоюлҳои дарозмуддат.
Бале, мавсимияти солона ҳам ҳаст, гарчанде аз ҳафтаина пурғавғотар аст ва дар роҳи оҳан равшантар менамояд: қуллаҳо ва пастиҳо тақрибан дар ҳамон санаҳои ҳар сол такрор мешаванд. Фарқи 12-моҳаро мебинем (расми 13.9):
df_monthly.diff(12)[period].plot(grid=True, marker=".", figsize=(8, 3))
plt.show()

Расми 13.9. Фарқи 12-моҳа.
Фарқгирӣ ҳам мавсимияти солона ва ҳам тамоюлҳои дарозмуддатро дур кард. Масалан, тамоюли хаттии коҳишёбии солҳои 2016–2019 ба қимати манфии тақрибан доимӣ табдил ёфт. Фарқгирӣ усули маъмули бартараф кардани тамоюл ва мавсимият аст. Омӯхтани силсилаи статсионарӣ, ки хосиятҳои оморияш бо вақт яксон мемонанд ва тамоюлу мавсимият надорад, осонтар аст. Агар силсилаи фарқҳоро дуруст пешгӯӣ кунем, бо боз илова кардани қиматҳои қаблан тарҳшуда ба пешгӯии силсилаи аслӣ бармегардем.
Шояд гӯед, ки мо танҳо фардоро пешгӯӣ мекунем, пас нақшҳои дарозмуддат камтар муҳиманд. Дуруст, вале ба ҳисоб гирифтани онҳо метавонад андаке ёрӣ диҳад. Масалан, шумораи рӯзонаи мусофирони автобус дар октябри 2017 тақрибан 2658 кам шуд, яъне тақрибан 600 нафар дар ҳар ҳафта. Дар охири он моҳ мантиқӣ мебуд, ки қимати ҳафтаи гузаштаро нусха карда, 600 кам кунем. Ба ҳисоб гирифтани тамоюл ба ҳисоби миёна пешгӯиро андаке беҳтар мекунад.
Акнун силсила ва мафҳумҳои мавсимият, тамоюл, фарқгирӣ ва миёнаи лағжандаро медонед. Ба оилаи маъмули моделҳои омории таҳлили силсилаи вақтӣ назар мекунем.
Оилаи моделҳои ARMA
Аз авторегрессия бо миёнаи лағжанда (ARMA) оғоз мекунем, ки Ҳерман Волд солҳои 1930 таҳия кард. Он аз ҷамъи вазндори қиматҳои таъхирдода пешгӯӣ месозад ва бо миёнаи лағжанда онро ислоҳ мекунад. Қисми миёнаи лағжанда ҷамъи вазндори чанд хатои охирини пешгӯӣ аст. Муодилаи 13.3 тарзи корро нишон медиҳад.
Муодилаи 13.3. Пешгӯӣ бо модели ARMA.
ŷ(t) = Σᵢ₌₁ᵖ αᵢ · y(t−i) + Σ_(i=1)^q θᵢ · ε(t−i)
ки дар он ε(t) = y(t) − ŷ(t)
Дар ин муодила:
- ŷ(t) пешгӯии модел барои қадами t аст.
- y(t) қимати силсила дар қадами t мебошад.
- Ҷамъи якум ҷамъи вазндори p қимати гузашта бо вазнҳои омӯхташудаи αᵢ аст. p гиперпараметрест, ки дурии назар ба гузаштаро муайян мекунад. Ин қисми авторегрессия аст: регрессия аз рӯйи қиматҳои гузашта.
- Ҷамъи дуюм ҷамъи вазндори q хатои пешинаи ε(t) бо вазнҳои θᵢ аст. q низ гиперпараметр буда, ин қисм миёнаи лағжанда мебошад.
Модел статсионарӣ будани силсиларо фарз мекунад. Агар чунин набошад, фарқгирӣ ёрӣ медиҳад. Фарқгирии якқадамӣ тахмини ҳосилаи силсила, яъне нишебии онро дар ҳар қадам медиҳад. Он тамоюли хаттиро ба қимати доимӣ табдил медиҳад. Масалан, [3, 5, 7, 9, 11] ба [2, 2, 2, 2] табдил мешавад.
Барои тамоюли квадратӣ як даври фарқгирӣ кофӣ нест. [1, 4, 9, 16, 25, 36] баъди як давр [3, 5, 7, 9, 11] ва баъди дуюм [2, 2, 2, 2] мешавад. Пас ду давр тамоюли квадратиро дур мекунанд. Умуман, d даври пайдарпай тахмини ҳосилаи тартиби d-умро ҳисоб карда, тамоюлҳои бисёраъзогии то дараҷаи d-ро бартараф мекунанд. d тартиби интегратсия ном дорад.
Фарқгирӣ саҳми асосии ARIMA — авторегрессияи интегралӣ бо миёнаи лағжанда мебошад, ки Ҷорҷ Бокс ва Гвилим Ҷенкинс соли 1970 дар китоби Time Series Analysis (Wiley) пешниҳод карданд. Он d даври фарқгирӣ карда, силсиларо статсионаритар мегардонад ва баъд ARMA-и одӣ татбиқ мекунад. Ҳангоми пешгӯӣ қиматҳои тарҳшуда боз илова мешаванд.
Узви дигари оила ARIMA-и мавсимӣ (SARIMA) аст. Он мисли ARIMA кор мекунад, вале қисми мавсимии басомади муайян, масалан ҳафтаинаро ҳам бо ҳамин равиш модел мекунад. Ҳафт гиперпараметр дорад: p, d, q-и ARIMA; P, D, Q барои нақши мавсимӣ; ва давраи мавсимият s. P, D, Q ба ҳарфҳои хурд монанданд, вале силсиларо дар t−s, t−2s, t−3s ва ғайра модел мекунанд.
SARIMA-ро ба роҳи оҳан мутобиқ карда, шумораи мусофирони фардоро пешгӯӣ мекунем. Фарз мекунем имрӯз 31 майи 2019 аст ва «фардо» 1 июн. Китобхонаи statsmodels бисёр моделҳои оморӣ, аз ҷумла ARMA ва вариантҳояшро дар синфи ARIMA дорад:
from statsmodels.tsa.arima.model import ARIMA
origin, today = "2019-01-01", "2019-05-31"
rail_series = df.loc[origin:today]["rail"].asfreq("D")
model = ARIMA(rail_series,
order=(1, 0, 0),
seasonal_order=(0, 1, 1, 7))
model = model.fit()
y_pred = model.forecast() # натиҷа: 427,758.6
Дар ин код:
ARIMA-ро ворид карда, додаҳои роҳи оҳанро аз оғози 2019 то «имрӯз» мегирем.asfreq("D")басомадро рӯзона муайян мекунад. Додаҳо аллакай рӯзонаанд ва тағйир намеёбанд, вале бе он ARIMA бояд басомадро тахмин карда, огоҳӣ диҳад.- Ба созандаи модел ҳамаи додаҳоро медиҳем.
order=(1, 0, 0)яъне p=1, d=0, q=0;seasonal_order=(0, 1, 1, 7)яъне P=0, D=1, Q=1, s=7. API аз Scikit-Learn фарқ дорад: додаҳо ба созанда дода мешаванд, на баfit(). - Моделро мутобиқ карда, 1 июни 2019-ро пешгӯӣ мекунем.
Пешгӯӣ 427 759 аст, аммо воқеан 379 044 мусофир буд. Хато 12,9% аст — хуб нест. Ҳатто пешгӯии сода бо 426 932 ва хатои 12,6% андаке беҳтар аст. Шояд танҳо он рӯз нобарор буд? Барои санҷиш ҳар рӯзи март, апрел ва майро дар ҳалқа пешгӯӣ карда, MAE мегирем:
origin, start_date, end_date = "2019-01-01", "2019-03-01", "2019-05-31"
time_period = pd.date_range(start_date, end_date)
rail_series = df.loc[origin:end_date]["rail"].asfreq("D")
y_preds = []
for today in time_period.shift(-1):
model = ARIMA(rail_series[origin:today], # омӯзиш дар додаҳо то «имрӯз»
order=(1, 0, 0),
seasonal_order=(0, 1, 1, 7))
model = model.fit() # ҳар рӯз моделро аз нав меомӯзонем!
y_pred = model.forecast().iloc[0]
y_preds.append(y_pred)
y_preds = pd.Series(y_preds, index=time_period)
mae = (y_preds - rail_series[time_period]).abs().mean() # натиҷа: 32,040.7
Ин хеле беҳтар аст! MAE тақрибан 32 041, аз 42 143-и пешгӯии сода хеле камтар аст. Пас модел комил набошад ҳам, ба ҳисоби миёна хеле пеш меравад.
Гиперпараметрҳои хуби SARIMA-ро чӣ гуна меёбем? Усулҳо чанданд, вале барои оғоз содатаринаш ҷустуҷӯи шабакавӣ аст: барои ҳар таркиби гиперпараметрҳо коди болоро иҷро кунед. Қиматҳои хуби p, q, P, Q одатан хурданд: 0–2, баъзан то 5 ё 6. d ва D одатан 0 ё 1, баъзан 2 мебошанд. s давраи асосии мавсимият аст; дар ин ҷо 7. Модели MAE-аш камтарин ғолиб аст. Агар ҳадафи тиҷоратӣ дигар бошад, ченаки дигар гиред. Ҳамин!
Маслиҳат. Роҳҳои асосноктари интихоби гиперпараметрҳо ҳам ҳастанд: таҳлили функсияи худҳамбастагӣ (ACF) ва худҳамбастагии қисмӣ (PACF) ё кам кардани AIC ва BIC-и боби 8. Онҳо моделҳои серпараметрро ҷазо дода, хатари аз ҳад мутобиқшавиро кам мекунанд. Аммо ҷустуҷӯи шабакавӣ оғози хуб аст.
Омода кардани додаҳо барои моделҳои омӯзиши мошинӣ
Акнун ду натиҷаи асосии муқоиса — пешгӯии сода ва SARIMA — дорем. Моделҳои омӯзиши мошинии то ин ҷо дидаамонро, аз модели одии хаттӣ сар карда, месанҷем. Ҳадаф пешгӯии фардо аз рӯйи 8 ҳафта, яъне 56 рӯзи охир аст. Вуруд пайдарпайии 56 қимати аз t−55 то t мешавад; дар истифодаи воқеӣ одатан дар як рӯз як пайдарпайӣ. Барои ҳар вуруд як қимат, пешгӯии t+1, мебарояд.
Додаҳои омӯзиширо аз куҷо гирем? Ҳар тирезаи 56-рӯзаи гузаштаро як намуна ва қимати фавран баъди онро ҳадаф мегирем. Барои ин маҷмӯи фармоишӣ месозем, ки силсиларо ба ҳамаи тирезаҳои имконпазири дарозии муайян бо ҳадафҳояшон ҷудо мекунад:
class TimeSeriesDataset(torch.utils.data.Dataset):
def __init__(self, series, window_length):
self.series = series
self.window_length = window_length
def __len__(self):
return len(self.series) - self.window_length
def __getitem__(self, idx):
if idx >= len(self):
raise IndexError("dataset index out of range")
end = idx + self.window_length # индекси аввал баъди тиреза
window = self.series[idx : end]
target = self.series[end]
return window, target
Синфро дар силсилаи содаи рақамҳои 0 то 5 месанҷем. Онро метавон [0, 1, 2, 3, 4, 5] навишт, аммо модулҳои RNN барои ҳар пайдарпайӣ шакли дученакаи [дарозии пайдарпайӣ, андозанокӣ] мехоҳанд. Барои силсилаи яктағйирёбанда андозанокӣ 1 аст, пас [[0], [1], [2], [3], [4], [5]] менависем. Дар зер ҳамаи тирезаҳои дарозии 3 ва ҳадафи ҳар кадом — аввалин қимати баъди тиреза — гирифта мешаванд:
>>> my_series = torch.tensor([[0], [1], [2], [3], [4], [5]])
>>> my_dataset = TimeSeriesDataset(my_series, window_length=3)
>>> for window, target in my_dataset:
... print("Window:", window, " Target:", target)
...
Window: tensor([[0], [1], [2]]) Target: tensor([3])
Window: tensor([[1], [2], [3]]) Target: tensor([4])
Window: tensor([[2], [3], [4]]) Target: tensor([5])
Синф дуруст кор мекунад! DataLoader месозем, ки тирезаҳоро омехта карда, ба бастаҳои дутоӣ ҷудо кунад:
>>> from torch.utils.data import DataLoader
>>> torch.manual_seed(0)
>>> my_loader = DataLoader(my_dataset, batch_size=2, shuffle=True)
>>> for X, y in my_loader:
... print("X:", X, " y:", y)
...
X: tensor([[[0], [1], [2]], [[2], [3], [4]]]) y: tensor([[3], [5]])
X: tensor([[[1], [2], [3]]]) y: tensor([[4]])
Бастаи аввал тирезаҳои [[0], [1], [2]] ва [[2], [3], [4]] бо ҳадафҳои [3] ва [5], бастаи дуюм танҳо [[1], [2], [3]] бо ҳадафи [4] дорад. Агар дарозии маҷмӯа ба андозаи баста тақсим нашавад, бастаи охир кӯтоҳтар мешавад.
Акнун роҳи табдили силсила ба маҷмӯи омӯзишӣ дорем. Додаҳои мусофиронро ба давраҳои омӯзишӣ, тасдиқӣ ва санҷиши ниҳоӣ тақсим мекунем. Ҳоло танҳо роҳи оҳанро мегирем. Онҳоро ба тензорҳои 32-битии нуқтаи шинокунанда табдил дода, ба як миллион тақсим мекунем, то қиматҳо ба ҳудуди 0–1 наздик шаванд. Ин бо ибтидосозии пешфарзи вазнҳо ва суръати омӯзиш хуб мувофиқ аст. df[["rail"]] ба ҷойи df["rail"] шакли [дарозии силсила, 1] медиҳад, на [дарозии силсила]:
rail_train = torch.FloatTensor(df[["rail"]]["2016-01":"2018-12"].values / 1e6)
rail_valid = torch.FloatTensor(df[["rail"]]["2019-01":"2019-05"].values / 1e6)
rail_test = torch.FloatTensor(df[["rail"]]["2019-06":].values / 1e6)
Эзоҳ. Силсилаҳои вақтиро одатан аз рӯйи вақт тақсим мекунанд. Баъзан андозаи дигар имкон медиҳад давраи дарозтари омӯзиш гиред. Масалан, додаҳои вазъи молиявии 10 000 ширкат дар солҳои 2001–2019-ро шояд аз рӯйи ширкатҳо тақсим кардан мумкин бошад. Вале бисёр ширкатҳо сахт ҳамбастаанд: тамоми соҳа метавонад якҷо боло ё поён равад. Агар ширкатҳои ҳамбаста дар омӯзиш ва санҷиш бошанд, арзёбии хатои умумисозӣ аз ҳад хушбинона мешавад ва санҷиш фоидаи камтар дорад.
Бо TimeSeriesDataset се маҷмӯа ва боркунакҳояшонро месозем. Тавре дар боби 4 дидем, поёнравии градиентӣ намунаҳои мустақил ва яксонтақсимшуда (IID)-ро интизор аст. Пас shuffle=True мегузорем: тартиби тирезаҳо омехта мешавад, на қиматҳои дохилашон:
window_length = 56
train_set = TimeSeriesDataset(rail_train, window_length)
train_loader = DataLoader(train_set, batch_size=32, shuffle=True)
valid_set = TimeSeriesDataset(rail_valid, window_length)
valid_loader = DataLoader(valid_set, batch_size=32)
test_set = TimeSeriesDataset(rail_test, window_length)
test_loader = DataLoader(test_set, batch_size=32)
Акнун ҳар модели регрессияи дилхоҳро сохта, омӯзондан мумкин аст!
Пешгӯӣ бо модели хаттӣ
Аввал модели одии хаттиро месанҷем. Талафоти Ҳубер истифода мешавад, ки одатан аз кам кардани мустақими MAE беҳтар кор мекунад (боби 9):
import torch.nn as nn
import torchmetrics
torch.manual_seed(42)
model = nn.Sequential(nn.Flatten(), nn.Linear(window_length, 1)).to(device)
loss_fn = nn.HuberLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.003, momentum=0.9)
metric = torchmetrics.MeanAbsoluteError().to(device)
[...] # омӯзиши модел, масалан бо train()-и боби 10
Пеш аз nn.Linear аз nn.Flatten истифода мебарем: вуруд [андозаи баста, дарозии тиреза, андозанокӣ] аст, вале қабати хаттӣ [андозаи баста, хусусиятҳо] мехоҳад. Баъди омӯзиш MAE-и тасдиқӣ тақрибан 37 726 мешавад; натиҷаи шумо метавонад фарқ кунад. Ин адад метрикаи ба 1e6 зарбзада аст: додаҳо ба миллион тақсим шуда буданд ва танҳо пас аз зарб хато бо шумораи мусофирон ифода мешавад. Ин аз пешгӯии сода беҳтар, вале аз SARIMA бадтар аст.
Оё RNN беҳтар мешавад? Биёед бинем!
Пешгӯӣ бо RNN-и сода
RNN-и якқабата (расми 13.2) ва дар охир nn.Linear месозем. Қабати хаттӣ ҳолати охирини пинҳониро гирифта, пешгӯӣ медиҳад:
class SimpleRnnModel(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super().__init__()
self.hidden_size = hidden_size
self.memory_cell = nn.Sequential(
nn.Linear(input_size + hidden_size, hidden_size),
nn.Tanh()
)
self.output = nn.Linear(hidden_size, output_size)
def forward(self, X):
batch_size, window_length, dimensionality = X.shape
X_time_first = X.transpose(0, 1)
H = torch.zeros(batch_size, self.hidden_size, device=X.device)
for X_t in X_time_first:
XH = torch.cat((X_t, H), dim=1)
H = self.memory_cell(XH)
return self.output(H)
torch.manual_seed(42)
univar_model = SimpleRnnModel(input_size=1, hidden_size=32, output_size=1)
Кодро мебинем:
- Созанда андозаи вуруд, ҳолати пинҳонӣ ва баромадро мегирад.
input_size=1, зеро силсила яктағйирёбанда аст.hidden_sizeшумораи нейронҳои бозгаштӣ аст; ин ҷо 32, вале танзимшаванда мебошад.output_size=1, зеро як қимат пешгӯӣ мекунем. - Ҳуҷайраи хотира аз
nn.Linearва tanh сохта шуда, дар ҳар қадам як бор истифода мешавад. Фаъолсозии дигар ҳам мумкин аст, вале tanh дар RNN одатан устувортар мебошад. nn.Linear-и охир ҳолати ниҳоиро ба баромад табдил медиҳад. Ҳолати пинҳонӣ барои ҳар нейрон як андоза, ин ҷо 32, дорад; ҳадаф танҳо як андоза аст. Ҳамчунин tanh танҳо аз −1 то +1 мебарорад, вале қиматҳои пешгӯишаванда баъзан аз +1 калон мешаванд.forward()бастаи[batch_size, window_length, dimensionality]мегирад;dimensionality=1аст.Hбо сифрҳо ибтидосозӣ мешавад: барои ҳар тиреза ва нейрон як сифр, яъне[batch_size, hidden_size].- Барои давр задан аз рӯйи вақт ду андозаи аввали
Xбоtranspose(0, 1)иваз мешаванд. Дар ҳар қадамX_tшакли[batch_size, dimensionality]дорад. X_tваHдар меҳвари хусусиятҳо,dim=1, часпонда шуда,XHбо шакли[batch_size, input_size + hidden_size]ҳосил мешавад. Ҳуҷайра аз он ҳолати нав месозад.- Дар охири ҳалқа
Hҳолати ниҳоӣ аст. Қабати хаттӣ аз он пешгӯии[batch_size, output_size]медиҳад.
Эзоҳи тарҷумон. Дар шарҳи аслӣ барои иваз кардани ду андозаи аввал
permute(0, 1)навишта шудааст. Барои тензори сеченака ин дуруст нест; коди овардашуда боtranspose(0, 1)дуруст аст.
Яъне модел ҳолатро аз сифр оғоз карда, дар ҳар қадам аз вуруди ҷорӣ ва ҳолати гузашта ҳолати нав месозад. Дар қадами охир ҳолат ба қабати хаттӣ меравад. Ҳамаи ин барои ҳамаи пайдарпайиҳои баста ҳамзамон иҷро мешавад.
Ин аввалин модели бозгаштии мо, аз навъи пайдарпайӣ-ба-вектор, аст. Чун баромад як нейрон дорад, вектори баромади ҳар пайдарпайӣ андозаи 1 дорад.
Агар моделро ба GPU гузаронда, мисли пешина омӯзонед ва арзёбӣ кунед, MAE-и тасдиқӣ ба 30 659 мерасад. Ин беҳтарин модели то ин ҷо ва ҳатто беҳтар аз SARIMA аст!
Маслиҳат. Танҳо нормализатсия кардем, тамоюлу мавсимиятро нагирифтем, вале модел хуб кор мекунад. Ин ҷустуҷӯи тези моделҳои умедбахшро бе пешкоркарди зиёд осон мекунад. Барои натиҷаи беҳтарин бошад, масалан бо фарқгирӣ статсионаритар кардани силсиларо бисанҷед.
PyTorch модули nn.RNN дорад, ки татбиқро хеле сода мекунад. Варианти зерин қариб ба пешина баробар аст:
class SimpleRnnModel(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super().__init__()
self.rnn = nn.RNN(input_size, hidden_size, batch_first=True)
self.output = nn.Linear(hidden_size, output_size)
def forward(self, X):
outputs, last_state = self.rnn(X)
return self.output(outputs[:, -1])
Код хеле кӯтоҳтар шуд:
- Ба ҷойи сохтани ҳуҷайра
nn.RNNмесозем. Андозаҳои вуруд ва пинҳонӣ ҳамонанд.batch_first=True, чун меҳвари аввал баста аст. Бе он модул[window_length, batch_size, dimensionality]мехоҳад, на[batch_size, window_length, dimensionality]. - Созанда қабати баромадро айнан мисли пешина месозад.
forward()бастаро мустақим баnn.RNNмедиҳад. Он ҳолатро бо сифрҳо ибтидосозӣ карда, ҳар қадамро бо ҳуҷайраи хаттӣ ва фаъолсозӣ, пешфарз tanh, коркард мекунад.
nn.RNN ду натиҷа медиҳад:
outputsбаромадҳои қабати болоии бозгаштӣ дар ҳамаи қадамҳост. Ҳоло як қабат дорем; дар бахши оянда чандқабатаро мебинем. Дар RNN-и сода инҳо ҳамон ҳолатҳои пинҳонии қабати болоӣ мебошанд. Шакл[batch_size, window_length, hidden_size]аст; беbatch_first=Trueду андозаи аввал иваз мешаванд.last_stateҳолати ҳар қабати бозгаштиро баъди қадами охирин нигоҳ медорад. Шакл[num_layers, batch_size, hidden_size]аст. Дар ин ҷо андозаи аввал 1 мебошад.
Дар охир баромади охиринро, ки ҳамон ҳолати охирини қабати болоӣ аст, ба nn.Linear медиҳем.
Натиҷа ба пешина монанд, вале омӯзиш одатан хеле тезтар аст, чун nn.RNN беҳина шудааст. Дар GPU-и Nvidia он аз cuDNN, ки татбиқҳои хеле беҳинаи шабакаҳо, аз ҷумла чанд навъи RNN, дорад, истифода мебарад.
Эзоҳ.
nn.RNNду параметри ғеҷиш дорад: яке барои вуруд ва дигаре барои ҳолати пинҳонӣ. Онҳо танҳо ҷамъ мешаванд ва қудрати моделро зиёд намекунанд, вале cuDNN чунин параметргузориро талаб мекунад. Аз ин рӯ натиҷа айнан бо пешина яксон намешавад.
Пешгӯӣ бо RNN-и амиқ
Чанд қабати ҳуҷайраро болои ҳам гузоштан маъмул аст (расми 13.10). Ин RNN-и амиқ медиҳад.

Расми 13.10. RNN-и амиқ (чап) ва кушодашавии он дар тӯли вақт (рост).
Дар PyTorch танҳо num_layers-ро ҳангоми сохтани nn.RNN муайян мекунем. Масалан, дар созандаи модели пешина num_layers=3 гузорем, RNN-и сеқабата мегирем; боқимондаи код ҳамон аст:
self.rnn = nn.RNN(input_size, hidden_size, num_layers=3, batch_first=True)
Баъди омӯзишу арзёбӣ MAE ба 29 273 мерасад — беҳтарин натиҷаи то ин ҷо!
Пешгӯии силсилаҳои вақтии бисёртағйирёбанда
Чандирӣ бартарии муҳими шабакаҳои нейронӣ аст: онҳо бе тағйири ҷиддии меъморӣ бо силсилаҳои бисёртағйирёбанда кор мекунанд. Роҳи оҳанро аз рӯйи ҳам додаҳои роҳи оҳан ва ҳам автобус пешгӯӣ мекунем. Навъи рӯзро ҳам илова мекунем! Пешакӣ медонем, ки фардо корӣ, истироҳат ё ид аст; пас навъи рӯзи ояндаро ба рӯзи ҷорӣ мувофиқ карда, ба вуруд медиҳем. Барои содафаҳмӣ Pandas истифода мешавад:
df_mulvar = df[["rail", "bus"]] / 1e6 # ҳар ду силсила вуруд мешаванд
df_mulvar["next_day_type"] = df["day_type"].shift(-1) # навъи фардоро медонем
df_mulvar = pd.get_dummies(df_mulvar, dtype=float) # рамзгузории one-hot
Акнун df_mulvar панҷ сутун дорад: роҳи оҳан, автобус ва се сутуни one-hot барои W, A ва U-и рӯзи оянда. Мисли пешина ба се давра тақсим карда, ба тензор табдил медиҳем:
mulvar_train = torch.FloatTensor(df_mulvar["2016-01":"2018-12"].values)
mulvar_valid = torch.FloatTensor(df_mulvar["2019-01":"2019-05"].values)
mulvar_test = torch.FloatTensor(df_mulvar["2019-06":].values)
Эзоҳи тарҷумон. Дар манбаъ дар ин се сатр боз ба
1e6тақсим карда мешавад.railваbusаллакай ҳангоми сохтаниdf_mulvarмиқёсбандӣ шудаанд, пас ин ҷо тақсимро гирифтем, то додаҳо дубора хурд ва сутунҳои one-hot ба миллион тақсим нашаванд.
Акнун маҷмӯаҳои PyTorch лозиманд. TimeSeriesDataset ҳамчун ҳадаф ҳам мусофирони роҳи оҳану автобус ва ҳам рамзгузории навъи рӯзи баъди онҳоро медод. Ҳоло танҳо роҳи оҳанро мехоҳем, пас қимати аввали ҳадафро нигоҳ медорем. Барои ин синфи меросгиранда месозем:
class MulvarTimeSeriesDataset(TimeSeriesDataset):
def __getitem__(self, idx):
window, target = super().__getitem__(idx)
return window, target[:1]
Маҷмӯа ва боркунакҳоро месозем:
mulvar_train_set = MulvarTimeSeriesDataset(mulvar_train, window_length)
mulvar_train_loader = DataLoader(mulvar_train_set, batch_size=32, shuffle=True)
[...] # сохтани маҷмӯа ва боркунакҳои тасдиқӣ ва санҷиш
Вурудҳои баста шакли [32, 56, 5] ва ҳадафҳо [32, 1] доранд. Хуб!
RNN-ро месозем:
torch.manual_seed(42)
mulvar_model = SimpleRnnModel(input_size=5, hidden_size=32, output_size=1)
mulvar_model = mulvar_model.to(device)
Модел бо univar_model яксон аст, танҳо input_size=5: дар ҳар қадам ба ҷойи як вуруд панҷто мегирад. MAE-и тасдиқӣ 23 227 мешавад. Ин пешрафти калон аст!
Пешгӯии ҳам роҳи оҳан ва ҳам автобус ҳам душвор нест: дар синфи маҷмӯа target[:2] ба ҷойи target[:1] ва дар модел output_size=2 гузоред. Ҳамин!
Тавре дар боби 9 гуфтем, як модел барои чанд вазифаи вобаста аксар вақт аз моделҳои ҷудогона беҳтар аст: хусусиятҳои як вазифа ба дигарӣ фоида мекунанд ва зарурати хуб кор кардан дар чанд вазифа аз ҳад мутобиқшавиро маҳдуд мекунад. Ин навъе мунтазамсозӣ аст. Аммо натиҷа аз вазифа вобаста мебошад. Дар ин мисол RNN-и бисёрвазифавӣ аз моделҳои махсуси ҳар силсила, ки ҳар панҷ сутунро вуруд мегиранд, андаке бадтар аст. Бо вуҷуди ин, MAE-и роҳи оҳан 26 441 ва автобус 26 178 аст, ки ҳанӯз хуб мебошад.
Пешгӯии чанд қадам ба пеш
То ин ҷо қадами навбатиро пешгӯӣ кардем. Бо иваз кардани ҳадаф метавон як қимати чанд қадам дурро пешгӯӣ кард: барои ду ҳафта пас қимати 14 рӯз баъдро ба ҷойи қимати 1 рӯз баъд ҳадаф мегирем. Вале агар ҳамаи 14 қимати ояндаро бо як модел хоҳем, чӣ?
Роҳи аввал: univar_model-и пешинаро гирифта, қимати навбатиро пешгӯӣ мекунем ва онро ба вуруд илова менамоем, гӯё воқеан рӯй дода бошад. Баъд қимати баъдиро пешгӯӣ карда, ҳамин тавр идома медиҳем:
n_steps = 14
univar_model.eval()
with torch.no_grad():
X = rail_valid[:window_length].unsqueeze(dim=0).to(device)
for step_ahead in range(n_steps):
y_pred_one = univar_model(X)
X = torch.cat([X, y_pred_one.unsqueeze(dim=1)], dim=1)
Y_pred = X[0, -n_steps:, 0]
56 рӯзи аввали тасдиқиро гирифта, бо unsqueeze() андозаи бастаи 1 илова мекунем, зеро модел вуруди 3D мехоҳад; баъд ба GPU мегузаронем. X шакли [1, 56, 1] дорад. Такроран пешгӯӣ карда, онро дар меҳвари вақт — dim=1 — илова мекунем. Ҳар пешгӯӣ [1, 1] аст, пас пеш аз часпондан бо unsqueeze() як андозаи вақт илова мешавад. Дар охир X шакли [1, 56 + 14, 1] дорад ва 14 қимати охир пешгӯиҳоянд. Натиҷа дар расми 13.11 аст.
Огоҳӣ. Хатои як қадам ба қадамҳои баъдӣ ҳам таъсир мекунад ва ҷамъ мешавад. Пас ин равишро барои шумораи хурди қадамҳо истифода бурдан беҳтар аст.

Расми 13.11. Пешгӯии 14 қадам ба пеш, як қадам дар ҳар навбат.
Роҳи дуюм: RNN-ро барои якбора баровардани 14 қимат омӯзонем. Он ҳанӯз пайдарпайӣ-ба-вектор аст, вале 14 баромад дорад. Аввал ҳадафҳоро ба векторҳои 14 қимати оянда табдил медиҳем:
class ForecastAheadDataset(TimeSeriesDataset):
def __len__(self):
return len(self.series) - self.window_length - 14 + 1
def __getitem__(self, idx):
end = idx + self.window_length # индекси аввал баъди тиреза
window = self.series[idx : end]
target = self.series[end : end + 14, 0] # 0 = мусофирони роҳи оҳан
return window, target
Маслиҳат. Дар ин ҷо 14 бевосита дар код навишта шудааст. Дар лоиҳаи воқеӣ онро, мисли
window_length, танзимшаванда кунед.
Синф аз TimeSeriesDataset мерос гирифта, __len__() ва __getitem__()-ро иваз мекунад. Ҳадаф ба ҷойи як қимат тензори 14 қимати ояндаи роҳи оҳан аст. Боз аз силсилаи бисёртағйирёбанда се маҷмӯа месозем:
ahead_train_set = ForecastAheadDataset(mulvar_train, window_length)
ahead_train_loader = DataLoader(ahead_train_set, batch_size=32, shuffle=True)
[...] # маҷмӯа ва боркунакҳои тасдиқӣ ва санҷиш
RNN-и мисли mulvar_model, вале бо output_size=14, месозем:
torch.manual_seed(42)
ahead_model = SimpleRnnModel(input_size=5, hidden_size=32, output_size=14)
ahead_model = ahead_model.to(device)
Баъди омӯзиш якбора 14 қимат мегирем:
ahead_model.eval()
with torch.no_grad():
window = mulvar_valid[:window_length] # шакл: [56, 5]
X = window.unsqueeze(dim=0) # шакл: [1, 56, 5]
Y_pred = ahead_model(X.to(device)) # шакл: [1, 14]
Ин хуб кор мекунад. Пешгӯии фардо табиатан аз рӯзи 14-ум дақиқтар аст, вале мисли роҳи пешина хато ҷамъ намешавад. Акнун мебинем, ки пайдарпайӣ-ба-пайдарпайӣ боз беҳтар мешавад ё не.
Маслиҳат. Ду равишро якҷо кардан мумкин аст: 14 рӯзи ояндаро якбора пешгӯӣ карда, ба вуруд часпонед ва боз барои 14 рӯзи баъдӣ иҷро кунед.
Пешгӯӣ бо модели пайдарпайӣ-ба-пайдарпайӣ
Ба ҷойи он ки танҳо дар қадами охирин 14 қимати ояндаро омӯзонем, моделро водор мекунем дар ҳар қадам 14 қимат пешгӯӣ кунад. Дар қадами 0 вектори пешгӯиҳои қадамҳои 1–14, дар қадами 1 пешгӯиҳои 2–15 ва ҳамин тавр мебарояд. Ҳадафҳо пайдарпайии тирезаҳои пайдарҳаманд, ки ҳар қадам як қадам кӯчонида мешаванд. Ҳадафи ҳар тирезаи вурудӣ акнун вектор не, балки пайдарпайии ҳамдарозии вуруд аст, ки ҳар қадамаш вектори 14-ченака дорад. Барои вуруди [batch_size, window_length, input_size] баромад [batch_size, window_length, output_size] мешавад. Ин дигар пайдарпайӣ-ба-вектор нест; пайдарпайӣ-ба-пайдарпайӣ, ё seq2seq, аст.
Эзоҳ. Шояд ғайриинтизор бошад, ки ҳадафҳо, ғайр аз қадами охирин, қиматҳои дар вуруд бударо доранд. Оё ин фиреб нест? Не: RNN дар ҳар қадам танҳо гузаштаро медонад ва ояндаро дида наметавонад. Он модели сабабӣ (causal model) аст.
Чаро seq2seq омӯзонем, агар танҳо баромади қадами охир барои оянда лозим бошад? Дуруст аст: баъди омӯзиш ҳамаи баромадҳои дигарро нодида гирифтан мумкин аст. Бартарии асосӣ дар омӯзиш аст: талафот барои ҳар қадам саҳм дорад, на танҳо охирин. Пас градиентҳои хато бештар мешаванд ва роҳашон дар вақт кӯтоҳтар аст, зеро аз ҳар қадам меоянд. Ин метавонад омӯзишро устувор ва ҳамгароиро тез кунад. Ҳамчунин модел дар ҳар қадам аз таърихи дарозиаш гуногун пешгӯӣ мекунад, ки метавонад аз ҳад мутобиқшавӣ ба дарозии муайяни тирезаро кам кунад. Ҳадди ақал умед ҳамин аст! Онро дар роҳи оҳан месанҷем. Аввал додаҳоро омода мекунем:
class Seq2SeqDataset(ForecastAheadDataset):
def __getitem__(self, idx):
end = idx + self.window_length # индекси аввал баъди тиреза
window = self.series[idx : end]
target_period = self.series[idx + 1 : end + 14, 0]
target = target_period.unfold(dimension=0, size=14, step=1)
return window, target
Seq2SeqDataset аз ForecastAheadDataset мерос гирифта, __getitem__()-ро иваз мекунад. Вуруд ҳамон аст, вале ҳадаф пайдарпайии тирезаҳои дар ҳар қадам як воҳид кӯчонидашуда мебошад. Кори асосиро unfold() мекунад: дар андозаи муайян бо қадами додашуда ҳаракат карда, блокҳои андозаи муайянро мебарорад. Масалан:
>>> torch.tensor([0, 1, 2, 3, 4, 5]).unfold(dimension=0, size=4, step=1)
tensor([[0, 1, 2, 3],
[1, 2, 3, 4],
[2, 3, 4, 5]])
Боз се маҷмӯа ва боркунакҳояшонро месозем:
seq_train_set = Seq2SeqDataset(mulvar_train, window_length)
seq_train_loader = DataLoader(seq_train_set, batch_size=32, shuffle=True)
[...] # маҷмӯа ва боркунакҳои тасдиқӣ ва санҷиш
Модели seq2seq:
class Seq2SeqRnnModel(SimpleRnnModel):
def forward(self, X):
outputs, last_state = self.rnn(X)
return self.output(outputs)
torch.manual_seed(42)
seq_model = Seq2SeqRnnModel(input_size=5, hidden_size=32, output_size=14)
seq_model = seq_model.to(device)
Аз SimpleRnnModel мерос гирифта, forward()-ро иваз мекунем. Акнун self.output на танҳо ба қадами охир, балки ба ҳамаи қадамҳо татбиқ мешавад. Шояд пурсед, ки ин чӣ гуна кор мекунад: то ҳол nn.Linear-ро ба вуруди 2D-и [batch_size, features] дода будем, аммо outputs ҳоло [batch_size, window_length, hidden_size], яъне 3D аст. Хушбахтона, қабат худкор ба ҳар қадам татбиқ мешавад ва натиҷа [batch_size, window_length, output_size] аст — маҳз он чизе, ки лозим дорем.
Дар дохил nn.Linear барои зарби матритса аз torch.matmul() истифода мебарад. Он массивҳои аз ду андоза зиёдро низ самаранок зарб мекунад. Масалан, [2, 3, 5, 7]-ро ба [2, 3, 7, 11] зарб кардан мумкин аст. Ҳар кадомро шабакаи 2 × 3-и матритсаҳо тасаввур мекунем; матритсаҳои мувофиқ зарб мешаванд. Аз 5 × 7 ва 7 × 11 матритсаи 5 × 11 ҳосил мешавад, пас натиҷа [2, 3, 5, 11] аст. Паҳнсозӣ (broadcasting) ҳам дастгирӣ мешавад. Масалан, [10, 56, 32] ба [32, 14]: ҳар яке аз 10 матритсаи 56 × 32 ба ҳамон матритсаи 32 × 14 зарб шуда, [10, 56, 14] медиҳад. Ҳангоми додани вуруди 3D ба nn.Linear ҳамин рӯй медиҳад.
Маслиҳат. Ҳамин натиҷаро бо иваз кардани баромади
nn.Linearбаnn.Conv1d-и ядроаш 1, яънеConv1d(32, 14, kernel_size=1), гирифтан мумкин аст. Вале ду андозаи охири ҳам вуруд ва ҳам баромадро иваз кардан лозим аст, то вақт ҳамчун андозаи фазоӣ коркард шавад.
Коди омӯзиш ҳамон аст. Ҳангоми омӯзиш ҳама баромадҳо истифода мешаванд, вале баъди он танҳо қадами охир муҳим аст. Пешгӯии 14 рӯзи ояндаи роҳи оҳан:
seq_model.eval()
with torch.no_grad():
some_window = mulvar_valid[:window_length] # шакл: [56, 5]
X = some_window.unsqueeze(dim=0) # шакл: [1, 56, 5]
Y_preds = seq_model(X.to(device)) # шакл: [1, 56, 14]
Y_pred = Y_preds[:, -1] # шакл: [1, 14]
Барои t+1 MAE-и тасдиқӣ 23 350 аст — хеле хуб. Барои дуртар дурустӣ кам мешавад: масалан барои t+14 MAE ба 35 315 мерасад.
RNN-и сода барои силсилаи вақтӣ ва дигар пайдарпайиҳо хуб буда метавонад, вале дар пайдарпайиҳои дароз чандон хуб нест. Биёед сабабу ҳалли онро бинем.
Кор бо пайдарпайиҳои дароз
Барои омӯзиш дар пайдарпайии дароз RNN аз қадамҳои зиёди вақт мегузарад ва шабакаи кушодашуда хеле амиқ мешавад. Мисли ҳар шабакаи амиқ он метавонад аз градиентҳои ноустувор-и боби 11 азоб кашад: омӯзиш хеле тӯлонӣ ё ноустувор мешавад. Илова бар ин, бо гузаштани вақт вурудҳои аввал тадриҷан фаромӯш мешаванд. Аввал мушкили градиентҳоро мебинем.
Мубориза бо градиентҳои ноустувор
Бисёр усулҳои шабакаҳои амиқ барои RNN ҳам мувофиқанд: ибтидосозии хуби параметрҳо, оптимизаторҳои тез, dropout ва ғайра. Аммо фаъолсозии сернашаванда, монанди ReLU, шояд чандон ёрӣ надиҳад ва ҳатто ноустувориро зиёд кунад. Чаро? Фарз кунед, поёнравии градиентӣ вазнҳоро чунон нав кард, ки баромади қадами аввал андаке зиёд шуд. Ҳамон вазнҳо дар ҳамаи қадамҳо истифода мешаванд, пас баромади дуюм, сеюм ва баъдӣ ҳам зиёд шуда, охир «метарканд». Функсияи сернашаванда инро бознамедорад.
Бо суръати хурдтари омӯзиш ё функсияи сершаванда, монанди тангенси гиперболӣ, ин хатарро кам кардан мумкин аст. Барои ҳамин tanh пешфарз аст.
Градиентҳо ҳам метавонанд тарканда шаванд. Агар омӯзиш ноустувор бошад, андозаи онҳоро назорат карда, эҳтимол маҳдудсозии градиентҳо (gradient clipping) истифода баред.
Нормализатсияи баста (BN) дар RNN мисли шабакаҳои амиқи пешрав самаранок нест. Он бештар байни қабатҳои бозгаштӣ муносиб аст, на байни қадамҳои вақт. Аз ҷиҳати техникӣ метавон BN-ро ба ҳуҷайра илова кард, то ҳар қадам ҳам вуруди ҷорӣ ва ҳам ҳолати пешинаро коркард кунад. Аммо ҳамон BN бо параметрҳои яксон, сарфи назар аз миқёс ва ғеҷиши воқеии додаҳо дар ҳар қадам, такрор мешавад. Тавре Сезар Лоран ва ҳамкоронаш соли 2015 нишон доданд, ин дар амал натиҷаи хуб намедиҳад. BN танҳо ба вурудҳо андаке фоида дошт, на ба ҳолатҳои пинҳонӣ. Яъне байни қабатҳои бозгаштӣ — амудӣ дар расми 13.10 — андаке беҳтар аз ҳеҷ буд, аммо дохили онҳо, дар самти уфуқӣ, не.
Нормализатсияи қабат (LN)-и боби 11 дар дохили қабатҳои бозгаштӣ одатан аз BN беҳтар аст. Онро дар ҳар қадам пеш аз фаъолсозӣ мегузоранд. Мутаассифона, nn.RNN LN-ро дастгирӣ намекунад. Бояд ҳалқаро дастӣ навишта, дар ҳар давр nn.LayerNorm татбиқ кунед. Ин чандон душвор нест, вале содагӣ ва суръати nn.RNN-ро аз даст медиҳед. Масалан, дар варианти якуми SimpleRnnModel чунин ҳуҷайра созед:
self.memory_cell = nn.Sequential(
nn.Linear(input_size + hidden_size, hidden_size),
nn.LayerNorm(hidden_size),
nn.Tanh()
)
Огоҳӣ. LN ҳамеша ёрӣ намедиҳад; бояд санҷед. Одатан дар RNN-ҳои дарвозадор, монанди LSTM ва GRU, беҳтар кор мекунад. Ҳамчунин баъди дур кардани мавсимият ва тамоюл эҳтимоли фоидааш бештар аст.
Барои dropout байни қадамҳои вақт низ RNN-и фармоишӣ лозим аст: nn.RNN инро надорад. Вале метавонад баъди қабатҳои бозгаштӣ dropout гузорад: гиперпараметри dropout-ро ба сатҳи дилхоҳ гузоред; пешфарз сифр аст.
Эзоҳи тарҷумон. Dropout-и дохилии
nn.RNNбайни қабатҳои бозгаштӣ татбиқ мешавад, на баъди қабати охирин. Аз ин рӯ, барои RNN-и танҳо якқабата он чунин таъсир надорад.
Бо ин усулҳо градиентҳоро устувортар ва омӯзишро самараноктар мекунед. Акнун хотираи кӯтоҳмуддатро мебинем.
Маслиҳат. Барои пешгӯии силсилаи вақтӣ доштани ҳудудҳои номуайянӣ дар баробари пешгӯӣ муфид аст. Як роҳ MC dropout-и боби 11 мебошад.
Ҳалли мушкили хотираи кӯтоҳмуддат
Бо ҳар табдили додаҳо дар RNN андаке иттилоот гум мешавад. Баъди муддате ҳолат қариб нишоне аз вурудҳои аввал надорад. Ин метавонад кори моделро вайрон кунад. Моҳии Дорӣ-ро тасаввур кунед, ки ҷумлаи дарозро тарҷума кардан мехоҳад: то охир мехонад, вале аввалашро дигар намедонад! Барои ҳал ҳуҷайраҳои дорои хотираи дарозмуддат пешниҳод шуданд. Онҳо чунон муваффақанд, ки ҳуҷайраҳои сода дигар камтар истифода мешаванд. Аввал маъмултаринашон — LSTM-ро мебинем.
Ҳуҷайраҳои LSTM
Ҳуҷайраи хотираи дарозу кӯтоҳмуддат (LSTM)-ро Зепп Ҳохрайтер ва Юрген Шмидҳубер соли 1997 пешниҳод карданд. Баъд муҳаққиқон, аз ҷумла Алекс Грейвс, Ҳошим Сак ва Войчех Заремба, онро беҳтар намуданд. Ба ҷойи nn.RNN танҳо nn.LSTM гузоштан мумкин аст. Он одатан беҳтар кор мекунад: омӯзиш тезтар ҳамгаро ва нақшҳои дарозмуддат беҳтар ошкор мешаванд.
Ин чӣ гуна кор мекунад? Меъморӣ дар расми 13.12 аст. Агар дохили қуттиро набинем, LSTM ба ҳуҷайраи одӣ монанд аст, вале ҳолаташ ба ду вектор ҷудо шудааст: h(t) ва c(t). c аз cell, яъне «ҳуҷайра», аст. h(t)-ро ҳолати кӯтоҳмуддат ва c(t)-ро дарозмуддат тасаввур кардан мумкин аст.

Расми 13.12. Ҳуҷайраи LSTM.
Қуттиро мекушоем. Ғояи асосӣ ин аст, ки шабака меомӯзад чиро дар хотираи дароз нигоҳ дорад, чиро дур кунад ва чиро аз он бихонад. Ҳолати c(t−1) аз чап ба рост аввал аз дарвозаи фаромӯшкунӣ гузашта, баъзе хотираҳоро гум мекунад. Баъд амали ҷамъ хотираҳои нави интихобкардаи дарвозаи вуруд-ро илова мекунад. c(t) бе табдили дигар мустақим берун меравад. Пас ҳар қадам баъзе хотираҳо дур ва баъзе илова мешаванд. Баъди ҷамъ ҳолати дарозмуддат нусха шуда, аз tanh ва дарвозаи баромад мегузарад. Ин h(t)-ро месозад, ки бо баромади ҷории ҳуҷайра y(t) баробар аст. Акнун манбаи хотираҳои нав ва кори дарвозаҳоро мебинем.
Вектори ҷории x(t) ва ҳолати кӯтоҳмуддати пешинаи h(t−1) ба чор қабати пуррапайвасти вазифаашон гуногун дода мешаванд:
- Қабати асосӣ g(t) мебарорад. Он вуруди ҷорӣ ва ҳолати пешинаро таҳлил мекунад. Дар ҳуҷайраи сода танҳо ҳамин қабат ҳаст ва баромадаш мустақим ба y(t) ва h(t) меравад. Дар LSTM бошад, муҳимтарин қисмҳои он дар ҳолати дарозмуддат нигоҳ дошта шуда, боқимонда дур мешаванд.
- Се қабати дигар идоракунандаҳои дарвоза мебошанд. Онҳо фаъолсозии логистикӣ доранд ва аз 0 то 1 мебароранд. Натиҷаҳо дар зарби элементӣ истифода мешаванд: 0 дарвозаро мебандад, 1 мекушояд.
Ҳар дарвоза вазифаи худро дорад:
- Фаромӯшкунӣ, бо f(t), муайян мекунад кадом қисмҳои ҳолати дарозмуддат пок шаванд.
- Вуруд, бо i(t), муайян мекунад кадом қисмҳои g(t) илова шаванд.
- Баромад, бо o(t), муайян мекунад кадом қисмҳои ҳолати дарозмуддат дар ин қадам хонда ва ба h(t) ва y(t) бароварда шаванд.
Ҳамин тавр, LSTM вуруди муҳимро мешиносад — вазифаи дарвозаи вуруд, онро дар хотираи дароз мегузорад, то вақти лозим нигоҳ медорад — вазифаи фаромӯшкунӣ, ва ҳангоми зарурат мебарорад — вазифаи баромад. Ҳамаи амалиётҳо ҳосилапазиранд. Барои ҳамин LSTM дар нақшҳои дарозмуддати силсилаҳои вақтӣ, матни дароз, аудио ва ғайра хеле муваффақ аст.
Муодилаи 13.4 ҳисобҳои ҳолати дарозмуддат, кӯтоҳмуддат ва баромадро барои як намуна ҷамъбаст мекунад. Муодилаҳои minibatch хеле монанданд.
Муодилаи 13.4. Ҳисобҳои LSTM.
i(t) = σ(Wₓᵢᵀ x(t) + Wₕᵢᵀ h(t−1) + bᵢ)
f(t) = σ(W_xfᵀ x(t) + W_hfᵀ h(t−1) + b_f)
o(t) = σ(Wₓₒᵀ x(t) + Wₕₒᵀ h(t−1) + bₒ)
g(t) = tanh(W_xgᵀ x(t) + W_hgᵀ h(t−1) + b_g)
c(t) = f(t) ⊗ c(t−1) + i(t) ⊗ g(t)
y(t) = h(t) = o(t) ⊗ tanh(c(t))
Дар ин муодила:
- Wₓᵢ, W_xf, Wₓₒ, W_xg матритсаҳои вазнҳои чор қабат барои пайвастҳо ба x(t) мебошанд.
- Wₕᵢ, W_hf, Wₕₒ, W_hg матритсаҳои вазнҳо барои пайвастҳо ба ҳолати кӯтоҳмуддати пешинаи h(t−1) мебошанд.
- bᵢ, b_f, bₒ, b_g ҳадҳои ғеҷиши чор қабатанд.
Маслиҳат. Дар моделҳои пешина
nn.RNN-ро боnn.LSTMиваз карда, натиҷаро дар додаҳои мусофирон бисанҷед. Шояд андаке танзими гиперпараметрҳо лозим шавад.
Барои LN ё dropout дар ҳар қадам, мисли RNN-и сода, ҳалқаи бозгаштиро дастӣ месозед. Метавон муодилаи 13.4-ро истифода бурд, вале роҳи сода nn.LSTMCell аст, ки як қадами вақтро иҷро мекунад:
class LstmModel(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super().__init__()
self.hidden_size = hidden_size
self.memory_cell = nn.LSTMCell(input_size, hidden_size)
self.output = nn.Linear(hidden_size, output_size)
def forward(self, X):
batch_size, window_length, dimensionality = X.shape
X_time_first = X.transpose(0, 1)
H = torch.zeros(batch_size, self.hidden_size, device=X.device)
C = torch.zeros(batch_size, self.hidden_size, device=X.device)
for X_t in X_time_first:
H, C = self.memory_cell(X_t, (H, C))
return self.output(H)
Ин ба татбиқи якуми SimpleRnnModel монанд аст, вале дар ҳар қадам nn.LSTMCell истифода мешавад ва ҳолат ду қисм дорад: H-и кӯтоҳмуддат ва C-и дарозмуддат.
LSTM чанд вариант дорад. Яке аз маъмултаринашон GRU аст.
Ҳуҷайраҳои GRU
Воҳиди бозгаштии дарвозадор (GRU)-ро Кёнҳён Чо ва ҳамкоронаш дар мақолаи соли 2014 пешниҳод карданд. Ҳамон мақола энкодер–декодери пештар дидаамонро низ муаррифӣ кард.

Расми 13.13. Ҳуҷайраи GRU.
GRU варианти содашудаи LSTM аст ва аксар вақт ба ҳамон андоза хуб кор мекунад. Фарқҳои асосӣ:
- Ду вектори ҳолат ба як h(t) якҷо шудаанд.
- Як идоракунандаи z(t) ҳам фаромӯшкунӣ ва ҳам вурудро идора мекунад. Агар 1 диҳад, фаромӯшкунӣ кушода (=1) ва вуруд баста (1−1=0) аст; агар 0 диҳад, баръакс. Яъне ҷойи нигоҳдории хотираи нав аввал пок мешавад.
- Дарвозаи баромад нест: дар ҳар қадам тамоми вектори ҳолат бароварда мешавад. Аммо идоракунандаи нави r(t) ҳаст, ки муайян мекунад кадом қисми ҳолати пешина ба қабати асосии g(t) нишон дода шавад.
Муодилаи 13.5 ҳисоби ҳолати ҳар қадамро барои як намуна нишон медиҳад.
Муодилаи 13.5. Ҳисобҳои GRU.
z(t) = σ(W_xzᵀ x(t) + W_hzᵀ h(t−1) + b_z)
r(t) = σ(Wₓᵣᵀ x(t) + Wₕᵣᵀ h(t−1) + bᵣ)
g(t) = tanh(W_xgᵀ x(t) + W_hgᵀ (r(t) ⊗ h(t−1)) + b_g)
h(t) = z(t) ⊗ h(t−1) + (1 − z(t)) ⊗ g(t)
PyTorch nn.GRU дорад: танҳо онро ба ҷойи nn.RNN ё nn.LSTM мегузоред. Барои RNN-и фармоишӣ nn.GRUCell низ ҳаст. Дар мисоли пешина nn.LSTMCell-ро бо он иваз карда, C-ро дур кунед ва ҳолати H-ро мустақиман диҳеду гиред.
LSTM ва GRU аз сабабҳои асосии муваффақияти RNN мебошанд. Онҳо нисбат ба RNN-и сода пайдарпайиҳои дарозтарро коркард кунанд ҳам, хотираашон маҳдуд аст ва омӯзиши нақшҳои дарозмуддати пайдарпайиҳои 100 қадам ё бештар, монанди аудио, силсилаи дароз ё ҷумлаи дароз, душвор мемонад. Як роҳ кӯтоҳ кардани вуруд, масалан бо конволютсияи якченака, аст.
Истифодаи конволютсияи 1D барои пайдарпайиҳо
Дар боби 12 дидем, ки конволютсияи 2D чанд ядрои хурдро рӯйи тасвир ҳаракат дода, барои ҳар ядро як харитаи 2D месозад. Конволютсияи 1D ҳамин корро рӯйи пайдарпайӣ карда, барои ҳар ядро харитаи 1D медиҳад. Ҳар ядро як нақши хеле кӯтоҳи дарозиаш на бештар аз ядроро меомӯзад. Бо 10 ядро баромад 10 пайдарпайии якченакаи ҳамдарозӣ аст, ё метавон онро як пайдарпайии 10-ченака донист. Пас омехтаи қабатҳои бозгаштӣ, конволютсияи 1D ва ҳатто pooling-и 1D сохтан мумкин аст. Вале ду андозаи охири вуруд ва баромадро иваз кардан лозим аст: nn.Conv1d шакли [batch_size, input_features, sequence_length] мегирад ва [batch_size, output_features, sequence_length] медиҳад.
Огоҳӣ. Бо қадами 1 ва пуркунии
sameдарозии баромад бо вуруд баробар аст. Боvalidё қадами аз 1 калон баромад кӯтоҳтар мешавад; ҳадафҳоро мувофиқ кунед.
Модели зерин як конволютсияи 1D, баъд GRU ва дар охир баромади хаттӣ дорад. Ҳама бастаҳои пайдарпайӣ — тензорҳои 3D — мегиранду медиҳанд. nn.Conv1d бо қадами 2 пайдарпайиро тақрибан ду баробар кӯтоҳ мекунад. Ядро аз қадам хурд нест, дар ин ҷо калонтар аст, пас ҳамаи вурудҳо истифода мешаванд. Модел метавонад иттилооти муҳимро нигоҳ дошта, танҳо ҷузъиёти номуҳимро партояд. Дар forward() қабатҳоро пайваст мекунем, вале пешу баъди конволютсия ду андозаи охирро иваз ва ҳолатҳои баргардондаи GRU-ро нодида мегирем:
class DownsamplingModel(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super().__init__()
self.conv = nn.Conv1d(input_size, hidden_size, kernel_size=4, stride=2)
self.gru = nn.GRU(hidden_size, hidden_size, batch_first=True)
self.linear = nn.Linear(hidden_size, output_size)
def forward(self, X):
Z = X.permute(0, 2, 1) # вақтро ҳамчун андозаи фазоӣ мегирем
Z = self.conv(Z)
Z = Z.permute(0, 2, 1) # тартиби вақт ва хусусиятҳоро барқарор мекунем
Z = torch.relu(Z)
Z, _states = self.gru(Z)
return self.linear(Z)
torch.manual_seed(42)
dseq_model = DownsamplingModel(input_size=5, hidden_size=32, output_size=14)
dseq_model = dseq_model.to(device)
Кӯтоҳшавӣ ба GRU барои гирифтани нақшҳои дарозтар кумак мекунад, пас тирезаро то 112 рӯз дучанд мекунем. Се қадами аввали ҳадафҳоро ҳам бояд бурем: ядро 4 аст ва баромади аввал аз қадамҳои 0–3 ҳосил мешавад, пас пешгӯии аввал бояд барои 4–17 бошад, на 1–14. Аз сабаби қадами 2 ҳадафҳоро низ якто дар миён мегирем. Барои ин зерсинфи Seq2SeqDataset месозем:
class DownsampledDataset(Seq2SeqDataset):
def __getitem__(self, idx):
window, target = super().__getitem__(idx)
return window, target[3::2] # буридани 3 қадами аввал ва якто дар миён гирифтан
window_length = 112
dseq_train_set = DownsampledDataset(mulvar_train, window_length)
dseq_train_loader = DataLoader(dseq_train_set, batch_size=32, shuffle=True)
[...] # маҷмӯа ва боркунакҳои тасдиқӣ ва санҷиш
Эзоҳи тарҷумон. Дар манбаъ ин ҷо
rail_trainомадааст, ки танҳо як хусусият дорад, вале моделinput_size=5дорад. Аз ин рӯ,mulvar_train(ва барои тасдиқу санҷишmulvar_validваmulvar_test) истифода шуд. Агар танҳо роҳи оҳан лозим бошад, моделро боinput_size=1созед.
Акнун моделро ба тарзи маъмул омӯзондан мумкин аст. Конволютсия ва бозгаштро муваффақона омехта кардем. Вале агар танҳо конволютсияи 1D монда, қабатҳои бозгаштиро тамоман гирем, чӣ?
WaveNet
Дар мақолаи соли 2016 Аарон ван ден Оорд ва муҳаққиқони дигари DeepMind меъмории нави WaveNet-ро пешниҳод карданд. Онҳо конволютсияҳои 1D-ро болои ҳам гузошта, дар ҳар қабат дараҷаи васеъшавӣ (dilation), яъне фосилаи вурудҳои нейронро, дучанд карданд. Қабати аввал якбора ду қадами вақт, дуюм чор, сеюм ҳашт ва ҳамин тавр мебинад (расми 13.14). Қабатҳои поёнӣ нақшҳои кӯтоҳ ва болоӣ дарозмуддатро меомӯзанд. Ба шарофати dilation-и дучандшаванда шабака пайдарпайиҳои хеле калонро самаранок коркард мекунад.

Расми 13.14. Меъмории WaveNet.
Муаллифон 10 конволютсия бо dilation-ҳои 1, 2, 4, 8, …, 256, 512, баъд боз як гурӯҳи ҳамон 10 қабат ва баъд гурӯҳи сеюм гузоштанд. Як чунин гурӯҳ мисли конволютсияи хеле самараноки ядроаш 1024 амал мекунад, вале хеле тезтару тавонотар ва бо параметрҳои хеле камтар. Пеш аз ҳар қабат аз чап ба қадри dilation сифр илова карданд, то дарозӣ дар тамоми шабака нигоҳ дошта шавад. Пуркунӣ аз чап, на аз ҳар ду тараф, муҳим аст: қабат ҳангоми пешгӯӣ ояндаро намебинад. Пас модел сабабӣ мемонад.
WaveNet-и содашударо барои ҳамон пайдарпайиҳо месозем. Аввал CausalConv1d месозем, ки мисли nn.Conv1d аст, вале аз чап ба қадри лозим пур мекунад, то дарозӣ нигоҳ дошта шавад:
import torch.nn.functional as F
class CausalConv1d(nn.Conv1d):
def forward(self, X):
padding = (self.kernel_size[0] - 1) * self.dilation[0]
X = F.pad(X, (padding, 0))
return super().forward(X)
Аз nn.Conv1d мерос гирифта, forward()-ро иваз мекунем. Пуркунии чапро ҳисоб карда, пеш аз даъвати forward()-и синфи асосӣ pad() мегузорем. Он ду аргумент мегирад: тензори X ва ҷуфти ададҳои пуркунии чапу рост дар андозаи охир, яъне вақт.
Акнун худи WaveNet:
class WavenetModel(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super().__init__()
layers = []
for dilation in (1, 2, 4, 8) * 2:
conv = CausalConv1d(input_size, hidden_size, kernel_size=2,
dilation=dilation)
layers += [conv, nn.ReLU()]
input_size = hidden_size
self.convs = nn.Sequential(*layers)
self.output = nn.Linear(hidden_size, output_size)
def forward(self, X):
Z = X.permute(0, 2, 1)
Z = self.convs(Z)
Z = Z.permute(0, 2, 1)
return self.output(Z)
torch.manual_seed(42)
wavenet_model = WavenetModel(input_size=5, hidden_size=32, output_size=14)
wavenet_model = wavenet_model.to(device)
Созанда ҳашт CausalConv1d бо dilation-ҳои 1, 2, 4, 8 ва боз 1, 2, 4, 8 месозад; баъди ҳар кадом ReLU аст. Ҳамаро дар self.convs, модули nn.Sequential, мегузорем ва nn.Linear-и баромад месозем. Дар forward() ду андозаи охирро иваз карда, конволютсияҳоро иҷро мекунем, тартибро барқарор карда, ба баромади хаттӣ медиҳем. Бо пуркунии сабабӣ ҳар қабат дарозиро нигоҳ медорад. Пас ҳадафҳо метавонанд тамоми пайдарпайии 112-рӯза бошанд: буридан ё камнамунагирӣ лозим нест. Метавон боркунакҳои барои Seq2SeqRnnModel сохташуда, яъне seq_train_loader ва seq_valid_loader-ро истифода бурд.
Эзоҳи тарҷумон. Боркунакҳои қаблан сохташуда дар он лаҳза тирезаҳои 56-рӯза доштанд. Барои омӯзиши маҳз 112-рӯза онҳоро бо
Seq2SeqDataset(..., window_length=112)аз нав созед. Модел бо ҳар ду дарозӣ кор мекунад.
Моделҳои ин бахш дар пешгӯии мусофирон натиҷаҳои монанд доранд, вале дар вазифа ё ҳаҷми додаҳои дигар фарқашон метавонад зиёд бошад. Дар мақолаи WaveNet муаллифон дар чанд вазифаи аудиоӣ — аз ҳамин ҷо номи модел — аз ҷумла табдили матн ба нутқ, натиҷаҳои пешқадам гирифта, овозҳои хеле воқеиятмонанди чанд забон сохтанд. Онҳо мусиқиро ҳам ҳар дафъа як намунаи аудио тавлид карданд. Ин махсусан таъсирбахш аст, зеро як сонияи аудио метавонад даҳҳо ҳазор қадами вақт дошта бошад; ҳатто LSTM ва GRU бо чунин пайдарпайиҳои дароз мушкилӣ мекашанд.
Огоҳӣ. Агар беҳтарин моделҳои мусофирони Чикагоро дар давраи санҷишии аз соли 2020 арзёбӣ кунед, натиҷа аз интизор хеле бадтар мешавад! Сабаб оғози пандемияи COVID-19 аст, ки ба нақлиёти ҷамъиятӣ сахт таъсир кард. Тавре гуфтем, ин моделҳо танҳо вақте хуб кор мекунанд, ки қонуниятҳои омӯхташудаи гузашта дар оянда ҳам идома ёбанд. Пеш аз ба истифодаи воқеӣ баровардан, кори моделро дар додаҳои нав санҷед. Баъди ҷойгиркунӣ низ натиҷаҳояшро мунтазам назорат кунед.
Акнун метавонед бо навъҳои гуногуни силсилаи вақтӣ кор кунед! Дар боби 14 омӯзиши RNN-ро идома дода, мебинем, ки он вазифаҳои гуногуни коркарди забони табииро чӣ гуна ҳал мекунад.

Барномасозӣ
Олами Афсонаҳо
Ахбор