Skryté Markovovy modely: Viterbiho algoritmus a nepoctivé kasino
Skrytý Markovův model, tři úlohy podle Rabinera, dopředný algoritmus, Viterbiho algoritmus, aposteriorní dekódování a Baumův–Welchův algoritmus — na kasinu, které občas tajně vymění kostku za falešnou.
Série Základy AI Díl 39 z 83
- Mapa umělé inteligence: co je co a jak to do sebe zapadá
- Stručná historie umělé inteligence: od Turinga po jazykové modely
- Data, model, trénink, inference: základní pojmy na jednom příkladu
- Jak číst matematický zápis: symboly, indexy, sumy a kvantifikátory
- Množiny a relace: stavebnice, ze které se skládají definice v AI
- Funkce a zobrazení: proč je každý model v AI funkce
- Logika a důkazy: pravidla, podle kterých se v AI uvažuje
- Grafy a stromy: struktura, na které stojí hledání i neuronové sítě
- Lineární algebra pro strojové učení: vektory, matice a proč AI hlavně násobí
- Derivace, gradient a řetízkové pravidlo: jak model zjistí, kterým směrem se učit
- Gradientní sestup: jak se model učí
- Co je pravděpodobnost: od hodu kostkou ke Kolmogorovovým axiomům
- Podmíněná pravděpodobnost a Bayesova věta: jak změnit odhad, když se dozvíte něco nového
- Náhodná veličina a distribuční funkce: jedna funkce, která o rozdělení řekne všechno
- Funkce hustoty a pravděpodobnostní funkce: proč má spojitá veličina v bodě pravděpodobnost nula
- Střední hodnota, rozptyl a kovariance: kde rozdělení leží, jak je široké a co s čím souvisí
- Přehled pravděpodobnostních rozdělení: Bernoulli, binomické, Poisson, rovnoměrné, normální a exponenciální
- Zákon velkých čísel a centrální limitní věta: proč se průměry chovají tak předvídatelně
- Statistika a odhady parametrů: maximální věrohodnost, MAP a intervaly spolehlivosti
- Teorie informace: entropie, křížová entropie a KL divergence — odkud se berou ztrátové funkce
- Prohledávání stavového prostoru: BFS, DFS, UCS a A* s animacemi
- Hry a protivník: minimax, alfa-beta ořezávání a Monte Carlo tree search
- Splňování omezujících podmínek: backtracking, propagace omezení a AC-3 na sudoku a mapě krajů
- Logika a znalostní systémy: dopředné a zpětné řetězení, rezoluce a expertní systémy
- Plánování: STRIPS, svět kostek, heuristiky z relaxace a plánování v AI agentech
- Lokální hledání a simulované žíhání: obchodní cestující přes krajská města
- Genetické algoritmy: populace, křížení, mutace a výběr na batohu na výlet
- Evoluční strategie a genetické programování: od pravidla jedné pětiny po Keplerův zákon
- Optimalizace mravenčí kolonií: jak feromonová stopa najde krátkou cestu
- Optimalizace hejnem částic: setrvačnost, vlastní zkušenost a zkušenost hejna
- Neuroevoluce: evoluce vah i tvaru neuronových sítí a algoritmus NEAT
- Fuzzy množiny a funkce příslušnosti: jak zapsat „trochu teplo“
- Fuzzy logika a pravděpodobnost: proč „trochu teplo“ neznamená „pravděpodobně teplo“
- Fuzzy logika: t-normy, implikace a pravidla KDYŽ–PAK
- Fuzzy řízení: Mamdani a Sugeno od ventilátoru po kyvadlo na vozíku
- Fuzzy shlukování: algoritmus fuzzy c-means a body mezi shluky
- Neuro-fuzzy systémy (ANFIS): fuzzy pravidla, která se učí z dat
- Markovovy řetězce: přechodová matice, stacionární rozdělení a PageRank
- Skryté Markovovy modely: Viterbiho algoritmus a nepoctivé kasino
- Bayesovské sítě: proč je tráva mokrá a co z toho plyne pro postřikovač
- Kalmanův filtr: jak z nepřesných měření odhadnout, kde auto je a jak rychle jede
- Druhy strojového učení: s učitelem, bez učitele, s částečným učitelem a další
- Přeučení a regularizace: proč model, který zná data nazpaměť, selže
- Vyhodnocení modelu: křížová validace, F1 a křivka ROC
- Shlukování a snížení dimenze: k-means, PCA a t-SNE
- Klasifikace a regrese: dvě úlohy učení s učitelem
- Lineární regrese: nejmenší čtverce geometricky i maticově
- Polynomiální a nelineární regrese: jak přímku ohnout
- Logistická regrese: sigmoida, šance a křížová entropie
- Vícetřídní klasifikace a softmax: od dvou tříd k mnoha
- Naivní Bayesův klasifikátor: spamový filtr, který funguje lépe, než by měl
- Ztrátové funkce: co přesně model minimalizuje
- Rozhodovací stromy a k nejbližších sousedů: modely bez vzorců
- Ansámbly: náhodný les a gradient boosting
- SVM a jádrový trik: nejširší hranice a jak ji ohnout
- Signál a vzorkování: proč platí Nyquistův teorém
- Fourierova transformace: rozklad signálu na frekvence
- Filtry a konvoluce: vyhlazení, hrany a odstranění šumu
- Zvuk a řeč: spektrogram, melová škála a MFCC
- Obraz jako signál: pixely, barvy, konvoluce a hrany
- Perceptron a umělý neuron: nejjednodušší neuronová síť a co neumí
- Neuronová síť od nuly: zpětné šíření chyby krok za krokem
- Aktivace, inicializace a normalizace: drobnosti, na kterých hluboká síť stojí
- Konvoluční sítě: jak síť poznává obrázky
- Rekurentní sítě: paměť pro posloupnosti a mizející gradient
- LSTM a GRU: brány, které rozhodují, co si síť zapamatuje
- Autoenkodéry: síť, která se učí data stlačit a zase rozbalit
- Embeddingy a word2vec: jak se slovo stane vektorem
- Mechanismus pozornosti: jak se model naučí, kam se dívat
- Transformery a velké jazykové modely: architektura krok za krokem
- Tokenizace: jak se text rozseká na kousky pro model
- Předtrénování a škálování: proč větší model a víc dat funguje
- Doladění a RLHF: jak se z doplňovače textu stane asistent
- Prompty, kontext a RAG: jak modelu dát správné informace
- Variační autoenkodéry (VAE): nové obrázky ze spojitého latentního prostoru
- Generativní adversariální sítě (GAN): padělatel proti detektivovi
- Difúzní modely: jak se z čistého šumu stane obrázek
- Markovovy rozhodovací procesy: stavy, akce, odměny a Bellmanovy rovnice
- Q-learning a DQN: učení hodnoty akcí od tabulky po neuronovou síť
- Policy gradient a PPO: učení přímo strategie
- Jak funguje AI agent: jazykový model ve smyčce s nástroji
- Programování s AI agenty: zadání, testy a kontrola
- Vyhodnocení a bezpečnost AI systémů: jak měřit a jak hlídat
Celá osnova série 83 z 83 témat hotovo
- Co je skrytý Markovův model a jak se z pozorování spočítá jejich pravděpodobnost bez procházení všech cest.
- Jak Viterbiho algoritmus najde nejpravděpodobnější skryté stavy a kde se plete.
- Jak se model naučí parametry jen z pozorování a kolik dat k tomu potřebuje.
Co se hodí znát předem: Markovovy řetězce: přechodová matice, stacionární rozdělení a PageRank
V minulém dílu jsme stav řetězce viděli: dnes svítí, zítra prší. Často ale vidíme jen jeho projevy. Mikrofon zachytí zvukovou vlnu, ne slovo, které ji vytvořilo. Sekvenátor přečte písmena DNA, ne to, kde začíná gen. A v kasinu vidíme, co padlo, ne kterou kostkou krupiér hází. Skryté Markovovy modely jsou řetězce, jejichž stavy nevidíme — a algoritmy, kterými se dá i tak zjistit, co se děje uvnitř. Po desetiletí na nich stálo rozpoznávání řeči.
Navazuje na díl Markovovy řetězce.
1. Nepoctivé kasino
Klasický učebnicový příklad (Durbin a kol., 1998): krupiér hází buď poctivou kostkou, nebo falešnou, na které padá šestka s pravděpodobností 1/2 a ostatní čísla s 1/10. Po každém hodu s pravděpodobností 0,05 vymění poctivou za falešnou a s pravděpodobností 0,1 falešnou zpátky za poctivou. Výměnu nevidíme. Vidíme jen řadu čísel — v našem pokusu 300 hodů, které začínají takhle:
163665521623524236522511663566613666222461626262162666626516…
Ptáme se: hraje se tu poctivě? A pokud ne, kdy přesně byla ve hře falešná kostka?
2. Skrytý Markovův model
Definice — skrytý Markovův model
Nechť N, M ≥ 1 jsou celá čísla, S = {1, …, N} množina skrytých stavů a V = {1, …, M} množina pozorovatelných symbolů. Dále nechť
- π = (π1, …, πN) ∈ ℝ1×N je rozdělení pravděpodobnosti na S (πi ≥ 0, ∑i πi = 1) — počáteční rozdělení;
- A = (aij) ∈ ℝN×N je stochastická matice — přechodová matice;
- B = (bi(o))i ∈ S, o ∈ V ∈ ℝN×M je stochastická matice — emisní pravděpodobnosti.
Skrytý Markovův model (HMM) s parametry λ = (π, A, B) je dvojice posloupností náhodných veličin X1, X2, … : Ω → S (skryté stavy) a O1, O2, … : Ω → V (pozorování) na pravděpodobnostním prostoru (Ω, 𝓕, P), pro kterou platí:
- (Xt)t ≥ 1 je homogenní Markovův řetězec s počátečním rozdělením P(X1 = i) = πi a přechodovou maticí A, tj. aij = P(Xt+1 = j | Xt = i);
- pro každé T ≥ 1, t ∈ {1, …, T}, o ∈ V a všechny hodnoty x1, …, xT ∈ S a os ∈ V (s ≠ t), pro které má podmínka kladnou pravděpodobnost, P(Ot = o | X1 = x1, …, XT = xT, Os = os pro s ∈ {1, …, T} ∖ {t}) = bxt(o).
Slovy: skryté stavy se mění jako obyčejný Markovův řetězec a pozorování v čase t závisí jen na stavu Xt — ne na ostatních stavech ani na ostatních pozorováních. Pozorujeme jen O1, O2, …, stavy Xt ne.
Za oponou běží obyčejný Markovův řetězec — kostka se mění podle matice A. My vidíme jen to, co z každého stavu „vypadne“: hod podle pravděpodobností B. Šestka je pro falešnou kostku pětkrát pravděpodobnější než jakékoli jiné číslo, ale může padnout i na poctivé. Jednotlivý hod proto nic neprozradí — prozradí to až vzorec v celé řadě.
Z definice plyne, jak pravděpodobná je jedna konkrétní „historie“: posloupnost kostek x = (x1, …, xT) spolu s hody O = (O1, …, OT).
P(x, O | λ) = πx1 bx1(O1) · ∏t=2T axt−1 xt bxt(Ot)
- πx1
- první kostka nemá předchůdce, ze kterého by se na ni přešlo — její pravděpodobnost dává počáteční rozdělení
- axt−1 xt
- každá další kostka závisí jen na té předchozí (Markovova vlastnost), takže za každý krok stačí jedno číslo z matice A
- bxt(Ot)
- hod závisí jen na kostce, kterou se právě hází — bereme pravděpodobnost čísla, které opravdu padlo
- ∏t=2T
- obecně by se násobily pravděpodobnosti podmíněné celou minulostí (řetězové pravidlo); oba předpoklady nezávislosti z definice je zkrátí na tato místní čísla, a proto je celek prostý součin
Příklad — dvě historie prvních tří hodů
Naše řada začíná hody 1, 6, 3. Označme P poctivou a F falešnou kostku. Historie „třikrát falešná“:
P(FFF, 1, 6, 3) = 0,5 · 0,1 · 0,9 · 0,5 · 0,9 · 0,1 = 0,002025,
historie „třikrát poctivá“:
P(PPP, 1, 6, 3) = 0,5 · 1/6 · 0,95 · 1/6 · 0,95 · 1/6 = 0,002089
(barvy podle vzorce výše: počáteční rozdělení, emise, přechody). Poctivá historie vyjde o chlup pravděpodobnější: šestka na falešné kostce sice dostane 0,5 místo 1/6, ale jednička a trojka jen 0,1 místo 1/6. (V simulaci se přitom všechny tři hody házely falešnou kostkou — ze tří hodů se to poznat nedá.)
Lawrence Rabiner ve slavném přehledu z roku 1989 shrnul, co se s takovým modelem dá dělat, do tří úloh: ohodnocení (jak pravděpodobná je pozorovaná řada?), dekódování (jaké skryté stavy za ní nejspíš byly?) a učení (jaké parametry model má, když známe jen pozorování?). Na každou existuje elegantní algoritmus.
3. Jak pravděpodobná je řada: dopředný algoritmus
Pravděpodobnost pozorované řady je součet přes všechny možné posloupnosti skrytých stavů. Těch je pro 300 hodů a dvě kostky 2300 ≈ 2 · 1090 — víc, než je atomů ve vesmíru. Přímo to sečíst nejde. Markovova vlastnost ale dovolí počítat postupně.
Příklad — pravděpodobnost tří hodů součtem přes všechny historie
Pravděpodobnost pozorování je součet sdružené pravděpodobnosti přes všechny možné historie: P(O | λ) = ∑x P(x, O | λ). Pro hody 1, 6, 3 jich je 23 = 8:
| Historie | P(x, O) |
|---|---|
| PPP | 0,002089 |
| PPF | 0,000066 |
| PFP | 0,000035 |
| PFF | 0,000188 |
| FPP | 0,000132 |
| FPF | 0,000004 |
| FFP | 0,000375 |
| FFF | 0,002025 |
| součet | 0,004913 |
Každá historie stojí 2T − 1 = 5 násobení, dohromady 8 · 5 = 40. Pro T hodů je to (2T − 1) · 2T — u 300 hodů nepředstavitelně mnoho.
Algoritmus — dopředný algoritmus
Vstup: skrytý Markovův model λ = (π, A, B) se stavy S = {1, …, N} a symboly V; pozorovaná posloupnost O = (O1, …, OT) ∈ VT, T ≥ 1.
Výstup: P(O | λ) = P(O1, …, OT) a dopředné proměnné
αt(j) = P(O1, …, Ot, Xt = j | λ), t = 1, …, T, j ∈ S.
- Inicializace: pro každé j ∈ S polož α1(j) = πj bj(O1).
- Indukce: pro t = 1, …, T − 1 a každé j ∈ S polož αt+1(j) = [∑i=1N αt(i) aij] · bj(Ot+1).
- Ukončení: po zpracování času T vrať P(O | λ) = ∑j=1N αT(j).
Algoritmus skončí po T − 1 krocích indukce; každý stojí O(N2) operací, celkem tedy O(N2 T) místo O(T · NT) při sčítání přes všechny historie. Aby čísla nepodtekla, v každém čase t se spočítá ct = ∑j αt(j) (s už normalizovaným αt−1 v indukci), αt(j) se nahradí αt(j)/ct a vrací se log P(O | λ) = ∑t=1T log ct.
Nemusíte sledovat každou možnou historii zvlášť. Stačí si po každém hodu pamatovat dvě čísla: „jak pravděpodobné je všechno, co jsem zatím viděl, a že teď je ve hře poctivá kostka“ — a totéž pro falešnou. Z nich se spočítají čísla pro další hod. Všechny historie, které končí stejným stavem, se sloučí do jednoho čísla.
αt+1(j) = ∑i αt(i) aij · bj(Ot+1)
- αt(i)
- všechno, co víme o minulosti: pravděpodobnost dosavadních hodů a toho, že teď je ve hře kostka i; v tomhle jediném čísle jsou sloučené všechny historie končící v i
- aij
- krok řetězce z i do j; díky Markovově vlastnosti nezáleží na tom, jak jsme se do i dostali
- ∑i
- nevíme, kterou kostkou se házelo minule, a do j se dá dojít z kterékoli — proto sčítáme přes všechny možnosti (historie jsou navzájem neslučitelné jevy)
- bj(Ot+1)
- nový hod musí kostka j opravdu vygenerovat; nezávisí na i, a tak se dá vytknout před součet
Příklad — dopředný algoritmus na hodech 1, 6, 3
Start: α1(P) = 0,5 · 1/6 = 0,0833, α1(F) = 0,5 · 0,1 = 0,0500. Druhý hod je šestka (barvy podle vzorce výše):
α2(P) = (0,0833 · 0,95 + 0,0500 · 0,1) · 1/6 = 0,0140,
α2(F) = (0,0833 · 0,05 + 0,0500 · 0,9) · 0,5 = 0,0246.
| t | hod | αt(P) | αt(F) | součet = P(O1, …, Ot) | ct |
|---|---|---|---|---|---|
| 1 | 1 | 0,0833 | 0,0500 | 0,133333 | 0,1333 |
| 2 | 6 | 0,0140 | 0,0246 | 0,038611 | 0,2896 |
| 3 | 3 | 0,0026 | 0,0023 | 0,004913 | 0,1273 |
Součet v posledním řádku je přesně součet osmi historií z předchozího příkladu, jen místo 40 násobení stačilo 2 + 2 · 6 = 14. Poslední sloupec ct = P(Ot | O1, …, Ot−1) je normalizační konstanta ze škálování: jak pravděpodobný byl hod t vzhledem k těm předchozím. Jejich součin je P(O | λ), takže log P(O | λ) = ∑t log ct.
Proč logaritmy? U 3 000 hodů vyjde P(O | λ) kolem 10−2 278, a nejmenší kladné číslo, které počítač v běžné dvojité přesnosti (IEEE 754) uloží, je asi 4,9 · 10−324. Bez škálování by α podteklo na nulu.
def dopredu(hody, pi, A, B):
alfa = pi * B[:, hody[0]]
logP = 0.0
for t in range(len(hody)):
if t > 0:
alfa = (alfa @ A) * B[:, hody[t]] # součet přes i, pak emise
c = alfa.sum() # c_t = P(O_t | O_1, …, O_t−1)
alfa = alfa / c # škálování: α_t teď sčítá do 1
logP += np.log(c)
return logP # log P(O | λ)
Pro našich 300 hodů vyjde P(hody | model kasina) ≈ 10−230,03. Kdyby se celou dobu hrálo poctivě, byla by pravděpodobnost (1/6)300 ≈ 10−233,45. Model s občasnou falešnou kostkou vysvětluje pozorované hody asi 2 600krát lépe — silný důkaz, že se tu podvádí, i když žádný jednotlivý hod podvod neprozradí.
Definice — poměr věrohodností (Bayesův faktor)
Nechť (Ω, 𝓕, P) je pravděpodobnostní prostor, M1, M2 ∈ 𝓕 jsou jevy „platí model 1“ a „platí model 2“ s P(M1) > 0, P(M2) > 0 a O ∈ 𝓕 je jev „pozorovali jsme data“ s P(O | M2) > 0. Bayesův faktor modelu M1 proti M2 je číslo
K = P(O | M1)/P(O | M2),
tj. podíl věrohodností dat v obou modelech. K > 1 znamená, že data jsou pravděpodobnější v modelu M1. Je-li navíc P(O) > 0, plyne z Bayesovy věty napsané pro oba modely a vydělené (P(O) se zkrátí):
P(M1 | O)/P(M2 | O) = P(O | M1)/P(O | M2) · P(M1)/P(M2)
- P(M1 | O)/P(M2 | O)
- poměr šancí po pozorování — to, co nás zajímá: o kolik víc věříme podvodu než poctivé hře, když jsme hody viděli
- P(O | M1)/P(O | M2)
- Bayesův faktor — jediné místo, kudy do vzorce vstupují data; obě věrohodnosti spočítá dopředný algoritmus. Jmenovatel P(O) z Bayesovy věty se při dělení zkrátí, a proto ho není třeba znát
- P(M1)/P(M2)
- poměr šancí předem — kdo kasinu předem věří, potřebuje silnější důkaz; data ho jen přenásobí
Výpočet. log10 K = −230,03 − (−233,45) = 3,42 (z nezaokrouhlených hodnot 3,413), tedy K ≈ 103,413 ≈ 2 590, zaokrouhleně 2 600. Kdo předem považoval podvod a poctivou hru za stejně pravděpodobné (poměr šancí 1), má po 300 hodech šance 2 600 · 1 = 2 600 : 1 pro podvod. Podle běžně používané stupnice Kasse a Rafteryho (1995) je Bayesův faktor nad 150 „velmi silný“ důkaz.
4. Kdy se podvádělo: Viterbiho algoritmus
Algoritmus — Viterbiho algoritmus (1967)
Vstup: skrytý Markovův model λ = (π, A, B) se stavy S = {1, …, N}; pozorování O = (O1, …, OT) ∈ VT, T ≥ 1.
Výstup: nejpravděpodobnější posloupnost skrytých stavů a její pravděpodobnost
x* = argmaxx ∈ ST P(X1 = x1, …, XT = xT, O | λ), P* = P(x*, O | λ).
Pro t = 1, …, T a j ∈ S označme δt(j) pravděpodobnost nejlepší cesty, která v čase t končí ve stavu j:
δt(j) = maxx1, …, xt−1 ∈ S P(X1 = x1, …, Xt−1 = xt−1, Xt = j, O1, …, Ot | λ).
- Inicializace: pro každé j ∈ S polož δ1(j) = πj bj(O1).
- Indukce: pro t = 1, …, T − 1 a každé j ∈ S polož δt+1(j) = maxi ∈ S [δt(i) aij] · bj(Ot+1), ψt+1(j) = argmaxi ∈ S δt(i) aij (ψt+1(j) je nejlepší předchůdce; při shodě vyber kterýkoli).
- Ukončení: P* = maxj ∈ S δT(j) a x*T = argmaxj ∈ S δT(j).
- Zpětný průchod: pro t = T − 1, T − 2, …, 1 polož x*t = ψt+1(x*t+1); po t = 1 vrať x* = (x*1, …, x*T) a P*.
Algoritmus skončí po T − 1 krocích indukce a T − 1 krocích zpětného průchodu; čas je opět O(N2 T). V praxi se místo součinů sčítají logaritmy, aby čísla nepodtekla.
Stejná myšlenka jako dopředný algoritmus, jen místo součtu přes všechny cesty se bere ta nejlepší. Do každého stavu v každém čase vede jediná „vítězná“ cesta — kdyby nejlepší celková cesta šla tudy, stejně by do tohoto bodu přišla po ní. Je to dynamické programování, příbuzné hledání nejkratší cesty z dílu o prohledávání.
δt+1(j) = maxi δt(i) aij · bj(Ot+1), ψt+1(j) = argmaxi δt(i) aij
- δt(i)
- pravděpodobnost nejlepší historie, která v čase t končí kostkou i; horší historie do i už nepotřebujeme — kdyby nejlepší celá cesta vedla přes i, pokračovala by po té nejlepší
- aij
- cena přechodu z i do j; malé aPF = 0,05 je důvod, proč se Viterbimu nechce přepínat kvůli jedné šestce
- maxi, ψt+1(j)
- na rozdíl od dopředného algoritmu nesčítáme, ale vybíráme nejlepšího předchůdce — chceme jednu cestu, ne pravděpodobnost všech; ψ si pamatuje, kdo vyhrál (v mřížce jantarové šipky), aby šla cesta na konci zpětně sestavit
- bj(Ot+1)
- jak dobře kostka j vysvětluje nový hod — stejně jako u α
Na dvanácti hodech je vidět, jak Viterbi rozhoduje. Prvních šest hodů (5, 2, 2, 5, 1, 1) vypadá poctivě. Pak padnou dvě šestky za sebou a další dvě o kousek dál — a nejlepší cesta přepne na falešnou kostku. Ve skutečnosti krupiér vyměnil kostku až o čtyři hody později; dvě šestky za sebou padají i poctivé kostce.
Příklad — Viterbi krok po kroku na hodech 1, 6, 3
Start je stejný jako u α: δ1(P) = 0,0833, δ1(F) = 0,0500. Pro šestku ve druhém hodu:
δ2(P) = max(0,0833 · 0,95; 0,0500 · 0,1) · 1/6 = 0,0792 · 1/6 = 0,0132 (vyhrál P),
δ2(F) = max(0,0833 · 0,05; 0,0500 · 0,9) · 0,5 = 0,0450 · 0,5 = 0,0225 (vyhrál F)
(barvy podle vzorce výše).
| t | hod | δt(P) | ψt(P) | δt(F) | ψt(F) |
|---|---|---|---|---|---|
| 1 | 1 | 0,083333 | — | 0,050000 | — |
| 2 | 6 | 0,013194 | P | 0,022500 | F |
| 3 | 3 | 0,002089 | P | 0,002025 | F |
Na konci vyhraje P (0,002089 > 0,002025) a zpětně po odkazech ψ vyjde cesta PPP — přesně historie z prvního příkladu, která byla ze všech osmi nejpravděpodobnější. Dopředný algoritmus ale pro třetí hod dává P(F | O1, O2, O3) = α3(F)/α3(P) + α3(F) = 0,002283/0,004913 = 0,465. Nejlepší cesta a nejpravděpodobnější stav se tu shodují (P), ale „vítězství“ je těsné a tři hody na jistotu nestačí. Obecně se shodovat nemusejí — k tomu se vrátíme u aposteriorního dekódování.
import numpy as np
def viterbi(hody, pi, A, B):
T, N = len(hody), len(pi)
delta = np.log(pi) + np.log(B[:, hody[0]])
odkud = np.zeros((T, N), dtype=int)
for t in range(1, T):
kandidati = delta[:, None] + np.log(A) # z každého i do každého j
odkud[t] = kandidati.argmax(axis=0)
delta = kandidati.max(axis=0) + np.log(B[:, hody[t]])
cesta = [int(delta.argmax())]
for t in range(T - 1, 0, -1): # zpátky po vítězných odkazech
cesta.append(odkud[t][cesta[-1]])
return cesta[::-1]
5. Všech 300 hodů
Falešná kostka byla ve hře 88krát, v devíti úsecích. Viterbi označil za falešné 62 hodů, z toho 56 správně, a celkem určil kostku správně u 87,3 % hodů. Dlouhé úseky podvodu našel, krátké ale přehlédl nebo slil dohromady — místo devíti úseků vidí dva. Na krátkém úseku prostě nepadne dost šestek, aby to stálo za „drahou“ výměnu kostky (pravděpodobnost výměny je jen 0,05).
Definice — aposteriorní pravděpodobnost stavu
Nechť λ = (π, A, B) je skrytý Markovův model se stavy S = {1, …, N} a O = (O1, …, OT) pozorování s P(O | λ) > 0. Aposteriorní pravděpodobnost stavu i ∈ S v čase t ∈ {1, …, T} je
γt(i) = P(Xt = i | O1, …, OT, λ).
K jejímu výpočtu slouží zpětné proměnné (pro t < T a P(Xt = i) > 0)
βt(i) = P(Ot+1, …, OT | Xt = i, λ), βT(i) = 1.
Dopředně-zpětný algoritmus spočítá αt(i) dopředným algoritmem, βt(i) odzadu (pro t = T − 1, …, 1) a z obou γt(i) podle vzorců níže. Slovy: γt(i) je pravděpodobnost, že v čase t byl model ve stavu i, když známe celou pozorovanou řadu — minulost i budoucnost.
Viterbi dá jednu odpověď „byla to tahle kostka“. Aposteriorní pravděpodobnost řekne, jak moc si jsme jistí — pro každý hod zvlášť, s ohledem na všechno, co padlo předtím i potom. Spodní křivka v obrázku: kde vystoupá nad polovinu, spíš se podvádí.
γt(i) = αt(i) · βt(i)/P(O | λ), βt(i) = ∑j aij bj(Ot+1) βt+1(j), βT(i) = 1
- γt(i)
- pravděpodobnost, že v čase t byla ve hře kostka i, když známe všechny hody — pro falešnou kostku je to červená křivka v obrázku
- αt(i)
- důkazy z minulosti: hody 1 až t a stav i v čase t
- βt(i)
- důkazy z budoucnosti: jak dobře stav i vysvětluje hody t + 1 až T. Počítá se odzadu stejným trikem jako α — jeden krok řetězce, emise, zbytek budoucnosti. Násobit α a β smíme, protože při známém stavu Xt jsou minulé a budoucí hody nezávislé
- P(O | λ)
- součin α·β je sdružená pravděpodobnost P(Xt = i, O); dělením pravděpodobností všech hodů z ní uděláme podmíněnou. Je to totéž číslo, které dal dopředný algoritmus, a platí ∑i αt(i) βt(i) = P(O | λ) v každém t
Příklad — druhý hod: co přidá budoucnost
Zůstaňme u hodů 1, 6, 3 a ptejme se na druhý hod (šestku). Zpětný krok od β3 = 1 s třetím hodem (trojkou):
β2(P) = 0,95 · 1/6 + 0,05 · 0,1 = 0,1633, β2(F) = 0,1 · 1/6 + 0,9 · 0,1 = 0,1067.
(V každém součinu je první činitel přechod aij, druhý emise trojky bj(3) a β3(j) = 1 se nepíše.) Podle vzorce výše (barvy odpovídají jeho členům):
γ2(F) = 0,024583 · 0,106667/0,004913 = 0,534.
Kdybychom znali jen první dva hody, bylo by P(F | O1, O2) = α2(F)/α2(P) + α2(F) = 0,637. Trojka, která padla potom, je na falešné kostce méně pravděpodobná, a tak podezření na druhý hod zpětně sníží na 0,534.
Definice — aposteriorní dekódování a přesnost
Nechť λ je skrytý Markovův model se stavy S, O = (O1, …, OT) pozorování a γt(i) aposteriorní pravděpodobnosti stavů z předchozí definice. Aposteriorní dekódování je posloupnost x̂ = (x̂1, …, x̂T) ∈ ST, kde
x̂t = argmaxi ∈ S γt(i) pro každé t = 1, …, T
(stav se volí v každém čase zvlášť). Nechť dále x ∈ ST je skutečná posloupnost stavů a y ∈ ST libovolné dekódování (aposteriorní nebo Viterbiho). Přesnost dekódování y je podíl časů, ve kterých se shoduje se skutečností:
přesnost(y, x) = |{t ∈ {1, …, T} : yt = xt}|/T ∈ [0, 1].
Aposteriorní dekódování maximalizuje očekávaný počet správně určených hodů, Viterbi pravděpodobnost, že je správně celá cesta naráz. Proto může aposteriorní posloupnost obsahovat i přechod, který má v modelu pravděpodobnost nula — u kasina takový není, ale obecně to nastat může.
Kdybychom u každého hodu vybrali kostku s větší aposteriorní pravděpodobností, trefili bychom 87,7 % — o chlup víc než Viterbi (263 hodů z 300 proti 262, rozdíl jediného hodu). (Viterbi hledá nejlepší celou cestu, aposteriorní dekódování nejlepší odhad každého hodu zvlášť; to není totéž.) A pozor na jednu simulaci: ve dvanácti různých pokusech se přesnost Viterbiho algoritmu pohybovala od 73,3 do 90,0 %, v průměru 83,4 %. Podle toho, jak dlouhé úseky podvodu náhoda vyrobí.
6. Učení: Baumův–Welchův algoritmus
Zatím jsme parametry kasina znali. Co když víme jen to, že kostky jsou dvě, a všechno ostatní je třeba vyčíst z hodů?
Algoritmus — Baumův–Welchův algoritmus
Je to algoritmus EM (Baum a kol., kolem roku 1970).
Vstup: počet skrytých stavů N ≥ 1, množina symbolů V, pozorování O = (O1, …, OT) ∈ VT s T ≥ 2, počáteční odhad parametrů λ(0) = (π, A, B) s P(O | λ(0)) > 0 a počet iterací K ≥ 1 (nebo tolerance ε > 0).
Výstup: parametry λ(K) s věrohodností P(O | λ(K)) ≥ P(O | λ(0)).
Pro k = 0, 1, …, K − 1 opakuj s parametry λ(k):
- Krok E: dopředně-zpětným algoritmem spočítej αt(i), βt(i), P(O | λ(k)), aposteriorní pravděpodobnosti γt(i) pro t = 1, …, T a pravděpodobnosti přechodů ξt(i, j) = P(Xt = i, Xt+1 = j | O, λ(k)), t = 1, …, T − 1, i, j ∈ S.
- Krok M: nové parametry λ(k+1) jsou pro všechna i, j ∈ S a o ∈ V πi = γ1(i), aij = ∑t=1T−1 ξt(i, j)/∑t=1T−1 γt(i), bi(o) = ∑t : Ot = o γt(i)/∑t=1T γt(i).
- Zastavení: skonči po K iteracích, případně dřív, když log P(O | λ(k+1)) − log P(O | λ(k)) < ε.
Platí P(O | λ(k+1)) ≥ P(O | λ(k)) — věrohodnost v žádné iteraci neklesne. Algoritmus konverguje ke stacionárnímu bodu věrohodnosti, v praxi obvykle k lokálnímu maximu, které nemusí být globální; výsledek proto závisí na počátečním odhadu.
Kdybychom věděli, kdy byla ve hře která kostka, spočítali bychom parametry prostým počítáním. Nevíme to — ale s nějakým odhadem parametrů umíme spočítat, jak pravděpodobně byla která kostka kde. Spočítáme tedy „měkké“ počty, z nich lepší parametry, z nich lepší počty… Každé kolo model zlepší.
Očekávaný počet přechodů se spočítá ze stejných α a β jako γ, jen se mezi ně vloží jeden krok řetězce:
ξt(i, j) = αt(i) aij bj(Ot+1) βt+1(j)/P(O | λ)
- αt(i)
- hody do času t a kostka i v čase t — levý konec přechodu
- aij
- samotný přechod i → j, jehož četnost odhadujeme
- bj(Ot+1)
- hod t + 1 musí vygenerovat nová kostka j
- βt+1(j)
- zbytek hodů od t + 2 dál, vysvětlený z kostky j — pravý konec přechodu
- P(O | λ)
- dělením ze sdružené pravděpodobnosti „přechod a všechny hody“ uděláme podmíněnou „přechod, když známe hody“
âij = ∑t=1T−1 ξt(i, j)/∑t=1T−1 γt(i)
- âij
- nový odhad pravděpodobnosti přechodu — relativní četnost, jako bychom počítali čárky
- ∑ ξt(i, j)
- očekávaný počet přechodů i → j; místo „přešlo se, nebo ne“ (1 nebo 0) sčítáme pravděpodobnosti, protože stavy nevidíme
- ∑ γt(i)
- očekávaný počet odchodů ze stavu i; sčítá se jen do T − 1, protože z posledního stavu už žádný přechod nevede. Díky tomu řádek nové matice sčítá do 1
opakuj (u nás 100krát):
α, β, P(O) ← dopředně-zpětný algoritmus s aktuálními π, A, B
# krok E: měkké počty
pro každé t, i: γ[t][i] ← α[t][i] · β[t][i] / P(O)
pro každé t < T, i, j: ξ[t][i][j] ← α[t][i] · a[i][j] · b[j](O[t+1]) · β[t+1][j] / P(O)
# krok M: relativní četnosti
π[i] ← γ[1][i]
a[i][j] ← ∑_{t<T} ξ[t][i][j] / ∑_{t<T} γ[t][i]
b[i](o) ← ∑_{t: O[t]=o} γ[t][i] / ∑_t γ[t][i]
Začali jsme od špatného odhadu: kostky se skoro neliší a vyměňují se často. Ze 300 hodů se Baum–Welch dostal k modelu, který kostky nevyměňuje skoro vůbec a falešné kostce dává šestku jen s pravděpodobností 0,39 — na spolehlivý odhad je 300 hodů málo. Z 3 000 hodů (jiné simulace téhož kasina) už vyšel velmi blízko pravdě:
| Parametr | Skutečnost | Odhad z 3 000 hodů |
|---|---|---|
| poctivá → falešná | 0,05 | 0,039 |
| falešná → poctivá | 0,10 | 0,090 |
| šestka na falešné kostce | 0,50 | 0,491 |
| šestka na poctivé kostce | 0,167 | 0,163 |
Proč 300 hodů nestačí, ukazuje věrohodnost. Během 100 iterací na našich 300 hodech vzrostla z 10−231,26 na 10−227,14 — a to je víc než 10−230,03 skutečného modelu kasina. Naučený model tedy vysvětluje právě tyhle hody lépe než pravda: přizpůsobil se i náhodným výkyvům konkrétních dat. Odhad maximální věrohodnosti se k pravdě blíží teprve s přibývajícími daty.
7. Skryté Markovovy modely v praxi
- Rozpoznávání řeči. Od osmdesátých let do nástupu hlubokých sítí kolem roku 2010 stály skoro všechny systémy na HMM: skryté stavy jsou části hlásek, pozorování krátké úseky zvukového spektra a Viterbi hledá nejpravděpodobnější posloupnost slov.
- Jazyk. Určování slovních druhů: skrytý stav je slovní druh, pozorování slovo. „Stát“ může být podstatné jméno i sloveso — rozhodne kontext.
- Bioinformatika. Hledání genů v DNA (kódující a nekódující úseky jsou skryté stavy) nebo profilové HMM pro rodiny bílkovin v databázi Pfam.
- Spojité stavy. Když skrytý stav není „kostka A nebo B“, ale poloha a rychlost, místo HMM se používá Kalmanův filtr — má v osnově vlastní díl.
8. Shrnutí
- Skrytý Markovův model je Markovův řetězec skrytých stavů, ze kterých vidíme jen emise; určují ho π, A a B.
- Dopředný algoritmus spočítá pravděpodobnost pozorování v čase O(N2T) místo součtu přes 2300 cest; kasino s podvodem vysvětluje naše hody asi 2 600krát lépe než poctivá hra.
- Viterbiho algoritmus najde nejpravděpodobnější cestu skrytých stavů dynamickým programováním — v naší simulaci určil kostku správně u 87,3 % hodů, krátké úseky ale přehlédl.
- Dopředně-zpětný algoritmus dá aposteriorní pravděpodobnost stavu v každém čase.
- Baumův–Welchův algoritmus se parametry naučí jen z pozorování — ale potřebuje dost dat: 300 hodů nestačilo, 3 000 ano.
Chcete si to osahat? Nechte AI agenta naučit HMM se dvěma stavy na denních výnosech akciového indexu — a podívejte se, jestli sám najde „klidná“ a „rozbouřená“ období trhu.
Máte na to jiný názor? Napište na support@coden-app.com.