05
This commit is contained in:
parent
073069b974
commit
317012455f
@ -96,7 +96,11 @@ $$P(w) = \fraq{\# w + 1}{|C| + |V|}.$$
|
|||||||
|
|
||||||
W modelowaniu języka wygładzanie $+1$ daje zazwyczaj niepoprawne
|
W modelowaniu języka wygładzanie $+1$ daje zazwyczaj niepoprawne
|
||||||
wyniki, dlatego częściej zamiast wartości 1 używa się współczynnika $0
|
wyniki, dlatego częściej zamiast wartości 1 używa się współczynnika $0
|
||||||
< \alpha < 1$. W innych praktycznych zastosowaniach statystyki
|
< \alpha < 1$:
|
||||||
|
|
||||||
|
$$P(w) = \fraq{\# w + \alpha}{|C| + \alpha|V|}.$$
|
||||||
|
|
||||||
|
W innych praktycznych zastosowaniach statystyki
|
||||||
przyjmuje się $\alpha = \frac{1}{2}$, ale w przypadku n-gramowych
|
przyjmuje się $\alpha = \frac{1}{2}$, ale w przypadku n-gramowych
|
||||||
modeli języka i to będzie zbyt duża wartość.
|
modeli języka i to będzie zbyt duża wartość.
|
||||||
|
|
||||||
@ -120,6 +124,178 @@ wyrazów, które pojawiły się określoną liczbę razy w pierwszej połówce k
|
|||||||
|
|
||||||
Ostatecznie możemy też po prostu policzyć perplexity na zbiorze testowym
|
Ostatecznie możemy też po prostu policzyć perplexity na zbiorze testowym
|
||||||
|
|
||||||
|
*** Przykład
|
||||||
|
|
||||||
|
Użyjemy polskiej części z korpusu równoległego Open Subtitles:
|
||||||
|
|
||||||
|
#+BEGIN_SRC
|
||||||
|
wget -O en-pl.txt.zip 'https://opus.nlpl.eu/download.php?f=OpenSubtitles/v2018/moses/en-pl.txt.zip'
|
||||||
|
unzip en-pl.txt.zip
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Usuńmy duplikaty (zachowując kolejność):
|
||||||
|
|
||||||
|
#+BEGIN_SRC
|
||||||
|
nl OpenSubtitles.en-pl.pl | sort -k 2 -u | sort -k 1 | cut -f 2- > opensubtitles.pl.txt
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Korpus zawiera ponad 28 mln słów, zdania są krótkie, jest to język potoczny, czasami wulgarny.
|
||||||
|
|
||||||
|
#+BEGIN_SRC
|
||||||
|
$ wc opensubtitles.pl.txt
|
||||||
|
28154303 178866171 1206735898 opensubtitles.pl.txt
|
||||||
|
$ head -n 10 opensubtitles.pl.txt
|
||||||
|
Lubisz curry, prawda?
|
||||||
|
Nałożę ci więcej.
|
||||||
|
Hey!
|
||||||
|
Smakuje ci?
|
||||||
|
Hey, brzydalu.
|
||||||
|
Spójrz na nią.
|
||||||
|
- Wariatka.
|
||||||
|
- Zadałam ci pytanie!
|
||||||
|
No, tak lepiej!
|
||||||
|
- Wygląda dobrze!
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Podzielimy korpus na dwie części:
|
||||||
|
|
||||||
|
#+BEGIN_SRC
|
||||||
|
head -n 14077151 < opensubtitles.pl.txt > opensubtitlesA.pl.txt
|
||||||
|
tail -n 14077151 < opensubtitles.pl.txt > opensubtitlesB.pl.txt
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
**** Tokenizacja
|
||||||
|
|
||||||
|
Stwórzmy generator, który będzie wczytywał słowa z pliku, dodatkowo:
|
||||||
|
|
||||||
|
- ciągi znaków interpunkcyjnych będziemy traktować jak tokeny,
|
||||||
|
- sprowadzimy wszystkie litery do małych,
|
||||||
|
- dodamy specjalne tokeny na początek i koniec zdania (~<s>~ i ~</s>~).
|
||||||
|
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
|
||||||
|
from itertools import islice
|
||||||
|
import regex as re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def get_words_from_file(file_name):
|
||||||
|
with open(file_name, 'r') as fh:
|
||||||
|
for line in fh:
|
||||||
|
line = line.rstrip()
|
||||||
|
yield '<s>'
|
||||||
|
for m in re.finditer(r'[\p{L}0-9\*]+|\p{P}+', line):
|
||||||
|
yield m.group(0)
|
||||||
|
yield '</s>'
|
||||||
|
|
||||||
|
list(islice(get_words_from_file('opensubtitlesA.pl.txt'), 0, 100))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
['<s>', 'Lubisz', 'curry', ',', 'prawda', '?', '</s>', '<s>', 'Nałożę', 'ci', 'więcej', '.', '</s>', '<s>', 'Hey', '!', '</s>', '<s>', 'Smakuje', 'ci', '?', '</s>', '<s>', 'Hey', ',', 'brzydalu', '.', '</s>', '<s>', 'Spójrz', 'na', 'nią', '.', '</s>', '<s>', '-', 'Wariatka', '.', '</s>', '<s>', '-', 'Zadałam', 'ci', 'pytanie', '!', '</s>', '<s>', 'No', ',', 'tak', 'lepiej', '!', '</s>', '<s>', '-', 'Wygląda', 'dobrze', '!', '</s>', '<s>', '-', 'Tak', 'lepiej', '!', '</s>', '<s>', 'Pasuje', 'jej', '.', '</s>', '<s>', '-', 'Hey', '.', '</s>', '<s>', '-', 'Co', 'do', '...?', '</s>', '<s>', 'Co', 'do', 'cholery', 'robisz', '?', '</s>', '<s>', 'Zejdź', 'mi', 'z', 'oczu', ',', 'zdziro', '.', '</s>', '<s>', 'Przestań', 'dokuczać']
|
||||||
|
:end:
|
||||||
|
|
||||||
|
**** Empiryczne wyniki
|
||||||
|
|
||||||
|
Zobaczmy, ile razy, średnio w drugiej połówce korpusu występują
|
||||||
|
wyrazy, które w pierwszej wystąpiły określoną liczbę razy.
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
|
||||||
|
from collections import Counter
|
||||||
|
|
||||||
|
counterA = Counter(get_words_from_file('opensubtitlesA.pl.txt'))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
:end:
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
|
||||||
|
counterA['taki']
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
42330
|
||||||
|
:end:
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
|
||||||
|
max_r = 10
|
||||||
|
|
||||||
|
buckets = {}
|
||||||
|
for token in counterA:
|
||||||
|
buckets.setdefault(counterA[token], 0)
|
||||||
|
buckets[counterA[token]] += 1
|
||||||
|
|
||||||
|
bucket_counts = {}
|
||||||
|
|
||||||
|
counterB = Counter(get_words_from_file('opensubtitlesB.pl.txt'))
|
||||||
|
|
||||||
|
for token in counterB:
|
||||||
|
bucket_id = counterA[token] if token in counterA else 0
|
||||||
|
if bucket_id <= max_r:
|
||||||
|
bucket_counts.setdefault(bucket_id, 0)
|
||||||
|
bucket_counts[bucket_id] += counterB[token]
|
||||||
|
if bucket_id == 0:
|
||||||
|
buckets.setdefault(0, 0)
|
||||||
|
buckets[0] += 1
|
||||||
|
|
||||||
|
nb_of_types = [buckets[ix] for ix in range(0, max_r+1)]
|
||||||
|
empirical_counts = [bucket_counts[ix] / buckets[ix] for ix in range(0, max_r)]
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
:end:
|
||||||
|
|
||||||
|
Policzmy teraz jakiej liczby wystąpień byśmy oczekiwali gdyby użyć wygładzania +1 bądź +0.01.
|
||||||
|
(Uwaga: zwracamy liczbę wystąpień, a nie względną częstość, stąd przemnażamy przez rozmiar całego korpusu).
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
|
||||||
|
def plus_alpha_smoothing(alpha, m, t, k):
|
||||||
|
return t*(k + alpha)/(t + alpha * m)
|
||||||
|
|
||||||
|
def plus_one_smoothing(m, t, k):
|
||||||
|
return plus_alpha_smoothing(1.0, m, t, k)
|
||||||
|
|
||||||
|
vocabulary_size = len(counterA)
|
||||||
|
corpus_size = counterA.total()
|
||||||
|
|
||||||
|
plus_one_counts = [plus_one_smoothing(vocabulary_size, corpus_size, ix) for ix in range(0, max_r)]
|
||||||
|
|
||||||
|
plus_alpha_counts = [plus_alpha_smoothing(0.01, vocabulary_size, corpus_size, ix) for ix in range(0, max_r)]
|
||||||
|
|
||||||
|
data = list(zip(nb_of_types, empirical_counts, plus_one_counts, plus_alpha_counts))
|
||||||
|
|
||||||
|
vocabulary_size
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
1181065
|
||||||
|
:end:
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
pd.DataFrame(data, columns=["liczba tokenów", "średnia częstość w części B", "estymacje +1", "estymacje +0.01"])
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
liczba tokenów średnia częstość w części B estymacje +1 estymacje +0.01
|
||||||
|
0 494664 1.805294 0.991839 0.009999
|
||||||
|
1 528998 0.591116 1.983678 1.009917
|
||||||
|
2 154689 1.574443 2.975517 2.009835
|
||||||
|
3 81398 2.512285 3.967356 3.009752
|
||||||
|
4 52899 3.502240 4.959196 4.009670
|
||||||
|
5 37917 4.433763 5.951035 5.009588
|
||||||
|
6 28921 5.280834 6.942874 6.009506
|
||||||
|
7 23267 6.209438 7.934713 7.009423
|
||||||
|
8 19014 7.265909 8.926552 8.009341
|
||||||
|
9 15849 8.193135 9.918391 9.009259
|
||||||
|
:end:
|
||||||
|
|
||||||
*** Wygładzanie Gooda-Turinga
|
*** Wygładzanie Gooda-Turinga
|
||||||
|
|
||||||
Inna metoda — wygładzanie Gooda-Turinga — polega na zliczaniu, ile
|
Inna metoda — wygładzanie Gooda-Turinga — polega na zliczaniu, ile
|
||||||
@ -131,6 +307,34 @@ W metodzie Gooda-Turinga używamy następującej estymacji:
|
|||||||
|
|
||||||
$$p(w) = \frac{\# w + 1}{|C|}\frac{N_{r+1}}{N_r}.$$
|
$$p(w) = \frac{\# w + 1}{|C|}\frac{N_{r+1}}{N_r}.$$
|
||||||
|
|
||||||
|
**** Przykład
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
|
||||||
|
good_turing_counts = [(ix+1)*nb_of_types[ix+1]/nb_of_types[ix] for ix in range(0, max_r)]
|
||||||
|
|
||||||
|
data2 = list(zip(nb_of_types, empirical_counts, plus_one_counts, good_turing_counts))
|
||||||
|
|
||||||
|
pd.DataFrame(data2, columns=["liczba tokenów", "średnia częstość w części B", "estymacje +1", "Good-Turing"])
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
liczba tokenów średnia częstość w części B estymacje +1 Good-Turing
|
||||||
|
0 494664 1.805294 0.991839 1.069409
|
||||||
|
1 528998 0.591116 1.983678 0.584838
|
||||||
|
2 154689 1.574443 2.975517 1.578613
|
||||||
|
3 81398 2.512285 3.967356 2.599523
|
||||||
|
4 52899 3.502240 4.959196 3.583905
|
||||||
|
5 37917 4.433763 5.951035 4.576470
|
||||||
|
6 28921 5.280834 6.942874 5.631513
|
||||||
|
7 23267 6.209438 7.934713 6.537671
|
||||||
|
8 19014 7.265909 8.926552 7.501893
|
||||||
|
9 15849 8.193135 9.918391 8.670579
|
||||||
|
:end:
|
||||||
|
|
||||||
|
Wygładzanie metodą Gooda-Turinga, mimo prostoty, daje wyniki zaskakująco zbliżone do rzeczywistych.
|
||||||
|
|
||||||
|
|
||||||
** Wygładzanie dla $n$-gramów
|
** Wygładzanie dla $n$-gramów
|
||||||
|
|
||||||
*** Rzadkość danych
|
*** Rzadkość danych
|
||||||
|
Loading…
Reference in New Issue
Block a user