Прогноз времени с TPOT

Автоматизируйте конвейер машинного обучения, найдите наиболее эффективную модель машинного обучения

Мой коллега по работе порекомендовал мне несколько замечательных библиотек машинного обучения, и некоторые из них были для меня новыми. Поэтому я решил попробовать их по очереди. Сегодня очередь TPOT.

Набор данных был о прогнозировании инженеров Daimler скорости системы тестирования автомобилей Mercedes-Benz, цель состоит в том, чтобы сократить время, которое автомобили тратят на тестирование, с более чем тремя сотнями функций. Честно говоря, у меня мало или совсем нет опыта в автомобильной промышленности. Тем не менее, я постараюсь делать самые точные прогнозы, используя TPOT, инструмент автоматизированного машинного обучения Python, который оптимизирует конвейеры машинного обучения с использованием генетического программирования.

Данные

Этот набор данных содержит анонимный набор переменных, каждая из которых представляет собой особенность автомобиля Mercedes.

Целевая функция помечена буквой y и представляет время (в секундах), которое потребовалось автомобилю, чтобы пройти тестирование для каждой переменной. Набор данных можно найти здесь.

train = pd.read_csv('mer_train.csv')
print('Train shape: ', train.shape)

Мы знаем, какие у нас проблемы: слишком много функций (столбцов) и недостаточно строк.

Кроме того, мы не знаем, что это за функции, кроме «y» и «ID».

Целевая особенность

Целевая характеристика «y» - это время (в секундах), которое потребовалось автомобилю для прохождения тестирования по каждой переменной. Посмотрим на его распространение.

plt.figure(figsize = (10, 6))
n, bins, patches = plt.hist(train['y'], 50, facecolor='blue', alpha=0.75)
plt.xlabel('y value in seconds')
plt.ylabel('count')
plt.title('Histogram of y value')
plt.show();

train['y'].describe()

plt.figure(figsize = (10, 6))
plt.scatter(range(train.shape[0]), np.sort(train['y'].values))
plt.xlabel('index')
plt.ylabel('y')
plt.title("Time Distribution")
plt.show();

Есть один выброс - максимальное время в 265 секунд.

Особенности исследования

cols = [c for c in train.columns if 'X' in c]
print('Number of features except ID and target feature: {}'.format(len(cols)))
print('Feature types :')
train[cols].dtypes.value_counts()

Из всех функций у нас есть 8 категориальных функций и 368 целочисленных функций. А как насчет количества элементов? Следующие идеи и сценарии были от Mikel Bober Irizar.

counts = [[], [], []]
for c in cols:
    typ = train[c].dtypes
    uniq = len(train[c].unique())
    if uniq == 1:
        counts[0].append(c)
    elif uniq == 2 and typ == np.int64:
        counts[1].append(c)
    else:
        counts[2].append(c)
print('Constant features: {} Binary features: {} Categorical features: {}\n'.format(*[len(c) for c in counts]))
print('Constant features: ', counts[0])
print()
print('Categorical features: ', counts[2])

Было 12 функций, в которых содержалось только одно значение (0), они бесполезны для контролируемых алгоритмов, и мы откажемся от них позже.

Остальная часть нашего набора данных состоит из 356 двоичных функций и 8 категориальных функций. Давайте сначала рассмотрим категориальные особенности.

Категориальные особенности

for cat in ['X0', 'X1', 'X2', 'X3', 'X4', 'X5', 'X6', 'X8']:
    print("Number of levels in category '{0}': \b {1:2}".format(cat, train[cat].nunique()))

Функция X0

sort_X0 = train.groupby('X0').size()\
                    .sort_values(ascending=False)\
                    .index
plt.figure(figsize=(12,6))
sns.countplot(x='X0', data=train, order = sort_X0)
plt.xlabel('X0')
plt.ylabel('Occurances')
plt.title('Feature X0')
sns.despine();

X0 по сравнению с целевой функцией y

sort_y = train.groupby('X0')['y']\
                    .median()\
                    .sort_values(ascending=False)\
                    .index
plt.figure(figsize = (14, 6))
sns.boxplot(y='y', x='X0', data=train, order=sort_y)
ax = plt.gca()
ax.set_xticklabels(ax.get_xticklabels())
plt.title('X0 vs. y value')
plt.show();

Функция X1

sort_X1 = train.groupby('X1').size()\
                    .sort_values(ascending=False)\
                    .index
plt.figure(figsize=(12,6))
sns.countplot(x='X1', data=train, order = sort_X1)
plt.xlabel('X1')
plt.ylabel('Occurances')
plt.title('Feature X1')
sns.despine();

X1 и целевая функция y

sort_y = train.groupby('X1')['y']\
                    .median()\
                    .sort_values(ascending=False)\
                    .index
plt.figure(figsize = (10, 6))
sns.boxplot(y='y', x='X1', data=train, order=sort_y)
ax = plt.gca()
ax.set_xticklabels(ax.get_xticklabels())
plt.title('X1 vs. y value')
plt.show();

Функция X2

sort_X2 = train.groupby('X2').size()\
                    .sort_values(ascending=False)\
                    .index
plt.figure(figsize=(12,6))
sns.countplot(x='X2', data=train, order = sort_X2)
plt.xlabel('X2')
plt.ylabel('Occurances')
plt.title('Feature X2')
sns.despine();

X2 и целевая функция y

sort_y = train.groupby('X2')['y']\
                    .median()\
                    .sort_values(ascending=False)\
                    .index
plt.figure(figsize = (12, 6))
sns.boxplot(y='y', x='X2', data=train, order=sort_y)
ax = plt.gca()
ax.set_xticklabels(ax.get_xticklabels())
plt.title('X2 vs. y value')
plt.show();

Функция X3

sort_X3 = train.groupby('X3').size()\
                    .sort_values(ascending=False)\
                    .index
plt.figure(figsize=(12,6))
sns.countplot(x='X3', data=train, order = sort_X3)
plt.xlabel('X3')
plt.ylabel('Occurances')
plt.title('Feature X3')
sns.despine();

X3 и целевая функция y

sort_y = train.groupby('X3')['y']\
                    .median()\
                    .sort_values(ascending=False)\
                    .index
plt.figure(figsize = (10, 6))
sns.boxplot(y='y', x='X3', data=train, order = sort_y)
ax = plt.gca()
ax.set_xticklabels(ax.get_xticklabels())
plt.title('X3 vs. y value')
plt.show();

Функция X4

sort_X4 = train.groupby('X4').size()\
                    .sort_values(ascending=False)\
                    .index
plt.figure(figsize=(12,6))
sns.countplot(x='X4', data=train, order = sort_X4)
plt.xlabel('X4')
plt.ylabel('Occurances')
plt.title('Feature X4')
sns.despine();

X4 и целевая функция y

sort_y = train.groupby('X4')['y']\
                    .median()\
                    .sort_values(ascending=False)\
                    .index
plt.figure(figsize = (10, 6))
sns.boxplot(y='y', x='X4', data=train, order = sort_y)
ax = plt.gca()
ax.set_xticklabels(ax.get_xticklabels())
plt.title('X4 vs. y value')
plt.show();

Функция X5

sort_X5 = train.groupby('X5').size()\
                    .sort_values(ascending=False)\
                    .index
plt.figure(figsize=(12,6))
sns.countplot(x='X5', data=train, order = sort_X5)
plt.xlabel('X5')
plt.ylabel('Occurances')
plt.title('Feature X5')
sns.despine();

X5 и целевая функция y

sort_y = train.groupby('X5')['y']\
                    .median()\
                    .sort_values(ascending=False)\
                    .index
plt.figure(figsize = (12, 6))
sns.boxplot(y='y', x='X5', data=train, order=sort_y)
ax = plt.gca()
ax.set_xticklabels(ax.get_xticklabels())
plt.title('X5 vs. y value')
plt.show();

Функция X6

sort_X6 = train.groupby('X6').size()\
                    .sort_values(ascending=False)\
                    .index
plt.figure(figsize=(12,6))
sns.countplot(x='X6', data=train, order = sort_X6)
plt.xlabel('X6')
plt.ylabel('Occurances')
plt.title('Feature X6')
sns.despine();

X6 и целевая функция y

sort_y = train.groupby('X6')['y']\
                     .median()\
                     .sort_values(ascending=False)\
                     .index
plt.figure(figsize = (12, 6))
sns.boxplot(y='y', x='X6', data=train, order=sort_y)
ax = plt.gca()
ax.set_xticklabels(ax.get_xticklabels())
plt.title('X6 vs. y value')
plt.show();

Функция X8

sort_X8 = train.groupby('X8').size()\
                    .sort_values(ascending=False)\
                    .index
plt.figure(figsize=(12,6))
sns.countplot(x='X8', data=train, order = sort_X8)
plt.xlabel('X8')
plt.ylabel('Occurances')
plt.title('Feature X8')
sns.despine();

X8 и целевая функция y

sort_y = train.groupby('X8')['y']\
                    .median()\
                    .sort_values(ascending=False)\
                    .index
plt.figure(figsize = (12, 6))
sns.boxplot(y='y', x='X8', data=train, order=sort_y)
ax = plt.gca()
ax.set_xticklabels(ax.get_xticklabels())
plt.title('X8 vs. y value')
plt.show();

К сожалению, мы не многому научились из вышеупомянутого EDA, это жизнь. Однако мы заметили, что некоторые категориальные особенности влияют на «y», а «X0», по-видимому, оказывает наибольшее влияние.

После изучения мы собираемся закодировать уровни этих категориальных функций в виде цифр с помощью Scikit-learn MultiLabelBinarizer и рассматривать их как новые функции.

Затем мы отбрасываем закодированные константы и категориальные особенности, а также нашу целевую функцию «y».

train_new = train.drop(['y','X11', 'X93', 'X107', 'X233', 'X235', 'X268', 'X289', 'X290', 'X293', 'X297', 'X330', 'X347', 'X0', 'X1', 'X2', 'X3', 'X4', 'X5', 'X6', 'X8'], axis=1)

Затем мы добавляем закодированные функции, чтобы сформировать окончательный набор данных, который будет использоваться с TPOT.

train_new = np.hstack((train_new.values, X0_trans, X1_trans, X2_trans, X3_trans, X4_trans, X5_trans, X6_trans, X8_trans))

Окончательный набор данных имеет форму массива numpy в форме (4209, 552).

TPOT

Пришло время построить и настроить регрессор TPOT. По завершении TPOT отобразит гиперпараметры «лучшей» модели (на основе тестовых данных MSE в нашем случае), а также выведет конвейеры в виде готового к выполнению файла сценария Python для дальнейшего использования или нашего исследования.

Выполнение приведенного выше кода обнаружит конвейер как результат, который достигает 56 среднеквадратичной ошибки (MSE) на тестовом наборе:

print("TPOT cross-validation MSE")
print(tpot.score(X_test, y_test))

Вы могли заметить, что MSE - отрицательное число, согласно этому потоку, это neg_mean_squared_error для TPOTRegressor, что означает отрицательное значение среднеквадратичной ошибки. Давай попробуем еще раз.

from sklearn.metrics import mean_squared_error
print('MSE:')
print(mean_squared_error(y_test, tpot.predict(X_test)))

print('RMSE:')
print(np.sqrt(mean_squared_error(y_test, tpot.predict(X_test))))

Итак, разница между нашим прогнозируемым временем и реальным временем составляет около 7,5 секунд. Это неплохой результат! И модель, которая дает этот результат, соответствует модели RandomForestRegressor, объединенной с алгоритмом KNeighborsRegressor в наборе данных.

Наконец, мы собираемся экспортировать этот конвейер:

tpot.export('tpot_Mercedes_testing_time_pipeline.py')

Мне понравилось изучать и использовать TPOT, надеюсь, вы такие же. Блокнот Jupyter можно найти на Github. Хороших выходных!

Ссылка: Учебник TPOT