Word2vec
Lad os se på en situation, hvor vi gerne vil kunne gætte næste ord i en sætning. Lad os sige, at vi har sætningen
"Min hund har en blød —"
og vil gætte næste ord. Hvis vi har en stor mængde tekst til rådighed, et såkaldt tekstkorpus, kan vi selvfølgelig lede efter ordsekvensen "Min hund har en blød" og se hvilket ord, der oftest kommer efter, som beskrevet i noten om simple sprogmodeller. Men hvis sekvensen ikke forekommer i vores korpus, så har vi et problem. I stedet kunne vi lede efter en sætning med en betydning, der minder om "Min hund har en blød" og se, hvad der kommer efter den. Men hvordan får vi en computer til at forstå betydningen af ord?
Den simpleste måde at repræsentere et ord på i en computer ville være ved at nummerere alle ordene i det danske sprog fra 1 til \(V\), hvor \(V\) er det samlede antal ord. Nummeret på et ord giver dog ikke megen information om ordets betydning.
En anden nærliggende idé kunne være at repræsentere et ord ved bogstaverne i ordet. For at en computer skal kunne forstå det, kunne man give hvert bogstav et tal ud fra bogstavets nummer i alfabetet. Så ville "kat" blive til \((11,1,20)\) og "hund" til \((8,21,14,4)\). Stavemåden fortæller dog heller ikke meget om betydningen af et ord. Ordet "mund" staves næsten lige som "hund", men har en helt anden betydning. Omvendt betyder ordet "vovse" næsten det samme som "hund", men staves helt anderledes.
I stedet vil vi gerne repræsentere hvert ord med en vektor. Idéen er, at ord, hvis betydning ligner hinanden, skal repræsenteres med vektorer, der peger i nogenlunde samme retning og har nogenlunde samme længde, som illustreret på figur 1.
I kender vektorer i 2 eller 3 dimensioner og ved, at de kan skrives på formen \[ \begin{pmatrix} a_1\\a_2\end{pmatrix}\text{ og } \begin{pmatrix} a_1\\a_2 \\a_3 \end{pmatrix} \] hvor \(a_1, a_2\) og (eventuelt) \(a_3\) er reelle tal, der kaldes vektorens koordinater. Tre koordinater er dog ikke nok til at indfange betydningen af alle ord i sproget. Derfor bruger man i stedet en \(m\)-dimensional vektor, som man kan tænke på som en liste af \(m\) koordinater \[ \begin{pmatrix} a_1\\a_2\\a_3\\ \vdots \\ a_m \end{pmatrix} \] hvor \(a_1,a_2,a_3,\ldots,a_m\) er reelle tal. I praksis vælger man \(m\) stort, for eksempel \(m=100\). Man kan regne med \(m\)-dimensionale vektorer, lige som man gør i to eller tre dimensioner. Vi kommer for eksempel til at se, hvordan man kan finde skalarprodukter. Til gengæld har man ikke mulighed for at visualisere en \(m\)-dimensional vektor som en pil i et koordinatsystem, men det har vi heldigvis heller ikke brug for.
I denne note ser vi på algoritmen Word2Vec som et eksempel på, hvordan man kan oversætte ord til vektorer, der repræsenterer ordenes betydning. Mere præcist kigger vi på en version af algoritmen, der hedder skip-gram. Word2Vec blev opfundet af en gruppe medarbejdere hos Google i 2013. I dag bruges diverse forfininger af algoritmen i mange store sprogmodeller.
Betydning og kontekst
Hvilke egenskaber skal de vektorer, der repræsenterer ord, så have? Jo, idéen er, at vektorerne skal indfange betydningen af et ord i den forstand, at ord, hvis betydning minder om hinanden, svarer til vektorer, der ligner hinanden. Hvad vi forstår ved, at vektorer ligner hinanden, det kommer vi tilbage til. Men hvordan ved vi, om to ords betydning minder om hinanden?
Sprogteoretikere, som for eksempel den danske Louis Hjelmslev (1899-1965), har fundet ud af, at vi forstår betydningen af et ord ud fra, hvilke sproglige sammenhænge det optræder i, når man kigger i rigtig mange dokumenter. Ordet “kælk” forekommer for eksempel ofte i nærheden af ord som “sne”, “leg” og “bakke”. Det giver os en idé om betydningen. Den sproglige sammenhæng, et ord indgår i, kaldes også en kontekst. Når vi skal forstå et ord med flere betydninger, kigger vi også på konteksten. Vi forstår for eksempel betydningen af ordet "marsvin" forskelligt alt efter, om ordet "fisk" eller ordet "mælkebøtte" optræder i nærheden af det. Det vil altså sige:
Betydningen af et ord er bestemt af den kontekst, ordet indgår i.
To ord, der betyder næsten det samme, vil ofte optræde i samme kontekst. Ordene "hund" og "kat" er for eksempel forskellige, men de vil ofte optræde i sammenhænge, der ligner hinanden. Se bare på sætningerne
"Min — har spist af sin madskål"
og
"Sikken en blød pels, din — har"
Her ville der kunne stå "hund" eller "kat", men nok ikke "kælk" eller "badedragt". Betydningen af ordene "hund" og "kat" er tættere på hinanden end betydningen af "hund" og "kælk". På den anden side kunne der ikke stå "hund" i sætningen
"Den lille — har hvide knurhår"
mens både "kat" og "mis" ville passe ind. Ordene "kat" og "hund" er altså tætte på hinanden, men ikke så tætte som "kat" og "mis".
Vi ville jo gerne lave vores vektorer, således at ord, hvis betydning ligner hinanden, svarer til vektorer, der ligner hinanden. Mere præcist vil vi lave dem, således at ord, der ofte har samme kontekst, svarer til vektorer, der ligner hinanden. Vi vil derfor gerne have, at vektorerne for "hund" og "kat" minder mere om hinanden end vektorerne for "hund" og "kælk", men ikke så meget som "kat" og "mis". I figur 1 ses et eksempel på, hvordan det kunne se ud.
Træningsdata
Vi har altså brug for at vide, hvilken kontekst ordene indgår i. For at lære, hvilken kontekst et ord forekommer i, tager vi udgangspunkt i et stort tilgængeligt tekstkorpus. Vi kalder dette korpus for vores træningsdata.
Ved konteksten til et ord vil vi her forstå de ord, der står umiddelbart før og efter ordet. Mere præcist vælger vi et vindue, lad os sige på fem ord, hvor ordet i midten er det, vi gerne vil kende betydningen af. Vi vil kalde dette for inputordet. De to første ord og de to sidste ord i vinduet er inputordets kontekstord. Se for eksempel på sætningen
Den sorte hund logrer med halen
Boksen angiver vores 5-ords vindue. Det midterste ord "hund" er vores inputord, ordene "Den", "sorte", "logrer" og "med" er kontekstord til "hund".
Vi starter med at placere vinduet omkring det første ord i vores datasæt og noterer dets fire kontekstord (hvoraf de to vil være blanke). Vi flytter nu vinduet mod højre et ord ad gangen, og hver gang noterer vi inputordet og dets fire kontekstord. Vi gør det for al teksten i vores træningsdata og samler informationen i et datasæt som vist i tabel 1. Hver række i tabellen består af et inputord og et af dets kontekstord.1
1 Når man vil gætte det næste ord i en sætning, har man selvfølgelig kun lov til at bruge de ord, der kommer før ordet. Det er dog ikke det, vi er ude på, når vi laver Word2Vec. Vi er ude på at forstå, hvordan et ord forholder sig til dets kontekst, altså de omkringstående ord. Derfor er der ikke noget problem i, at vinduet både indeholder ord før og efter inputordet.
Input | Kontekst |
---|---|
\(\vdots\) | \(\vdots\) |
sorte | |
sorte | Den |
sorte | hund |
sorte | logrer |
hund | Den |
hund | sorte |
hund | logrer |
hund | med |
\(\vdots\) | \(\vdots\) |
I de næste to afsnit ser vi på, hvordan man kan bruge vektorer til at modellere sandsynligheden for, at et inputord \(w\) har \(c\) som kontekstord. Det vil vi sidenhen benytte til at vælge vektorerne, således at sandsynlighederne matcher, hvor hyppigt vi observerer \(c\) som kontekstord til \(w\) i datasættet.
Input- og kontekstvektorer
I første omgang vil vi lade hvert ord \(w\) være repræsenteret af to vektorer, \(\vec{v}_{w}\) og \(\vec{k}_{w}\), hvor \(\vec{v}_{w}\) repræsenterer ordet, når det optræder som input, mens \(\vec{k}_{w}\) repræsenterer ordet, når det optræder som kontekst. Ordet "hund" vil altså være repræsenteret ved vektoren \(\vec{v}_{\text{hund}}\), når det optræder som input og ved vektoren \(\vec{k}_{\text{hund}}\), når det optræder som kontekst.
Betydningen af ordet \(w\) afgøres som nævnt af, hvordan ordet forholder sig til konteksten. Det vil vi oversætte matematisk til, hvordan inputvektoren \(\vec{v}_{w}\) forholder sig til kontekstvektorerne \(\vec{k}_{c}\) for diverse kontekstord \(c\). Vi vil derfor tænke på \(\vec{v}_{w}\) som den vektor, der repræsenterer betydningen af ordet, og altså den vi er ude på at bestemme.
For at måle hvordan inputvektoren \(\vec{v}_{w}\) for ordet \(w\) forholder sig til kontekstvektoren \(\vec{k}_{c}\) for ordet \(c\), vil vi bruge skalarproduktet \(\vec{v}_{w}\cdot \vec{k}_{c}\). Husk på, at man finder skalarproduktet mellem to vektorer i to dimensioner ved formlen \[ \begin{pmatrix} a_1\\a_2 \end{pmatrix} \cdot \begin{pmatrix} b_1\\b_2 \end{pmatrix} =a_1b_1+a_2b_2 \] I tre dimensioner er formlen \[ \begin{pmatrix} a_1\\a_2\\a_3 \end{pmatrix} \cdot \begin{pmatrix} b_1\\b_2\\b_3 \end{pmatrix} =a_1b_1+a_2b_2+a_3b_3 \] Tilsvarende kan man definere skalarproduktet mellem to \(m\)-dimensionale vektorer ved \[ \begin{pmatrix} a_1\\a_2\\a_3\\ \vdots\\a_m \end{pmatrix} \cdot \begin{pmatrix} b_1\\b_2\\b_3\\ \vdots \\b_m \end{pmatrix} =a_1b_1+a_2b_2+a_3b_3+\dotsm + a_mb_m \] Det overordnede mål er nu:
Vi vil gerne have, at vores input- og kontekstvektorer skal opfylde, at hvis \(w\) ofte har \(c\) som kontekst, så er skalarproduktet \(\vec{v}_{w}\cdot \vec{k}_{c}\) stort, mens en meget negativ værdi af \(\vec{v}_{w}\cdot \vec{k}_{c}\) indikerer, at \(w\) sjældent har \(c\) som kontekst.
Hvad fortæller skalarproduktet om, hvordan to vektorer forholder sig til hinanden? I 2 og 3 dimensioner kan vi give en geometrisk fortolkning af skalarproduktet ved hjælp af formlen \[ \vec{a}\cdot\vec{b} = |\vec{a}|\cdot |\vec{b}| \cdot \cos(v) \tag{1}\] hvor \(v\) er vinklen mellem vektorerne \(\vec{a}\) og \(\vec{b}\), og \(|\vec{a}|\) betegner længden af \(\vec{a}\), som findes med formlen \[ |\vec{a}|=\sqrt{\vec{a}\cdot\vec{a}} \]
I 2 dimensioner svarer det til \[ |\vec{a}|=\sqrt{a_1^2 + a_2^2} \]
Cosinus er en aftagende funktion på intervallet \([0^\circ,180^\circ ]\), så jo større vinklen \(v\) er, desto mindre vil \(\cos(v)\) være – se figur 2.

Det betyder, at \(\cos(v)\) er størst når vinklen mellem \(\vec{a}\) og \(\vec{b}\) er \(0^\circ\), svarende til at vektorerne peger samme vej. Her er \(\cos(v)=1\). Den mindste værdi af \(\cos(v)\) er \(-1\), som antages ved en vinkel på \(180^\circ\), hvor vektorerne peger i modsat retning.
Det vil sige, jo mindre vinklen mellem \(\vec{v}_{w}\) og \(\vec{k}_{c}\) er, desto større er deres skalarprodukt \(\vec{v}_{w}\cdot \vec{k}_{c}\) altså, og jo oftere har ordet \(w\) dermed \(c\) som kontekst. Desuden viser (1), at lange vektorer tæller mere, både positivt og negativt, end korte vektorer. Ord, der er gode til at forudsige konteksten udfra, for eksempel "logre", der ofte vil forekomme i hunderelaterede kontekster, vil derfor blive repræsenteret med lange inputvektorer. Ord som "og" eller "er", der ikke indeholder megen information om konteksten, vil blive repræsenteret med kortere inputvektorer. Altså vil \(|\vec{v}_{\text{logre}}|\) være større end \(|\vec{v}_{\text{og}}|\) og \(|\vec{v}_{\text{er}}|\).
Bemærk også, at hvis to ord ofte optræder i samme kontekst, skal deres inputvektorer gerne have nogenlunde samme skalarprodukt med alle kontekstvektorer. Formlen (1) viser, at det betyder, at de dels skal have nogenlunde samme længde og dels skal have nogenlunde samme vinkel med alle kontekstvektorerne. Sidstnævnte kræver, at de selv har nogenlunde samme retning. Ord, hvis betydning ligner hinanden, kommer derfor til at svare til inputvektorer, hvis længde og retning ligner hinanden.
I praksis bruger vi vektorer af højere dimension end 3. Det er måske ikke helt oplagt, hvad man skal forstå ved længden af en vektor eller vinklen mellem to vektorer i højere dimensioner, men det viser sig, at man stadigvæk godt kan give mening til formlen (1). Intuitionen fra to eller tre dimensioner er derfor god at have, selv om vi arbejder med højere dimensioner.
Eksempel 1
Lad os sige, at vi har fundet en vektorrepræsentation, der opfylder det ønskede. Det gav vektorerne \[ \begin{aligned} &\vec{v}_{\text{hund}} = \begin{pmatrix} 3\\ 2\end{pmatrix}, \quad \vec{v}_{\text{kat}} = \begin{pmatrix} 2\\ 3\end{pmatrix} \\ &\vec{k}_{\text{madskål}} = \begin{pmatrix} 3\\3\end{pmatrix}, \quad \vec{k}_{\text{badedragt}} = \begin{pmatrix} -2\\-1.5 \end{pmatrix}, \quad \vec{k}_{\text{lufte}} = \begin{pmatrix} 2\\0.5 \end{pmatrix} \end{aligned} \] Vektorerne er vist i figur 3, hvor inputvektorer er lyserøde, og kontekstvektorer er blå.

Vi kan udregne skalarprodukterne \[ \begin{aligned} &\vec{v}_{\text{kat}}\cdot \vec{k}_{\text{madskål}} = \begin{pmatrix} 2\\ 3\end{pmatrix} \cdot \begin{pmatrix} 3\\ 3\end{pmatrix} = 2\cdot 3 + 3\cdot 3 = 15 \\ &\vec{v}_{\text{kat}}\cdot \vec{k}_{\text{badedragt}} = \begin{pmatrix} 2\\ 3\end{pmatrix} \cdot \begin{pmatrix} -2\\ -1.5\end{pmatrix} = 2\cdot (-2) + 3\cdot (-1.5) = -8.5 \end{aligned} \] Vi ser, at \(\vec{v}_{\text{kat}}\cdot \vec{k}_{\text{madskål}}\) er større end \(\vec{v}_{\text{kat}}\cdot \vec{k}_{\text{badedragt}}\). Det svarer til, at ordet "kat" oftere har "madskål" som kontekst, end det har "badedragt". Det ses også ved, at \(\vec{k}_{\text{madskål}}\) peger i nogenlunde samme retning som \(\vec{v}_{\text{kat}}\), mens \(\vec{k}_{\text{badedragt}}\) peger i en helt anden retning.
Vi kan også udregne \[ \begin{aligned} &\vec{v}_{\text{hund}}\cdot \vec{k}_{\text{madskål}} = \begin{pmatrix} 3\\ 2\end{pmatrix} \cdot \begin{pmatrix} 3\\ 3\end{pmatrix} = 3\cdot 3 + 2\cdot 3 = 15 \end{aligned} \] Vi ser, at \(\vec{v}_{\text{hund}}\cdot \vec{k}_{\text{madskål}} =\vec{v}_{\text{kat}}\cdot \vec{k}_{\text{madskål}} =15\), svarende til at både "hund" og "kat" ofte har "madskål" som kontekst. Vi ser da også, at vektorerne \(\vec{v}_{hund}\) og \(\vec{v}_{kat}\) peger i nogenlunde samme retning og har nogenlunde samme længde, fordi de tit har samme kontekst. De to vektorer er dog ikke helt ens, da der også vil være nogle kontekstord, der ikke er lige hyppige for "hund" og "kat". Vi kan for eksempel udregne \[ \begin{aligned} &\vec{v}_{\text{hund}}\cdot \vec{k}_{\text{lufte}} = \begin{pmatrix} 3\\ 2\end{pmatrix} \cdot \begin{pmatrix} 2\\ 0.5\end{pmatrix} = 3\cdot 2 + 2\cdot 0.5 =7\\ &\vec{v}_{\text{kat}}\cdot \vec{k}_{\text{lufte}} =\begin{pmatrix} 2\\ 3\end{pmatrix} \cdot \begin{pmatrix} 2\\ 0.5\end{pmatrix} = 2\cdot 2 + 3\cdot 0.5 = 5.5 \end{aligned} \] Her er \(\vec{v}_{\text{hund}}\cdot \vec{k}_{\text{lufte}}\) større end \(\vec{v}_{\text{kat}}\cdot \vec{k}_{\text{lufte}}\) svarende til, at "hund" oftere har "lufte" som kontekst, end "kat" har.
Model for sandsynligheder
Som nævnt vil vi gerne have, at jo større skalarproduktet \(\vec{v}_{w}\cdot \vec{k}_{c}\) er, desto mere sandsynligt er det, at ordet \(w\) har ordet \(c\) som kontekst. Hvis der er \(V\) antal ord i sproget, kan vi nummerere de mulige kontekstord som \(\text{ord}_1,\text{ord}_2,\ldots,\text{ord}_V\). For hvert af dem får vi et skalarprodukt \(\vec{v}_{w}\cdot \vec{k}_{\text{ord}_i}\) for \(i=1,\ldots,V\). Hvis vi betegner sandsynligheden for, at \(\text{ord}_i\) er et kontekstord til \(w\), med \(P(\text{ord}_i\mid w)\), så vil vi gerne have, at følgende er opfyldt:
For hvert \(\text{ord}_i\) er \[0\leq P(\text{ord}_i \mid w)\leq 1\] da sandsynligheder skal ligge mellem 0 og 1.
Den samlede sandsynlighed for at få et af de mulige kontekstord skal være 1, det vil sige, \[P(\text{ord}_1\mid w) + P(\text{ord}_2\mid w) + \dotsm + P(\text{ord}_V\mid w ) = 1.\]
Jo større skalarproduktet \(\vec{v}_{w}\cdot \vec{k}_{\text{ord}_i}\) er, desto større er sandsynligheden \(P(\text{ord}_i\mid w)\) for at få \(\text{ord}_i\) som kontekstord.
Skalarprodukter kan imidlertid antage alle reelle værdier, så de egner sig ikke til at repræsentere sandsynligheder, der jo skal være tal mellem 0 og 1. Vi bruger derfor en funktion på skalarprodukterne for at få dem lavet om til sandsynligheder, der opfylder punkt 1. til 3 ovenfor. Den funktion, vi vil bruge, hedder Softmax. Hvis \(\vec{y}\) er en vektor med \(V\) koordinater:
\[ \vec{y} = \begin{pmatrix} y_1 \\ y_2 \\ \vdots \\ y_V \end{pmatrix} \]
så er \(\text{Softmax}\big(\vec{y}\big)=\vec{z}\), hvor \(\vec{z}\) er en ny vektor med \(V\) koordinater. Den \(i\)’te koordinat i \(\vec{z}\) er givet ved \[z_i = \frac{\mathrm{e}^{y_i}}{\mathrm{e}^{y_1} + \dotsm + \mathrm{e}^{y_V}}\] Vi viser i boksen nedenfor, at Softmax opfylder:
\(0<z_i<1\)
\(z_1 + z_2 + \dotsm + z_V = 1\)
Hvis \(y_i < y_j\), så er \(z_i < z_j\).
Lader vi \(\vec{y}\) være vektoren med \(i\)’te koordinat \[y_i = \vec{v}_{w}\cdot \vec{k}_{\text{ord}_i}\] og bruger Softmax på \(\vec{y}\), så får vi en vektor \(\vec{z}=\text{Softmax}\big(\vec{y}\big)\), der kan bruges som sandsynligheder. Mere præcist lader vi
\[ \begin{aligned} P(\text{ord}_i\mid w) &= z_i = \frac{\mathrm{e}^{y_i}}{\mathrm{e}^{y_1} + \dotsm + \mathrm{e}^{y_V}} \\ &= \frac{\mathrm{e}^{\vec{v}_{w}\cdot \vec{k}_{\text{ord}_i}}}{\mathrm{e}^{\vec{v}_{w}\cdot \vec{k}_{\text{ord}_1}} + \dotsm + \mathrm{e}^{\vec{v}_{w}\cdot \vec{k}_{\text{ord}_V}}} \end{aligned} \tag{2}\]
Egenskaberne ved softmax sikrer, at punkt 1. til 3. ovenfor er opfyldt.
Eksempel 2
Antag, at vores ordforråd kun består af de tre ord "hund", "pels" og "fjer", og at vi har fundet vektorrepræsentationer for de tre ord.
Antag, at inputvektoren for "hund" og kontekstvektorerne for "fjer", "pels" og "hund" er givet ved \[ \begin{aligned} \vec{v}_{\text{hund}}=\begin{pmatrix} 0.5\\2\end{pmatrix},\quad \vec{k}_{\text{fjer}}=\begin{pmatrix} 1\\-1 \end{pmatrix}, \quad \vec{k}_{\text{pels}}=\begin{pmatrix} 0\\1\end{pmatrix},\quad \vec{k}_{\text{hund}}=\begin{pmatrix} 4\\-1\end{pmatrix} \end{aligned} \] Vi beregner først skalarprodukterne \[ \begin{aligned} \vec{v}_{\text{hund}} \cdot \vec{k}_{\text{fjer}} &= 0.5\cdot 1 + 2\cdot (-1) =-1.5\\ \vec{v}_{\text{hund}} \cdot \vec{k}_{\text{pels}}&= 0.5\cdot 0 + 2\cdot 1 = 2\\ \vec{v}_{\text{hund}} \cdot \vec{k}_{\text{hund}} &= 0.5 \cdot 4 + 2 \cdot (-1) = 0 \end{aligned} \] Derefter anvender vi Softmax for at finde sandsynlighederne \[ \begin{aligned} P(\text{fjer} \mid \text{hund}) &= \frac{\mathrm{e}^{\vec{v}_{\text{hund}} \cdot \vec{k}_{\text{fjer}} }}{\mathrm{e}^{\vec{v}_{\text{hund}} \cdot \vec{k}_{\text{fjer}} }+\mathrm{e}^{\vec{v}_{\text{hund}} \cdot \vec{k}_{\text{pels}} }+\mathrm{e}^{\vec{v}_{\text{hund}} \cdot \vec{k}_{\text{hund}} }} \\ &= \frac{\mathrm{e}^{-1.5}}{\mathrm{e}^{-1.5}+\mathrm{e}^{2 }+\mathrm{e}^{0 }} \approx 0.026\\ \\ P(\text{pels} \mid \text{hund}) &= \frac{\mathrm{e}^{\vec{v}_{\text{hund}} \cdot \vec{k}_{\text{pels}} }}{\mathrm{e}^{\vec{v}_{\text{hund}} \cdot \vec{k}_{\text{fjer}} }+\mathrm{e}^{\vec{v}_{\text{hund}} \cdot \vec{k}_{\text{pels}} }+\mathrm{e}^{\vec{v}_{\text{hund}} \cdot \vec{k}_{\text{hund}} }} \\ &= \frac{\mathrm{e}^{2}}{\mathrm{e}^{-1.5}+\mathrm{e}^{2 }+\mathrm{e}^{0 }} \approx 0.858\\ \\ P(\text{hund} \mid \text{hund}) &= \frac{\mathrm{e}^{\vec{v}_{\text{hund}} \cdot \vec{k}_{\text{hund}} }}{\mathrm{e}^{\vec{v}_{\text{hund}} \cdot \vec{k}_{\text{fjer}} }+\mathrm{e}^{\vec{v}_{\text{hund}} \cdot \vec{k}_{\text{pels}} }+\mathrm{e}^{\vec{v}_{\text{hund}} \cdot \vec{k}_{\text{hund}} }} \\ &= \frac{\mathrm{e}^{0}}{\mathrm{e}^{-1.5}+\mathrm{e}^{2 }+\mathrm{e}^{0 }} \approx 0.116 \end{aligned} \] “Pels” er altså det mest sandsynlige kontekstord til "hund", mens "fjer" sjældent er kontekst til "hund".
Estimation af vektorrepræsentationer
Vi mangler stadig at bestemme vektorrepræsentationerne \(\vec{v}_{w}\) og \(\vec{k}_{c}\), så modellen i (2) kommer til at passe til virkelig tekst. Her får vi brug for det datasæt, som vi lavede ud fra vores træningsdata. Hver række i datasættet bestod af et inputord \(w\) og et kontekstord \(c\), som forekom i vores træningsdata. Lad os sige, at der er \(M\) rækker i vores træningsdata. Vi betegner data i den \(j\)te række med \((w_j,c_j)\) for \(j=1,\ldots,M\). Vores datasæt har altså formen (jævnfør tabel 1):
Input | Kontekst |
---|---|
\(w_1\) | \(c_1\) |
\(w_2\) | \(c_2\) |
\(w_3\) | \(c_3\) |
\(\vdots\) | \(\vdots\) |
\(w_j\) | \(c_j\) |
\(\vdots\) | \(\vdots\) |
Bemærk, at hvert inputord godt kan optræde flere gange i ovenstående tabel, som vi også så det i tabel 1.
Vi ser nu på \(j\)te række. Sandsynligheden for, at \(w_j\) har netop \(c_j\) som kontekst, er \(P(c_j\mid w_j)\). Vi vil nu finde den samlede sandsynlighed for, at inputordene \(w_1,\ldots,w_M\) i vores datasæt har henholdsvis \(c_1,\ldots,c_M\) som kontekstord. Den kalder vi \(P(\text{data})\). Lad os antage, at alle vores par af input- og kontekstord er uafhængige af hinanden2. Så får vi den samlede sandsynlighed ved at gange de enkelte sandsynligheder sammen. \[P(\text{data}) = P(c_1\mid w_1) \cdot P(c_2\mid w_2) \dotsm P(c_M\mid w_M) \tag{4}\] Hvis vores model fra (2) passer godt til datasættet, skulle denne sandsynlighed gerne være høj.
2 I praksis er ordpar, der forekommer i nærheden af hinanden ikke helt uafhængige. Hvis for eksempel parret \((\text{hund},\text{madskål})\) forekommer, er det mere sandsynligt, at \((\text{hund}, \text{fodre})\) forekommer i nærheden, end hvis der havde stået \((\text{hund},\text{hundesnor})\).
3 For eksempel er det meget nemmere at differentiere summer end produkt!
Da summer er nemmere at regne på end produkter3, vælger vi at tage den naturlige logaritme på begge sider af (4). Ved at bruge logaritmeregnereglen \(\ln(ab) = \ln(a) + \ln(b)\) får vi, at \[ \begin{aligned} \ln (P(\text{data})) &= \ln\big(P(c_1\mid w_1) \cdot P(c_2\mid w_2) \dotsm P(c_M\mid w_M)\big)\\ &= \ln(P(c_1\mid w_1)) + \ln(P(c_2\mid w_2)) + \dotsm + \ln(P(c_M\mid w_m)) \end{aligned} \]
Da den naturlige logaritme er en voksende funktion, svarer store værdier af \(P(\text{data})\) til store værdier af \(\ln (P(\text{data}))\), som igen svarer til små værdier af \(-\ln(P(\text{data}))\). Dette er illustreret på figur 4:

Hvis vores model er god, skal \[ \begin{aligned} L&= - \ln(\text{data}) \\ &= -\ln(P(c_1\mid w_1)) - \ln(P(c_2\mid w_2)) - \dotsm - \ln(P(c_M\mid w_m)) \end{aligned} \tag{5}\]
altså gerne være lav4. Vi kalder \(L\) for vores tabsfunktion. Denne tabsfunktion kaldes også nogle gange for cross-entropy.
4 Bemærk, at da \(P(c_j\mid w_j)\) er en sandsynlighed, der ligger mellem 0 og 1, er \(\ln (P(c_j\mid w_j))\) negativ. Derfor er \(-\ln (P(c_j\mid w_j))\) positiv og \(L\) er dermed også positiv. Når \(L\) gerne skal være lav, betyder det altså, at den skal være så tæt på 0 som muligt.
Vi kan indsætte vores udtryk for sandsynlighederne fra (2) i (5) og få \[ \begin{aligned} L = &-\ln\left(\frac{\mathrm{e}^{\vec{v}_{w_1}\cdot \vec{k}_{c_1}}}{\mathrm{e}^{\vec{v}_{w_1}\cdot \vec{k}_{\text{ord}_1}} + \dotsm + \mathrm{e}^{\vec{v}_{w_1}\cdot \vec{k}_{\text{ord}_V}}}\right) \\ &- \ln\left(\frac{\mathrm{e}^{\vec{v}_{w_2}\cdot \vec{k}_{c_2}}}{\mathrm{e}^{\vec{v}_{w_2}\cdot \vec{k}_{\text{ord}_1}} + \dotsm + \mathrm{e}^{\vec{v}_{w_2}\cdot \vec{k}_{\text{ord}_V}}}\right) - \dotsm \\ &- \ln\left(\frac{\mathrm{e}^{\vec{v}_{w_M}\cdot \vec{k}_{c_M}}}{\mathrm{e}^{\vec{v}_{w_M}\cdot \vec{k}_{\text{ord}_1}} + \dotsm + \mathrm{e}^{\vec{v}_{w_M}\cdot \vec{k}_{\text{ord}_V}}}\right) \end{aligned} \tag{6}\] Bemærk her, at \(w_j\) og \(c_j\) refererer til henholdsvis input- og kontekstordet i \(j\)te række i datasættet, mens \(\text{ord}_i\) refererer til \(i\)’te ord i ordforrådet.
Ligning (6) viser, at \(L\) afhænger af, hvordan vi har valgt input- og kontekstvektorerne. Vi kan altså betragte \(L\) som en funktion af input- og kontekstvektorerne. Mere præcist er \(L\) en funktion af alle koordinaterne i disse vektorer. For at få den bedst mulige model for vores træningsdata, ønsker vi at bestemme input- og kontekstvektorerne, således at de minimerer \(L\). Hvordan finder man minimum for \(L\) i praksis? Det kan man for eksempel gøre ved hjælp af gradientnedstigning, som vi ikke vil komme nærmere ind på her.
Der er rigtig mange ord i det danske sprog. For hvert af dem skal vi finde både en input- og en kontekstvektor, der hver har \(m\) koordinater. Alt i alt giver det rigtig mange koordinater, der skal bestemmes. Antallet af ord på dansk afhænger lidt af, hvad man forstår ved et ord, men 200.000 er et fornuftigt bud. Hvis vi vil repræsentere hver af dem ved to vektorer af dimension \(m=100\), får man brug for at bestemme 40.000.000 koordinater. For at kunne gøre det meningsfuldt, er man også nødt til at have enormt store mængder træningsdata til rådighed i form af et tekstkorpus med rigtig mange ord.
Nu har vi set, hvordan man kan repræsentere et ord \(w\) ved en inputvektor \(\vec{v}_{w}\) og en kontekstvektor \(\vec{k}_{w}\). Vektoren \(\vec{v}_{w}\) er den, der viser, hvordan \(w\) forholder sig til sin kontekst, så det er den, der repræsenterer betydningen af \(w\). Normalt vil man derfor arbejde videre med \(\vec{v}_{w}\), mens \(\vec{k}_{w}\) smides væk. Når vektorerne \(\vec{v}_{w}\) er fundet med Word2Vec, kan man bruge dem til at lave algoritmer til at generere tekst. Det ser vi på i næste afsnit.
Fra vektorer til tekstgenerering
I det følgende ser vi på, hvordan vektorrepræsentationerne, som vi fandt med Word2Vec, kan bruges til at lave en algoritme, der kan generere ny tekst. De fleste tekstgenereringsalgoritmer fungerer ved, at de danner teksten et ord ad gangen. Givet den tekst der allerede er dannet, prøver algoritmen hele tiden at gætte, hvad det næste ord skal være. Det kan gøres i to trin:
Først benyttes Word2Vec til at oversætte alle ordene i sproget til vektorer.
Dernæst genereres teksten. Givet den tekst, der allerede er dannet, bruger vi en (kompliceret) funktion, der tager vektorrepræsentationerne af de hidtil genererede ord som input. Som output giver funktionen det mest sandsynlige næste ord (eller et af de mest sandsynlige).
Hvis vi for eksempel har genereret teksten
"Hunden spiser sit —"
så skal vi prøve at gætte, hvilket ord der kommer efter "sit". Vi oversætter derfor ordene "Hunden", "spiser" og "sit" til vektorerne \(\vec{v}_{\text{Hunden}}\), \(\vec{v}_{\text{spiser}}\) og \(\vec{v}_{\text{sit}}\). Disse tre vektorer giver vi funktionen som input, og som output får vi et nyt ord. Det kunne være "kødben". Den funktion, der bruges i punkt 2., kunne for eksempel være et neuralt netværk. Du kan læse mere om, hvordan det fungerer i Tekstgenerering med neurale netværk.
Det smarte ved at bruge vektorrepræsentationerne er, hvis vi for eksempel vil generere næste ord i
"Jeg skal huske, at katten skal have —"
Det skulle gerne give "mad" som muligt næste ord. Men måske har sprogmodellen aldrig set sætningen "katten skal have mad". Hvis den til gengæld har set "hunden skal have mad", og modellen ved at "hunden" og "katten" tit har samme kontekst, og dermed har næsten samme vektorrepræsentation, så vil man alligevel få "mad" som muligt næste ord.
Bemærk, at resultatet af Word2Vec afhænger meget af, hvilket træningsdata vi har brugt. Antag for eksempel, at vi kun træner modellen på tekster skrevet af folk, der ikke kan lide hunde. Så vil \(\vec{v}_{\text{hund}}\) have en tendens til at pege i samme retning som andre negativt ladede ord, fordi de ofte optræder sammen med negative kontekstord. Når vi sidenhen genererer ny tekst, vil vi få en tendens til at danne sætninger, der omtaler hunde negativt.