Последнее обновление:
Я загрузил полный код (записную книжку Python и Jupyter) на GitHub: https://github.com/javedsha/text-classification

Классификация документов / текста - одна из важных и типичных задач контролируемого машинного обучения (ML). Назначение категорий документам, которые могут быть веб-страницей, библиотечной книгой, статьями в СМИ, галереей и т. Д., Имеет множество приложений, например, фильтрация спама, маршрутизация электронной почты, анализ настроений и т. д. В этой статье я хотел бы продемонстрировать, как мы можем проводить классификацию текста с помощью python, scikit-learn и немного NLTK.

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

Давайте разделим задачу классификации на следующие этапы:

  1. Предпосылка и настройка окружения.
  2. Загрузка набора данных в jupyter.
  3. Извлечение функций из текстовых файлов.
  4. Запуск алгоритмов машинного обучения.
  5. Сетка Поиск для настройки параметров.
  6. Полезные советы и немного НЛТК.

Шаг 1. Предварительные требования и настройка среды

Необходимыми условиями для выполнения этого примера являются python версии 2.7.3 и блокнот jupyter. Вы можете просто установить anaconda, и он получит все для вас. Кроме того, требуется немного основ Python и ML, включая классификацию текста. В нашем примере мы будем использовать библиотеки scikit-learn (python).

Шаг 2: Загрузка набора данных в jupyter.

Набор данных, который будет использоваться в этом примере, - это знаменитый набор данных 20 ​​Newsgoup. О данных с оригинального сайта:

Набор данных 20 групп новостей представляет собой набор примерно из 20 000 документов групп новостей, разделенных (почти) равномерно по 20 различным группам новостей. Насколько мне известно, первоначально он был собран Кеном Лэнгом, вероятно, для его статьи Newsweeder: обучение фильтрации сетевых новостей, хотя он явно не упоминает эту коллекцию. Коллекция 20 групп новостей стала популярным набором данных для экспериментов в текстовых приложениях методов машинного обучения, таких как классификация текста и кластеризация текста.

Этот набор данных встроен в scikit, поэтому нам не нужно загружать его явно.

я. Откройте командную строку в Windows и введите «jupyter notebook». Это откроет записную книжку в браузере и запустит для вас сеанс.

II. Выберите «Создать» ›Python 2. Вы можете дать записной книжке имя - Демонстрация классификации текста 1

iii. Загрузка набора данных: (это может занять несколько минут, наберитесь терпения)

from sklearn.datasets import fetch_20newsgroups
twenty_train = fetch_20newsgroups(subset='train', shuffle=True)

Примечание. Выше мы загружаем только данные обучения. Мы загрузим тестовые данные отдельно позже в этом примере.

iv. Вы можете проверить целевые имена (категории) и некоторые файлы данных с помощью следующих команд.

twenty_train.target_names #prints all the categories
print("\n".join(twenty_train.data[0].split("\n")[:3])) #prints first line of the first data file

Шаг 3. Извлечение объектов из текстовых файлов.

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

Scikit-learn имеет компонент высокого уровня, который будет создавать векторы функций для нас CountVectorizer. Подробнее об этом здесь.

from sklearn.feature_extraction.text import CountVectorizer
count_vect = CountVectorizer()
X_train_counts = count_vect.fit_transform(twenty_train.data)
X_train_counts.shape

Здесь, выполняя «count_vect.fit_transform (enty_train.data)», мы изучаем словарный словарь, и он возвращает матрицу «документ-термин». [n_samples, n_features].

TF: простой подсчет количества слов в каждом документе имеет одну проблему: это придаст больший вес более длинным документам, чем более коротким. Чтобы избежать этого, мы можем использовать частоту (TF - Term Frequencies), то есть #count (word) / #Total words, в каждом документе.

TF-IDF: Наконец, мы можем даже уменьшить вес более распространенных слов, таких как (the, is, an и т. д.), которые встречаются во всем документе. Это называется TF-IDF, т.е. частота термина, умноженная на обратную частоту документа.

Мы можем добиться того и другого, используя следующую строку кода:

from sklearn.feature_extraction.text import TfidfTransformer
tfidf_transformer = TfidfTransformer()
X_train_tfidf = tfidf_transformer.fit_transform(X_train_counts)
X_train_tfidf.shape

Последняя строка выведет размер матрицы Document-Term - ›(11314, 130107).

Шаг 4. Запуск алгоритмов машинного обучения.

Существуют различные алгоритмы, которые можно использовать для классификации текста. Мы начнем с самого простого Наивного Байеса (NB) (не думайте, что это слишком наивно! 😃)

Вы можете легко создать NBclassifier в scikit, используя ниже 2 строки кода: (обратите внимание - существует много вариантов NB, но обсуждение их выходит за рамки)

from sklearn.naive_bayes import MultinomialNB
clf = MultinomialNB().fit(X_train_tfidf, twenty_train.target)

Это обучит классификатор NB на предоставленных нами обучающих данных.

Создание конвейера. Мы можем писать меньше кода и выполнять все вышеперечисленное, создавая конвейер следующим образом:

>>> from sklearn.pipeline import Pipeline
>>> text_clf = Pipeline([('vect', CountVectorizer()),
...                      ('tfidf', TfidfTransformer()),
...                      ('clf', MultinomialNB()),
... ])
text_clf = text_clf.fit(twenty_train.data, twenty_train.target)

Имена "vect", "tfidf" и "clf" произвольны, но будут использованы позже.

Производительность NB-классификатора: Теперь мы проверим производительность NB-классификатора на тестовой выборке.

import numpy as np
twenty_test = fetch_20newsgroups(subset='test', shuffle=True)
predicted = text_clf.predict(twenty_test.data)
np.mean(predicted == twenty_test.target)

Получаемая точность составляет ~ 77,38%, что неплохо для начала и для наивного классификатора. Также поздравляю !!! вы успешно написали алгоритм классификации текста 👍

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

>>> from sklearn.linear_model import SGDClassifier
>>> text_clf_svm = Pipeline([('vect', CountVectorizer()),
...                      ('tfidf', TfidfTransformer()),
...                      ('clf-svm', SGDClassifier(loss='hinge', penalty='l2',
...                                            alpha=1e-3, n_iter=5, random_state=42)),
... ])
>>> _ = text_clf_svm.fit(twenty_train.data, twenty_train.target)
>>> predicted_svm = text_clf_svm.predict(twenty_test.data)
>>> np.mean(predicted_svm == twenty_test.target)

Полученная точность составляет ~ 82,38%. Ура, немного лучше 👌

Шаг 5. Поиск по сетке

Почти все классификаторы будут иметь различные параметры, которые можно настраивать для достижения оптимальной производительности. Scikit предоставляет чрезвычайно полезный инструмент «GridSearchCV».

>>> from sklearn.model_selection import GridSearchCV
>>> parameters = {'vect__ngram_range': [(1, 1), (1, 2)],
...               'tfidf__use_idf': (True, False),
...               'clf__alpha': (1e-2, 1e-3),
... }

Здесь мы создаем список параметров, для которых мы хотели бы настроить производительность. Все имена параметров начинаются с имени классификатора (запомните произвольное имя, которое мы дали). Например. vect__ngram_range; здесь мы советуем использовать юниграммы и биграммы и выбрать ту, которая является оптимальной.

Затем мы создаем экземпляр поиска по сетке, передавая классификатор, параметры и n_jobs = -1, который сообщает об использовании нескольких ядер с пользовательской машины.

gs_clf = GridSearchCV(text_clf, parameters, n_jobs=-1)
gs_clf = gs_clf.fit(twenty_train.data, twenty_train.target)

Это может занять несколько минут в зависимости от конфигурации машины.

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

gs_clf.best_score_
gs_clf.best_params_

Теперь точность увеличена до ~ 90,6% для классификатора NB (уже не так наивно! 😄), а соответствующие параметры - {'clf__alpha': 0,01, 'tfidf__use_idf' : Верно, 'vect__ngram_range': (1, 2)}.

Точно так же мы получаем повышенную точность ~ 89,79% для классификатора SVM с приведенным ниже кодом. Примечание. Вы можете дополнительно оптимизировать классификатор SVM, настроив другие параметры. Это оставлено на ваше усмотрение, чтобы узнать больше.

>>> from sklearn.model_selection import GridSearchCV
>>> parameters_svm = {'vect__ngram_range': [(1, 1), (1, 2)],
...               'tfidf__use_idf': (True, False),
...               'clf-svm__alpha': (1e-2, 1e-3),
... }
gs_clf_svm = GridSearchCV(text_clf_svm, parameters_svm, n_jobs=-1)
gs_clf_svm = gs_clf_svm.fit(twenty_train.data, twenty_train.target)
gs_clf_svm.best_score_
gs_clf_svm.best_params_

Шаг 6: Полезные советы и немного NLTK.

  1. Удаление стоп-слов: (то, затем и т. д.) из данных. Вы должны делать это только тогда, когда стоп-слова бесполезны для решения основной проблемы. В большинстве задач классификации текста это действительно бесполезно. Посмотрим, повысит ли точность удаление стоп-слов. Обновите код для создания объекта CountVectorizer следующим образом:
>>> from sklearn.pipeline import Pipeline
>>> text_clf = Pipeline([('vect', CountVectorizer(stop_words='english')),
...                      ('tfidf', TfidfTransformer()),
...                      ('clf', MultinomialNB()),
... ])

Это конвейер, который мы строим для классификатора NB. Выполните оставшиеся шаги, как раньше. Это улучшает точность с 77,38% до 81,69% (что слишком хорошо). Вы можете попробовать то же самое для SVM, а также при поиске по сетке.

2. FitPrior = False: Если для MultinomialNB установлено значение false, будет использоваться единый предшествующий. Это не очень помогает, но увеличивает точность с 81,69% до 82,14% (небольшой выигрыш). Попробуйте и посмотрите, работает ли это для вашего набора данных.

3. Основание: Согласно Википедии, основание - это процесс сведения слов, измененных сгибанием (или иногда производных), до основы слова, основы или формы корня. Например. Алгоритм стемминга сокращает слова рыбалка, рыбалка и рыбак до корневого слова рыба.

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

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

import nltk
nltk.download()
from nltk.stem.snowball import SnowballStemmer
stemmer = SnowballStemmer("english", ignore_stopwords=True)
class StemmedCountVectorizer(CountVectorizer):
    def build_analyzer(self):
        analyzer = super(StemmedCountVectorizer, self).build_analyzer()
        return lambda doc: ([stemmer.stem(w) for w in analyzer(doc)])
stemmed_count_vect = StemmedCountVectorizer(stop_words='english')
text_mnb_stemmed = Pipeline([('vect', stemmed_count_vect),
...                      ('tfidf', TfidfTransformer()),
...                      ('mnb', MultinomialNB(fit_prior=False)),
... ])
text_mnb_stemmed = text_mnb_stemmed.fit(twenty_train.data, twenty_train.target)
predicted_mnb_stemmed = text_mnb_stemmed.predict(twenty_test.data)
np.mean(predicted_mnb_stemmed == twenty_test.target)

Точность при постановке ствола составляет ~ 81,67%. Незначительное улучшение в нашем случае с классификатором NB. Вы также можете попробовать SVM и другие алгоритмы.

Вывод: мы узнали классическую проблему НЛП - классификацию текста. Мы узнали о важных концепциях, таких как набор слов, TF-IDF и 2 важных алгоритмах NB и SVM. Мы увидели, что для нашего набора данных оба алгоритма были почти одинаково согласованы при оптимизации. Иногда, если у нас достаточно данных, выбор алгоритма практически не имеет значения. Мы также увидели, как выполнять поиск по сетке для настройки производительности, и использовали метод стемминга NLTK. Вы можете использовать этот код в своем наборе данных и посмотреть, какие алгоритмы лучше всего подходят для вас.

Обновление: если кто-то попробует другой алгоритм, поделитесь результатами в разделе комментариев, он будет полезен для всех.

Пожалуйста, дайте мне знать, если были какие-то ошибки, и обратная связь приветствуется ✌️

Рекомендуйте, комментируйте, делитесь, если вам понравилась эта статья.

Ссылки:

Http://scikit-learn.org/ (код)

Http://qwone.com/~jason/20Newsgroups/ (набор данных)