jancll.com

Lekce 08 · 18 min čtení

Multi-head + poziční kódování

Víc hlav pozornosti naráz a jak modelu vůbec říct, v jakém pořadí slova jdou.

Úvod

V minulé lekci jsme postavili jednu hlavu pozornosti. Naučila se pěkný vztah: sloveso si najde svůj podmět a předmět. Jenže ve větě běží vztahů víc najednou. Kdo s kým, co kde, co bylo dřív. Jedna hlava to všechno naráz neuhlídá, protože si musí vybrat jeden vzorec dotazů a klíčů.

Řešení je otravně jednoduché: pusť na větu víc hlav zároveň, každou s vlastníma očima. A pak vyřešíme jeden tichý problém, kterého sis možná všiml už minule: attention vůbec netuší, v jakém pořadí slova jdou.

Tři vojáci v řadě se dívají různě, zatímco důstojník hlídá pořadí

Více pohledů na tutéž situaci. A pořadí v řadě se nesmí poplést.

Víc hlav pozornosti

Multi-head attention není nic jiného než několik attention z minulé lekce běžících vedle sebe. Každá hlava má svoje vlastní matice pro Query, Key a Value, takže si každá počítá svoje skóre a dívá se jinam. Jedna se naučí hlídat vazby slovesa, jiná místo, další třeba kdo je v ději.

Tady jsou tři hlavy nad naší větou. Vyber slovo a koukni, jak se každá dívá jinam:

MultiHeadAttention · víc úhlů pohledu naráz

Vyber slovo a koukni, jak se na něj dívá každá hlava jinak.

slovo

hlava 1 · vazby slovesa

kdo co dělá s čím

  • pan
  • švejk
  • vypil
  • pivo
  • v
  • hospodě

hlava 2 · místo

kde se to odehrává

  • pan
  • švejk
  • vypil
  • pivo
  • v
  • hospodě

hlava 3 · postavy

kdo je ve hře

  • pan
  • švejk
  • vypil
  • pivo
  • v
  • hospodě

Tři hlavy, tři pohledy na slovo vypil. Jejich výstupy se pak slepí za sebe a prožene se jedna vrstva navrch.

Vyber třeba vypil. První hlava si najde, kdo pil a co (pan, švejk, pivo). Druhá hlava řeší, kde se to dělo (hospodě). Třetí sleduje postavy. Tři pohledy na to samé slovo, každý užitečný pro něco jiného. Tohle žádná jednotlivá hlava nedokáže, musela by se rozhodnout pro jeden vzorec.

Jak to do sebe zapadá

Kdyby každá hlava pracovala v plné velikosti embeddingu, bylo by to draho. Trik je, že se rozměr rozdělí mezi hlavy. Když má embedding velikost 512 a máš 8 hlav, každá hlava pracuje jen v rozměru 64. Spočítají se paralelně, jejich výstupy se slepí za sebe zpátky na 512 a prožene se jedna závěrečná vrstva.

MultiHead=Concat(hlava1,,hlavaH)WO\text{MultiHead} = \text{Concat}(\text{hlava}_1, \dots, \text{hlava}_H)\, W^O

Takže víc pohledů přijde skoro zadarmo. Místo jedné velké hlavy máš osm menších, každou specializovanou. Celkový počet čísel zůstane zhruba stejný.

Tichý problém: attention neví, kde co stojí

Teď ta zákeřnost. Vrať se k vzorci attention z minulé lekce. Je to vážený součet přes všechna slova. A součtu je jedno pořadí. a + b je to samé jako b + a.

To znamená, že čistá attention vůbec nerozlišuje, v jakém pořadí slova přišla. Věta „pes kousl muže" a „muž kousl psa" obsahují stejná slova, takže by z holé attention vypadlo to samé. A to je očividně špatně. Pořadí ve větě nese půlku významu.

Poziční kódování

Oprava je elegantní: ke každému slovu přičteme vektor, který kóduje jeho pozici. Slovo na první pozici dostane jiný „otisk" než to samé slovo na páté. Model tak má pořadí přímo ve vstupu, ještě než attention začne.

Klasický recept používá siny a kosiny o různých frekvencích. Každá pozice tím dostane unikátní vzorec čísel:

PE(pos,2i)=sin ⁣(pos100002i/d),PE(pos,2i+1)=cos ⁣(pos100002i/d)PE(pos, 2i) = \sin\!\left(\frac{pos}{10000^{2i/d}}\right), \quad PE(pos, 2i+1) = \cos\!\left(\frac{pos}{10000^{2i/d}}\right)

Nemusíš ten vzorec milovat, stačí pochopit záměr: vyrobit pro každou pozici rozpoznatelný otisk, který se přičte k embeddingu. Pojďme si ty otisky vypsat:

python · pyodide

Každý řádek je jiný, takže model dokáže první slovo odlišit od pátého. Mimochodem, moderní modely tenhle konkrétní sinusový recept často nahrazují pozicemi, které se model rovnou naučí, ale myšlenka zůstává: bez informace o pozici by si slova ve větě byla na nerozeznání.

V kódu

Teď celá multi-head attention v numpy. Tři hlavy, každá si promítne vstup do svého menšího rozměru, spočítá attention, a výstupy se slepí. Klikni Run.

python · pyodide

Všimni si těch rozměrů. Tři hlavy po 4 se slepí zpátky na 12, takže vstup i výstup mají stejný tvar. To je důležité, protože tyhle bloky se budou v transformeru stohovat na sebe a každý musí dostat to, co předchozí vydal.

Cvičení

Cvičení · lekce8-cv1

Model má embedding velikosti 512 a 8 hlav pozornosti. V jakém rozměru pracuje jedna hlava?

Cvičení · lekce8-cv2

Těch 8 hlav po 64 se slepí za sebe. Jakou délku má vektor po slepení?

Cvičení · lekce8-cv3

Pozná čistá attention bez pozičního kódování rozdíl mezi větami „pes kousl muže" a „muž kousl psa"? (1 = ano, 0 = ne.)

Cvičení · lekce8-cv4

V playgroundu nahoře proklikej slova a hlavy. Najdi hlavu, která se ti zdá nejužitečnější, a napiš, jaký vztah podle tebe sleduje.

Shrnutí

  • Multi-head attention pustí na větu víc hlav naráz, každou s vlastními Q/K/V. Každá se může naučit jiný typ vztahu.
  • Rozměr se mezi hlavy rozdělí, hlavy běží paralelně a jejich výstupy se slepí zpátky: Concat(hlava1,)WO\text{Concat}(\text{hlava}_1, \dots)\,W^O.
  • Čistá attention je slepá vůči pořadí slov, protože je to jen vážený součet.
  • Poziční kódování přičte ke každému slovu otisk jeho pozice, takže model ví, co stálo kde.
  • Vstup a výstup bloku mají stejný tvar, což umožní bloky stohovat na sebe.

Co bude příště

Máme všechny součástky transformeru: embeddingy, poziční kódování, multi-head attention. Zbývá je poskládat do jednoho bloku a ukázat dvě lepidla, která to drží pohromadě i v hloubce: reziduální spojení a normalizaci.

V Lekci 9 (Transformer blok) složíme jeden kompletní blok, ze kterého se stohováním stane celý transformer. Pak už zbývá jen finální projekt.