Проблема

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

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

Данные

В нашем эксперименте мы будем использовать 30 000 строк, помеченных как «одобренные» (подлинный бренд), и 30 000 строк, помеченных как «черный список» (плохое название бренда). Мы вычислим различные числовые характеристики для каждой строки и сохраним их в виде CSV-файла.

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

https://medium.com/swlh/nlp-all-them-features-every-feature-that-can-be-extracted-from-text-7032c0c87dee

Модель

Мы решили попробовать поиграть с логистической регрессией для бинарной классификации (бренд/небренд).

import pandas as pd

features = ['number_of_characters', 'number_of_unique_characters', 'number_of_alnum_characters', 'number_of_numeric_characters', 'number_of_capital_characters', 'number_of_commas', 'pct_of_alnum_characters', 'pct_of_numeric_characters', 'pct_of_capital_characters', 'pct_of_unique_tokens', 'pct_of_commas', 'pct_of_unique_characters', 'number_of_tokens', 'number_of_unique_tokens']
columns = ['status', ] + features

data = pd.read_csv("nr2.txt", header=None, names=columns)

data.head()

Затем мы определяем, в каком столбце хранится прогнозируемое значение, а в каких — функции.

X = data[features] # features
y = data.status  # actual class

Нам нужно разделить наш набор данных на две части — обучающую (75% данных) и тестовую (25% данных).

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=16)

Наконец, мы готовы обучить нашу модель.

from sklearn.linear_model import LogisticRegression

model = LogisticRegression(random_state=16)
model.fit(X_train.values, y_train)

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

Тест

Я протестирую двух кандидатов: «98,5% katoen, 1,5% elastaan ​​Kleur» (фактическая строка, отправленная продавцами в наш пайплайн в качестве значения атрибута бренда продукта) и «Apple».

Особенности этих строк:

[33, 18, 24, 5, 1, 3, 0.73, 0.15, 0.03, 1.0, 0.09, 0.55, 5, 5]
[5, 4, 5, 0, 1, 0, 1.0, 0.0, 0.2, 1.0, 0.0, 0.8, 1, 1]

Хорошо, но насколько хороша ваша модель в этих прогнозах?

Ручная проверка случайной строки — хороший подход, но насколько хороша модель в масштабе? Мы проверим несколько стандартных показателей, используемых для классификаторов оценки, чтобы измерить это.

from sklearn.metrics import classification_report

target_names = ['brand', 'not brand']
print(classification_report(y_test, y_pred, target_names=target_names))

Эти цифры по всем показателям можно считать очень хорошими.

Подробнее о показателях:

https://en.wikipedia.org/wiki/Precision_and_recall
https://en.wikipedia.org/wiki/F-score

Матрица

Еще один способ измерить качество классификатора — запутанная матрица.

from sklearn import metrics

y_pred = model.predict(X_test.values)
matrix = metrics.confusion_matrix(y_test, y_pred)
print(matrix)

Для визуализации матрицы мы можем построить график.

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

class_names = ['brand', 'not brand']
fig, ax = plt.subplots()
tick_marks = np.arange(len(class_names))
plt.xticks(tick_marks, class_names)
plt.yticks(tick_marks, class_names)

sns.heatmap(pd.DataFrame(matrix), annot=True, cmap="YlGnBu", fmt='g')
ax.xaxis.set_label_position("top")
plt.tight_layout()
plt.title('Confusion matrix', y=1.1)
plt.ylabel('Actual label')
plt.xlabel('Predicted label')
plt.show()

https://en.wikipedia.org/wiki/Confusion_matrix

Кривая ROC

y_pred_proba = model.predict_proba(X_test)[::,1]
fpr, tpr, _ = metrics.roc_curve(y_test,  y_pred_proba, pos_label='blacklisted')
auc = metrics.roc_auc_score(y_test, y_pred_proba)
plt.plot(fpr,tpr,label="data 1, auc="+str(auc))
plt.legend(loc=4)
plt.show()

Результат просто отличный (по словам моей жены, которая является настоящим ученым-статистиком)…

Подробнее об этом https://en.wikipedia.org/wiki/Receiver_operating_characteristic

Как запустить в производство?

ИМО, это будет самая утомительная и очевидная часть статьи. Давай засолим!

import pickle

filename = 'brands_classifier.sav'
pickle.dump(model, open(filename, 'wb'))

Например, вы можете сохранить полученный файл в S3, а затем загрузить/использовать его в своем веб-сервисе Flask/Sanic/FastAPI, в своем процессоре Faust для потока Kafka и т. д. Оставьте комментарий, если хотите, чтобы об этом была отдельная запись в блоге.

import pickle

filename = 'brands_classifier.sav'
loaded_model = pickle.load(open(filename, 'rb'))
loaded_model.predict([[29,18,26,0,2,1,0.9,0.0,0.07,1.0,0.03,0.62,3,3]])