Как использовать GridSearchCV для многочленов разных степеней?

#python #python-3.x #scikit-learn #gridsearchcv #k-fold

#python #python-3.x #scikit-learn #gridsearchcv #k-кратный

Вопрос:

Что я хотел сделать, так это просмотреть некоторые OLS, подходящие для разных степеней многочленов, чтобы увидеть, какая степень лучше работает при прогнозировании mpg horsepower (при использовании LOOCV и KFold). Я написал код, но я не мог понять, как применить PolynomialFeatures функцию к каждой итерации с использованием GridSearchCv , поэтому я закончил тем, что написал это:

 import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import LeaveOneOut, KFold
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error



df = pd.read_csv('http://web.stanford.edu/~oleg2/hse/auto/Auto.csv')[['horsepower','mpg']].dropna()

pows = range(1,11)
first, second, mse = [], [], 0     # 'first' is data for the first plot and 'second' is for the second one

for p in pows:
  mse = 0
  for train_index, test_index in LeaveOneOut().split(df):
      x_train, x_test = df.horsepower.iloc[train_index], df.horsepower.iloc[test_index]
      y_train, y_test = df.mpg.iloc[train_index], df.mpg.iloc[test_index]
      polynomial_features = PolynomialFeatures(degree = p)
      x = polynomial_features.fit_transform(x_train.values.reshape(-1,1))   #getting the polynomial
      ft = LinearRegression().fit(x,y_train)
      x1 = polynomial_features.fit_transform(x_test.values.reshape(-1,1))   #getting the polynomial
      mse  = mean_squared_error(y_test, ft.predict(x1))
  first.append(mse/len(df))
    
for p in pows: 
    temp = []   
    for i in range(9):      # this is to plot a few graphs for comparison
        mse = 0
        for train_index, test_index in KFold(10, True).split(df):
            x_train, x_test = df.horsepower.iloc[train_index], df.horsepower.iloc[test_index]
            y_train, y_test = df.mpg.iloc[train_index], df.mpg.iloc[test_index]
            polynomial_features = PolynomialFeatures(degree = p)
            x = polynomial_features.fit_transform(x_train.values.reshape(-1,1))   #getting the polynomial
            ft = LinearRegression().fit(x,y_train)
            x1 = polynomial_features.fit_transform(x_test.values.reshape(-1,1))   #getting the polynomial
            mse  = mean_squared_error(y_test, ft.predict(x1))
        temp.append(mse/10)
    second.append(temp)      


f, pt = plt.subplots(1,2,figsize=(12,5.1))
f.tight_layout(pad=5.0)
pt[0].set_ylim([14,30])
pt[1].set_ylim([14,30])
pt[0].plot(pows, first, color='darkblue', linewidth=1)
pt[0].scatter(pows, first, color='darkblue')
pt[1].plot(pows, second)
pt[0].set_title("LOOCV", fontsize=15)
pt[1].set_title("10-fold CV", fontsize=15)
pt[0].set_xlabel('Degree of Polynomial', fontsize=15)
pt[1].set_xlabel('Degree of Polynomial', fontsize=15)
pt[0].set_ylabel('Mean Squared Error', fontsize=15)
pt[1].set_ylabel('Mean Squared Error', fontsize=15)
plt.show()
  

Он выдает:
График

Это полностью работает, и вы можете запустить его на своем компьютере, чтобы протестировать его. Это делает именно то, что я хочу, но это кажется действительно чрезмерным. Я прошу совета о том, как улучшить его, используя GridSearchCv или что-нибудь еще, на самом деле. Я попытался передать PolynomialFeatures как конвейер LinearRegression() , но не смог заставить его изменять x на лету. Рабочий пример был бы очень признателен.

Комментарии:

1. Может быть, вы можете указать, почему ваш конвейерный подход не сработал? Здесь это кажется «правильным» подходом, извлекающим результаты из поиска по cv_results_ сетке для построения графика.

2. @BenReiniger Я действительно не знаю, как работают конвейеры, если честно, но я попытался передать степень многочлена в качестве аргумента моей функции, которую я включил в конвейер. Проблема заключалась в том, что я хочу x , чтобы параметр (в данном случае df.horsepower) был передан этой функции, чтобы я мог его изменить, а затем подгонка будет выполнена в отредактированной версии, но, похоже, способа сделать это нет.

Ответ №1:

Похоже, что это способ сделать это:

 pipe = Pipeline(steps=[
    ('poly', PolynomialFeatures(include_bias=False)),
    ('model', LinearRegression()),
])

search = GridSearchCV(
    estimator=pipe,
    param_grid={'poly__degree': list(pows)},
    scoring='neg_mean_squared_error',
    cv=LeaveOneOut(),
)

search.fit(df[['horsepower']], df.mpg)

first = -search.cv_results_['mean_test_score']
  

(отрицательный в этой последней строке, потому что показатель оценки отрицательный mse)

И затем построение графика может происходить более или менее одинаково. (Здесь мы полагаемся на cv_results_ то, что записи расположены в том же порядке, pows что и; возможно, вы захотите построить график, используя соответствующие столбцы pd.DataFrame(search.cv_results_) вместо.)

Вы можете использовать RepeatedKFold для эмуляции вашего цикла KFold , хотя тогда вы получите только один график; если вам действительно нужны отдельные графики, вам все равно нужен внешний цикл, но поиск по сетке с cv=KFold(...) помощью может заменить внутренний цикл.

Комментарии:

1. Это именно то, что я хотел. Спасибо. Хотя для вычисления этого требуется в два раза больше времени (по сравнению с моим примером). Я не понимаю, почему. Я даже добавил параметр n_jobs = -1 , но это не помогло. Я понимаю, что для вычисления дополнительной статистики должно потребоваться некоторое время, но разница не должна быть такой существенной.

2. Чтобы было понятно, под «временем» я подразумеваю «время стены». Процессорное время на самом деле лучше, чем в моем примере, что имеет еще меньше смысла на 4-ядерном процессоре.

3. Проблема с синхронизацией странная. Единственное реальное отличие заключается в том, что поиск по сетке будет перестроен на «лучший» k; вы можете отказаться от этого refit=False . Все остальное — небольшие дополнения, такие как упомянутая вами дополнительная статистика…

4. Я уже пробовал это на самом деле. Просто сделал это еще раз, чтобы убедиться, но, к сожалению, это не имеет значения

5. Преобразование из фрейма данных в массив numpy перед подгонкой (добавление .values в поиск fit ) помогает совсем немного, хотя все еще немного медленнее в экземпляре colab; Я думаю, повторное преобразование довольно дорого. Странно, что даже после этого мы работаем медленнее.