Осталось менее трех дней до окончания конкурса «Оценка производительности». Возможно, данная статья кому-то поможет улучшить свое решение. Суть задачи — предсказать время умножения двух матриц на разных вычислительных системах. В качестве оценки качества предсказания берется наименьшая средняя относительная ошибка MAPE.
На текущий момент первое место — 4.68%. Ниже хочу описать свой путь к 6.69% (а это уже 70+ место).
Итак, у нас имеются данные для обучения в виде таблицы c 951 колонкой. Такое огромное количество признаков даже нет смысла начинать анализировать «вручную». Поэтому попробуем применить какой-нибудь стандартный алгоритм «не глядя», но с небольшой подготовкой данных:
Данные манипуляции дают mape = 11.22%. А это 154 из 362 место. Т.е. лучше, чем половина участников.
Для применения линейных алгоритмов необходимо масштабировать признаки. Кроме этого иногда помогает добавление новых признаков на основании уже имеющихся. Например, с помощью PolynomialFeatures. Поскольку вычислить полином для всех 951 признака крайне ресурсоёмко, то разобьём все признаки на две части:
И будем вычислять полином только на матричных признаках. Кроме этого, вектор откликов (y) перед обучением прологарифмируем, а при расчете ответов вернем прежний масштаб.
Пару нехитрых манипуляций уже дают mape = 6.91% (место 80 из 362). Стоит обратить внимание, что модель RidgeCV() вызывается со стандартными параметрами. В теории её можно еще потюнинговать.
Самый лучший результат mape = 6.69% (72/362) дало «ручное» добавление признаков. Добавил три признака m*n, m*k, k*n.
Така же добавил отношение максимального измерения матрицы к минимальному измерению для обоих матриц.
Признаюсь, что матрицы умею умножать только с помощью Википедии. А методы Штрассена и алгоритмы Виноградова, указанные в описании к заданию, для меня что-то нереальное к освоению. Для меня это первое участие в соревновании по машинному обучению. И чувство собственной гордости повышает тот факт, что полученный результат неплохо смотрится на фоне работы, на которую ссылаются авторы конкурса — А. А. Сиднева, В. П. Гергеля «Автоматический выбор наиболее эффективных реализаций алгоритмов».
На текущий момент первое место — 4.68%. Ниже хочу описать свой путь к 6.69% (а это уже 70+ место).
Итак, у нас имеются данные для обучения в виде таблицы c 951 колонкой. Такое огромное количество признаков даже нет смысла начинать анализировать «вручную». Поэтому попробуем применить какой-нибудь стандартный алгоритм «не глядя», но с небольшой подготовкой данных:
Попытка №1
- Пропуски есть только в memFreq, около 11%. Заменим пропуски на среднее значение;
- Удалим неинформативные колонки (в которых одно значение признака);
- Применим ExtraTreesRegressor.
Данные манипуляции дают mape = 11.22%. А это 154 из 362 место. Т.е. лучше, чем половина участников.
Попытка №2
Для применения линейных алгоритмов необходимо масштабировать признаки. Кроме этого иногда помогает добавление новых признаков на основании уже имеющихся. Например, с помощью PolynomialFeatures. Поскольку вычислить полином для всех 951 признака крайне ресурсоёмко, то разобьём все признаки на две части:
- связанные с производительностью;
- связанные с самой матрицей (m k n);
И будем вычислять полином только на матричных признаках. Кроме этого, вектор откликов (y) перед обучением прологарифмируем, а при расчете ответов вернем прежний масштаб.
Пару нехитрых манипуляций уже дают mape = 6.91% (место 80 из 362). Стоит обратить внимание, что модель RidgeCV() вызывается со стандартными параметрами. В теории её можно еще потюнинговать.
Попытка №3
Самый лучший результат mape = 6.69% (72/362) дало «ручное» добавление признаков. Добавил три признака m*n, m*k, k*n.
Така же добавил отношение максимального измерения матрицы к минимальному измерению для обоих матриц.
Код для воспроизведения результата
import numpy as np
import pandas as pd
from sklearn import linear_model
def write_answer(data, str_add=''):
with open("answer"+str(str_add)+".txt", "w") as fout:
fout.write('\n'.join(map(str, data)))
def convert_cat(inf,inf_data):
return inf_data[inf_data == inf].index[0]
X = pd.read_csv('x_train.csv')
y = pd.read_csv('y_train.csv')
X_check = pd.read_csv('x_test.csv')
#Преобразуем memFreq в число. Пустые значения заполним средним
X.memFreq = pd.to_numeric(X.memFreq, errors = 'coerce')
mean_memFreq = 525.576
X.fillna(value = mean_memFreq, inplace=True)
X_check.memFreq = pd.to_numeric(X_check.memFreq, errors = 'coerce')
X_check.fillna(value = mean_memFreq, inplace=True)
# удаляем неинформативные колонки
for c in X.columns:
if len(np.unique(X_check[c])) == 1:
X.drop(c, axis=1, inplace=True)
X_check.drop(c, axis=1, inplace=True)
# Преобразуем категориальные признаки
cpuArch_ = pd.Series(np.unique(X.cpuArch))
X.cpuArch = X.cpuArch.apply(lambda x: convert_cat(x,cpuArch_))
X_check.cpuArch = X_check.cpuArch.apply(lambda x: convert_cat(x,cpuArch_))
memType_ = pd.Series(np.unique(X.memType))
X.memType = X.memType.apply(lambda x: convert_cat(x,memType_))
X_check.memType = X_check.memType.apply(lambda x: convert_cat(x,memType_))
memtRFC_ = pd.Series(np.unique(X.memtRFC))
X.memtRFC = X.memtRFC.apply(lambda x: convert_cat(x,memtRFC_))
X_check.memtRFC = X_check.memtRFC.apply(lambda x: convert_cat(x,memtRFC_))
os_ = pd.Series(np.unique(X.os))
X.os = X.os.apply(lambda x: convert_cat(x,os_))
X_check.os = X_check.os.apply(lambda x: convert_cat(x,os_))
cpuFull_ = pd.Series(np.unique(X.cpuFull))
X.cpuFull = X.cpuFull.apply(lambda x: convert_cat(x,cpuFull_))
X_check.cpuFull = X_check.cpuFull.apply(lambda x: convert_cat(x,cpuFull_))
# Признаки связанные с производительностью
perf_features = X.columns[3:]
# Признаки матриц
X['log_mn'] = np.log(X.m * X.n)
X['log_mk'] = np.log(np.int64(X.m*X.k))
X['log_kn'] = np.log(np.int64(X.k*X.n))
X['min_max_a'] = np.float64(X.loc[:, ['m', 'k']].max(axis=1)) / X.loc[:, ['m', 'k']].min(axis=1)
X['min_max_b'] = np.float64(X.loc[:, ['n', 'k']].max(axis=1)) / X.loc[:, ['n', 'k']].min(axis=1)
X_check['log_mn'] = np.log(X_check.m * X_check.n)
X_check['log_mk'] = np.log(np.int64(X_check.m*X_check.k))
X_check['log_kn'] = np.log(np.int64(X_check.k*X_check.n))
X_check['min_max_a'] = np.float64(X_check.loc[:, ['m', 'k']].max(axis=1)) / X_check.loc[:, ['m', 'k']].min(axis=1)
X_check['min_max_b'] = np.float64(X_check.loc[:, ['n', 'k']].max(axis=1)) / X_check.loc[:, ['n', 'k']].min(axis=1)
model = linear_model.RidgeCV(cv=5)
model.fit(X, np.log(y))
y_answer = np.exp(model.predict(X_check))
write_answer(y_answer.reshape(4947), '_habr_RidgeCV')
Послесловие
Признаюсь, что матрицы умею умножать только с помощью Википедии. А методы Штрассена и алгоритмы Виноградова, указанные в описании к заданию, для меня что-то нереальное к освоению. Для меня это первое участие в соревновании по машинному обучению. И чувство собственной гордости повышает тот факт, что полученный результат неплохо смотрится на фоне работы, на которую ссылаются авторы конкурса — А. А. Сиднева, В. П. Гергеля «Автоматический выбор наиболее эффективных реализаций алгоритмов».
Поделиться с друзьями
Комментарии (5)
kenoma
19.07.2016 07:46Ради интереса решил поучаствовать, чтобы испытать xgboost на деле. Сходу, после предобработки данных выхлоп составил ~10%, после подгонки параметров модели ~8.8%. Ну а дальше следовало строить стекованные модели, но внятных результатов получить не успел.
makiiim
19.07.2016 11:45я использовал идею описанную в А. А. Сиднева, В. П. Гергеля «Автоматический выбор наиболее эффективных реализаций алгоритмов», с некоторыми модификациями, получил итоговые 5.7% и 44 место
Koncopd
Разбиение датасета на 89 категорий (по признаку ос+процессор+размер памяти) и построение взвешенной по признаку time lad регрессии дало ошибку в 5.4% и 21 место. Знаю, что человек на третьем месте также использовал этот вариант, но с несколькими дополнениями.
Belov
Это отлично. Для меня это был первый опыт. Узнал много нового.
Но больше удивило, как люди получали более 15-20%. Когда варианты «в лоб» давали лучший результат.
polar_winter
Почти тоже самое: разбиение датасета на категории(по признаку ос+процессор+размер памяти), логарифмизация времени и сложности (m*n*k), RANSAC (от времени и сложности) — 5.8% 58 место