81 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
import pandas as pd
from sklearn.metrics import confusion_matrix, classification_report, accuracy_score, roc_auc_scoreЗагрузка выборки
Выборка 20 news groups представляет собой сообщения, котороые состоят из заголовка (header), основной части, подписи или сноски (footer), а также могут содержать в себе цитирование предыдущего сообщения (quotes). Модуль fetch_20newsgroups позволяет выбирать интересующие тематики и удалять ненужные части сообщений. Для того чтобы выбрать сообщения по интересующим тематикам, необходимо передать список тематик в параметр categories.
Для того чтобы удалить ненужные части сообщений, нужно передать их в параметр remove. Кроме того, важным параметром fetch_20newsgroups является subset - тип выборки – обучающая или тестовая.
Выберем сообщения по тематикам Атеизм и Компьютерная графика, а также укажем, что нас не интересуют заголовки, цитаты и подписи:
categories = ['alt.atheism', 'comp.graphics']
remove = ('headers', 'footers', 'quotes')
twenty_train = fetch_20newsgroups(subset='train', shuffle=True, random_state=42, categories = categories, remove = remove )
twenty_test = fetch_20newsgroups(subset='test', shuffle=True, random_state=42, categories = categories, remove = remove )Возвращаемый набор данных — это 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'– на данный момент модулем поддерживается отсечение английских стоп-слов. Кроме того, здесь можно указать список стоп-слов вручную. Если параметр не указывать, будут использованы все термины словаря.
vect = CountVectorizer(max_features = 10000, stop_words = 'english')Также, в работе может потребоваться настройка следующих параметров:
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)
train_data = vect.transform(twenty_train.data)
test_data = vect.transform(twenty_test.data)Также, можно отметить что эти два действия могут быть объединены одним методом fit_transform(). Однако, в этом случае нужно учесть, что для перевода тестовой выборки в вектор признаков, по-прежнему нужно использовать метод transform():
train_data = vect.fit_transform(twenty_train.data)
test_data = vect.transform(twenty_test.data)Если для тестовых данных также воспользоваться методом fit_transform(), это приведет к перестроению словаря признаков и неправильным результатам классификации.
Следующий блок кода позволит вывести первые 10 терминов, упорядоченных по частоте встречаемости:
x = list(zip(vect.get_feature_names_out(), np.ravel(train_data.sum(axis=0))))
def SortbyTF(inputStr):
return inputStr[1]
x.sort(key=SortbyTF, reverse = True)
print (x[:10])[('image', np.int64(489)), ('don', np.int64(417)), ('graphics', np.int64(410)), ('god', np.int64(409)), ('people', np.int64(384)), ('does', np.int64(364)), ('edu', np.int64(349)), ('like', np.int64(329)), ('just', np.int64(327)), ('know', np.int64(319))]
Стемминг
Существует целый ряд алгоритмов стемминга. Один из популярных - алгоритм Портера, реализация которого приведена в библиотеке nltk Для проведения стемминга нужно создать объект PorterStemmer(). Стеммер работает таким образом: у созданного объекта PorterStemmer есть метод stem, производящий стемминга. Таким образом, необходимо каждую из частей выборки (обучающую и тестовую) разбить на отдельные документы, затем, проходя в цикле по каждому слову в документе, произвести стемминг и объединить эти слова в новый документ.
В ЛР проводить стемминг не требуется.
import nltk
from nltk.stem import *
from nltk import word_tokenize
nltk.download('punkt_tab')
porter_stemmer = PorterStemmer()
stem_train = []
for text in twenty_train.data:
nltk_tokens = word_tokenize(text)
line = ''
for word in nltk_tokens:
line += ' ' + porter_stemmer.stem(word)
stem_train.append(line)
print (stem_train[0])[nltk_data] Downloading package punkt_tab to /home/andrey/nltk_data...
[nltk_data] Package punkt_tab is already up-to-date!
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 веса.
tfidf = TfidfTransformer(use_idf = True).fit(train_data)
train_data_tfidf = tfidf.transform(train_data)Отметим, что в метод 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 будут выглядеть следующим образом:
text_clf = Pipeline([('vect', CountVectorizer(max_features= 1000, stop_words = 'english')),
('tfidf', TfidfTransformer(use_idf = True)),
('clf', KNeighborsClassifier (n_neighbors=1)),]) Названия vect, tfidf и clf выбраны нами произвольно. Мы рассмотрим их использование в следующей лабораторной работе. Теперь обучим модель с помощью всего 1 команды:
text_clf = text_clf.fit(twenty_train.data, twenty_train.target)И проведем классификацию на тестовой выборке:
prediction = text_clf.predict(twenty_test.data)Настройка параметров с использованием grid search
from sklearn.model_selection import GridSearchCVС помощью конвейерной обработки (pipelines) стало гораздо проще указывать параметры обучения модели, однако перебирать все возможные варианты вручную даже в нашем простом случае выйдет затратно. В нашем случае имеется четыре настраиваемых параметра: max_features, stop_words, use_idf и n_neighbors.
Вместо поиска лучших параметров в конвейере вручную, можно запустить поиск (методом полного перебора) лучших параметров в сетке возможных значений. Сделать это можно с помощью объекта класса GridSearchCV.
Для того чтобы задать сетку параметров необходимо создать переменную-словарь, ключами которого являются конструкции вида: «НазваниеШагаКонвейера__НазваниеПараметра», а значениями – кортеж из значений параметра
parameters = {'vect__max_features': (100,500,1000,5000,10000),
'vect__stop_words': ('english', None),
'tfidf__use_idf': (True, False),
'clf__n_neighbors': (1,3,5,7)} Далее необходимо создать объект класса GridSearchCV, передав в него объект pipeline или классификатор, список параметров сетки, а также при необходимости, задав прочие параметры, такие так количество задействованых ядер процессора n_jobs, количество фолдов кросс-валидации cv, метрику, по которой будем судить о качестве модели scoring, и другие
gs_clf = GridSearchCV(text_clf, parameters, n_jobs=-1, cv=3, scoring = 'f1_weighted')Теперь объект gs_clf можно обучить по всем параметрам, как обычный классификатор, методом fit(). После того как прошло обучение, узнать лучшую совокупность параметров можно, обратившись к атрибуту best_params_. Для необученной модели атрибуты будут отсутствовать.
gs_clf.fit(twenty_train.data, twenty_train.target)gs_clf.best_params_{'clf__n_neighbors': 3,
'tfidf__use_idf': True,
'vect__max_features': 100,
'vect__stop_words': 'english'}
gs_clf.cv_results_gs_clf.best_score_np.float64(0.8093364049132378)
Word embedding
# Импортируем библиотеку gensim и посмотрим какие обученные модели в ней есть.
import gensim.downloader
print(list(gensim.downloader.info()['models'].keys()))['fasttext-wiki-news-subwords-300', 'conceptnet-numberbatch-17-06-300', 'word2vec-ruscorpora-300', 'word2vec-google-news-300', 'glove-wiki-gigaword-50', 'glove-wiki-gigaword-100', 'glove-wiki-gigaword-200', 'glove-wiki-gigaword-300', 'glove-twitter-25', 'glove-twitter-50', 'glove-twitter-100', 'glove-twitter-200', '__testing_word2vec-matrix-synopsis']
GloVe
Загрузим "glove-twitter-25" и будем работать с ней
glove_model = gensim.downloader.load("glove-twitter-25") # load glove vectors# Можем посмотреть, что за векторы у слова "cat"
print(glove_model['cat']) # word embedding for 'cat'
glove_model.most_similar("cat") # show words that similar to word 'cat'[-0.96419 -0.60978 0.67449 0.35113 0.41317 -0.21241 1.3796
0.12854 0.31567 0.66325 0.3391 -0.18934 -3.325 -1.1491
-0.4129 0.2195 0.8706 -0.50616 -0.12781 -0.066965 0.065761
0.43927 0.1758 -0.56058 0.13529 ]
[('dog', 0.9590820074081421),
('monkey', 0.920357882976532),
('bear', 0.9143136739730835),
('pet', 0.9108031392097473),
('girl', 0.8880629539489746),
('horse', 0.8872726559638977),
('kitty', 0.8870542049407959),
('puppy', 0.886769711971283),
('hot', 0.886525571346283),
('lady', 0.8845519423484802)]
Векторизуем обучающую выборку
Получаем матрицу "Документ-термин". В столбцах - все слова выборки, в строках - каждый документ. Значения в ячейках - количество встречаний слова в документе. В отображенной части видим только нули - наглядная демонстрация проблем разреженности матрицы
vectorizer = CountVectorizer(stop_words='english')
train_data = vectorizer.fit_transform(twenty_train['data'])
CV_data=pd.DataFrame(train_data.toarray(), columns=vectorizer.get_feature_names_out())
print(CV_data.shape)
CV_data.head()(1064, 16170)
| 00 | 000 | 000005102000 | 000100255pixel | 0007 | 000usd | 001200201pixel | 00196 | 0028 | 0038 | ... | zorg | zorn | zsoft | zues | zug | zur | zurich | zus | zvi | zyxel | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
5 rows × 16170 columns
# Создадим список уникальных слов, присутствующих в словаре.
words_vocab=CV_data.columns
print(words_vocab[0:10])Index(['00', '000', '000005102000', '000100255pixel', '0007', '000usd',
'001200201pixel', '00196', '0028', '0038'],
dtype='str')
Векторизуем с помощью GloVe
Нужно для каждого документа сложить glove-вектора слов, из которых он состоит. В результате получим вектор документа как сумму векторов слов, из него состоящих
Посмотрим на примере как будет работать векторизация
# Пусть выборка состоит из двух документов:
text_data = ['Hello world I love python', 'This is a great computer game! 00 000 zyxel']
# Векторизуем с помощью обученного CountVectorizer
X = vectorizer.transform(text_data)
CV_text_data=pd.DataFrame(X.toarray(), columns=vectorizer.get_feature_names_out())
CV_text_data| 00 | 000 | 000005102000 | 000100255pixel | 0007 | 000usd | 001200201pixel | 00196 | 0028 | 0038 | ... | zorg | zorn | zsoft | zues | zug | zur | zurich | zus | zvi | zyxel | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
2 rows × 16170 columns
# Создадим датафрейм, в который будем сохранять вектор документа
glove_data=pd.DataFrame()
# Пробегаем по каждой строке датафрейма (по каждому документу)
for i in range(CV_text_data.shape[0]):
# Вектор одного документа с размерностью glove-модели:
one_doc = np.zeros(25)
# Пробегаемся по каждому документу, смотрим, какие слова документа присутствуют в нашем словаре
# Суммируем glove-вектора каждого известного слова в one_doc
for word in words_vocab[CV_text_data.iloc[i,:] >= 1]:
if word in glove_model.key_to_index.keys():
print(word, ': ', glove_model[word])
one_doc += glove_model[word]
print(text_data[i], ': ', one_doc)
glove_data= pd.concat([glove_data, pd.DataFrame([one_doc])], ignore_index=True) hello : [-0.77069 0.12827 0.33137 0.0050893 -0.47605 -0.50116
1.858 1.0624 -0.56511 0.13328 -0.41918 -0.14195
-2.8555 -0.57131 -0.13418 -0.44922 0.48591 -0.6479
-0.84238 0.61669 -0.19824 -0.57967 -0.65885 0.43928
-0.50473 ]
love : [-0.62645 -0.082389 0.070538 0.5782 -0.87199 -0.14816 2.2315
0.98573 -1.3154 -0.34921 -0.8847 0.14585 -4.97 -0.73369
-0.94359 0.035859 -0.026733 -0.77538 -0.30014 0.48853 -0.16678
-0.016651 -0.53164 0.64236 -0.10922 ]
python : [-0.25645 -0.22323 0.025901 0.22901 0.49028 -0.060829 0.24563
-0.84854 1.5882 -0.7274 0.60603 0.25205 -1.8064 -0.95526
0.44867 0.013614 0.60856 0.65423 0.82506 0.99459 -0.29403
-0.27013 -0.348 -0.7293 0.2201 ]
world : [ 0.10301 0.095666 -0.14789 -0.22383 -0.14775 -0.11599 1.8513
0.24886 -0.41877 -0.20384 -0.08509 0.33246 -4.6946 0.84096
-0.46666 -0.031128 -0.19539 -0.037349 0.58949 0.13941 -0.57667
-0.44426 -0.43085 -0.52875 0.25855 ]
Hello world I love python : [ -1.55058002 -0.081683 0.27991899 0.58846928 -1.00551002
-0.82613902 6.18642995 1.44844997 -0.71108004 -1.14717001
-0.78294002 0.58841 -14.32649982 -1.41929996 -1.09575997
-0.430875 0.87234702 -0.806399 0.27203003 2.23921998
-1.23571999 -1.31071102 -1.96934 -0.17641005 -0.1353 ]
computer : [ 0.64005 -0.019514 0.70148 -0.66123 1.1723 -0.58859 0.25917
-0.81541 1.1708 1.1413 -0.15405 -0.11369 -3.8414 -0.87233
0.47489 1.1541 0.97678 1.1107 -0.14572 -0.52013 -0.52234
-0.92349 0.34651 0.061939 -0.57375 ]
game : [ 1.146 0.3291 0.26878 -1.3945 -0.30044 0.77901 1.3537
0.37393 0.50478 -0.44266 -0.048706 0.51396 -4.3136 0.39805
1.197 0.10287 -0.17618 -1.2881 -0.59801 0.26131 -1.2619
0.39202 0.59309 -0.55232 0.005087]
great : [-8.4229e-01 3.6512e-01 -3.8841e-01 -4.6118e-01 2.4301e-01 3.2412e-01
1.9009e+00 -2.2630e-01 -3.1335e-01 -1.0970e+00 -4.1494e-03 6.2074e-01
-5.0964e+00 6.7418e-01 5.0080e-01 -6.2119e-01 5.1765e-01 -4.4122e-01
-1.4364e-01 1.9130e-01 -7.4608e-01 -2.5903e-01 -7.8010e-01 1.1030e-01
-2.7928e-01]
zyxel : [ 0.79234 0.067376 -0.22639 -2.2272 0.30057 -0.85676 -1.7268
-0.78626 1.2042 -0.92348 -0.83987 -0.74233 0.29689 -1.208
0.98706 -1.1624 0.61415 -0.27825 0.27813 1.5838 -0.63593
-0.10225 1.7102 -0.95599 -1.3867 ]
This is a great computer game! 00 000 zyxel : [ 1.73610002 0.74208201 0.35545996 -4.74411008 1.41543998
-0.34222007 1.78697008 -1.45404002 2.56643 -1.32184002
-1.04677537 0.27867999 -12.95450976 -1.00809997 3.15975004
-0.52662008 1.93239999 -0.89686999 -0.60924001 1.51628
-3.16624993 -0.89275002 1.86969995 -1.33607102 -2.23464306]
На основе ячейки выше, напишем функцию, которая для каждого слова в тексте ищет это слово в glove_model
def text2vec(text_data):
# Векторизуем с помощью обученного CountVectorizer
X = vectorizer.transform(text_data)
CV_text_data=pd.DataFrame(X.toarray(), columns=vectorizer.get_feature_names_out())
CV_text_data
# Создадим датафрейм, в который будем сохранять вектор документа
glove_data=pd.DataFrame()
# Пробегаем по каждой строке (по каждому документу)
for i in range(CV_text_data.shape[0]):
# Вектор одного документа с размерностью glove-модели:
one_doc = np.zeros(25)
# Пробегаемся по каждому документу, смотрим, какие слова документа присутствуют в нашем словаре
# Суммируем glove-вектора каждого известного слова в one_doc
for word in words_vocab[CV_text_data.iloc[i,:] >= 1]:
if word in glove_model.key_to_index.keys():
#print(word, ': ', glove_model[word])
one_doc += glove_model[word]
#print(text_data[i], ': ', one_doc)
glove_data = pd.concat([glove_data, pd.DataFrame([one_doc])], axis = 0)
#print('glove_data: ', glove_data)
return glove_data# Наша выборка, векторизованная через glove выглядит так.
# Попытаться понять, почему такая размерность матрицы и что за значения в ячейках
glove_data| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | -1.55058 | -0.081683 | 0.279919 | 0.588469 | -1.00551 | -0.826139 | 6.18643 | 1.44845 | -0.71108 | -1.14717 | ... | -0.430875 | 0.872347 | -0.806399 | 0.27203 | 2.23922 | -1.23572 | -1.310711 | -1.96934 | -0.176410 | -0.135300 |
| 1 | 1.73610 | 0.742082 | 0.355460 | -4.744110 | 1.41544 | -0.342220 | 1.78697 | -1.45404 | 2.56643 | -1.32184 | ... | -0.526620 | 1.932400 | -0.896870 | -0.60924 | 1.51628 | -3.16625 | -0.892750 | 1.86970 | -1.336071 | -2.234643 |
2 rows × 25 columns
Можем использовать написанную функцию на нашей реальной выборке
train_data_glove = text2vec(twenty_train['data'])
train_data_glove| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.499258 | 3.090499 | -17.267230 | -0.997832 | -2.746523 | 1.586072 | 25.890665 | -27.914878 | -1.656527 | -6.599336 | ... | 6.840056 | 5.475214 | 4.488208 | 17.680875 | -8.254198 | 1.772313 | 6.184074 | -8.879014 | -0.216338 | -13.408250 |
| 0 | 2.579560 | -1.498130 | 1.752350 | 0.407907 | 1.528970 | 0.152390 | 0.283640 | 1.937990 | -0.659960 | -2.136370 | ... | 0.434040 | -0.274430 | 0.445033 | -0.645750 | 1.055500 | -0.850250 | -0.433200 | 0.212190 | -1.164330 | 0.905880 |
| 0 | -3.838998 | 3.339424 | 3.943270 | -1.428108 | 4.171174 | 1.918481 | 9.177921 | -3.241092 | 1.280560 | -1.781829 | ... | -3.719443 | 9.627485 | -1.198027 | -4.010974 | 1.464305 | 0.398107 | -1.013318 | -1.591893 | 4.290867 | -3.888199 |
| 0 | -1.182812 | 51.118736 | -28.117878 | -28.586500 | 24.244726 | -9.567325 | 111.896700 | -114.387355 | 12.647457 | -24.559720 | ... | 24.085172 | 39.112016 | -23.011944 | 41.767239 | -42.362471 | -16.060568 | -20.090755 | -4.457053 | -3.181086 | -90.876348 |
| 0 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | ... | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 0 | -5.839209 | 12.729176 | -6.201106 | -11.255287 | 2.878065 | -11.993908 | 11.418405 | -15.479517 | 16.634253 | -10.534109 | ... | 11.665809 | 13.761738 | -2.579413 | -7.877240 | 3.299712 | -9.241404 | -16.102834 | 5.343221 | -3.646908 | -17.216441 |
| 0 | 1.286424 | 4.341360 | -29.110272 | 5.057644 | 5.610822 | -10.954865 | 29.974885 | -38.261129 | 10.369041 | -15.652271 | ... | 12.201855 | 5.913883 | 15.941801 | 26.251438 | -9.280780 | -6.656035 | 6.158630 | -14.878060 | -1.138172 | -25.532792 |
| 0 | 6.698275 | 8.135166 | -10.347349 | 0.165546 | 2.089456 | -7.695734 | 31.593479 | -38.582911 | -3.285587 | -6.087527 | ... | 16.575447 | 15.696628 | 0.785602 | 13.384657 | -13.329273 | 1.983042 | -4.041431 | -5.571411 | 0.581912 | -32.037549 |
| 0 | -0.461364 | 4.499502 | -10.446904 | 1.502338 | -10.415269 | -8.801252 | 18.272721 | -10.398248 | -6.686713 | -0.250122 | ... | 5.758498 | 4.540718 | -0.372422 | 10.668546 | -6.377995 | 9.305774 | 11.568183 | -18.814704 | -1.204665 | -14.551387 |
| 0 | 8.348875 | 10.837893 | -1.528737 | -18.246933 | 33.950209 | -0.376254 | 48.017779 | -57.636160 | 25.231878 | -13.371779 | ... | 29.297609 | 34.342018 | -8.527635 | 13.555851 | -12.649073 | -28.706501 | -27.042654 | 4.292338 | 2.614328 | -48.834758 |
1064 rows × 25 columns
Мы получили матрицу, где каждый документ представлен вектором-строкой. Можем подавать эти вектора на вход для обучения классифкатора
clf = KNeighborsClassifier(n_neighbors = 5)
clf.fit(train_data_glove, twenty_train['target'])Аналогичную операци проводим с тестовой частью выборки и оцениваем качество модели
test_data_glove = text2vec(twenty_test['data'])
test_data_glove| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2.118167 | 10.172361 | -12.929062 | -10.235129 | -5.895256 | -2.960585 | 13.365803 | -26.779260 | 0.811607 | -3.139421 | ... | 4.893207 | 4.652184 | 2.903990 | 13.077540 | -12.438726 | 2.602789 | -7.450777 | -5.202635 | -20.983743 | -14.107781 |
| 0 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | ... | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| 0 | 4.873320 | 27.474242 | -10.773876 | 8.112889 | 0.623212 | -2.199475 | 59.094818 | -59.831612 | -2.963759 | -18.048856 | ... | 23.890453 | 12.469234 | -3.087457 | 12.981009 | -29.858967 | 13.105756 | 10.670630 | -17.378340 | -6.079635 | -41.408141 |
| 0 | -3.543091 | 1.986200 | -2.191232 | 0.006059 | -2.351208 | 0.354323 | 5.127530 | -4.232705 | 0.855633 | -1.511475 | ... | -1.034241 | 2.211705 | -3.255103 | 3.145205 | 0.115450 | -0.696187 | 0.647662 | 2.188560 | 1.237610 | -3.731906 |
| 0 | -0.810280 | -0.041986 | 0.309050 | 0.268330 | -0.735400 | -0.585930 | -0.207310 | 0.084744 | -0.024049 | -1.729000 | ... | -0.981390 | -0.348830 | -0.012710 | -0.399160 | 0.237930 | 0.608960 | 0.257370 | -0.611980 | -0.569530 | -0.627590 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 0 | -2.713570 | 17.157689 | 3.372045 | -5.867599 | -6.276993 | -12.745005 | 25.654686 | -28.636649 | 0.390143 | -9.184376 | ... | 1.313231 | 11.260476 | -3.974922 | 13.881956 | -5.040806 | 4.269640 | 9.495090 | -6.392856 | -17.443242 | -8.663642 |
| 0 | 0.330970 | 4.718122 | -1.751891 | -2.035309 | -0.343631 | -4.570000 | 6.742370 | -4.367192 | -2.187082 | -1.016773 | ... | -0.032756 | 3.431770 | -4.601260 | -0.623544 | -1.379208 | -2.430931 | -7.340540 | -4.909410 | 1.812950 | -7.518150 |
| 0 | 2.374983 | 5.077650 | -3.002268 | -3.933630 | 2.078387 | -5.470335 | 7.654599 | -12.212619 | 7.815100 | -2.849330 | ... | -0.234639 | 5.634821 | -3.027221 | 1.661357 | 0.393759 | -6.279475 | -4.927666 | -2.592987 | 1.981002 | -12.255251 |
| 0 | 1.410159 | 0.770070 | -3.955777 | -2.791190 | 5.210700 | 1.621952 | 7.780780 | -9.859718 | 8.394491 | -5.347944 | ... | 1.896947 | 4.079237 | -1.207350 | 5.772389 | 1.186500 | -7.286901 | -2.485993 | -0.947197 | 0.788222 | -9.518580 |
| 0 | 5.710371 | 6.638114 | 0.178107 | -6.406337 | 9.719197 | -8.779200 | 8.866295 | -11.437220 | 4.871641 | -11.415244 | ... | 0.851029 | 4.182781 | -3.302557 | 1.213709 | -4.294990 | -7.751608 | -12.556040 | -10.339437 | 1.448986 | -10.394629 |
708 rows × 25 columns
predict = clf.predict(test_data_glove )print (confusion_matrix(twenty_test['target'], predict))
print(classification_report(twenty_test['target'], predict))[[258 61]
[ 31 358]]
precision recall f1-score support
0 0.89 0.81 0.85 319
1 0.85 0.92 0.89 389
accuracy 0.87 708
macro avg 0.87 0.86 0.87 708
weighted avg 0.87 0.87 0.87 708