aitech-eks-pub/wyk/12_bpe.ipynb

835 lines
29 KiB
Plaintext
Raw Normal View History

2021-06-09 12:43:29 +02:00
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Podział na jednostki podwyrazowe\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Słownik nie może być za duży…\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Jeśli używamy wyuczalnych zanurzeń słów (embeddingów), wówczas musimy\n",
"je dopisać do listy parametrów całego modelu — jest to $|V|n$ wag,\n",
"gdzie $n$ to rozmiar embeddingów; w wypadku uczenia dodatkowo musimy\n",
"jeszcze pamiętać związane z embeddingami gradienty. Pamięć RAM karty\n",
"graficznej jest rzecz jasna ograniczona, słownik więc nie może być\n",
"dowolnie duży. Dla danego modelu karty graficznej dość łatwo ustalić\n",
"maksymalny rozmiar słownika — jest „twarde” ograniczenie, które musimy\n",
"spełnić.\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Czy rzeczywiście słownik może być taki duży?\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ile jest różnych form fleksyjnych w języku polskim? Zobaczmy w słowniku PoliMorf…\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"a\n",
"aa\n",
"AA\n",
"Aachen\n",
"Aalborg\n",
"Aalborgiem\n",
"Aalborgowi\n",
"Aalborgu\n",
"AAP\n",
"Aar\n",
"Aarem\n",
"Aarowi\n",
"Aaru\n",
"Aarze\n",
"Aara\n",
"Aarą\n",
"Aarę\n",
"Aaro\n",
"Aary\n",
"Aarze\n",
"uniq: błąd zapisu: Przerwany potok\n"
]
}
],
"source": [
"! wget -q 'http://zil.ipipan.waw.pl/PoliMorf?action=AttachFile&do=get&target=PoliMorf-0.6.7.tab.gz' -O - | zcat | cut -f 1 | uniq | head -n 20"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3844535\r\n"
]
}
],
"source": [
"! wget -q 'http://zil.ipipan.waw.pl/PoliMorf?action=AttachFile&do=get&target=PoliMorf-0.6.7.tab.gz' -O - | zcat | cut -f 1 | sort -u | wc -l"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Pytanie** W którym języku europejskim wyrazów będzie jeszcze więcej niż języku polskim?\n",
"\n",
"Tak naprawdę form jest jeszcze więcej, oczywiście PoliMorf nie wyczerpuje zbioru…\n",
"\n",
"**Pytanie** Podaj przykłady „oczywistych” wyrazów, których w PoliMorfie. Jak w sposób systematyczny szukać takich wyrazów?\n",
"\n",
"Z drugiej strony, w PoliMorfie jest dużo dziwnych, „sztucznych” wyrazów.\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"niemałomieszczańskiej\r\n",
"kwatereczki\r\n",
"słowniejszej\r\n",
"oranżysta\r\n",
"myrmekofagów\r\n",
"hipokratesowego\r\n",
"rozdziałująca\r\n",
"wielosettysięczne\r\n",
"redempcyjno\r\n",
"łącznikowce\r\n",
"niesłowacyzowana\r\n",
"sosnowieckościach\r\n",
"niewschodoznawczy\r\n",
"niekłosokształtnego\r\n",
"niegenialności\r\n",
"Gierowskiego\r\n",
"nieumierzwiających\r\n",
"bezzakłóceniowości\r\n",
"niedziurkowatościach\r\n",
"Krzaklewskich\r\n"
]
}
],
"source": [
"! wget -q 'http://zil.ipipan.waw.pl/PoliMorf?action=AttachFile&do=get&target=PoliMorf-0.6.7.tab.gz' -O - | zcat | cut -f 1 | shuf -n 20"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Inaczej, zobaczmy, ile różnych wyrazów jest w jakimś rzeczywistym zbiorze tekstów, rozpatrzmy\n",
"teksty zebrane na potrzeby identyfikacji płci autora tekstu:\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"# Out[7]:"
]
}
],
"source": [
"! git clone --single-branch --depth 1 git://gonito.net/petite-difference-challenge2"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"! xzcat petite-difference-challenge2/train/in.tsv.xz | perl -C -ne 'print \"$&\\n\" while/\\p{L}+/g;' | sort -u > vocab.txt"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ˆ\r\n",
"ˇ\r\n",
"゚\r\n",
"a\r\n",
"A\r\n",
"á\r\n",
"Á\r\n",
"à\r\n",
"À\r\n",
"ă\r\n",
"Ă\r\n",
"â\r\n",
"Â\r\n",
"å\r\n",
"Å\r\n",
"ä\r\n",
"Ä\r\n",
"Ã\r\n",
"ā\r\n",
"aa\r\n",
"aA\r\n",
"Aa\r\n",
"AA\r\n",
"aĂ\r\n",
"AĂ\r\n",
"aâ\r\n",
"aÂ\r\n",
"Aâ\r\n",
"aÅ\r\n",
"aÄ\r\n",
"ª\r\n",
"aaa\r\n",
"aAa\r\n",
"Aaa\r\n",
"AaA\r\n",
"AAa\r\n",
"AAA\r\n",
"aaaa\r\n",
"aAaa\r\n",
"Aaaa\r\n",
"AaAa\r\n",
"AAaa\r\n",
"AAAa\r\n",
"AAAA\r\n",
"aaaaa\r\n",
"Aaaaa\r\n",
"AaaaA\r\n",
"AAaaa\r\n",
"AAAAA\r\n",
"aaaaaa\r\n"
]
}
],
"source": [
"! head -n 50 vocab.txt"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2974556 vocab.txt\r\n"
]
}
],
"source": [
"! wc -l vocab.txt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Co gorsza, nawet jak weźmiemy cały taki słownik bez ograniczeń i tak\n",
"nie pokryje on sporej części tekstów przetwarzanych w czasie inferencji.\n",
"Zobaczmy, ilu wyrazów ze zbioru deweloperskiego nie będzie w słowniku.\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"81380\r\n"
]
}
],
"source": [
"! cat petite-difference-challenge2/dev-0/in.tsv | perl -C -ne 'print \"$&\\n\" while/\\p{L}+/g;' | sort -u | comm vocab.txt - -13 | wc -l"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Takie wyrazy nazywamy wyrazami **OOV** (*out-of-vocabulary*).\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Obcięcie słownika\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Najprostszy sposób ograniczenia słownika to po prostu obcięcie do $N$ najczęstszych słów.\n",
"\n",
"Spróbujmy zastosować do korpusu „płci”:\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"# Out[8]:"
]
}
],
"source": [
"! xzcat petite-difference-challenge2/train/in.tsv.xz | perl -C -ne 'print \"$&\\n\" while/\\p{L}+/g;' | sort | uniq -c | sort -k 1rn | head -n 50000 | sort -k 2 > vocab50000.txt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Daje to lepszy efekt niż można się spodziewać. Odrzucamy w ten sposób\n",
"tylko bardzo rzadkie słowa (albo takie, które wystąpiły tylko raz w\n",
"korpusie — tzw. *hapax legomena*), choć tych słów jest bardzo dużo.\n",
"\n",
"**Zagadka**: 50000 najczęstszych słów (1,9% **typów**) pokrywa jaki odsetek **wystąpień**?\n",
"\n",
"Rozkład normalny w języku nie jest… normalny — nie spotkamy się z nim\n",
"badając języki. W tekstach dominują „skrzywione” rozkłady z długimi,\n",
"„chudymi” ogonami.\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"! xzcat petite-difference-challenge2/train/in.tsv.xz | perl -C -ne 'print \"$&\\n\" while/\\p{L}+/g;' | sort | uniq -c | sort -k 1rn | cut -f 1 > freqs.txt"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'word-distribution.png'"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"%matplotlib inline\n",
"import matplotlib.pyplot as plt\n",
"import re\n",
"from math import log\n",
"\n",
"freqs = []\n",
"\n",
"with open('freqs.txt', 'r') as fh:\n",
" for line in fh:\n",
" m = re.match(r'\\s*(\\d+)', line)\n",
" if m:\n",
" freqs.append(int(m.group(1)))\n",
"\n",
"plt.plot(range(len(freqs)), freqs)\n",
"fname = 'word-distribution.png'\n",
"plt.savefig(fname)\n",
"fname"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[[file:# Out[25]:\n",
"\n",
" 'word-distribution.png'\n",
"\n",
"![img](./obipy-resources/c0TrCn.png)]]\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Lematyzacja\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Lematyzacja wydaje się dobrym pomysłem, zwłaszcza dla języków dla bogatej fleksji:\n",
"\n",
"- znacznie redukujemy słownik,\n",
"- formy fleksyjne tego samego wyrazu są traktowane tak samo (co wydaje się słuszne).\n",
"\n",
"W praktyce współcześnie **nie** stosuje się lematyzacji (w połączeniu z\n",
"metodami opartymi na sieciach neuronowych):\n",
"\n",
"- lematyzacja wymaga wiedzy językowej (reguł lub słownika),\n",
" wytworzenie takiej wiedzy może być kosztowne, obecnie preferowane\n",
" są metody niezależne od języka;\n",
"- tracimy pewną informację niesioną przez formę fleksyjną (co w szczególnych\n",
" przypadkach może być niefortunne, np. *aspiracja* i *aspiracje*);\n",
"- lematyzacja nie jest trywialnym problemem ze względu na niejednoznaczności\n",
" (*Lekarzu, lecz się sam*);\n",
"- niektóre niejednoznaczności są seryjne, wybór lematu może być arbitralny,\n",
" np. czy *posiadanie*, *gotowanie*, *skakanie* to rzeczowniki czy czasowniki?\n",
" a *urządzenie*, *mieszkanie*?\n",
"- zazwyczaj sieci neuronowe (czy nawet prostsze modele typu Word2vec)\n",
" są w stanie nauczyć się rekonstruowania zależności między formami fleksyjnymi\n",
" (i więcej: błędnych form, błędów ortograficznych, form archaicznych itd.)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Zejście na poziom znaków\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Skoro słownik wyrazów jest zbyt duży, to może zejść na poziom znaków?\n",
"\n",
"- pojedynczy znak alfabetu wprawdzie nic nie znaczy (co znaczy *h*?)\n",
"\n",
"- … ale rozmiar wejścia przy kodowaniu gorącą jedynką\n",
" dramatycznie się zmniejsza\n",
"\n",
"- może działać, jeśli dodać wielowarstwową sieć\n",
" neuronową\n",
"\n",
"- … ale może być bardzo kosztowne obliczeniowo\n",
"\n",
"A może coś pośredniego między znakami a wyrazami?\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### BPE\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ani znaki, ani wyrazy — coś pomiędzy: jednostki podwyrazowe (*subword\n",
"units*). Moglibyśmy np. dzielić wyraz *superkomputera* na dwie\n",
"jednostki *super/+/komputera*, a może nawet trzy: *super/+/komputer/+/a*?\n",
"\n",
"Najpopularniejszy algorytm podziału na jednostki podwyrazowe to BPE\n",
"(*byte-pair encoding*), zainspirowany algorytmami kompresji danych.\n",
"Lista jednostek jest automatycznie indukowana na podstawie tekstu (nie\n",
"potrzeba żadnej wiedzy o języku!). Ich liczba musi być natomiast z góry\n",
"określona.\n",
"\n",
"W kroku początkowym zaznaczamy końce wyrazów (tokenów), robimy to po\n",
"to, żeby jednostki podwyrazowe nie przekraczały granic wyrazów.\n",
"\n",
"Następnie wykonujemy tyle kroków iteracji, ile wynosi rozmiar zadanego\n",
"słownika. W każdym kroku szukamy najczęstszego bigramu, od tego\n",
"momentu traktujemy go jako całostkę (wkładamy go do „pudełka”).\n",
"\n",
"![img](./bpe.png)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Implementacja w Pythonie\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['e$', 'to', 'to$', 'be$', 't$', 'th', 'or', 'or$', 'no', 'not$']"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from collections import Counter\n",
"\n",
"def replace_bigram(l, b, r):\n",
" i = 0\n",
" while i < len(l) - 1:\n",
" if (l[i], l[i+1]) == b:\n",
" l[i:i+2] = [r]\n",
" i += 1\n",
" return l\n",
"\n",
"def learn_bpe_vocab(d, max_vocab_size):\n",
" d = list(d.replace(' ', '$') + '$')\n",
"\n",
" vocab = []\n",
"\n",
" for ix in range(0, max_vocab_size):\n",
" bigrams = [(d[i], d[i+1]) for i in range(0, len(d) - 1) if d[i][-1] != '$']\n",
" selected_bigram = Counter(bigrams).most_common(1)[0][0]\n",
"\n",
" new_subword = selected_bigram[0] + selected_bigram[1]\n",
" d = replace_bigram(d, selected_bigram, new_subword)\n",
"\n",
" vocab.append(new_subword)\n",
"\n",
" return vocab\n",
"\n",
"vocab1 = learn_bpe_vocab('to be or not to be that is the question', 10)\n",
"vocab1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Słownik jednostek podwyrazowych możemy zastosować do dowolnego tekstu, np. do tekstu,\n",
"na którym słownik był wyuczony:\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'to$ b e$ or$ no t$ to$ b e$ th a t$ i s $ th e$ q u e s t i o n $'"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def apply_bpe_vocab(vocab, d):\n",
" d = list(d.replace(' ', '$') + '$')\n",
" vocab_set = set(vocab)\n",
"\n",
" ix = 0\n",
" while ix < len(d) - 1:\n",
" bigram = d[ix] + d[ix+1]\n",
" if bigram in vocab_set:\n",
" d[ix:ix+2] = [bigram]\n",
" else:\n",
" ix += 1\n",
"\n",
" return d\n",
"\n",
"' '.join(apply_bpe_vocab(vocab1, 'to be or not to be that is the question'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Zauważmy, że oprócz jednostek podwyrazowych zostały izolowane litery,\n",
"zazwyczaj dodajemy je do słownika. (I zazwyczaj, słownik jest trochę\n",
"większy niż wartość podana jako parametr przy uczeniu BPE — jest\n",
"większy o znaki i specjalne tokeny typu `UNK`, `BOS`, `EOS`, `PAD`.)\n",
"\n",
"**Pytanie**: Jaki problem może pojawić przy zastosowaniu BPE dla tekstu,\n",
"gdzie pojawiają się chińskie znaki? Jak można sobie z nim poradzić?\n",
"\n",
"Słownik jednostek podwyrazowych można stosować dla dowolnego tekstu:\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'to m $ w i l l $ b e$ th e$ b e s t$'"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"' '.join(apply_bpe_vocab(vocab1, 'tom will be the best'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Jak można zauważyć algorytm BPE daje dwa rodzaje jednostek podwyrazowych:\n",
"\n",
"- jednostki, które mogą doklejane na początku wyrazu;\n",
"- jednostki, które stanowią koniec wyrazu, w szczególności są całym wyrazem.\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Gotowa implementacja\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Po raz pierwszy BPE użyto do neuronowego tłumaczenia maszynowego.\n",
"Użyjmy modułu autorstwa Rica Sennricha ([https://github.com/rsennrich/subword-nmt](https://github.com/rsennrich/subword-nmt)).\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"! pip install subword-nmt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Wyindukujmy słownik dla zbioru uczącego zadania identyfikacji płci\n",
"autora tekstu:\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"! xzcat petite-difference-challenge2/train/in.tsv.xz | perl -C -ne 'print \"$&\\n\" while/\\p{L}+/g;' | python -m subword_nmt.learn_bpe -s 50000 -v > bpe_vocab.txt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Procedura trwa kilka minut, trzeba uzbroić się w cierpliwość (ale wypisywanie bigramów przyspieszy!).\n",
"\n",
" pair 0: n i -> ni (frequency 17625075)\n",
" pair 1: i e -> ie (frequency 11471590)\n",
" pair 2: c z -> cz (frequency 9143490)\n",
" pair 3: ni e</w> -> nie</w> (frequency 7901783)\n",
" pair 4: p o -> po (frequency 7790826)\n",
" pair 5: r z -> rz (frequency 7542046)\n",
" pair 6: s t -> st (frequency 7269069)\n",
" pair 7: e m</w> -> em</w> (frequency 7207280)\n",
" pair 8: d z -> dz (frequency 6860931)\n",
" pair 9: s z -> sz (frequency 6609907)\n",
" pair 10: r a -> ra (frequency 6601618)\n",
" pair 11: o w -> ow (frequency 6395963)\n",
" pair 12: i e</w> -> ie</w> (frequency 5906869)\n",
" pair 13: n a -> na (frequency 5300380)\n",
" pair 14: r o -> ro (frequency 5181363)\n",
" pair 15: n a</w> -> na</w> (frequency 5125807)\n",
" pair 16: a ł -> ał (frequency 4786696)\n",
" pair 17: j e -> je (frequency 4599579)\n",
" pair 18: s i -> si (frequency 4300984)\n",
" pair 19: a l -> al (frequency 4276823)\n",
" pair 20: t e -> te (frequency 4033344)\n",
" pair 21: w i -> wi (frequency 3939063)\n",
" pair 22: c h</w> -> ch</w> (frequency 3919410)\n",
" pair 23: c h -> ch (frequency 3661410)\n",
" pair 24: k o -> ko (frequency 3629840)\n",
" pair 25: z a -> za (frequency 3625424)\n",
" pair 26: t a -> ta (frequency 3570094)\n",
" pair 27: p rz -> prz (frequency 3494551)\n",
" pair 28: g o</w> -> go</w> (frequency 3279997)\n",
" pair 29: a r -> ar (frequency 3081492)\n",
" pair 30: si ę</w> -> się</w> (frequency 2973681)\n",
" ...\n",
" pair 49970: brz mieniu</w> -> brzmieniu</w> (frequency 483)\n",
" pair 49971: bieżą cych</w> -> bieżących</w> (frequency 483)\n",
" pair 49972: biegu nkę</w> -> biegunkę</w> (frequency 483)\n",
" pair 49973: ban kowości</w> -> bankowości</w> (frequency 483)\n",
" pair 49974: ba ku</w> -> baku</w> (frequency 483)\n",
" pair 49975: ba cznie</w> -> bacznie</w> (frequency 483)\n",
" pair 49976: Przypad kowo</w> -> Przypadkowo</w> (frequency 483)\n",
" pair 49977: MA Ł -> MAŁ (frequency 483)\n",
" pair 49978: Lep pera</w> -> Leppera</w> (frequency 483)\n",
" pair 49979: Ko za -> Koza (frequency 483)\n",
" pair 49980: Jak byś</w> -> Jakbyś</w> (frequency 483)\n",
" pair 49981: Geni alne</w> -> Genialne</w> (frequency 483)\n",
" pair 49982: Że nada</w> -> Żenada</w> (frequency 482)\n",
" pair 49983: ń czykiem</w> -> ńczykiem</w> (frequency 482)\n",
" pair 49984: zwie ń -> zwień (frequency 482)\n",
" pair 49985: zost ałaś</w> -> zostałaś</w> (frequency 482)\n",
" pair 49986: zni szczona</w> -> zniszczona</w> (frequency 482)\n",
" pair 49987: ze stawi -> zestawi (frequency 482)\n",
" pair 49988: za sób</w> -> zasób</w> (frequency 482)\n",
" pair 49989: węd rówkę</w> -> wędrówkę</w> (frequency 482)\n",
" pair 49990: wysko czyła</w> -> wyskoczyła</w> (frequency 482)\n",
" pair 49991: wyle czenia</w> -> wyleczenia</w> (frequency 482)\n",
" pair 49992: wychowaw cze</w> -> wychowawcze</w> (frequency 482)\n",
" pair 49993: w t -> wt (frequency 482)\n",
" pair 49994: un da -> unda (frequency 482)\n",
" pair 49995: udzie lałem</w> -> udzielałem</w> (frequency 482)\n",
" pair 49996: tę czy</w> -> tęczy</w> (frequency 482)\n",
" pair 49997: tro sce</w> -> trosce</w> (frequency 482)\n",
" pair 49998: słusz ności</w> -> słuszności</w> (frequency 482)\n",
" pair 49999: su me</w> -> sume</w> (frequency 482\n",
"\n",
"Zastosujmy teraz wyindukowany słownik BPE dla jakiegoś rzeczywistego tekstu.\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Cier@@ piałem na straszne la@@ gi kilkanaście sekund lub dłużej czarnego ekranu przy próbie przełą@@ czenia się uruchomienia prawie każdej aplikacji Dodatkowo telefon mi się wyłą@@ czał czasem bez powodu sam z siebie albo rese@@ tował Ostatnio nawet przeglądarka zaczęła się często zawie@@ szać i Android proponował wymu@@ szone zamknięcie Do tego te problemy z połączeniem do komputera przez USB "
]
}
],
"source": [
"! echo 'Cierpiałem na straszne lagi kilkanaście sekund lub dłużej czarnego ekranu przy próbie przełączenia się / uruchomienia prawie każdej aplikacji. Dodatkowo telefon mi się wyłączał czasem bez powodu sam z siebie, albo resetował. Ostatnio nawet przeglądarka zaczęła się często zawieszać i Android proponował wymuszone zamknięcie. Do tego te problemy z połączeniem do komputera przez USB.' | perl -C -ne 'print \"$& \" while/\\p{L}+/g;' | python -m subword_nmt.apply_bpe -c bpe_vocab.txt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ta konkretna implementacja zaznacza za pomocą sekwencji ~@@ ~ koniec jednostki podwyrazowej.\n",
"\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.2"
},
"org": null
},
"nbformat": 4,
"nbformat_minor": 1
}