Реализация мультиклассовой классификации текста с помощью Doc2Vec

Вступление

В этом посте вы узнаете, как классифицировать текстовые документы по различным категориям при использовании Doc2Vec для представления документов. Мы узнаем это с помощью простого для понимания примера классификации сюжетов фильмов по жанрам с использованием Doc2vec для представления функций и использования логистической регрессии в качестве алгоритма классификации. Набор данных фильма содержит краткие описания сюжета фильма, а метки для них представляют жанр. В наборе данных присутствуют шесть жанров:

  1. Научная фантастика
  2. Действие
  3. Комедия
  4. Фантазия
  5. Анимация
  6. Романтика

Что такое Doc2Vec и зачем он нам?

Итак, почему мы должны выбрать метод представления doc2vec, а не широко известный метод набора слов (BOW). Для сложных алгоритмов классификации текста BOW не подходит, поскольку ему не хватает возможности фиксировать семантику и синтаксический порядок слов в тексте. Таким образом, использование их в качестве входных данных для алгоритма машинного обучения не приведет к значительной производительности. Doc2Vec, с другой стороны, может определять отношения между словами и понимать семантику текста. Doc2Vec - это неконтролируемый алгоритм, который изучает векторы признаков фиксированной длины для абзацев / документов / текстов. Чтобы понять основы работы doc2vec, необходимо понимать, как работает word2vec, поскольку он использует ту же логику, за исключением того, что вектор, специфичный для документа, является вектором добавленных функций. Подробнее об этом вы можете прочитать в этом блоге. Теперь, когда мы знаем, почему мы его используем и как doc2vec будет использоваться в этой программе, мы можем перейти к следующему этапу фактической реализации классификатора.

Приступим к созданию классификатора сюжетов фильмов !!

Реализация

Для этого задействованы три основные части:

  1. Загрузка и подготовка текстовых данных
  2. Получение вектора признаков с помощью модели doc2vec
  3. Обучение классификатора

Вот первые три строки входного CSV-файла:

,movieId,plot,tag
0,1,"A little boy named Andy loves to be in his room, playing with his toys, especially his doll named ""Woody"". But, what do the toys do when Andy is not with them, they come to life. Woody believes that he has life (as a toy) good. However, he must worry about Andy's family moving, and what Woody does not know is about Andy's birthday party. Woody does not realize that Andy's mother gave him an action figure known as Buzz Lightyear, who does not believe that he is a toy, and quickly becomes Andy's new favorite toy. Woody, who is now consumed with jealousy, tries to get rid of Buzz. Then, both Woody and Buzz are now lost. They must find a way to get back to Andy before he moves without them, but they will have to pass through a ruthless toy killer, Sid Phillips.",animation
1,2,"When two kids find and play a magical board game, they release a man trapped for decades in it and a host of dangers that can only be stopped by finishing the game.",fantasy

Импорт необходимых библиотек:

Мы используем многопроцессорность для использования всех ядер для более быстрого обучения с Doc2Vec. Пакет tqdm используется для отображения индикатора выполнения во время тренировки. Мы используем пакет gensim для Doc2Vec. Для целей классификации используется логистическая регрессия от scikit-learn. Пакет NLTK используется для задачи токенизации.

from gensim.test.utils import common_texts
from gensim.models.doc2vec import Doc2Vec, TaggedDocument
from sklearn.metrics import accuracy_score, f1_score
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn import utils
import csv
from tqdm import tqdm
import multiprocessing
import nltk
from nltk.corpus import stopwords

1. Чтение и подготовка текстовых данных

Следующий код предназначен для чтения данных из CSV и функции токенизации, которая будет использоваться при создании обучающих и тестовых документов в качестве входных данных для модели doc2vec. Данные содержат 2448 строк, первые 2000 строк мы выбрали для обучения, а остальные - для тестирования.

tqdm.pandas(desc="progress-bar")
# Function for tokenizing
def tokenize_text(text):
    tokens = []
    for sent in nltk.sent_tokenize(text):
        for word in nltk.word_tokenize(sent):
            if len(word) < 2:
                continue
            tokens.append(word.lower())
    return tokens
# Initializing the variables
train_documents = []
test_documents = []
i = 0
# Associating the tags(labels) with numbers
tags_index = {'sci-fi': 1 , 'action': 2, 'comedy': 3, 'fantasy': 4, 'animation': 5, 'romance': 6}
#Reading the file
FILEPATH = 'data/tagged_plots_movielens.csv'
with open(FILEPATH, 'r') as csvfile:
with open('data/tagged_plots_movielens.csv', 'r') as csvfile:
    moviereader = csv.reader(csvfile, delimiter=',', quotechar='"')
    for row in moviereader:
        if i == 0:
            i += 1
            continue
        i += 1
        if i <= 2000:
            train_documents.append(          TaggedDocument(words=tokenize_text(row[2]), tags=[tags_index.get(row[3], 8)] ))
        else:
            test_documents.append( TaggedDocument(words=tokenize_text(row[2]),
 tags=[tags_index.get(row[3], 8)]))
print(train_documents[0])

Результатом, который является обучающим_документом для первой строки, является объект TaggedDocument. Это показывает токены в качестве первого аргумента, которые являются токенами, и labelID в качестве второго аргумента (5: Анимация) для TaggedDocument.

TaggedDocument(['little', 'boy', 'named', 'andy', 'loves', 'to', 'be', 'in', 'his', 'room', 'playing', 'with', 'his', 'toys', 'especially', 'his', 'doll', 'named', '``', 'woody', "''", 'but', 'what', 'do', 'the', 'toys', 'do', 'when', 'andy', 'is', 'not', 'with', 'them', 'they', 'come', 'to', 'life', 'woody', 'believes', 'that', 'he', 'has', 'life', 'as', 'toy', 'good', 'however', 'he', 'must', 'worry', 'about', 'andy', "'s", 'family', 'moving', 'and', 'what', 'woody', 'does', 'not', 'know', 'is', 'about', 'andy', "'s", 'birthday', 'party', 'woody', 'does', 'not', 'realize', 'that', 'andy', "'s", 'mother', 'gave', 'him', 'an', 'action', 'figure', 'known', 'as', 'buzz', 'lightyear', 'who', 'does', 'not', 'believe', 'that', 'he', 'is', 'toy', 'and', 'quickly', 'becomes', 'andy', "'s", 'new', 'favorite', 'toy', 'woody', 'who', 'is', 'now', 'consumed', 'with', 'jealousy', 'tries', 'to', 'get', 'rid', 'of', 'buzz', 'then', 'both', 'woody', 'and', 'buzz', 'are', 'now', 'lost', 'they', 'must', 'find', 'way', 'to', 'get', 'back', 'to', 'andy', 'before', 'he', 'moves', 'without', 'them', 'but', 'they', 'will', 'have', 'to', 'pass', 'through', 'ruthless', 'toy', 'killer', 'sid', 'phillips'], [5])

2. Получение вектора признаков из модели doc2vec

Далее мы инициализируем модель gensim doc2vec и тренируемся в течение 30 эпох. Это довольно простой процесс. Архитектура Doc2Vec также имеет два алгоритма, такие как word2vec, и они являются соответствующими алгоритмами для этих двух алгоритмов, а именно Непрерывный пакет слов (CBOW) и Skip-Gram (SG). Один из алгоритмов в doc2vec называется Вектор абзаца - Распределенный пакет слов (PV-DBOW), который похож на модель SG в word2vec, за исключением того, что добавлен дополнительный вектор идентификатора абзаца. Здесь нейронная сеть обучается предсказывать вектор окружающих слов в данном абзаце и вектор идентификатора абзаца на основе данного слова в абзаце. Второй алгоритм - Paragraph Vector (PV-DM), который похож на CBOW в векторе слов.

Несколько важных параметров модели doc2vec включают в себя:

dm ({0,1}, необязательно) 1: PV-DM, 0: PV-DBOW

vector_size Размерность вектора признаков (мы выбрали 300)

workers это количество потоков, которым мы присвоили количество ядер

Остальную информацию о параметрах можно найти здесь. После инициализации мы создаем словарь, используя train_documents

cores = multiprocessing.cpu_count()

model_dbow = Doc2Vec(dm=1, vector_size=300, negative=5, hs=0, min_count=2, sample = 0, workers=cores, alpha=0.025, min_alpha=0.001)
model_dbow.build_vocab([x for x in tqdm(train_documents)])
train_documents  = utils.shuffle(train_documents)
model_dbow.train(train_documents,total_examples=len(train_documents), epochs=30)
def vector_for_learning(model, input_docs):
    sents = input_docs
    targets, feature_vectors = zip(*[(doc.tags[0], model.infer_vector(doc.words, steps=20)) for doc in sents])
    return targets, feature_vectors
model_dbow.save('./movieModel.d2v')

3. Обучение классификатора.

Наконец, используя приведенную выше функцию построения вектора признаков, мы обучаем классификатор логистической регрессии. Здесь мы использовали вектор признаков, сгенерированный для train_documents во время обучения, и использовали векторы признаков test_documents на этапе прогнозирования.

y_train, X_train = vector_for_learning(model_dbow, train_documents)
y_test, X_test = vector_for_learning(model_dbow, test_documents)

logreg = LogisticRegression(n_jobs=1, C=1e5)
logreg.fit(X_train, y_train)
y_pred = logreg.predict(X_test)
print('Testing accuracy for movie plots%s' % accuracy_score(y_test, y_pred))
print('Testing F1 score for movie plots: {}'.format(f1_score(y_test, y_pred, average='weighted')))

Результат при dm=1 выглядит следующим образом:

Testing accuracy 0.42316258351893093
Testing F1 score: 0.41259684559985876

Эта точность достигается только при использовании нескольких записей с очень коротким текстом, и, следовательно, ее можно улучшить, добавив более совершенные функции, такие как n-граммы, и используя стоп-слова для удаления шума.

Дальнейшие разработки

Опираясь на это, вы можете легче экспериментировать с другими наборами данных, а изменить алгоритмы для doc2vec так же просто, как изменить параметр dm. Надеюсь, это поможет вам начать свой первый проект по классификации текста с помощью Doc2Vec!

Автор: Дипика Баад