Soubor programů pro práci se skrytými Markovovými modely (HTK) Zdeněk Smékal, Hicham Atassi, Vojtěch Stejskal, Jiří Mekyska Ústav telekomunikací, FEKT VUT, Purkyňova 118, Brno smekal@feec.vutbr.cz, xatass01@stud.feec.vutbr.cz, v.stejskal@gmail.com, xmekys01@stud.feec.vutbr.cz 5. března 2009 Úvod Modul HMM (the Hidden Markov Models) je soubor programů, které jsou určeny k tvorbě a práci se skrytými Markovovými modely. Tento modul (toolkit), označený jako HTK, byl původně vyvinut v roce 1989 ve Speech Vision and Robotics Group na Cambridgské univerzitě. Během let procházel postupným vývojem a v roce 2006, kdy vyšla zatím poslední stabilní verze 3.4, se jednalo o velmi rozsáhlý a účinný nástroj k rozpoznávání spojité řeči. Modul HTK se pro práci s HMM osvědčil, a tak se dnes využívá i v mnoha jiných oblastech, než je zpracování řeči [1]. V článku je uveden stručný popis struktury modulu a jsou popsány některé aplikace rozpoznávání spojité řeči s omezeným slovníkem. Klíčová slova: Skryté Markovovy modely, rozpoznávání spojité řeči. 1 Skryté Markovovy modely Skryté Markovovy modely jsou statistickým nástrojem pro modelování náhodných procesů [2]. Jedná se o modely, které jsou založeny na pravděpodobnostním konečném automatu, který v každém časovém okamžiku t změní svůj stav podle daných pravděpodobností přechodu do stavu s j, přičemž při této změně generuje vektor pozorování o t s výstupní pravděpodobností b j (o t ). 11-1
Obr. 1.1: Markovův model se šesti stavy 1.1 Rozpoznávání izolovaných slov Uvažujme nyní, že vektory pozorování o 1, o 2,..., o T jsou parametrické vektory vypočítané pro každý rámec řečového signálu. Symbol T vyjadřuje počet časových okamžiků, při kterých dochází ke generování vektorů pozorování (celkový počet vektorů pozorování). Tyto vektory obsahují typické parametry, které charakterizují analyzovaný signál. Jeden vektor o t tak může např. obsahovat melovské kepstrální koeficienty (MFCC) nebo percepční lineární predikční koeficienty (PLP) apod. Každé slovo pak bude popsáno posloupností těchto vektorů (parametrickou maticí): O = [o 1, o 2,..., o T ]. (1.1) Dále uvažujme, že disponujeme slovníkem N slov (difonů, trifonů apod.) w i, kde i = 2,..., N. Celý problém rozpoznávání izolovaných slov pak můžeme popsat pomocí podmíněné pravděpodobnosti slova w i, známe-li vektor O, takto (algoritmus EM) [3]: arg max i {P (w i O)}. (1.2) Pravděpodobnost P (w i O) nemůžeme vypočítat přímo, nicméně ji lze vypočítat pomocí Bayesova vztahu [3]: P (w i O) = P (O w i) P (w i ), (1.3) P (O) kde P (w i ) je apriorní pravděpodobnost výskytu w i, P (O) je pravděpodobnost posloupnosti vektorů pozorování a P (O w i ) je podmíněná pravděpodobnost posloupnosti O, jestliže známe slovo w i. Apriorní pravděpodobnost P (w i ) iterativně přizpůsobujeme v dalších krocích pomocí učení, nebo se ji snažíme nějak vynechat nebo 11-2
ji obejít. V případě, že slova budou mít stejnou pravděpodobnost P (w i ) a P (O) bude konstantní, poté bude pravděpodobnost P (w i O) závislá pouze na P (O w i ) [4]. V tomto bodě aplikujeme model HMM. Ke každému slovu w i bude přiřazen model M, který bude generovat sekvenci O. Příklad skrytého Markovova modelu je na obr. 1.1. Model se skládá ze 4 emitujících stavů (2, 3, 4 a 5) a prvního a posledního neemitujícího stavu. Neemitující stavy negenerují při přechodu do jiného stavu vektor pozorování, slouží k navazování modelů, což se využívá např. při rozpoznávání spojité řeči. Mezi jednotlivými stavy modelu existují přechodové pravděpodobnosti a ij, které určují pravděpodobnost přechodu ze stavu s i do stavu s j při posunu z času t do t + 1. Ke každému emitujícímu stavu jsou pak přiřazeny funkce (jedna nebo více) rozdělení výstupní pravděpodobnosti b j (o t ). 1.1.1 Přechodové pravděpodobnosti Přechodové pravděpodobnosti a ij se uvažují v čase konstantní po celou dobu generování. Rovněž platí, že součet těchto pravděpodobností vycházejících z jednoho stavu je roven 1: a ij = 1. (1.4) j V našem příkladě na obr. 1.1 existují tři druhy přechodových pravděpodobností: a i,i přechodová pravděpodobnost setrvání ve stavu s i, a i,i+1 pravděpodobnost přechodu do následujícího stavu s i+1, a i,i+2 přechodová pravděpodobnost přeskočení stavu (používá se zřídka, většinou u modelů krátkého ticha) [4]. Přechodové pravděpodobnosti je vhodné zapisovat maticově, zápis matice pro náš příklad bude následující: 0 a 12 0 0 0 0 0 a 22 a 23 a 24 0 0 A = 0 0 a 33 a 34 a 35 0 0 0 0 a 44 a 45 0. (1.5) 0 0 0 0 a 55 a 56 0 0 0 0 0 0 Většina modelů, které se používají k rozpoznávání řeči, je levo-pravých, tudíž platí a ji = 0 pro i < j [4]. V matici A se to projeví tím, že jsou všechny členy pod hlavní diagonálou nulové. 11-3
1.1.2 Funkce rozdělení výstupní pravděpodobnosti Jak jsme již zmínili, funkce rozdělení výstupní pravděpodobnosti b j (o t ) popisuje rozdělení pravděpodobnosti vektoru pozorování o t produkovaného ve stavu s j v čase t. V závislosti na pozorování můžeme tuto funkci popsat dvěma způsoby: Diskrétní rozdělení výstupní pravděpodobnosti V tomto případě nabývá pozorování konečného počtu diskrétních hodnot. Vektory pozorování jsou tedy konvertovány na diskrétní symboly Ŝi pomocí klasifikátoru založeném na minimální vzdálenosti. Každý stav pak obsahuje tabulku vysílacích pravděpodobností jednotlivých symbolů b j (Ŝi). Tyto modely se označují jako diskrétní modely HMM. Spojité rozdělení výstupní pravděpodobnosti V tomto případě je b j (o t ) funkcí hustoty pravděpodobností, která je popsána určitým rozdělením. Toto rozdělení pravděpodobnosti musí být dostatečně specifické, aby od sebe odlišilo různé zvuky, a zároveň dostatečně robustní, aby zahrnulo značnou variabilitu řečového signálu [2]. Při zpracování řečového signálu se nejčastěji používá buď jedno Gaussovo rozdělení pravděpodobnosti nebo směs těchto rozdělení [3]. Uvažujme nyní, že rozdělíme vektor pozorování do několika toků (vláken) S. Jednotlivé proudy mohou být například takové: 1. tok melovské kepstrální koeficienty, 2. tok 1. diference MFCC (rychlostní koeficienty), 3. tok 2. diference MFCC (akcelerační koeficienty), 4. tok logaritmus energie. Vztah pro výpočet b j (o t ) bude následující [3]: [ γs S Ms b j (o t ) = c jsm N (o st ; µ jsm, Σ jsm )], (1.6) s=1 m=1 kde mezi sebou násobíme směsi Gaussových rozdělení pro jednotlivé toky. Parametr M s zde udává počet směsí pro tok s, c jsm je váha m-té směsi, γ s váha toku a N (o; µ, Σ) je vícerozměrné Gaussovo rozdělení pravděpodobnosti s vektorem středních hodnot µ a kovarianční maticí Σ. Gaussovo rozdělení je definováno takto [3]: 1 N (o; µ, Σ) = 1 (2π)n Σ e 2 (o µ)t Σ 1 (o µ), (1.7) kde n je dimenze vektoru o a Σ determinant kovarianční matice. 11-4
1.1.3 Stavová posloupnost Obr. 1.2: Stavové posloupnosti definující přiřazení vektorů pozorování jednotlivým stavům. Červeně vyznačená cesta je cesta stavové sekvence X = [1, 2, 2, 3, 4, 4, 5, 6]. Vektory pozorování mohou být rozděleny mezi stavy modelu HMM pomocí stavové posloupnosti. Tato posloupnost udává pořadí stavů, kterými model prochází, jinými slovy se jedná o cestu modelem. Jde o podobnou úlohu, jako je úloha obchodního cestujícího, která se řeší např. metodami dynamického programování. V našem příkladě pro model na obr. 1.1 by stavová posloupnost byla X = [1, 2, 2, 3, 4, 4, 5, 6]. Jestliže tedy bude počet vektorů pozorování K, pak bude délka stavové posloupnosti rovna K + 2, jelikož x(0) a x(k + 1) jsou neemitující stavy. Stavovou posloupnost můžeme vyjádřit i ve formě grafu tak, jak je uvedeno na obr. 1.2. 1.1.4 Určení pravděpodobnosti generování posloupnosti vektorů pozorování modelem M Pravděpodobnost generování posloupnosti vektorů pozorování O modelem M při průchodu cesty X definujeme následovně [4]: P (O, X M) = a x(0)x(1) T t=1 b x(t) (o t )a x(t)x(t+1). (1.8) Jedná se v podstatě o součin všech pravděpodobností, na které při průchodu cestou X narazíme. Pro náš příklad na obr. 1.2 by měla pravděpodobnost tvar: P (O, X M) = a 12 b 2 (o 1 )a 22 b 2 (o 2 )a 23 b 3 (o 3 )... a 56 b 5 (o 6 ). 11-5
Nicméně ve většině případů známe pouze posloupnost vektorů pozorování O a stavová posloupnost X je před námi skryta. To je důvod, proč se tyto modely označují jako skryté Markovovy modely. Uvažujme nyní, že máme K vektorů pozorování. Délka stavové posloupnosti tedy bude K + 2. Počet stavů modelu je N, přičemž první a poslední stav je neemitující. Model může setrvat v jednom stavu (K N)krát. Se znalostí těchto hodnot můžeme sestavit graf tak, jak je tomu např. na obr. 1.2. Hrany grafu zde představují přechodové pravděpodobnosti a ij a jednotlivé uzly funkci hustoty výstupní pravděpodobnosti b j (o t ). Existují dva způsoby, jak můžeme nyní pravděpodobnost generování posloupnosti vektorů pozorování O modelem M vypočítat: 1. Vypočítáme sumu pravděpodobností všech cest modelem (grafem) [3]: P (O M) = X T a x(0)x(1) t=1 b x(t) (o t )a x(t)x(t+1). (1.9) Tento vzorec slouží k výpočtu Baumovy-Welchovy pravděpodobnosti [4]. Symbol X znamená sumu pro celou stavovou posloupnost. 2. Uvažujeme pouze nejvíce pravděpodobnou stavovou posloupnost (na obr. 1.2 vyznačeno červeně) [3]: ˆP (O M) = max X { a x(0)x(1) T t=1 b x(t) (o t )a x(t)x(t+1) } Tento vzorec popisuje Viterbiho pravděpodobnost [4].. (1.10) 1.1.5 Rozpoznání izolovaného slova Nyní si vše, o čem jsme doposud uvažovali, shrneme. Disponujeme záznamem slova, které chceme rozpoznat. Signál v časové oblasti rozdělíme na rámce, přičemž z každého rámce vypočítáme určité parametry. Každému rámci bude tedy odpovídat jeden vektor pozorování, který bude tyto parametry uchovávat. Posloupnost těchto vektorů pozorování označíme jako O. Uvažujme, že hledané slovo patří do omezené množiny slov. Hledáme tedy v této množině slovo w i, které bude nejvíce odpovídat sekvenci O, jinými slovy hledáme arg max i {P (w i O)}. Po jistých úvahách jsme přišli na to, že stačí vypočítat P (O w i ). Jestliže ke každému slovu z množiny existuje natrénovaný model HMM, můžeme tuto pravděpodobnost definovat takto [3]: P (O w i ) = P (O M i ). (1.11) 11-6
Zjistili jsme tedy, že k tomu, abychom slovo rozpoznali, stačí zjistit, který model generuje toto slovo (jeho posloupnost pozorování O) s největší pravděpodobností. Tuto pravděpodobnost lze vypočítat dvěma způsoby, přičemž při rozpoznávání se nejčastěji používá Viterbiho rozpoznávání [3]. Nicméně k tomu, abychom měli modely natrénované, je potřeba pro každý model určit jeho a ij a b j (o t ). Tyto parametry lze získat trénováním pomocí trénovacích množin. 1.1.6 Trénování modelu HMM a odhad jeho parametrů Princip trénování modelu M pro posloupnost pozorování O, kterou jsme vybrali z množiny trénovacích dat, je založen na nastavení všech parametrů modelu tak, aby tento model generoval sekvenci O s největší pravděpodobností. K tomuto účelu se používají metody maximální věrohodnosti MLE (Maximum Likehood Estimation). Při výpočtu parametrů uvedenou metodou je možné použít rychlé iterativní algoritmy. Při trénování se používá Baumův-Welchův algoritmus [2]. Tento algoritmus vypočítává z modelu M nový model ˆM tak, že platí P (O ˆM) P (O M). Algoritmus se ukončí, jakmile nastane mezi pravděpodobnostmi rovnost. K tomu dojde v okamžiku, kdy pravděpodobnost dosáhne svého globálního maxima. Bohužel může nastat i případ, kdy toto maximum je pouze lokální. Proto se musí na začátku algoritmu vhodně inicializovat parametry modelu tak, abychom se tomuto nežádoucímu jevu vyhnuli. Před popisem samotného Baumova-Welchova algoritmu si ještě zavedeme pravděpodobnost 1 toho, že v čase t generujeme vektor pozorování o t skrytým Markovovým modelem M, který se nachází ve stavu s j [3]: L j (t) = P (x(t) = s j O, M). (1.12) K výpočtu L j (t) se používá tzv. dopředně zpětný (forward-backward) algoritmus, který při výpočtu využívá dvě pravděpodobnosti: částečná dopředná (forward) pravděpodobnost α j (t), která je definována jako suma pravděpodobností všech částečných posloupností, které začínají na začátku modelu a v čase t se nachází ve stavu s j [4], [2]: α j (t) = P (o 1, o 2,..., o t, s(t) = s j M). (1.13) 1 Přesnější výraz než pravděpodobnost by možná byla věrohodnost (anglicky likelihood). Nicméně v textu budeme používat výraz pravděpodobnost. 11-7
částečná zpětná (backward) pravděpodobnost β j (t), která je definována jako suma pravděpodobností všech částečných posloupností, které začínají v posledním časovém okamžiku v posledním stavu a v čase t se nacházejí ve stavu s j [4], [2]: β j (t) = P (o t+1, o t+2,..., o T s(t) = s j, M). (1.14) Součinem těchto pravděpodobností obdržíme vztah [2]: α j (t)β j (t) = P (O, x(t) = s j M). (1.15) Normalizací pravděpodobnosti ve vztahu (1.15) Baumovou-Welchovou pravděpodobností P (O M) vypočítáme L j (t) [3]: L j (t) = P (x(t) = s j O, M) = P (O, x(t) = s j M). (1.16) P (O M) Dosazením rovnice (1.15) do (1.16) získáme tvar pro výpočet L j (t): L j (t) = α j(t)β j (t) P (O M). (1.17) Pomocí pravděpodobností α j (t) a β j (t) můžeme však spočítat i Baumovu-Welchovu pravděpodobnost, platí totiž [2]: P (O M) = N 1 j=2 P (O, x(t) = s j M), (1.18) kde N je počet stavů modelu HMM. Dosazením vztahu (1.15) získáme: P (O M) = N 1 j=2 α j (t)β j (t). (1.19) Další vztah pro L j (t) tedy obdržíme dosazením rovnice (1.19) do (1.17): L j (t) = α j(t)β j (t). (1.20) α j (t)β j (t) N 1 j=2 Abychom však mohli L j (t) vypočítat, musíme nejdříve α j (t) a β j (t) určit. Jestliže budeme uvažovat levo-pravý model, jaký je např. na obr. 1.1, můžeme α j (t) vypočítat iterativním algoritmem. Na začátku se provede inicializace [4]: α j (1) = a 1j b j (o 1 ) pro 2 j N 1. (1.21) 11-8
V dalších časových okamžicích bude pro α j (t) platit vztah [4]: [ N 1 ] α j (t) = α i (t 1)a ij b j (o t ) pro 2 t T. (1.22) i=2 Algoritmus se pak uzavírá vztahem: [4]: α N (T + 1) = N 1 i=2 α i (T )a in. (1.23) Jestliže si nyní opět uvědomíme definici Baumovy-Welchovy pravděpodobnosti, popsané vztahem (1.9), dojdeme k závěru, že výpočtem rovnice (1.23) obdržíme rovněž tuto pravděpodobnost. Podobným způsobem vypočítáme i pravděpodobnost β j (t), nicméně v tomto případě budeme procházet modelem odzadu (jedná se o částečnou zpětnou pravděpodobnost). Nejdříve tedy opět provedeme inicializaci (nyní však v posledním časovém okamžiku), poté se dále pohybujeme v čase zpět a nakonec se algoritmus ukončuje v čase t = 0 [3]: β i (T ) = a in pro 2 i N 1, (1.24) β i (t) = β 1 (0) = N 1 j=2 N 1 j=2 a ij b j (o t+1 )β j (t + 1) pro 1 t T 1, (1.25) a 1j b j (o 1 )β j (1). (1.26) Poslední rovnice je opět vztahem Baumovy-Welchovy pravděpodobnosti. Tuto pravděpodobnost tedy můžeme získat dvěma způsoby: P (O M) = α N (T + 1) = β 1 (0). (1.27) Toho se může někdy využít při kontrole správnosti výpočtu. Uvažujme nyní, že disponujeme množinou trénovacích dat D, která obsahuje R posloupností pozorování: D = O r, r = 1, 2,..., R. Posloupnost pozorování je definována jako O r = o r t, kde t = 1, 2,..., T r. Při trénování se snažíme nastavit parametry modelu tak, aby množinu D generoval s největší pravděpodobností. Nyní si již můžeme uvést Baumův-Welchův algoritmus výpočtu parametrů pro levo-pravý model s jednoduchým spojitým Gaussovským rozdělením výstupní pravděpodobnosti [2]: 1. Provedou se odhady počátečních hodnot matice přechodů  = â ij, kde i, j = 1,..., N. Zvolí se počáteční odhady hodnot parametrů výstupní pravděpodobnosti ˆµ j, ˆΣ j, kde j = 2,..., N 1. 2. Provede se přiřazení a ij = â ij pro všechna i, j = 1,..., N. Provede se přiřazení µ j = ˆµ j a Σ j = ˆΣ j pro všechna j = 2,..., N 1. 11-9
3. Pro každou posloupnost O r z trénovací množiny dat se dopředně zpětným algoritmem vypočítají podle vztahů (1.23), (1.26), (1.15) a (1.17) hodnoty: kde t = 1, 2,..., T r. α r j(t), β r j (t), (1.28) P (O r M) = N 1 j=2 α r j(t)β r j (t), (1.29) L r j(t) = αr j(t)β r j (t) P (O r M), (1.30) 4. Vztahy pro určení odhadu se vyskytují ve formě vážených průměrů parametrů z trénovacích dat. Váhami jsou zde aposteriorní pravděpodobnosti, že každý vektor z trénovacích dat je pozorován v každém stavu modelu M. Přechodové pravděpodobnosti odhadneme podle vztahů: â ij = R r=1 â 1j = 1 R â in = kde i, j = 2,..., N 1. T r 1 t=1 L r j(t)a ij b j (o r t+1) βr j (t+1) β r j (t) R T r r=1 t=1 L r j (t), (1.31) R L r j(1), (1.32) r=1 R L r j(t ) r=1 T r, (1.33) R L r j (T ) r=1 t=1 Dále vypočítáme parametry potřebné k určení hustot výstupních pravděpodobností pro všechna j = 2,..., N 1 dle vztahů: ˆµ j = ˆΣ j = R T r r=1 t=1 T r L r j(t)o r t, (1.34) R L r j (t) r=1 t=1 R T r L r j(t)(o r t ˆµ j )(o r t ˆµ j ) T r=1 t=1 R T r r=1 t=1 L r j (t). (1.35) 5. Testujeme, zdali se hodnoty nově vypočítaných parametrů významně liší od předchozích. Pokud ano, přecházíme znovu na krok 2. 11-10
Výhodou tohoto algoritmu je také to, že můžeme předem určit maximální počet cyklů přetrénování, což je užitečné především při zautomatizovaných výpočtech. 1.1.7 Rozpoznávání pomocí Viterbiho algoritmu V předchozí kapitole jsme ukázali, jak vypočítat z množiny trénovacích dat parametry modelu tak, aby jednotlivé posloupnosti pozorování generoval s největší pravděpodobností. To znamená, že nyní již disponujeme natrénovanými modely a můžeme přejít k rozpoznávání. Jak jsme uvedli v kap. 1.1.5, při rozpoznávání by stačilo zjistit, který model generuje danou posloupnost pozorování O neznámého slova w i s největší pravděpodobností. V mnoha aplikacích je však užitečné přiřadit dané posloupnosti vektorů pozorování posloupnost stavovou, jinými slovy zjistit, kterými stavy model při generování posloupnosti O procházel [2]. Rovněž jsme uvedli, že tato stavová posloupnost je před námi ukryta, nicméně můžeme vypočítat alespoň nejpravděpodobnější stavovou sekvenci, a to pomocí Viterbiho algoritmu. Viterbiho algoritmus využívá k výpočtu dvě pravděpodobnostní funkce: φ j (t) a ψ j (t). Funkce φ j (t) je částečná Viterbiho pravděpodobnost nejvíce pravděpodobné posloupnosti stavů při generování posloupnosti vektorů pozorování o 1,..., o t, jestliže se model M po vygenerování této posloupnosti nachází ve stavu s j [2]: φ j (t) = max P (o 1,..., o t, s(t) = s j M), (1.36) X(t 1) kde X(t 1) je množina všech stavových posloupností modelu v čase (t 1). φ j (t) můžeme vypočítat podobně jako částečnou dopřednou pravděpodobnost α j (t), pouze místo sumace využíváme maximalizaci. Na začátku tedy provedeme inicializaci, poté se pohybujeme modelem směrem k poslednímu stavu a nakonec algoritmus ukončujeme v čase t = T + 1 [4]: φ j (1) = a 1j b j (o 1 ) pro 2 j N 1, (1.37) φ j (t) = max i(t 1)a ij } b j (o t ) i=2,...,n 1 pro 2 j N 1, (1.38) φ N (T + 1) = max i(t )a in }. i=2,...,n 1 (1.39) Jestliže si nyní opět uvědomíme definici Viterbiho pravděpodobnosti popsanou vztahem (1.10), zjistíme, že φ N (T + 1) je rovněž předpisem této pravděpodobnosti: ˆP (O M) = φ N (T + 1). (1.40) Další pravděpodobnostní funkce, kterou Viterbiho algoritmus využívá, je funkce ψ j (t). Této pravděpodobnosti se využívá k určení posloupnosti stavů. Funkce si 11-11
v každém kroku t zapamatuje stav, který v předchozím iteračním kroku maximalizoval rovnici (1.38). Tuto pravděpodobnost tedy můžeme popsat vztahy [2]: ψ j (t) = arg max i(t 1)a ij }, i=2,...,n 1 kde t = 2,..., T, (1.41) ψ N (T + 1) = arg max i(t )a in }. i=2,...,n 1 (1.42) ψ j (t) uchovává nejpravděpodobnější stavy, kterými model procházel. Stavovou posloupnost pak získáme zpětným trasováním. Jestliže bude stavová posloupnost X obsahovat stavy x(0), x(1),..., x(t + 1), pak pro tyto stavy bude platit [2]: x(t + 1) = N, (1.43) x(t ) = ψ N (T + 1), (1.44) x(t) = ψ x(t+1) (t + 1), kde t = 1,..., T 1, (1.45) x(0) = 1. (1.46) 1.1.8 Algoritmus putující značky (Token passing algorithm) V modulu HTK se používá určitá modifikace Viterbiho algoritmu pro získání celkové akumulované pravděpodobnosti při průchodu jednotlivými stavy HMM modelu. Algoritmus si značkou (token) označuje stav, v němž se právě nachází, a postupně sčítá (akumuluje) logaritmy jednotlivých pravděpodobností. Algoritmus postupující značky lze popsat následovně [4]: 1. Je provedena inicializace tím, že do vstupního stavu každého modelu je vložena jedna prázdná značka. 2. Pro časové okamžiky t = 1,..., T opakujeme kroky 3) a 4). 3. V každém stavu s i vložíme do značky logaritmus pravděpodobnosti, který danému stavu odpovídá. Při cestě do dalších stavů jsou logaritmy pravděpodobností přičítány a opět uloženy do značky log a ij + log b j (o t ). 4. Pokud se v nějakém stavu objeví více značek, vybereme tu, která má největší akumulovanou pravděpodobnost. Je to možné proto, že se do daného stavu můžeme dostat různými cestami. 5. Ze všech stavů s i pošleme do výstupního stavu značku s největší akumulovanou pravděpodobností. Tato pravděpodobnost pak odpovídá logaritmu Viterbiho pravděpodobnosti ˆP (O M). 11-12
1.2 Rozpoznávání spojité řeči Obr. 1.3: Síť HMM určená k rozpoznávání spojité řeči. Při rozpoznávání spojité řeči si již nevystačíme s jedním modelem, ale naopak použijeme těchto modelů více. V tomto případě s výhodou využijeme neemitující stavy, jejichž pomocí modely spojíme do jednoho velkého makromodelu. Jednotlivé modely pak mohou být natrénovány na izolovaná slova, nebo na drobnější řečové jednotky jako jsou např. difony nebo trifony. Při trénování tohoto makromodelu se používají postupy podobné jako při trénování modelů jednotlivých, nicméně výpočty se vztahují na celý makromodel. Stejně tak při rozpoznávání musíme token passing algoritmus drobně upravit. Nejčastěji se na token pomocí ukazatele naváže struktura WLR (Word Link Record), která uchovává identitu právě opuštěného slova a čas. Zpětným trasováním WLR pak zjistíme slova, kterými model procházel [3]. Pokud budeme například rozpoznávat spojitou řeč, ve které se budou vyskytovat pouze slova jedna, dva a tři, mohla by síť hláskových modelů vypadat jako na obr. 1.3. Jednotlivé ovály zde reprezentují skryté Markovovy modely a obdélníky výsledná slova, která vzniknou sloužením těchto modelů. V tomto příkladě byla použita fonetická abeceda SAMPA [5]. 11-13
2 Rozpoznávání spojité řeči pomocí modulu HTK Proces rozpoznávání si můžeme rozdělit na dvě části. Nejdříve je potřeba navrhnout vhodnou strukturu modelu HMM (určit počet stavů, inicializovat matice přechodových pravděpodobností apod.) a dále natrénovat na patřičném množství trénovacích dat jednotlivé modely. Modul HTK k zjištění parametrů modelů HMM používá Baumův-Welchův algoritmus. V dalším kroku pak provádíme samotné rozpoznávání, přičemž modul HTK pro tyto účely využívá token passing algoritmus [3]. Pokud bychom chtěli detailnější popis postupu návrhu rozpoznávače spojité řeči, rozdělili bychom jej do čtyř fází: příprava dat, trénování, testování a analýza. 2.1 Příprava dat Modul HTK při trénování využívá parametrický popis dat. Může se jednat například o melovské frekvenční koeficienty (MFCC), lineární predikční koeficienty (LPC), percepční lineární predikční koeficienty (PLP) atd. Nejprve je třeba tyto parametry ze zvukových nahrávek vypočítat. K tomu slouží nástroj HCopy, který tyto parametry ze souborů typu *.wav, *.raw nebo jiných formátů vypočítá. Pomocí nástroje HList si pak můžeme vypsat obsahy těchto souborů. Pokud k trénovacím datům již existují transkripce (jak na úrovni slov, tak na úrovni hlásek), je potřeba tyto transkripce konvertovat do formátů čitelných modulem HTK. K tomu nám slouží nástroj HLEd, pomocí něhož můžeme rovněž z několika transkripcí vytvořit jeden Master Label File (MLF), který je pak výhodnější pro další zpracování. Soubor MLF usnadňuje orientaci ve velkém množství zpracovávaných souborů. Jestliže si chceme pořídit vlastní nahrávky, můžeme využít nástroj HSLab, který rovněž umožní nahrávky ihned označit. 2.2 Trénování V dalším kroku si definujeme topologii každého modelu HMM a rovněž nastavíme počáteční hodnoty matice přechodových pravděpodobností. V prvním cyklu se data rovnoměrně rozdělí a vypočítají se střední hodnoty a rozptyly. V dalších cyklech je rovnoměrné rozdělení nahrazeno Viterbiho algoritmem. Během inicializace je z trénovacích dat první sada parametrů vypočítána nástrojem HInit. K dalšímu přetrénování se pak používá nástroj HERest, který využívá již zmíněný Baumův-Welchův algoritmus. 11-14
Filozofie modulu HTK je taková, že by se modely HMM měly zdokonalovat postupně. Proto se nejčastěji začíná s kontextově nezávislými modely, které se dále rozšiřují na kontextově závislé, tj. na modely, které zohledňují své okolí. Nástroj HHEd se používá k tomu účelu, že provede klonování modelů do kontextově závislých sad s následným svazováním parametrů. To zvyšuje robustnost rozpoznávače. 2.3 Rozpoznávání Pro rozpoznávání používá modul HTK jeden ze tří programů: HVite, HLRecsore a HDecode. Podrobněji si popíšeme práci programu HVite. Tento program používá k rozpoznávání již zmíněný token passing algoritmus. Na vstup programu se předává síť možných slov, která se v řeči mohou vyskytovat, a pouze tato slova se mohou v řeči objevit. Dále je nutné uložit slovník s fonetickou transkripcí jednotlivých slov a nakonec sadu modelů HMM. Program HVite pak dokáže na základě těchto informací určit hranice slov v řečových datech, která jsou buď uložena na disku, nebo která přichází přímo ze zvukové karty. Síť možných slov lze vygenerovat automaticky např. pomocí programů HBuild nebo HParse. Pokud máme vytvořenou síť, můžeme nástrojem HSGen vygenerovat náhodné posloupnosti slov, která se mohou na vstupu vyskytovat. Pořadí výskytu těchto slov se však může řídit některými pravidly. To může sloužit ke kontrole správně vytvořené sítě, nebo tyto věty můžeme použít k nahrání nových řečových dat. Pokud již existuje k datům slovník fonetické transkripce, musíme jeho formát transformovat na formát používaný v modulu HTK. K tomu slouží program HDMan. 2.4 Analýza Jakmile je rozpoznávač připravený, provedeme testování a analýzu rozpoznávání na testovacích datech. Testovací nahrávky by měly být označené na úrovni hlásek, nicméně může postačit i transkripce na úrovni slov. Nejdříve nahrávky zpracujeme natrénovanými modely a poté zjistíme úspěšnost rozpoznávání na základě statistik získaných nástrojem HResults. Ze statistik pak můžeme zjistit počet správně rozpoznaných slov (nebo delších úseků) a počet chyb vzniklých substitucí, vynecháním nebo přidáním. V modulu HTK existuje mnoho dalších programů, které mají různé využití. Jejich detailní popis je spolu s příklady použití dostupný v dokumentaci k modulu HTK, tzv. HTK Book [3]. 11-15
3 Příklad vytvoření rozpoznávače spojité řeči s o- mezeným slovníkem v modulu HTK verze 3.4 V této části popisu si uvedeme jednoduchý postup vytvoření rozpoznávače, který bude pracovat pouze s omezeným slovníkem. Účelem tohoto popisu není tvorba robustního a velmi přesného rozpoznávače. Chceme zde jen uvést postup návrhu použitím modulu HTK, shlukování trifonových stavů, svazování trifonů a následné rozpoznávání spojité řeči. Nicméně pokud budeme mít již vytvořený jednoduchý rozpoznávač, můžeme jeho přesnost a robustnost dále vylepšovat (kapitola 3.14). Zde jsou použity nahrávky TIMIT, které jsou popsány v kapitole 3.1, nicméně je možné použít i jakékoliv jiné nahrávky. Dokumentace k modulu HTK je volně dostupná na internetu. Další návody, které se zabývají tvorbou rozpoznávače spojité řeči, jsou v pramenech [1] [6] [7]. 3.1 TIMIT data Řečový korpus 2 TIMIT (Texas Instruments/Massachusetts Institute of Technology) byl navržen k vývoji a testování automatických systémů rozpoznávání řeči. Korpus TIMIT (1993) obsahuje řečové nahrávky 630 mluvčích, reprezentujících osm hlavních dialektů americké angličtiny. Od každého mluvčího je v korpusu uchováno deset projevů. Z celkového počtu mluvčích je mužské pohlaví zastoupeno 70 % a ženské 30 %. Každá z promluv byla zaznamenána v bezodrazové komoře, přičemž řečový signál byl vzorkován kmitočtem f vz = 20 khz a dále decimován na f vz = 16 khz. Tato data jsou pro účely rozpoznávání již předem rozdělena na trénovací a testovací [8]. 3.2 Příprava modulu HTK Modul HTK je soubor programů s otevřeným zdrojovým kódem, který je po registraci volně stažitelný ze zdroje [1]. Je doporučeno stáhnout z těchto stránek také soubory s příklady a pomocnými skripty v jazyce Perl, které se budou v tomto návodu používat. Způsobů instalace je několik, existuje verze modulu HTK jak pro operační systém UNIX, tak pro MS Windows. Ve Windows se nejvíce osvědčila kompilace zdrojových *.c souborů v Unixu podobném prostředí Cygwin. Toto prostředí je volně dostupné ze zdroje [9]. Nicméně soubory je možné zkompilovat i přímo 2 Pojmem řečový korpus se zde rozumí soubor nahrávek řečového signálu, ke kterému existuje anotace, což je symbolická reprezentace řeči, která podává informaci o tom, jak byly jednotlivé promluvy v nahrávce vysloveny. 11-16
pomocí externího kompilátoru jazyka C. K další práci bude také potřeba nainstalovat program, který by umožňoval spouštět skripty napsané v jazyce Perl. K tomu lze použít například ActivePerl, který je volně stažitelný z pramenu [10]. I když je přímo na internetových stránkách modul HTK popsán postup instalace, tento popis je dosti strohý a nemusí vždy vést k úspěšné kompilaci. Proto se doporučuje raději postupovat podle návodu, který je zveřejněn na stránkách VoxForge, [11]. Po úspěšné instalaci modulu HTK si vytvoříme adresář, ve kterém budeme dále vytvářet soubory potřebné k rozpoznávání a do kterého nahrajeme TIMIT data. Dále se již bude pokračovat podle následujícího postupu. 3.3 Gramatika Nejdříve je potřeba vytvořit k rozpoznávání gramatiku (možnou posloupnost slov) gram. V našem případě se omezíme pouze na některá slova, přičemž je budeme psát verzálkami. Výpis souboru gram bude vypadat následovně: $word = A ALL ARE BEFORE BELOW BOX CONVENIENT DARK DROP EXPECTATIONS FALL FAR FIVE FOR FORMS GO GREASY HAD IN LUNCH MAY OUT PIZZERIAS PRODUCTION QUICK SHE SUIT THE WASH WATER YEAR YOU YOUR; ( SENT-START (<$word>) SENT-END ). Do proměnné $word nejdříve uložíme všechna slova, která chceme rozpoznávat. Poslední příkaz pak slouží k definování různého sledu slov. V našem jednoduchém příkladě umístíme proměnnou $word do ostrých závorek, což znamená jedno a více opakování slov. Takto vytvořenou gramatiku uložíme do adresáře grammar. Dále je potřeba získat síť slov. Modul HTK totiž nedokáže pracovat přímo s gramatikou, proto je potřeba vytvořit soubor, který by gramatiku pro HTK interpretoval pomocí HTK Standard Lattice Format (SLF), což lze pomocí HParse vytvořit přímo z gramatiky následujícím příkazem: HParse gram wdnet, kde gram je soubor s gramatikou a wdnet vytvořený soubor se sítí. 11-17
3.4 Tvorba slovníku Dalším krokem je tvorba slovníku 3. Slovník musí být abecedně seřazen, přičemž každý výraz musí být na zvláštním řádku a fonetická transkripce od výrazu oddělena mezerou nebo tabulátorem 4. V adresáři se soubory TIMIT 5 je na adrese TIMIT\DOC\TIMITDIC.TXT uložen již připravený slovník, nicméně pro použití modulu HTK je potřeba jej upravit. Vytvoříme si nový adresář dictionary, kde vytvoříme soubor timit trans.txt, do kterého zkopírujeme obsah TIMITDIC.TXT. Je nutné zde dále doplnit některá slova, a naopak některé záznamy smazat. Všechny výrazy musí být napsány verzálkami a transkripce minuskami. Rovněž je nutné odstranit lomítka a poslední záznam ukončit novým řádkem. Tento slovník lze vytvářet také automaticky. Příklad upraveného slovníku může být například takový 6 : A ax ABBREVIATE ax b r iy1 v iy ey2 t ABDOMEN ae1 b d ax m ax n ABIDES ax b ay1 d z... ZONING z ow1 n ix ng ZOO z uw1 ZOOLOGIST z ow aa1 l ax jh ix s t ZOOS z uw1 z~. V dalším kroku vytvoříme soubor wlist.txt, který obsahuje pouze slova bez transkripce ze souboru timit trans.txt. Poté je již možné vytvořit slovník příkazem HDMan -m -w wlist.txt -n monophones1 -l dlog dict timit\_trans.txt, 3 Jedná se o seznam slov, kterým jsou přiřazeny jejich fonetické transkripce. 4 Nedodržení těchto podmínek způsobuje v dalších krocích časté chyby, proto je nutné vše správně dodržet. 5 Tyto soubory jsou na CD spolu s TIMIT nahrávkami. 6 V tomto případě je použita fonetická abeceda ARPABET, nicméně na použitém druhu fonetické abecedy v modulu HTK nezáleží, [12] [3]. Pokud disponujeme pouze slovy bez fonetické transkripce, můžeme fonetický přepis vytvořit automaticky pomocí různých nástrojů, [13]. 11-18
kde monophones1 je seznam všech vyskytujících se monofonů 7, dlog je soubor obsahující různé informace (např. jestli byla přeložena všechna slova, kolik jich chybí atd.) a dict je již samotný slovník. Aby byla tvorba provedena korektně, je nutné mít v adresáři ještě soubor s názvem global.ded, který obsahuje následující příkazy: AS sp RS cmu MP sil sil sp. kde první příkaz přidává na konec každé fonetické transkripce krátkou pauzu sp (short pause), druhý příkaz odstraňuje z fonetické abecedy přízvuk značený číslicí (např. ae1 transformuje na ae) a poslední příkaz spojuje dlouhou pauzu sil (silence) a krátkou pauzu sp, přičemž toto spojení nahradí dlouhou pauzou sil. 3.5 Vytvoření seznamu vět a MLF souborů V dalším kroku je potřeba vytvořit seznamy trénovacích a testovacích TIMIT dat a jejich popisů. Nejdříve vytvoříme soubor promptlist.txt, což je seznam trénovacích zdrojů a příslušných vět. Jelikož je manuální tvorba tohoto souboru při velkém počtu trénovacích dat časově náročná, doporučujeme k tvorbě využít některý ze skriptovacích jazyků, jako např. Perl či Python. Výpis souboru promptlist.txt může vypadat např. takto:..\data\train\dr1\fcjf0\sx307 THE MEETING IS NOW ADJOURNED..\data\TRAIN\DR1\FCJF0\SX217 HOW PERMANENT ARE THEIR RECORDS..\data\TRAIN\DR1\FDML0\SI2075 NOW FORGET ALL THIS OTHER.... Pokud máme seznam všech vět v jednom souboru, můžeme vytvořit MLF (Master Label File) soubor všech slov v promluvách, který bude umístěn v adresáři mlf, do kterého se nyní přesuneme. Soubor vytvoříme pomocí skriptu v jazyce Perl prompts2mlf, který je uložen v HTKTutorial skupině souborů. Tato skupina je volně dostupná ze stránek HTK[1]. Spustíme následující příkaz: perl prompts2mlf words.mlf../dictionary/promptlist.txt. 7 Základní jednotkou fonetických popisů jazyka je hláska, minimální zvuková jednotka řeči, zobecněný typ zvukové reality patřící určitému jazyku. Foném je základní jednotkou fonologie. Jedná se o nejmenší zvukový prostředek schopný rozlišit minimální významovou jednotku jazyka, [14]. Při popisu mluvené podoby jazyka se obě jednotky vzájemně doplňují, v tomto článku je budeme společně označovat jako monofony. 11-19
Vznikne soubor words.mlf. Nyní vytvoříme MLF soubor na úrovní hlásek. Připravíme si soubor mkphones0.led, který bude obsahovat příkazy (sil silence, sp short pause): EX IS sil sil DE sp. kde první příkaz rozděluje slova na fonémy podle slovníku dict, druhý příkaz přidává na začátek a konec věty dlouhou pauzu sil, a poslední příkaz maže všechny výskyty krátké pauzy sp. Poslední příkaz je nutné ukončit novým řádkem. Dále spustíme příkaz: HLEd -d../dictionary/dict -i phones0.mlf mkphones0.led words.mlf. Výstupem tohoto příkazu bude soubor phones0.mlf. Je třeba upozornit, že vhodné umístění pauz uvnitř slov a mezi slovy dosti ovlivňuje účinnost rozpoznávání. 3.6 Kódování dat V dalším kroku vypočítáme ze všech nahrávek trénovacích a testovacích dat koeficienty typu MFCC. K tomu si musíme nejdříve vytvořit několik podpůrných souborů a skriptů 8. Nejdříve si vytvoříme seznamy trénovacích a testovacích dat. Vytvoříme si nový adresář scripts, kam se nyní přesuneme. Zde vytvoříme dva seznamy codetr.scp a codets.scp, které budou mít na řádku cestu k *.wav souboru a cestu k souboru *.mfc, kde budou uloženy koeficienty. Výpis souboru codetr.scp může vypadat např. takto:..\data\train\dr1\fcjf0\sa1.wav..\data\train\dr1\fcjf0\sa1.mfc..\data\train\dr1\fcjf0\si648.wav..\data\train\dr1\fcjf0\si648.mfc..\data\train\dr1\fcjf0\sx127.wav..\data\train\dr1\fcjf0\sx127.mfc..\data\train\dr1\fcjf0\sx307.wav..\data\train\dr1\fcjf0\sx307.mfc.... Dále si vytvoříme dva seznamy train.scp a test.scp, kde se budou vyskytovat cesty k *.mfc souborům. Zatímco seznam train.scp bude obsahovat všechna trénovací data, seznam test.scp bude obsahovat jen data, ve kterých se budou vyskytovat pouze slova z gramatiky grammar/gram 9. Výpis souboru test.scp tak bude např. následující: 8 Skripty zde nejsou myšleny nějaké posloupnosti příkazů, ale spíše konfigurační soubory. 9 Je to zřejmé, jelikož jiná slova by rozpoznávač zaměnil za slova z gramatiky, a došlo by tak k rozpoznávacím chybám. 11-20
..\data\test\dr1\faks0\sa1.mfc..\data\test\dr1\faks0\si943.mfc..\data\test\dr1\faks0\sx133.mfc..\data\test\dr1\faks0\sx313.mfc. Nyní vytvoříme skript configtr.txt pro trénovací data a configts.txt pro testovací data. Oba skripty budou obsahovat následující příkazy: # Coding parameters SOURCEFORMAT = NIST TARGETKIND = MFCC_0_D_A TARGETRATE = 100000.0 SAVECOMPRESSED = T SAVEWITHCRC = T WINDOWSIZE = 250000.0 USEHAMMING = T PREEMCOEF = 0.97 NUMCHANS = 26 CEPLIFTER = 22 NUMCEPS = 12 ENORMALISE = F. Příkaz MFCC 0 D A znamená že se kódují koeficienty spolu s logaritmem energie a odhadem první a druhé diference. SOURCEFORMAT určuje formát vstupních dat. TARGETRATE určuje vzorkovací periodu výstupních vektorů po 100 ns (v našem případě tedy 100 ms), SAVECOMPRESSED = T znamená, že výstup bude komprimován, SAVEWITHCRC = T zabezpečuje data pomocí CRC, WINDOWSIZE určuje délku okna 100 ns, USEHAMMING znamená použití Hammingova okna, PREEMCOEF značí, že bude provedena preemfáze s koeficientem a = 0,97, NUMCHANS značí, že banka filtrů bude mít 26 kanálů, CEPLIFTER značí liftering koeficientů typu MFCC, NUMCEPS určuje počet koeficientů typu MFCC, ENORMALISE = F vypíná normalizaci energie 10. Nyní již provedeme parametrizaci pomocí následujících příkazů: HCopy -T 1 -C configtr.txt -S codetr.scp, HCopy -T 1 -C configts.txt -S codets.scp. Tímto k datům přibudou nové *.mfc soubory s koeficienty. Tyto soubory lze číst pomocí příkazu: HList -h nazev\_souboru.mfc. 10 Normalizace se používá při zpracování v reálném čase [3]. 11-21
V dalším kroku již můžeme vytvářet skryté Markovovy modely (HMM). 3.7 Tvorba monofonových modelů typu HMM Pro tento příklad budeme využívat tzv. flat start, což znamená, že budeme uvažovat trénovací data, která nejsou předem označena 11. V hlavním adresáři vytvoříme adresáře hmm0 a proto, do kterých se přesuneme. Nejdříve vytvoříme prototypový model a uložíme do souboru proto, který rovněž zkopírujeme do adresáře hmm0. Obsah tohoto souboru bude následující: ~o <VecSize> 39 <MFCC_0_D_A> ~h "proto" <BeginHMM> <NumStates> 5 <State> 2 <Mean> 39 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 <Variance> 39 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 <State> 3 <Mean> 39 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 <Variance> 39 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 <State> 4 <Mean> 39 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 <Variance> 39 11 I když naše TIMIT data již označena jsou, a to jak na úrovni slov, tak na úrovni hlásek. 11-22
1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 <TransP> 5 0.0 1.0 0.0 0.0 0.0 0.0 0.6 0.4 0.0 0.0 0.0 0.0 0.6 0.4 0.0 0.0 0.0 0.0 0.7 0.3 0.0 0.0 0.0 0.0 0.0 <EndHMM>. V našem případě tedy používáme pětistavové modely typu HMM, přičemž první a poslední stav není emitující, ale slouží k navazování modelů. Každý stav má nastavené střední hodnoty na nuly a rozptyly na jedničku. Za příkazem <TransP> 5 následuje matice přechodových pravděpodobností, kde pozice hodnoty na řádku n a sloupci m udává přechodovou pravděpodobnost a n,m. Dále do adresáře proto zkopírujeme soubor configtr.txt z adresáře scripts. V tomto souboru odstraníme řádek SOURCEFORMAT = NIST. Nyní musíme nastavit střední hodnoty, odchylky a všechny Gaussovy průběhy v HMM. To provedeme příkazem HCompV -C configtr.txt -f 0.01 -m -S../scripts/train.scp -M../hmm0../hmm0/proto. Tím se nám v adresáři hmm0 upraví soubor proto, a rovněž se vytvoří makro vfloors. Nyní se přesuneme do adresáře hmm0. V dalším kroku vytvoříme dva soubory. Ze souboru vfloors vytvoříme soubor macros přidáním hlavičky: ~o <VecSize> 39 <MFCC_0_D_A>. Dále se přesuneme do složky dictionary, kde kopírujeme soubor monophones1 a přejmenujeme jej na monophones0. V tomto souboru smažeme mezislovní pauzu sp a přidáme dlouhou sil. Takto upravený soubor zkopírujeme do adresáře hmm0 a přejmenujeme jej na hmmdefs. Tento soubor upravíme tak, že ke každému řádku přidáme data ze souboru proto, přičemž příklad takového souboru může být následující: ~h "sil" <BEGINHMM>... <ENDHMM> 11-23
~h "ax" <BEGINHMM>... <ENDHMM> ~h "sp" <BEGINHMM>... <ENDHMM>.... V hlavním adresáři vytvoříme podadresáře hmm1, hmm2 a hmm3. Nyní se přesuneme do adresáře hmm0 a spustíme příkazem: HERest -C../proto/configtr.txt -I../mlf/phones0.mlf -t 250.0 150.0 1000.0 -S../scripts/train.scp -H macros -H hmmdefs -M../hmm1../dictionary/monophones0. Přesuneme se do adresáře hmm1 a spustíme příkazem: HERest -C../proto/configtr.txt -I../mlf/phones0.mlf -t 250.0 150.0 1000.0 -S../scripts/train.scp -H macros -H hmmdefs -M../hmm2../dictionary/monophones0. Nakonec se přesuneme do adresáře hmm2 a spustíme příkazem: HERest -C../proto/configtr.txt -I../mlf/phones0.mlf -t 250.0 150.0 1000.0 -S../scripts/train.scp -H macros -H hmmdefs -M../hmm3../dictionary/monophones0. Tím jsme třikrát přetrénovali model typu HMM, přičemž jsme zatím neuvažovali mezislovní pauzy sp. Příznakem -t jsme v příkazech nastavovali prahovou úroveň logaritmu částečné zpětné pravděpodobnosti β. Při překročení této úrovně dojde k zastavení procesu přetrénování. Zvyšováním prahové úrovně zajišťujeme přesnější přetrénování modelů. 3.8 Zavedení modelů pauzy V předchozím bodě jsme vygenerovali pětistavové levo-pravé modely HMM (první a poslední stav není emitující) pro každou hlásku a model pauzy sil. V dalším kroku vygenerujeme třístavový model (s jedním emitujícím stavem) pro krátkou pauzu sp. Tento model bude mít emitující stav svázán s prostředním stavem modelu pauzy sil. Takový model vytvoříme ve dvou krocích. Z adresáře hmm3 zkopírujeme soubor hmmdefs do nového adresáře hmm4. Tento soubor dále editujeme, přičemž zkopírujeme prostřední stav modelu sil a vytvoříme 11-24
nový model sp. Takto nově vytvořený sp model by mohl vypadat např. takto 12 : ~h "sp" <BEGINHMM> <NUMSTATES> 3 <STATE> 2 <MEAN> 39-1.905831e+01-2.318672e+00-6.234688e+00-1.990136e+00-2.092976e+00-2.573914e-01-7.386095e-01 2.931617e-01-6.817085e-01 4.748913e-01-4.791465e-01 2.573071e-01 3.286690e+01-1.675636e-01 1.354374e-01 1.738080e-01 1.213613e-01 8.271752e-02 8.141164e-02 1.469952e-02-4.667811e-02 3.661389e-03 1.093205e-01 5.605623e-02-3.884711e-02-1.527206e-01 5.819472e-02-6.436200e-02-1.883480e-02-5.772859e-02-3.444229e-02-5.697624e-02-2.284278e-02 9.972874e-05-2.019464e-02-3.792312e-02-3.076673e-02-6.435335e-03 1.199906e-01 <VARIANCE> 39 6.160837e+00 7.934437e+00 9.820024e+00 1.162281e+01 1.183406e+01 1.410295e+01 1.464444e+01 1.622942e+01 1.635606e+01 1.679205e+01 1.503452e+01 1.273678e+01 6.774286e+00 4.137819e-01 6.588092e-01 8.895987e-01 1.036966e+00 1.177018e+00 1.361918e+00 1.475117e+00 1.586270e+00 1.646126e+00 1.696561e+00 1.497926e+00 1.331998e+00 3.486073e-01 7.852182e-02 1.228069e-01 1.630246e-01 2.019144e-01 2.285312e-01 2.646243e-01 2.946185e-01 3.124516e-01 3.234148e-01 3.302834e-01 2.955636e-01 2.645462e-01 7.693569e-02 <GCONST> 8.368510e+01 <TRANSP> 3 0.000000e+00 8.486999e-01 1.513001e-01 0.000000e+00 8.486999e-01 1.513001e-01 0.000000e+00 0.000000e+00 0.000000e+00 <ENDHMM>. V dalším kroku svážeme emitující stav modelu sp s prostředním emitujícím stavem modelu sil. Využijeme k tomu příkaz HHEd, přičemž si nejdříve vytvoříme pomocný soubor sil.hed, jehož obsah bude následující: AT 2 4 0.2 {sil.transp} 12 Při tvorbě matice přechodových pravděpodobností musíme dbát na to, aby členy a 1,1, a 2,1, a 3,1, a 3,2 a a 3,3 byly nulové. Součet hodnot na řádku musí být rovněž 1. 11-25
AT 4 2 0.2 {sil.transp} AT 1 3 0.3 {sp.transp} TI silst {sil.state[3],sp.state[2]}. kde obecně příkaz AT i j prob itemlist(t) zvyšuje přechodovou pravděpodobnost ze stavu i do j o hodnotu prob ve všech maticích přechodových pravděpodobností v seznamu itemlist. Poslední příkaz pak svazuje všechny stavy ze seznamu a výsledek pojmenuje silst. Nyní se ještě ujistíme, že se v souboru dictionary/monophones1 vyskytuje dlouhá pauza sil. Pokud ne, tak ji sem připíšeme. Dále již provedeme příkaz: HHEd -H macros -H hmmdefs -M../hmm5 sil.hed../dictionary/monophones1, pomocí něhož vytvoříme svázaný stav silst, jehož parametry budou uloženy v souboru hmmdefs, který se vytvořil v adresáři hmm5. V dalším kroku modely dvakrát přetrénujeme: HERest -C../proto/configtr.txt -I../mlf/phones0.mlf -t 250.0 150.0 1000.0 -S../scripts/train.scp -H macros -H hmmdefs -M../hmm6../dictionary/monophones1 HERest -C../proto/configtr.txt -I../mlf/phones0.mlf -t 250.0 150.0 1000.0 -S../scripts/train.scp -H macros -H hmmdefs -M../hmm7../dictionary/monophones1. 3.9 Výpočet nových hranic v trénovacích datech Dříve vytvořený slovník dictionary/dict může pro jedno slovo obsahovat několik různých výslovností. Námi vytvořené modely HMM pro jednotlivé hlásky můžeme využít k vytvoření nové fonetické transkripce z již existující transkripce na úrovni slov, která je uložena v souboru mlf/words.mlf. Ještě před tvorbou nové fonetické transkripce upravíme slovník dictionary/dict přidáním nového řádku: silence sil, přičemž musíme stále pamatovat na to, že slovník je setříděn nejdříve podle velikosti písma (první jsou v pořadí verzálky, poté mínusky). V druhé řadě je slovník seřazen abecedně. Fonetickou transkripci vytvoříme příkazem: HVite -l * -o SWT -b silence -C../proto/configtr.txt -a -H macros -H hmmdefs -i../mlf/aligned.mlf -m -t 250.0 -y lab -I 11-26
../mlf/words.mlf -S../scripts/train.scp../dictionary/dict../dictionary/monophones1. Fonetickou transkripci nalezneme na adrese mlf/aligned.mlf. V dalším kroku znovu dvakrát přetrénujeme modely: HERest -C../proto/configtr.txt -I../mlf/phones0.mlf -t 250.0 150.0 1000.0 -S../scripts/train.scp -H macros -H hmmdefs -M../hmm8../dictionary/monophones1 HERest -C../proto/configtr.txt -I../mlf/phones0.mlf -t 250.0 150.0 1000.0 -S../scripts/train.scp -H macros -H hmmdefs -M../hmm9../dictionary/monophones1. 3.10 Tvorba trifonů z monofonů Dalším požadavkem je vytvořit kontextově závislé trifonové modely HMM 13. To rovněž proběhne v několika krocích. Nejdříve konvertujeme fonetickou transkripci na trifonovou. Dále vytvoříme z monofonových modelů trifonové, a to zkopírováním a přetrénováním. V druhém kroku svážeme podobné akustické stavy trifonů 14. Kontextově závislé hláskové modely jednoduše vytvoříme klonováním monofonových a přetrénováním pomocí trifonové transkripce. Nejdříve si vytvoříme trifonovou transkripci. K tomu budeme potřebovat soubor mktri.led, jehož obsah je následující: WB sp WB sil TC. Aby vše správně fungovalo, musí být poslední příkaz ukončen novým řádkem. Nyní spustíme příkaz: HLEd -n../dictionary/triphones1 -l * -i../mlf/wintri.mlf mktri.led../mlf/aligned.mlf. Monofonová transkripce v souboru mlf/aligned.mlf bude konvertována na trifonovou do nového souboru mlf/wintri.mlf. Rovněž se vytvoří seznam všech použitých trifonů v souboru dictionary/triphones1. Jelikož se některá slova mohou skládat 13 Trifonem zde v podstatě rozumíme kontextově závislou hlásku, neboli hlásku závislou na svém levém a pravém okolí. 14 Svazováním se míní to, že jeden nebo více modelů HMM sdílí stejné sady parametrů. 11-27
pouze ze dvou hlásek, vytvoří se v souboru i některé difony 15. Před klonováním si dále vytvoříme skript mktri.hed, který bude obsahovat klonovací příkaz a příkazy, které budou svazovat všechny přechodové matice v každé trifonové sadě. Tento soubor vytvoříme pomocí skriptu v jazyce Perl maketrihed, který je uložen v HTKTutorial adresáři: perl maketrihed../dictionary/monophones1../dictionary/triphones1. Některá varování, která skript vypíše, budeme ignorovat, jelikož u kontextově nezávislých modelů sp a sil nemáme co svazovat. Skript vytvoří soubor mktri.hed, který dále použijeme v příkazu: HHEd -B -H macros -H hmmdefs -M../hmm10 mktri.hed../dictionary/monophones1. Tento příkaz nám v adresáři hmm10 vytvoří nové soubory macros a hmmdefs. Tyto soubory jsou však zapsány již v binární formě. Jakmile máme naklonované kontextově závislé modely, můžeme nové trifonové sady přetrénovat: HERest -B -C../proto/configtr.txt -I../mlf/wintri.mlf -t 250.0 150.0 1000.0 -s stats -S../scripts/train.scp -H macros -H hmmdefs -M../hmm11../dictionary/triphones1, HERest -B -C../proto/configtr.txt -I../mlf/wintri.mlf -t 250.0 150.0 1000.0 -s stats -S../scripts/train.scp -H macros -H hmmdefs -M../hmm12../dictionary/triphones1. Příznakem -s stats zajistíme vygenerování souboru, který nese statistiky o počtech výskytu jednotlivých trifonů v trénovacích datech. 3.11 Tvorba trifonů se sloučenými stavy V předchozím kroku jsme vytvořili sadu trifonových modelů HMM. V posledním kroku vytvoříme z některých trifonových stavů shluky, přičemž stavy v těchto shlucích budou sdílet podobné vlastnosti. K vytvoření shluků použijeme binární rozhodovací stromy, které jsou založeny na otázkách o levém a pravém kontextu každého trifonu. Nejdříve spustíme funkci HDMan, přičemž jako vstup ji předáme slovník beep, který by měl obsahovat slova pokrývající všechny požadované trifony 16 : 15 HTK totiž z těchto hlásek nevytvořilo trifony, které by obsahovaly krátkou nebo dlouhou pauzu. 16 Nemělo by se jednat o slovník, který byl použit při trénování. 11-28
HDMan -b sp -n fulllist -g../dictionary/global.ded -l flog beep-tri../dictionary/beep. Tímto příkazem vytvoříme dva soubory fulllist a beep-tri. V textovém editoru vytvoříme nový soubor fulllist1, kam zkopírujeme obsah souboru fulllist a obsah souboru dictionary/triphones1. Ze souboru fulllist1 odstraníme duplicitní záznamy. To můžeme udělat například skriptem v jazyce Perl, který je uložen na stránkách VoxForge 17. perl fixfulllist.pl fulllist1 fulllist. Vzniklý soubor fulllist je tak připraven k dalšímu zpracování. Nyní si připravíme konfigurační soubor s binárními rozhodovacími stromy tree.hed. Do adresáře hmm12 si zkopírujeme soubor quests.hed, který je uložen pod cestou RMHTK/lib. Adresáře RMHTK a HTKTutorial jsou ve stejném bloku s příklady. Soubor quests.hed obsahuje seznam možných otázek, které budeme pro naše shlukování potřebovat. Dále si vytvoříme seznam všech příkazů, které budou sloužit ke shlukování stavů. Tento soubor vygenerujeme skriptem v jazyce Perl mkclscript, který je uložen na adrese RMHTK/perl scripts/mkclscript.prl: perl mkclscript.prl TB 350.0../dictionary/monophones1 > tree1. Výstupem tohoto skriptu je soubor tree1. Nyní již můžeme vytvořit skript tree.hed spojením a drobnou modifikací souborů quests.hed a tree1 tak, jak je tomu v příkladě níže: RO 100.0../hmm11/stats TR 0 QS "R_NonBoundary" { *+* } QS "R_Silence" { *+sil } QS "R_Stop" { *+p,*+pd,*+b,*+t,*+td,*+d,*+dd,*+k,*+kd,*+g } QS "R_Nasal" { *+m,*+n,*+en,*+ng }... QS "L_w" { w-* } QS "L_y" { y-* } QS "L_z" { z-* } TR 2 17 Tento skript je dostupný na adrese : http://www.voxforge.org/uploads/lu/di/ludii uuszs wennppu-6iq/fixfulllist pl.txt 11-29