From 65e28bbbb9c6146ee6553a98cd0a0c723624d549 Mon Sep 17 00:00:00 2001 From: Filip Gralinski Date: Wed, 19 May 2021 13:21:13 +0200 Subject: [PATCH] org --- wyk/09_neurozoo.org | 643 ++++++++++++++++++ .../analyzer.py | 0 .../analyzer_classification.py | 0 .../linear0-infer.py | 0 .../linear0.py | 0 .../linear1-infer.py | 0 .../linear1.py | 0 .../linear1b.py | 0 .../linear2.py | 0 .../linear3-infer.py | 0 .../linear3.py | 0 .../linear4-batches.py | 0 .../linear4.py | 0 .../linear5.py | 0 .../linear6.py | 0 .../logistic6.py | 0 .../my_linear_regressor.py | 0 .../my_linear_regressor2.py | 0 .../my_neural_network.py | 0 19 files changed, 643 insertions(+) create mode 100644 wyk/09_neurozoo.org rename wyk/{pytorch-regression => pytorch_regression}/analyzer.py (100%) rename wyk/{pytorch-regression => pytorch_regression}/analyzer_classification.py (100%) rename wyk/{pytorch-regression => pytorch_regression}/linear0-infer.py (100%) rename wyk/{pytorch-regression => pytorch_regression}/linear0.py (100%) rename wyk/{pytorch-regression => pytorch_regression}/linear1-infer.py (100%) rename wyk/{pytorch-regression => pytorch_regression}/linear1.py (100%) rename wyk/{pytorch-regression => pytorch_regression}/linear1b.py (100%) rename wyk/{pytorch-regression => pytorch_regression}/linear2.py (100%) rename wyk/{pytorch-regression => pytorch_regression}/linear3-infer.py (100%) rename wyk/{pytorch-regression => pytorch_regression}/linear3.py (100%) rename wyk/{pytorch-regression => pytorch_regression}/linear4-batches.py (100%) rename wyk/{pytorch-regression => pytorch_regression}/linear4.py (100%) rename wyk/{pytorch-regression => pytorch_regression}/linear5.py (100%) rename wyk/{pytorch-regression => pytorch_regression}/linear6.py (100%) rename wyk/{pytorch-regression => pytorch_regression}/logistic6.py (100%) rename wyk/{pytorch-regression => pytorch_regression}/my_linear_regressor.py (100%) rename wyk/{pytorch-regression => pytorch_regression}/my_linear_regressor2.py (100%) rename wyk/{pytorch-regression => pytorch_regression}/my_neural_network.py (100%) diff --git a/wyk/09_neurozoo.org b/wyk/09_neurozoo.org new file mode 100644 index 0000000..56669ea --- /dev/null +++ b/wyk/09_neurozoo.org @@ -0,0 +1,643 @@ + +* Neurozoo +** 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. + +$$\sigma(x) = \frac{1}{1 + e^{-x}}$$ + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + import torch + + def sigmoid(x): + return 1 / (1 + torch.exp(-x)) + + sigmoid(torch.tensor(0.6)) +#+END_SRC + +#+RESULTS: +:results: +# Out[1]: +: tensor(0.6457) +:end: + +#+BEGIN_SRC ipython :session mysession :results file + %matplotlib inline + import matplotlib.pyplot as plt + import torch + + x = torch.linspace(-5,5,100) + plt.xlabel("x") + plt.ylabel("y") + plt.plot(x, sigmoid(x)) + fname = 'sigmoid.png' + plt.savefig(fname) + fname +#+END_SRC + +#+RESULTS: +[[file:# Out[32]: +: 'sigmoid.png' +[[file:./obipy-resources/Tb0Of9.png]]]] + +*** PyTorch + +Funkcja ~torch.sigmoid~ po prostu stosuje sigmoidę do każdego elementu tensora (/element-wise/). + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + import torch + + torch.sigmoid(torch.tensor([0.6, 1.0, -5.0])) +#+END_SRC + +#+RESULTS: +:results: +# Out[38]: +: tensor([0.6457, 0.7311, 0.0067]) +:end: + +Istnieje również ~torch.nn.Sigmoid~, które może być używane jako warstwa. + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + import torch.nn as nn + + s = nn.Sigmoid() + s(torch.tensor([0.0, -0.2, 0.4])) +#+END_SRC + +#+RESULTS: +:results: +# Out[49]: +: tensor([0.5000, 0.4502, 0.5987]) +:end: + +**** Implementacja w Pytorchu + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + import torch.nn as nn + import torch + + class MySigmoid(nn.Module): + def __init__(self): + super(MySigmoid, self).__init__() + + def forward(self, x): + return 1 / (1 + torch.exp(-x)) + + s = MySigmoid() + s(torch.tensor([0.0, 0.5, 0.3])) +#+END_SRC + +#+RESULTS: +:results: +# Out[48]: +: tensor([0.5000, 0.6225, 0.5744]) +:end: + +*** Wagi + +Funkcja sigmoidalna nie ma żadnych wyuczalnych wag. + +**** *Pytanie*: Czy można rozszerzyć funkcję sigmoidalną o jakieś wyuczalne wagi? + +** Regresja liniowa + +** Softmax + +W klasyfikacji wieloklasowej należy zwrócić musimy zwrócić rozkład +prawdopodobieństwa po wszystkich klasach, w przeciwieństwie do +klasyfikacji binarnej, gdzie wystarczy zwrócić jedną liczbę — +prawdopodobieństwo pozytywnej klasy ($p$; prawdopodobieństwo drugiej +klasy to po prostu $1-p$). + +A zatem na potrzeby klasyfikacji wieloklasowej potrzeba wektorowego +odpowiednika funkcji sigmoidalnej, to jest funkcji, która zamienia +nieznormalizowany wektor $\vec{z} = [z_1,\dots,z_k]$ (pochodzący np. z +poprzedzającej warstwy liniowej) na rozkład prawdopobieństwa. +Potrzebujemy zatem funkcji $s: \mathcal{R}^k \rightarrow [0,1]^k$ + +spełniającej następujące warunki: + +- $s(z_i) = s_i(z) \in [0,1]$ +- $\Sigma_i s(z_i) = 1$ +- $z_i > z_j \Rightarrow s(z_i) > s(z_j)$ + +Można by podać takie (*błędne*!) rozwiązanie: + +$$s(z_i) = \frac{z_i}{\Sigma_{j=1}^k z_j}$$ + +To rozwiązanie zadziała błędnie dla liczb ujemnych, trzeba najpierw +użyć funkcji monotonicznej, która przekształaca $\mathcal{R}$ na $\mathcal{R^+}$. +Naturalna funkcja tego rodzaju to funkcja wykładnicza $\exp{x} = e^x$. +Tym sposobem dochodzimy do funkcji softmax: + +$$s(z_i) = \frac{e^{z_i}}{\Sigma_{j=1}^k e^{z_j}}$$ + +Mianownik ułamka w definicji funkcji softmax nazywamy czasami czynnikiem normalizacyjnym: +$Z(\vec{z}) = \Sigma_{j=1}^k e^{z_j}$, wtedy: + +$$s(z_i) = \frac{e^{z_i}}{Z(\vec{z})}$$ + +Definicja w PyTorchu: + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + import torch + + def softmax(z): + z_plus = torch.exp(z) + return z_plus / torch.sum(z_plus) + + softmax(torch.tensor([3., -1., 0., 5.])) +#+END_SRC + +#+RESULTS: +:results: +# Out[75]: +: tensor([0.1182, 0.0022, 0.0059, 0.8737]) +:end: + +*** Soft vs hard + +Dlaczego /softmax/? Czasami używa się funkcji *hardmax*, która np. +wektora $[3, -1, 0, 5]$ zwróciłaby $[0, 0, 0, 5]$ — to jest po prostu +wektorowa wersja funkcji zwracającej maksimum. Istnieje też funkcja +hard*arg*max, która zwraca wektor /one-hot/ — z jedną jedynką na +pozycji dla największej wartości (zamiast podania największej +wartości), np. wartość hardargmax dla $[3, -1, 0, 5]$ zwróciłaby $[0, +0, 0, 1]$. + +Zauważmy, że powszechnie przyjęta nazwa /softmax/ jest właściwie +błędna, funkcja ta powinna nazywać się /softargmax/, jako że w +„miękki” sposób identyfikuje największą wartość przez wartość zbliżoną +do 1 (na pozostałych pozycjach wektora nie będzie 0). + +**** *Pytanie*: Jak można zdefiniować funkcję /softmax/ w ścisłym tego słowa znaczeniu („miękki” odpowiednik hardmax, nie hardargmax)? + + + +*** PyTorch + +Funkcja ~torch.nn.functional.softmax~ normalizuje wartości dla całego tensora: + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + import torch.nn as nn + + nn.functional.softmax(torch.tensor([0.6, 1.0, -5.0])) +#+END_SRC + +#+RESULTS: +:results: +# Out[5]: +: tensor([0.4007, 0.5978, 0.0015]) +:end: + +… zobaczmy, jak ta funkcja zachowuje się dla macierzy: + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + import torch.nn as nn + + nn.functional.softmax(torch.tensor([[0.6, 1.0], [-2.0, 3.5]])) +#+END_SRC + +#+RESULTS: +:results: +# Out[6]: +#+BEGIN_EXAMPLE + tensor([[0.4013, 0.5987], + [0.0041, 0.9959]]) +#+END_EXAMPLE +:end: + +Za pomocą (zalecanego zresztą) argumentu ~dim~ możemy określić wymiar, wzdłuż którego dokonujemy normalizacji: + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + import torch.nn as nn + + nn.functional.softmax(torch.tensor([[0.6, 1.0], [-2.0, 3.5]]), dim=0) +#+END_SRC + +#+RESULTS: +:results: +# Out[8]: +#+BEGIN_EXAMPLE + tensor([[0.9309, 0.0759], + [0.0691, 0.9241]]) +#+END_EXAMPLE +:end: + +Istnieje również ~torch.nn.Softmax~, które może być używane jako warstwa. + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + import torch.nn as nn + + s = nn.Softmax(dim=0) + s(torch.tensor([0.0, -0.2, 0.4])) +#+END_SRC + +#+RESULTS: +:results: +# Out[10]: +: tensor([0.3021, 0.2473, 0.4506]) +:end: + +**** Implementacja w Pytorchu + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + import torch.nn as nn + import torch + + class MySoftmax(nn.Module): + def __init__(self): + super(MySoftmax, self).__init__() + + def forward(self, x): + ex = torch.exp(x) + return ex / torch.sum(ex) + + s = MySigmoid() + s(torch.tensor([0.0, 0.5, 0.3])) +#+END_SRC + +#+RESULTS: +:results: +# Out[48]: +: tensor([0.5000, 0.6225, 0.5744]) +:end: + +***** *Pytanie*: Tak naprawdę wyżej zdefiniowana klasa ~MySoftmax~ nie zachowuje się identycznie jak ~nn.Softmax~. Na czym polega różnica? + +*** Przypadek szczególny + +Sigmoida jest przypadkiem szczególnym funkcji softmax: + +$$\sigma(x) = \frac{1}{1 + e^{-x}} = \frac{e^x}{e^x + 1} = \frac{e^x}{e^x + e^0} = s([x, 0])_1$$ + +Ogólniej: softmax na dwuelementowych wektorach daje przesuniętą sigmoidę (przy ustaleniu jednej z wartości). + +#+BEGIN_SRC ipython :session mysession :results file + %matplotlib inline + import matplotlib.pyplot as plt + import torch + import torch.nn as nn + + x = torch.linspace(-5,5,100) + plt.xlabel("x") + plt.ylabel("y") + a = torch.Tensor(x.size()[0]).fill_(2.) + m = torch.stack([x, a]) + plt.plot(x, nn.functional.softmax(m, dim=0)[0]) + fname = 'softmax3.png' + plt.savefig(fname) + fname +#+END_SRC + +#+RESULTS: +[[file:# Out[19]: +: 'softmax3.png' +[[file:./obipy-resources/gjBA7K.png]]]] + +#+BEGIN_SRC ipython :session mysession :results file + %matplotlib inline + import matplotlib.pyplot as plt + from mpl_toolkits import mplot3d + import torch + import torch.nn as nn + + x = torch.linspace(-5,5,10) + y = torch.linspace(-5,5,10) + fig = plt.figure() + ax = fig.add_subplot(111, projection='3d') + plt.xlabel("x") + plt.ylabel("y") + X, Y = torch.meshgrid(x, y) + m = torch.stack([X, Y]) + z = nn.functional.softmax(m, dim=0) + ax.plot_wireframe(x, y, z[0]) + fname = 'softmax3d.png' + plt.savefig(fname) + fname +#+END_SRC + +#+RESULTS: +[[file:# Out[27]: +: 'softmax3d.png' +[[file:./obipy-resources/p96515.png]]]] + +*** Wagi + +Podobnie jak funkcja sigmoidalna, softmax nie ma żadnych wyuczalnych wag. + +*** Zastosowania + +Podstawowym zastosowaniem funkcji softmax jest klasyfikacja +wieloklasowa, również w wypadku zadań przetwarzania sekwencji, które +mogą być interpretowane jako klasyfikacja wieloklasowa: + +- przewidywanie kolejnego słowa w modelowaniu języka (klasą jest słowo, zbiór klas to słownik) +- przypisywanie etykiet (np. części mowy) słowom. + +** LogSoftmax + +Ze względów obliczeniowych często korzysta się z funkcji *LogSoftmax* +która zwraca logarytmy pradopodobieństw (/logproby/). + +$$log s(z_i) = log \frac{e^{z_i}}{\Sigma_{j=1}^k e^{z_j}}$$ + +*** PyTorch + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + import torch.nn as nn + + s = nn.LogSoftmax(dim=0) + s(torch.tensor([0.0, -0.2, 0.4])) +#+END_SRC + +#+RESULTS: +:results: +# Out[25]: +: tensor([-1.1971, -1.3971, -0.7971]) +:end: + +Niektóre funkcje kosztu (np. ~NLLLoss~) zaimplementowane w PyTorchu +operują właśnie na logarytmach prawdopobieństw. + +** Przykład: klasyfikacja wieloklasowa + +Na przykładzie rozpoznawania dyscypliny sportu: git://gonito.net/sport-text-classification.git + +Wczytujemy zbiór uczący: + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + import gzip + from pytorch_regression.analyzer import vectorize_text, vector_length + + texts = [] + labels = [] + labels_dic = {} + labels_revdic = {} + c = 0 + + with gzip.open('sport-text-classification/train/train.tsv.gz', 'rt') as fh: + for line in fh: + line = line.rstrip('\n') + line = line.replace('\\\t', ' ') + label, text = line.split('\t') + texts.append(text) + if label not in labels_dic: + labels_dic[label] =c + labels_revdic[c] = label + c += 1 + labels.append(labels_dic[label]) + nb_of_labels = len(labels_dic) + labels_dic +#+END_SRC + +#+RESULTS: +:results: +# Out[23]: +#+BEGIN_EXAMPLE + {'zimowe': 0, + 'moto': 1, + 'tenis': 2, + 'pilka-reczna': 3, + 'sporty-walki': 4, + 'koszykowka': 5, + 'siatkowka': 6, + 'pilka-nozna': 7} +#+END_EXAMPLE +:end: + +Przygotowujemy model: + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + import torch.nn as nn + from torch import optim + + model = nn.Sequential( + nn.Linear(vector_length, nb_of_labels), + nn.LogSoftmax() + ) + + optimizer = optim.Adam(model.parameters()) +#+END_SRC + +#+RESULTS: +:results: +# Out[8]: +:end: + +Funkcja kosztu to log-loss. + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + import torch + import torch.nn.functional as F + + loss_fn = torch.nn.NLLLoss() + + expected_class_id = torch.tensor([2]) + loss_fn(torch.log( + torch.tensor([[0.3, 0.5, 0.1, 0.0, 0.1]])), + expected_class_id) +#+END_SRC + +#+RESULTS: +:results: +# Out[9]: +: tensor(2.3026) +:end: + +Pętla ucząca: + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + iteration = 0 + step = 50 + closs = torch.tensor(0.0, dtype=torch.float, requires_grad=False) + + for t, y_exp in zip(texts, labels): + x = vectorize_text(t).float().unsqueeze(dim=0) + + optimizer.zero_grad() + + y_logprobs = model(x) + + loss = loss_fn(y_logprobs, torch.tensor([y_exp])) + + loss.backward() + + with torch.no_grad(): + closs += loss + + optimizer.step() + + if iteration % 50 == 0: + print((closs / step).item(), loss.item(), iteration, y_exp, torch.exp(y_logprobs), t) + closs = torch.tensor(0.0, dtype=torch.float, requires_grad=False) + iteration += 1 + + if iteration == 5000: + break +#+END_SRC + +#+RESULTS: +:results: +# Out[25]: +:end: + +Model jest tak prosty, że jego wagi są interpretowalne. + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + with torch.no_grad(): + x = vectorize_text('NBA').float().unsqueeze(dim=0) + y_prob = model(x) + torch.exp(y_prob) +#+END_SRC + +#+RESULTS: +:results: +# Out[26]: +: tensor([[0.0070, 0.0075, 0.0059, 0.0061, 0.0093, 0.9509, 0.0062, 0.0071]]) +:end: + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + with torch.no_grad(): + x = vectorize_text('NBA').float().unsqueeze(dim=0) + ix = torch.argmax(x).item() + model[0].weight[:,ix] +#+END_SRC + +#+RESULTS: +:results: +# Out[32]: +#+BEGIN_EXAMPLE + tensor([-2.3693, -2.3421, -2.4205, -2.4353, -2.1499, 2.5163, -2.4351, -2.4546], + grad_fn=) +#+END_EXAMPLE +:end: + +Możemy nawet zaprezentować wykres przedstawiający rozmieszczenie słów względem dwóch osi odnoszących się do poszczególnych wybranych dyscyplin. + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer + %matplotlib inline + import matplotlib.pyplot as plt + + with torch.no_grad(): + words = ['piłka', 'klub', 'kort', 'boisko', 'samochód'] + words_ixs = [torch.argmax(vectorize_text(w).float().unsqueeze(dim=0)).item() for w in words] + + x_label = labels_dic['pilka-nozna'] + y_label = labels_dic['tenis'] + + x = [model[0].weight[x_label, ix] for ix in words_ixs] + y = [model[0].weight[y_label, ix] for ix in words_ixs] + + fig, ax = plt.subplots() + ax.scatter(x, y) + + for i, txt in enumerate(words): + ax.annotate(txt, (x[i], y[i])) +#+END_SRC + +#+RESULTS: +:results: +# Out[45]: +[[file:./obipy-resources/5egYcv.png]] +:end: + +** Zadanie etykietowania sekwencji + +Zadanie etykietowania sekwencji (/sequence labelling/) polega na przypisaniu poszczególnym wyrazom (tokenom) tekstu *etykiet* ze skończonego zbioru. Definiując formalnie: + +- rozpatrujemy ciąg wejściowy tokenów $(t^1,\dots,t^K)$ +- dany jest skończony zbiór etykiet $L = \{l_1,\dots,l_{|L|}\}$, dla uproszczenia można założyć, że etykietami + są po prostu kolejne liczby, tj. $L=\{0,\dots,|L|-1\}$ +- zadanie polega na wygenerowaniu sekwencji etykiet (o tej samej długości co ciąg wejściowy!) $(y^1,\dots,y^K)$, + $y^k \in L$ + +Zadanie etykietowania można traktować jako przypadek szczególny klasyfikacji wieloklasowej, z tym, że klasyfikacji dokonujemy wielokrotnie — dla każdego tokenu (nie dla każdego tekstu). + +Przykłady zastosowań: + +- oznaczanie częściami mowy (/POS tagger/) — czasownik, przymiotnik, rzeczownik itd. +- 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? + +*** Przykładowe wyzwanie NER CoNLL-2003 + +Zob. . + +Przykładowy przykład uczący (~xzcat train.tsv.xz| head -n 1~): + +O O B-MISC I-MISC O O O O O B-LOC O B-LOC O O O O O O O O O O O B-MISC I-MISC O O O O O B-PER I-PER O B-LOC O O O O O O B-PER I-PER O B-LOC O O O O O O B-PER I-PER I-PER O B-LOC O O O O O B-PER I-PER O O B-LOC O O O O O O B-PER I-PER O B-LOC O O O O O B-PER I-PER O O O O O B-PER I-PER O B-LOC O O O O O B-PER I-PER O B-LOC O B-LOC O O O O O O B-PER I-PER O O O O B-PER I-PER O B-LOC O O O O O O B-PER I-PER O B-LOC O O O O O B-PER I-PER O O O O O B-PER I-PER O B-LOC O O O O O B-PER I-PER O B-LOC O O O O O O B-PER I-PER O O O O O B-PER I-PER O B-LOC O O O O O B-PER I-PER O B-LOC O O O O O O B-PER I-PER O O O O B-PER I-PER I-PER O B-LOC O O O O O O B-PER I-PER O O O O B-PER I-PER O B-LOC O O O O O O B-PER I-PER O B-LOC O O O O O O B-PER I-PER O O O O B-PER I-PER O B-LOC O O O O O O B-PER I-PER O O O O B-PER I-PER O B-LOC O O O O O O B-PER I-PER O B-LOC O O O O O B-PER I-PER O B-LOC O B-LOC O O O O O B-PER I-PER O O O O O GOLF - BRITISH MASTERS THIRD ROUND SCORES . NORTHAMPTON , England 1996-08-30 Leading scores after the third round of the British Masters on Friday : 211 Robert Allenby ( Australia ) 69 71 71 212 Pedro Linhart ( Spain ) 72 73 67 216 Miguel Angel Martin ( Spain ) 75 70 71 , Costantino Rocca ( Italy ) 71 73 72 217 Antoine Lebouc ( France ) 74 73 70 , Ian Woosnam 70 76 71 , Francisco Cea ( Spain ) 70 71 76 , Gavin Levenson ( South Africa ) 66 75 76 218 Stephen McAllister 73 76 69 , Joakim Haeggman ( Swe ) 71 77 70 , Jose Coceres ( Argentina ) 69 78 71 , Paul Eales 75 71 72 , Klas Eriksson ( Sweden ) 71 75 72 , Mike Clayton ( Australia ) 69 76 73 , Mark Roe 69 71 78 219 Eamonn Darcy ( Ireland ) 74 76 69 , Bob May ( U.S. ) 74 75 70 , Paul Lawrie 72 75 72 , Miguel Angel Jimenez ( Spain ) 74 72 73 , Peter Mitchell 74 71 75 , Philip Walton ( Ireland ) 71 74 74 , Peter O'Malley ( Australia ) 71 73 75 220 Barry Lane 73 77 70 , Wayne Riley ( Australia ) 71 78 71 , Martin Gates 71 77 72 , Bradley Hughes ( Australia ) 73 75 72 , Peter Hedblom ( Sweden ) 70 75 75 , Retief Goosen ( South Africa ) 71 74 75 , David Gilford 69 74 77 . + +W pierwszym polu oczekiwany wynik zapisany za pomocą notacji *BIO*. + +Jako metrykę używamy F1 (z pominięciem tagu ~O~) + +*** Metryka F1 + +*** Etykietowanie za pomocą klasyfikacji wieloklasowej + +Można potraktować problem etykietowania dokładnie tak jak problem +klasyfikacji wieloklasowej (jak w przykładzie klasyfikacji dyscyplin +sportowych powyżej), tzn. rozkład prawdopodobieństwa możliwych etykiet +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},$$ + +gdzie $\vec{v}(t^k)$ to reprezentacja wektorowa tokenu $t^k$. +Zauważmy, że tutaj (w przeciwieństwie do klasyfikacji całego tekstu) +reprezentacja wektorowa jest bardzo uboga: wektor _one-hot_! Taki +klasyfikator w ogóle nie będzie brał pod uwagę kontekstu, tylko sam +wyraz, więc tak naprawdę zdegeneruje się to do zapamiętania częstości +etykiet dla każdego słowa osobno. + +**** Bogatsza reprezentacja słowa + +Można spróbować uzyskać bogatszą reprezentację dla słowa biorąc pod uwagę na przykład: + +- 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)$ + +Cały czas nie rozpatrujemy jednak w tej metodzie kontekstu wyrazu. +(/Renault/ w pewnym kontekście może być nazwą firmy, w innym — +nazwiskiem). + +**** Reprezentacja kontekstu + +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=i) = \frac{e^{\vec{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 +sekwencji wyjściowej (etykiet), np. może być bardzo mało +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^{\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}$$ + +** Warunkowe pola losowe + +Warunkowe pola losowe (/Conditional Random Fields/, /CRF/) to klasa +modeli, które pozwalają uwzględnić zależności między punktami danych +(które można wyrazić jako graf). Najprostszym przykładem będzie prosty +graf wyrażający „następowanie po” (czyli sekwencje). Do poprzedniego +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$. + +*** *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. + +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. + +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}$$ diff --git a/wyk/pytorch-regression/analyzer.py b/wyk/pytorch_regression/analyzer.py similarity index 100% rename from wyk/pytorch-regression/analyzer.py rename to wyk/pytorch_regression/analyzer.py diff --git a/wyk/pytorch-regression/analyzer_classification.py b/wyk/pytorch_regression/analyzer_classification.py similarity index 100% rename from wyk/pytorch-regression/analyzer_classification.py rename to wyk/pytorch_regression/analyzer_classification.py diff --git a/wyk/pytorch-regression/linear0-infer.py b/wyk/pytorch_regression/linear0-infer.py similarity index 100% rename from wyk/pytorch-regression/linear0-infer.py rename to wyk/pytorch_regression/linear0-infer.py diff --git a/wyk/pytorch-regression/linear0.py b/wyk/pytorch_regression/linear0.py similarity index 100% rename from wyk/pytorch-regression/linear0.py rename to wyk/pytorch_regression/linear0.py diff --git a/wyk/pytorch-regression/linear1-infer.py b/wyk/pytorch_regression/linear1-infer.py similarity index 100% rename from wyk/pytorch-regression/linear1-infer.py rename to wyk/pytorch_regression/linear1-infer.py diff --git a/wyk/pytorch-regression/linear1.py b/wyk/pytorch_regression/linear1.py similarity index 100% rename from wyk/pytorch-regression/linear1.py rename to wyk/pytorch_regression/linear1.py diff --git a/wyk/pytorch-regression/linear1b.py b/wyk/pytorch_regression/linear1b.py similarity index 100% rename from wyk/pytorch-regression/linear1b.py rename to wyk/pytorch_regression/linear1b.py diff --git a/wyk/pytorch-regression/linear2.py b/wyk/pytorch_regression/linear2.py similarity index 100% rename from wyk/pytorch-regression/linear2.py rename to wyk/pytorch_regression/linear2.py diff --git a/wyk/pytorch-regression/linear3-infer.py b/wyk/pytorch_regression/linear3-infer.py similarity index 100% rename from wyk/pytorch-regression/linear3-infer.py rename to wyk/pytorch_regression/linear3-infer.py diff --git a/wyk/pytorch-regression/linear3.py b/wyk/pytorch_regression/linear3.py similarity index 100% rename from wyk/pytorch-regression/linear3.py rename to wyk/pytorch_regression/linear3.py diff --git a/wyk/pytorch-regression/linear4-batches.py b/wyk/pytorch_regression/linear4-batches.py similarity index 100% rename from wyk/pytorch-regression/linear4-batches.py rename to wyk/pytorch_regression/linear4-batches.py diff --git a/wyk/pytorch-regression/linear4.py b/wyk/pytorch_regression/linear4.py similarity index 100% rename from wyk/pytorch-regression/linear4.py rename to wyk/pytorch_regression/linear4.py diff --git a/wyk/pytorch-regression/linear5.py b/wyk/pytorch_regression/linear5.py similarity index 100% rename from wyk/pytorch-regression/linear5.py rename to wyk/pytorch_regression/linear5.py diff --git a/wyk/pytorch-regression/linear6.py b/wyk/pytorch_regression/linear6.py similarity index 100% rename from wyk/pytorch-regression/linear6.py rename to wyk/pytorch_regression/linear6.py diff --git a/wyk/pytorch-regression/logistic6.py b/wyk/pytorch_regression/logistic6.py similarity index 100% rename from wyk/pytorch-regression/logistic6.py rename to wyk/pytorch_regression/logistic6.py diff --git a/wyk/pytorch-regression/my_linear_regressor.py b/wyk/pytorch_regression/my_linear_regressor.py similarity index 100% rename from wyk/pytorch-regression/my_linear_regressor.py rename to wyk/pytorch_regression/my_linear_regressor.py diff --git a/wyk/pytorch-regression/my_linear_regressor2.py b/wyk/pytorch_regression/my_linear_regressor2.py similarity index 100% rename from wyk/pytorch-regression/my_linear_regressor2.py rename to wyk/pytorch_regression/my_linear_regressor2.py diff --git a/wyk/pytorch-regression/my_neural_network.py b/wyk/pytorch_regression/my_neural_network.py similarity index 100% rename from wyk/pytorch-regression/my_neural_network.py rename to wyk/pytorch_regression/my_neural_network.py