sobota, 27 kwietnia, 2024

Deep Data

Machine Learning dla Twoich danych

Tutorial

Przygotowanie polskiego modelu word2vec z wykorzystaniem korpusu OpenSubtitles

Jednym z częstych zagadnień związanych z Machine Learning jest budowa tzw. word embedding, czyli sposobu reprezentacji wyrazów w postaci liczbowej. Technika ta pojawiła się na początku lat 2000 za sprawą serii prac Yoshuy Bengio, jednak prawdziwy jej rozkwit nastąpił w 2013 roku za sprawą publikacji prac zespołu google pod przewodnictwem Tomasa Mikolova. Zespół opracował a następnie udoskonalił metodę zapisu znaczenia słów w postaci wektorów w wielowymiarowej przestrzeni. Typowo stosowane są przestrzenie 100 i 300 wymiarowe. Zapis ten określony został mianem word2vec.  Do jego stworzenia stosuje się dwie metody Continuous Bag of Words (CBoW) oraz Continuous skip-gram. W pierwszej z nich model przewiduje aktualnie uczone słowo na podstawie słów otaczających bez uwzględnienia kolejności (bag of words). W drugiej model wykorzystuje aktualne słowo do prognozowania słów otaczających, przy czym słowom bliższym przyporządkowana jest wyższa waga. Według autorów CBOW jest szybszy ale algorytm skip-gram osiąga lepsze rezultaty dla rzadszych słów.


Reprezentacja wektorowa słów word2vec posiada  bardzo ciekawą cechę, mianowicie pozwala ona przeprowadzać na słowach operacje arytmetyczne. Przykładowo:

brat - mężczyzna + kobieta = siostra

W powyższym wyrażeniu w słowie brat odjęty zostanie  aspekt męski a dodany kobiecy co powoduje że w wyniku tej operacji otrzymujemy słowo siostra.

Dla języka polskiego nie istnieje jeszcze zbyt wiele gotowych reprezentacji wektorowych word2vec. Głównym problemem jest niedostatek korpusów językowych. Innym zagadnieniem jest obfitość wyrazów w naszym języku. Ze względu na bogatą fleksję ilość słów jest znacznie większa niż w językach posiadających ubogą odmianę co ciągnie za sobą wymóg zbierania znacznie większych korpusów aby każde słowo miało odpowiednio liczną reprezentację. Alternatywą może być tu stosowanie lematyzacji ale nie jest to zagadnienie proste.  Odsyłam tu do stron http://nkjp.pl/  i http://clip.ipipan.waw.pl/NationalCorpusOfPolish gdzie znajdziemy różne zasoby językowe w tym Narodowy Korpus Języka Polskiego.

Przykładowa reprezentacja wektorowa  dla języka Polskiego jaką udało mi się znaleźć to Kędzia, Paweł; Czachor, Gabriela; Piasecki, Maciej; et al., 2016, Vector representations of polish words (Word2Vec method), CLARIN-PL digital repository, http://hdl.handle.net/11321/327. Model wykorzystuje metodę skip gram do stworzenia wektorów w przestrzeni 100 wymiarowej. Trenowany był na Korpusie Słowosieci wersja 10, zawierającym ponad 4 miliardy tokenów, na który składa się między innymi całość KIPI, NKJP, wersja Wiki, Korpus Rzeczpospolitej i wiele tekstów z internetu. Dane źródłowe były obrabiane, między innymi zostały zwinięte nazwy własne i wielowyrazowe, oraz poddane segmentacji, lematyzacji i ujednoznacznianiu morfosyntaktycznemu, za pomocą tagera WCRFT2 (korzystającego z analizatora morf. Morfeusz 2.0). W samym modelu znajdują się słowa zapisane w następujący sposób:  lemmat::pos np. pszczoła::noun.
Mimo iż zastosowanie lematyzacji rozwiązuje poniekąd problem ogromnego zasobu słów w naszym języku , to jednak tak intensywna obróbka powoduje że model ten stosunkowo trudno wykorzystać wprost w systemach zbudowanych w oparciu  deep learning. Dane źródłowe należałoby poddawać takiej samej obróbce, jak te które posłużyły do zbudowania modelu co wobec braku podania szczegółowego algorytmu oraz narzędzi stawia ten proces pod znakiem zapytania.

Kolejna grupa reprezentacji word2vec dla języka polskiego to  modele oparte o korpusy zbudowane na podstawie Wikipedii. Jeden z przykładowych znajduje się na stronie: https://github.com/Kyubyong/wordvectors. Wadą tych reprezentacji jest stosunkowo niewielki korpus, do tego zbudowany wyłącznie w oparciu o teksty encyklopedyczne. Implikuje to brak lub nieliczność bardzo wielu słów, w szczególności różnych form czasowników, często spotykanych w mowie. Np. nie uświadczymy tam słów: „zawieź”, „zaświeć”, „pojedź” itp., które mogą być potrzebne np. podczas budowania interfejsów językowych typu człowiek-maszyna.

Z powodu powyższych ograniczeń postanowiłem zbudować własny model word2vec dla języka polskiego.  Ponieważ celem moich dalszych prac było stworzenie interfejsu polskiego chat-bota chciałem aby zawierał on jak najwięcej słów pojawiających się w dialogach. Rozpocząłem więc od poszukiwania korpusu zwierającego takie właśnie słownictwo. W ich efekcie natrafiłem na stronę projektu Open Parallel Corpus, w szczególności korpusu OpenSubtitles2016, który zawiera poddane tokenizacji napisy z serwisu OpenSubttles.org. Jest on dość obszerny, zawiera około 775 milionów słów w ok 143 milionach zdań. Cały kod wykorzystany do budowy modelu znajduje się repozytorium: https://github.com/emsi/wordvectors/

Pierwszym etapem tworzenia modelu jest budowa pliku tekstowego zawierającego oczyszczony korpus. Służy do tego notatnik Build OpenSubtitles Corpus.ipynb. Pobiera on dane źródłowe, następnie wypakowuje pliki xml z napisami. W trakcie analiz zauważyłem, że część z nich zawiera teksty niepoprawnie zakodowane, dla tego są one usuwane (stanowią one mniej niż 0.07% wszystkich plików).  Następnie z plików xml odczytywane są same teksty napisów i zapisywanie do pliku z korpusem tak, ze każde zdanie stanowi oddzielną linie. W korpusie OpenSubtitles pozostawiłem też znaki interpunkcyjne końca zdania (tam gdzie one występują), to jest kropkę, wykrzyknik i znak zapytania które stanowią oddzielne tokeny (zrezygnowałem z ich kodowania jako <PERIOD>, <EXCLAMATION> itp.), oraz apostrof i myślnik,  jeśli stanowią one część wyrazu.

Drugim etapem tworzenia modelu jest jego trenowanie. Służy do tego notatnik Make WordVectors.ipynb, który został tak przygotowany aby możliwe było wytrenowanie modelu nawet na komputerze z niewielką ilością pamięci RAM (cały korpus po wczytaniu do formatu wymaganego przez pakiet gensim jako lista list zajmowałby ponad 64GB). Operacja trenowania modelu jest dość czasochłonna, na komputerze z 12 rdzeniami trwa ponad 3 godziny przy standardowych ustawieniach.  W wyniku działania notatnika w folderze data zostaną zapisane pliki z modelem:

data/w2v-773752559-1000000-300-5-5-OpenSubtitles2016.bin
data/w2v-773752559-1000000-300-5-5-OpenSubtitles2016.bin.syn1neg.npy
data/w2v-773752559-1000000-300-5-5-OpenSubtitles2016.bin.wv.syn0.npy

Tak utworzony model można wykorzystać do dalszej pracy. Przykłady wykorzystania modelu zostały pokazane w notatniku: Example.ipynb. Oprócz klasycznych przykładów:

model.most_similar_cosmul(positive=['lepszy', 'duży'], negative=['dobry'])
[('większy', 1.0121806859970093),
 ('mniejszy', 0.9419892430305481),
 ('ogromny', 0.8567739725112915),
 ('większe', 0.8496302962303162),
 ('wiekszy', 0.8493665456771851),
 ('ładniejszy', 0.8317573070526123),
 ('wyższy', 0.8315264582633972),
 ('olbrzymi', 0.8272036910057068),
 ('niewielki', 0.8253382444381714),
 ('cięższy', 0.8177832365036011)]

model.most_similar(positive=['kobieta', 'król'], negative=['mężczyzna'])
[('królowa', 0.7126762866973877),
 ('księżniczka', 0.5617338418960571),
 ('elżbieta', 0.5241243839263916),
 ('andegaweńska', 0.5153706073760986),
 ('królem', 0.4992064833641052),
 ('aelle', 0.49542781710624695),
 ('euphemia', 0.49308931827545166),
 ('nierządnica', 0.4864857792854309),
 ('dama', 0.4855978488922119),
 ('cesarzowa', 0.48290133476257324)]

Znajdziemy tam również mnie oczywisty przypadek odejmowania wyrazów:

model.most_similar(positive=['żonaty'], negative=['kobieta'])
[('rozwiedziony', 0.34714269638061523),
 ('zaręczony', 0.33659571409225464),
 ('komunistą', 0.31170615553855896),
 ('adoptowany', 0.3025285601615906),
 ('obrzezany', 0.3008688688278198),
 ('gejem', 0.3006894588470459),
 ('zajęty', 0.299405038356781),
 ('hazardzistą', 0.29934167861938477),
 ('zonaty', 0.29853737354278564),
 ('aktorem', 0.2903991937637329)]

Ciekawa może być też wizualizacja rzutowania wektorów przykładowych słów wykonana za pomocą algorytmu t-SNE. Ukazuje ona jak grupują się blisko siebie słowa o podobnym znaczeniu (lub takie które występują w podobnym kontekście).

W artykule opisano w jaki sposób można wygenerować własny model word2vec na bazie korpusu OpenSubtitles. Jeśli nie chcesz generować korpusu samodzielnie, zapraszam do kontaktu poprzez poniższy formularz:

Mariusz Wołoszyn

Ponad 20 lat pracy zawodowej w dziedzinie IT. Od bezpieczeństwa poprzez zarządzanie IT po architekturę rozwiązań cloud oraz budowę systemów Machine Learning. Od kilku lat głęboko zainteresowany Deep Learning :) Wcześniej Reinforcement Learning i sieciami neuronowymi oraz programowaniem genetycznym.

3 komentarze do “Przygotowanie polskiego modelu word2vec z wykorzystaniem korpusu OpenSubtitles

  • Pingback: Przetwarzasz teksty, robisz NLP, TorchText Ci pomoże! - About Data

  • Cześć !
    Właśnie przeczytałem artykuł. Właśnie bawię się analizą tekstu i mam takie pytanie: czy word2Vect, które jest zaimplementowane na twoim gicie znajduje tylko słowa biskoznaczne, czy znajdzie również inne zależności powiedzmy pomiędzy samochód a wypadek, lek, a choroba lub zasłabnięcie a choroba? Bardzo podoba mi się forma, którą przekazałeś.
    Pozdrawiam

    • Word2Vec zawiera różne zależności gdyż jest budowany z wykorzystaniem bliskości występowania słów w zdaniu. Nie zawsze będzie to korzystne, np. antonimy będą kodowane blisko siebie. Przy czym trzeba zrozumieć że odległość w wielowymiarowej przestrzeni w praktyce działa trochę inaczej niż to do czego przyzwyczaiła nas intuicja kształtowana przez to że żyjemy na dwuwymiarowej powierzchni naszej planety. Przy tej ilości wymiarów homografy mogą być blisko wszystkich grup związanych ze swoim znaczeniem a jednocześnie same te grupy (biorąc pod uwagę ich centra) mogą być od siebie bardzo daleko.

Dodaj komentarz