CRF cd.
This commit is contained in:
parent
90b0947029
commit
10981fc2bc
File diff suppressed because one or more lines are too long
@ -1,5 +1,29 @@
|
|||||||
|
|
||||||
* Neurozoo
|
* Neurozoo
|
||||||
|
** Kilka uwag dotyczących wektorów
|
||||||
|
|
||||||
|
Wektor wierszowy $\left[x_1,\dots,x_n\right]$ czy kolumnowy $\left[\begin{array}{c}
|
||||||
|
x_1 \\ \vdots \\ x_n\end{array}\right]$?
|
||||||
|
|
||||||
|
Często zakłada się wektor kolumny, będziemy używać *transpozycji*, by otrzymać wektor
|
||||||
|
wierszowy $\vec{x}^T = \left[x_1,\dots,x_n\right]$.
|
||||||
|
|
||||||
|
W praktyce, np. w PyTorchu, może to nie mieć wielkiego znaczenia:
|
||||||
|
|
||||||
|
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
|
||||||
|
import torch
|
||||||
|
x = torch.tensor([1.0, -0.5, 2.0])
|
||||||
|
x
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
# Out[2]:
|
||||||
|
: tensor([ 1.0000, -0.5000, 2.0000])
|
||||||
|
:end:
|
||||||
|
|
||||||
|
Musimy tylko uważać, jeśli przemnażamy wektor przez macierz!
|
||||||
|
|
||||||
** Funkcja sigmoidalna
|
** Funkcja sigmoidalna
|
||||||
|
|
||||||
Funkcja sigmoidalna zamienia dowolną wartość („sygnał”) w wartość z przedziału $(0,1)$, czyli wartość, która może być interperetowana jako prawdopodobieństwo.
|
Funkcja sigmoidalna zamienia dowolną wartość („sygnał”) w wartość z przedziału $(0,1)$, czyli wartość, która może być interperetowana jako prawdopodobieństwo.
|
||||||
@ -101,6 +125,210 @@ Funkcja sigmoidalna nie ma żadnych wyuczalnych wag.
|
|||||||
**** *Pytanie*: Czy można rozszerzyć funkcję sigmoidalną o jakieś wyuczalne wagi?
|
**** *Pytanie*: Czy można rozszerzyć funkcję sigmoidalną o jakieś wyuczalne wagi?
|
||||||
|
|
||||||
** Regresja liniowa
|
** Regresja liniowa
|
||||||
|
*** Iloczyn skalarny — przypomnienie
|
||||||
|
|
||||||
|
$$\left[1.0, -0.5, 2.0\right]
|
||||||
|
\left[\begin{array}{c}
|
||||||
|
3.0 \\
|
||||||
|
1.5 \\
|
||||||
|
0.0\end{array}\right]
|
||||||
|
=
|
||||||
|
1.0 \cdot 3.0 + -0.5 \cdot 1.5 + 2.0 \cdot 0.0 = 2.25$$
|
||||||
|
**** Intuicje
|
||||||
|
|
||||||
|
- $\vec{a}^T \vec{b}$ mierzy jak bardzo $\vec{a}$ „pasuje” do
|
||||||
|
$\vec{b}$,
|
||||||
|
- … zwłaszcza gdy znormalizujemy wektory dzieląc przez $|\vec{a}|$ i $|\vec{b}|$:
|
||||||
|
$\frac{\vec{a}^T \vec{b}}{|\vec{a}||\vec{b}|} = \cos \theta$,
|
||||||
|
gdzie $\theta$ to kąt pomiędzy $\vec{a}$ and $\vec{b}$ (podobieństwo kosinusowe!)
|
||||||
|
- co, jeśli if $\vec{a}^T \vec{b} = 0$? — $\vec{a}$ i $\vec{b}$ są prostopadłe, np.
|
||||||
|
$\left[1, 2\right] \cdot \left[-2, -1\right]^T = 0$
|
||||||
|
- a co, jeśli $\vec{a}^T \vec{b} = -1$ — wektor są skierowane w przeciwnym kierunku, jeśli dodatkowo $|\vec{a}|=|\vec{b}|=1$, np.
|
||||||
|
$\left[\frac{\sqrt{2}}{2},\frac{\sqrt{2}}{2}\right] \cdot \left[-\frac{\sqrt{2}}{2},-\frac{\sqrt{2}}{2}\right]^T = -1$
|
||||||
|
|
||||||
|
**** W PyTorchu
|
||||||
|
|
||||||
|
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
|
||||||
|
import torch
|
||||||
|
x = torch.tensor([1.0, -0.5, 2.0])
|
||||||
|
y = torch.tensor([3.0, 1.5, 0.0])
|
||||||
|
x @ y
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
# Out[3]:
|
||||||
|
: tensor(2.2500)
|
||||||
|
:end:
|
||||||
|
|
||||||
|
*** Regresja liniowa jako element sieci neuronowej
|
||||||
|
|
||||||
|
Przypomnijmy sobie wzór na regresję liniową:
|
||||||
|
|
||||||
|
$$y = w_0 + w_1x_1 + w_2x_2 + \dots + w_{|V|}x_{|v|}$$
|
||||||
|
|
||||||
|
Jeśli wprowadzimy sztuczny element wektora $\vec{x}$ ustawiony zawsze na 1 ($x_0 = 1$), wówczas
|
||||||
|
wzór może przyjąc bardziej zwartą postać:
|
||||||
|
|
||||||
|
$$y = \sum_{i=0}^{|V|} w_ix_i = \vec{w}\vec{x}$$
|
||||||
|
|
||||||
|
*** PyTorch
|
||||||
|
|
||||||
|
**** Implementacja w PyTorchu
|
||||||
|
|
||||||
|
Zakładamy, że wektor wejściowy *nie* obejmuje dodatkowego elementu $x_0 = 1$.
|
||||||
|
|
||||||
|
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
|
||||||
|
import torch
|
||||||
|
import torch.nn as nn
|
||||||
|
|
||||||
|
class MyLinearRegressor(nn.Module):
|
||||||
|
def __init__(self, vlen):
|
||||||
|
super(MyLinearRegressor, self).__init__()
|
||||||
|
self.register_parameter(name='w', param=torch.nn.Parameter(
|
||||||
|
torch.zeros(vlen, dtype=torch.double, requires_grad=True)))
|
||||||
|
self.register_parameter(name='b', param=torch.nn.Parameter(
|
||||||
|
torch.tensor(0., dtype=torch.double, requires_grad=True)))
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
return self.b + x @ self.w
|
||||||
|
|
||||||
|
regressor = MyLinearRegressor(3)
|
||||||
|
regressor(torch.tensor([0.3, 0.4, 1.0], dtype=torch.double))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
# Out[11]:
|
||||||
|
: tensor(0., dtype=torch.float64, grad_fn=<AddBackward0>)
|
||||||
|
:end:
|
||||||
|
|
||||||
|
**** Gotowy moduł w PyTorchu
|
||||||
|
|
||||||
|
Możemy skorzystać z ogólniejszej konstrukcji — warstwy liniowej (ale,
|
||||||
|
uwaga!, na wyjściu będzie wektor jednoelementowy).
|
||||||
|
|
||||||
|
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
|
||||||
|
import torch
|
||||||
|
import torch.nn as nn
|
||||||
|
|
||||||
|
regressor = torch.nn.Linear(in_features=3, out_features=1, bias=True)
|
||||||
|
regressor(torch.tensor([0.3, 0.4, 1.0]))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
# Out[18]:
|
||||||
|
: tensor([0.1882], grad_fn=<AddBackward0>)
|
||||||
|
:end:
|
||||||
|
|
||||||
|
*** Zastosowania
|
||||||
|
|
||||||
|
Bezpośrednio możemy zastosować do zadania regresji dla tekstu (np.
|
||||||
|
przewidywanie roku publikacji tekstu).
|
||||||
|
|
||||||
|
[[./img-linear-regression.png]]
|
||||||
|
|
||||||
|
W połączeniu z sigmoidą otrzymamy regresją logistyczną, np. dla zadania klasyfikacji tekstu:
|
||||||
|
|
||||||
|
$$p(c|\vec{x}) = \sigma(w_0 + w_1x_1 + w_2x_2 + \dots + w_{|V|}x_{|v})
|
||||||
|
= \sigma(\Sigma_{i=0}^{|V|} w_ix_i) = \sigma(\vec{w}\vec{x})$$
|
||||||
|
|
||||||
|
[[./img-logistic-regression.png]]
|
||||||
|
|
||||||
|
Tak sieć będzie aktywowana dla tekstu _aardvark in Aachen_:
|
||||||
|
|
||||||
|
[[./img-logistic-regression-aardvark.png]]
|
||||||
|
|
||||||
|
Regresje logistyczną (liniową zresztą też) dla tekstu możemy połączyć z trikiem z haszowaniem:
|
||||||
|
|
||||||
|
\[p(c|\vec{x}) = \sigma(w_0 + w_1x_1 + w_2x_2 + \dots + w_{2^b}x_{2^b})
|
||||||
|
= \sigma(\Sigma_{i=0}^{2^b} w_ix_i) = \sigma(\vec{w}\vec{x})\] \\
|
||||||
|
{\small hashing function $H : V \rightarrow \{1,\dots,2^b\}$,
|
||||||
|
e.g. MurmurHash3}
|
||||||
|
|
||||||
|
[[./img-logistic-regression-hashing.png]]
|
||||||
|
|
||||||
|
**Pytanie:** Jaki tekst otrzyma na pewno taką samą klasę jak _aardvark in Aachen_?
|
||||||
|
|
||||||
|
*** Wagi
|
||||||
|
|
||||||
|
Liczba wag jest równa rozmiarowi wektora wejściowego (oraz opcjonalnie
|
||||||
|
obciążenie).
|
||||||
|
|
||||||
|
Każda waga odpowiada wyrazowi ze słownika, możemy więc interpretować
|
||||||
|
wagi jako jednowymiarowy parametr opisujący słowa.
|
||||||
|
|
||||||
|
** Warstwa liniowa
|
||||||
|
*** Mnożenie macierzy przez wektor — przypomnienie
|
||||||
|
|
||||||
|
Mnożenie macierzy przez wektor można interpretować jako zrównolegloną operację mnożenie wektora przez wektor.
|
||||||
|
|
||||||
|
$$\left[\begin{array}{ccc}
|
||||||
|
\alert<2>{1.0} & \alert<2>{-2.0} & \alert<2>{3.0} \\
|
||||||
|
\alert<3>{-2.0} & \alert<3>{0.0} & \alert<3>{10.0}\end{array}\right]
|
||||||
|
\left[\begin{array}{c}
|
||||||
|
\alert<2-3>{1.0} \\
|
||||||
|
\alert<2-3>{-0.5} \\
|
||||||
|
\alert<2-3>{2.0}\end{array}\right]
|
||||||
|
=
|
||||||
|
\left[\begin{array}{c}
|
||||||
|
\uncover<2->{\alert<2>{8.0}} \\
|
||||||
|
\uncover<3->{\alert<3>{18.0}}\end{array}\right]$$
|
||||||
|
|
||||||
|
Jeśli przemnożymy macierz $n \times m$ przez wektor kolumnowy o długości
|
||||||
|
$m$, otrzymamy wektor o rozmiarze $n$.
|
||||||
|
|
||||||
|
W PyTorchu:
|
||||||
|
|
||||||
|
#+BEGIN_SRC ipython :session mysession :results file
|
||||||
|
import torch
|
||||||
|
m = torch.tensor([[1.0, -2.0, 3.0],
|
||||||
|
[-2.0, 0.0, 10.0]])
|
||||||
|
x = torch.tensor([1.0, -0.5, 2.0])
|
||||||
|
m @ x
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
[[file:# Out[19]:
|
||||||
|
: tensor([ 8., 18.])]]
|
||||||
|
|
||||||
|
*** Definicja warstwy liniowej
|
||||||
|
|
||||||
|
Warstwa liniowa polega na przemnożeniu wejścia przez macierz. Można
|
||||||
|
to intepretować jako zrównolegloną operację regresji liniowej (równolegle
|
||||||
|
uczymy czy wykonujemy $n$ regresji liniowych).
|
||||||
|
|
||||||
|
*** PyTorch
|
||||||
|
|
||||||
|
Warstwa liniowa, która przyjmuje wektor o rozmiarze 3 i zwraca wektor o rozmiarze 2.
|
||||||
|
|
||||||
|
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
|
||||||
|
import torch
|
||||||
|
import torch.nn as nn
|
||||||
|
|
||||||
|
regressor = torch.nn.Linear(in_features=3, out_features=2, bias=True)
|
||||||
|
regressor(torch.tensor([0.3, 0.4, 1.0]))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
# Out[23]:
|
||||||
|
: tensor([-1.1909, -0.5831], grad_fn=<AddBackward0>)
|
||||||
|
:end:
|
||||||
|
|
||||||
|
*Pytanie*: Ile wag (parametrów) ma powyżej użyta warstwa?
|
||||||
|
|
||||||
|
*** Zastosowania
|
||||||
|
|
||||||
|
Warstwa liniowa jest podstawowym elementem sieci neuronowych —
|
||||||
|
począwszy od prostych sieci neuronowych feed-forward, gdzie warstwy
|
||||||
|
liniowe łączymy używając funkcji aktywacji (np. sigmoidy).
|
||||||
|
|
||||||
|
Oto przykład prostej dwuwarstwowej sieci neuronowej do klasyfikacji binarnej.
|
||||||
|
|
||||||
|
[[./img-feed-forward.png]]
|
||||||
|
|
||||||
|
|
||||||
** Softmax
|
** Softmax
|
||||||
|
|
||||||
@ -576,7 +804,6 @@ Przykłady zastosowań:
|
|||||||
- oznaczanie etykiet nazw w zadaniu NER (nazwisko, kwoty, adresy — najwięcej tokenów będzie miało etykietę pustą, zazwyczaj oznaczaną przez ~O~)
|
- oznaczanie etykiet nazw w zadaniu NER (nazwisko, kwoty, adresy — najwięcej tokenów będzie miało etykietę pustą, zazwyczaj oznaczaną przez ~O~)
|
||||||
|
|
||||||
*** *Pytanie*: czy zadanie tłumaczenia maszynowego można potraktować jako problem etykietowania sekwencji?
|
*** *Pytanie*: czy zadanie tłumaczenia maszynowego można potraktować jako problem etykietowania sekwencji?
|
||||||
|
|
||||||
*** Przykładowe wyzwanie NER CoNLL-2003
|
*** Przykładowe wyzwanie NER CoNLL-2003
|
||||||
|
|
||||||
Zob. <https://gonito.net/challenge/en-ner-conll-2003>.
|
Zob. <https://gonito.net/challenge/en-ner-conll-2003>.
|
||||||
@ -590,7 +817,6 @@ W pierwszym polu oczekiwany wynik zapisany za pomocą notacji *BIO*.
|
|||||||
Jako metrykę używamy F1 (z pominięciem tagu ~O~)
|
Jako metrykę używamy F1 (z pominięciem tagu ~O~)
|
||||||
|
|
||||||
*** Metryka F1
|
*** Metryka F1
|
||||||
|
|
||||||
*** Etykietowanie za pomocą klasyfikacji wieloklasowej
|
*** Etykietowanie za pomocą klasyfikacji wieloklasowej
|
||||||
|
|
||||||
Można potraktować problem etykietowania dokładnie tak jak problem
|
Można potraktować problem etykietowania dokładnie tak jak problem
|
||||||
@ -598,7 +824,7 @@ klasyfikacji wieloklasowej (jak w przykładzie klasyfikacji dyscyplin
|
|||||||
sportowych powyżej), tzn. rozkład prawdopodobieństwa możliwych etykiet
|
sportowych powyżej), tzn. rozkład prawdopodobieństwa możliwych etykiet
|
||||||
uzyskujemy poprzez zastosowanie prostej warstwy liniowej i funkcji softmax:
|
uzyskujemy poprzez zastosowanie prostej warstwy liniowej i funkcji softmax:
|
||||||
|
|
||||||
$$p(l^k=i) = s(\vec{w}\vec{v}(t^k))_i = \frac{e^{\vec{w}\vec{v}(t^k)}}{Z},$$
|
$$p(l^k=i) = s(W\vec{v}(t^k))_i = \frac{e^{W\vec{v}(t^k)}}{Z},$$
|
||||||
|
|
||||||
gdzie $\vec{v}(t^k)$ to reprezentacja wektorowa tokenu $t^k$.
|
gdzie $\vec{v}(t^k)$ to reprezentacja wektorowa tokenu $t^k$.
|
||||||
Zauważmy, że tutaj (w przeciwieństwie do klasyfikacji całego tekstu)
|
Zauważmy, że tutaj (w przeciwieństwie do klasyfikacji całego tekstu)
|
||||||
@ -626,7 +852,7 @@ Za pomocą wektora można przedstawić nie pojedynczy token $t^k$, lecz
|
|||||||
cały kontekst, dla /okna/ o długości $c$ będzie to kontekst $t^{k-c},\dots,t^k,\dots,t^{k+c}$.
|
cały kontekst, dla /okna/ o długości $c$ będzie to kontekst $t^{k-c},\dots,t^k,\dots,t^{k+c}$.
|
||||||
Innymi słowy klasyfikujemy token na podstawie jego samego oraz jego kontekstu:
|
Innymi słowy klasyfikujemy token na podstawie jego samego oraz jego kontekstu:
|
||||||
|
|
||||||
$$p(l^k=i) = \frac{e^{\vec{w}\vec{v}(t^{k-c},\dots,t^k,\dots,t^{k+c})}}{Z_k}.$$
|
$$p(l^k=i) = \frac{e^{W\vec{v}(t^{k-c},\dots,t^k,\dots,t^{k+c})}}{Z_k}.$$
|
||||||
|
|
||||||
Zauważmy, że w tej metodzie w ogóle nie rozpatrujemy sensowności
|
Zauważmy, że w tej metodzie w ogóle nie rozpatrujemy sensowności
|
||||||
sekwencji wyjściowej (etykiet), np. może być bardzo mało
|
sekwencji wyjściowej (etykiet), np. może być bardzo mało
|
||||||
@ -635,7 +861,12 @@ prawdopodobne, że bezpośrednio po nazwisku występuje data.
|
|||||||
Napiszmy wzór określający prawdopodobieństwo całej sekwencji, nie
|
Napiszmy wzór określający prawdopodobieństwo całej sekwencji, nie
|
||||||
tylko pojedynczego tokenu. Na razie będzie to po prostu iloczyn poszczególnych wartości.
|
tylko pojedynczego tokenu. Na razie będzie to po prostu iloczyn poszczególnych wartości.
|
||||||
|
|
||||||
$$p(l) = \prod_{k=1}^K \frac{e^{\vec{w}\vec{v}(t^{k-c},\dots,t^k,\dots,t^{k+c})}}{Z_k} = \frac{e^{\sum_{k=1}^K\vec{w}\vec{v}(t^{k-c},\dots,t^k,\dots,t^{k+c})}}{\prod_{k=1}^K Z_k}$$
|
$$p(l) = \prod_{k=1}^K \frac{e^{W\vec{v}(t^{k-c},\dots,t^k,\dots,t^{k+c})}}{Z_k} = \frac{e^{\sum_{k=1}^KW\vec{v}(t^{k-c},\dots,t^k,\dots,t^{k+c})}}{\prod_{k=1}^K Z_k}$$
|
||||||
|
|
||||||
|
Reprezentacja kontekstu może być funkcją embeddingów wyrazów
|
||||||
|
(zakładamy, że embedding nie zależy od pozycji słowa).
|
||||||
|
|
||||||
|
$$\vec{v}(t^{k-c},\dots,t^k,\dots,t^{k+c}) = f(\vec{E}(t^{k-c}),\dots,\vec{E}(t^k),\dots,\vec{E}({t^{k+c}})$$
|
||||||
|
|
||||||
** Warunkowe pola losowe
|
** Warunkowe pola losowe
|
||||||
|
|
||||||
@ -646,13 +877,178 @@ graf wyrażający „następowanie po” (czyli sekwencje). Do poprzedniego
|
|||||||
wzoru dodamy składnik $V_{i,j}$ (który można interpretować jako
|
wzoru dodamy składnik $V_{i,j}$ (który można interpretować jako
|
||||||
macierz) określający prawdopodobieństwo, że po etykiecie o numerze $i$ wystąpi etykieta o numerze $j$.
|
macierz) określający prawdopodobieństwo, że po etykiecie o numerze $i$ wystąpi etykieta o numerze $j$.
|
||||||
|
|
||||||
*** *Pytanie*: Czy macierz $V$ musi być symetryczna? Czy $V_{i,j} = V_{j,i}$? Czy jakieś specjalne wartości występują na przekątnej?
|
*Pytanie*: Czy macierz $V$ musi być symetryczna? Czy $V_{i,j} = V_{j,i}$? Czy jakieś specjalne wartości występują na przekątnej?
|
||||||
|
|
||||||
Macierz $V$ wraz z wektorem $\vec{w}$ będzie stanowiła wyuczalne wagi w naszym modelu.
|
Macierz $V$ wraz z macierzą $W$ będzie stanowiła wyuczalne wagi w naszym modelu.
|
||||||
|
|
||||||
Wartości $V_{i,j}$ nie stanowią bezpośrednio prawdopodobieństwa, mogą
|
Wartości $V_{i,j}$ nie stanowią bezpośrednio prawdopodobieństwa, mogą
|
||||||
przyjmować dowolne wartości, które będę normalizowane podobnie jak to się dzieje w funkcji Softmax.
|
przyjmować dowolne wartości, które będę normalizowane podobnie, tak jak to się dzieje w funkcji Softmax.
|
||||||
|
|
||||||
W takiej wersji warunkowych pól losowych otrzymamy następujący wzór na prawdopodobieństwo całej sekwencji.
|
W takiej wersji warunkowych pól losowych otrzymamy następujący wzór na prawdopodobieństwo całej sekwencji.
|
||||||
|
|
||||||
$$p(l) = \frac{e^{\sum_{k=1}^K\vec{w}\vec{v}(t^{k-c},\dots,t^k,\dots,t^{k+c}) + \sum_{k=1}^{K-1} V_{l_k,l_{k+1}}}}{\prod_{k=1}^K Z_k}$$
|
$$p(l) = \frac{e^{\sum_{k=1}^KW\vec{v}(t^{k-c},\dots,t^k,\dots,t^{k+c}) + \sum_{k=1}^{K-1} V_{l_k,l_{k+1}}}}{\prod_{k=1}^K Z_k}$$
|
||||||
|
|
||||||
|
** Algorytm Viterbiego
|
||||||
|
|
||||||
|
W czasie inferencji mamy ustalone wagi funkcji $\vec{v}(\dots)$ oraz
|
||||||
|
macierz $V$. Szukamy sekwencji $y$ która maksymalizuje prawdopodobieństwo estymowane przez model:
|
||||||
|
|
||||||
|
$$y = \underset{l}{\operatorname{argmax}} \hat{p}(l|t^1,\dots,t^K)$$
|
||||||
|
|
||||||
|
Naiwne podejście polegające na obliczeniu prawdopodobieństw wszystkich możliwych sekwencji miałoby
|
||||||
|
nieakceptowalną złożoność czasową $O(|L|^K)$.
|
||||||
|
|
||||||
|
Na szczęście, możemy użyć *algorytmu Viterbiego* o lepszej złożoności
|
||||||
|
obliczeniowej, algorytmu opartego na idei programowania dynamicznego.
|
||||||
|
|
||||||
|
W algorytmie będziemy wypełniać dwuwymiarowe tabele $s[i, j]$ i $b[i, j]$:
|
||||||
|
|
||||||
|
- $s[i, j]$ — będzie zawierać maksymalne prawdopodobieństwo (właściwie: nieznormalizowaną wartość,
|
||||||
|
która jest monotoniczna względem prawdopodobieństwa)
|
||||||
|
dla ciągów o długości $i$ zakończonych etykietą $l_j$,
|
||||||
|
- $b[i, j]$ — będzie zawierać „wskaźnik” wsteczny (/backpointer/) do podciągu o długości $i-1$, dla którego
|
||||||
|
razem z $l_j$ jest osiągana maksymalna wartość $s[i, j]$.
|
||||||
|
|
||||||
|
Inicjalizacja:
|
||||||
|
|
||||||
|
- $s[1, j] = (W\vec{v}(t^k,\dots,t^{k+c}))_j$,
|
||||||
|
- $b[1, j]$ — nie musimy wypełniać tej wartości.
|
||||||
|
|
||||||
|
Dla $i > 1$ i dla każdego $j$ będziemy teraz szukać:
|
||||||
|
|
||||||
|
$$\underset{q \in \{1,\dots,|V|}} \operatorname{max} s[i-1, q] + (W\vec{v}(t^{k-c},\dots,t^k,\dots,t^{k+c}))_j + V_{q, j}$$
|
||||||
|
|
||||||
|
Tę wartość przypiszemy do $s[i, j]$, z kolei do $b[i, j]$ — indeks
|
||||||
|
$q$, dla którego ta największa wartość jest osiągnięta.
|
||||||
|
|
||||||
|
Najpierw obliczenia wykonujemy wprzód wypełniając tabelę dla coraz większych wartości $j$.
|
||||||
|
W ten sposób otrzymamy największą wartość (nieznormalizowanego) prawdopodobieństwa:
|
||||||
|
|
||||||
|
$$\underset{q \in \{1,\dots,|V|}} \operatorname{max} s[K, q]$$
|
||||||
|
|
||||||
|
oraz ostatnią etykietę:
|
||||||
|
|
||||||
|
$$y^K = \underset{q \in \{1,\dots,|V|}} \operatorname{argmax} s[K, q]$$
|
||||||
|
|
||||||
|
Aby uzyskać cały ciąg, kierujemy się /wstecz/ używając wskaźników:
|
||||||
|
|
||||||
|
$$y^i = b[i, y^{i+1}]$$
|
||||||
|
|
||||||
|
*** Złożoność obliczeniowa
|
||||||
|
|
||||||
|
Zauważmy, że rozmiar tabel $s$ i $b$ wynosi $K \times |L|$, a koszt
|
||||||
|
wypełnienia każdej komórki to $|L|$, a zatem złożoność algorytmu jest wielomianowa:
|
||||||
|
$O(K|L|^2)$.
|
||||||
|
|
||||||
|
*Pytanie:* Czy gdyby uzależnić etykietę nie tylko od poprzedniej
|
||||||
|
etykiety, lecz również od jeszcze wcześniejszej, to złożoność
|
||||||
|
obliczeniowa byłaby taka sama?
|
||||||
|
|
||||||
|
*** Przykład
|
||||||
|
|
||||||
|
Rozpatrzmy uproszczony przykład tagowania częściami mowy:
|
||||||
|
|
||||||
|
- słownik $V=\{\mathit{Ala}, \mathit{powieść}, \mathit{ma}\}$,
|
||||||
|
- zbiór etykiet $L=\{\mathit{C}, \mathit{P}, \mathit{R}\}$,
|
||||||
|
- kontekst nie jest uwzględniany ($c = 0$).
|
||||||
|
|
||||||
|
(To, że liczba słów i etykiet jest taka sama, jest przypadkowe, nie ma znaczenia)
|
||||||
|
|
||||||
|
Zakładamy, że słowa reprezentujemy wektorowo za pomocą prostej reprezentacji one-hot.
|
||||||
|
|
||||||
|
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
|
||||||
|
import torch
|
||||||
|
|
||||||
|
vocab = ['Ala', 'ma', 'powieść']
|
||||||
|
labels = ['C', 'P', 'R']
|
||||||
|
|
||||||
|
onehot = {
|
||||||
|
'Ala': torch.tensor([1., 0., 0.]),
|
||||||
|
'ma': torch.tensor([0., 1., 0.]),
|
||||||
|
'powieść': torch.tensor([0., 0., 1.])
|
||||||
|
}
|
||||||
|
|
||||||
|
onehot['ma']
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
# Out[2]:
|
||||||
|
: tensor([0., 1., 0.])
|
||||||
|
:end:
|
||||||
|
|
||||||
|
Przyjmijmy, że w czasie uczenia zostały ustalone następujące wartości
|
||||||
|
macierzy $W$ i $V$ (samego procesu uczenia nie pokazujemy tutaj):
|
||||||
|
|
||||||
|
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
|
||||||
|
import torch
|
||||||
|
import torch.nn as nn
|
||||||
|
|
||||||
|
matrixW = torch.tensor(
|
||||||
|
[[-1., 3.0, 3.0],
|
||||||
|
[0., 2.0, -2.0],
|
||||||
|
[4., -2.0, 3.0]])
|
||||||
|
|
||||||
|
# rozkład prawdopodobieństwa, gdyby patrzeć tylko na słowo
|
||||||
|
nn.functional.softmax(matrixW @ onehot['powieść'], dim=0)
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
# Out[9]:
|
||||||
|
: tensor([0.4983, 0.0034, 0.4983])
|
||||||
|
:end:
|
||||||
|
|
||||||
|
|
||||||
|
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
|
||||||
|
import torch
|
||||||
|
import torch.nn as nn
|
||||||
|
|
||||||
|
matrixV = torch.tensor(
|
||||||
|
[[-0.5, 1.5, 2.0],
|
||||||
|
[0.5, 0.8, 2.5],
|
||||||
|
[2.0, 0.8, 0.2]])
|
||||||
|
|
||||||
|
# co występuje po przymiotniku? - rozkład prawdopodobieństwa
|
||||||
|
nn.functional.softmax(matrixV[1], dim=0)
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
# Out[10]:
|
||||||
|
: tensor([0.1027, 0.1386, 0.7587])
|
||||||
|
:end:
|
||||||
|
|
||||||
|
Algorytm Viterbiego:
|
||||||
|
|
||||||
|
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
|
||||||
|
d = ['Ala', 'ma', 'powieść']
|
||||||
|
|
||||||
|
s = []
|
||||||
|
b = []
|
||||||
|
|
||||||
|
# inicjalizacja
|
||||||
|
s.append(matrixW @ onehot[d[0]])
|
||||||
|
b.append(None)
|
||||||
|
|
||||||
|
# wprzód
|
||||||
|
i = 1
|
||||||
|
os = []
|
||||||
|
ob = []
|
||||||
|
for j in range(0, len(labels)):
|
||||||
|
z = s[i-1] + matrixV[:,j] + matrixW @ onehot[d[i]]
|
||||||
|
|
||||||
|
ns = torch.max(z).item()
|
||||||
|
nb = torch.argmax(z).item()
|
||||||
|
|
||||||
|
os.append(ns)
|
||||||
|
ob.append(nb)
|
||||||
|
|
||||||
|
os
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
# Out[16]:
|
||||||
|
: [4.0, 3.5, 4.5]
|
||||||
|
:end:
|
||||||
|
BIN
wyk/img-feed-forward.png
Normal file
BIN
wyk/img-feed-forward.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
BIN
wyk/img-linear-regression.png
Normal file
BIN
wyk/img-linear-regression.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
wyk/img-logistic-regression-aardvark.png
Normal file
BIN
wyk/img-logistic-regression-aardvark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
wyk/img-logistic-regression-hashing.png
Normal file
BIN
wyk/img-logistic-regression-hashing.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
wyk/img-logistic-regression.png
Normal file
BIN
wyk/img-logistic-regression.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Loading…
Reference in New Issue
Block a user