🤖 Практическое руководство по NLP: изучаем классификацию текстов с помощью библиотеки fastText
Рассматриваем практическое применение supervised NLP модели fastText для обнаружения сарказма в новостных заголовках. Около 80% всей информации не структурировано, а текст является одним из наиболее распространенных типов неструктурированных данных. Из-за ее беспорядочной природы, анализ, понимание, организация и сортировка текстовой информации становятся сложными и трудоемкими задачами. Здесь на помощь приходят NLP и классификация текста. Классификация текстов – метод машинного обучения, используемый для их фрагментации на категории. Классификация текста является одной из фундаментальных задач в обработке естественного языка с широким применением: анализ сентиментов, маркировка тем, обнаружение спама, намерений клиентов и т.д. Обученные модели классификации текстов также нужны для исследовательских целей. Используя модели классификаторов, компании могут автоматически структурировать все виды текста, начиная с электронных писем, юридических документов, постов в социальных сетях, сообщений в чат-ботах, результатов опросов и т. д. Это позволяет экономить затраченное на анализ информации время, автоматизировать бизнес-процессы и принимать основанные на данных бизнес-решения. fastText – популярная библиотека с открытым исходным кодом для классификации текстов, которая была опубликована в 2015 году исследовательской лабораторией искусственного интеллекта Facebook. Компания также предоставляет модели: English word vectors (предварительно обучена английскому webcrawl и Википедии) и Multi-lingual word vectors (обученные модели для 157 различных языков), которые позволяют создать алгоритмы Supervised и Unsupervised learning для получения векторных представлений слов. Библиотеку можно использовать как инструмент командной строки или как пакет Python. В этой статье мы рассмотрим ее применение для классификации новостных заголовков. TIP для ознакомления с документацией библиотеки: Датасет представляет собой коллекцию заголовков новостных статей и их аннотацию как сарказм (статьи из новостного издания The Onion) и не сарказм (из HuffPost). Ссылка на данные: https://www.kaggle.com/rmisra/news-headlines-dataset-for-sarcasm-detection Приведем несколько примеров саркастических и не саркастических заголовков: Один из первых шагов для повышения производительности модели – применение простой предварительной обработки текста. Прежде чем приступить к построению классификатора, нам необходимо подготовить текст: привести все слова к нижнему регистру, удалить пунктуацию, специальные знаки и цифры. Для этого создадим функцию очистки и применим ее к переменной Прежде чем мы начнем обучение модели, нужно разделить данные так. Чаще всего для обучения модели используется 80% информации (в зависимости от объема данных размер выборки может варьироваться) и 20% – для тестирования (проверки точности). Далее нам нужно подготовить файлы в формате Для обучения модели необходимо задать fastText входной файл и ее имя: Результаты, хотя и не идеальные, выглядят многообещающими. Поиск наилучших гиперпараметров вручную может занять много времени. По умолчанию модель fastText во время обучения включает каждый обучающий пример только пять раз, что довольно мало, учитывая, что в нашем наборе всего 12 тыс. примеров. Количество просмотров каждого примера (также известное как количество эпох) может быть увеличено с помощью ручной оптимизации Как можно заметить, точность модели не возрасла. Еще один способ изменить скорость процесса – увеличить (или уменьшить) скорость обучения алгоритма. Это соответствует тому, насколько сильно меняется модель после обработки каждого примера. Скорость обучения равная 0 будет означать, что модель вообще не меняется и, следовательно, ничему не учится. Хорошие значения скорости обучения находятся в диапазоне Наконец, мы можем улучшить производительность модели, используя биграммы слов, а не просто юниграммы. Это особенно значимо для задач классификации, где важен порядок слов, например, анализ сентиментов, определение критики, сарказма и т. д. Для этого мы включим в модель аргумент Благодаря этой последовательности шагов мы смогли перейти от точности в 86%: Вы также можете адаптировать поиск гиперпараментров через оценку определенного лейбла, добавив аргумент Функция автоматической настройки fastText позволяет оптимизировать гиперпараметры для получения наивысшего показателя F1. Для этого необходимо включить модель аргумент Сохраним результаты модели и создадим функцию для классификации новых данных: fastText также способен сжимать модель, чтобы получить файл гораздо меньшего размера, жертвуя лишь небольшой производительностью за счет количественной оценки. Мы можем проверить результаты на новых данных, введя любое предложение: Метка результата с максимальной вероятностью равна Мы также можем симулировать новые данные и проверить модели на реальных заголовках. Для этого будет использовать News Aggregator Dataset из Kaggle: Применим функцию классификацию текста к новым заголовкам и создадим переменные с прогнозируемым лейблом и его вероятностью: Мы видим, что 28% заголовков были классифицированы как сарказм. В заключение следует отметить, что fastText не является одной из самых последних разработок классификации текстов (библиотека была опубликована в 2015 году). В то же время это хорошая основа для новичков: при выполнении NLP-классификации текстов любой сложности, модель имеет существенное преимущество из-за простоты ее использования, скорости обучения и автоматической настройки гиперпараметров. Подробнее с документацией fastText можно ознакомится по ссылке.Подготовка среды и данных
Загружаем необходимые библиотеки
import pandas as pd import fasttext from sklearn.model_selection import train_test_split import re from gensim.parsing.preprocessing import STOPWORDS from gensim.parsing.preprocessing import remove_stopwords pd.options.display.max_colwidth = 1000
help(fasttext.FastText)
Данные
Переменные
is_sarcastic
: 1 если заголовок с сарказмом, в противном случае 0headline
: заголовок новостной статьиarticle_link
: ссылка на оригинальную статью
# Загружаем данные df_headline = pd.read_json("Sarcasm_Headlines_Dataset.json", lines=True) # Проверяем количество переменных и наблюдений df_headline.shape (26709, 3) # Отобразим примеры заголовков df_headline.head(3)
article_link
headline
is_sarcastic
https://www.huffingtonpost.com/entry/versace-black-code_us_5861fbefe4b0de3a08f600d5
former versace store clerk sues over secret ‘black code’ for minority shoppers
0
https://www.huffingtonpost.com/entry/roseanne-revival-review_us_5ab3a497e4b054d118e04365the
the ‘roseanne’ revival catches up to our thorny political mood, for better and worse
0
https://local.theonion.com/mom-starting-to-fear-son-s-web-series-closest-thing-she-1819576697
mom starting to fear son’s web series closest thing she will have to grandchild
1
# Отобразим количество саркастических и несаркастических статей в датасете и их процентное соотношение df_headline.is_sarcastic.value_counts() 0 14985 1 11724 df_headline.is_sarcastic.value_counts(normalize=True) 0 0.561047 1 0.438953
df_headline[df_headline['is_sarcastic']==1].head(3)
article_link
headline
is_sarcastic
https://local.theonion.com/mom-starting-to-fear-son-s-web-series-closest-thing-she-1819576697
mom starting to fear son’s web series closest thing she will have to grandchild
1
https://politics.theonion.com/boehner-just-wants-wife-to-listen-not-come-up-with-alt-1819574302
boehner just wants wife to listen, not come up with alternative debt-reduction ideas
1
https://politics.theonion.com/top-snake-handler-leaves-sinking-huckabee-campaign-1819578231
top snake handler leaves sinking huckabee campaign
1
df_headline[df_headline['is_sarcastic']==0].head(3)
article_link
headline
is_sarcastic
https://www.huffingtonpost.com/entry/versace-black-code_us_5861fbefe4b0de3a08f600d5
former versace store clerk sues over secret ‘black code’ for minority shoppers
0
https://www.huffingtonpost.com/entry/roseanne-revival-review_us_5ab3a497e4b054d118e04365
the ‘roseanne’ revival catches up to our thorny political mood, for better and worse0
0
https://www.huffingtonpost.com/entry/jk-rowling-wishes-snape-happy-birthday_us_569117c4e4b0cad15e64fdcb
j.k. rowling wishes snape happy birthday in the most magical way
0
Подготовка данных
Предварительная обработка текста
headline
.
# Создадим функцию очистки текста def clean_text(text): text = text.lower() text = re.sub(r'[^sa-zA-Z0-9@[]]',' ',text) # Удаляет пунктцацию text = re.sub(r'w*d+w*', '', text) # Удаляет цифры text = re.sub('s{2,}', " ", text) # Удаляет ненужные пробелы return text # Применяем ее к заголовку df_headline['headline'] = df_headline['headline'].apply(clean_text)
Разделение данных на обучающие и тестовые
# Разделяем данные на обучающие и текстовые train, test = train_test_split(df_headline, test_size = 0.2)
Создание текстового файла
txt
. Формат файла по умолчанию должен включать __label__
<Label>
<Text>
. Мы можем использовать другой префикс вместо __label__
, соответствующим образом изменив параметр во время обучения. В нашем датасете __ label __0
подразумевает отсутствие сарказма, а __label __1
подразумевает сарказм.
# Создадим текстовые файля для обучения модели с лейблом и текстом with open('train.txt', 'w') as f: for each_text, each_label in zip(train['headline'], train['is_sarcastic']): f.writelines(f'__label__{each_label} {each_text}n') with open('test.txt', 'w') as f: for each_text, each_label in zip(test['headline'], test['is_sarcastic']): f.writelines(f'__label__{each_label} {each_text}n') # Отобразим, как теперь выглядят наши данные для обучения !head -n 10 train.txt __label__0 singapore airlines flight catches fire no casualties __label__1 area man wins conversation __label__0 stephen colbert explains the conspiracies against donald trump in nsfw diagram __label__1 turkey sandwich given locally relevant name __label__1 quiet riot speaks out against nation s poor metal health care
Построение модели
# Первая модель без оптимизации гиперпараметров model1 = fasttext.train_supervised('train.txt') # Создадим функцую для отображения результатов обучения модели def print_results(sample_size, precision, recall): precision = round(precision, 2) recall = round(recall, 2) print(f'{sample_size=}') print(f'{precision=}') print(f'{recall=}') # Применяем функцию print_results(*model1.test('test.txt')) sample_size=5342 precision=0.85 recall=0.85
Оптимизация гиперпараметров
epoch
:
# Вторая модель с количеством эпох равной 25 model2 = fasttext.train_supervised('train.txt', epoch=25) print_results(*model2.test('test.txt')) sample_size=5342 precision=0.83 recall=0.83
0.1 – 1.0
. Мы также можем вручную оптимизировать этот гиперпараметр с помощью аргумента lr
:
# Третья модель с количеством эпох равной 10 и скоростью обучния равной 1 model3 = fasttext.train_supervised('train.txt', epoch=10, lr=1.0) print_results(*model3.test('test.txt')) sample_size=5342 precision=0.83 recall=0.83
wordNgrams
равный 2.
model4 = fasttext.train_supervised('train.txt', epoch=10, lr=1.0, wordNgrams =2) print_results(*model4.test('test.txt')) sample_size=5342 precision=0.86 recall=0.86
epoch
, стандартный диапазон [5 – 50]
) ; lr
, стандартный диапазон [0,1 – 1,0]
) ;wordNgrams
, стандартный диапазон [1-5]
).autotuneMetric
:
model5 = fasttext.train_supervised('train.txt', autotuneValidationFile='test.txt') print_results(*model5.test('test.txt')) sample_size=5342 precision=0.87 recall=0.87
autotuneValidationFile
и тестовый датасет:
model6 = fasttext.train_supervised('train.txt', autotuneValidationFile='test.txt', autotuneMetric="f1:__label__1") print_results(*model6.test('test.txt')) sample_size=5342 precision=0.87 recall=0.87
# Сохраним модель с оптимизированными гиперпараметрами и самой высокой точностью model6.save_model('optimized.model')
model.quantize(input='train.txt', retrain=True)
Результаты
# Загружаем, сохраненную ранее модель model = fasttext.load_model("optimized.model") # Пример классификации одного заголовка model.predict("Fed official says weak data caused by weather, should not slow taper") (('__label__0',), array([0.99874038]))
__label__0
. Это означает, что заголовок не является саркастическим. В model.predict()
значение k обозначает количество лейблов, которые вы хотите показать вместе с их показателями вероятности. Поскольку мы использовали активацию softmax (по умолчанию в fastText), сумма вероятностей двух меток равна 1.
# Загружаем данные df_headline_test = pd.read_csv('uci-news-aggregator.csv') # Отобразим заголовки df_headline_test.TITLE.head(3) 0 Fed official says weak data caused by weather, should not slow taper 1 Fed's Charles Plosser sees high bar for change in pace of tapering 2 US open: Stocks fall after Fed official hints at accelerated tapering 3 Fed risks falling 'behind the curve', Charles Plosser says 4 Fed's Plosser: Nasty Weather Has Curbed Job Growth
# Подготовим новые данные для классификации df_headline_test['TITLE'] = df_headline_test['TITLE'].apply(clean_text) # Создадим функцию для классификации текста def predict_sarcasm(text): return model.predict(text, k=1) # Трансформируем переменные в удобный формат df_headline_test['predict_score'] = df_headline_test.TITLE.apply(predict_sarcasm) df_headline_test['predict_score'] = df_headline_test['predict_score'].astype(str) df_headline_test[['label','probability']] = df_headline_test.predict_score.str.split(" ",expand=True) df_headline_test['label'] = df_headline_test['label'].str.replace("(", '') df_headline_test['label'] = df_headline_test['label'].str.replace(")", '') df_headline_test['label'] = df_headline_test['label'].str.replace("__", ' ') df_headline_test['label'] = df_headline_test['label'].str.replace(",", '') df_headline_test['label'] = df_headline_test['label'].str.replace("'", '') df_headline_test['label'] = df_headline_test['label'].str.replace("label", '') df_headline_test['probability'] = df_headline_test['probability'].str.replace("array", '') df_headline_test['probability'] = df_headline_test['probability'].str.replace("(", '') df_headline_test['probability'] = df_headline_test['probability'].str.replace(")", '') df_headline_test['probability'] = df_headline_test['probability'].str.replace("[", '') df_headline_test['probability'] = df_headline_test['probability'].str.replace("]", '') # Удаляем ненужную переменную df_headline_test = df_headline_test.drop(columns=['predict_score']) # Отобразим количество спрогнозированых саркастических и несаркастических заголовков df_headline_test.label.value_counts(normalize=True) 0 0.710827 1 0.289173
Заключение
- 5 views
- 0 Comment
Свежие комментарии