Время прочтения: 4 мин.

Похожие кейсы мы уже рассматривали на нашем сайте:

В сегодняшнем кейсе мы использовали библиотеки языка Python, такие как: Selenium, BeautifulSoup. Получилось около 27 тысяч отзывов, начиная с 2018 года. В среднем каждый отзыв занимал 2 абзаца листа А4. В 70 % отзывов была проставлена оценка клиентами, в оставшихся 30% — оценка не была проставлена. Полученные данные, у которых были проставлены оценки, мы решили использовать как исходные данные для построения модели обучения с учителем. Модель в дальнейшем нам нужна была, чтобы определить оценку у оставшихся 30% отзывов.

Проставленные оценки были от 1 до 5, но нам нужно было узнать, каким отзыв являлся по качеству, т. е. положительный он или отрицательный. Использовали логику школьных оценок в России:  оценки  1,2 — отрицательные, их преобразовали в 0; оценки 3,4,5 – положительные, их преобразовали в 1. Тем самым задача свелась к бинарной классификации.

dataframe.tail(5)

Рисунок 1

Далее начали экспериментировать с моделью бинарной классификации.

Для первого прохода выбрали метод XGBoost для обучения с учителем. Учителем выступают оценки клиентов. В роли модели текста были взяты векторные представления слов Word2Vec, которые формируются в зависимости от контекста.

Предварительно провели предобработку текста, такую как: лемматизация, удаление стоп-слов, нормализация и его токенизация.

from nltk.tokenize import sent_tokenize
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
def tokenize_ru(x):
      tokens = x.lower()
       tokens = re.sub(r'[^\w\s]+|[\d]',' ',tokens)
       tokens = word_tokenize(tokens)
       tokens = [i for i in tokens if (i not in string.punctuation)]
       stop_words = stopwords.words('russian')
       stop_words.extend(['что', 'это', 'так', 'вот', 'быть', 'как', 'в', '—', '–', 'к', 'на', '...'])
       tokens = [i for i in tokens if (i not in stop_words)]
       tokens = [i.replace("«", "").replace("»", "") for i in tokens]
       return tokens

Перебирали лучшие параметры, используя метод GridSearch.

Оптимальные параметры по точности получились при глубине деревьев 8 (max_depth=8) и количество деревьев (n_estimators=88). По умолчанию в библиотеке XGBoost установлено: n_estimators=100, max_depth=3.

xgb_word2vec = Pipeline([
              ("word2vec", MeanVect(w2v)),
               ('model_fitting',  xgb)]) 

xgb_word2vec.fit(X_train2, y_train2)
Рисунок 2

для данных условий Confusion matrix (Матрица Путаницы) следующая:

import seaborn as sns
import matplotlib.pylab as plt
%matplotlib inline
from matplotlib.pylab import rcParams
rcParams['figure.figsize'] = 3, 3
plt.figure(figsize=(2,2))
sns.set(font_scale=1.5)
ax = sns.heatmap((pd.crosstab(y_test2, pred2).apply(lambda r: r/r.sum()*100, axis=0)), cbar=None, annot=True, cmap="Blues")
ax.set_ylabel("")
ax.set_xlabel("")
plt.yticks(rotation=0, size = 15)
plt.xticks(rotation=0, size = 15)
Рисунок 3

Далее пробовали использовать SGDClassifier и для векторов слов TF-IDF.

Ниже приведены различные сочетания вариантов и их Confusion matrix:

1. SGDClassifier + Word2Vec (Рисунок 4).

2. SGDClassifier+ TF-IDF (Рисунок 5).

3. XGBoost+ TF-IDF (Рисунок 6).

Рассчитывается f-mera по формуле f-mera = 2*Precision*Recall/(Precision+Recall), где Precision(точность) и Recall(полнота) вычисляются из Confusion matrix, это гармоническое среднее между точностью и полнотой, для бинарной классификации этого вполне достаточно для оценки точности модели. Лучший результат получился при сочетании SGDClassifier + TF-IDF.

Далее, в уже выбранную модель, с лучшей точностью, подставили неактуальные оцененные отзывы, начиная с 2016 по 2018 годы, которые не участвовали в обучении, чтобы проверить правильность обучения и работы модели, в результате получили f-mera: 0.9326314212969897. Эту модель в дальнейшем можно использовать для классификации отзывов, чтобы не тратить время на поиск вручную отрицательных отзывов.

precision_recall_fscore_support(y_prov, predtest, average='macro')
Рисунок 7

Дальше использовали модель для оценки оставшихся не оцененных 30 процентов отзывов.

Вывод: Для обработки естественного языка (NLP) из нашего опыта, описанного выше, результаты лучше показала связка быстро сходящегося SGDClassifier с TF-IDF, но в свою очередь ключевая часть успеха лежит в предобработке данных.