原链接:https://www.kaggle.com/apapiu/regularized-linear-models
线性模型:¶
作者: Alexandru Papiu (@apapiu, GitHub)
There have been a few great scripts on xgboost already so I'd figured I'd try something simpler: a regularized linear regression model. 现在已经有一些很棒的xgboost的脚本了,但是我想尝试一些简单的模型:一个正则化的线性回归模型。令人惊讶的是,虽然特征很少,但是它很有用。关键是要对数字变量进行对数变换,因为它们中的大多数的分布都是偏斜的。
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib
import matplotlib.pyplot as plt
from scipy.stats import skew
from scipy.stats.stats import pearsonr
%config InlineBackend.figure_format = 'retina' #set 'png' here when working on notebook
%matplotlib inline
train = pd.read_csv("input/train.csv")
test = pd.read_csv("input/test.csv")
train.head()
all_data = pd.concat((train.loc[:,'MSSubClass':'SaleCondition'],
test.loc[:,'MSSubClass':'SaleCondition']))
matplotlib.rcParams['figure.figsize'] = (12.0, 6.0)
prices = pd.DataFrame({"price":train["SalePrice"], "log(price + 1)":np.log1p(train["SalePrice"])})
prices.hist()
#log transform the target:
train["SalePrice"] = np.log1p(train["SalePrice"])
#log transform skewed numeric features:
numeric_feats = all_data.dtypes[all_data.dtypes != "object"].index
skewed_feats = train[numeric_feats].apply(lambda x: skew(x.dropna())) #compute skewness
skewed_feats = skewed_feats[skewed_feats > 0.75]
skewed_feats = skewed_feats.index
all_data[skewed_feats] = np.log1p(all_data[skewed_feats])
all_data = pd.get_dummies(all_data)
#filling NA's with the mean of the column:
all_data = all_data.fillna(all_data.mean())
#creating matrices for sklearn:
X_train = all_data[:train.shape[0]]
X_test = all_data[train.shape[0]:]
y = train.SalePrice
训练模型¶
现在我们将使用scikit学习模块中的正则化线性回归模型。 我将尝试l_1(Lasso)和l_2(Ridge)正则化。 我还将定义一个返回交叉验证rmse错误的函数,以便我们可以评估我们的模型并选择最佳调整标准
from sklearn.linear_model import Ridge, RidgeCV, ElasticNet, LassoCV, LassoLarsCV
from sklearn.model_selection import cross_val_score
def rmse_cv(model):
rmse= np.sqrt(-cross_val_score(model, X_train, y, scoring="neg_mean_squared_error", cv = 5))
return(rmse)
model_ridge = Ridge()
Ridge模型的主要调整参数是alpha - 一个正则化参数,用于衡量模型的灵活程度。 正规化越高,我们的模型就越不容易过度拟合。 但是它也会失去灵活性,并且可能无法捕获数据中的所有信号。
alphas = [0.05, 0.1, 0.3, 1, 3, 5, 10, 15, 30, 50, 75]
cv_ridge = [rmse_cv(Ridge(alpha = alpha)).mean()
for alpha in alphas]
cv_ridge = pd.Series(cv_ridge, index = alphas)
cv_ridge.plot(title = "Validation - Just Do It")
plt.xlabel("alpha")
plt.ylabel("rmse")
注意上面的U形状曲线。 当alpha太大时,正则化太强,模型无法捕获数据中的所有复杂性。 然而,如果我们让模型过于灵活(alpha小),模型就会开始过度拟合。 根据上图,alpha = 10的值大约是正确的。
cv_ridge.min()
领回归的rmsle大概是0.127
让我们试试Lasso模型吧。 我们将在这里采用略微不同的方法,并使用内置的Lasso CV为我们找出最佳的alpha。 出于某种原因,Lasso CV中的alpha实际上是Ridge中的alpha。
model_lasso = LassoCV(alphas = [1, 0.1, 0.001, 0.0005]).fit(X_train, y)
rmse_cv(model_lasso).mean()
太好了! lasso表现得更好,所以我们只是用这个来预测测试集。 关于lasso的另一个好处是它为你做了特色选择 - 设置它认为不重要的特征系数为零。 我们来看看系数:
coef = pd.Series(model_lasso.coef_, index = X_train.columns)
print("Lasso picked " + str(sum(coef != 0)) + " variables and eliminated the other " + str(sum(coef == 0)) + " variables")
干得好Lasso。 然而,有一点需要注意的是,所选择的特征不一定是“正确的” - 特别是因为该数据集中有许多线性相关的特征(特征存在共线性)。 尝试一下在选择的样本上运行Lasso几次,看看特征选择的稳定性。
我们可以看看最重要的特征是什么:
imp_coef = pd.concat([coef.sort_values().head(10),
coef.sort_values().tail(10)])
matplotlib.rcParams['figure.figsize'] = (8.0, 10.0)
imp_coef.plot(kind = "barh")
plt.title("Coefficients in the Lasso Model")
最重要的特征是GrLivArea
。一些位置和房屋质量的特征也有积极的作用。 一些负面特征不太有意义,它们可能来自不平衡的分类变量。
另请注意,与从随机森林中获得的特征重要性不同,这些是模型中的实际系数 - 因此您可以准确地说出为什么预测价格就是这样。 这里唯一的问题是我们做了对数变换,所以实际的幅度有点难以解释。
#let's look at the residuals as well:
matplotlib.rcParams['figure.figsize'] = (6.0, 6.0)
preds = pd.DataFrame({"preds":model_lasso.predict(X_train), "true":y})
preds["residuals"] = preds["true"] - preds["preds"]
preds.plot(x = "preds", y = "residuals",kind = "scatter")
这个图看起来相当不错
添加xgboost:¶
让我们在线性模型中添加一个xgboost模型,看看我们是否可以提高分数:
import xgboost as xgb
dtrain = xgb.DMatrix(X_train, label = y)
dtest = xgb.DMatrix(X_test)
params = {"max_depth":2, "eta":0.1}
model = xgb.cv(params, dtrain, num_boost_round=500, early_stopping_rounds=100)
model.loc[30:,["test-rmse-mean", "train-rmse-mean"]].plot()
model_xgb = xgb.XGBRegressor(n_estimators=360, max_depth=2, learning_rate=0.1) #the params were tuned using xgb.cv
model_xgb.fit(X_train, y)
xgb_preds = np.expm1(model_xgb.predict(X_test))
lasso_preds = np.expm1(model_lasso.predict(X_test))
predictions = pd.DataFrame({"xgb":xgb_preds, "lasso":lasso_preds})
predictions.plot(x = "xgb", y = "lasso", kind = "scatter")
很多时候,对不相关的结果进行加权平均是有意义的 - 这通常可以提高分数,尽管在这种情况下它没有那么大帮助。
preds = 0.7*lasso_preds + 0.3*xgb_preds
solution = pd.DataFrame({"id":test.Id, "SalePrice":preds})
solution.to_csv("ridge_sol.csv", index = False)
Trying out keras?¶
前馈神经网络似乎效果并不好......我也不知道为什么。
from keras.layers import Dense
from keras.models import Sequential
from keras.regularizers import l1
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
X_train = StandardScaler().fit_transform(X_train)
X_tr, X_val, y_tr, y_val = train_test_split(X_train, y, random_state = 3)
X_tr.shape
X_tr
model = Sequential()
#model.add(Dense(256, activation="relu", input_dim = X_train.shape[1]))
model.add(Dense(1, input_dim = X_train.shape[1], W_regularizer=l1(0.001)))
model.compile(loss = "mse", optimizer = "adam")
model.summary()
hist = model.fit(X_tr, y_tr, validation_data = (X_val, y_val))
pd.Series(model.predict(X_val)[:,0]).hist()