forked from filipg/aitech-eks-pub
Merge branch 'master' of git.wmi.amu.edu.pl:filipg/aitech-eks
This commit is contained in:
commit
af907e23af
File diff suppressed because one or more lines are too long
@ -840,7 +840,7 @@ Można spróbować uzyskać bogatszą reprezentację dla słowa biorąc pod uwag
|
||||
- długość słowa
|
||||
- kształt słowa (/word shape/), np. czy pisany wielkimi literami, czy składa się z cyfr itp.
|
||||
- n-gramy znakowe wewnątrz słowa (np. słowo /Kowalski/ można zakodować jako sumę wektorów
|
||||
trigramów znakówych $\vec{v}(Kow) + \vec{v}(owa) + \vec{v}(wal) + \vec{v}(als) + \vec{v}(lsk) + + \vec{v}(ski)$
|
||||
trigramów znakówych $\vec{v}(Kow) + \vec{v}(owa) + \vec{v}(wal) + \vec{v}(als) + \vec{v}(lsk) + \vec{v}(ski)$
|
||||
|
||||
Cały czas nie rozpatrujemy jednak w tej metodzie kontekstu wyrazu.
|
||||
(/Renault/ w pewnym kontekście może być nazwą firmy, w innym —
|
||||
@ -852,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}$.
|
||||
Innymi słowy klasyfikujemy token na podstawie jego samego oraz jego kontekstu:
|
||||
|
||||
$$p(l^k=j) = \frac{e^{(W\vec{v}(t^{k-c},\dots,t^k,\dots,t^{k+c}))_j}}{Z_k}.$$
|
||||
$$p(l^k=j) = \frac{e^{(W\vec{v}(t^{k-c},\dots,t^k,\dots,t^{k+c}))_j}}{Z}.$$
|
||||
|
||||
Zauważmy, że w tej metodzie w ogóle nie rozpatrujemy sensowności
|
||||
sekwencji wyjściowej (etykiet), np. może być bardzo mało
|
||||
@ -861,12 +861,12 @@ prawdopodobne, że bezpośrednio po nazwisku występuje data.
|
||||
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.
|
||||
|
||||
$$p(l) = \prod_{k=1}^K \frac{e^{(W\vec{v}(t^{k-c},\dots,t^k,\dots,t^{k+c}))_{l^k}}}{Z_k} = \frac{e^{\sum_{k=1}^K (W\vec{v}(t^{k-c},\dots,t^k,\dots,t^{k+c}))_{l^k}}}{\prod_{k=1}^K Z_k}$$
|
||||
$$l = (l^1,\\dots,l^k), p(l) = \prod_{k=1}^K \frac{e^{(W\vec{v}(t^{k-c},\dots,t^k,\dots,t^{k+c}))_{l^k}}}{Z_k} = \frac{e^{\sum_{k=1}^K (W\vec{v}(t^{k-c},\dots,t^k,\dots,t^{k+c}))_{l^k}}}{\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}})$$
|
||||
$$\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
|
||||
|
||||
@ -934,6 +934,8 @@ Aby uzyskać cały ciąg, kierujemy się /wstecz/ używając wskaźników:
|
||||
|
||||
$$y^i = b[i, y^{i+1}]$$
|
||||
|
||||
[[./crf-viterbi.png]]
|
||||
|
||||
*** Złożoność obliczeniowa
|
||||
|
||||
Zauważmy, że rozmiar tabel $s$ i $b$ wynosi $K \times |L|$, a koszt
|
||||
@ -985,10 +987,10 @@ macierzy $W$ i $V$ (samego procesu uczenia nie pokazujemy tutaj):
|
||||
import torch.nn as nn
|
||||
|
||||
matrixW = torch.tensor(
|
||||
[[-1., 3.0, 3.0],
|
||||
[0., 2.0, -2.0],
|
||||
[4., -2.0, 3.0]])
|
||||
|
||||
[[-1., 3.0, 3.0], # C
|
||||
[0., 2.0, -2.0], # P
|
||||
[4., -2.0, 3.0]]) # R
|
||||
# Ala ma powieść
|
||||
# rozkład prawdopodobieństwa, gdyby patrzeć tylko na słowo
|
||||
nn.functional.softmax(matrixW @ onehot['powieść'], dim=0)
|
||||
#+END_SRC
|
||||
@ -1005,9 +1007,10 @@ macierzy $W$ i $V$ (samego procesu uczenia nie pokazujemy tutaj):
|
||||
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]])
|
||||
[[-0.5, 1.5, 2.0], # C
|
||||
[0.5, 0.8, 2.5], # P
|
||||
[2.0, 0.8, 0.2]]) # R
|
||||
# C P R
|
||||
|
||||
# co występuje po przymiotniku? - rozkład prawdopodobieństwa
|
||||
nn.functional.softmax(matrixV[1], dim=0)
|
||||
|
1
wyk/11_rnn.ipynb
Normal file
1
wyk/11_rnn.ipynb
Normal file
File diff suppressed because one or more lines are too long
288
wyk/11_rnn.org
Normal file
288
wyk/11_rnn.org
Normal file
@ -0,0 +1,288 @@
|
||||
|
||||
* Rekurencyjne sieci neuronowe
|
||||
** Inne spojrzenie na sieci przedstawione do tej pory
|
||||
*** Regresja liniowa/logistyczna lub klasyfikacja wieloklasowa na całym tekście
|
||||
|
||||
W regresji liniowej czy logistycznej bądź w klasyfikacji wieloklasowej
|
||||
(z funkcją Softmax) stosowaliśmy następujący schemat:
|
||||
|
||||
Do tej pory patrzyliśmy na to tak, że po prostu cały tekst jest od
|
||||
razu przetwarzany przez (prostą) sieć neuronową, popatrzmy na ten
|
||||
przypadek, jak na sytuację przetwarzania sekwencyjnego. Będzie to
|
||||
trochę sztuczne, ale uogólnimy to potem w sensowny sposób.
|
||||
|
||||
**** Wektoryzacja
|
||||
|
||||
Po pierwsze, zauważmy, że w wielu schematach wektoryzacji (np. tf), wektor
|
||||
dokumentów jest po prostu sumą wektorów poszczególnych składowych:
|
||||
|
||||
$$\vec{v}(d) = \vec{v}(t^1,\ldots,t^K) = \vec{v}(t^1) + \ldots + \vec{v}(t^K) = \sum_{k=1}^K \vec{v}(t^i),$$
|
||||
|
||||
gdzie w schemacie tf \vec{v}(t^i) to po prostu wektor /one-hot/ dla słowa.
|
||||
|
||||
*Pytanie* Jak postać przyjmie w \vec{v}(t^i) dla wektoryzacji tf-idf?
|
||||
|
||||
Wektory $\vec{v}(t^k)$ mogą być również gęstymi wektorami
|
||||
($\vec{v}(t^k) \in \mathcal{R}^n$, gdzie $n$ jest rzędu 10-1000), np.
|
||||
w modelu Word2vec albo mogą to być *wyuczalne* wektory (zanurzenia
|
||||
słów, /embeddings/), tzn. wektory, które są parametrami uczonej sieci!
|
||||
|
||||
*Pytanie* Ile wag (parametrów) wnoszą wyuczalne wektory do sieci?
|
||||
|
||||
**** Prosta wektoryzacja wyrażona w modelu sekwencyjnym
|
||||
|
||||
Jak zapisać równoważnie powyższą wektoryzację w modelu *sekwencyjnym*, tj. przy założeniu, że
|
||||
przetwarzamy wejście token po tokenie (a nie „naraz”)? Ogólnie wprowadzimy bardzo
|
||||
ogólny model sieci *rekurencyjnej*.
|
||||
|
||||
Po pierwsze zakładamy, że sieć ma pewien stan $\vec{s^k} \in
|
||||
\mathcal{R}^m$ (stan jest wektorem o długości $m$), który może
|
||||
zmieniać się z każdym krokiem (przetwarzanym tokenem). Zmiana stanu
|
||||
jest określona przez pewną funkcję $R : \mathcal{R}^m \times
|
||||
\mathcal{R}^n \rightarrow \mathcal{R}^m$ ($n$ to rozmiar wektorów
|
||||
$\vec{v}(t^k)$):
|
||||
|
||||
$$\vec{s^k} = R(\vec{s^{k-1}}, \vec{v}(t^k)).$$
|
||||
|
||||
W przypadku wektoryzacji tf-idf mamy do czynienia z prostym
|
||||
sumowaniem, więc $R$ przyjmuje bardzo prostą postać:
|
||||
|
||||
$$\vec{s^0} = [0,\dots,0],$$
|
||||
|
||||
$$R(\vec{s}, \vec{x}) = \vec{s} + \vec{x}.$$
|
||||
|
||||
**** Wyjście z modelu
|
||||
|
||||
Dla regresji liniowej/logistycznej, oprócz funkcji $R$, która określa
|
||||
zmianę stanu, potrzebujemy funkcji $O$, która określa wyjście systemu w każdym kroku.
|
||||
|
||||
$$y^k = O(\vec{s^k})$$
|
||||
|
||||
W zadaniach klasyfikacji czy regresji, kiedy patrzymy na cały tekst w
|
||||
zasadzie wystarczy wziąć /ostatnią/ wartość (tj. $y^K$). Można sobie
|
||||
wyobrazić sytuację, kiedy wartości $y^k$ dla $k < k$ również mogą być jakoś przydatne
|
||||
(np. klasyfikujemy na bieżąco tekst wpisywany przez użytkownika).
|
||||
|
||||
W każdym razie dla regresji liniowej funkcja $O$ przyjmie postać:
|
||||
|
||||
$$O(\vec{s}) = \vec{w}\vec{s}$$,
|
||||
|
||||
gdzie $\vec{w}$ jest wektorem wyuczylnych wag, dla regresji zaś logistycznej:
|
||||
|
||||
$$O(\vec{s}) = \operatorname{softmax}(\vec{w}\vec{s})$$
|
||||
|
||||
*Pytanie*: jaką postać przyjmie $O$ dla klasyfikacji wieloklasowej
|
||||
|
||||
** Prosta sieć rekurencyjna
|
||||
|
||||
W najprostszej sieci rekurencyjnej (/Vanilla RNN/, sieć Elmana,
|
||||
czasami po prostu RNN) w każdym kroku oprócz właściwego wejścia
|
||||
($\vec{v}(t^k)$) będziemy również podawać na wejściu poprzedni stan
|
||||
sieci ($\vec{s^{k-1}}$).
|
||||
|
||||
Innymi słowy, funkcje $R$ przyjmie następującą postać:
|
||||
|
||||
$$s^k = \sigma(W\langle\vec{v}(t^k), \vec{s^{k-1}}\rangle + \vec{b}),$$
|
||||
|
||||
gdzie:
|
||||
|
||||
- $\langle\vec{x},\vec{y}\rangle$ to konkatenacja dwóch wektorów,
|
||||
- $W \in \mathcal{R}^m \times \mathcal{R}^{n+m} $ — macierz wag,
|
||||
- $b \in \mathcal{R}^m$ — wektor obciążeń (/biases/).
|
||||
|
||||
Taką sieć RNN można przedstawić schematycznie w następujący sposób:
|
||||
|
||||
[[./img-rnn.png]]
|
||||
|
||||
Zauważmy, że zamiast macierzy $W$ działającej na konkatenacji wektorów można wprowadzić dwie
|
||||
macierze $U$ i $V$ i tak zapisać wzór:
|
||||
|
||||
$$s^k = \sigma(U\vec{v}(t^k) + V\vec{s^{k-1}} + \vec{b}).$$
|
||||
|
||||
Jeszcze inne spojrzenie na sieć RNN:
|
||||
|
||||
[[./rnn.png]]
|
||||
|
||||
Powyższy rysunek przedstawia pojedynczy krok sieci RNN. Dla całego
|
||||
wejścia (powiedzmy, 3-wyrazowego) możemy sieć rozwinąć (/unroll/):
|
||||
|
||||
[[./rnn-seq.png]]
|
||||
|
||||
*** Zastosowanie sieci RNN do etykietowania sekwencji
|
||||
|
||||
*** Problemy z prostymi sieciami RNN
|
||||
|
||||
W praktyce proste sieci RNN są bardzo trudne w uczenia, zazwyczaj
|
||||
pojawia się problem *zanikających* (rzadziej: *eksplodujących*)
|
||||
gradientów: w propagacji wstecznej błąd szybko zanika i nie jest w
|
||||
stanie dotrzeć do początkowych wejść.
|
||||
|
||||
** Sieci RNN z bramkami
|
||||
|
||||
W prostych sieciach RNN podstawowa trudność polega na tym, że mamy
|
||||
niewielką kontrolę nad tym jak pamięć (stan) jest aktualizowana. Aby
|
||||
zwiększyć tę kontrolę, potrzebujemy *bramek*.
|
||||
|
||||
*** Bramki
|
||||
|
||||
Zazwyczaj do tej pory rozpatrywaliśmy iloczyn skalarny wektorów, w
|
||||
wyniku którego otrzymujemy liczbę (w PyTorchu wyrażany za pomocą operatora ~@~), np.
|
||||
|
||||
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
|
||||
import torch
|
||||
|
||||
a = torch.tensor([-1, 0, 3])
|
||||
b = torch.tensor([2, 5, -1])
|
||||
a @ b
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
:results:
|
||||
# Out[2]:
|
||||
: tensor(-5)
|
||||
:end:
|
||||
|
||||
Czasami przydatny jest *iloczyn Hadamarda*, czyli przemnożenie
|
||||
wektorów (albo macierzy) po współrzędnych. W PyTorchu taki iloczyn
|
||||
wyrażany jest za pomocą operatora ~*~, w notacji matematycznej będziemy używali
|
||||
znaku $\odot$.
|
||||
|
||||
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
|
||||
import torch
|
||||
|
||||
a = torch.tensor([-1, 0, 3])
|
||||
b = torch.tensor([2, 5, -1])
|
||||
a * b
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
:results:
|
||||
# Out[3]:
|
||||
: tensor([-2, 0, -3])
|
||||
:end:
|
||||
|
||||
Zauważmy, że iloczyn Hadamarda przez wektor złożony z zer i jedynek daje nam /filtr/, możemy
|
||||
selektywnie wygaszać pozycje wektora, np. tutaj wyzerowaliśmy 2. i 5. pozycję wektora:
|
||||
|
||||
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
|
||||
import torch
|
||||
|
||||
a = torch.tensor([1., 2., 3., 4., 5.])
|
||||
b = torch.tensor([1., 0., 1., 1., 0.])
|
||||
a * b
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
:results:
|
||||
# Out[4]:
|
||||
: tensor([1., 0., 3., 4., 0.])
|
||||
:end:
|
||||
|
||||
|
||||
Co więcej, za pomocą bramki możemy selektywnie kontrolować, co
|
||||
zapamiętujemy, a co zapominamy. Rozpatrzmy mianowicie wektor zer i
|
||||
jedynek $\vec{g} \in \{0,1}^m$, dla stanu (pamięci) $\vec{s}$ i nowej informacji
|
||||
$\vec{x}$ możemy dokonywać aktualizacji w następujący sposób:
|
||||
|
||||
$$\vec{s} \leftarrow \vec{g} \odot \vec{x} + (1 - \vec{g}) \odot \vec{s}$$
|
||||
|
||||
Na przykład, za pomocą bramki można wpisać nową wartość na 2. i 5. pozycję wektora.
|
||||
|
||||
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
|
||||
import torch
|
||||
|
||||
s = torch.tensor([1., 2., 3., 4., 5.])
|
||||
x = torch.tensor([8., 7., 15., -3., -8.])
|
||||
|
||||
g = torch.tensor([0., 1., 0., 0., 1.])
|
||||
|
||||
s = g * x + (1 - g) * s
|
||||
s
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
:results:
|
||||
# Out[8]:
|
||||
: tensor([ 1., 7., 3., 4., -8.])
|
||||
:end:
|
||||
|
||||
Wektor bramki nie musi być z góry określony, może być wyuczalny. Wtedy
|
||||
jednak lepiej założyć, że bramka jest „miękka”, np. jej wartości
|
||||
pochodzi z sigmoidy zastosowanej do jakiejś wcześniejszej warstwy.
|
||||
|
||||
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
|
||||
import torch
|
||||
|
||||
s = torch.tensor([1., 2., 3., 4., 5.])
|
||||
x = torch.tensor([8., 7., 15., -3., -8.])
|
||||
|
||||
pre_g = torch.tensor([-2.5, 10.0, -1.2, -101., 1.3])
|
||||
g = torch.sigmoid(pre_g)
|
||||
|
||||
s = g * x + (1 - g) * s
|
||||
s
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
:results:
|
||||
# Out[14]:
|
||||
: tensor([ 1.5310, 6.9998, 5.7777, 4.0000, -5.2159])
|
||||
:end:
|
||||
|
||||
*Pytanie:* dlaczego sigmoida zamiast tanh?
|
||||
|
||||
*** Sieć LSTM
|
||||
|
||||
Architektura LSTM (/Long Short-Term Memory/) pozwala rozwiązać problem
|
||||
znikających gradientów — za cenę komplikacji obliczeń.
|
||||
|
||||
W sieci LSTM stan $\vec{s^k}$ ma dwie połówki, tj. $\vec{s^k} =
|
||||
\langle\vec{c^k},\vec{h^k}\rangle$, gdzie
|
||||
|
||||
- $\vec{c^k}$ to *komórka pamięci*, która nie zmienia swojej, chyba że celowo zmodyfikujemy jej wartość
|
||||
za pomocą bramek,
|
||||
- $\vec{h^k}$ to ukryty stan (przypominający $\vec{s^k}$ ze zwykłej sieci RNN).
|
||||
|
||||
Sieć LSTM zawiera 3 bramki:
|
||||
|
||||
- bramkę zapominania (/forget gate/), która steruje wymazywaniem informacji z komórki
|
||||
pamięci $\vec{c^k}$,
|
||||
- bramkę wejścia (/input gate/), która steruje tym, na ile nowe informacje aktualizują
|
||||
komórkę pamięci $\vec{c^k}$,
|
||||
- bramkę wyjścia (/output gate/), która steruje tym, co z komórki
|
||||
pamięci przekazywane jest na wyjście.
|
||||
|
||||
Wszystkie trzy bramki definiowane są za pomocą bardzo podobnego wzoru — warstwy liniowej na
|
||||
poprzedniej wartości warstwy ukrytej i bieżącego wejścia.
|
||||
|
||||
$$\vec{i} = \sigma(W_i\langle\vec{v}(t^k),\vec{h^{k-1}}\rangle)$$
|
||||
|
||||
$$\vec{f} = \sigma(W_f\langle\vec{v}(t^k),\vec{h^{k-1}}\rangle)$$
|
||||
|
||||
$$\vec{o} = \sigma(W_f\langle\vec{v}(t^k),\vec{h^{k-1}}\rangle)$$
|
||||
|
||||
Jak widać, wzory różnią się tylko macierzami wag $W_*$.
|
||||
|
||||
Zmiana komórki pamięci jest zdefiniowana jak następuje:
|
||||
|
||||
$$\vec{c^k} = \vec{f} \odot \vec{c^{k-1}} + \vec{i} \vec{z^k}$$,
|
||||
|
||||
gdzie
|
||||
|
||||
$$\vec{z^k} = \operatorname{tanh}(W_z\langle\vec{v}(t^k),\vec{h^{k-1}}\rangle)$$
|
||||
|
||||
Stan ukryty zmienia się w następujący sposób:
|
||||
|
||||
$$\vec{h^K} = \vec{o} \odot \operatorname{tanh}(\vec{c^k})$$.
|
||||
|
||||
Ostateczne wyjście może być wyliczane na podstawie wektora $\vec{h^k}$:
|
||||
|
||||
$$O(\vec{s}) = O(\langle\vec{c},\vec{h}\rangle) = \vec{h}$$
|
||||
|
||||
*Pytanie*: Ile wag/parametrów ma sieć RNN o rozmiarze wejścia $n$ i
|
||||
|
||||
|
||||
** Literatura
|
||||
|
||||
Yoav Goldberg, /Neural Network Methods for Natural Language
|
||||
Processing/, Morgan & Claypool Publishers, 2017
|
1
wyk/crf-viterbi.drawio
Normal file
1
wyk/crf-viterbi.drawio
Normal file
@ -0,0 +1 @@
|
||||
<mxfile host="app.diagrams.net" modified="2021-05-26T13:43:59.377Z" agent="5.0 (X11)" etag="4emVx1cQBqc02lQgOp_X" version="14.6.13" type="device"><diagram id="7pFB8Xg2-vPC_YrQG171" name="Page-1">5ZpNc5swEIZ/Dcd2jAQ2PsbYTQ/pTKaZTt3eFJCNWowYWY5xf31FEAa0dr4DDLl40IIk9Lyr1UrYwv4muxQkjb7xkMYWGoWZhecWQlPHVr+54VAYXIwLw1qwsDDZleGG/aPaONLWHQvptvGg5DyWLG0aA54kNJANGxGC75uPrXjc7DUlawoMNwGJofUnC2VUWD00qexfKVtHZc/2eFrc2ZDyYT2SbURCvq+Z8MLCvuBcFlebzKdxzq7kUtT7cubu8cUETeRTKiwvJ+j3gvFb5s3Dq/2PdDlzPulW7ki80wO+iIl+X3koIUiaqS5mkdzEymCry60U/C/1ecyFsiQ8UU/OViyODROJ2TpRxUC9JFX22R0Vkim8F/rGhoVh3s1sHzFJb1IS5H3ulS8pm+C7JKT5+49USb+qaoBmZxnYR7LKIynfUCkO6pGywliLob3RdnR5X2lbmqKarKWGRHvT+thyBVxdaObP4I8A/82A8SO3Z/gxwJ/yPaOW71rTmeU7ljcZrhq4b2o4QA0f0FfNqNB/jlFNFLJNi/VgxbKc2tuED7dJzIPE8Ali+L2IjQGx634TQyd8rFViE0Dse7+J4VHHxJAHAYUqR9JFLmTE1zwh8aKyGsGqeuaK81TD+0OlPOiEj+wkb6KlGZPL2vWvvKnPri7NM93yfeFQFhI13mW9UKuVF6tq96WyXjG+fFAPa6YY8J0I6AOwNCtJxJrKh/KuM04gaEwku2u+yJtL6vU90CIzT+s60E7fNdDWcwUL4ZUX0CAAiYW6c+u5jvtWyZjBuPPQXG75+hubTWSdx2Yb7t96NpOx27OUyYZbrp7lTCay7icm3Cb1bGKayLqfmHAvs7XcmQKp2hvZljsHAIezr/SMKT+GYrS6r7Rh0m8PFz844zoxF9rFD9NNNFz83uNZVbv0YeqKh0sfO0bs6dr5EUxqBxx7DOe3y5WgM/ow2Rtw6AHH6507P0wcBxx7DOfHo66d3wX08xwUFTkoGnQOCpZh1PVUgEf1NTGGvSEAy8K465kBNwQ1MfCHEgN1vUZjeIYF6NMkvMj/zJFjjMl2y4KmFi89QH38zB9yrHFyT3AqbU8+ydc9XHOmOq5kmhoyuQb/4lOErlVJABrC5jGvbTRUfKsADd1reRz2K+SFOcCz5X3dx6Dq+8+vso12PgaVpF/8NagdVzselpUeMn6pq03OLLptuRo8dLv9KIdujiGiPe044cGn11g1Cn/YSthTM9t5NyVUsfr/YDGLqj9h4sV/</diagram></mxfile>
|
BIN
wyk/crf-viterbi.png
Normal file
BIN
wyk/crf-viterbi.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
Loading…
Reference in New Issue
Block a user