Учитесь, создав конвейер для классификации отзывов

Сегодня мы строим конвейер без излишеств, чтобы классифицировать, является ли обзор фильма положительным или нет. Это задача булевой контролируемой классификации.

Этапы:
1. Загрузите набор данных
2. Загрузите набор данных в память
3. Разделите данные на обучающие и тестовые наборы
4. Сопоставьте и преобразуйте data
5. Обучить классификатор
6. Предсказать результаты набора тестов
7. Оценить результаты
8. Подведение итогов

1. Скачать набор данных

Загрузите набор данных по ссылке ниже и поместите его в тот же каталог
, что и ваш блокнот ipython. Данные содержат 300 положительных и
300 отрицательных отзывов о фильмах.

http://www.cs.cornell.edu/people/pabo/movie-review-data/review_polarity.tar.gz

Структура данных:

--review_polarity
    |-- txt_sentoken
         |-- pos
              |-- cv999_13106.txt
              |-- cv998_14111.txt
              |-- ...
         |-- neg
              |-- cv999_14636.txt
              |-- cv998_15691.txt
              |-- ...

2. Загрузить набор данных в память

import os
# intialize X and y to store review text and sentiment
X = []
y = []
# create lists of files in /pos and /neg directories
pos_review_filenames = os.listdir('review_polarity/txt_sentoken/pos')
new_review_filenames = os.listdir('review_polarity/txt_sentoken/neg')
# add text from positive reviews to X and label 1
for filename in pos_review_filenames:
    file = open('review_polarity/txt_sentoken/pos/' + filename)
    text = file.read()
    # remove line breaks, '/n'
    string = ' '.join(text.split('\n'))
    # add to X
    X.append(string)
    # these reviews are all positive so label them, 1
    y.append(1)
    
# do the same with negative reviews and label 0 
for filename in new_review_filenames:
    file = open('review_polarity/txt_sentoken/neg/' + filename)
    text = file.read()
    # remove line breaks, '/n'
    string = ' '.join(text.split('\n'))
    # add to X
    X.append(string)
    # these reviews are not positive so label this data, 0
    y.append(0)

3. Разделить данные на обучающие и тестовые наборы

Теперь разделим данные на отдельные группы для обучения и тестирования. Обучение научит нашу модель классифицировать данные, а тестирование проверит ее на данных, не используемых для обучения.

В идеале оба набора разбиваются случайным образом, но сохраняют одинаковые пропорции классов. StratifiedShuffleSplit делает именно это (http://scikit-learn.org/stable/modules/generated/sklearn.model_selection.StratifiedShuffleSplit.html).

Теперь определите функцию для разделения данных.

from sklearn.model_selection import StratifiedShuffleSplit
import numpy as np
def split_data(contents, labels):
    splitter = StratifiedShuffleSplit(n_splits=1, test_size=0.3, random_state=0)
for train_index, test_index in splitter.split(contents, labels):
        # Notice we're not splitting at a specific index but passing an array withindices were assigning to each set
        # '_pre_vectorize' here reminds me this data has not yet been converted into tf-idf features
        X_train_pre_vectorize, X_test_pre_vectorize = X[train_index], X[test_index]
        y_train, y_test = y[train_index], y[test_index]
        
    return X_train_pre_vectorize, y_train, X_test_pre_vectorize, y_test

И разделить данные.

# convert the X and y to arrays as this allows us to extract multiple elements via list of indices
X = np.array(X)
y = np.array(y)
X_train_pre_vectorize, y_train, X_test_pre_vectorize, y_test = split_data(X, y)

4. Подгонка и преобразование данных

Это преобразует данные в матрицу оценок tf-idf на основе модели набора слов. Числовой вывод будет передан нашему классификатору, который не может обрабатывать слова самостоятельно.

from sklearn.feature_extraction.text import TfidfVectorizer
# initialize instance of TfidfVectorizer
vectorizer = TfidfVectorizer()
# note we .fit_transform train, but only transform test, so all features are learned from train
X_train = vectorizer.fit_transform(X_train_pre_vectorize)
X_test = vectorizer.transform(X_test_pre_vectorize)

5. Классификатор поездов

Я выбрал классификатор, который в целом работает хорошо, но в реальном проекте я бы поэкспериментировал с несколькими классификаторами.

from sklearn.ensemble import RandomForestClassifier
# initialize an instance of RandomForestClassifier
classifier = RandomForestClassifier()
# train the classifier
classifier.fit(X_train, y_train)

6. Прогнозирование результатов набора тестов

И вуаля! Наша модель предсказывает метку на основе матриц tf-idf, полученных из исходных текстов обзоров.

y_pred = classifier.predict(X_test)

7. Оцените результаты

Давайте посмотрим, как поступила наша модель.

from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred))

Результаты:

                precision    recall  f1-score   support

          0       0.64      0.83      0.72       300
          1       0.76      0.53      0.63       300

avg / total       0.70      0.68      0.67       600

8. Подведение итогов

Результаты показывают, что наш классификатор «хорошо» классифицирует отзывы как положительные. Хотя со взвешенной оценкой f1 0,67 я бы не поставил эту модель близко к серийному производству.

Тем не менее, это зависит от последствий ложноположительных и ложноотрицательных результатов в каждом уникальном варианте использования.

Несколько быстрых способов, которыми мы могли бы попытаться улучшить результаты:
i) использование диапазона ngram в нашем векторизаторе
ii) удаление стоп-слов
iii) создание корневого или лемматизирующего текста отзыва
iv) экспериментировать с другими классификаторами