21 KiB
Методические указания к лабораторной работе №2
В данной работе мы продолжаем работать с библиотекой scikit-learn (http://scikit-learn.org), и хотим выяснить ее возможности при работе с текстовыми документами.
Ниже приведены новые модули, которые будут использованы в данной работе:
- fetch_20newsgroups - http://scikit-learn.org/stable/modules/generated/sklearn.datasets.fetch_20newsgroups.html - загружает датасет «20 news groups», состоящий приблизительно из 18000 сообщений на английском языке по 20 тематикам, разбитым на обучающую и тестовую выборки. Векторизаторы текста:
- CountVectorizer - http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.CountVectorizer.html
- TfidfTransformer - http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.TfidfVectorizer.html#sklearn.feature_extraction.text.TfidfVectorizer
- Pipeline - http://scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html - конвейерный классификатор
- MultinominalNB - http://scikit-learn.org/stable/modules/generated/sklearn.naive_bayes.MultinomialNB.html - Полиномиальный (Мультиномиальный) Наивный Байесовский метод – разновидность Наивного Байесовского метода, которая хорошо работает с текстами, длины которых сильно варьируются.
Для проведения стемминга предлагается использовать библиотеку NLTK и стеммер Портера
Импорт библиотек
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer
import numpy as np
Загрузка выборки
Выборка 20 news groups представляет собой сообщения, котороые состоят из заголовка (header), основной части, подписи или сноски (footer), а также могут содержать в себе цитирование предыдущего сообщения (quotes). Модуль fetch_20newsgroups
позволяет выбирать интересующие тематики и удалять ненужные части сообщений. Для того чтобы выбрать сообщения по интересующим тематикам, необходимо передать список тематик в параметр categories.
Для того чтобы удалить ненужные части сообщений, нужно передать их в параметр remove
. Кроме того, важным параметром fetch_20newsgroups
является subset
- тип выборки – обучающая или тестовая.
Выберем сообщения по тематикам Атеизм и Компьютерная графика, а также укажем, что нас не интересуют заголовки, цитаты и подписи:
= ['alt.atheism', 'comp.graphics']
categories = ('headers', 'footers', 'quotes')
remove = fetch_20newsgroups(subset='train', shuffle=True, random_state=42, categories = categories, remove = remove )
twenty_train = fetch_20newsgroups(subset='test', shuffle=True, random_state=42, categories = categories, remove = remove ) twenty_test
Возвращаемый набор данных — это scikit-learn совокупность: одномерный контейнер с полями, которые могут интерпретироваться как признаки объекта (object attributes). Например, target_names
содержит список названий запрошенных категорий, target
- тематику сообщения, а data
– непосредственно текст сообщения:
print (twenty_train.data[2])
Does anyone know of any good shareware animation or paint software for an SGI
machine? I've exhausted everyplace on the net I can find and still don't hava
a nice piece of software.
Thanks alot!
Chad
Векторизация
Чтобы использовать машинное обучение на текстовых документах, первым делом, нужно перевести текстовое содержимое в числовой вектор признаков. Предобработка текста, токенизация и отбрасывание стоп-слов включены в состав модуля CountVectorizer
, который позволяет создать словарь характерных признаков и перевести документы в векторы признаков. Создадим объект-векторизатор vect
со следующими параметрами:
max_features = 10000
- количество наиболее частотных терминов, из которых будет состоять словарь. По умолчанию используются все слова.stop_words = 'english'
– на данный момент модулем поддерживается отсечение английских стоп-слов. Кроме того, здесь можно указать список стоп-слов вручную. Если параметр не указывать, будут использованы все термины словаря.
= CountVectorizer(max_features = 10000, stop_words = 'english') vect
Также, в работе может потребоваться настройка следующих параметров:
max_df
-float
в диапазоне [0.0, 1.0] илиint
, по умолчанию = 1.0. При построении словаря игнорирует термины, частота которых в документе строго превышает заданный порог (стоп-слова для конкретного корпуса). Еслиfloat
, параметр обозначает долю документов, если целое число – то абсолютное значение.min_df
-float
в диапазоне [0.0, 1.0] илиint
, по умолчанию = 1.0. При построении словаря игнорируйте термины, частота которых в документе строго ниже заданного порога. В литературе это значение также называется порогом. Еслиfloat
, параметр обозначает долю документов, если целое число – то абсолютное значение.
После того как объект-векторизатор создан, необходимо создать словарь характерных признаков с помощью метода fit() и перевести документы в векторы признаков c помощью метода transform(), подав на него обучающую выборку:
vect.fit(twenty_train.data)
= vect.transform(twenty_train.data)
train_data = vect.transform(twenty_test.data) test_data
Также, можно отметить что эти два действия могут быть объединены одним методом fit_transform()
. Однако, в этом случае нужно учесть, что для перевода тестовой выборки в вектор признаков, по-прежнему нужно использовать метод transform()
:
= vect.fit_transform(twenty_train.data)
train_data = vect.transform(twenty_test.data) test_data
Если для тестовых данных также воспользоваться методом fit_transform()
, это приведет к перестроению словаря признаков и неправильным результатам классификации.
Следующий блок кода позволит вывести первые 10 терминов, упорядоченных по частоте встречаемости:
= list(zip(vect.get_feature_names_out(), np.ravel(train_data.sum(axis=0))))
x def SortbyTF(inputStr):
return inputStr[1]
=SortbyTF, reverse = True)
x.sort(keyprint (x[:10])
[('image', 489), ('don', 417), ('graphics', 410), ('god', 409), ('people', 384), ('does', 364), ('edu', 349), ('like', 329), ('just', 327), ('know', 319)]
Если для каждого класса по-отдельности получить подобные списки наиболее частотных слов, то можно оценить пересекаемость терминов двух классов, например, с помощью меры сходства Жаккара. Ниже приведена функция, которая на вход принимает два списка слов и возвращает значение коэффициента Жаккара:
def jaccard_similarity(list1, list2):
= len(list(set(list1).intersection(list2)))
intersection = (len(set(list1)) + len(set(list2))) - intersection
union return float(intersection) / union
Стемминг
Существует целый ряд алгоритмов стемминга. В работе предлагается использовать алгоритм Портера, реализация которого приведена в библиотеке nltk Для проведения стемминга нужно создать объект PorterStemmer()
. Стеммер работает таким образом: у созданного объекта PorterStemmer
есть метод stem
, производящий стемминга. Таким образом, необходимо каждую из частей выборки (обучающую и тестовую) разбить на отдельные документы, затем, проходя в цикле по каждому слову в документе, произвести стемминг и объединить эти слова в новый документ.
from nltk.stem import *
from nltk import word_tokenize
= PorterStemmer()
porter_stemmer = []
stem_train for text in twenty_train.data:
= word_tokenize(text)
nltk_tokens = ''
line for word in nltk_tokens:
+= ' ' + porter_stemmer.stem(word)
line
stem_train.append(line)print (stem_train[0])
i 'll take a wild guess and say freedom is object valuabl . i base thi on the assumpt that if everyon in the world were depriv utterli of their freedom ( so that their everi act wa contrari to their volit ) , almost all would want to complain . therefor i take it that to assert or believ that `` freedom is not veri valuabl '' , when almost everyon can see that it is , is everi bit as absurd as to assert `` it is not rain '' on a raini day . i take thi to be a candid for an object valu , and it it is a necessari condit for object moral that object valu such as thi exist .
TF- и TF-IDF взвешивание
CountVectorizer
позволяет лишь определять частоту встречаемости термина во всей выборке, но такой подход к выявлению информативных терминов не всегда дает качественный результат. На практике используют более продвинутые способы, наибольшее распространение из которых получили TF- и TF-IDF взвешивания. Воспользуемся методом fit()
класса TfidfTransformer()
, который переводит матрицу частот встречаемости в TF- и TF-IDF веса.
= TfidfTransformer(use_idf = True).fit(train_data)
tfidf = tfidf.transform(train_data) train_data_tfidf
Отметим, что в метод fit()
нужно передавать не исходные текстовые данные, а вектор слов и их частот, полученный с помощью метода transform()
класса CountVectorizer
. Для того, чтобы получить tf-idf значения, необходимо установить параметр use_idf = True
, в противном случае на выходе мы получим значения tf
Классификация
После того как мы провели векторизацию текста, обучение модели и классификация для текстовых данных выглядит абсолютно идентично классификации объектов в первой лабораторной работе.
Задача обучения модели заключается не только в выборе подходящих данных обучающей выборки, способных качественно охарактеризовать объекты, но и в настройке многочисленных параметров метода классификации, предварительной обработке данных и т.д. Рассмотрим, какие возможности предлагаются в библиотеке scikit-learn для автоматизации и упрощения данной задачи.
Pipeline
Чтобы с цепочкой vectorizer => transformer => classifier было проще работать, в scikit-learn есть класс Pipeline
(конвейер), который функционирует как составной (конвейерный) классификатор.
from sklearn.pipeline import Pipeline
Промежуточными шагами конвейера должны быть преобразования, то есть должны выполняться методы fit()
и transform()
, а последний шаг – только fit()
. При этом, pipeline позволяет устанавливать различные параметры на каждом своем шаге. Таким образом, проделанные нами действия по векторизации данных, взвешиванию с помощью TF-IDF и классификации методом К-БС с использованием pipeline будут выглядеть следующим образом:
= Pipeline([('vect', CountVectorizer(max_features= 1000, stop_words = 'english')),
text_clf 'tfidf', TfidfTransformer(use_idf = True)),
('clf', KNeighborsClassifier (n_neighbors=1)),]) (
Названия vect
, tfidf
и clf
выбраны нами произвольно. Мы рассмотрим их использование в следующей лабораторной работе. Теперь обучим модель с помощью всего 1 команды:
= text_clf.fit(twenty_train.data, twenty_train.target) text_clf
И проведем классификацию на тестовой выборке:
= text_clf.predict(twenty_test.data) prediction