https://habr.com/ru/post/507238/
В кейсе мы использовали библиотеки языка Python, такие как: Selenium, BeautifulSoup. Получилось около 27 тысяч отзывов, начиная с 2018 года. В среднем каждый отзыв занимал 2 абзаца листа А4. В 70 % отзывов была проставлена оценка клиентами, в оставшихся 30% — оценка не была проставлена. Полученные данные, у которых были проставлены оценки, мы решили использовать как исходные данные для построения модели обучения с учителем. Модель в дальнейшем нам нужна была, чтобы определить оценку у оставшихся 30% отзывов.
Проставленные оценки были от 1 до 5, но нам нужно было узнать, каким отзыв являлся по качеству, т. е. положительный он или отрицательный. Использовали логику школьных оценок в России: оценки 1,2 — отрицательные, их преобразовали в 0; оценки 3,4,5 – положительные, их преобразовали в 1. Тем самым задача свелась к бинарной классификации.
dataframe.tail(5)
Далее начали экспериментировать с моделью бинарной классификации.
Для первого прохода выбрали метод 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)
для данных условий 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)
Далее пробовали использовать SGDClassifier и для векторов слов TF-IDF.
Ниже приведены различные сочетания вариантов и их Confusion matrix:
1. SGDClassifier + Word2Vec
2. SGDClassifier+ TF-IDF
3. XGBoost+ TF-IDF
Рассчитывается 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')
Дальше использовали модель для оценки оставшихся не оцененных 30 процентов отзывов.
Вывод: Для обработки естественного языка (NLP) из нашего опыта, описанного выше, результаты лучше показала связка быстро сходящегося SGDClassifier с TF-IDF, но в свою очередь ключевая часть успеха лежит в предобработке данных.