Выполним классификацию отзывов по тональности на готовой обучающей выборке
In [4]:
from nltk.corpus import stopwords as nltk_stop_words
from nltk.corpus import words
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
#from sklearn.model_selection import cross_val_score
from sklearn.cross_validation import cross_val_score
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.svm import LinearSVC
from sklearn.linear_model import SGDClassifier
import pandas as pd
import datetime
import numpy as np
from time import time
from sklearn.grid_search import GridSearchCV
Определим необходимые функции. Чтение данных и разделение на обучающую и тестовую выборки.
In [5]:
def read_data():
X_train = []
X_test = []
id_test = []
y_train = []
with open('products_sentiment_train.tsv') as f:
for line in f:
parts = line.rsplit('\t', 1)
X_train.append(parts[0].strip())
y_train.append(parts[1].strip())
with open('products_sentiment_test.tsv') as f:
f.readline()
for line in f:
parts = line.split('\t', 1)
id_test.append(parts[0].strip())
X_test.append(parts[1].strip())
return X_train, y_train, id_test, X_test
Обучение и классификация выбранным классификатором, сохранение результатов в файл с временной меткой в названии для удобства.
In [6]:
def predict(predictor, data_train, y, id_test, data_test, cv_score=None):
predictor.fit(data_train, y)
prediction = predictor.predict(data_test)
#print predictor
timestamp = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
filepath_prediction = 'data/prediction-%s-data.csv' % timestamp
filepath_description = 'data/prediction-%s-estimator.txt' % timestamp
# Create a dataframe with predictions and write it to CSV file
predictions_df = pd.DataFrame(data=prediction, columns=['y'])
predictions_df.to_csv(filepath_prediction, sep=',', index_label='Id')
# Write a short description of the classifier that was used
f = open(filepath_description, 'w')
f.write(str(predictor))
score = '\nCross-validation score %.8f' % cv_score
f.write(score)
f.close()
Далее описаны различные конфигурации для выделения признаков и классификации, использовавшиеся в экспериментах. Для каждой конфигурации также указывается набор параметров и перечень значений, использовавшихся для подбора наилучшего сочетания.
Вариант-1 с признаками на основе счетчика слов и классификация логистической регрессией.
In [37]:
def get_pipeline_and_params_1():
pipeline = Pipeline([
('vect', CountVectorizer()),
('logreg', LogisticRegression()),
])
parameters = {
'vect__max_df': (0.6, 0.8, 1.0),
'vect__min_df': (0, 1, 2, 5),
'vect__stop_words': ('english', None),
'vect__ngram_range': ((1, 1), (1, 2)), # unigrams or bigrams
'logreg__C': (0.0001, 0.01, 1),
'logreg__penalty': ('l2', 'l1'),
}
return pipeline, parameters
Вариант-2 с признаками на основе частотности слов (TF-IDF) и классификация логистической регрессией.
In [41]:
def get_pipeline_and_params_2():
pipeline = Pipeline([
('tfidf', TfidfVectorizer()),
('logreg', LogisticRegression()),
])
parameters = {
'tfidf__max_df': (0.6, 0.8, 1.0),
'tfidf__min_df': (0, 5, 10, 15),
'tfidf__ngram_range': ((1, 1), (1, 2), (1,3), (2,3)), # unigrams or bigrams
#'tfidf__use_idf': (True, False),
'tfidf__norm': ('l1', 'l2'),
'logreg__C': (0.0001, 0.01, 1),
'logreg__penalty': ('l2', 'l1'),
}
return pipeline, parameters
Вариант-3 с признаками на основе частотности слов (TF-IDF) и классификатор на основе стохастического градиентного спуска.
In [28]:
def get_pipeline_and_params_3():
# gives 0.7895 on cross-validation and 0.835 on Kaggle. done in 108.629s
pipeline = Pipeline([
('vect', CountVectorizer()),
('tfidf', TfidfTransformer()),
('clf', SGDClassifier()),
])
parameters = {
'vect__max_df': (0.5, 0.75, 1.0),
#'vect__max_features': (None, 5000, 10000, 50000),
'vect__ngram_range': ((1, 3), (1, 2)), # unigrams or bigrams
#'tfidf__use_idf': (True, False),
'tfidf__norm': ('l1', 'l2'),
'clf__alpha': (0.00001, 0.000001),
'clf__penalty': ('l2', 'elasticnet'),
'clf__n_iter': (10, 50, 80),
}
return pipeline, parameters
Вариант-4 с признаками на основе частотности слов (TF-IDF) и классификация методом опорных векторов.
In [51]:
def get_pipeline_and_params_4():
nltk_sw = nltk_stop_words.words('english')
pipeline = Pipeline([
('vect', CountVectorizer()),
('tfidf', TfidfTransformer()),
('clf', LinearSVC()),
])
parameters = {
#'vect__max_df': (0.8, 0.9, 1),
'vect__min_df': (0, 5, 10),
#'vect__vocabulary': (None),
'vect__stop_words': ('english', nltk_sw, None),
'vect__max_features': (None, 5000, 10000, 50000),
#'vect__analyzer' : ('word', 'char', 'char_wb',),
'vect__ngram_range': ((1,1), (1, 4), (1, 3)), # unigrams or bigrams
#'tfidf__use_idf': (True, False),
#'tfidf__smooth_idf': (True, False),
#'tfidf__norm': ('l1', 'l2'),
#'tfidf__sublinear_tf': (True, False),
#'clf__C': (0.00001, 0.001, 0.1, 1),
}
return pipeline, parameters
Вариант-5 с признаками на основе частотности слов (TF-IDF) и классификатор на основе стохастического градиентного спуска с использванием корпуса английских слов, стоп-слов и анализатора частей слов (параметр "char_wb").
In [87]:
def get_pipeline_and_params_5():
wordlist = set(words.words())
nltk_sw = nltk_stop_words.words('english')
pipeline = Pipeline([
('vect', CountVectorizer(vocabulary=wordlist)),
('tfidf', TfidfTransformer()),
('clf', SGDClassifier()),
])
parameters = {
'vect__strip_accents': ('ascii', None),
'vect__analyzer': ('word', 'char_wb'),
'vect__max_df': (0.8, 1.0),
#'vect__min_df': (0, 1, 2),
#'vect__max_features': (100, 200, 500, 1000, 2000, 5000),
'vect__vocabulary': (None, wordlist),
'vect__stop_words': ('english', nltk_sw, None),
'vect__ngram_range': ((1, 3), (1, 1), (1, 2)),
'tfidf__use_idf': (True,),
'tfidf__norm': ('l1',),
'clf__alpha': (0.000001,),
'clf__penalty': ('l1',),
#'clf__n_iter': (10, 50, 80),
}
return pipeline, parameters
Функция перебора комбинаций параметров и определения наилучшей конфигурации с помощью GridSearchCV
In [30]:
def do_grid_search(pipeline, parameters, X_train, y_train):
grid_search = GridSearchCV(pipeline, parameters, scoring='accuracy')
t0 = time()
grid_search.fit(X_train, y_train)
print "done in %0.3fs" % (time() - t0)
print("Best score: %.4f" % grid_search.best_score_)
print("Best parameters set:")
best_parameters = grid_search.best_estimator_.get_params()
for param_name in sorted(parameters.keys()):
print("\t%s: %r" % (param_name, best_parameters[param_name]))
return grid_search
Основная фунция, описывающая отдельный эксперимент: сформировать тестируюмую конфигирацию и наборы параметров, выполнить перебор для определения наилучших и выполнить классификацию тестовых данных с помощью лучшего классификатора.
In [34]:
def do_experiment(X_train, y_train, id_test, X_test, get_pipeline_and_params):
pipeline, parameters = get_pipeline_and_params()
gs = do_grid_search(pipeline, parameters, X_train, y_train)
predict(gs.best_estimator_, X_train, y_train, id_test, X_test, gs.best_score_)
Чтение исходных данных
In [35]:
X_train, y_train, id_test, X_test = read_data()
Выполнение серии экспериментов, их результаты по точности при кросс-валидации и подобранные значения параметров.
In [38]:
do_experiment(X_train, y_train, id_test, X_test, get_pipeline_and_params_1)
In [43]:
do_experiment(X_train, y_train, id_test, X_test, get_pipeline_and_params_2)
In [64]:
do_experiment(X_train, y_train, id_test, X_test, get_pipeline_and_params_3)
In [52]:
do_experiment(X_train, y_train, id_test, X_test, get_pipeline_and_params_4)
In [89]:
do_experiment(X_train, y_train, id_test, X_test, get_pipeline_and_params_5)
Результаты при кросс-валидации на обучающей выборке, разумеется, отличались от результатов на тестовой выборке, полученных на сайте Kaggle.
На тестовых данных лучший результат показала конфигурация, описанная в варианте-3 - признаки по TF-IDF и классификация с помощью SGDClassifier. Результат на Kaggle составил 0.835.
In [ ]: