Files
TDA/labs/OATD_LR2_metod.ipynb
2026-03-17 08:48:16 +03:00

81 KiB
Исходник Ответственный История

Методические указания к лабораторной работе №2

В данной работе мы продолжаем работать с библиотекой scikit-learn (http://scikit-learn.org), и хотим выяснить ее возможности при работе с текстовыми документами.

Ниже приведены новые модули, которые будут использованы в данной работе:

Для проведения стемминга предлагается использовать библиотеку 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)
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)
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; } </style>
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
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; } </style>
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
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; } </style>
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
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; } </style>
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
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; } </style>
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