Machine Learning, NLP

Использование текстовых и извлечённых числовых признаков в задаче классификации комментариев

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

Как выглядят данные?

Во-первых, посмотрим на имеющиеся тестовые и тренировочные данные (данные соревнования «Toxic comment classification challenge» на платформе kaggle.com). В тренировочных данных, в отличии от тестовых, имеются метки для классификации:

Рисунок 1 — Train data head

Из таблицы видно, что мы имеем в тренировочных данных 6 столбцов-меток («toxic», «severe_toxic», «obscene», «threat», «insult», «identity_hate»), где значение «1» свидетельствует о принадлежности комментария к классу, также присутствует столбец «comment_text», содержащий комментарий и столбец «id» – идентификатор комментария.

Тестовые данные не содержат меток классов, так как используются для отправки решения:

Рисунок 2 — Test data head

Извлечение признаков

Следующий этап – извлечение признаков из комментариев и проведение исследовательского анализа данных (EDA). Во-первых, посмотрим распределение типов комментариев в тренировочном наборе данных. Для этого был создан новый столбец «toxic_type», содержащий все классы, к которым относился комментарий:

Рисунок 3 — Топ-10 типов токсичных комментариев

Из таблицы видно, преобладающий тип – отсутствие каких-либо меток класса, и многие комментарии принадлежат больше чем к одному классу.

Также посмотрим, как распределено количество типов для каждого комментария:

Рисунок 4 — Количество встречающихся типов

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

Теперь перейдём к этапу извлечения признаков из текста, который часто называют features extraction. Я извлекла следующие признаки:

  • Длина комментария. Я предполагаю, что гневные комментарии скорее всего будут короткими;
  • Верхний регистр. В агрессивно-эмоциональных комментариях возможно в словах будет чаще встречаться верхний регистр;
  • Смайлики. При написании токсичного комментария вряд ли будут использоваться позитивно окрашенные смайлики ( :), 🙂 и т.д.), так же рассмотрим наличие грустных смайликов ( :(, 🙁 и т.д.);
  • Пунктуация. Вероятно, авторы негативных комментариев не придерживаются правил пунктуации, в большей степени они используют «!»;
  • Количество сторонних символов. Некоторые люди при написании оскорбительных слов часто используют символы @, $ и т.д.

Добавление признаков осуществляется следующим образом:

train_data[‘total_length’] = train_data[‘comment_text’].apply(len)
train_data[‘uppercase’] = train_data[‘comment_text’].apply(lambda comment: sum(1 for c in comment if c.isupper()))
train_data[‘exclamation_punction’] = train_data[‘comment_text’].apply(lambda comment: comment.count(‘!’))
train_data[‘num_punctuation’] = train_data[‘comment_text’].apply(lambda comment: comment.count(w) for w in ‘.,;:?’))
train_data[‘num_symbols’] = train_data[‘comment_text’].apply(lambda comment: sum(comment.count(w) for w in ‘*&$%’))
train_data[‘num_words’] = train_data[‘comment_text’].apply(lambda comment: len(comment.split()))
train_data[‘num_happy_smilies’] = train_data[‘comment_text’].apply(lambda comment: sum(comment.count(w) for w in (‘:-)’, ‘:)’, ‘;)’, ‘;-)’)))
train_data[‘num_sad_smilies’] = train_data[‘comment_text’].apply(lambda comment: sum(comment.count(w) for w in (‘:-(’, ‘:(’, ‘;(’, ‘;-(’)))

Исследовательский анализ данных

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

Рисунок 5 — Корреляция

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

Например, можно увидеть, что количество слов и длина текста сильно коррелированы между собой (значение 0.99), значит, какой-то признак из них можно убрать, я убрала количество слов. Также мы можем сделать еще несколько выводов: корреляция практически отсутствует между выделенными признаками и метками классов, самый мало коррелируемый признак – количество символов, а длина текста коррелирует с количеством символов пунктуации и количеством символов, приведённых к верхнему регистру.

Далее, построим несколько визуализаций для более детального понимания влияния признаков на метку класса. Для начала посмотрим, как распределены длины комментариев:

Рисунок 6 — Распределение длин комментариев (график интерактивный, но тут скриншот)

Как и предполагалось, для комментариев, которые были не отнесены к какому-либо классу (т.е. являются нормальными), длина намного больше, чем для помеченных комментариях. Из негативных комментариев самыми короткими являются угрозы (threat), а самыми длинными – токсичные (toxic).

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

Рисунок 7 — Средние значения пунктуации (график интерактивный, но тут скриншот)

Из рисунка видно, что мы получили три кластера.

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

Второй состоит из угроз (threat) и очень токсичных комментариев (severe toxic), для такой группы характерно обильное использование восклицательных знаков и на среднем уровне используются иные знаки пунктуации.

Третий кластер – токсичные (toxic), непристойные (obscene), оскорбления (insult) и ненавистные по отношению к некоторой личности (identity hate) имеют небольшое количество как пунктуационных знаков, так и восклицательных.

Добавим третью ось для большей наглядности – верхний регистр:

Рисунок 8 -Трёхмерное изображение (интерактивное, но тут скриншот)

Здесь видим аналогичную ситуацию – выделяется три кластера. Также отметим, что расстояние между элементами второго кластера больше, чем расстояние между элементами третьего кластера. Это можно увидеть и на двумерном графике:

Рисунок 9 — Верхний регистр и пунктуация (интерактивное, здесь скриншот)

Теперь рассмотрим на типы комментариев в разрезе верхнего регистра/количества сторонних символов:

Рисунок 10 — Верхний регистр и количество сторонних символов (интерактивное, здесь скриншот)

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

Таким образом, выделение новых признаков и их визуализация позволяет интерпретировать имеющиеся данные лучше и выше построенные визуализации можно резюмировать следующим образом:

  • Очень токсичные комментарии отделены от остальных;
  • Нормальные комментарии также стоят особняком;
  • Токсичные, непристойные и носящие оскорбительный характер комментарии очень близки друг к другу в разрезе рассмотренных характеристик.

Использование DataFrameMapper для объединения текстовых и числовых признаков

Теперь рассмотрим, как можно использовать вместе текстовые и числовые признаки в Logistic regression.

Во-первых, нужно выбрать модель для репрезентации текста в подходящей для алгоритмов машинного обучения форме. Я использовала tf-idf модель, так как она способна выделить специфичные слова и сделать менее значимыми частые слова (например, предлоги):

tvec = TfidfVectorizer(
sublinear_tf=True,
strip_accents=’unicode’,
analyzer=’word’,
token_pattern=r’\w{1,}’,
stop_words=’english’,
ngram_range=(1, 1),
max_features=10000
)

Итак, если мы хотим работать с предоставляемыми библиотекой Pandas dataframe и алгоритмами машинного обучения библиотеки Sklearn, можно использовать модуль Sklearn-pandas, служащий неким связующим между данными в формате dataframe и Sklearn-методами.

mapper = DataFrameMapper([
			([‘uppercase’], StandardScaler()),
			([‘exclamation_punctuation’], StandardScaler()),
			([‘num_punctuation’], StandardScaler()),
			([‘num_symbols’], StandardScaler()),
			([‘num_happy_smilies’], StandardScaler()),
			([‘num_sad_smilies’], StandardScaler()),
			([‘total_length’], StandardScaler())
		], df_out=True)

Сначала нужно создать DataFrameMapper как показано выше, он должен содержать названия столбцов, имеющих numeric features. Далее создаём матрицу признаков, которую далее будем передавать в Logistic regression для обучения:

x_train = np.round(mapper.fit_transform(numeric_features_train.copy()), 2).values
x_train_features = sparse.hstack((csr_matrix(x_train), train_texts))

Аналогичная последовательность действий производится также над набором данных для тестирования.

Вычислительный эксперимент

Для проведения multi-label classification построим цикл, который будет проходиться по всем категориям и оценивать качество классификации кросс-валидацией с параметрами cv=3 и scoring=’roc_auc’:

scores = []
class_names = [‘toxic’, ‘severe_toxic’, ‘obscene’, ‘threat’, ‘identity_hate’]
for class_name in class_names:
	train_target = train_data[class_name]
	classifier = LogisticRegression(C=0.1, solver= ‘sag’)
	cv_score = np.mean(cross_val_score(classifier, x_train_features, train_target, cv=3, scoring= ‘auc_roc’))
	scores.append(cv_score)
	print(‘CV score for class {} is {}’.format(class_name, cv_score))
	classifier.fit(train_features, train_target)

print(‘Total CV score is {}’.format(np.mean(scores)))

Результат работы:

Рисунок 11 — Результат работы

Лучше всего классификатор справился с непристойными комментариями, также неплохие результаты были получены для токсичных, оскорбительных комментариев, которые, как мы видели при проведении исследовательского анализа, часто встречались вместе. Скорее всего, это можно обосновать тем, что в непристойных комментариях есть специфичные слова, которые хорошо их отделяют от остальных. Хорошее значение на кросс-валидации комментариев, отнесенных к “toxic”, возможно, обусловлено тем, что эта категория является самой популярной (согласно рисунку 3). Также классификатор хорошо справился с определением очень токсичных комментариев, вероятно, потому что этот тип явно выражен по отношению к остальным, о чём говорилось при проведении исследовательского анализа данных.

Советуем почитать