18 KiB
Modelowanie Języka
6. Biblioteki do statystycznych modeli językowych [ćwiczenia]
KENLM
W praktyce korzysta się z gotowych bibliotek do statystycznych modeli językowych. Najbardziej popularną biblioteką jest KENLM ( https://kheafield.com/papers/avenue/kenlm.pdf ). Repozytorium znajduje się https://github.com/kpu/kenlm a dokumentacja https://kheafield.com/code/kenlm/
Na komputerach wydziałowych nie powinno być problemu ze skompilowaniem biblioteki.
Instalacja
(Zob. też dokumentacja)
sudo apt-get install build-essential libboost-all-dev cmake zlib1g-dev libbz2-dev liblzma-dev
wget -O - https://kheafield.com/code/kenlm.tar.gz | tar xz
mkdir kenlm/build
cd kenlm/build
cmake ..
make -j2
Najprostszy scenariusz użycia
KENLM_BUILD_PATH='/home/pawel/kenlm/build' # ścieżka, w której jest zainstalowany KenLM (zob. dokumentacja - link powyżej)
!wget https://wolnelektury.pl/media/book/txt/lalka-tom-pierwszy.txt
--2024-04-10 12:13:27-- https://wolnelektury.pl/media/book/txt/lalka-tom-pierwszy.txt Resolving wolnelektury.pl (wolnelektury.pl)... 51.83.143.148, 2001:41d0:602:3294:: Connecting to wolnelektury.pl (wolnelektury.pl)|51.83.143.148|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 860304 (840K) [text/plain] Saving to: ‘lalka-tom-pierwszy.txt.1’ lalka-tom-pierwszy. 100%[===================>] 840.14K 3.59MB/s in 0.2s 2024-04-10 12:13:27 (3.59 MB/s) - ‘lalka-tom-pierwszy.txt.1’ saved [860304/860304]
!wget https://wolnelektury.pl/media/book/txt/lalka-tom-drugi.txt
--2024-04-10 12:13:30-- https://wolnelektury.pl/media/book/txt/lalka-tom-drugi.txt Resolving wolnelektury.pl (wolnelektury.pl)... 51.83.143.148, 2001:41d0:602:3294:: Connecting to wolnelektury.pl (wolnelektury.pl)|51.83.143.148|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 949497 (927K) [text/plain] Saving to: ‘lalka-tom-drugi.txt.1’ lalka-tom-drugi.txt 100%[===================>] 927.24K 3.39MB/s in 0.3s 2024-04-10 12:13:30 (3.39 MB/s) - ‘lalka-tom-drugi.txt.1’ saved [949497/949497]
budowa modelu
!$KENLM_BUILD_PATH/bin/lmplz -o 4 < lalka-tom-pierwszy.txt > lalka_tom_pierwszy_lm.arpa
=== 1/5 Counting and sorting n-grams === Reading /home/pawel/moj-2024/lab/lalka-tom-pierwszy.txt ----5---10---15---20---25---30---35---40---45---50---55---60---65---70---75---80---85---90---95--100 **************************************************************************************************** Unigram tokens 122871 types 33265 === 2/5 Calculating and sorting adjusted counts === Chain sizes: 1:399180 2:2261987584 3:4241227008 4:6785963520 Statistics: 1 33265 D1=0.737356 D2=1.15675 D3+=1.59585 2 93948 D1=0.891914 D2=1.20314 D3+=1.44945 3 115490 D1=0.964904 D2=1.40636 D3+=1.66751 4 116433 D1=0.986444 D2=1.50367 D3+=1.9023 Memory estimate for binary LM: type kB probing 7800 assuming -p 1.5 probing 9157 assuming -r models -p 1.5 trie 3902 without quantization trie 2378 assuming -q 8 -b 8 quantization trie 3649 assuming -a 22 array pointer compression trie 2125 assuming -a 22 -q 8 -b 8 array pointer compression and quantization === 3/5 Calculating and sorting initial probabilities === Chain sizes: 1:399180 2:1503168 3:2309800 4:2794392 ----5---10---15---20---25---30---35---40---45---50---55---60---65---70---75---80---85---90---95--100 #################################################################################################### === 4/5 Calculating and writing order-interpolated probabilities === Chain sizes: 1:399180 2:1503168 3:2309800 4:2794392 ----5---10---15---20---25---30---35---40---45---50---55---60---65---70---75---80---85---90---95--100 #################################################################################################### === 5/5 Writing ARPA model === ----5---10---15---20---25---30---35---40---45---50---55---60---65---70---75---80---85---90---95--100 **************************************************************************************************** Name:lmplz VmPeak:13142592 kB VmRSS:7564 kB RSSMax:2623832 kB user:0.28374 sys:1.02734 CPU:1.3111 real:1.25256
plik arpa
Powyższa komenda tworzy model językowy z wygładzaniem i zapisuje go do pliku tekstowego arpa. Parametr -o 4 odpowiada za maksymalną ilość n-gramów w modelu: 4-gramy.
Plik arpa zawiera w sobie prawdopodobieństwa dla poszczególnych n-gramów. W zasadzie są to logarytmy prawdopodbieństw o podstawie 10.
Podejrzyjmy plik arpa:
!head -n 30 lalka_tom_pierwszy_lm.arpa
\data\ ngram 1=33265 ngram 2=93948 ngram 3=115490 ngram 4=116433 \1-grams: -5.0133595 <unk> 0 0 <s> -0.99603957 -1.4302719 </s> 0 -4.7287908 Bolesław -0.049677044 -4.9033437 Prus -0.049677044 -4.9033437 Lalka -0.049677044 -4.9033437 ISBN -0.049677044 -4.9033437 978-83-288-2673-1 -0.049677044 -4.9033437 Tom -0.049677044 -3.0029354 I -0.17544968 -4.9033437 I. -0.049677044 -3.5526814 Jak -0.1410632 -3.8170912 wygląda -0.16308141 -4.608305 firma -0.049677044 -4.33789 J. -0.3295009 -3.9192266 Mincel -0.12910372 -1.624716 i -0.20128249 -4.1086636 S. -0.098223634 -2.6843808 Wokulski -0.19202113 -2.8196363 przez -0.15214005 -4.9033437 szkło -0.049677044 -4.9033437 butelek? -0.049677044 -2.848008 W -0.19964235
Linijka to kolejno: prawdopodobieństwo (log10), n-gram, waga back-off (log10).
Aby spradzić prawdopodobieństwo sekwencji (a także PPL modelu) należy użyć komendy query
test_str=!(head -n 17 lalka-tom-drugi.txt | tail -n 1)
test_str = test_str[0]
test_str
'Sytuacja polityczna jest tak niepewna, że wcale by mnie nie zdziwiło, gdyby około grudnia wybuchła wojna.'
test_str
'Sytuacja polityczna jest tak niepewna, że wcale by mnie nie zdziwiło, gdyby około grudnia wybuchła wojna.'
!echo $test_str | $KENLM_BUILD_PATH/bin/query lalka_tom_pierwszy_lm.arpa 2> /dev/null
Sytuacja=0 1 -6.009399 polityczna=21766 1 -4.9033437 jest=123 1 -2.6640298 tak=231 2 -1.7683144 niepewna,=0 1 -5.1248584 że=122 1 -2.1651394 wcale=5123 1 -4.167491 by=1523 1 -3.55168 mnie=2555 2 -1.6694618 nie=127 2 -1.4439836 zdziwiło,=0 1 -5.2158937 gdyby=814 1 -3.2300434 około=1462 1 -3.7384818 grudnia=0 1 -5.123236 wybuchła=0 1 -5.0133595 wojna.=1285 1 -4.9033437 </s>=2 2 -0.8501559 Total: -61.54222 OOV: 5 Perplexity including OOVs: 4169.948113875898 Perplexity excluding OOVs: 834.2371454470355 OOVs: 5 Tokens: 17
Zgodnie z dokumentacją polecenia query, format wyjściowy to dla każdego słowa:
word=vocab_id ngram_length log10(p(word|context))
A co jeśli trochę zmienimy początek zdania?
test2_str = "Lubię placki i wcale by mnie nie zdziwiło, gdyby około grudnia wybuchła wojna."
!echo $test2_str | $KENLM_BUILD_PATH/bin/query lalka_tom_pierwszy_lm.arpa 2> /dev/null
Lubię=17813 1 -5.899383 placki=0 1 -5.0630364 i=16 1 -1.624716 wcale=5123 2 -3.2397003 by=1523 1 -3.6538217 mnie=2555 2 -1.6694618 nie=127 2 -1.4439836 zdziwiło,=0 1 -5.2158937 gdyby=814 1 -3.2300434 około=1462 1 -3.7384818 grudnia=0 1 -5.123236 wybuchła=0 1 -5.0133595 wojna.=1285 1 -4.9033437 </s>=2 2 -0.8501559 Total: -50.668617 OOV: 4 Perplexity including OOVs: 4160.896818387522 Perplexity excluding OOVs: 1060.0079770155185 OOVs: 4 Tokens: 14
Trochę bardziej zaawansowane użycie
Pierwsza rzecz, która rzuca się w oczy: tokeny zawierają znaki interpunkcyjne. Użyjemy zatem popularnego tokenizera i detokenizera moses z https://github.com/moses-smt/mosesdecoder
https://github.com/moses-smt/mosesdecoder/tree/master/scripts/tokenizer
tokenizacja i lowercasing
TOKENIZER_SCRIPTS='/home/pawel/mosesdecoder/scripts/tokenizer'
!echo $test_str
!echo $test_str | $TOKENIZER_SCRIPTS/tokenizer.perl --language pl
W łatwy sposób można odzyskać tekst źródłowy:
!echo $test_str | $TOKENIZER_SCRIPTS/tokenizer.perl --language pl | $TOKENIZER_SCRIPTS/detokenizer.perl --language pl
W naszym przykładzie stworzymy model językowy lowercase. Można osobno wytrenować też truecaser (osobny model do przywracania wielkości liter), jeżeli jest taka potrzeba.
!echo $test_str | $TOKENIZER_SCRIPTS/tokenizer.perl --language pl | $TOKENIZER_SCRIPTS/lowercase.perl
!cat lalka-tom-pierwszy.txt | $TOKENIZER_SCRIPTS/tokenizer.perl --language pl | $TOKENIZER_SCRIPTS/lowercase.perl > lalka-tom-pierwszy-tokenized-lowercased.txt
!cat lalka-tom-drugi.txt | $TOKENIZER_SCRIPTS/tokenizer.perl --language pl | $TOKENIZER_SCRIPTS/lowercase.perl > lalka-tom-drugi-tokenized-lowercased.txt
!$KENLM_BUILD_PATH/bin/lmplz -o 4 --prune 1 1 1 1 < lalka-tom-pierwszy-tokenized-lowercased.txt > lalka_tom_pierwszy_lm.arpa
test_str=!(head -n 17 lalka-tom-drugi-tokenized-lowercased.txt | tail -n 1)
test_str=test_str[0]
test_str
model binarny
Konwertując model do postaci binarnej, inferencja będzie szybsza
!$KENLM_BUILD_PATH/bin/build_binary lalka_tom_pierwszy_lm.arpa lalka_tom_pierwszy_lm.binary
!echo $test_str | $KENLM_BUILD_PATH/bin/query lalka_tom_pierwszy_lm.binary
sprawdzanie dokumentacji
Najłatwiej sprawdzić wywołując bezpośrednio komendę
!$KENLM_BUILD_PATH/bin/lmplz
wrapper pythonowy
!pip install https://github.com/kpu/kenlm/archive/master.zip
import kenlm
model = kenlm.Model('lalka_tom_pierwszy_lm.binary')
print(model.score(test_str, bos = True, eos = True))
for i in model.full_scores(test_str):
print(i)
Zadanie
Stworzyć model językowy za pomocą gotowej biblioteki (KenLM lub inna)
Rozwiązanie proszę umieścić na https://gonito.csi.wmi.amu.edu.pl/challenge/challenging-america-word-gap-prediction
Warunki zaliczenia:
- wynik widoczny na platformie zarówno dla dev i dla test
- wynik dla dev i test lepszy (niższy) niż 1024.00 (liczone przy pomocy geval)
- deadline: 24 kwietnia 2024
- commitując rozwiązanie proszę również umieścić rozwiązanie w pliku /run.py (czyli na szczycie katalogu). Można przekonwertować jupyter do pliku python przez File → Download as → Python. Rozwiązanie nie musi być w pythonie, może być w innym języku.
- zadania wykonujemy samodzielnie
- w nazwie commita podaj nr indeksu
- w tagach podaj kenlm!
- uwaga na specjalne znaki \\n w pliku 'in.tsv' oraz pierwsze kolumny pliku in.tsv (które należy usunąć)
Punktacja:
- podstawa: 40 punktów
- dodatkowo 50 (czyli 40 + 50 = 90) punktów z najlepszy wynik
- dodatkowo 20 (czyli 40 + 20 = 60) punktów za znalezienie się w pierwszej połowie, ale poza najlepszym wynikiem