In [1]:
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
plt.style.use('bmh')
%matplotlib inline
In [2]:
import random
random.seed(42)
np.random.seed(42)
In [3]:
data = pd.read_csv('/Users/tatanakuzenko/cian_data_clear_for_modeling.csv')
data.head()
Out[3]:
Готовим данные к линейной модели. Уберем n-ные столбцы после one-hot encoding, чтоб не образовывалась линейная зависимость
In [4]:
data.drop(['Bal_na', 'Distr_N', 'Brick_na'], axis = 1, inplace = True)
Вариант с квадратами: зависимость цены от каких-то признаков может быть квадратичной. Не будем исследовать очень подробно, от каких именно, просто по всем не категориальным добавим колонку "признак в квадрате". По идее тем, которые зависят скорее линейно, модель сама поставит в коэфициент при квадрате что-то маленькое.
In [5]:
data_sq = data.copy()
squared_columns = ['Distance', 'Kitsp', 'Livsp', 'Totsp', 'Metrokm']
squared_columns_new = ['Distance_sq', 'Kitsp_sq', 'Livsp_sq', 'Totsp_sq', 'Metrokm_sq']
for i in range(len(squared_columns)):
data_sq[squared_columns_new[i]] = [x**2 for x in data_sq[squared_columns[i]]]
In [6]:
data_sq.head(3)
Out[6]:
Вариант поскейленных данных
In [7]:
from sklearn.preprocessing import scale
to_scale = ['Distance', 'Floor', 'Kitsp', 'Livsp', 'Nfloors', 'Totsp', 'Metrokm']
data_sc = data.copy()
data_sc[to_scale] = scale(data_sc[to_scale], axis = 1)
In [8]:
data_sc.head(3)
Out[8]:
В прошлом ноутбуке изучается рпспределение цен. Оно так себе - очень большая плотность в начале вариационного ряда и она быстро падает до очень маленьких значений. При таких данных имеет смысл прологарифмировать колонку и работать уже с логарифмом. Не забудем перед посчетом ошибки модели возвести экспоненту в получившуюся степень.
In [9]:
data_log = data.copy()
data_log['Price'] = np.log(data_log['Price'])
In [10]:
data_log['Price'].head()
Out[10]:
Комбинации вышеизложенных вариантов.
In [11]:
data_sc_log = data.drop('Price', axis = 1)
data_sc_log[to_scale] = scale(data_sc_log[to_scale], axis = 1)
data_sc_log['Price'] = np.log(data['Price'])
In [12]:
data_sc_log.head(3)
Out[12]:
In [13]:
data_sq_log = data.drop('Price', axis = 1)
for i in range(len(squared_columns)):
data_sq_log[squared_columns_new[i]] = [x**2 for x in data_sq_log[squared_columns[i]]]
data_sq_log['Price'] = np.log(data['Price'])
In [14]:
data_sq_log.head(3)
Out[14]:
In [15]:
data_sc_sq_log = data.drop('Price', axis = 1)
data_sc_sq_log[to_scale] = scale(data_sc_sq_log[to_scale], axis = 1)
for i in range(len(squared_columns)):
data_sc_sq_log[squared_columns_new[i]] = [x**2 for x in data_sc_sq_log[squared_columns[i]]]
data_sc_sq_log['Price'] = np.log(data['Price'])
In [16]:
data_sc_sq_log.head(3)
Out[16]:
In [17]:
data_sq_sc_log = data.drop('Price', axis = 1)
for i in range(len(squared_columns)):
data_sq_sc_log[squared_columns_new[i]] = [x**2 for x in data_sq_sc_log[squared_columns[i]]]
data_sq_sc_log[to_scale] = scale(data_sq_sc_log[to_scale], axis = 1)
data_sq_sc_log['Price'] = np.log(data['Price'])
In [18]:
data.head(3)
Out[18]:
In [19]:
datasets = [data, data_sq, data_sc, data_log, data_sc_log, data_sq_log, data_sc_sq_log, data_sq_sc_log]
Для каждого варианта составляем модель, обучаем ее и сопоставляем ошибки.
In [20]:
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
from sklearn.metrics import mean_absolute_error
from math import sqrt
simple = LinearRegression()
squared = LinearRegression()
scaled = LinearRegression()
log = LinearRegression()
sc_log = LinearRegression()
sq_log = LinearRegression()
sc_sq_log = LinearRegression()
sq_sc_log = LinearRegression()
models = [
('simple', simple),
('squared', squared),
('scaled', scaled),
('log', log),
('sc_log', sc_log),
('sq_log', sq_log),
('sc_sq_log', sc_sq_log),
('sq_sc_log', sq_sc_log)
]
for i in range(len(models)):
tmp = datasets[i]
train = tmp.drop('Price', axis = 1)
target = tmp['Price']
target = pd.DataFrame(target)
Xtr, Xval, Ytr, Yval = train_test_split(train, target, test_size=0.2)
modelname, model = models[i]
model.fit(Xtr, Ytr);
predictions = model.predict(Xval)
if (modelname.find('log', 0, len(modelname)) < 0):
rmse = sqrt(mean_squared_error(Yval, predictions))
mae = mean_absolute_error(Yval, predictions)
print('Model:', modelname, ' RMSE: ', rmse, ' MAE: ', mae)
else:
rmse = sqrt(mean_squared_error(np.exp(Yval), np.exp(predictions)))
mae = mean_absolute_error(np.exp(Yval), np.exp(predictions))
print('Model:', modelname, ' RMSE: ', rmse, ' MAE: ', mae)
При нескольких прогонках мною было подмечено, что ошибки моделей хоть и меняются, но относительно друг друга модели в среднем особо не передвигатся: лидируют simple, squared и sq_log. Теперь попробуем регуляризовать.
In [21]:
from sklearn.linear_model import Lasso
simple = Lasso(alpha = 0.01, max_iter = 100000, tol = 0.01)
squared = Lasso(alpha = 0.01, max_iter = 100000, tol = 0.01)
scaled = Lasso(alpha = 0.01, max_iter = 100000, tol = 0.01)
log = Lasso(alpha = 0.01, max_iter = 100000, tol = 0.01)
sc_log = Lasso(alpha = 0.01, max_iter = 100000, tol = 0.01)
sq_log = Lasso(alpha = 0.01, max_iter = 100000, tol = 0.01)
sc_sq_log = Lasso(alpha = 0.01, max_iter = 100000, tol = 0.01)
sq_sc_log = Lasso(alpha = 0.01, max_iter = 100000, tol = 0.01)
models = [
('simple', simple),
('squared', squared),
('scaled', scaled),
('log', log),
('sc_log', sc_log),
('sq_log', sq_log),
('sc_sq_log', sc_sq_log),
('sq_sc_log', sq_sc_log)
]
for i in range(len(models)):
tmp = datasets[i]
train = tmp.drop('Price', axis = 1)
target = tmp['Price']
target = pd.DataFrame(target)
Xtr, Xval, Ytr, Yval = train_test_split(train, target, test_size=0.2)
modelname, model = models[i]
model.fit(Xtr, Ytr);
predictions = model.predict(Xval)
coef = model.coef_
zeros = []
for j in range(len(Xtr.columns)):
if coef[j] == 0:
zeros.append(Xtr.columns[j])
if (modelname.find('log', 0, len(modelname)) < 0):
rmse = sqrt(mean_squared_error(Yval, predictions))
mae = mean_absolute_error(Yval, predictions)
print('Model:', modelname, ' RMSE: ', rmse, ' MAE: ', mae, ' Coef: ', zeros)
else:
rmse = sqrt(mean_squared_error(np.exp(Yval), np.exp(predictions)))
mae = mean_absolute_error(np.exp(Yval), np.exp(predictions))
print('Model:', modelname, ' RMSE: ', rmse, ' MAE: ', mae, ' Coef: ', zeros)
В среднем RMSE была чуть меньше. Лучше остальных те же модели (вот это уже стабильно просматривалось): sq_log, simple, squared.
In [23]:
from sklearn.linear_model import Ridge
simple = Ridge(alpha = .01, max_iter = 10000)
squared = Ridge(alpha = .01, max_iter = 10000)
scaled = Ridge(alpha = .01, max_iter = 10000)
log = Ridge(alpha = .01, max_iter = 10000)
sc_log = Ridge(alpha = .01, max_iter = 10000)
sq_log = Ridge(alpha = .01, max_iter = 10000)
sc_sq_log = Ridge(alpha = .01, max_iter = 10000)
sq_sc_log = Ridge(alpha = .01, max_iter = 10000)
models = [
('simple', simple),
('squared', squared),
('scaled', scaled),
('log', log),
('sc_log', sc_log),
('sq_log', sq_log),
('sc_sq_log', sc_sq_log),
('sq_sc_log', sq_sc_log)
]
for i in range(len(models)):
tmp = datasets[i]
train = tmp.drop('Price', axis = 1)
target = tmp['Price']
target = pd.DataFrame(target)
Xtr, Xval, Ytr, Yval = train_test_split(train, target, test_size=0.2)
modelname, model = models[i]
model.fit(Xtr, Ytr);
predictions = model.predict(Xval)
if (modelname.find('log', 0, len(modelname)) < 0):
rmse = sqrt(mean_squared_error(Yval, predictions))
mae = mean_absolute_error(Yval, predictions)
print('Model:', modelname, ' RMSE: ', rmse, ' MAE: ', mae)
else:
rmse = sqrt(mean_squared_error(np.exp(Yval), np.exp(predictions)))
mae = mean_absolute_error(np.exp(Yval), np.exp(predictions))
print('Model:', modelname, ' RMSE: ', rmse, ' MAE: ', mae)
Если усреднять запуски, то результаты лучше у модели с регуляризацией, причем у Ridge. По итогам всех эспериментов наилучшей выглядит модель для sq_log данных.
Подгрузим заново датасет, чтоб можно было использовать удаленные для линейных моделей колонки. Деревьям они как раз не помешают.
In [24]:
data_rf = pd.read_csv('/Users/tatanakuzenko/cian_data_clear_for_modeling.csv')
In [25]:
data_sq_rf = data_rf
for i in range(len(squared_columns_new)):
data_sq_rf[squared_columns_new[i]] = 0
for i in range(len(squared_columns)):
data_sq_rf[squared_columns_new[i]] = [x**2 for x in data_sq_rf[squared_columns[i]]]
In [26]:
data_sc_rf = data.copy()
data_sc_rf[to_scale] = scale(data_sc_rf[to_scale], axis = 1)
In [27]:
data_log_rf = data.copy()
data_log_rf['Price'] = np.log(data_log_rf['Price'])
In [28]:
data_sq_log_rf = data.drop('Price', axis = 1)
for i in range(len(squared_columns)):
data_sq_log_rf[squared_columns_new[i]] = [x**2 for x in data_sq_log_rf[squared_columns[i]]]
data_sq_log_rf['Price'] = np.log(data['Price'])
In [29]:
data_sc_log_rf = data.drop('Price', axis = 1)
data_sc_log_rf[to_scale] = scale(data_sc_log_rf[to_scale], axis = 1)
data_sc_log_rf['Price'] = np.log(data['Price'])
In [30]:
data_sc_sq_log_rf = data.drop('Price', axis = 1)
data_sc_sq_log_rf[to_scale] = scale(data_sc_sq_log_rf[to_scale], axis = 1)
for i in range(len(squared_columns)):
data_sc_sq_log_rf[squared_columns_new[i]] = [x**2 for x in data_sc_sq_log_rf[squared_columns[i]]]
data_sc_sq_log_rf['Price'] = np.log(data['Price'])
In [31]:
data_sq_sc_log_rf = data.drop('Price', axis = 1)
for i in range(len(squared_columns)):
data_sq_sc_log_rf[squared_columns_new[i]] = [x**2 for x in data_sq_sc_log_rf[squared_columns[i]]]
data_sq_sc_log_rf[to_scale] = scale(data_sq_sc_log_rf[to_scale], axis = 1)
data_sq_sc_log_rf['Price'] = np.log(data['Price'])
In [32]:
datasets_rf = [data_rf, data_sq_rf, data_sc_rf, data_log_rf, data_sc_log_rf, data_sq_log_rf, data_sc_sq_log_rf, data_sq_sc_log_rf]
In [33]:
from sklearn.ensemble import RandomForestRegressor
simple = RandomForestRegressor(n_estimators=60, criterion='mse', max_depth=15)
squared = RandomForestRegressor(n_estimators=60, criterion='mse', max_depth=15)
scaled = RandomForestRegressor(n_estimators=60, criterion='mse', max_depth=15)
log = RandomForestRegressor(n_estimators=60, criterion='mse', max_depth=15)
sc_log = RandomForestRegressor(n_estimators=60, criterion='mse', max_depth=15)
sq_log = RandomForestRegressor(n_estimators=60, criterion='mse', max_depth=15)
sc_sq_log = RandomForestRegressor(n_estimators=60, criterion='mse', max_depth=15)
sq_sc_log = RandomForestRegressor(n_estimators=60, criterion='mse', max_depth=15)
models = [
('simple', simple),
('squared', squared),
('scaled', scaled),
('log', log),
('sc_log', sc_log),
('sq_log', sq_log),
('sc_sq_log', sc_sq_log),
('sq_sc_log', sq_sc_log)
]
for i in range(len(models)):
tmp = datasets[i]
train = tmp.drop('Price', axis = 1)
target = tmp['Price']
Xtr, Xval, Ytr, Yval = train_test_split(train, target, test_size=0.2)
modelname, model = models[i]
model.fit(Xtr, Ytr);
predictions = model.predict(Xval)
if (modelname.find('log', 0, len(modelname)) < 0):
rmse = sqrt(mean_squared_error(Yval, predictions))
mae = mean_absolute_error(Yval, predictions)
print('Model:', modelname, ' RMSE: ', rmse, ' MAE: ', mae)
else:
rmse = sqrt(mean_squared_error(np.exp(Yval), np.exp(predictions)))
mae = mean_absolute_error(np.exp(Yval), np.exp(predictions))
print('Model:', modelname, ' RMSE: ', rmse, ' MAE: ', mae)
На лесах сопоставимо хорошие результаты дают все, кроме scaled, sc_log, sc_sq_log
В среднем суммарная ошибка составляет 105e05 на 1000 объектов для предсказаний линейных моделей и 97e05 для случайных лесов.