Přes léto chci zas o trochu víc proniknout do strojového učení a chci si z toho udělat pár zápisků sem na blog - pomáhá to utřídit si myšlenky. Nejsem žádný expert, ani zdaleka, ale třeba vám to pomůže se zorientovat, pokud taky začínáte. Všechny profíky prosím a shovívavost a hlavně upozornění na chyby a doporučení na co se podívat, kde si dát pozor a tak. Díky!
Umělá inteligence je obecně cokoli, co dokáže vykazovat inteligenci a je to stroj, nikoli živočich. Mimo téma je asi dobré zmínit, že inteligence není totéž co vědomí a vědomí není totéž, co emoce. Morální otázky spojené například se schopností chytrý stroj vypnout (tzn. je to vražda nebo ne) souvisí právě s tím, že strach ze smrti a touha přežít je základní vlastnost všech úspěšných živočichů (ti co jí neměli už tu nejsou), ale není daná ani vědomím ani inteligencí (inteligentní stroj tedy nutně nemusí dostat do vínku tato “omezení”, takže vypnutí nebo nemožnost se rozmnožit mu nijak nevadí). Do AI lze zařadit i to, že nejchytřejší doktoři na planetě dají hlavy dohromady a svoje znalosti zapíší ve formě kódu a vznikne tak chytrý algoritmus, který bude vykazovat inteligentní chování (rozhodně inteligentnější, než u většiny nelékařské populace nebo u zástupců z řad hmyzu). Stejně tak možná v budoucnu nějaké kvantové počítače přinesou zajímavá řešení pro AI. Jedním ze způsobů, jak vykazovat chytré chování, je strojové učení - Machine Learning.
ML je schopnost stroje se učit aniž by byl na problém explicitně naprogramován. Tedy místo toho, že zmínění nejlepší lékaře na planetě napíšou společně chytrou aplikaci, si stroj “aplikaci” napíše sám. Dostane data, resp. hromadu dat, a sám má najít “vzoreček” nebo chcete-li kód. To samozřejmě může udělat relativně jednoduchou matematikou - vezme data, třeba ceny bytů podle dispozice, lokality a stáří, a vezme jednoduchou rovnici (očekávaná cena bytu = něco krát typ dispozice + něco krát lokalita + něco krát stáří) a pokusí se najít hodnoty těch “něco”. Realitní makléř, pokud by tedy uměl matematiku (o čemž lze možná pochybovat), by ze zkušenosti mohl najít rovnici taky, není to žádná magie. Nicméně pro opravdu pokročilé situace si stroje mohou vzít příklad z fungování mozku a nasadit neuronové sítě. Komplexita něčeho takového bude enoromní, to už nestačí na papír a tužku a také výsledek bude víc a víc nepochopitelný (zatímco v předchozím případě je rovnice, která dává váhy parametrům a je jasné co se v ní děje, komplexní deep learning model už takhle nějak interpretovat nejde). Mimochodem to je v některých situacích zásadní problém - když pošlete někoho do vězení, musíte mu předložit “účet” - za co a proč, nemůžete jen tak říct, že ML ho posílá za mříže a předložit nic neříkající čísla vah v nic neříkajících vstvách neuronové sítě. Inspirování mozkem … to je zajímavé, pojďme dál.
Základním stavebním blokem mozku je neuron, který má velké množství vstupů (dendrity) a jeden výstup, který se ale může větvit na víc napojených neuronů (axon). Neuron funguje jako takové to překlápěcí vědro, co najdete ve vodních parcích, jen není na vodu, ale na ionty. Dendrity vědro naplňují, ale také umí obsah neutralizovat (snižovat). Jakmile je vědro zcela plné, akční potenciál ho převrhne a celé se vyleje ve formě signálu do axonu. To, jak každý vstup přispívá k obsahu vědra, se dá označit za váhu a může se to měnit v čase stejně tak, jako se v čase mohou přidávat nová spojení (tímto způsobem se mozek učí - nevytváří nové neurony ani nemění jejich vlastnosti, ale mění topologii). Synapse, místa spojení, jsou buď elektrická nebo chemická (ty fungují na delší vzdálenosti a jsou celkově spolehlivější). To má dva zajímavé důsledky. Rychlost šíření informací je díky tomu v zásadě dost pomalá - neuron může “tiknout” maximálně jednou za 4ms - šnečí tempo v porovnání s tím, co dokáže transistor ve vašem mobilu. Takový transistor se umí přepnout už mnoho let za méně jak 1ns (tedy o 6-7 řádů rychleji). Přesto - lidský mozek bravurně zvládá obecnou inteligenci (oblast, kde AI zatím nemá šanci) a to vše při spotřebě asi 24W (to je energie, na kterou stačí obyčejná Quick Charge nabíječka na telefon - ani nepotřebujete USB-C PD). Podle teorií co jsem četl (A thousand brains theory) funguje mozek z vyššího pohledu na principu vytváření modelů. Mozek defacto neustále předpovídá - nabírá vstupy, předpovídá co se má stát a zjišťuje, jestli se to stalo. Pokud ne, zaměříte na to svou pozornost (např. natáhnete se pro hrnek bez koukání, ale sáhnete na plyšáka - mozek předpověděl co očekáváte, že ucítíte a vůbec to neodpovídá, proto se leknete a zaměříte se na to, protože na modelu je něco špatně - buď špatně předpovídám jak mám hmatově pociťovat hrnek nebo v 3D modelu rozložení předmětů je chyba a hrnek není tam co očekávám). Výsledné chování je vlastně interakce mnoha těchto modelů mezi sebou.
Hned na začátek nutno říct, že perceptron (teoretický “neuron”) neodpovídá tomu fyzickému, jen je jím inspirovaný. Například nemá paměťový efekt, kdy se vědro naplňuje po několik cyklů a celková topologie zapojení perceptronů je obvykle (pro realizovatelnost řešení) hodně pravidelná (plně propojené vrstvy vs. “chaos” v propojení neuronů). Každopádně v neuronové síti je sada (vrstva) neuronů na vstupu (analogie senzorických neuronů nabírajících vstupy, třeba ty na sítnici oka nebo tlakové senzory v kůži) a jedna na výstupu (to je “displej”, z kterého si přečteme výsledek). Mezi nimi jsou tzv. skryté vrstvy a pokud jich je víc jak jedna, mluvíme o deep neural network. Klasická topologie propojuje každý neuron vrstvy vlevo s každým neuronem vrstvy následující směrem doprava. Takhle se to opakuje až se předposlední vrsta (poslední “hidden layer”) napojí na výstupní vrstvu.
Tak jako každý dendrit má nějakou citlivost, tak každý vstup (x) do neuronu má nějakou číselnou váhu (w). Na vstupní vrstvě se to dá ještě interpretovat - lokalita bytu má větší váhu, než patro - nicméně v dalších vrstvách už je interpretace prakticky nemožná. Kromě toho může vzniknout potřeba, aby nějaký vstupní parametr byl nějak posunutý - to je bias (b). Výsledek tedy je w*x+b. Nicméně ne vždy je tenhle výsledek vhodné prostě poslat dál, někdy je potřeba ho nějak normalizovat - na to se použije aktivační funkce. Tak například - jaký má příjem vliv na nakupování pečiva? Pokud jste pod životním minimem, tak je to samozřejmě problém, ale jestli máte průměrný příjem nebo nadprůměrný se asi neprojeví na množství nakupovaného pečiva. Možná tedy budete chtít výstup upravit do roviny “má na rohlík” (1) nebo nemá na rohlík (0) a to vám zajistí binary step aktivační funkce. Jindy potřebujete normalizovat výstup do čísla mezi 0 a 1 na to dobře použijete sigmoid (typicky když signalizujete pravděpodobnost příslušnosti k nějaké kategorii), jindy zase mezi -1 a 1 a použijete tanh. Pro některé scénáře vás třeba nezajímají hodnoty do určité míry (pod nulou) - pro cokoli záporného chci vrátit nulu, pro cokoli kladného chci vrátit reálnou hodnotu (to dělá ReLU). Je to hodně i o tom, jak to funguje s extrémy. Průměrné dožití není zas tak daleko od maximálního, extrémy mají tu vlastnost, že s každým rokem navíc klesá pravděpodobnost dožití. Pokud do místnosti s 50 lidmi vejde někdo, kdo je extrémní co do věku, průměrný věk celé skupiny se pohne jen trochu. Když se ale zaměříme na platy fotbalistů nebo spisovatelů, tak pokud na běžné zasedání průměrných spisovatelů vejde třeba Stephen King nebo JK Rowlongová, tak se rázem průměrný příjem skupiny zvedne na někalikanásobek. Protože většina modelů nemá za cíl dobře předpovídat 1% extrémů, ale spíše skvěle fungovat na 99% případů, jsou aktivační funkce důležité pro odstínění od šíleností (ale samozřejmě nesmíme zapomínat, že pak vás mohou extrémy nepříjemně překvapit - viz knížka Černá labuť od Nassima Nicholase Taleba o vysoce nepravděpodobných událostech, které ale na rozdíl od těch pravděpodobných, mohou vládnout světu). Ještě nutno zmínit, že příklady si hledám na vysvětlitelných parametrech (typicky na vstupech nebo výstupech), ale ve skrytých vrstvách těžko něčemu přiřazovat konkrétní interpretaci.
Nejčastěší typ strojového učení je supervised learning, tedy situace, kdy jste schopni stroji předložit ohromné množství dat a k tomu i informaci, jak to dopadlo. Třeba seznam fotek (vstupní data) a co na každé fotce je (výsledek) nebo seznam finančních transakcí s mnoha parametry (vstupní data - kdy, kdo, kde, co) a k tomu seznam těch, co se ukázaly jako podvodné. Na rozdíl od extrémně efektivního lidského mozku, kterému stačí realitvně málo vstupních informací pro natrénování, potřebuje ML opravdu hodně dat.
Druhý typ je unsupervised learning, kdy sice předložíme data, ale neřekneme co je výsledek. Nechť si tam počítač najde nějaké vzorce chování sám. Výsledkem je, že ML vrátí rozčlenění vstupních událostí do nějakých (nepojmenovaných) kategorií. Tak například pokud byste dokázali dát do ML statistiku pohybu a chování hráčů hokeje na ledu, je pravděpodobné, že dostanete jako výsledek seznam hráčů, kteří jsou brankáři, obránci a útočníci. Stroj vůbec netuší jak kategorie pojmenovat, ale najde je.
Pro mě dost zajímavý typ učení je reinforcement learning. Je to defacto variace na první téma, ale tentokrát si potřebná data generuje stroj sám svou interakcí s okolím. Má tedy k dispozici sadu vstupů (například schopnost klikat na mezerník či šipky ve hře jako je Geometry Dash a k tomu grafický vstup) a na své akce dostává výsledky (umřel jsem, dostal jsem body apod.). Robot tedy musí trochu změnit mačkání kláves a sledovat, jestli se dostal dál nebo ne. Ve finále je to tedy zase obrovský seznam vstupů (časy a sekvence mačkání kláves a k tomu obrazová informace) a výstupů (score), ale tentokrát ne dopředu, ale robot si musí data získávat sám. Oproti člověku bude mít typicky hodně pomalý a trapný začátek, nicméně díky své přesnosti a schopnosti hrát hru rychle a bez přestávek na čůrání, vás brzy dostane mimo hru.
Na příkladu supervised learningu určíte topologii (počet vrstev a počet členů v každě vrstvě) a necháte systém náhodně vybrat parametry w a b a proženete jím vstupní data a získate k ním výsledky. Ty bude následně potřeba porovnat s reálnými výsledky (jsme v supervised learning). Tzv. cost function bude typicky využívat principu penalizace extrémních chyb, například chyby v jednotlivých výsledcích bude brát kvadraticky, čímž jednak vyřeší problém znamenénka, ale hlavně hodně znevýhodní modely s extrémními chybami.
Co mi nejdřív nedocházelo je proč se tolik nadělá s tím najít minimum takové funkce - udělám derivaci a je to ne, moje kalkulačka to umí. Potíž je, že počet neznámých je tak obrovský, že je to nesmírně výpočetně náročné (tady bude do budoucna určitě zajímavé jak s tím zamává kvantové počítání, které je schopné řešit jedním průchodem problémy vyžadující jinak náročné procházení všech možností - Quantum Machine Learning bude určitě zajímavý obor). Je tedy potřeba nasadit trochu dřevácké metody využívající náhody, která najde uspokojivé výsledky s výrazně nižší rozpočtem. Příklad mimo ML je známá Monte Carlo simulace, kdy vezmete terč ve tvaru čtverce o délce strany 2r a v něm je vepsaná kružnice o poloměru r. Jak zjistit obsah kružnice, když byste neznali Pi, takže vzoreček 2Pir^2 je pro vás neznámý? Stačí zavřít oči a házet na terč šipky (za předpokladu, že je to opravdu náhodný proces) a následně spočítat poměr šipek uvnitř vs. vně kruhu vynásobený čtyřmi - vyjde Pi ! V ML půjde typicky o nějakou variaci na gradient descent. Představte si krajinu, kde server/jih a východ/západ jsou dva parametry a nadmořská výška je výsledek cost funkce. Vrtulník vás vysadí na náhodném místě, vy si změříte nakolnění země pod nohama a uděláte krok směrem dolu. Takhle to opakujete tak dlouho, až se ocitnete v údolí, kde jste nejníž. Musíte samozřejmě trochu řešit problém, že se třeba ocitnete v lokálním dolíčku a ne skutečném údolí, ale to samozřejmě záleží na velikosti vašeho kroku a jak s ním pracujete (nejdřív budete dělat kroky stometrové a jak postupně je zkracovat, takže lokální jámy pravděpodobně přeskočíte případně do toho přimícháte víc náhody) a navíc v n rozměrech taková situace zas tak častá není. Každopádně můžete vybírat metody typu GD, SDG, Adam nebo jiné a u některých určovat velikost vašeho kroku (označuje se často za learning rate - u malého kroku můžete trefit přesněji minimum, ale může to trvat déle a můžete snadno uvíznout v lokální díře, která není globální minimum, velký krok ale zase může znamenat, že skutečné minimum překročíte a nenajdete ho).
To co jsem řešil v předchozím odstavci se musí nějak promítnout do modifikace vah a bias na jednotlivých spojích v neuronové síti, k čemuž se pužije backpropagation mechanismus, který jsem matematicky nepochopil. Ale princip je v tom, že potřebuji udělat ten krok v krajině, což se musí propsat do změny všech těchto parametrů ve všech vrstvách odzadu dopředu. Udělám krok a přepočítám to (podle nastavení třeba jednou pokaždé když projdu všechna data nebo víckrát), pak pokračujeme dál. Kdy přestat a jak moc minimalizace cost funkce na datech bude přínosem pro reálné výsledky modelu?
Model možná datům příliš neodpovídá - tak například takový, který bez ohledu na vstup vrací na výstupu číslo 1 je určitě validní, ale vstupní data moc v úvahu nebere. Určitě jde o případ underfit - na data to moc nesedí. Opačný extrém ale také není žádoucí, protože místo nalezení nějakého vzorce chování se model defacto naučí všechny vstupy nazpaměť a k nim mechanickou odpověď. Protože ale vstupy jsou jen vzorky (množina reálných možností je nekonečná nebo minimálně téměř nekonečná), tak v této situaci model dost možná v konfrontaci s novými reálnými daty bude úplně mimo - pravda skrytá v datech mu unikla. To je případ overfit situace - ladíme tak dlouho, až to přeladíme.
Jak se takové situaci bránit? Potřebujeme si ověřit, že model funguje dobře a to uděláme tak, že ho nebudeme trénovat na všech datech, ale část mu jich zatajíme. Oproti nim se pak ubezpečíme, že jsme to nepřeladili. Někdy si data rozdíleme dokonce na tři kbelíky - jeden na trénování, druhý na validaci a ladění hyperparametrů (tzn. ty hodnoty, které nejsou laděny v rámci modelu samotného, který řeší hodnoty w a b, ale jsou to informace jak model vůbec stavět - například jaké aktivační funkce se mají použít). Tato validační data tedy nevstupují přímo do modelu (takže přímo nezpůsobují overfit), ale do jisté míry výsledek ovlivňují přes výběr modelů (na jejich základě se mění hyperparametry - jsou techniky, kdy se tohle děje automaticky, třeba v případě k-fold validace), takže dává smysl mít ještě jednu brzdu ve formě třetí testovací sady dat úplně na závěr.
Jak ale vyhodnocovat úspěšnost modelu?
Pro měření se používá několik ukazatelů a každý může dávat smysl v jiné situaci.
Statistické metody jsou vhodné pro případy, kde je výsledkem modelu nějaké číslo ve spojitém spektru, tedy ne kategorie (není to úloha typu pes nebo kočka, ale spíše kolik je predikovaná cena pozemku). Mohli bychom tedy vzít jednotlivé předpověděné výsledky a srovnat je s reálnými výsledky, tyto rozdíly v absolutních hodnotách vzít a spočítat jejich průměr za celá data. Nevýhodou bude, že v takovém případě model může někdy dělat dost extrémní chyby - v průměru to třeba není tak zlé, ale předpovědět denní teploty v Praze ve výši 75°C je zkrátka trapné. Potřebujeme v modelu trestat velké chyby, takže odychylky hodíme na druhou a se zvyšující se vzdáleností od reality bude model hodně trpět, takže ho naučíme excesy nedělat. Nevýhodou je, že výsledné číslo (rozptyl) je v nic neříkajících jednotkách (stupně Celsia na druhou se špatně představují), tak je dobré výsledek odmocnit a získat tak směrodatnou odchylku (a vrátit se k představitelným jednotkám).
Dál pojďme na klasifikační problém - je to pes nebo kočka? Má pacient nemoc nebo nemá?
Accuracy je poměr úspěšných odpovědí k celkovému množství odpovědí. To je jednoduché a dobře srozumitelné, nicméně to nic moc neříká, pokud nevíme jak jsou kategorie na vstupu rozloženy. Pokud má třeba model, který z výšky, váhy, tepové frekvence a podkožního tuku poznává zda jde o muže či ženu, accuracy 97%, je to myslím dost dobré - obou pohlaví je totiž zhruba stejně. Ale co když je v první kategorii 99 výskytů ze 100? Pokud sestrojím model, který na jakýkoli podnět vždy vrátí “je to kategorie 1”, tak jeho accuracy bude 99%, nicméně jeho přínos pro nás není vůbec žádný.
Precission říká jak dokonale jsme dokázali identifikovat zástupce kategorie, tedy je to poměr správně určených zastupců k součtu správně a nesprávně určených zástupnců této kategorie. Jinak řečeno jak moc se mezi skutečné psy dostaly i nějaké kočky. 100% může také znamenat, že jsem v celém vzorku tisícovky koček a tisícovky psů odhalil jednoho jediného psa a opravdu jsem se trefil. Pokud má můj výběr velké reálné negativní dopady na vybrané, asi bych měl chtít precission velkou. Pokud to třeba znamená odmítnout zákazníka, protože se mi nezdá a mohl by se podle modelu chovat špatně, určitě si ho tím nezískám pro dlouhodobou loajalitu, když je nevinný, takže chci model, který takové chyby nedělá.
Recall jde často bohužel proti precission a říká jestli jsem dobře našel všechny zástupce kategorie, tedy poměr správně určených k součtu správně určených a přehlédnutých (false negative). Jinak řečeno jestli jsem ve vzorku našel opravdu všechny psy a neřeším, že jsem do toho omylem spláchl i pár koček. Pokud je pro mě někoho zapomenout horší, než omylem přimíchat i pár co tam nepatří, bude pro mě zásadní vyladit recall. Tak například pokud hledám pacienty, kterí by mohli mít určitou nemoc (očekávám výskyt v populaci 1%), abych zahájil zcela neškodnou a levnou léčbu, tak rozhodně nechci situaci, kdy jsem na někoho zapomněl a on mi pak umře. Těch pár korun za léčbu i těch, co jsou vybráni omylem a nijak jim to neuškodí, rád dám (místo 1% budu léčit třeba 1,3%, to ještě není tak hrozné).
Pokud mě trápí jak precission tak recall, můžu koukat na jejich harmonický průměr - ukazatel F-score.
To bychom měli k dnešním zápiskům na téma úvod do strojového učení pro zelenáče, jako jsem já. Paralelně s tím už se tady hrabu v noteboocích, hadech, pandách, mořských potvorách a tak podobně - už brzo si sem pár dalších poznámek odložím. Pokud máte nápady, připomínky či pravy, pište mi prosím na LinkedIn. Díky!