Обучение NLP-модели Word2veс на русских текстах с Python

Продолжаем решать NLP-задачи на примере корпуса с русскоязычными twitter-постами, на основе которого мы получили датасет [вот здесь]. Сегодня мы расскажем, как построить и обучить свою word2vec-модель Machine Learning, используя Python-библиотеку Gensim.

Модель Word2vec на основе датасета русскоязычных twitter-постов

В предыдущей статье мы подготовили датасет: провели лемматизацию и удалили стоп слова из корпуса, который содержит twitter-посты на русском языке [1]. Вот так он сейчас выглядят:

>>> data
0         [школотый, поверь, самый, общество, профилиров...
1          [да, таки, немного, похожий, но, мальчик, равно]
2                                 [ну, идиотка, испугаться]
3         [кто, угол, сидеть, погибать, голод, ещё, порц...
4         [вот, значит, страшилка, но, блин, посмотреть,...
...
205174                     [но, каждый, хотеть, исправлять]
205175          [скучать, вправлять, мозги, равно, скучать]
205176                       [вот, школа, говно, это, идти]
205177                           [тауриэль, грусть, обнять]
205178    [такси, везти, работа, раздумывать, приплатить...

Далее построим модель Word2vec, обученную на полученном датасете, с библиотекой Gensim [2].

Обучение модели Word2vec

Подготовив датасет, можем обучить модель. Для этого воспользуемся библиотекой Gensim и инициализируем модель Word2vec:

from gensim.models import Word2Vec

w2v_model = Word2Vec(
    min_count=10,
    window=2,
    size=300,
    negative=10,
    alpha=0.03,
    min_alpha=0.0007,
    sample=6e-5,
    sg=1)

Модель имеет множество аргументов:

  • min_count — игнорировать все слова с частотой встречаемости меньше, чем это значение.
  • windоw — размер контекстного окна, о котором говорили тут, обозначает диапазон контекста.
  • size — размер векторного представления слова (word embedding).
  • negative — сколько неконтекстных слов учитывать в обучении, используя negative sampling, о нем также упоминалось здесь.
  • alpha — начальный learning_rate, используемый в алгоритме обратного распространения ошибки (Backpropogation).
  • min_alpha — минимальное значение learning_rate, на которое может опуститься в процессе обучения.
  • sg — если 1, то используется реализация Skip-gram; если 0, то CBOW. О реализациях также говорили тут.

Далее, требуется получить словарь:

w2v_model.build_vocab(data)

А после уже можно обучить модель, используя метод train:

w2v_model.train(data, total_examples=w2v_model.corpus_count, epochs=30, report_delay=1)

Если в дальнейшем не требуется снова обучать модель, то для сохранения оперативной памяти можно написать следующее:

w2v_model.init_sims(replace=True)

Насколько похожи слова обученной модели Word2vec

После того, как модель была обучена, можем смотреть результаты. Каждое слово представляется вектором, следовательно, их можно сравнивать. В качестве инструмента сравнения в Gensim используется косинусный коэффициент (Cosine similarity) [3].

У модели Word2vec имеется в качестве атрибута объект wv, который и содержит векторное представление слов (word embeddings). У этого объекта есть методы для получения мер схожестей слов. Например, определим, какие слова находятся ближе всего к слову “любить”:

>>> w2v_model.wv.most_similar(positive=["любить"])
[('дорожить', 0.5577003359794617),
('скучать', 0.4815309941768646),
('обожать', 0.477267324924469),
('машенька', 0.4503161907196045),
('предновогодниеобнимашка', 0.4403109550476074),
('викуль', 0.43941542506217957),]

Число после запятой обозначает косинусный коэффициент, чем он больше, тем выше близость слов. Можно заметить, что слова “дорожить”, “скучать”, “обожать” наиболее близкие к слову “любить” Можно также рассмотреть другие слова:

>>> w2v_model.wv.most_similar(positive=["мужчина"])
[('женщина', 0.6242121458053589),
('девушка', 0.5410279035568237),
('любящий', 0.5005632638931274),
('парень', 0.4864271283149719),
('идеал', 0.45188209414482117),
('существо', 0.44532185792922974),
('недостаток', 0.4350862503051758),
('пёсик', 0.42453521490097046),
('послушный', 0.42428380250930786),
('эгоизм', 0.4202057421207428)]
...
>>> w2v_model.wv.most_similar(positive=["день", "завтра"])
[('сегодня', 0.6082668304443359),
('неделя', 0.5371285676956177),
('суббота', 0.48631012439727783),
('выходной', 0.4772387742996216),
('понедельник', 0.4697558283805847),
('денёчек', 0.4688040316104889),
('каникулы', 0.45828908681869507),
('подъесть', 0.4555707573890686),
('отсыпаться', 0.44570696353912354),
('аттестация', 0.4408838450908661)]

Векторы можно складывать и вычитать. Например, рассмотрим такой вариант: “папа” + “брат” — “мама”. Получили следующее:

>>> w2v_model.wv.most_similar(positive=["папа", "брат"], negative=["мама"])
[('младший', 0.3892076015472412),
('сестра', 0.31560415029525757),
('двоюродный', 0.3024488091468811),
('старший', 0.2937452793121338),]

Как видим, на этом корпусе результатом являются слова “младший”, “сестра”, “двоюродный”. Несмотря на то, что мы обучили модель на twitter-постах, получаем достаточно адекватные результаты.

Есть также возможность определить наиболее близкое слово из списка к данному слово. Для этого нужно воспользоваться методом:

>>> w2v_model.wv.most_similar_to_given("хороший", ["приятно", "город", "мальчик"])
'приятно'

Слово “приятно” из всего списка наиболее близок к слову “хороший”. Кроме того, так как для сравнения находится косинусный коэффициент, то его можно получить, написав:

>>> w2v_model.wv.similarity("плохой", "хороший")
0.5427995
>>> w2v_model.wv.similarity("плохой", "герой")
0.04865976

Само векторное представление слова можно получит либо напрямую, обратившись через квадратные скобки, либо через метод word_vec:

>>> w2v_model.wv.word_vec("страшилка")
array([-0.05101333, -0.03730767,  0.03676478,  0.18950877,  0.02496774,
        0.00176699, -0.0966768 ,  0.04010197, -0.00862965, -0.00530563,
...
dtype=float32)
>>> w2v_model.wv["страшилка"]
array([-0.05101333, -0.03730767,  0.03676478,  0.18950877,  0.02496774,
        0.00176699, -0.0966768 ,  0.04010197, -0.00862965, -0.00530563,
...
dtype=float32)
>>> w2v_model.wv.word_vec("страшилка").shape
(300,)

Векторное представление слов на плоскости

Так как слова в модели Word2vec — это векторы, то можно их выстроить на координатную плоскость. Поскольку каждый вектор модели имеет размерность 300, который был указан как аргумент size, то не предоставляется возможности построить 300-мерное пространство. Поэтому мы воспользуемся методом уменьшения размерности t-SNE, который есть в Python-библиотеке Scikit-learn [4], и снизим размерность векторов с 300 до 2. Согласно документации, если размерность больше 50, то стоит сначала воспользоваться другим методом уменьшения размерности — методом главных компонент PCA [5]. Для этого в аргументе достаточно указать init=”pca”.

В результате мы написали функцию, которая принимает входное слово и список слов и строит на плоскости входное слово, ближайшие к нему слова и переданный список слов:

import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.manifold import TSNE

def tsne_scatterplot(model, word, list_names):
    """Plot in seaborn the results from the t-SNE dimensionality reduction 
    algorithm of the vectors of a query word,
    its list of most similar words, and a list of words."""
    vectors_words = [model.wv.word_vec(word)]
    word_labels = [word]
    color_list = ['red']

    close_words = model.wv.most_similar(word)
    for wrd_score in close_words:
        wrd_vector = model.wv.word_vec(wrd_score[0])
        vectors_words.append(wrd_vector)
        word_labels.append(wrd_score[0])
        color_list.append('blue')

    # adds the vector for each of the words from list_names to the array
    for wrd in list_names:
        wrd_vector = model.wv.word_vec(wrd)
        vectors_words.append(wrd_vector)
        word_labels.append(wrd)
        color_list.append('green')

    # t-SNE reduction
    Y = (TSNE(n_components=2, random_state=0, perplexity=15, init="pca")
        .fit_transform(vectors_words))
    # Sets everything up to plot
    df = pd.DataFrame({"x": [x for x in Y[:, 0]],
                    "y": [y for y in Y[:, 1]],
                    "words": word_labels,
                    "color": color_list})
    fig, _ = plt.subplots()
    fig.set_size_inches(9, 9)
    # Basic plot
    p1 = sns.regplot(data=df,
                    x="x",
                    y="y",
                    fit_reg=False,
                    marker="o",
                    scatter_kws={"s": 40,
                                "facecolors": df["color"]}
    )
    # Adds annotations one by one with a loop
    for line in range(0, df.shape[0]):
        p1.text(df["x"][line],
                df["y"][line],
                " " + df["words"][line].title(),
                horizontalalignment="left",
                verticalalignment="bottom", size="medium",
                color=df["color"][line],
                weight="normal"
        ).set_size(15)

    plt.xlim(Y[:, 0].min()-50, Y[:, 0].max()+50)
    plt.ylim(Y[:, 1].min()-50, Y[:, 1].max()+50)
    plt.title('t-SNE visualization for {}'.format(word.title()))

Далее рассмотрим, как далеко располагается слово “история” и слова «неделя», «россия», «зима»:

tsne_scatterplot(w2v_model, "история", ["неделя", "россия", "зима"])

Рисунок ниже показывает результат уменьшения размерности. Как видим, около истории находятся слова, связанные с учебой (“педагогика”, “теорема”), а вот “неделя”, которую мы передали как элемент списка, находится далеко от “истории”.

Двумерное представление векторов Word2vec на плоскости
Слова на системе координат

Мы переобучили модель, увеличив число эпох обучения (аргумент epoch в методе train) в два раза, и построили через ту же самую функцию систему координат:

Двумерное представление векторов Word2vec на плоскости после перенастраивания модели
Слова на системе координат после увеличения числа эпох в два раза

Результаты оправдали себя, с “историей” стало ассоциироваться больше слов, связанных с учебой и приблизилась к нему “неделя”. Machine Learning требует постоянного экспериментирования, поэтому важно всячески настраивать и перенастраивать свою модель.

 

Еще больше подробностей о NLP-моделях и особенностях их обучения на реальных примерах Data Science с помощью средств языка Python, вы узнаете на наших курсах в лицензированном учебном центре обучения и повышения квалификации ИТ-специалистов в Москве.

Источники
  1. Рубцова Ю. Автоматическое построение и анализ корпуса коротких текстов (постов микроблогов) для задачи разработки и тренировки тонового классификатора //Инженерия знаний и технологии семантического веба. – 2012. – Т. 1. – С. 109-116.
  2. https://radimrehurek.com/gensim/
  3. https://en.wikipedia.org/wiki/Cosine_similarity
  4. https://scikit-learn.org/stable/modules/generated/sklearn.manifold.TSNE.html
  5. https://ru.wikipedia.org/wiki/Метод_главных_компонент

Добавить комментарий

Поиск по сайту