This commit is contained in:
Filip Gralinski 2022-03-20 20:40:49 +01:00
parent abcb43bd4d
commit 6b35645469
22 changed files with 816 additions and 48 deletions

View File

@ -9,7 +9,7 @@ Używać będziemy generatorów.
*Pytanie* Dlaczego generatory zamiast list?
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
import requests
url = 'https://wolnelektury.pl/media/book/txt/pan-tadeusz.txt'
@ -31,7 +31,7 @@ Powrót pani
*** Znaki
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
from itertools import islice
def get_characters(t):
@ -45,7 +45,7 @@ Powrót pani
['K', 's', 'i', 'ę', 'g', 'a', ' ', 'p', 'i', 'e', 'r', 'w', 's', 'z', 'a', '\r', '\n', '\r', '\n', '\r', '\n', '\r', '\n', 'G', 'o', 's', 'p', 'o', 'd', 'a', 'r', 's', 't', 'w', 'o', '\r', '\n', '\r', '\n', 'P', 'o', 'w', 'r', 'ó', 't', ' ', 'p', 'a', 'n', 'i']
:end:
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
from collections import Counter
c = Counter(get_characters(pan_tadeusz))
@ -65,7 +65,7 @@ Napiszmy pomocniczą funkcję, która zwraca *listę frekwencyjną*.
Counter({' ': 63444, 'a': 30979, 'i': 29353, 'e': 25343, 'o': 23050, 'z': 22741, 'n': 15505, 'r': 15328, 's': 15255, 'w': 14625, 'c': 14153, 'y': 13732, 'k': 12362, 'd': 11465, '\r': 10851, '\n': 10851, 't': 10757, 'm': 10269, 'ł': 10059, ',': 9130, 'p': 8031, 'u': 7699, 'l': 6677, 'j': 6586, 'b': 5753, 'ę': 5534, 'ą': 4794, 'g': 4775, 'h': 3915, 'ż': 3334, 'ó': 3097, 'ś': 2524, '.': 2380, 'ć': 1956, ';': 1445, 'P': 1265, 'W': 1258, ':': 1152, '!': 1083, 'S': 1045, 'T': 971, 'I': 795, 'N': 793, 'Z': 785, 'J': 729, '—': 720, 'A': 698, 'K': 683, 'ń': 651, 'M': 585, 'B': 567, 'O': 567, 'C': 556, 'D': 552, '«': 540, '»': 538, 'R': 489, '?': 441, 'ź': 414, 'f': 386, 'G': 358, 'L': 316, 'H': 309, 'Ż': 219, 'U': 184, '…': 157, '*': 150, '(': 76, ')': 76, 'Ś': 71, 'F': 47, 'é': 43, '-': 33, 'Ł': 24, 'E': 23, '/': 19, 'Ó': 13, '8': 10, '9': 8, '2': 6, 'v': 5, 'Ź': 4, '1': 4, '3': 3, 'x': 3, 'V': 3, '7': 2, '4': 2, '5': 2, 'q': 2, 'æ': 2, 'à': 1, 'Ć': 1, '6': 1, '0': 1})
:end:
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
from collections import Counter
from collections import OrderedDict
@ -88,7 +88,7 @@ OrderedDict([(' ', 63444), ('a', 30979), ('i', 29353), ('e', 25343), ('o', 23050
:end:
#+BEGIN_SRC ipython :session mysession :results file
#+BEGIN_SRC python :session mysession :results file
import matplotlib.pyplot as plt
from collections import OrderedDict
@ -119,7 +119,7 @@ Co rozumiemy pod pojęciem słowa czy wyrazu, nie jest oczywiste. W praktyce zal
Załóżmy, że przez wyraz rozumieć będziemy nieprzerwany ciąg liter bądź cyfr (oraz gwiazdek
— to za chwilę ułatwi nam analizę pewnego tekstu…).
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
from itertools import islice
import regex as re
@ -138,7 +138,7 @@ Załóżmy, że przez wyraz rozumieć będziemy nieprzerwany ciąg liter bądź
Zobaczmy 20 najczęstszych wyrazów.
#+BEGIN_SRC ipython :session mysession :results file
#+BEGIN_SRC python :session mysession :results file
rang_freq_with_labels('pt-words-20', get_words(pan_tadeusz), top=20)
#+END_SRC
@ -147,7 +147,7 @@ Zobaczmy 20 najczęstszych wyrazów.
Zobaczmy pełny obraz, już bez etykiet.
#+BEGIN_SRC ipython :session mysession :results file
#+BEGIN_SRC python :session mysession :results file
import matplotlib.pyplot as plt
from math import log
@ -172,7 +172,7 @@ Zobaczmy pełny obraz, już bez etykiet.
Widać, jak różne skale obejmuje ten wykres. Zastosujemy logarytm,
najpierw tylko do współrzędnej $y$.
#+BEGIN_SRC ipython :session mysession :results file
#+BEGIN_SRC python :session mysession :results file
import matplotlib.pyplot as plt
from math import log
@ -222,7 +222,7 @@ logarytmicznej dla **obu** osi, otrzymamy kształt zbliżony do linii prostej.
Tę własność tekstów nazywamy **prawem Zipfa**.
#+BEGIN_SRC ipython :session mysession :results file
#+BEGIN_SRC python :session mysession :results file
import matplotlib.pyplot as plt
from math import log
@ -249,7 +249,7 @@ Tę własność tekstów nazywamy **prawem Zipfa**.
Powiązane z prawem Zipfa prawo językowe opisuje zależność między
częstością użycia słowa a jego długością. Generalnie im krótsze słowo, tym częstsze.
#+BEGIN_SRC ipython :session mysession :results file
#+BEGIN_SRC python :session mysession :results file
def freq_vs_length(name, g, top=None):
freq = freq_list(g)
@ -294,7 +294,7 @@ po prostu na jednostkach, nie na ich podciągach.
Statystyki, które policzyliśmy dla pojedynczych liter czy wyrazów, możemy powtórzyć dla n-gramów.
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
def ngrams(iter, size):
ngram = []
for item in iter:
@ -317,7 +317,7 @@ Zawsze powinniśmy się upewnić, czy jest jasne, czy chodzi o n-gramy znakowe c
*** 3-gramy znakowe
#+BEGIN_SRC ipython :session mysession :results file
#+BEGIN_SRC python :session mysession :results file
log_rang_log_freq('pt-3-char-ngrams-log-log', ngrams(get_characters(pan_tadeusz), 3))
#+END_SRC
@ -326,7 +326,7 @@ Zawsze powinniśmy się upewnić, czy jest jasne, czy chodzi o n-gramy znakowe c
*** 2-gramy wyrazowe
#+BEGIN_SRC ipython :session mysession :results file
#+BEGIN_SRC python :session mysession :results file
log_rang_log_freq('pt-2-word-ngrams-log-log', ngrams(get_words(pan_tadeusz), 2))
#+END_SRC
@ -348,7 +348,7 @@ transkrybować manuskrypt, pozostaje sprawą dyskusyjną, natomiast wybór
takiego czy innego systemu transkrypcji nie powinien wpływać
dramatycznie na analizę statystyczną.
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
import requests
voynich_url = 'http://www.voynich.net/reeds/gillogly/voynich.now'
@ -370,28 +370,28 @@ dramatycznie na analizę statystyczną.
9 OR 9FAM ZO8 QOAR9 Q*R 8ARAM 29 [O82*]OM OPCC9 OP
:end:
#+BEGIN_SRC ipython :session mysession :results file
#+BEGIN_SRC python :session mysession :results file
rang_freq_with_labels('voy-chars', get_characters(voynich))
#+END_SRC
#+RESULTS:
[[file:02_Jezyki/voy-chars.png]]
#+BEGIN_SRC ipython :session mysession :results file
#+BEGIN_SRC python :session mysession :results file
log_rang_log_freq('voy-log-log', get_words(voynich))
#+END_SRC
#+RESULTS:
[[file:02_Jezyki/voy-log-log.png]]
#+BEGIN_SRC ipython :session mysession :results file
#+BEGIN_SRC python :session mysession :results file
rang_freq_with_labels('voy-words-20', get_words(voynich), top=20)
#+END_SRC
#+RESULTS:
[[file:02_Jezyki/voy-words-20.png]]
#+BEGIN_SRC ipython :session mysession :results file
#+BEGIN_SRC python :session mysession :results file
log_rang_log_freq('voy-words-log-log', get_words(voynich))
#+END_SRC
@ -406,7 +406,7 @@ Podstawowe litery są tylko cztery, reprezentują one nukleotydy, z których zbu
a, g, c, t.
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
import requests
dna_url = 'https://raw.githubusercontent.com/egreen18/NanO_GEM/master/rawGenome.txt'
@ -423,7 +423,7 @@ a, g, c, t.
TATAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTA
:end:
#+BEGIN_SRC ipython :session mysession :results file
#+BEGIN_SRC python :session mysession :results file
rang_freq_with_labels('dna-chars', get_characters(dna))
#+END_SRC
@ -436,7 +436,7 @@ Nukleotydy rzeczywiście są jak litery, same w sobie nie niosą
znaczenia. Dopiero ciągi trzech nukleotydów, /tryplety/, kodują jeden
z dwudziestu aminokwasów.
#+BEGIN_SRC ipython :session mysession :results file
#+BEGIN_SRC python :session mysession :results file
genetic_code = {
'ATA':'I', 'ATC':'I', 'ATT':'I', 'ATG':'M',
'ACA':'T', 'ACC':'T', 'ACG':'T', 'ACT':'T',
@ -472,7 +472,7 @@ Z aminokwasów zakodowanych przez tryplet budowane są białka.
Maszyneria budująca białka czyta sekwencję aż do napotkania
trypletu STOP (_ powyżej). Taka sekwencja to /gen/.
#+BEGIN_SRC ipython :session mysession :results file
#+BEGIN_SRC python :session mysession :results file
def get_genes(triplets):
gene = []
for ammino in triplets:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 14 KiB

657
wyk/03_Entropia.ipynb Normal file

File diff suppressed because one or more lines are too long

View File

@ -11,12 +11,12 @@ W termodynamice entropia jest miarą nieuporządkowania układów
fizycznych, na przykład pojemników z gazem. Przykładowo, wyobraźmy
sobie dwa pojemniki z gazem, w którym panuje różne temperatury.
[[./03_Jezyki/gas-low-entropy.drawio.png]]
[[./03_Entropia/gas-low-entropy.drawio.png]]
Jeśli usuniemy przegrodę między pojemnikami, temperatura się wyrówna,
a uporządkowanie się zmniejszy.
[[./03_Jezyki/gas-high-entropy.drawio.png]]
[[./03_Entropia/gas-high-entropy.drawio.png]]
Innymi słowy, zwiększy się stopień nieuporządkowania układu, czyli właśnie entropia.
@ -46,7 +46,7 @@ losowania do odbiorcy $O$ używając zer i jedynek (bitów).
Teorioinformacyjną entropię można zdefiniować jako średnią liczbę
bitów wymaganych do przesłania komunikatu.
[[./03_Jezyki/communication.drawio.png]]
[[./03_Entropia/communication.drawio.png]]
*** Obliczanie entropii — proste przykłady
@ -187,6 +187,25 @@ Załóżmy, że chcemy zmierzyć entropię języka polskiego na przykładzie
„Pana Tadeusza” — na poziomie znaków. W pierwszym przybliżeniu można
by policzyć liczbę wszystkich znaków…
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
import requests
from itertools import islice
url = 'https://wolnelektury.pl/media/book/txt/pan-tadeusz.txt'
pan_tadeusz = requests.get(url).content.decode('utf-8')
def get_characters(t):
yield from t
list(islice(get_characters(pan_tadeusz), 100, 150))
#+END_SRC
#+RESULTS:
:results:
['K', 's', 'i', 'ę', 'g', 'a', ' ', 'p', 'i', 'e', 'r', 'w', 's', 'z', 'a', '\r', '\n', '\r', '\n', '\r', '\n', '\r', '\n', 'G', 'o', 's', 'p', 'o', 'd', 'a', 'r', 's', 't', 'w', 'o', '\r', '\n', '\r', '\n', 'P', 'o', 'w', 'r', 'ó', 't', ' ', 'p', 'a', 'n', 'i']
:end:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
chars_in_pan_tadeusz = len(set(get_characters(pan_tadeusz)))
chars_in_pan_tadeusz
@ -241,6 +260,30 @@ Policzmy entropię przy takim założeniu:
*** Ile wynosi entropia rękopisu Wojnicza?
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
import requests
import re
voynich_url = 'http://www.voynich.net/reeds/gillogly/voynich.now'
voynich = requests.get(voynich_url).content.decode('utf-8')
voynich = re.sub(r'\{[^\}]+\}|^<[^>]+>|[-# ]+', '', voynich, flags=re.MULTILINE)
voynich = voynich.replace('\n\n', '#')
voynich = voynich.replace('\n', ' ')
voynich = voynich.replace('#', '\n')
voynich = voynich.replace('.', ' ')
voynich[100:150]
#+END_SRC
#+RESULTS:
:results:
9 OR 9FAM ZO8 QOAR9 Q*R 8ARAM 29 [O82*]OM OPCC9 OP
:end:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
unigram_entropy(get_characters(voynich))
#+END_SRC

File diff suppressed because one or more lines are too long

View File

@ -50,15 +50,15 @@ Model języka:
czy transkrypcję w systemach rozpoznawania mowy (ASR)
(zanim zaczęto używać do tego sieci neuronowych, gdzie nie
ma już wyraźnego rozróżnienia między modelem tłumaczenia
czy modelem akustycznym a modelem języka
czy modelem akustycznym a modelem języka),
- pomaga znaleźć „podejrzane” miejsca w tekście
(korekta pisowni/gramatyki)
(korekta pisowni/gramatyki),
- może być stosowany jako klasyfikator (potrzeba wtedy więcej niż jednego modelu,
np. model języka spamów kontra model języka niespamów)
- może być stosowany w kompresji danych
np. model języka spamów kontra model języka niespamów),
- może być stosowany w kompresji danych,
- bardzo dobry model języka *musi* mieć *w środku* bardzo dobrą *wiedzę*
o języku i o świecie, można wziąć *„wnętrzności”* modelu, nie dbając o prawdopodobieństwa
i użyć modelu w zupełnie innym celu
i użyć modelu w zupełnie innym celu.
** N-gramowy model języka
@ -120,13 +120,21 @@ inny dla trigramowego. Jedne modele będą lepsze, inne — gorsze. Jak
obiektywnie odróżnić dobry model od złego? Innymi słowy, jak ewaluować
modele języka?
*** Ewaluacja zewnętrzna i wewnętrzna
W ewaluacji zewnętrznej (ang. /extrinsic/) ewaluację modelu języka sprowadzamy
do ewaluacji większego systemu, którego częścią jest model języka, na przykład
systemu tłumaczenia maszynowego albo systemu ASR.
Ewaluacja wewnętrzna (ang. /intrinsic/) polega na ewaluacji modelu języka jako takiego.
*** Podział zbioru
Po pierwsze, jak zazwyczaj bywa w uczeniu maszynowym, powinniśmy
podzielić nasz zbiór danych. W modelowaniu języka zbiorem danych jest
zbiór tekstów w danym języku, czyli korpus języka.
Powinniśmy podzielić nasz korpus na część uczącą (/training set/) $C = \{w_1\ldots w_N\}$ i testową
(/test set/) $C' = \{w_1'\ldots w_N'\}$.
(/test set/) $C' = \{w_1'\ldots w_{N'}'\}$.
Warto też wydzielić osobny „deweloperski” zbiór testowy (/dev set/) —
do testowania na bieżąco, optymalizacji hiperparametrów itd. Zbiory
@ -212,7 +220,7 @@ tym lepiej. Zatem im wyższe $P_M(C')$, tym lepiej.
Zazwyczaj będziemy rozbijali $P_M(C')$ na prawdopodobieństwa
przypisane do poszczególnych słów:
$$P_M(w_1'\dots w_N') = P_M(w'_1)P_M(w'_2|w'_1)\dots P_M(w'_{N'}|w'_1\dots w'_{N'-1}) = \prod_{i=1}^{N'} P_M(w'_i|w'_1\ldots w'_{i-1}).$$
$$P_M(w_1'\dots w_{N'}') = P_M(w'_1)P_M(w'_2|w'_1)\dots P_M(w'_{N'}|w'_1\dots w'_{N'-1}) = \prod_{i=1}^{N'} P_M(w'_i|w'_1\ldots w'_{i-1}).$$
*** Entropia krzyżowa
@ -230,7 +238,7 @@ realnie wystąpić, na przykład: z prawdopodobieństwem zbliżonym do 1 po wyra
/Hong/ wystąpi słowo /Kong/, a po wyrazie /przede/ — wyraz /wszystkim/.
Model języka może pomóc również w mniej skrajnym przypadkach, np.
jeśli na danej pozycji w tekście redukuje cały słownik do dwóch
jeżeli na danej pozycji w tekście model redukuje cały słownik do dwóch
wyrazów z prawdopodobieństwem 1/2, wówczas nadawca może zakodować tę
pozycję za pomocą jednego bitu.
@ -248,7 +256,7 @@ tę wartość, tzn. podzielić przez długość tekstu:
$$H(M) = -\frac{\sum_{i=1}^{N'} log P_M(w'_i|w'_1\ldots w'_{i-1})}{N'}.$$
Tę wartość nazywamy *entropią krzyżową* modelu $M$. Entropia krzyżowa
mierzy naszą niewiedzę przy posiadaniu modelu $M$. Im niższa wartość
mierzy naszą niewiedzę przy założeniu, że dysponujemy modelem $M$. Im niższa wartość
entropii krzyżowej, tym lepiej, im bowiem mniejsza nasza niewiedza,
tym lepiej.
@ -265,13 +273,13 @@ wspomnieliśmy, im wyższe prawdopodobieństwo (wiarygodność) przypisane
testowej części korpusu, tym lepiej. Innymi słowy, jako metrykę ewaluacji
używać będziemy prawdopodobieństwa:
$$P_M(w_1'\dots w_N') = P_M(w'_1)P_M(w'_2|w'_1)\dots P_M(w'_{N'}|w'_1\dots w'_{N'-1}) = \prod_{i=1}^{N'} P_M(w'_i|w'_1\ldots w'_{i-1}),$$
$$P_M(w_1'\dots w_{N'}') = P_M(w'_1)P_M(w'_2|w'_1)\dots P_M(w'_{N'}|w'_1\dots w'_{N'-1}) = \prod_{i=1}^{N'} P_M(w'_i|w'_1\ldots w'_{i-1}),$$
z tym, że znowu warto znormalizować to prawdopodobieństwo względem rozmiaru korpusu.
Ze względu na to, że prawdopodobieństwa przemnażamy zamiast średniej arytmetycznej
Ze względu na to, że prawdopodobieństwa przemnażamy, zamiast średniej arytmetycznej
lepiej użyć *średniej geometrycznej*:
$$\sqrt[N']{P_M(w_1'\dots w_N')} = \sqrt[N']{\prod_{i=1}^{N'} P_M(w'_i|w'_1\ldots w'_{i-1})}.$$
$$\sqrt[N']{P_M(w_1'\dots w_{N'}')} = \sqrt[N']{\prod_{i=1}^{N'} P_M(w'_i|w'_1\ldots w'_{i-1})}.$$
**** Interpretacja wiarygodności
@ -283,9 +291,9 @@ to *średnia geometryczna prawdopodobieństw przypisanych przez model języka do
**** Związek między wiarygodnością a entropią krzyżową
Istnieje bardzo prosty związek między entropią krzyżową a wiarygodnością.
Otóż entropia krzyżowa to po prostu logarytm wiarygodności:
Otóż entropia krzyżowa to po prostu logarytm wiarygodności (z minusem):
$$\log_2\sqrt[N']{P_M(w_1'\dots w_N')} = \frac{\log_2\prod_{i=1}^{N'} P_M(w'_i|w'_1\ldots w'_{i-1})}{N'} = \frac{\sum_{i=1}^{N'} \log_2 P_M(w'_i|w'_1\ldots w'_{i-1})}{N'}.$$
-$$\log_2\sqrt[N']{P_M(w_1'\dots w_N')} = -\frac{\log_2\prod_{i=1}^{N'} P_M(w'_i|w'_1\ldots w'_{i-1})}{N'} = -\frac{\sum_{i=1}^{N'} \log_2 P_M(w'_i|w'_1\ldots w'_{i-1})}{N'}.$$
**** „log-proby”
@ -302,7 +310,7 @@ zamiast wprost operować na prawdopodobieństwach:
Tak naprawdę w literaturze przedmiotu na ogół używa się jeszcze innej metryki ewaluacji —
*perplexity*. Perplexity jest definiowane jako:
$$PP(M) = 2^{H(M)}.$$
$$\operatorname{PP}(M) = 2^{H(M)}.$$
Intuicyjnie można sobie wyobrazić, że perplexity to liczba możliwości
prognozowanych przez model z równym prawdopodobieństwem. Na przykład,
@ -313,7 +321,7 @@ perplexity — 32.
Inaczej: perplexity to po prostu odwrotność wiarygodności:
$$\OperatorName{PP}(M) = \sqrt[N']{P_M(w_1'\dots w_N').}
$$\operatorname{PP}(M) = \sqrt[N']{P_M(w_1'\dots w_N')}.$$
Perplexity zależy oczywiście od języka i modelu, ale typowe wartości
zazwyczaj zawierają się w przedziale 20-400.
@ -330,10 +338,25 @@ dwa modele, jeden 3-gramowy, drugi 4-gramowy.
Z powodu, który za chwilę stanie się jasny, teksty w zbiorze uczącym musimy sobie „poskładać” z kilku „kawałków”.
#+BEGIN_SRC
xzcat in.tsv.xz | paste expected.tsv - | perl -ne 'chomp;s/\\n/ /g;@f=split/\t/;print "$f[7] $f[0] $f[8]"' | lmplz -o 3 > model3.arpa
xzcat in.tsv.xz | paste expected.tsv - | perl -ne 'chomp;s/\\n/ /g;@f=split/\t/;print "$f[7] $f[0] $f[8]"' | lmplz -o 4 > model3.arpa
$ cd train
$ xzcat in.tsv.xz | paste expected.tsv - | perl -ne 'chomp;s/\\n/ /g;s/<s>/ /g;@f=split/\t/;print "$f[7] $f[0] $f[8]\n"' | lmplz -o 3 --skip-symbols > model3.arpa
$ xzcat in.tsv.xz | paste expected.tsv - | perl -ne 'chomp;s/\\n/ /g;s/<s>/ /g;@f=split/\t/;print "$f[7] $f[0] $f[8]\n"' | lmplz -o 4 --skip-symbols > model4.arpa
$ cd ../dev-0
$ xzcat in.tsv.xz | paste expected.tsv - | perl -ne 'chomp;s/\\n/ /g;s/<s>/ /g;@f=split/\t/;print "$f[7] $f[0] $f[8]\n"' | query ../train/model3.arpa
Perplexity including OOVs: 976.9905056314793
Perplexity excluding OOVs: 616.5864921901557
OOVs: 125276
Tokens: 3452929
$ xzcat in.tsv.xz | paste expected.tsv - | perl -ne 'chomp;s/\\n/ /g;s/<s>/ /g;@f=split/\t/;print "$f[7] $f[0] $f[8]\n"' | query ../train/model4.arpa
Perplexity including OOVs: 888.698932611321
Perplexity excluding OOVs: 559.1231510292068
OOVs: 125276
Tokens: 3452929
#+END_SRC
Jak widać model 4-gramowy jest lepszy (ma niższe perplexity) niż model 3-gramowy, przynajmniej
jeśli wierzyć raportowi programu KenLM.
*** Entropia krzyżowa, wiarygodność i perplexity — podsumowanie
Trzy omawiane metryki ewaluacji modeli języka (entropia krzyżowa,
@ -342,9 +365,9 @@ rzeczy to po prostu jedna miara.
|Metryka | Kierunek |Najlepsza wartość | Najgorsza wartość |
|------------------+----------------------+------------------+-------------------|
|entropia krzyżowa | im mniej, tym lepiej | 0 | $\inf$ |
|entropia krzyżowa | im mniej, tym lepiej | 0 | $\infty$ |
|wiarygodność | im więcej, tym lepiej| 1 | 0 |
|perplexity | im mniej, tym lepiej | 1 | $\inf$ |
|perplexity | im mniej, tym lepiej | 1 | $\infty$ |
**** Uwaga na zerowe prawdopodobieństwa
@ -403,6 +426,50 @@ tekstu oczekuje się podania rozkładu prawdopodobieństwa dla brakującego sło
Mianowicie, w każdym wierszu wejściu (plik ~in.tsv.xz~) w 7. i 8. polu
podany jest, odpowiednio, lewy i prawy kontekst słowa do odgadnięcia.
(W pozostałych polach znajdują się metadane, o których już wspomnieliśmy,
na razie nie będziemy ich wykorzystywać).
W pliku z oczekiwanym wyjściem (~expected.tsv~), w odpowiadającym
wierszu, podawane jest brakujące słowo. Oczywiście w ostatecznym
teście ~test-A~ plik ~expected.tsv~ jest niedostępny, ukryty przed uczestnikami konkursu
teście ~test-A~ plik ~expected.tsv~ jest niedostępny, ukryty przed uczestnikami konkursu.
**** Zapis rozkładu prawdopodobieństwa
Dla każdego wiersza wejścia podajemy rozkład prawdopodobieństwa dla
słowa w luce w formacie:
#+BEGIN_SRC
wyraz1:prob1 wyraz2:prob2 ... wyrazN:probN :prob0
#+END_SRC
gdzie wyraz1, …, wyrazN to konkretne wyrazy, prob1, …, probN ich prawdopodobieństwa.
Można podać dowolną liczbę wyrazów.
Z kolei prob0 to „resztowe” prawdopodobieństwo przypisane do wszystkich pozostałych wyrazów,
prawdopodobieństwo to pozwala uniknąć problemów związanych ze słowami OOV, trzeba jeszcze tylko dokonać
modyfikacji metryki
**** Metryka LikelihoodHashed
Metryka LikelihoodHashed jest wariantem metryki Likelihood
(wiarygodności) opracowanym z myślą o wyzwaniach czy konkursach
modelowania języka. W tej metryce każde słowo wpada pseudolosowo do
jednego z $2^{10}=1024$ „kubełków”. Numer kubełka jest wyznaczony na
podstawie funkcji haszującej MurmurHash.
Prawdopodobieństwa zwrócone przez ewaluowany model są sumowane w
każdym kubełku, następnie ewaluator zagląda do pliku `expected.tsv` i
uwzględnia prawdopodobieństwo z kubełka, do którego „wpada” oczekiwane
słowo. Oczywiście czasami więcej niż jedno słowo może wpaść do
kubełka, model mógł też „wrzucić” do kubełka tak naprawdę inne słowo
niż oczekiwane (przypadkiem oba słowa wpadają do jednego kubełka).
Tak więc LikelihoodHashed będzie nieco zawyżone w stosunku do Likelihood.
Dlaczego więc taka komplikacja? Otóż LikelihoodHashed nie zakłada
żadnego słownika, znika problem słów OOV — prawdopodobieństwa resztowe prob0
są rozkładane równomiernie między wszystkie 1024 kubełki.
**** Alternatywne metryki
LikelihoodHashed została zaimplementowana w narzędziu ewaluacyjnym
[[https://gitlab.com/filipg/geval|GEval]]. Są tam również dostępne
analogiczne warianty entropii krzyżowej (log loss) i perplexity
(LogLossHashed i PerplexityHashed).

View File

@ -0,0 +1 @@
<mxfile host="app.diagrams.net" modified="2022-03-18T21:08:49.892Z" agent="5.0 (X11)" etag="RzIxxXjLYk9oBS8CnMsQ" version="16.2.2" type="device"><diagram id="E-zPRpFz5prVeiZgI5WF" name="Page-1">7VjbUtswEP0aP4bxJU7IIyQUhkJhYIbCEyPbii0iW0GWE4evr2TJVzmBcm1n4CFoj+S1tGePdhPDmcb5MQXL6JwEEBu2GeSGMzNsezK0+KcANhJwbVcCIUWBhKwauEZPUIGmQjMUwLS1kBGCGVq2QZ8kCfRZCwOUknV72Zzg9luXIIQacO0DrKO/UcAiie7b4xo/gSiMyjdbo4mciUG5WJ0kjUBA1g3IOTKcKSWEyVGcTyEWsSvjIp/7sWW22hiFCXvJA3R1NohuBmByf3NxZV/f0+Or04Gj9sY25YFhwM+vTEJZREKSAHxUo4eUZEkAhVeTW/WaM0KWHLQ4+AAZ2ygyQcYIhyIWYzULc8RuxeN7rrLulDMxnuVNY9MwLiFFMWSQlljC6Oa2adw1jcKR61amcDWuZrvO5iRhasf2kNsyMCIaW+OtoJRk1FerflL7Mb8zT7xgha5wkmWrU3tQ5i2gIWQ7yLCqrOBqgoTvjm74cxRiwNCqvQ+g8jqs1tXU84Fivz8Tdm1yBXCm3vQLBGDtAy1DeCIvxTCL8YHPCA/g4QpShrhozoAH8SVJEUMk4Us8whiJGwsOMArFBBOp0swJkjGMEjitZGxWHPSEXLiDeQPSo6Zm7aGS36Zjr2s1OwqKGkIemW+Pcy/J7rfiPl5xwxcqbviVihtqirsIPETo/y451/k6yfUG2tUCvTXAcwzzA9E2FEkbqOHMxyBNkd+OX1uVWwPXSWg9bI2wuD1hKbEXJ6V6wyVBfCf1RbjfYWXcCbcUlXqq2Ud0HLn2M46k6jRHBXXVsV/P5khjcwESYExdY1/gCxJnCVoA/wH13rSFXtpEAqUQn1MHaY90YhQE8iKGKXoCXuFPkL4UhyyO7R4a7qw3DXamZFdUVSOrXtLqFfvENjD3rLKlfm2GlEvIfJ7CD+Fs/98petbuotdf4Paczl+r4pmtgre13r2+nI31crarh3v2xvmc+jbWhHoME0iBqF/80JvYI1jX6NvrWnmX83S+5F/PVH0rimY7p1KZOTI/3qHwWWb7ZrRGeuEbfWavqff0hj3CTPVerbiPHjNSTgxkYA74Anu4zOtJPgrF//PSDd+V9CTxrWXVzzwh6XWEGLxegiLZ1xR0iPMkO2deBQB/ERacXcheReEBoIsL7gaxQnB7ptsG7QK1qh31XLMa0du/QXRZdXRWJz2sdsviu7Gq943frP41q8PJp7HKzfpXHllR65/KnKM/</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB