Классификация текстов с помощью мешка слов. Руководство

В рамках одного из соревнований Kaggle участникам предлагается классифицировать тексты, в частности рецензии на кинофильмы. Это прекрасная возможность поэкспериментировать.

На сайте Kaggle есть руководство для этого соревнования, где рассмотрен популярный подход под названием «мешок слов» (bag-of-words) и технология Word2Vec. Руководство содержит базовые материалы и обеспечивает результаты, которые можно легко улучшить. Этим мы и займемся.

Валидация

Валидация – краеугольный камень машинного обучения. Это единственный рациональный способ оценить, насколько хорошо обобщает модель. Если исходный обучающий набор данных достаточно велик, его можно разделить на две части и использовать одну из них для обучения, другую — для валидации. Если же исходный обучающий набор данных мал, тогда не обойтись без кросс-валидации (cross-validation), более требовательной к ресурсам.

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

Создание набора данных для валидации

Первым делом модифицируем код из руководства Kaggle, реализовав валидацию. Для этого необходимо разделить исходный обучающий набор данных. Поскольку в нем присутствует 25 000 элементов, мы выделим из них 5 000 в качестве тестового набора для валидации, а 20 000 оставим в качестве обучающего набора. Одним из способов разделения исходного файла на две части является применение скрипта split.py из набора скриптов phraug2:

python split.py train.csv train_v.csv test_v.csv -p 0.8 -r dupa

Чтобы обеспечить воспроизводимость, в качестве инициализирующего значения (random seed) для генератора случайных чисел мы использовали произвольную строку «dupa». «Dupa» – это польское кодовое слово для подобных случаев. Дальнейшие результаты, представленные ниже, основаны на наборах данных, полученных при таком разделении.

Исходный обучающий набор данных достаточно мал, поэтому в качестве другого способа разделения, можно загрузить файл целиком в память и затем разделить его с помощью отличных инструментов, предоставляемых библиотекой scikit-learn:

from sklearn.cross_validation import train_test_split
train, test = train_test_split( data, train_size = 0.8, random_state = 44 )

В наших скриптах, размещенных на GitHub, для удобства реализуется именно этот механизм вместо первого способа. Обратите внимание, необходимо использовать индексы, потому что мы работаем с типом данных DataFrame из библиотеки Pandas, а не с массивами NumPy.

all_i = np.arange( len( data ))
train_i, test_i = train_test_split( all_i, train_size = 0.8, random_state = 44 )

train = data.ix[train_i]
test = data.ix[test_i]

Метод оценки результатов

Для оценки результатов в данном соревновании применяется показатель AUC (area under the ROC curve, площадь под ROC-кривой), для вычисления которого необходимы вероятности. По какой-то причине в руководстве Kaggle предсказываются только нули и единицы. Это легко исправить:

p = rf.predict_proba( test_x )
auc = AUC( test_y, p[:,1] )

Мы видим, что результат случайного леса (random forest) составляет примерно 91,9%.

Случайный лес для мешка слов? Нет.

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

Мы будем использовать логистическую регрессию (logistic regression) со значениями гиперпараметров по умолчанию.

В случае логистической регрессии показатель AUC составляет 92,8%, при этом обучение происходит значительно быстрее по сравнению со случайным лесом. Если выделить один ключевой момент из данной статьи, он будет следующим: используйте линейную модель при работе с разреженными данными большой размерности, такими как мешок слов.

TF-IDF

TFIDF (term frequencyinverse document frequency, частота слова – обратная частота документа) – это метод, который увеличивает веса слов, часто встречающихся в данном документе, и уменьшает веса слов, часто встречающихся во многих документах.

Векторизатор TfidfVectorizer и 20 000 признаков обеспечили нам результат 95,6%, что является значительным улучшением.

Стоп-слова и N-граммы

Автор руководства, размещенного на сайте Kaggle, счел необходимым исключить стоп-слова (stop words) из рецензий. Стоп-слова – это широко распространенные слова, такие как «this», «that», «and», «so», «on» («этот», «тот», «и», «поэтому», «на»). Правильное ли это решение? Мы не знаем – необходимо проверить, ведь для этого и существует валидация. Не исключая стоп-слова, мы получили результат 92,9% (без применения TF-IDF).

Существует еще одна серьезная причина не исключать стоп-слова: имеет смысл попробовать применить N-граммы (N-gram), а для этого необходимо оставить все слова на месте.

N-грамма – это последовательность из N слов. Например, биграммы состоят из двух слов: «cat ate», «ate my», «my precious», «precious homework» («кот съел», «съел мою», «мою драгоценную», «драгоценную работу»); триграммы состоят из трех слов: «cat ate my», «ate my homework», «my precious homework» («кот съел мою», «съел мою работу», «мою драгоценную работу»); 4-граммы состоят из четырех слов и т.д.

Почему N-граммы эффективны? Возьмем для примера фразу «movie not good» («фильм не хороший»). Очевидно, что она имеет негативную тональность, однако, если рассматривать каждое слово по отдельности, невозможно будет это определить. Кроме того, модель, вероятно, «выучит», что слово «good» («хороший») имеет положительную тональность, но в данном случае это не то, что нам нужно.

С другой стороны, с помощью биграмм модель, вероятно, «выучит», что выражение «not good» («не хороший») имеет негативную тональность.

Рассмотрим более сложный пример со страницы, посвященной анализу тональности текста, на сайте Стэнфордского университета:

«This movie was actually neither that funny, nor super witty». («На самом деле, этот фильм не был ни очень смешным, ни супер остроумным».)

В этом случае биграммы «that funny» («очень смешной») и «super witty» («супер остроумный») дадут неправильный результат. Здесь необходимы, по крайней мере, триграммы, чтобы правильно трактовать тональность выражений «neither that funny» («ни очень смешной») и «nor super witty» («ни супер остроумный»). Впрочем, подобные фразы встречаются не слишком часто, так что, если мы используем ограниченное количество признаков или регуляризацию, они могут и не оказать существенного влияния на модель. Это подталкивает нас к применению более сложной модели, такой как нейронная сеть, но тут мы уже отклоняемся от темы.

Если вычисление N-грамм кажется немного сложным, векторизаторы (vectorizer) из библиотеки scikit-learn могут сделать это автоматически. Такими же возможностями обладает и пакет Vowpal Wabbit, но в данном случае мы не будем его применять.

При использовании триграмм показатель AUC составил 95,9%.

Размерность

Каждое слово – это признак: встречается данное слово в документе или нет (0/1), или сколько раз оно встречается (целое число >= 0). Мы начали с исходной размерности, описанной в руководстве и составляющей 5 000. Такая размерность имеет смысл для случайного леса, который представляет собой классификатор с высокой нелинейностью и дисперсией, требующий, чтобы количество элементов в обучающей выборке значительно превышало размерность. Линейные модели менее требовательны в этом отношении, они могут работать, даже если объем обучающей выборки значительно меньше размерности.

Выяснилось, что если не ограничивать размерность, мы превышаем доступный нам объем памяти, даже при таком малом наборе данных. Мы смогли позволить себе примерно 40 000 признаков на машине с 12 ГБ оперативной памяти. Большее количество приводило к обмену с жестким диском.

Мы начали с 20 000 признаков. Логистическая регрессия показала результат 94,2% (без применения TF-IDF и N-грамм) по сравнению с 92,9% при 5 000 признаков. Большее количество признаков обеспечило еще более высокие результаты: 30 000 признаков – 96,0%, 40 000 признаков – 96,3% (с применением TF-IDF и N-грамм).

Чтобы решить проблему с памятью, можно использовать хеширующий векторизатор (hashing vectorizer). Но этот подход обеспечил в результате всего лишь 93,2% по сравнению с 96,3% без него, отчасти потому, что он не поддерживает TF-IDF.

Эпилог

В этой статье мы рассказали, как повысить эффективность классификации текстов, используя следующие подходы:

  • создание набора данных для валидации;

  • вычисление вероятностей для AUC;

  • замена случайного леса на линейную модель;

  • вычисление весов слов с помощью TF-IDF;

  • отказ от исключения стоп-слов;

  • применение биграмм или триграмм.

Оценка в открытой турнирной таблице достаточно точно соответствует оценке при валидации: обе составляют примерно 96,3%. На момент отправки на сервер этого результата было достаточно, чтобы попасть в топ 20 из 500 участников.

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

Напомним, что код для этой статьи доступен на GitHub.

По материалам: fastml.com

Добавить комментарий

Ваш e-mail не будет опубликован.

закрыть

Поделиться

Отправить на почту
закрыть

Вход

закрыть

Регистрация

+ =