{ "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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\n", "text/plain": [ "
" ] }, "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 -> nie (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 -> em (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 -> ie (frequency 5906869)\n", " pair 13: n a -> na (frequency 5300380)\n", " pair 14: r o -> ro (frequency 5181363)\n", " pair 15: n a -> na (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 -> ch (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 -> go (frequency 3279997)\n", " pair 29: a r -> ar (frequency 3081492)\n", " pair 30: si ę -> się (frequency 2973681)\n", " ...\n", " pair 49970: brz mieniu -> brzmieniu (frequency 483)\n", " pair 49971: bieżą cych -> bieżących (frequency 483)\n", " pair 49972: biegu nkę -> biegunkę (frequency 483)\n", " pair 49973: ban kowości -> bankowości (frequency 483)\n", " pair 49974: ba ku -> baku (frequency 483)\n", " pair 49975: ba cznie -> bacznie (frequency 483)\n", " pair 49976: Przypad kowo -> Przypadkowo (frequency 483)\n", " pair 49977: MA Ł -> MAŁ (frequency 483)\n", " pair 49978: Lep pera -> Leppera (frequency 483)\n", " pair 49979: Ko za -> Koza (frequency 483)\n", " pair 49980: Jak byś -> Jakbyś (frequency 483)\n", " pair 49981: Geni alne -> Genialne (frequency 483)\n", " pair 49982: Że nada -> Żenada (frequency 482)\n", " pair 49983: ń czykiem -> ńczykiem (frequency 482)\n", " pair 49984: zwie ń -> zwień (frequency 482)\n", " pair 49985: zost ałaś -> zostałaś (frequency 482)\n", " pair 49986: zni szczona -> zniszczona (frequency 482)\n", " pair 49987: ze stawi -> zestawi (frequency 482)\n", " pair 49988: za sób -> zasób (frequency 482)\n", " pair 49989: węd rówkę -> wędrówkę (frequency 482)\n", " pair 49990: wysko czyła -> wyskoczyła (frequency 482)\n", " pair 49991: wyle czenia -> wyleczenia (frequency 482)\n", " pair 49992: wychowaw cze -> wychowawcze (frequency 482)\n", " pair 49993: w t -> wt (frequency 482)\n", " pair 49994: un da -> unda (frequency 482)\n", " pair 49995: udzie lałem -> udzielałem (frequency 482)\n", " pair 49996: tę czy -> tęczy (frequency 482)\n", " pair 49997: tro sce -> trosce (frequency 482)\n", " pair 49998: słusz ności -> słuszności (frequency 482)\n", " pair 49999: su me -> sume (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 }