Загружаем необходимые библиотеки
%pylab inline
import numpy as np
import warnings
from scipy import sparse
from sklearn.metrics import f1_score, make_scorer
from sklearn.model_selection import GridSearchCV
from sklearn.base import BaseEstimator, ClassifierMixin
from sklearn.neighbors import KNeighborsClassifier
from sklearn.multiclass import OneVsRestClassifier
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
warnings.filterwarnings('ignore')
Populating the interactive namespace from numpy and matplotlib
import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = (20, 10)
Выключаем предупреждения вида
UserWarning: Label not 83 is present in all training examples.
Такие сообщения возникают, если в данных не представлен какой-либо класс. Это будет происходит при кросс-валидации.
Напишем функцию для считывания данных в разреженную матрицу.
def read_data_from_file(filename, shape):
values = list()
rows = list()
cols = list()
header = True
for line in open(filename):
if header:
header = False
continue
row, col, value = [x for x in line.strip().split(',')]
row, col = int(row), int(col)
value = float(value)
row -= 1
col -= 1
values.append(value)
rows.append(row)
cols.append(col)
return sparse.csr_matrix((values, (rows, cols)), shape=shape)
Считаем данные
X_train = read_data_from_file('X_train.csv', (15000, 30000)).astype(float)
X_test = read_data_from_file('X_test.csv', (15000, 30000)).astype(float)
print X_train.shape, X_test.shape
(15000, 30000) (15000, 30000)
Нормируем признаки
from sklearn.preprocessing import scale
X_all = sparse.vstack([X_train, X_test])
X_all = scale(X_all, with_mean=False)
X_train = X_all[:15000, :]
X_test = X_all[15000:, :]
del X_all
Напишем функцию для считывания меток
def read_labels_from_file(filename, shape):
labels = np.zeros(shape).astype(int)
header = True
for line in open(filename):
if header:
header = False
continue
row, indeces = line.strip().split(',')
row = int(row) - 1
indeces = [int(x) - 1 for x in indeces.split()]
labels[row, indeces] = 1
return labels
Считаем истинные метки
y_train = read_labels_from_file('y_train.csv', (15000, 98))
print y_train.shape
(15000, 98)
И, наконец, напишем функция для вывода меток в нужном формате
def write_labels_to_file(labels, filename):
outfile = open(filename, 'w')
print >> outfile, "Id,Labels"
for i, line in enumerate(labels):
elements = [str(x) for x in list(nonzero(line)[0] + 1)]
print >> outfile, "%d,%s" % (i + 1, ' '.join(elements))
В качестве бейзлайна воспользуемся методом ближайшего соседа. Подберем оптимальное число соседей в соответствии с требуемой метрикой.
clf = KNeighborsClassifier(weights='distance')
params = {'n_neighbors': np.logspace(0, 2, 5).astype(int)}
grid_searcher = GridSearchCV(
clf,
params,
scoring=make_scorer(f1_score, average='samples'),
cv=5
)
grid_searcher.fit(X_train, y_train)
GridSearchCV(cv=5, error_score='raise', estimator=KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski', metric_params=None, n_jobs=1, n_neighbors=5, p=2, weights='distance'), fit_params={}, iid=True, n_jobs=1, param_grid={'n_neighbors': array([ 1, 3, 10, 31, 100])}, pre_dispatch='2*n_jobs', refit=True, return_train_score=True, scoring=make_scorer(f1_score, average=samples), verbose=0)
Напишем вспомогательную функцию для визуализации результатов
def plot_grid_searcher_results(grid_seacher, log_scale):
means = list()
param_values = list()
for score in grid_searcher.grid_scores_:
print score.cv_validation_scores
means.append(score.mean_validation_score)
param_values.append(score.parameters.values()[0])
parameter_name = grid_searcher.grid_scores_[0].parameters.keys()[0]
means = np.array(means)
plot(param_values, means)
if log_scale:
xscale('log')
xlabel(parameter_name)
ylabel('Quality')
Визуализируем качество работы алгоритма в зависимости от числа соседей
plot_grid_searcher_results(grid_searcher, log_scale=True)
[ 0.17516349 0.20506032 0.19978889 0.20208889 0.17111746] [ 0.07706667 0.09141111 0.09285556 0.10145556 0.07095556] [ 0.00937778 0.01782222 0.01397778 0.02093333 0.0095 ] [ 0.00266667 0.00388889 0.00482222 0.0066 0.00255556] [ 0.00133333 0.00133333 0.0016 0.0026 0.00133333]
Видно, что оптимум достигается при малом числе соседей. Посмотрим ближе на интересующий нас кусок графика.
params = {'n_neighbors': range(1, 5)},
grid_searcher = GridSearchCV(
clf,
params,
scoring=make_scorer(f1_score, average='samples'),
cv=5
)
grid_searcher.fit(X_train, y_train)
plot_grid_searcher_results(grid_searcher, log_scale=False)
[ 0.17516349 0.20506032 0.19978889 0.20208889 0.17111746] [ 0.17483016 0.20506032 0.19978889 0.20208889 0.17111746] [ 0.07706667 0.09141111 0.09285556 0.10145556 0.07095556] [ 0.07946667 0.08521111 0.08474444 0.09351111 0.06967778]
Видно, что оптимум достигается при значениях равных 1 или 2, мы возьмём 1.
Обучимся на произвольной трети объектов (чтобы сократить время обучения) и сохраним результат в файл.
X_train_sampled, _, y_train_sampled, _ = train_test_split(X_train, y_train, test_size=0.66, random_state=0)
print X_train_sampled.shape
print y_train_sampled.shape
clf = KNeighborsClassifier(n_neighbors=1)
clf.fit(X_train_sampled, y_train_sampled)
y_test_knn = clf.predict(X_test)
write_labels_to_file(y_test_knn, 'y_test_knn.csv')
(5100, 30000) (5100, 98)
Оценим качество в зависимости от параметра регуляризации
params = {'estimator__C': np.logspace(-5, 5, 5)},
clf = OneVsRestClassifier(LogisticRegression())
grid_searcher = GridSearchCV(
clf,
params,
scoring=make_scorer(f1_score, average='samples'),
cv=5
)
grid_searcher.fit(X_train, y_train)
GridSearchCV(cv=5, error_score='raise', estimator=OneVsRestClassifier(estimator=LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1, penalty='l2', random_state=None, solver='liblinear', tol=0.0001, verbose=0, warm_start=False), n_jobs=1), fit_params={}, iid=True, n_jobs=1, param_grid=({'estimator__C': array([ 1.00000e-05, 3.16228e-03, 1.00000e+00, 3.16228e+02, 1.00000e+05])},), pre_dispatch='2*n_jobs', refit=True, return_train_score=True, scoring=make_scorer(f1_score, average=samples), verbose=0)
Визуализируем зависимость качества от регуляризации
plot_grid_searcher_results(grid_searcher, log_scale=True)
[ 0.012 0.01827778 0.01911111 0.01617778 0.01727778] [ 0.26772222 0.28839524 0.30769524 0.31254603 0.27094127] [ 0.30994444 0.35085238 0.34671746 0.36735714 0.31738571] [ 0.30760317 0.34440952 0.33458968 0.35829921 0.32420714] [ 0.30205748 0.33565108 0.33240847 0.35022884 0.31906854]
Видно, что наиболее удачным оказывается значение $C = 1$. Обучим классификатор на всех данных и сохраним результат в файл.
clf.fit(X_train, y_train)
y_test_lr = clf.predict(X_test)
write_labels_to_file(y_test_lr, 'y_test_lr.csv')