Работа с текстовыми данными в scikit-learn (перевод документации) — часть 1
Данная статья представляет перевод главы, обучающей работе с текстовыми данными, из официальной документации scikit-learn.
Цель этой главы — это исследование некоторых из самых важных инструментов в scikit-learn на одной частной задаче: анализ коллекции текстовых документов (новостные статьи) на 20 различных тематик.
В этой главе мы рассмотрим как:
- загрузить содержимое файла и категории
- выделить вектора признаков, подходящих для машинного обучения
- обучить одномерную модель выполнять категоризацию
- использовать стратегию grid search, чтобы найти наилучшую конфигурацию для извлечения признаков и для классификатора
Инструкция по установке
Чтобы начать практическое занятие, описанное в данной главе, у вас должен быть установлен scikit-learn и все компоненты, от которых он зависит (numpy, Scipy).
Чтобы получить инструкцию по установке и рекомендации для разных ОС перейдите на эту страницу.
Локальную копию данного занятия вы можете найти в своей папке:
scikit-learn/doc/tutorial/text_analytics/
Теперь scikit-learn не устанавливается с папкой doc/ и прочим содержимым. Вы можете ее скачать с github.
Папка с обучающими примерами должна содержать следующие файлы и папки:
- *.rst files — источник учебных документов, обработанных с помощью sphinx
- data — папка для хранения наборов данных в процессе обучения
- skeletons — образцы неполных скриптов для упражнений
- solutions — решения упражнений
Также вы можете скопировать skeletons в новую папку в любое место на вашем жестком диске, названную sklearn_tut_workspace, где вы будете редактировать ваши собственные файлы для упражнений. Так изначальные skeletons останутся неизменными:
% cp -r skeletons work_directory/sklearn_tut_workspace
Алгоритмы машинного обучения нуждаются в данных. Зайдите в каждую подпапку $TUTORIAL_HOME/data и запустите оттуда скрипт fetch_data.py (для начала прочтите их).
Например:
% cd $TUTORIAL_HOME/data/languages
% less fetch_data.py
% python fetch_data.py
Загрузка 20 новостных наборов данных
Набор данных называется «Twenty Newsgroups». Вот его официальное описание, взятое с сайта:
Данные «The 20 Newsgroups» — это коллекция примерно из 20000 новостных документов, разделенная (приблизительно) равномерно между 20 различными категориями. Насколько нам известно, изначально она собиралась Кеном Ленгом (Ken Lang), возможно, для его работы «Newsweeder: Learning to filter netnews» («Новостной обозреватель: учимся фильтровать новости из сети»), хотя он явно не заявлял об этом. Коллекция «The 20 newsgroups» стала популярным набором данных для экспериментов с техниками машинного обучения для текстовых приложений, таких как классификация текста или его кластеризация.
Далее мы будем использовать встроенный загрузчик наборов данных для выборки «The 20 newsgroups» из scikit-learn. Иначе, выборку можно загрузить вручную с вэб сайта, использовать функцию sklearn.datasets.load_files и указав папку »20news-bydate-train» для сохранения распакованного архива.
Чтобы первый пример быстрее исполнялся, мы будем работать только с частью нашего набора данных, разбитой на 4 категории из 20 возможных:
>>> categories = ['alt.atheism', 'soc.religion.christian',
... 'comp.graphics', 'sci.med']
Мы можем загрузить список файлов, совпадающих с нужными категориями, как показано ниже:
>>> from sklearn.datasets import fetch_20newsgroups
>>> twenty_train = fetch_20newsgroups(subset='train',
... categories=categories, shuffle=True, random_state=42)
Возвращаемый набор данных — это scikit-learn совокупность: одномерный контейнер с полями, которые могут интерпретироваться как ключи в словаре python (dict keys), проще говоря — как признаки объекта (object attributes). Например, target_names содержит список названий запрошенных категорий:
>>> twenty_train.target_names
['alt.atheism', 'comp.graphics', 'sci.med', 'soc.religion.christian']
Сами файлы загружаются в память как атрибут data. Вы также можете ссылаться на названия файлов:
>>> len(twenty_train.data)
2257
>>> len(twenty_train.filenames)
2257
Давайте выведем на печать первые строки из первого загруженного файла:
>>> print("\n".join(twenty_train.data[0].split("\n")[:3]))
From: sd345@city.ac.uk (Michael Collier)
Subject: Converting images to HP LaserJet III?
Nntp-Posting-Host: hampton
>>> print(twenty_train.target_names[twenty_train.target[0]])
comp.graphics
Алгоритмы для обучения с учителем (контролируемого обучения) требуют, чтобы у каждого документа в обучающей выборке была помета определенной категории. В нашем случае, категория — это название новостной выборки, которая «случайно» оказывается названием папки, содержащей характерные документы.
Для увеличения скорости и эффективного использования памяти, scikit-learn загружает целевой атрибут как массив целых чисел, который соответствует индексу названия категории из списка target_names. Индекс категории каждой выборки хранится в атрибуте target:
>>> twenty_train.target[:10]
array([1, 1, 3, 3, 3, 3, 3, 2, 2, 2])
Можно получить название категории:
>>> for t in twenty_train.target[:10]:
... print(twenty_train.target_names[t])
...
comp.graphics
comp.graphics
soc.religion.christian
soc.religion.christian
soc.religion.christian
soc.religion.christian
soc.religion.christian
sci.med
sci.med
sci.med
Вы можетет заметить, что выборки были рандомно перетасованы (с помощью рандомного сгенирированного числа — fixed RNG seed). Такой метод подойдет, если вы хотите использовать только первые выборки для быстрого обучения модели и если вы хотите получить общее представление о результатах перед последующим переобучением на полном наборе данных.
Извлечение характерных признаков из текстовых файлов
Чтобы использовать машинное обучение на текстовых документах, первым делом, нужно перевести текстовое содержимое в числовой вектор признаков.
«Мешок слов» (набор слов)
Наиболее интуитивно понятный способ сделать описанное выше преобразование — это представить текст в виде набора слов:
- приписать уникальный целочисленный индекс каждому слову, появляющемуся в документах в обучающей выборке (например, построив словарь из слов с целочисленными индексами).
- для каждого документа #i посчитать количество употреблений каждого слова w и сохранить его (количество) в X[i, j]. Это будет значение признака #j, где j — это индекс слова w в словаре.
Представление «мешок слов» подразумевает, что n_features — это некоторое количество уникальных слов в корпусе. Обычно, это количество превышает 100000.
Если n_samples == 10000, то Х, сохраненный как массив numpy типа float32, потребовал бы 10000×100000 x 4 bytes = 4GB оперативной памяти (RAM), что едва осуществимо в современным компьютерах.
К счастью, большинство значений в X являются нулями, поскольку в одном документе используется менее чем пара сотен уникальных слов. Поэтому «мешок слов» чаще всего является высоко размерным разреженным набором данных. Мы можем сэкономить много свободной оперативки, храня в памяти только лишь ненулевые части векторов признаков.
Матрицы scipy.sparse — это структуры данных, которые именно это и делают — структурируют данные. В scikit-learn есть встроенная поддержка этих структур.
Токенизация текста с scikit-learn
Предобработка текста, токенизация и отфильтровывание стоп-слов включены в состав высоко уровневого компонента, который позволяет создать словарь характерных признаков и перевести документы в векторы признаков:
>>> from sklearn.feature_extraction.text import CountVectorizer
>>> count_vect = CountVectorizer()
>>> X_train_counts = count_vect.fit_transform(twenty_train.data)
>>> X_train_counts.shape
(2257, 35788)
CountVectorizer поддерживает подсчет N-грам слов или последовательностей символов. Векторизатор строит словарь индексов признаков:
>>> count_vect.vocabulary_.get(u'algorithm')
4690
Значение индекса слова в словаре связано с его частотой употребления во всем обучающем корпусе.
От употреблений к частотности
Подсчет словоупотреблений — это хорошее начало, но есть проблема: в длинных документах среднее количество словоупотреблений будет выше, чем в коротких, даже если они посвящены одной теме.
Чтобы избежать этих потенциальных несоответствий, достаточно разделить количество употреблений каждого слова в документе на общее количество слов в документе. Этот новый признак называется tf — Частота термина.
Следующее уточнение меры tf — это снижение веса слова, которое появляется во многих документах в корпусе, и отсюда является менее информативным, чем те, которые используются только в небольшой части корпуса. Примером низко ифнормативных слов могут служить служебные слова, артикли, предлоги, союзы и т.п.
Это снижение называется tf–idf, что значит «Term Frequency times Inverse Document Frequency» (обратная частота термина).
Обе меры tf и tf–idf могут быть вычислены следующим образом:
>>> from sklearn.feature_extraction.text import TfidfTransformer
>>> tf_transformer = TfidfTransformer(use_idf=False).fit(X_train_counts)
>>> X_train_tf = tf_transformer.transform(X_train_counts)
>>> X_train_tf.shape
(2257, 35788)
В примере кода, представленного выше, мы сначала используем метод fit (…), чтобы прогнать наш алгоритм оценки на данных, а потом — метод transform (…), чтобы преобразовать нашу числовую матрицу к представлению tf-idf. Эти два шага могут быть объединены и дадут тот же результат на выходе, но быстрее, что можно сделать с помощью пропуска излишней обработки. Для этого нужно использовать метод fit_transform (…), как показано ниже:
>>> tfidf_transformer = TfidfTransformer()
>>> X_train_tfidf = tfidf_transformer.fit_transform(X_train_counts)
>>> X_train_tfidf.shape
(2257, 35788)
…
Продолжение будет в части 2.