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

Определенно вам придется применить стандартные методы классификации ML (из постановки задачи легко понять, что это проблема классификации). Но здесь проблема в кусках текстовых данных. Это не числа, так как же заставить компьютер / алгоритм понять важность обзора? Давайте рассмотрим пошаговый подход

Получение данных из источника

Примеры рецензий на фильмы доступны на портале Корнельского университета (http://www.cs.cornell.edu/people/pabo/movie-review-data/). Вы можете скачать Набор данных полярности v2.0. Этот набор данных содержит две папки с именами pos и neg и по 1000 файлов в каждой папке. В каждом из этих файлов есть обзорные статьи, написанные в текстовом формате. Вы можете прочитать и распечатать набор данных, используя фрагмент кода Python ниже:

import pandas as pd
import pathlib as pl
def _read_all_reviews_():
 all_reviews = []
 for p in pl.Path('../data/txt_sentoken/pos').iterdir():
 file = open(p, 'r')
 all_reviews.append({'reviews_content': file.read(), 'category': 'positive'})
 file.close()
 for p in pl.Path('../data/txt_sentoken/neg').iterdir():
 file = open(p, 'r')
 all_reviews.append({'reviews_content': file.read(), 'category': 'negative'})
 file.close()
 all_reviews_df = pd.DataFrame(all_reviews)
 return all_reviews_df
print(_read_all_reviews_())

Концепция конвейера машинного обучения

Создание конвейера соответствует лучшим практикам выполнения любого алгоритма машинного обучения. Однако, не создавая конвейера, вы также можете выполнить алгоритм машинного обучения. Но конвейерная обработка упрощает вашу жизнь. Давайте сначала разберемся, что означает «трубопровод»?

Большая часть алгоритма машинного обучения требует предварительной обработки данных о стоимости проезда. Вся эта предварительная обработка и сам алгоритм могут быть сконфигурированы как отдельные повторно используемые шаги. Вместе все эти ступени, соединенные в единое целое с входом и выходом, известны как «трубопровод». Теперь давайте посмотрим, какие шаги необходимы для решения нашей проблемы.

Создание модели векторного пространства

Первым шагом в вашем конвейере будет преобразование данных в числовые значения, поскольку в настоящее время они представлены в текстовом формате. Доступны стандартные модели, такие как «Tf-Idf», «Word2Vec», «Doc2Vec» и т. Д. Все они известны как «векторные пространственные модели» в текстовой аналитике, и каждая из них не дает ничего, кроме числового векторного представления текстовых данных. Этот вектор работает как вектор функций документа и определяет, сколько функций будет в нем. Я использовал «Doc2Vec» в качестве модели для этой проблемы, потому что каждый обзор можно рассматривать как отдельный документ, а «Doc2Vec» действительно хорошо работает для понимания контекстного значения текста (по сравнению с Tf-Idf, который представляет собой не что иное, как чистую частоту- на основе модели). «Doc2Vec» для документа создается путем обучения нейронной сети по соседнему слову случайно выбранного слова и наоборот. Здесь только изученные веса скрытого слоя вызывают беспокойство и принимаются как значения «Doc2Vec». Фактически существует два метода: распределенная память (DM) и распределенный пакет слов (DBOW). Для получения более подробной информации вы можете воспользоваться следующими ресурсами:

Следующий фрагмент кода Python работает как этап конвейера для создания Doc2Vec для документа:

from sklearn.pipeline import Pipeline
from sklearn.decomposition import PCA
from gensim.models.doc2vec import TaggedDocument, Doc2Vec
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from gensim.parsing.preprocessing import preprocess_string
from sklearn import utils
from tqdm import tqdm
class Doc2VecTransformer(BaseEstimator):
 def __init__(self, vector_size=100, learning_rate=0.02, epochs=20):
 self.learning_rate = learning_rate
 self.epochs = epochs
 self._model = None
 self.vector_size = vector_size
 self.workers = multiprocessing.cpu_count()
 def fit(self, df_x, df_y=None):
 tagged_x = [TaggedDocument(preprocess_string(row['reviews_content']), [index]) for index, row in df_x.iterrows()]
 model = Doc2Vec(documents=tagged_x, vector_size=self.vector_size, workers=self.workers)
 for epoch in range(self.epochs):
 model.train(utils.shuffle([x for x in tqdm(tagged_x)]), total_examples=len(tagged_x), epochs=1)
 model.alpha -= self.learning_rate
 model.min_alpha = model.alpha
 self._model = model
 return self
 def transform(self, df_x):
return np.asmatrix(np.array([self._model.infer_vector(preprocess_string(row['reviews_content'])) for index, row in df_x.iterrows()]))

В приведенном выше фрагменте кода параметр «vector_size» определяет длину вектора для каждого документа. В основном это количество извлеченных функций документа, как описано выше. Эти особенности имеют «виртуальную природу» и их трудно визуализировать физически. Вы можете сказать, что физически это не имеет никакого значения, но они добавляют большую ценность для математического моделирования. Вы можете видеть, что модель обучается многократно несколько раз с настроенной скоростью обучения. Это просто для лучшего результата. «Vector_size» и «learning_rate» можно использовать как гиперпараметры для настройки модели. Я объясню это позже. В конечном итоге функции Doc2Vec возвращаются в виде матрицы, где строки - это номера документов, а столбцы - значения функций.

PCA-моделирование Doc2Vec

Как видите, Doc2Vec может создавать большое количество функций в зависимости от того, какое значение вы передаете в параметре «vector_size», поэтому всегда удобно уменьшить его до соответствующего набора функций. Основные компоненты могут сыграть здесь хорошую роль. Итак, следующим шагом вашего конвейера будет преобразование файла Doc2Vec в PCA. Основные компоненты также являются виртуальными функциями, такими как «Doc2Vec», и их трудно визуализировать / концептуализировать, но они подходят для математического моделирования. Вы можете передать «n_components» в качестве параметра (количество основных компонентов) в преобразователе PCA. В результате вы получите векторы PCA. Очевидно, что ваши «n_components» должны быть намного меньше, чем «vector_size», может составлять половину или одну треть от него.

Подбор классификатора логистической регрессии

После того, как вы закончите с PCA, вы можете подобрать векторы PCA в классификаторе двоичной логистической регрессии (потому что здесь выходные данные являются категориальной переменной и могут иметь только два значения: «положительное» и «отрицательное»), и это будет ваш последний шаг в конвейере. В зависимости от значений вектора PCA этот классификатор предсказывает, будет ли история обзора фильма положительной или отрицательной.

Весь конвейер показан во фрагменте кода Python:

def train_and_build_model():
 all_reviews_df = _read_all_reviews_()
 train_x_df, test_x_df, train_y_df, test_y_df = train_test_split(all_reviews_df[['reviews_content']], all_reviews_df[['category']])
 pl = Pipeline(steps=[('doc2vec', Doc2VecTransformer(vector_size=220)),
 ('pca', PCA(n_components=100)),
 ('logistic', LogisticRegression())
 ])
 pl.fit(train_x_df[['reviews_content']], train_y_df[['category']])
 predictions_y = pl.predict(test_x_df[['reviews_content']])
 print('Accuracy: ', metrics.accuracy_score(y_true=test_y_df[['category']], y_pred=predictions_y))

Теперь вы можете подумать, откуда я взял значения «n_components» как 100 и «vector_size» как 220. Вы можете передавать сюда любые значения. Обычно для Doc2Vec значение «vector_size» составляет от 100 до 300. Но для PCA вам решать, сколько основных компонентов вы хотите сохранить, чтобы поддерживать достаточную изменчивость данных. И «vector_size», и «n_components» здесь являются «гиперпараметрами».

Настройка гиперпараметров с помощью поиска по сетке

Библиотека поиска по сетке в Python дает вам механизм для поиска оптимального набора гиперпараметров. Вы можете предоставить диапазон возможных значений, чтобы получить максимальную точность от модели классификатора. Следующий фрагмент кода Python может дать вам следующее:

from sklearn.model_selection import GridSearchCV
def train_long_range_grid_search():
 all_reviews_df = _read_all_reviews_()
 train_x_df, test_x_df, train_y_df, test_y_df = train_test_split(all_reviews_df[['reviews_content']], all_reviews_df[['category']])
 pl = Pipeline(steps=[('doc2vec', Doc2VecTransformer()),
 ('pca', PCA()),
 ('logistic', LogisticRegression())
 ])
 param_grid = {
 'doc2vec__vector_size': [x for x in range(100, 250)],
 'pca__n_components': [x for x in range(1, 50)]
 }
 gs_cv = GridSearchCV(estimator=pl, param_grid=param_grid, cv=5, n_jobs=-1,
 scoring="accuracy")
 gs_cv.fit(train_x_df[['reviews_content']], train_y_df[['category']])
 print("Best parameter (CV score=%0.3f):" % gs_cv.best_score_)
 print(gs_cv.best_params_)
 predictions_y = gs_cv.predict(test_x_df[['reviews_content']])
 print('Accuracy: ', metrics.accuracy_score(y_true=test_y_df[['category']], y_pred=predictions_y))

Но для получения оптимального набора потребуется много времени. Короче говоря, вы можете сократить возможный диапазон и при этом получить более или менее оптимальный набор. Для этого предназначен следующий фрагмент кода:

def train_short_range_grid_search():
 all_reviews_df = _read_all_reviews_()
 train_x_df, test_x_df, train_y_df, test_y_df = train_test_split(all_reviews_df[['reviews_content']], all_reviews_df[['category']])
 pl = Pipeline(steps=[('doc2vec', Doc2VecTransformer()),
 ('pca', PCA()),
 ('logistic', LogisticRegression())
 ])
 param_grid = {
 'doc2vec__vector_size': [200, 220, 250],
 'pca__n_components': [50, 75, 100]
 }
 gs_cv = GridSearchCV(estimator=pl, param_grid=param_grid, cv=3, n_jobs=-1,
 scoring="accuracy")
 gs_cv.fit(train_x_df[['reviews_content']], train_y_df[['category']])
 print("Best parameter (CV score=%0.3f):" % gs_cv.best_score_)
 print(gs_cv.best_params_)
 predictions_y = gs_cv.predict(test_x_df[['reviews_content']])
 print('Accuracy: ', metrics.accuracy_score(y_true=test_y_df[['category']], y_pred=predictions_y))

Из приведенного выше мы получаем оценку точности около 55%. Это можно улучшить, выбрав правильную модель или подход. Вы можете получить всю кодовую базу здесь, в репозитории github.

Заключение и оптимизация подхода

В машинном обучении совершенно очевидно одно: «Ни одна модель не является абсолютно правильной или неправильной». У каждой модели есть свои плюсы и минусы. Вышеупомянутую проблему можно было решить и по-другому. На самом деле, если бы я применил подход к глубокому обучению, это могло бы дать лучший результат. Но я намеревался продемонстрировать технику с использованием PCA и логистической регрессии, чтобы вы могли применить ее для решения любой задачи текстовой аналитики.

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

В любом случае !! В следующий раз займусь другой темой. А пока наслаждайтесь «Машинным обучением» и «Счастливым кодированием».

Использованная литература :

а) Учебник Python-Gensim

б) Учебник PCA с математической концепцией

в) Учебник по логистической регрессии с математической концепцией

Недавно я написал книгу по ML (https://twitter.com/bpbonline/status/1256146448346988546)

Первоначально опубликовано на medium.com 21 марта 2019 г.

Эта история опубликована в The Startup, крупнейшем предпринимательском издании Medium, за которым следят +436 678 человек.

Подпишитесь, чтобы получать наши главные новости здесь.