Розв'язання завдання «Оцінка продуктивності» mlbootcamp.ru

Залишилося менше трьох днів до закінчення конкурсу «Оцінка продуктивності». Можливо, дана стаття комусь допоможе поліпшити своє рішення. Суть завдання — передбачити час множення двох матриць на різних обчислювальних системах. В якості оцінки якості передбачення береться найменша середня відносна помилка MAPE.

На поточний момент перше місце — 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, оперативне=True)

X_check.memFreq = pd.to_numeric(X_check.memFreq, errors = 'coerce')
X_check.fillna(value = mean_memFreq, оперативне=True)

# видаляємо неінформативні колонки
for c in X. columns:
if len(np.unique(X_check[c])) == 1:
X. drop(c, axis=1, оперативне=True)
X_check.drop(c, axis=1, оперативне=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(os X.))
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')



Післямова

Зізнаюся, що матриці вмію множити тільки з допомогою Вікіпедії. А методи Штрассена і алгоритми Виноградова, вказані в описі до завдання, для мене щось нереальне до освоєння. Для мене це перша участь у змаганні за машинного навчання. І почуття власної гордості підвищує той факт, що отриманий результат непогано виглядає на тлі роботи, на яку посилаються автори конкурсу — А. А. Сиднева, В. П. Гергеля «Автоматичний вибір найбільш ефективних реалізацій алгоритмів».
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.