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

Начало работы

Я начал с загрузки набора данных из Kaggle в Google Colab.

Затем я прочитал DataFrame и проверил в нем нулевые значения. В текстовых статьях 7 нулевых значений, 122 в заголовке и 503 у автора из 20800 строк, я решил отбросить строки. Для тестовых данных я заполнил их бланком.

Кроме того, я также проверяю распространение «фейковых» и «подлинных» новостей в наборе данных. Обычно я устанавливаю rcParams для всех графиков в записной книжке при импорте matplotlib.

Соотношение подлинных и фальшивых новостей меняется с 1: 1 до 4: 5.

Затем я решил посмотреть длину статьи, как показано ниже -

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

Видно, что они начинаются с 0, что вызывает беспокойство. На самом деле он начинается с 1, когда я использовал .describe (), чтобы увидеть числа. Я взглянул на эти тексты и обнаружил, что они пустые. Очевидный ответ на это - нулевая длина полосы и капли. Я проверил, что общее количество текстов нулевой длины - 74.

Я решил начать сначала. Итак, я бы заполнил все nans пробелом и затем удалил их, затем удалил тексты нулевой длины, и это должно быть хорошо для начала предварительной обработки. Ниже приведен новый код, который существенно обрабатывает пропущенные значения. Окончательная форма данных - (20684, 6), то есть они содержат 20684 строки, всего на 116 меньше, чем 20800.

Так выяснилось, что после этого появляется больше текстов, длина которых составляет однозначную цифру или всего 10. Они больше походили на комментарии, чем на настоящие тексты. Я оставлю их пока как есть и перейду к следующему шагу.

Предварительная обработка текста

Итак, прежде чем я начал с предварительной обработки текста, я действительно посмотрел на совпадающее количество авторов, у которых есть поддельные и подлинные статьи. Другими словами, будет ли информация автора хоть как-то полезна? Выяснилось, что существует 3838 авторов, из которых 2225 - настоящие, а 1618 - фейковые. Из них 5 авторов являются авторами как подлинных, так и фейковых новостей.

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

Как только это будет сделано, выполняется обычная токенизация слова с последующим удалением стоп-слова.

Текстовый анализ

Теперь, когда данные готовы, я собираюсь посмотреть на часто используемые слова с помощью wordcloud. Для этого я сначала объединил все токенизированные тексты в строки в отдельных столбцах, поскольку они будут использоваться позже при обучении модели.

Затем для каждой метки создайте строку всех текстов и создайте облако слов, как показано ниже:

В облаке фейковых новостей частота одних слов заметно выше, чем других. В облаке слов настоящих новостей используется сочетание шрифтов разных размеров. Напротив, в наборе данных фейковых новостей более мелкие тексты находятся на заднем плане, а некоторые слова используются гораздо чаще. В облаке фальшивых новостей меньше слов среднего размера или, другими словами, наблюдается разрыв в постепенно уменьшающейся частоте появления. Частота либо высокая, либо низкая.

Стилометрический анализ

Стилометрический анализ часто называют анализом авторского стиля. Я рассмотрю несколько стилометрических характеристик, таких как количество предложений в статье, среднее количество слов в предложении в статье, средняя длина слов в статье и количество тегов POS.

Количество предложений в статье

Чтобы получить это, мне нужен исходный набор данных, поскольку я потерял информацию о предложении в train_df. Итак, я сохранил копию реальных данных в orginal_train_df, которую я использовал для преобразования предложений в последовательности.

Затем я посмотрел на количество предложений по каждой целевой категории следующим образом:

Очевидно, что у фейковых статей много выбросов, но в 75% фальшивых статей количество предложений ниже, чем у 50% настоящих новостных статей.

Среднее количество слов в предложении в статье

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

Видно, что в среднем поддельные статьи более многословны, чем подлинные.

Средняя длина слова в статье

Это средняя длина слова в одной статье. На диаграмме видно, что средняя длина слова выше в поддельных статьях.

Количество тегов POS

Затем я попытался изучить комбинации частей речи (POS) в статьях Fake vs Genuine. Я только сохранил POS слов в списке во время итерации по каждой статье, поместил соответствующее количество POS в один DataFrame и использовал гистограмму, чтобы показать процентную комбинацию тегов POS в фальшивых и новостных статьях. Существительные в обеих статьях стоят намного выше. В целом четкой закономерности нет, за исключением того, что процент глаголов в прошедшем времени в фейковых новостях вдвое меньше, чем в настоящих. Кроме того, все остальные типы POS практически не уступают поддельным и подлинным товарам.

Классификация текста с использованием машинного обучения

Tf-idf и векторизатор подсчета

После завершения анализа я сначала выбрал обычный способ использования Count Vectorizer и назвал частоту-обратную частоту документа или Tf-idf. Векторизатор подсчета, настроенный в коде, также генерирует биграммы. Подсчет их вхождений получается в форме матрицы с помощью CountVectorizer (), и эта матрица подсчета слов затем преобразуется в нормированное представление частоты термов (tf-idf). Здесь я использовал smooth = False, чтобы избежать ошибки нулевого деления. Предоставляя smooth = False, я в основном добавляю единицу к частоте документа, поскольку это знаменатель в формуле для расчета idf, как показано ниже -

idf(t) = log [ n / (df(t) + 1) ]

Сравнительный анализ с конфигурациями по умолчанию

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

Я использовал логистическую регрессию, полиномиальный наивный байесовский метод, деревья решений, случайный лес, градиентное ускорение и классификаторы Ada Boost. Точность MultinomialNB - лучшая среди всех, но f1-score дает сбой из-за плохой оценки отзыва. Фактически, уровень отзыва - самый плохой (68%). Лучшими моделями по результатам были логистическая регрессия и AdaBoost, результаты которых аналогичны. Я выбрал логистическую регрессию, чтобы сэкономить время на обучение.

GridSearchCV для настройки классификатора логистической регрессии

Итак, пора настроить выбранный мной классификатор. Я начал с более широкого диапазона для max_iter и C. Затем использовал GirdSearchCV с cv = r, т.е. 5 раз для перекрестной проверки, поскольку распределение меток достаточно распределено. Я использовал показатель f1 для подсчета очков и использовал refit, чтобы вернуть обученную модель с лучшим показателем f1.

Модель с наилучшим результатом имела точность 97,62% и показатель f1 97,60%. В обоих случаях мы достигли улучшения на 4%. Теперь я заметил, что лучшее значение max_iter было 100, что было нижней границей диапазона, а для C оно также было 100, но это была верхняя граница диапазона. Итак, чтобы обеспечить поиск параметров, я использовал max_iter = 50, 70, 100 и C = 75, 100, 125. Было незначительное улучшение с max_iter = 100 и C = 125. Итак, я решил оставить это постоянным и увеличил поиск параметров для C со 120 до 150 с размером шага 10. Все показатели производительности для этого прогона были равны результатам стартовой сетки. Однако значение C = 140 для этого прогона.

В последний раз я провел поиск по сетке на max_iter = 100 и C = [100, 125, 140], где C имел лучшие параметры из всех прогонов. Лучшими были max_iter = 100 и C = 140, которые я в итоге сохранил как лучшую модель.

Одна из потенциальных будущих работ здесь - это тестирование с GradientBoost и AdaBoost Classifier, поскольку их производительность также была хорошей. В некоторых случаях производительность после настройки может быть намного лучше, но в интересах экономии времени я бы сделал здесь вывод, так как логистическая регрессия является наиболее производительной моделью с max_iter = 100 и C = 140.

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

Классификация текста с использованием GloVe и LSTM

Подготовка данных

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

Затем я использовал класс Tokenizer Keras API для токенизации текстов и заменил токен вне словарного запаса на oov_token = ‹OOV›, который фактически создает словарный индекс на основе частоты слов. Затем я помещаю токенизатор в тексты и преобразовываю их в последовательности целых чисел, которые используют словарный индекс, созданный путем подгонки токенизатора. Наконец, поскольку последовательности могут иметь разную длину, я использовал pad_sequences, чтобы дополнить их нулями в конце, используя padding = post. Следовательно, ожидается, что каждая из последовательностей будет иметь длину 40 в соответствии с кодом. Наконец, я разделил их на обучающие и тестовые наборы.

Модель двоичной классификации

Чтобы создать модель для классификации текста, я начал с простейшей формы структуры модели бинарной классификации, где первый слой представляет собой слой Встраивание с ожидаемым встраиванием текстов размером 6000 слов (заданных в vocab_size), каждая последовательность длиной 40 (таким образом, input_length = max_length) и дает на выходе 40 векторов 10 измерений для каждой входной последовательности. Затем я использовал слой Flatten, чтобы сгладить матрицу формы (40, 10) в единый массив фигур (400,). Затем этот массив пропускали через слой Dense для получения одномерного вывода и использовали сигмовидную функцию активации для создания двоичных классификаций. Сначала я подумал о том, чтобы больше поэкспериментировать с этой моделью, поэтому создал для нее функцию, и мне также нравится группирование слоев в функцию в качестве практики. На самом деле это не нужно для этой работы. Наконец, я скомпилировал модель, используя точность и отзыв для показателей, которые нужно отслеживать во время обучения и проверки.

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

Пришло время подобрать модель!

Поскольку я использовал точность и отзыв, а также потери, я также могу отследить точность и вспомнить значения здесь. Как показано на графике ниже, потери при проверке были самыми низкими в 6-ю эпоху, а затем потери были либо неизменными, либо увеличивались. Таким образом, лучшая модель была сохранена после 6-й эпохи обучения. Очевидно, что модель переоснащалась с улучшением потерь при обучении, в то время как потери при проверке увеличивались после 6-й эпохи.

Ниже приведен код, который я использовал для построения графика потерь, точности и отзыва при обучении и проверке. Я использовал max (history.epoch) + 2 в функции диапазона, так как history.epoch начинается с 0. Следовательно, для 20 эпох максимум будет 19, а диапазон будет генерировать список от 1 до 18 для max (history.epoch ).

Эта модель имела значение точности 96,6% и показатель f1 96,6%. Я также проверил производительность этой модели на тестовых данных Kaggle, и она была неплохой, но не лучше, чем логистическая регрессия, которую я тренировал ранее.

LSTM

Уф! Теперь давайте подгоним модель LSTM к текстовым данным. Первый и последний слой одинаковы, поскольку вход и выход одинаковы. В промежутке я использовал слой Dropout, чтобы отфильтровать 30% единиц, а затем перейти к слою LSTM из 100 единиц. Долгосрочная краткосрочная память (LSTM) - это особый вид RNN, способный изучать долгосрочные зависимости. Их особенность заключается в запоминании информации на более длительный период времени. После использования LSTM я использовал еще один слой Dropout, затем полностью связанный слой с 64 скрытыми блоками, затем еще один слой Dropout и, наконец, еще один полностью связанный слой одного блока с функцией активации сигмоид для двоичной классификации.

После этого я выполнил тот же процесс, описанный в предыдущем разделе, чтобы скомпилировать, использовать обратный вызов и подогнать под модель. Я предоставил 20. Но в этом случае модель обучалась только 16 эпох, потому что для 15 последовательных итераций после первой эпохи не было улучшения в потерях проверки. Это также видно из приведенного ниже сюжета. Потери при проверке только увеличивались, в то время как потери в обучении снижались из-за переобучения. Вспомните настройки обратного вызова, в которых я закодировал модель, чтобы дождаться улучшения потерь при проверке в течение 15 последовательных эпох, прежде чем останавливаться.

В этой модели не было значительных улучшений, хотя есть потенциальные улучшения, которые можно было бы внести в эту модель. Он имеет точность 96,1% и показатель f1 96,14%.

Использование предварительно обученного встраивания слов - GloVe

Теперь мы также можем использовать предварительно обученные вложения слов, такие как GloVe. GloVe - это алгоритм обучения без учителя для получения векторных представлений слов. Обучение выполняется на агрегированной глобальной статистике совместной встречаемости слова и слова из корпуса, и полученные в результате представления демонстрируют интересные линейные подструктуры векторного пространства слов. [4]

Я использовал тот, который был обучен на 6 миллиардах токенов с 400k словарным запасом, представленным в 300-мерном векторном формате.

В следующем коде у меня есть код для загрузки GloVe в Google Colab, поскольку я частично работал над Colab.

Здесь я описал, как загрузить файл с вашего локального. Скачайте вложение слова отсюда.

Затем наша цель - найти токены в данных фейковых новостей во встраивании GloVe и получить соответствующие веса.

Простая модель с перчаткой

Теперь, когда у меня есть встраивание GloVe для наших обучающих данных, я использовал слой Embedding с output_dim = 300, который является формой векторного представления GloVe. Кроме того, я использовал trainable = False, поскольку я использую предварительно натренированные веса, мне не следует обновлять их во время тренировки. Они поддерживают отношения с другими словами, поэтому лучше не беспокоить их.

Наконец, с помощью того же процесса, который я использовал ранее, я обучил модель с 50 эпохами. Однако, поскольку после 3-й эпохи улучшения не произошло, модель перестала тренироваться после 18-й эпохи. Оценки были ниже, чем у двух предыдущих моделей. Точность и оценка f1 составили ~ 93%.

Перчатка с LSTM

И… наконец, я использовал встраивание GloVe для обучения модели LSTM, которую использовал ранее, чтобы добиться лучших результатов. Полный код ниже -

Опять же, я использовал 50 эпох, и модель не улучшилась после третьей эпохи. Поэтому тренировочный процесс остановился после 18-й эпохи. Точность и показатель f1 повысились до 96,5%, что близко к первой модели Keras.

Итак, я попробовал прогнозы для этой модели на тестовых данных Kaggle, и вот мой результат -

Заключение

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

использованная литература

  1. Https://faroit.com/keras-docs/1.0.1/getting-started/sequential-model-guide/
  2. Https://colah.github.io/posts/2015-08-Understanding-LSTMs/
  3. Https://machinelearningmaster.com/use-word-embedding-layers-deep-learning-keras/
  4. Https://nlp.stanford.edu/projects/glove/

Полный код здесь.

Спасибо за посещение!

Мои ссылки: Средний | LinkedIn | GitHub