“Na topole podle skal zelený můžík …”, no, co udělal? Rozpoznání žánru hudby, shoda podmětu s přísudkem, analýza sentimentu v textu, převod mluvené řeči na text či překlady - v tom všem se obvykle vyskytují prvky Recurrent Neural Networks nebo Transformers. Jak to funguje, tedy alespoň z pohledu znalostí zelenáče?
Hudba je architektura v pohybu. Zmrazená v čase není nic jiného, než akord nebo jiný souzvuk. Nedává smysl bez harmonie, melodie či rytmu a to všechno je o vývoji v čase. Řeč je na tom podobně. Pokud si uděláte glosář knihy, kde abecedně seřadíte všechna její slova s počty jejich výskytů, děj románu moc nezjistíte. Rozhodující je totiž jejich pořadí.
Klasické feed-forward sítě (třeba DNN) pro to nejsou vhodné, ale třeba Convolutional Neural Network (CNN) někdy ano, protože ty pracují s topologickými informacemi (vyhodnocují vstupy v souvisejících shlucích). Zajímavý příklad může být rozpoznávání žánru hudby, kdy si nejprve nakreslíte spektogram (frekvenční analýza zvuku v čase - jedna osa je frekvence, barvou intenzita a druhá osa je čas) a ten můžete prohnat CNN tak jako když se analyzují běžné obrázky (např. první vrstva se možná zaměří na identifikaci ostrých přechodů na časové ose což defacto znamená rozeznění nebo ukončení tónu o určité frekvenci, takže v dalších vrstvách už můžete najít pravidelnosti a třeba zjistit jak proti sobě jde kopák a snare a to už je určitě jedno z mnoha dobrých vodítek k rozpoznání žánru … ale jak jsem psal minule takhle to interpretuje člověk, CNN se možná bude soustředit na jinačí aspekty). Nicméně pro většinu úloh tohle nebude ideální jako hlavní postup, CNN možná bude jedna z vrstev, ale potřebujeme spíše něco, co rozumí sekvencím, tedy posloupnosti informací spíše než všemu najednou (DNN) nebo po shlucích s následným odzoomováváním (CNN).
Než se pustím jednotlivých typů pojďme zmínit kategorizaci co do rozdílů mezi délkou vstupu a očekávaného výstupu. To totiž vede i na různé scénáře použití.
U převodu jedné sekvence na jinou (překlad, sumarizace textu) je často problém, že vstupní délka obou neodpovídá (jinak by to vedlo na doslova doslovné překlady a to se nedá, to by nás vracelo o 20 let zpět). To se řeší tak, že se první sekvence nejprve zakóduje do jakéhosi strojového jazyka a v další fázi se použije dekodér, který to převede na požadovaný výstup. Je to vlastně logické - při překladu si také nejprve zpracujeme vstupní sekvenci do hlavy a teprve tam syntetizujeme větu v jiném jazyce (pokud bychom překládali slovo za slovem a bez kontextu výsledek bude otřesný zejména mezi jazyky s výraznými odlišnostmi ve stavbě věty).
Základní myšlenka Recurrent Neural Network je vlastně velmi jednoduchá. Neuron přijímá nějakou informaci na vstupu a vyplivne výstup, ale tentokrát ten výstup ještě napojí zpět na svůj vlastní vstup. Při druhé iteraci tak neuron rozhoduje na základě aktuální vstupní informace v kombinaci s výstupem předchozí interakce. Tím se RNN dokáže učit sekvence. Niméně kromě toho, že je to docela pomalé, tak pokud neuron neustále krmíte jeho vlastním výstupem, začne například rychle konvergovat k nule nebo k nekonečnu. To znamená “krajina” začne být moc placatá nebo úplně kolmá a zpětná propagace přestane mít potřebu nějak měnit váhy, protože to s výsledkem vlastně stejně nic moc nedělá. Jinak řečeno RNN se poměrně brzy přestane učit, brzy zapomíná. To se dá ale přidáním pár dalších “obvodů” dovnitř řešit.
Long Short-Term Memory je varianta, která přidává schopnost držet si dlouhodobě nějaký stav (mít paměť) a taky schopnost selektivně zapomínat. V čase si předává nejen aktuální výstup, ale i stav dlouhodobé paměti. Na vstupu je forget gate, aby neuron mohl zapomínat, tedy ovlivňovat svou paměť. Zapomínání je škála od úplného vyresetování až po nesahat. Pak následuje input gate, která mimo jiné říká jak se má (pokud vůbec) přidat tato nová informace do dloudobé paměti. Zatímco první funkce byla o schopnosti zapomínat (resetovat), druhá je o schopnosti do paměti zapisovat nové informace. Vysokou míru zapomenutí může například vyvolat tečka na konci věty nebo nějaký nový podmět (síť si řekne aha, tak teď už se nebavíme o letadlech, ale o plyšácích, takže moje dosavadní poznámky na téma aeroplán už si můžu do značné míry smazat). Slova jako jsou členy v angličtině určitě nebudou mít velkou váhu pro modifikaci paměti (input gate) a třeba zmíněná tečka na konci věty taky ne (přestože pro forget gate mohla být důležitá), nicméně nový větný podmět bude určitě důležitý (čili je tak zásadní, že vyresetoval paměť a ještě se do ní nacpal). Nicméně taková interpretace je samozřejmě nepřesná a bude určitě záležet na úloze. Pokud se snažíme předpovědět jak bude příběh pokračovat, půjdeme nepochybně po podstatných jménech a slovesech, kudrlinky okolo nás zajímají méně. Ale pokud analyzujeme sentiment recenze, přídavná jména typu úžasný nebo špatný či příslovce typu znechuceně či nadšeně mají v takovém případě zásadní smysl (a možná větší, než slovesa). Nicméně jsme u strojového učení, tak nechme na mašinu, ať si přemýšlí o důležitosti slov jak chce, pro nás je zásadní výsledek.
LSTM tedy umožňuje vnímat větší kus historie, řádově třeba místo desítek iterací se dostat ke stovkám. Nicméně nárůst složitosti znamená zase ještě větší výkonnostní problémy. Proto vznikla zjednodušená varianta - Gated Recurrent Units. Ta má, podobně jako LSTM, “obvody” pro aktualizaci a zapomínání, ale to vše nad průtokovým stavem (nemá už institut separátního paměťového stavu). Je tedy jednodušší, efektivnější na trénování, a přitom má výsledky dost podobné LTSM.
Nicméně stále je tu zásadní problém s výkonnem a ten je principiální. RNN, LSTM i GRU stojí na sériovém zpracování a nedají se tak dobře rozhodit na tisíce nezávislých výpočetních větví tak, jak je to běžné u sítí typu DNN nebo CNN. Tím pádem nedokážete dobře využít paralelizace na GPU, což je klíč k vysokému výkonnu a cenové efektivitě.
Možná potřebujeme sledovat třeba teplotní čidlo u motoru nebo prodeje zmrzliny a cílem je udělat předpověď budoucího vývoje (kolik zmrzlin mám naskladnit) nebo detekovat anomálii a rozsvítit červenou kontrolku u motoru (a přepnout ho do provizorního ochranného režimu). Použití něčeho jako LSTM je určitě jedna z možností a rozhodně ta hodně zajímavá, ale existují i tradičnější statistické metody nevyužívající komplexního (a tedy drahého a pomalého) učení. Třeba taková SARIMAX dokáže z jedné či vícero vstupních řad (vzájemně se ovlivňujících, tedy multi-varietní úlohy) vytvořit předpověď dalšího vývoje. To může mít následující výhody:
Pokud tedy mám jednoduchou časovou řadu, bude asi nejlepší vyzkoušet metody typu SARIMAX. Pokud model nefunguje dobře, tedy obsahuje v složitejší skryté vzorce, pak je čas povolat třeba LSTM.
Vraťme se ke scénáři převodu sekvence na sekvenci s tím, že tyto mohou mít různé délky. Už tady padlo, že to lze elegantně řešit jakýmsi překladem přes “strojový jazyk”. Encoder, který může být složen třeba z jedné či více vrstev LSTM, vezme vstup (třeba větu v češtině) libovolné délky a až se dostane nakonec, vyplivne výsledný vektor. Představme si to jako nějakou sadu čísel s tím, že ta je pokaždé stejně velká bez ohledu na to, jak dlouhý byl vstup. Tenhle vektor je pak vstupem do dekodéru, což může být zase například LSTM, které ze strojového jazyka vygeneruje výslednou sekvenci v japonštině.
Problémy jsou tady ale minimálně dva. Takové zpracování se neumí dobře soustředit na klíčové informace, například nejdůležitější slova nesoucí význam, ale hlavně nedokáže nelineárně hodnotit vzájemné pozice slov v textu. Například vezměme si text “V zoo byl hezký pes, který se zdál trochu hloupý a chytrý opičák, který ale hezký nebyl”. Pouhé průtokové “zapomínání” ala LSTM bude mít dost problém - tak třeba “hloupý a chytrý” jsou hned vedle sebe, ale přitom hloupý se vztahuje k psovi dost nalevo a chytrý k opičákovi úplně na konci. Důležitost jednotlivých slov bychom potřebovali brát v kontextu jejich vztahu k ostatním - hloupý je slovo kritické pro našeho psa, ale naprosto nerelevantní pro opičáka, kterému není určeno. Druhým nedostatkem je, že je to sériová úloha nevhodná pro vysoce paralelizovaná GPU a tím pádem jsou nějaké extrémně komplikované modely výpočetně neúnosné.
Řešením první potíže je přidat koncept pozornosti. Ke každému slovu ve větě se naučíme jeho důležitost v kontextu ostatních a to dokonce ne jednou, ale hned několikrát, řekněme z několika pohledů. Tato informace není jen na začátku, ale modelem postupně úpravami prochází přes všechny vrstvy enkodéru. Co se druhého problému týče, tak v tom vězí obrovský úspěch těchto přístupů postavených na “transformerech”. Vstup se nezpracovává sekvenčně, ale rovnou celý najednou s tím, že pořadí slov ve větě se jednoduše přidá jako další dimenze a tím modelu naservírujeme kompletní vstup najedou. Tranformer typicky bude mít několik vrstev enkoderů, které začínají self-attention funkcí následovanou klasickou feed-forward sítí a to víceméně pro každé slovo paraleleně. Výstup se předá do dalšího enkodéru, jeho self-attention a jeho feed-forward sítě a takhle několikrát dokola. Postup je tedy postaven podobně jako v CNN na vrstvách, které nejprve odhalí základní principy (například gramatiku) a postupně se dostávají k složitějším a složitějším věcem (smysl). To všechno umožňuje velmi vysokou míru parelelizace a ke slovu tak přijde cenově efektivní GPU. Tak například v roce 2020 v rámci OpenAI použil Microsoft slušnou “herní” sestavičku v Azure čítající 285000 CPU jader a 10000 GPU karet (každé GPU má tisíce výpočetních jader). Počítat na tomhle děle nějakou RNN úlohu schopnou zatížit jedno jádro by řádný hospodář asi nechtěl.
Pro složité věci jako jsou práce s textem a překlady dnes transormeři rozhodně nejvíc frčí - například GPT-3 model a úžasné reálné aplikace jako je GitHub Copilot.
Na těchto typech úloh mě kromě toho co jsem popsal zaujaly ještě dva tématické okruhy. Jednak je to schopnost přenášet pracně spočítaný model do jiných oblastí a tam si ho “přitrénovat” - transfer learning. Zajímavé aplikace taky zahrnují unsupervised nebo self-supervized úlohy, třeba nechat model napsat novinový článek. Pro dnešek už to ale stačí - pokud najdete něco špatně, pinkněte mi na LinkedIn a díky za dočtení až sem.