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

Обработка естественного языка (NLP) - это огромная и постоянно растущая область с бесчисленными приложениями, такими как анализ тональности, распознавание именованных сущностей (NER), классификация текста и многое другое.

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

Мы будем использовать Набор данных категорий новостей от Kaggle. Используемое ядро ​​доступно здесь.

Давайте прыгнем прямо в игру!

Во-первых, импорт библиотек…

Набор инструментов для естественного языка (NLTK) является основой НЛП в Python. Он предоставляет множество функций обработки текста и корпусов, чтобы упростить работу любого специалиста по данным! Официальную документацию можно найти здесь.

CountVectorizer преобразует корпус в нечто, называемое мешком слов (BoW). Это один из самых простых способов представления текстовых данных для алгоритмов машинного обучения. По сути, он объединяет все слова в корпусе и создает матрицу, содержащую подсчет каждого слова в каждом документе (или, в нашем случае, в каждой новости) корпуса. Пример из официальной документации:

Здесь vectorizer.get_feature_names () дает нам набор слов, то есть все отдельные слова в корпусе. Матрицу можно представить как:

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

Важным параметром функции CountVectorizer () является ngram_range. Самым простым определением «n-граммов» будет последовательность слов «n». Например, биграмма означает последовательность из 2-х слов. ngram_range указывает границу этого диапазона, который будет извлечен из корпуса. Например, с ngram_range (1,2) мы извлечем все uni и bi-граммы.

Вот как будет токенизироваться предложение «Это первый документ», если мы используем диапазон ngram (1,2):
'This', 'is', 'the', 'first', 'document ',' Это ',' это ',' первый ',' первый документ '.

Преимущество использования более высоких диапазонов состоит в том, что они помогают модели учиться на последовательности текста и тем самым повышают точность модели. В противном случае эта информация была бы потеряна, если бы использовались только униграммы. Компромисс - это увеличение пространства функций и, следовательно, требуемых времени и вычислительной мощности. Обратите внимание, что предложение «Это первый документ» будет сокращено до 5 токенов с униграммами, но 5 + 4 = 9 с биграммами и 5 + 4 + 3 = 12 с триграммами. Значение ngram_range больше 3 используется редко.

Затем мы загружаем набор данных в фреймворк pandas:

Некоторый исследовательский анализ данных (EDA) по данным:

df.head()

Столбец «категория» будет нашим целевым столбцом, и на данный момент мы будем использовать только столбцы «заголовок» и «короткое_описание» в качестве наших функций. Df.info ()

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 200853 entries, 0 to 200852
Data columns (total 6 columns):
category             200853 non-null object
headline             200853 non-null object
authors              200853 non-null object
link                 200853 non-null object
short_description    200853 non-null object
date                 200853 non-null datetime64[ns]
dtypes: datetime64[ns](1), object(5)
memory usage: 9.2+ MB

В этом наборе данных нет NULL, и это хорошо. Однако это не так часто случается с наборами данных реального мира, и NULL нужно будет обрабатывать как часть предварительной обработки, отбрасывая строки NULL или заменяя их пробелом (‘’).

Теперь давайте посмотрим, какие категории присутствуют в наборе данных ...

labels = list(df.category.unique())
labels.sort()
print(labels)
['ARTS', 'ARTS & CULTURE', 'BLACK VOICES', 'BUSINESS', 'COLLEGE', 'COMEDY', 'CRIME', 'CULTURE & ARTS', 'DIVORCE', 'EDUCATION', 'ENTERTAINMENT', 'ENVIRONMENT', 'FIFTY', 'FOOD & DRINK', 'GOOD NEWS', 'GREEN', 'HEALTHY LIVING', 'HOME & LIVING', 'IMPACT', 'LATINO VOICES', 'MEDIA', 'MONEY', 'PARENTING', 'PARENTS', 'POLITICS', 'QUEER VOICES', 'RELIGION', 'SCIENCE', 'SPORTS', 'STYLE', 'STYLE & BEAUTY', 'TASTE', 'TECH', 'THE WORLDPOST', 'TRAVEL', 'WEDDINGS', 'WEIRD NEWS', 'WELLNESS', 'WOMEN', 'WORLD NEWS', 'WORLDPOST']

Мы видим, что есть несколько категорий, которые можно объединить, например «ИСКУССТВО», «ИСКУССТВО и КУЛЬТУРА» и «КУЛЬТУРА И ИСКУССТВО». Давайте сделаем это:

Так выглядит лучше. Мы уменьшили количество меток с 41 до 36. Также мы видим, что набор данных довольно несбалансирован. У нас около 35000 новостей ПОЛИТИКИ, но меньше 1000 новостей ОБРАЗОВАНИЯ (в значительной степени суммирует текущее положение дел tbh: p). Обычно нам нужен сбалансированный набор данных для обучения нашей модели, но большинство наборов данных в реальном мире почти никогда не будут сбалансированы. Существуют методы увеличения и выборки, позволяющие сбалансировать набор данных, но они не рассматриваются в данной статье.

Теперь о предварительной обработке, которая, безусловно, является самым важным этапом!

Это стандартная пользовательская функция предварительной обработки текста (UDF), которую я использую. Давайте рассмотрим это подробно.

lower = col.apply(str.lower)

Это преобразует корпус в нижний регистр, потому что в противном случае CountVectorizer будет рассматривать «hello», «HELLO» и «hElLo» как разные слова, что не является хорошей идеей.

Это удаляет HTML-теги из корпуса. Это очень важно, если корпус взят с веб-страницы. Библиотека BeautifulSoup предоставляет более изощренный способ сделать это. Вы можете проверить это здесь".

Stemming ‘- это процесс создания морфологических вариантов корня / основного слова». Алгоритм выделения корня сокращает слова шоколадные конфеты, шоколадный, шоколадный до корневого слова, шоколад и поиск, извлеченный, извлекает сокращаются до основного слова извлекать. Построение работает путем удаления завершающих символов слова, чтобы попытаться добраться до корневого слова. В результате корневое слово может не быть словарным. Основным преимуществом стемминга является сокращение пространства признаков, то есть уменьшение количества отдельных слов в корпусе, по которым модель может обучаться. Еще один способ добраться до корневого слова - Лемматизация. В отличие от стемминга, лемматизация следует словарному подходу, так что слова чаще всего сводятся к их реальным словарным корням. Компромисс здесь - скорость обработки. Узнайте больше о стемминге и лемматизации здесь.

Стоп-слова - это общеупотребительные слова, которые обычно не придают данных большого значения. Удаление игнорируемых слов из корпуса, поскольку это значительно уменьшает размер пространства функций. Однако игнорируемые слова нельзя использовать вслепую. Некоторые слова, которые находятся в корпусе NLTK стоп-слов, могут иметь значение в наборе данных. Например, вы не захотите удалять слово «не» (которое является стоп-словом NLTK) из корпуса, в котором вы проводите анализ тональности. Это приведет к тому, что такие предложения, как "Это хороший фильм" и "Это не хороший фильм", будут иметь одно и то же значение.

В нашем случае удаление игнорируемых слов улучшило производительность модели, поэтому мы продолжим это делать.

rem_lngth1 = rem_num.apply(lambda x: re.sub(r'[^\w\s]',' ',x))

Здесь мы удаляем все слова длиной 1, поскольку они обычно не добавляют смысла в корпус. Такие слова, как «а», будут удалены. Вы можете спросить, какие еще слова имеют длину 1? Помните, раньше мы заменяли пунктуацию пробелом? Это преобразуется как «ядро Джона Доу» в «ядро Джона Доу». Буква "s" здесь не добавляет никакого значения. Теперь, когда мы удалим слова длиной 1, у нас останется «ядро Джона Доу». Так что, если для вашего ядра не важно владение, это хорошо.

Мы делаем это с помощью регулярных выражений (regex) в модуле python re. Регулярное выражение широко используется в НЛП для различных задач, таких как извлечение информации (адреса электронной почты, номера телефонов, почтовые индексы и т. Д.), Очистка данных и т. Д. Официальная документация Python по регулярным выражениям - отличное место, чтобы познакомиться с выражениями регулярных выражений. .

Это может быть немного сложно понять. Давайте посмотрим на это глубже.

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

counts = pd.Series(''.join(df.short_description).split()).value_counts()
counts
the                166126
to                 111620
of                  95175
a                   94604
and                 89678
                    ...  
catch!"                 1
big-day                 1
incarcerates            1
323-square-foot         1
co-trustee,             1
Length: 208227, dtype: int64

Это количество раз, когда все слова встречаются в нашем наборе данных. В нашем наборе данных было 208227 различных слов, поэтому при h_pct 1.0 будет удален верхний 1% наиболее часто встречающихся слов в корпусе.

high_freq = counts[:int(pd.Series(''.join(df.short_description).split()).count()*1/100)]
high_freq
the            166126
to             111620
of              95175
a               94604
and             89678
                ...  
butternut           5
NGO                 5
Mary,               5
songwriter,         5
distracted,         5
Length: 39624, dtype: int64

Это самые частые слова, которые будут удалены из набора данных.

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

low_freq = counts[:-int(pd.Series(''.join(df.short_description).split()).count()*1/100):-1]
low_freq
co-trustee,        1
323-square-foot    1
incarcerates       1
big-day            1
catch!"            1
                  ..
Brie.              1
non-plant          1
fetus?             1
Techtopus”         1
Look).             1
Length: 39623, dtype: int64

Это верхний 1% наименее частых слов. Все эти слова встречаются в словаре только один раз, поэтому не имеют особого значения и могут быть удалены.

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

Проверка оптимальной комбинации h_pct и l_pct

Это находит оптимальные значения h_pct и l_pct. Мы начинаем с целочисленных значений от 0 до 10 и, основываясь на результатах, можем дополнительно настроить проценты с шагом 0,5%. Имейте в виду, что на это уходит очень много времени. Модель будет обучена i * j раз, где i - количество значений h_pct, а j - количество значений l_pct. Итак, для значений h_pct и l_pct от 0 до 10 (оба включительно) моя модель была обучена в общей сложности 121 раз.

Для меня первая итерация вернула значения 0,0 и 1,0 для h_pct и l_pct соответственно, ниже приведены результаты при работе со значениями от 0,0 до 0,5% для h_pct и от 0,5 до 1,5% для l_pct:

SVC max: 63.79560061555173%, pct:0.0|1.0

Мы видим, что оптимальные значения по-прежнему равны 0,0 и 1,0 для h_pct и l_pct соответственно. Мы будем придерживаться этих ценностей.

df.loc[df.short_description.str.len()==df.short_description.str.len().max()]

df.loc[58142]['short_description']
'This week the nation watched as the #NeverTrump movement folded faster than one of the presumptive nominee\'s beachfront developments. As many tried to explain away Trump\'s reckless, racist extremism, a few put principle over party. The wife of former Republican Senator Bob Bennett, who died on May 4, revealed that her husband spent his dying hours reaching out to Muslims. "He would go to people with the hijab [on] and tell them he was glad they were in America," she told the Daily Beast. "He wanted to apologize on behalf of the Republican Party." In the U.K., Prime Minister David Cameron called Trump\'s proposal to ban Muslims from entering the U.S., "divisive, stupid and wrong." Trump\'s reply was that he didn\'t think he and Cameron would "have a very good relationship." The press is also doing its part to whitewash extremism. The New York Times called Trump\'s racism "a reductive approach to ethnicity," and said Trump\'s attitude toward women is "complex" and "defies simple categorization," as if sexism is suddenly as complicated as string theory. Not everybody\'s going along. Bob Garfield, co-host of "On the Media," warned the press of the danger of normalizing Trump. "Every interview with Donald Trump, every single one should hold him accountable for bigotry, incitement, juvenile conduct and blithe contempt for the Constitution," he said. "The voters will do what the voters will do, but it must not be, cannot be because the press did not do enough."'

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

Построение модели

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

Давайте разберем компоненты:

df['short_description_processed'] = preprocessing(df['short_description'],h_pct,l_pct)
df['concatenated'] = df['headline'] + '\n' + df['short_description_processed']
df['concat_processed'] = preprocessing(df['concatenated'],0,0)

Сначала мы запускаем функцию предварительной обработки в столбце 'short_description' со значениями h_pct и l_pct и сохраняем результат в 'short_description_processed'.
Затем мы добавляем 'заголовок' к этому столбцу и сохраняем результат в 'объединенном '.
Наконец, мы снова запускаем функцию предварительной обработки для «конкатенированного», но на этот раз без удаления более или менее частых слов, и сохраняем результат в «concat_processed». Не удалять слова из этого набора - это способ увеличить количество слов, встречающихся в заголовке, по сравнению с теми, которые встречаются в рассказе.

X = df['concat_processed']
y = df['category']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42, stratify=y) 
    
bow_xtrain = bow.fit_transform(X_train)
bow_xtest = bow.transform(X_test)

Мы используем «concat_processed» в качестве столбца переменных и «категорию» в качестве цели.

Затем мы генерируем Bag-of-Words для поезда и тестового корпуса соответственно, используя объект Bow в CountVectorizer. Как правило, CountVectorizer подбирается и преобразуется в наборе поездов, но преобразуется только в тестовом наборе. Это сделано для того, чтобы модель ничего не узнала из набора тестов.

model.fit(bow_xtrain,y_train)
preds = model.predict(bow_xtest)

Модель обучается на обучающем BoW, и для тестового BoW создаются прогнозы.

Собираем вместе

Запуск функции Prep_fit_pred на фрейме данных с 0 и 1 в качестве h_pct и l_pct (как было найдено ранее) и с модулем LinearSVC () с подробным значением True.

preds_abc, acc_abc, abc = prep_fit_pred(df, 0, 1, LinearSVC(), verbose=True)
Number of words in corpus before processing: 3985816
Number of words in corpus after processing: 2192635 (55.0%)
Number of words in final corpus: 3498319 (88.0%)
Raw story:
This week the nation watched as the #NeverTrump movement folded faster than one of the presumptive nominee's beachfront developments. As many tried to explain away Trump's reckless, racist extremism, a few put principle over party. The wife of former Republican Senator Bob Bennett, who died on May 4, revealed that her husband spent his dying hours reaching out to Muslims. "He would go to people with the hijab [on] and tell them he was glad they were in America," she told the Daily Beast. "He wanted to apologize on behalf of the Republican Party." In the U.K., Prime Minister David Cameron called Trump's proposal to ban Muslims from entering the U.S., "divisive, stupid and wrong." Trump's reply was that he didn't think he and Cameron would "have a very good relationship." The press is also doing its part to whitewash extremism. The New York Times called Trump's racism "a reductive approach to ethnicity," and said Trump's attitude toward women is "complex" and "defies simple categorization," as if sexism is suddenly as complicated as string theory. Not everybody's going along. Bob Garfield, co-host of "On the Media," warned the press of the danger of normalizing Trump. "Every interview with Donald Trump, every single one should hold him accountable for bigotry, incitement, juvenile conduct and blithe contempt for the Constitution," he said. "The voters will do what the voters will do, but it must not be, cannot be because the press did not do enough."
Processed story:
week nation watch nevertrump movement fold faster one presumpt nomine beachfront developments mani tri explain away trump reckless racist extremism put principl party wife former republican senat bob bennett die may reveal husband spent die hour reach muslims would go peopl hijab tell glad america told daili beast want apolog behalf republican party u k prime minist david cameron call trump propos ban muslim enter u divisive stupid wrong trump repli think cameron would veri good relationship press also part whitewash extremism new york time call trump racism reduct approach ethnicity said trump attitud toward women complex defi simpl categorization sexism sudden complic string theory everybodi go along bob garfield co host media warn press danger normal trump everi interview donald trump everi singl one hold account bigotry incitement juvenil conduct blith contempt constitution said voter voter must cannot becaus press enough
Adding additional columns to story:
Sunday Roundup
week nation watch nevertrump movement fold faster one presumpt nomine beachfront developments mani tri explain away trump reckless racist extremism put principl party wife former republican senat bob bennett die may reveal husband spent die hour reach muslims would go peopl hijab tell glad america told daili beast want apolog behalf republican party u k prime minist david cameron call trump propos ban muslim enter u divisive stupid wrong trump repli think cameron would veri good relationship press also part whitewash extremism new york time call trump racism reduct approach ethnicity said trump attitud toward women complex defi simpl categorization sexism sudden complic string theory everybodi go along bob garfield co host media warn press danger normal trump everi interview donald trump everi singl one hold account bigotry incitement juvenil conduct blith contempt constitution said voter voter must cannot becaus press enough
Final story:
sunday roundup week nation watch nevertrump movement fold faster one presumpt nomin beachfront develop mani tri explain away trump reckless racist extrem put principl parti wife former republican senat bob bennett die may reveal husband spent die hour reach muslim would go peopl hijab tell glad america told daili beast want apolog behalf republican parti u k prime minist david cameron call trump propo ban muslim enter u divis stupid wrong trump repli think cameron would veri good relationship press also part whitewash extrem new york time call trump racism reduct approach ethnic said trump attitud toward women complex defi simpl categor sexism sudden complic string theori everybodi go along bob garfield co host media warn press danger normal trump everi interview donald trump everi singl one hold account bigotri incit juvenil conduct blith contempt constitut said voter voter must cannot becaus press enough
Predicted class: POLITICS
Actual class: POLITICS

precision    recall  f1-score   support

ARTS & CULTURE       0.56      0.47      0.51      1280
  BLACK VOICES       0.59      0.40      0.48      1494
      BUSINESS       0.51      0.48      0.49      1959
       COLLEGE       0.48      0.42      0.45       377
        COMEDY       0.48      0.43      0.45      1708
         CRIME       0.57      0.59      0.58      1124
       DIVORCE       0.85      0.72      0.78      1131
     EDUCATION       0.43      0.31      0.36       331
 ENTERTAINMENT       0.64      0.75      0.69      5299
   ENVIRONMENT       0.67      0.26      0.37       437
         FIFTY       0.37      0.15      0.22       462
  FOOD & DRINK       0.64      0.73      0.68      2055
     GOOD NEWS       0.40      0.20      0.27       461
         GREEN       0.41      0.37      0.39       865
HEALTHY LIVING       0.35      0.33      0.34      2209
 HOME & LIVING       0.75      0.72      0.73      1384
        IMPACT       0.44      0.26      0.33      1141
 LATINO VOICES       0.66      0.29      0.40       373
         MEDIA       0.55      0.46      0.50       929
         MONEY       0.56      0.32      0.41       563
     PARENTING       0.66      0.76      0.71      4169
      POLITICS       0.71      0.84      0.77     10804
  QUEER VOICES       0.79      0.69      0.74      2084
      RELIGION       0.55      0.50      0.53       843
       SCIENCE       0.59      0.47      0.53       719
        SPORTS       0.68      0.74      0.71      1612
STYLE & BEAUTY       0.78      0.81      0.80      3928
         TASTE       0.37      0.16      0.22       692
          TECH       0.58      0.41      0.48       687
        TRAVEL       0.69      0.76      0.73      3263
      WEDDINGS       0.80      0.78      0.79      1205
    WEIRD NEWS       0.41      0.26      0.32       881
      WELLNESS       0.63      0.74      0.68      5883
         WOMEN       0.41      0.29      0.34      1152
    WORLD NEWS       0.51      0.17      0.26       718
     WORLDPOST       0.56      0.59      0.57      2060

      accuracy                           0.64     66282
     macro avg       0.57      0.49      0.52     66282
  weighted avg       0.63      0.64      0.62     66282

Accuracy: 63.83%

Столбец short_description содержит 3985816 слов. После применения функции предварительной обработки это сокращается до 2192635 (сокращение на 45%).
В окончательном корпусе после добавления заголовка и повторного выполнения предварительной обработки содержится 3498319 слов.

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

Отчет о классификации показывает, что самый низкий балл F1 для ПЯТЬДЕСЯТ и ВКУС, которые имеют довольно низкое количество новостей, и самый высокий для СТИЛЯ И КРАСОТЫ, который имеет большое количество новостей.

Средняя точность составляет 63,83%.
Хотя это может показаться не очень привлекательным, для 36 этикеток случайное угадывание будет иметь точность всего 2,78%. Таким образом, наша модель в 23 раза лучше, чем случайное предположение. Так звучит намного лучше! 😄

На этом завершается это (короткое?) Введение. Надеюсь, теперь вы сможете начать свои собственные проекты классификации текста.

Следующими шагами вы должны попробовать разные модели (что можно легко сделать, просто передав другую модель в функцию prepare_fit_pred), изучить и поэкспериментировать с этапами предварительной обработки, разработать функции (есть ли какая-либо связь между длиной истории и ее ярлык?) и более подробно объясняет, почему около 40% историй были классифицированы неправильно (подсказка: 20% историй ОБРАЗОВАНИЯ были классифицированы как ПОЛИТИКА).

Когда вы будете уверены в основах, вы, возможно, захотите следовать некоторым методам, используемым некоторыми из ведущих Kagglers в их материалах по НЛП. Хорошие ребята из Neptune.ai позаботятся о вас.



Как уже упоминалось в начале, на Kaggle доступны и набор данных, и код. Они также доступны на GitHub, если вы хотите попробовать это на своем локальном компьютере или в Google Colab.

Спасибо, что остались здесь. Будем рады любым отзывам!

Вы можете связаться со мной по адресу [email protected] и / или связаться со мной в LinkedIn.