房价预测链接:https://www.kaggle.com/serigne/stacked-regressions-top-4-on-leaderboard
对于一个完整的数据挖掘项目,其中首当其冲的就是对数据做Feature Engeneering,一个好的特征工程决定了后面我们使用算法的上限;因此特征工程在整个环节中非常的重要;但是由于特征工程往往需要花费更多的时间且不一定是完全有用的;作为初学者在做特征工程时往往会觉得很无趣,觉得没什么意思;但是工作总是要有人做的;同时由于特征工程的重要性,我们对特征工程更应该pay more attention。
特征工程主要包含以下items:(一定要对项目了解特征的具体意义,对该项目了解才能更加的有助于做特征工程)
1、缺失值补全;通过遍历数据集对数据项为空的进行补全;噪声点过滤(有时需要注意有些数据只是偏差较大并不是真正意义上的噪声点,因此在处理噪声点时需要额外注意)。
2、 数据类型转换;在数据挖掘中主要存在两种类型数据:数值型变量[Numerical Variables]、类别型变量[Categorical Variales]。在数据转换这块我们需要做的是将数值型和类比型的数据区分开来。对某些分类变量进行标签编码,这些变量的排序集中可能存在一些我们需要的信息。
3、Box-Cox Transformation;对一些分布有偏差的特征使用Box-Cox转换,而不是简单的log变换。Box-Cox变化对比于log变化的形式更加多样且实用性更高。对数据下使用转换能够使得我们在进行交叉验证和结果输出上取得较好的结果。
4、获取分类特征的虚拟变量
总体来说做特征工程的工作非常重要,一定要重视这个过程。
下面对具体过程进行介绍
1、对输出进行评估,确定我们需要处理问题的总类即分类问题还是回归问题。由于我们这里是关于房价的,且根据我们对数据集进行观察知,我们的输出是连续值,所以我们这里处理的是回归问题。[在确定好问题类型后方便我们后面模型的选择]。
2、对数据集进行describe()观察,观察数据集的分布情况,缺失值的多少,数据的方差、标准差等。
3、在确定好问题的类型之后,我们对数据集的输出进行观察,若输出值为非正态分布,我们应对其进行数据转换将尽可能的变为正态分布,需要注意的一点是,若变换前的数值已经较大,若经过变换后的数据变得更大的话,会降低模型后面处理的速度。
4、对数据进行相关性校验,根据相关性矩阵观察变量与输出之间的相关性大小。
corrmat = train.corr()
plt.subplots(figsize=(12, 9))
sns.heatmap(corrmat, vmax=0.9, square=True)
# 根据前面已得到的相关性矩阵,将与输出相关的前10个特征进行观察
k = 10 # 前10个特征
cols = corrmat.nlargest(k, 'Target')['Target'].index # Target为输出项
cm = np.corrcoef(df_train[cols].values.T)
sns.set(font_scale=1.25)
hm = sns.heatmap(cm, bar=True, annot=True, square=True, fmt='.2f', annot_kws={'size':10}, yticklabels=cols.values, xticklabels=cols.values)
plt.show()
5、缺失值处理;对于缺失较为严重的数据我们一方面可以将该特征删除,因为若该特征确实比较重要那样的话,在做数据收集的时候就不会让该特征缺失的那么厉害了。另一方面我们可以将该特征的缺失值进行全部填充None或者其他没有实际意义的变量。此外对于特征中绝大部分数据都是重复一样则说明该特征对输出没有贡献因此可以直接将该特征删除。对于缺失值情形下数据类型为类别类型的数据时,一般填充采用most common填充即用出现频率高的数据进行填充。在完成对所有缺失值处理之后,最好对数据的describe()进行再次的校验;确定数据集中是否所有的数据都已经是非空的状态。
6、若数据中存在一些数值变量,但是其实际意义为类别型变量时需要我们将其转换为类别型变量。
all_data['MSSubClass'] = all_data['MSSubClass'].apply(str)
7、对类别型变量[Categorical Variables]进行编码
from sklearn.preprocessing import LabelEnconder # 调用sklearn包中的预处理包进行对数据进行编码
cols = ('FirepalceQu', 'BstmQual', 'BsmtCond', 'GargeQual', 'GargeCond', 'ExterQual','ExterCond', 'HeatingQC', 'PoolQC', 'KitchenQual', 'BsmtFinType1', ..., '')
for c in cols:
lbl = LabelEnconder()
lbl.fit(list(all_data[c].values))
all_data[c] = lbl.transfrom(list(all_data[C].values))
print('Shape all_data: {}'.format(all_data.shape))
9、对数值型数据中呈偏态分布进行Box-Cox变换。
numberic_feats = all_data.dtypes[all_data.dtypes != 'object'].index
skewed_feats = all_data[numberic_feats].apply(lambda x :skew(x.dropna())).sort_values(ascending=False)
print('\nSkew in numberic features: \n')
skewness = pd.DataFrame({'Skew' :skewed_feats})
skewness.head(10)
skewness = skewness[abs(skewness) > 0.75]
print("There are {} skewed numerical features to Box Cox transform".format(skewness.shape[0]))
from scipy.special import boxcox1p # box-cox变换
skewed_features = skewness.index
lam = 0.15 # lambda参数根据特征的分布来确定
for feat in skewed_features:
all_data[feat] = boxcox1p(all_data[feat], lam)
all_data = pd.get_dummies(all_data) # 使用pandas实现one hot encode编码
print(all_data.shape)
在做完特征工程之后接下来就是大家期望已久的建模环节了;当然目前机器学习领域已经有很多现成的机器学习包可以用了,我们也不需要重复造轮子了。在此我们直接导包就好。
# 若有对这些包不清楚的同学会有详细介绍这些包的文档
# ElasticNet为弹性网络,即同时使用了L1和L2作为正则项,其中l1_ratio为L1范数惩罚项所占比例,参数中的alpha为惩罚项。
# 加入L1正则化的优化函数叫做Lasso回归,加入L2正则化的叫做岭回归
# BayesianRidge 贝叶斯回归,当我们的数据有很多缺失或者矛盾的病态数据时[鲁棒性较好],可以考虑使用bayesianRidge,比较耗时
from sklearn.linear_model import ElasticNet, Lasso, BayesianRidge, LassoLarsCV
# 集成模型 随机森林回归、梯度提升回归
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor # 导入
from sklearn.kernel_ridge import KernelRidge # 导入核函数
# pipeline将多个估计器串联起来(串行化方法),具体的作用取决于最后一个estimator的作用,可直接使用predict、fit对pipeline中的模型进行训练预测,makepipe()是pipeline类的简单实现,只需传入每个step的类即可。
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import RobustScaler # 用来解决离群点
from sklearn.base import BaseEstimator, TransformerMixin, RegressorMixin, clone
# cross_val_score返回交叉验证的每次运行的评分数组, train_test_split用于将数据集划分为训练集和测试集
from sklearn.model_selection import KFold, cross_val_score, train_test_split # Kfold为K折交叉验证
from sklearn.metrics import mean_squared_error # 导入计算误差的模块
# 集成方法boosting方法
import xgboost as xgb # 国人编写的库,很有效
import lightgbm import lgb #
导完包之后当然是需要调用包里面的模型了,那么在调用之前我们考虑一个问题,到底如何判断一个模型的好坏呢?这时候就需要cross_validation了。在此我们定义一个验证函数
# 使用K折交叉验证方法
n_folds = 5
def rmsle_cv(model):
kf = KFold(n_folds, shuffle=True, random_state=42).get_n_splits(train.values)
rmse = np.sqrt(-cross_val_score(model, train.values, y_train, scoring='neg_mean_squared_error'), cv=kf)
return (rmse)
定义完我们的评估函数之后当然就是到了我们期待已久的环节了,模型建立环节
# 定义Lasso回归
lasso = makepipeline(RobustScaler(), Lasso(alpha=0.0004, random_state=1))
# 定义弹性网络
ENet = makepipeline(RobustScaler(), ElasticNet(alpha=0.0005, l1_ratio=0.9, random_state=3))
# 定义核岭回归
KRR = KernalRidge(alpha=0.6, kernal='polynomial', degree=2, coef0=2.5)
# 定义梯度提升回归
GBoost = GradientBoostingRegressor(n_estimator=3000, learning_rate=0.05,
max_depth=4, max_features='sqrt',
min_samples_leaf=15, min_samples_split=10,
loss='huber', random_state=5)
# 定义XGboost
model_xgb = xgb.XGBRegressor(colsample_bytree=0.4603, gamma=0.0468,
learning_rate=0.05, max_depth=3,
min_child_weight=1.7817, n_estimators=2200,
reg_alpha=0.4640, reg_lambda=0.8571,
subsample=0.5213, silent=1,
random_state =7, nthread = -1)
# 定义LightGBM
model_lgb = lgb.LGBMRegressor(objective='regression',num_leaves=5,
learning_rate=0.05, n_estimators=720,
max_bin = 55, bagging_fraction = 0.8,
bagging_freq = 5, feature_fraction = 0.2319,
feature_fraction_seed=9, bagging_seed=9,
min_data_in_leaf =6, min_sum_hessian_in_leaf = 11)
# 对我们定义的模型进行评分估计
score = rmsle_cv(lasso) # 其中的lasso可替代为其他的model
print("\nLasso score: {:.4f} ({:.4f})\n".format(score.mean(), score.std()))
在对单个模型进行评估之后,也许我们会觉得单个基本模型的效果可能不怎么好,下面就是我们重头戏了stacking models.
在此我们先介绍最简单的stacking:Averaging base models
class AveragingModels(BaseEstimator, RegressorMixin, TransformerMixin):
def __init__(self, models):
self.models = models
# we define clones of the original models to fit the data in
def fit(self, X, y):
self.models_ = [clone(x) for x in self.models]
# Train cloned base models
for model in self.models_:
model.fit(X, y)
return self
#Now we do the predictions for cloned models and average them
def predict(self, X):
predictions = np.column_stack([
model.predict(X) for model in self.models_
])
return np.mean(predictions, axis=1)
# 在定义完我们自己的基础模型之后我们就可以使用了
averaged_models = AveragingModels(models = (ENet, GBoost, KRR, lasso)) # 这里的models组合是多样的,可根据自己的评分效果去选择
score = rmsle_cv(averaged_models)print(" Averaged base models score: {:.4f} ({:.4f})\n".format(score.mean(), score.std()))
下面的话,再介绍一个较为常用的:Adding a Meta-model (增加元模型) 我们再原始模型的基础上通过对这个基础模型的输出加上一级meta models。使用基础模型的结果来训练元模型。这种方法往往会取得较好的结果。
class StackingAveragedModels(BaseEstimator, RegressorMixin, TransformerMixin):
def __init__(self, base_models, meta_model, n_folds=5):
self.base_models = base_models
self.meta_model = meta_model
self.n_folds = n_folds
# We again fit the data on clones of the original models
def fit(self, X, y):
self.base_models_ = [list() for x in self.base_models]
self.meta_model_ = clone(self.meta_model)
kfold = KFold(n_splits=self.n_folds, shuffle=True, random_state=156)
# Train cloned base models then create out-of-fold predictions
# that are needed to train the cloned meta-model
out_of_fold_predictions = np.zeros((X.shape[0], len(self.base_models)))
for i, model in enumerate(self.base_models):
for train_index, holdout_index in kfold.split(X, y):
instance = clone(model)
self.base_models_[i].append(instance)
instance.fit(X[train_index], y[train_index])
y_pred = instance.predict(X[holdout_index])
out_of_fold_predictions[holdout_index, i] = y_pred
# Now train the cloned meta-model using the out-of-fold predictions as new feature
self.meta_model_.fit(out_of_fold_predictions, y)
return self
#Do the predictions of all base models on the test data and use the averaged predictions as
#meta-features for the final prediction which is done by the meta-model
def predict(self, X):
meta_features = np.column_stack([
np.column_stack([model.predict(X) for model in base_models]).mean(axis=1)
for base_models in self.base_models_ ])
return self.meta_model_.predict(meta_features)
# 使用如下
stacked_averaged_models = StackingAveragedModels(base_models = (ENet, GBoost, KRR),
meta_model = lasso)
score = rmsle_cv(stacked_averaged_models)print("Stacking Averaged models score: {:.4f} ({:.4f})".format(score.mean(), score.std()))
在对模型进行完评分之后找出那些评分较好的模型进行最后的训练和预测
stack_averaged_models.fit(train.values, y_train)
stack_train_pred = stack_averaged_models.predict(train.values)
stack_pred = np.expm1(stack_averaged_models.predict(test.values))
print(rmsle(y_train, stack_train_pred))
# 同理使用其他的模型也一样通过这种方式进行预测;将较好的结果作为输出文件输出
# 使用多种平均调和的方式
'''RMSE on the entire Train data when averaging'''
print('RMSLE score on train data:')print(rmsle(y_train,stacked_train_pred*0.70 +
xgb_train_pred*0.15 + lgb_train_pred*0.15 ))
def rmsle(y, y_pred):
return np.sqrt(mean_squared_error(y, y_pred))
至此;一个完整的房价预测项目就差不多结束了。自己对stacking方面的知识还有boosting方面的还是有一定的欠缺,这是自己下一阶段的学习方向。需要本文档对你有帮助。