В четвертой части знакомства с автоматизированное машинное обучение будем работать с библиотекой TROT по книге "Radečić Dario. Machine Learning Automation with TPOT: Build, validate, and deploy fully automated machine learning models with Python".
Работаем с тремя наборами данных : рыбный рынок, страхование и транспортные средства.
| Species | Weight | Length1 | Length2 | Length3 | Height | Width | |
|---|---|---|---|---|---|---|---|
| 0 | Bream | 242.0 | 23.2 | 25.4 | 30.0 | 11.5200 | 4.0200 |
| 1 | Bream | 290.0 | 24.0 | 26.3 | 31.2 | 12.4800 | 4.3056 |
| 2 | Bream | 340.0 | 23.9 | 26.5 | 31.1 | 12.3778 | 4.6961 |
| 3 | Bream | 363.0 | 26.3 | 29.0 | 33.5 | 12.7300 | 4.4555 |
| 4 | Bream | 430.0 | 26.5 | 29.0 | 34.0 | 12.4440 | 5.1340 |
Species 0 Weight 0 Length1 0 Length2 0 Length3 0 Height 0 Width 0 dtype: int64
Пропущенных значений нет. Это делает процесс подготовки данных гораздо прощеи короче. Следующим шагом является проверка распределения целевой переменной.Для этого набора данных мы пытаемся предсказать вес. Нарисуем простоюгистограмму для этой переменной
df['Weight'].hist(bins=20, figsize = (12,8))plt.title('Target variable (Weight) distribution', size=20)plt.xlabel('Weight', size=14)plt.ylabel('Count', size=14)
============ BREAM ============ Count: 35 Pct. total: 22.01% Avg. Weight: 617.83 +/- 209.21 Avg. Length1: 30.31 +/- 3.59 Avg. Length2: 33.11 +/- 3.91 Avg. Length3: 38.35 +/- 4.16 Avg. Height: 15.18 +/- 1.96 Avg. Width: 5.43 +/- 0.72 ============ ROACH ============ Count: 20 Pct. total: 12.58% Avg. Weight: 152.05 +/- 88.83 Avg. Length1: 20.65 +/- 3.46 Avg. Length2: 22.28 +/- 3.65 Avg. Length3: 24.97 +/- 4.03 Avg. Height: 6.69 +/- 1.26 Avg. Width: 3.66 +/- 0.69 ============ WHITEFISH ============ Count: 6 Pct. total: 3.77% Avg. Weight: 531.0 +/- 309.6 Avg. Length1: 28.8 +/- 5.58 Avg. Length2: 31.32 +/- 5.72 Avg. Length3: 34.32 +/- 6.02 Avg. Height: 10.03 +/- 1.83 Avg. Width: 5.47 +/- 1.19 ============ PARKKI ============ Count: 11 Pct. total: 6.92% Avg. Weight: 154.82 +/- 78.76 Avg. Length1: 18.73 +/- 3.28 Avg. Length2: 20.35 +/- 3.56 Avg. Length3: 22.79 +/- 3.96 Avg. Height: 8.96 +/- 1.62 Avg. Width: 3.22 +/- 0.64 ============ PERCH ============ Count: 56 Pct. total: 35.22% Avg. Weight: 382.24 +/- 347.62 Avg. Length1: 25.74 +/- 8.56 Avg. Length2: 27.89 +/- 9.02 Avg. Length3: 29.57 +/- 9.53 Avg. Height: 7.86 +/- 2.88 Avg. Width: 4.75 +/- 1.77 ============ PIKE ============ Count: 17 Pct. total: 10.69% Avg. Weight: 718.71 +/- 494.14 Avg. Length1: 42.48 +/- 9.03 Avg. Length2: 45.48 +/- 9.71 Avg. Length3: 48.72 +/- 10.17 Avg. Height: 7.71 +/- 1.66 Avg. Width: 5.09 +/- 1.14 ============ SMELT ============ Count: 14 Pct. total: 8.81% Avg. Weight: 11.18 +/- 4.13 Avg. Length1: 11.26 +/- 1.22 Avg. Length2: 11.92 +/- 1.43 Avg. Length3: 13.04 +/- 1.43 Avg. Height: 2.21 +/- 0.35 Avg. Width: 1.34 +/- 0.29
Для кодирования используем функцию get_dummies. Параметр набор дамми-переменных drop_first задает тип дамми-кодирования. По умолчанию для этого параметра задано значение False и выполняется дамми-кодирование по методу неполного ранга, в противном случае будет выполнено дамми-кодирование по методу полного ранга. В нашем случае функция pd.get_dummies() автоматически преобразует заданную категориальную переменную методом полного ранга в набор из (n-1) дамми-переменных.
| Is_Parkki | Is_Perch | Is_Pike | Is_Roach | Is_Smelt | Is_Whitefish | Weight | Length1 | Length2 | Length3 | Height | Width | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 242.0 | 23.2 | 25.4 | 30.0 | 11.5200 | 4.0200 |
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 290.0 | 24.0 | 26.3 | 31.2 | 12.4800 | 4.3056 |
| 2 | 0 | 0 | 0 | 0 | 0 | 0 | 340.0 | 23.9 | 26.5 | 31.1 | 12.3778 | 4.6961 |
| 3 | 0 | 0 | 0 | 0 | 0 | 0 | 363.0 | 26.3 | 29.0 | 33.5 | 12.7300 | 4.4555 |
| 4 | 0 | 0 | 0 | 0 | 0 | 0 | 430.0 | 26.5 | 29.0 | 34.0 | 12.4440 | 5.1340 |
((119,), (40,))
Далее создадим модель с алгоритмом линейной регрессии. Эта модель задаст базовый уровень, который должен превзойти TPOT82.10709519987505
Базовая модель ошибается в среднем на 82 единицы веса. Неплохо, учитывая у нас вес до 1500. Далее подгоним модель оптимизации конвейера TPOT. Будемиспользовать показатель RMSE и ограничим время оптимизации 10 минутами.
rmse_scorer = make_scorer(rmse, greater_is_better=False)pipeline_optimizer = TPOTRegressor(scoring=rmse_scorer,max_time_mins=10,random_state=42)pipeline_optimizer.fit(X_train, y_train)TPOTRegressor(max_time_mins=10, random_state=42, scoring=make_scorer(<lambda>, greater_is_better=False))После завершения оптимизации вот вывод RMSE, который отображается в консоли:pipeline_optimizer.score(X_test, y_test)-78.16527159947128Не беспокойтесь о знаке минус перед числом. Фактическое среднеквадратичноеотклонение равно 73,35 единицы веса. Модель TPOT превзошла базовую. В качествепоследнего шага мы можем экспортировать конвейер в файл Python :pipeline_optimizer.fitted_pipeline_Pipeline(steps=[('polynomialfeatures', PolynomialFeatures(include_bias=False)), ('robustscaler', RobustScaler()), ('maxabsscaler', MaxAbsScaler()), ('stackingestimator-1', StackingEstimator(estimator=ExtraTreesRegressor(bootstrap=True, max_features=0.9000000000000001, min_samples_leaf=17, random_state=42))), ('stackingestimator-2', StackingEstimator(estimator=DecisionTreeRegressor(max_depth=10, min_samples_leaf=10, min_samples_split=9, random_state=42))), ('adaboostregressor', AdaBoostRegressor(learning_rate=0.5, loss='square', n_estimators=100, random_state=42))])pipeline_optimizer.export('fish_pipeline.py')Далее используем решение для автоматизированного машинного обучения к немного
более сложному набор данных. Это набор данных о стоимости медицинского
страхования (https://www.kaggle.com/mirichoi0218/insurance), чтобы предсказать, сколько страховка будет стоить на основе нескольких переменных-предикторов.
| age | sex | bmi | children | smoker | region | charges | |
|---|---|---|---|---|---|---|---|
| 0 | 19 | female | 27.900 | 0 | yes | southwest | 16884.92400 |
| 1 | 18 | male | 33.770 | 1 | no | southeast | 1725.55230 |
| 2 | 28 | male | 33.000 | 3 | no | southeast | 4449.46200 |
| 3 | 33 | male | 22.705 | 0 | no | northwest | 21984.47061 |
| 4 | 32 | male | 28.880 | 0 | no | northwest | 3866.85520 |
age 0 sex 0 bmi 0 children 0 smoker 0 region 0 charges 0 dtype: int64
Для визуализации нашего анализа используем функцию, которая делает столбчатую диаграмму для выбранного показателя. Она вычисляет медиану из сгруппированного набор данных и визуализирует гистограмму с заголовком, метками, легендой и текстом поверх столбца.
| region_northwest | region_southeast | region_southwest | age | is_female | bmi | children | smoker | charges | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 1 | 19 | 1 | 27.900 | 0 | 1 | 16884.92400 |
| 1 | 0 | 1 | 0 | 18 | 0 | 33.770 | 1 | 0 | 1725.55230 |
| 2 | 0 | 1 | 0 | 28 | 0 | 33.000 | 3 | 0 | 4449.46200 |
| 3 | 1 | 0 | 0 | 33 | 0 | 22.705 | 0 | 0 | 21984.47061 |
| 4 | 1 | 0 | 0 | 32 | 0 | 28.880 | 0 | 0 | 3866.85520 |
((1003,), (335,))
Создадим базовую модель с алгоритмом линейной регрессии. Её показатели TPOT должен превзойти.
from sklearn.linear_model import LinearRegressionfrom sklearn.metrics import r2_score, mean_squared_errorrmse = lambda y, y_hat: np.sqrt(mean_squared_error(y, y_hat))lm = LinearRegression()lm.fit(X_train, y_train)lm_preds = lm.predict(X_test)print(f'R2 = {r2_score(y_test, lm_preds):.2f}')print(f'RMSE = {rmse(y_test, lm_preds):.2f}')R2 = 0.77 RMSE = 5926.02В среднем простая модель линейной регрессии ошибается на 5 926,02 доллара. Этамодель фиксирует 77% дисперсии в наборе данных. Следующий фрагмент кодавыводит имя переменной и соответствующий ему коэффициенты:for i, column in enumerate(df.columns[:-1]):coef = np.round(lm.coef_[i], 2)print(f'{column:17}: {coef:8}')region_northwest : -355.15 region_southeast : -781.37 region_southwest : -860.27 age : 259.62 is_female : -45.62 bmi : 339.81 children : 426.5 smoker : 23630.4Как видите, колонка с наибольшим коэффициентом — курильщик. Что подтверждаетнашу визуализацию, сделанную на этапе исследовательского анализа данных. Далеебудем использовать библиотеку TPOT для создания автоматизированного конвейерамашинного обучения. Следующий фрагмент кода импортирует библиотеку TPOT,создает ее экземпляр и конвейер оптимизации.from tpot import TPOTRegressorpipeline_optimizer = TPOTRegressor(scoring='r2',max_time_mins=10,random_state=42,verbosity=2)pipeline_optimizer.fit(X_train, y_train)10 минут не хватило, вышло следующее сообщение10.02 minutes have elapsed. TPOT will close down. TPOT closed during evaluation in one generation. WARNING: TPOT may not provide a good pipeline if TPOT is stopped/interrupted in a early generation. TPOT closed prematurely. Will use the current best pipeline. Best pipeline: RandomForestRegressorОценка R2 на тестовом наборе может быть получена с помощью следующего кода:pipeline_optimizer.score(X_test, y_test)0.865150174670897Можете получить значения R2 и RMSE для тестового :tpot_preds = pipeline_optimizer.predict(X_test)print(f'R2 = {r2_score(y_test, tpot_preds):.2f}')print(f'RMSE = {rmse(y_test, tpot_preds):.2f}')R2 = 0.87 RMSE = 4510.84В качестве последнего шага мы экспортируем оптимизированный конвейер в файлPython.Следующее фрагмент кода делает это:pipeline_optimizer.export('insurance_pipeline.py')
| 0 | 1 | 2 | 3 | 4 | |
|---|---|---|---|---|---|
| name | Maruti Swift Dzire VDI | Skoda Rapid 1.5 TDI Ambition | Honda City 2017-2020 EXi | Hyundai i20 Sportz Diesel | Maruti Swift VXI BSIII |
| year | 2014 | 2014 | 2006 | 2010 | 2007 |
| selling_price | 450000 | 370000 | 158000 | 225000 | 130000 |
| km_driven | 145500 | 120000 | 140000 | 127000 | 120000 |
| fuel | Diesel | Diesel | Petrol | Diesel | Petrol |
| seller_type | Individual | Individual | Individual | Individual | Individual |
| transmission | Manual | Manual | Manual | Manual | Manual |
| owner | First Owner | Second Owner | Third Owner | First Owner | First Owner |
| mileage | 23.4 kmpl | 21.14 kmpl | 17.7 kmpl | 23.0 kmpl | 16.1 kmpl |
| engine | 1248 CC | 1498 CC | 1497 CC | 1396 CC | 1298 CC |
| max_power | 74 bhp | 103.52 bhp | 78 bhp | 90 bhp | 88.2 bhp |
| torque | 190Nm@ 2000rpm | 250Nm@ 1500-2500rpm | 12.7@ 2,700(kgm@ rpm) | 22.4 kgm at 1750-2750rpm | 11.5@ 4,500(kgm@ rpm) |
| seats | 5.0 | 5.0 | 5.0 | 5.0 | 5.0 |
name 0 year 0 selling_price 0 km_driven 0 fuel 0 seller_type 0 transmission 0 owner 0 mileage 221 engine 221 max_power 215 torque 222 seats 221 dtype: int64
Некоторые значения отсутствуют, и мы решим эту проблему с помощью самого простого подход – путем их удаления.
df.dropna(inplace=True)df.isnull().sum()name 0 year 0 selling_price 0 km_driven 0 fuel 0 seller_type 0 transmission 0 owner 0 mileage 0 engine 0 max_power 0 torque 0 seats 0 dtype: int64Список шагов, необходимых для того, чтобы сделать этот набор данных подходящимдля машинное обучение:• Преобразование transmission в целое число – 1, механическая, 0 в противномслучае. Переименуйте столбец в is_manual.• Переназначить столбец owner на целые числа.• Извлечь марку автомобиля, пробег, двигатель и максимальную мощность изсоответствующих атрибутов• Создать фиктивные переменные из имени атрибута, топлива и типа продавца.• Объединить исходный набор данных с фиктивными переменными и удалитьненужные атрибуты.Вот код функции remap_owner():
| 0 | 1 | 2 | 3 | 4 | |
|---|---|---|---|---|---|
| year | 2014.0 | 2014.00 | 2006.0 | 2010.0 | 2007.0 |
| selling_price | 450000.0 | 370000.00 | 158000.0 | 225000.0 | 130000.0 |
| km_driven | 145500.0 | 120000.00 | 140000.0 | 127000.0 | 120000.0 |
| is_manual | 1.0 | 1.00 | 1.0 | 1.0 | 1.0 |
| owner | 1.0 | 2.00 | 3.0 | 1.0 | 1.0 |
| mileage | 23.4 | 21.14 | 17.7 | 23.0 | 16.1 |
| engine | 1248.0 | 1498.00 | 1497.0 | 1396.0 | 1298.0 |
| max_power | 74.0 | 103.52 | 78.0 | 90.0 | 88.2 |
| seats | 5.0 | 5.00 | 5.0 | 5.0 | 5.0 |
| brand_Ashok | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Audi | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_BMW | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Chevrolet | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Daewoo | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Datsun | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Fiat | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Force | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Ford | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Honda | 0.0 | 0.00 | 1.0 | 0.0 | 0.0 |
| brand_Hyundai | 0.0 | 0.00 | 0.0 | 1.0 | 0.0 |
| brand_Isuzu | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Jaguar | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Jeep | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Kia | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Land | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Lexus | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_MG | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Mahindra | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Maruti | 1.0 | 0.00 | 0.0 | 0.0 | 1.0 |
| brand_Mercedes-Benz | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Mitsubishi | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Nissan | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Opel | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Renault | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Skoda | 0.0 | 1.00 | 0.0 | 0.0 | 0.0 |
| brand_Tata | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Toyota | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Volkswagen | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| brand_Volvo | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| fuel_Diesel | 1.0 | 1.00 | 0.0 | 1.0 | 0.0 |
| fuel_LPG | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
| fuel_Petrol | 0.0 | 0.00 | 1.0 | 0.0 | 1.0 |
| seller_Individual | 1.0 | 1.00 | 1.0 | 1.0 | 1.0 |
| seller_Trustmark Dealer | 0.0 | 0.00 | 0.0 | 0.0 | 0.0 |
((5929,), (1977,))
Для оценки точности модели используем среднюю абсолютную процентную ошибку(MAPE).Эта метрика не встроена в scikit-learn. библиотеку, поэтому нам придется реализоватьее вручную.
def mape(y, y_hat):y, y_hat = np.array(y), np.array(y_hat)return np.mean(np.abs((y - y_hat) / y)) * 100Делаем базовую модель - линейная регрессия с показателями R2 и MAPE
R2 = 0.87
MAPE = 42.87
В среднем базовая модель ошибочна на 43%. Это много. Посмотрим на коэффициентмодели линейной регрессии, чтобы определить, их важность
for i, column in enumerate(df.columns[:-1]):coef = np.round(lm.coef_[i], 2)print(f'{column:20}: {coef:12}')year : 43014.37 selling_price : -0.99 km_driven : -93417.99 is_manual : -29134.49 owner : -3223.42 mileage : 51.82 engine : 6442.18 max_power : -6664.42 seats : -0.0 brand_Ashok : 732079.41 brand_Audi : 2062806.87 brand_BMW : -413173.11 brand_Chevrolet : 143836.25 brand_Daewoo : -456934.44 brand_Datsun : -412771.18 brand_Fiat : -350143.14 brand_Force : -351419.7 brand_Ford : -359997.28 brand_Honda : -350773.5 brand_Hyundai : 382947.89 brand_Isuzu : 1084736.28 brand_Jaguar : 403502.72 brand_Jeep : 61492.6 brand_Kia : 2185633.63 brand_Land : 3094325.97 brand_Lexus : 206214.99 brand_MG : -327026.98 brand_Mahindra : -256695.95 brand_Maruti : 689295.55 brand_Mercedes-Benz : -57724.47 brand_Mitsubishi : -371642.93 brand_Nissan : -0.0 brand_Opel : -369176.64 brand_Renault : -422164.25 brand_Skoda : -455033.96 brand_Tata : -19784.95 brand_Toyota : -432636.85 brand_Volkswagen : 1494271.37 brand_Volvo : 153467.18 fuel_Diesel : 178327.49 fuel_LPG : 58605.79 fuel_Petrol : -49419.66 seller_Individual : -76786.21Чем хороша линейная регрессия, так это понятной интерпретируемостьюкоэффициентов.Чем выше год, тем новее автомобиль, тем более высокая цена. Чем больше километров автомобиль проехал, тем больше снижается цена. Автомобили с автоматическойпередачей стоят дороже. Нот точность недостаточна. Ее должен улучшить TROT.Подгоним модель TPOT и оптимизируем ее для оценки MAPE. Обучим модель в течение 10 минут на каждом доступном ядре ЦП (обозначается n_jobs=-1):from tpot import TPOTRegressorfrom sklearn.metrics import make_scorermape_scorer = make_scorer(mape, greater_is_better=False)pipeline_optimizer = TPOTRegressor(scoring=mape_scorer,max_time_mins=10,random_state=42,verbosity=2,n_jobs=-1)pipeline_optimizer.fit(X_train, y_train)На выходе получаем :10.07 minutes have elapsed. TPOT will close down. TPOT closed during evaluation in one generation. WARNING: TPOT may not provide a good pipeline if TPOT is stopped/interrupted in a early generation. TPOT closed prematurely. Will use the current best pipeline. Best pipeline: GradientBoostingRegressorОптимизация через 10 минут была прервана, посмотрим что получается при этом натестовой выборкеtpot_preds = pipeline_optimizer.predict(X_test)print(f'R2 = {r2_score(y_test, tpot_preds):.2f}')print(f'MAPE = {mape(y_test, tpot_preds):.2f}')R2 = 0.97 MAPE = 14.78Как видно, TROT значительно улучшил показатели точности.








Комментариев нет:
Отправить комментарий