Kaggle入门实例-预测房价
2017-11-17 17:09
786 查看
问题链接:House Prices: Advanced Regression Techniques
问题描述: 通过79个变量(几乎)描述爱荷华州埃姆斯(Ames)住宅的每一个特征,在这个竞赛里,需要你预测每个住宅的最终价格,并最终提交。
参考文献:
第一次真正对这么复杂的数据进行操作,初学者都会有点不知所措。参考了其他参赛者的笔记:
1、Comprehensive data exploration with Python by Pedro Marcelino:非常适合初学者的数据探索分析[Exploratory Data Analysis]。
2、Stacked Regressions : Top 4% on LeaderBoard by Serigne :以成绩为导向的数据处理过程。
目录:
1、数据探索
2、数据处理
3、特征工程
4、模型选择
5、模型融合
导入数据:
ID特征对分类没有影响,但最后我们得到结果以后提交的时候需要,所以需要将ID单独提取出来:
当然数据探索部分并没有结束,在数据处理部分,我们会边探索边处理:
在数据中会有个别离群点,他们对分类结果噪音太大,我们选择将其删掉。但是如果不是太过分的离群点,就不能删掉,因为如果删掉所有噪声会影响模型的健壮性,对测试数据的泛化能力就会下降。
右下方的两个数据,living area特别大,但是价格又低的离谱,应该是远离市区的无人地带。对最后的分类结果没有影响的离群点(Oultliers),我们可以放心将其删除。
删除后:
目标值处理:
线性的模型需要正态分布的目标值才能发挥最大的作用。我们需要检测房价时候偏离正态分布。使用probplot函数,即正太概率图:
按照参考文献的说法,此时正态分布明显属于右态分布,整体峰值向左偏离,并且skewness较大,需要对目标值做log转换,以恢复目标值的正态性。
缺失数据可视化:
分析各个特征与房价的相关性,相关性的分析最好使用热力图:
可以看到对角线有一条白线,这代表相同的特征相关性为最高,但值得注意的是,有两个正方形小块:TotaLBsmtSF和1stFlrSF、GarageAreas和GarageCars处。这代表全部建筑面积TotaLBsmtSF与一层建筑面积1stFlrSF成强正相关,车库区域GarageAreas和车库车辆GarageCars成强正相关,那么在填补缺失值的时候就有了依据,我们可以直接删掉一个多余的特征或者使用一个填补另一个。
填补缺失值:
因为缺失值较多,就直接贴代码了:
更多的数据工程:
1、有许多特征实际上是类别型的特征,但给出来的是数字。比如MSSubClass,是评价房子种类的一个特征,给出的是10-100的数字,但实际上是类别,所以我们需要将其转化为字符串类别。
2、接下来 LabelEncoder,对文本类别的特征进行编号。
3、接下来添加一个重要的特征,因为我们实际在购买房子的时候会考虑总面积的大小,但是此数据集中并没有包含此数据。总面积等于地下室面积+1层面积+2层面积。
4、我们对房价进行分析,不符合正态分布我们将其log转换,使其符合正态分布。那么偏离正态分布太多的特征我们也对它进行转化:
5、将类别特征进行哑变量转化:
至此,我们的特征工程已经处理完毕。
我们使用Sklearn的cross_val_score函数。然而这个函数没有shuffle方法,我们添加了一行代码,为了在交叉验证之前shuffle数据集。
Score的方法为MSE,求出几个模型的Baseline:
我们来看一下各个模型的得分:
Average-Stacking:
我们从最简单的平均基本模型的Stacking方法开始模型融合。建立一个新的类来扩展scikit模型融合方法:
平均四个模型ENet,GBoost,KRR和lasso。利用上面重写的方法,我们可以轻松地添加更多的模型:
可以看到,均方误差比单独使用几个模型有所下降,这还是最简单的模型融合,这鼓励我们向着更深的模型融合的方向继续努力。
Meta-model Stacking:
算法:
1、将整个训练集分解成两个不相交的集合(这里是train和.holdout)。
2、在第一部分(train)上训练几个基本模型。
3、在第二个部分(holdout)上测试这些基本模型。
4、使用(3)中的预测(称为 out-of-fold 预测)作为输入,并将正确的标签(目标变量)作为输出来训练更高层次的学习模型称为元模型。
前三个步骤是迭代完成的。例如,如果我们采取5倍的fold,我们首先将训练数据分成5次。然后我们会做5次迭代。在每次迭代中,我们训练每个基础模型4倍,并预测剩余的fold(holdout fold)。
测试Meta-model Stacking结果:
我们得到了一个更好的结果!
然后为了得到最后提交的结果,我们将StackedRegressor、XGBoost和LightGBM进行融合,得到rmsle的结果。
最终的训练和预测:
将三者进行融合,然后得到Ensemble prediction:
得到待提交的CSV文件:
问题描述: 通过79个变量(几乎)描述爱荷华州埃姆斯(Ames)住宅的每一个特征,在这个竞赛里,需要你预测每个住宅的最终价格,并最终提交。
参考文献:
第一次真正对这么复杂的数据进行操作,初学者都会有点不知所措。参考了其他参赛者的笔记:
1、Comprehensive data exploration with Python by Pedro Marcelino:非常适合初学者的数据探索分析[Exploratory Data Analysis]。
2、Stacked Regressions : Top 4% on LeaderBoard by Serigne :以成绩为导向的数据处理过程。
目录:
1、数据探索
2、数据处理
3、特征工程
4、模型选择
5、模型融合
数据探索(EDA)
我们拿到数据后,先对数据要有个大致的了解,我们有1460的训练数据和1459的测试数据,数据的特征列有79个,其中35个是数值类型的,44个类别类型。导入数据:
#import some necessary librairies import numpy as np # linear algebra import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv) %matplotlib inline import matplotlib.pyplot as plt # Matlab-style plotting import seaborn as sns color = sns.color_palette() sns.set_style('darkgrid') import warnings def ignore_warn(*args, **kwargs): pass warnings.warn = ignore_warn #ignore annoying warning (from sklearn and seaborn) from scipy import stats from scipy.stats import norm, skew #for some statistics pd.set_option('display.float_format', lambda x: '{:.3f}'.format(x)) #Limiting floats output to 3 decimal points from subprocess import check_output print(check_output(["ls", "../input"]).decode("utf8")) #check the files available in the directory
#Now let's import and put the train and test datasets in pandas dataframe train = pd.read_csv('../input/train.csv') test = pd.read_csv('../input/test.csv') ##display the first five rows of the train dataset. train.head(5)
##display the first five rows of the test dataset. test.head(5)
ID特征对分类没有影响,但最后我们得到结果以后提交的时候需要,所以需要将ID单独提取出来:
#check the numbers of samples and features print("The train data size before dropping Id feature is : {} ".format(train.shape)) print("The test data size before dropping Id feature is : {} ".format(test.shape)) #Save the 'Id' column train_ID = train['Id'] test_ID = test['Id'] #Now drop the 'Id' colum since it's unnecessary for the prediction process. train.drop("Id", axis = 1, inplace = True) test.drop("Id", axis = 1, inplace = True) #check again the data size after dropping the 'Id' variable print("\nThe train data size after dropping Id feature is : {} ".format(train.shape)) print("The test data size after dropping Id feature is : {} ".format(test.shape))
数据处理
离群点处理:当然数据探索部分并没有结束,在数据处理部分,我们会边探索边处理:
在数据中会有个别离群点,他们对分类结果噪音太大,我们选择将其删掉。但是如果不是太过分的离群点,就不能删掉,因为如果删掉所有噪声会影响模型的健壮性,对测试数据的泛化能力就会下降。
fig, ax = plt.subplots() ax.scatter(x = train['GrLivArea'], y = train['SalePrice']) plt.ylabel('SalePrice', fontsize=13) plt.xlabel('GrLivArea', fontsize=13) plt.show()
右下方的两个数据,living area特别大,但是价格又低的离谱,应该是远离市区的无人地带。对最后的分类结果没有影响的离群点(Oultliers),我们可以放心将其删除。
删除后:
#Deleting outliers train = train.drop(train[(train['GrLivArea']>4000) & (train['SalePrice']<300000)].index) #Check the graphic again fig, ax = plt.subplots() ax.scatter(train['GrLivArea'], train['SalePrice']) plt.ylabel('SalePrice', fontsize=13) plt.xlabel('GrLivArea', fontsize=13) plt.show()
目标值处理:
线性的模型需要正态分布的目标值才能发挥最大的作用。我们需要检测房价时候偏离正态分布。使用probplot函数,即正太概率图:
sns.distplot(train['SalePrice'] , fit=norm); # Get the fitted parameters used by the function (mu, sigma) = norm.fit(train['SalePrice']) print( '\n mu = {:.2f} and sigma = {:.2f}\n'.format(mu, sigma)) #Now plot the distribution plt.legend(['Normal dist. ($\mu=$ {:.2f} and $\sigma=$ {:.2f} )'.format(mu, sigma)], loc='best') plt.ylabel('Frequency') plt.title('SalePrice distribution') #Get also the QQ-plot fig = plt.figure() res = stats.probplot(train['SalePrice'], plot=plt) plt.show()
按照参考文献的说法,此时正态分布明显属于右态分布,整体峰值向左偏离,并且skewness较大,需要对目标值做log转换,以恢复目标值的正态性。
#We use the numpy fuction log1p which applies log(1+x) to all elements of the column
train["SalePrice"] = np.log1p(train["SalePrice"])
#Check the new distribution
sns.distplot(train['SalePrice'] , fit=norm); # Get the fitted parameters used by the function (mu, sigma) = norm.fit(train['SalePrice']) print( '\n mu = {:.2f} and sigma = {:.2f}\n'.format(mu, sigma)) #Now plot the distribution plt.legend(['Normal dist. ($\mu=$ {:.2f} and $\sigma=$ {:.2f} )'.format(mu, sigma)], loc='best') plt.ylabel('Frequency') plt.title('SalePrice distribution') #Get also the QQ-plot fig = plt.figure() res = stats.probplot(train['SalePrice'], plot=plt) plt.show()
特征工程
为了方便处理数据,我们将训练集和测试集先进行合并:#We use the numpy fuction log1p which applies log(1+x) to all elements of the column
train["SalePrice"] = np.log1p(train["SalePrice"])
#Check the new distribution
sns.distplot(train['SalePrice'] , fit=norm); # Get the fitted parameters used by the function (mu, sigma) = norm.fit(train['SalePrice']) print( '\n mu = {:.2f} and sigma = {:.2f}\n'.format(mu, sigma)) #Now plot the distribution plt.legend(['Normal dist. ($\mu=$ {:.2f} and $\sigma=$ {:.2f} )'.format(mu, sigma)], loc='best') plt.ylabel('Frequency') plt.title('SalePrice distribution') #Get also the QQ-plot fig = plt.figure() res = stats.probplot(train['SalePrice'], plot=plt) plt.show()
缺失数据可视化:
f, ax = plt.subplots(figsize=(15, 12)) plt.xticks(rotation='90') sns.barplot(x=all_data_na.index, y=all_data_na) plt.xlabel('Features', fontsize=15) plt.ylabel('Percent of missing values', fontsize=15) plt.title('Percent missing data by feature', fontsize=15)
分析各个特征与房价的相关性,相关性的分析最好使用热力图:
#Correlation map to see how features are correlated with SalePrice corrmat = train.corr() plt.subplots(figsize=(12,9)) sns.heatmap(corrmat, vmax=0.9, square=True)
可以看到对角线有一条白线,这代表相同的特征相关性为最高,但值得注意的是,有两个正方形小块:TotaLBsmtSF和1stFlrSF、GarageAreas和GarageCars处。这代表全部建筑面积TotaLBsmtSF与一层建筑面积1stFlrSF成强正相关,车库区域GarageAreas和车库车辆GarageCars成强正相关,那么在填补缺失值的时候就有了依据,我们可以直接删掉一个多余的特征或者使用一个填补另一个。
填补缺失值:
因为缺失值较多,就直接贴代码了:
We impute them by proceeding sequentially through features with missing values PoolQC : data description says NA means "No Pool". That make sense, given the huge ratio of missing value (+99%) and majority of houses have no Pool at all in general. In [14]: all_data["PoolQC"] = all_data["PoolQC"].fillna("None") MiscFeature : data description says NA means "no misc feature" In [15]: all_data["MiscFeature"] = all_data["MiscFeature"].fillna("None") Alley : data description says NA means "no alley access" In [16]: all_data["Alley"] = all_data["Alley"].fillna("None") Fence : data description says NA means "no fence" In [17]: all_data["Fence"] = all_data["Fence"].fillna("None") FireplaceQu : data description says NA means "no fireplace" In [18]: all_data["FireplaceQu"] = all_data["FireplaceQu"].fillna("None") LotFrontage : Since the area of each street connected to the house property most likely have a similar area to other houses in its neighborhood , we can fill in missing values by the median LotFrontage of the neighborhood. In [19]: #Group by neighborhood and fill in missing value by the median LotFrontage of all the neighborhood all_data["LotFrontage"] = all_data.groupby("Neighborhood")["LotFrontage"].transform( lambda x: x.fillna(x.median())) GarageType, GarageFinish, GarageQual and GarageCond : Replacing missing data with None In [20]: for col in ('GarageType', 'GarageFinish', 'GarageQual', 'GarageCond'): GarageYrBlt, GarageArea and GarageCars : Replacing missing data with 0 (Since No garage = no cars in such garage.) In [21]: for col in ('GarageYrBlt', 'GarageArea', 'GarageCars'): all_data[col] = all_data[col].fillna(0) BsmtFinSF1, BsmtFinSF2, BsmtUnfSF, TotalBsmtSF, BsmtFullBath and BsmtHalfBath : missing values are likely zero for having no basement In [22]: for col in ('BsmtFinSF1', 'BsmtFinSF2', 'BsmtUnfSF','TotalBsmtSF', 'BsmtFullBath', 'BsmtHalfBath'): all_data[col] = all_data[col].fillna(0) BsmtQual, BsmtCond, BsmtExposure, BsmtFinType1 and BsmtFinType2 : For all these categorical basement-related features, NaN means that there is no basement. In [23]: for col in ('BsmtQual', 'BsmtCond', 'BsmtExposure', 'BsmtFinType1', 'BsmtFinType2'): MasVnrArea and MasVnrType : NA most likely means no masonry veneer for these houses. We can fill 0 for the area and None for the type. In [24]: all_data["MasVnrType"] = all_data["MasVnrType"].fillna("None") all_data["MasVnrArea"] = all_data["MasVnrArea"].fillna(0) MSZoning (The general zoning classification) : 'RL' is by far the most common value. So we can fill in missing values with 'RL' In [25]: all_data['MSZoning'] = all_data['MSZoning'].fillna(all_data['MSZoning'].mode()[0]) Utilities : For this categorical feature all records are "AllPub", except for one "NoSeWa" and 2 NA . Since the house with 'NoSewa' is in the training set, this feature won't help in predictive modelling. We can then safely remove it. In [26]: all_data = all_data.drop(['Utilities'], axis=1) Functional : data description says NA means typical In [27]: all_data["Functional"] = all_data["Functional"].fillna("Typ") Electrical : It has one NA value. Since this feature has mostly 'SBrkr', we can set that for the missing value. In [28]: all_data['Electrical'] = all_data['Electrical'].fillna(all_data['Electrical'].mode()[0]) KitchenQual: Only one NA value, and same as Electrical, we set 'TA' (which is the most frequent) for the missing value in KitchenQual. In [29]: all_data['KitchenQual'] = all_data['KitchenQual'].fillna(all_data['KitchenQual'].mode()[0]) Exterior1st and Exterior2nd : Again Both Exterior 1 & 2 have only one missing value. We will just substitute in the most common string In [30]: all_data['Exterior1st'] = all_data['Exterior1st'].fillna(all_data['Exterior1st'].mode()[0]) all_data['Exterior2nd'] = all_data['Exterior2nd'].fillna(all_data['Exterior2nd'].mode()[0]) SaleType : Fill in again with most frequent which is "WD" In [31]: all_data['SaleType'] = all_data['SaleType'].fillna(all_data['SaleType'].mode()[0]) MSSubClass : Na most likely means No building class. We can replace missing values with None In [32]: all_data['MSSubClass'] = all_data['MSSubClass'].fillna("None") Is there any remaining missing value ? In [33]: #Check remaining missing values if any all_data_na = (all_data.isnull().sum() / len(all_data)) * 100 all_data_na = all_data_na.drop(all_data_na[all_data_na == 0].index).sort_values(ascending=False) missing_data = pd.DataFrame({'Missing Ratio' :all_data_na}) missing_data.head() Out[33]: Missing Ratio
更多的数据工程:
1、有许多特征实际上是类别型的特征,但给出来的是数字。比如MSSubClass,是评价房子种类的一个特征,给出的是10-100的数字,但实际上是类别,所以我们需要将其转化为字符串类别。
#MSSubClass=The building class all_data['MSSubClass'] = all_data['MSSubClass'].apply(str) #Changing OverallCond into a categorical variable all_data['OverallCond'] = all_data['OverallCond'].astype(str) #Year and month sold are transformed into categorical features. all_data['YrSold'] = all_data['YrSold'].astype(str) all_data['MoSold'] = all_data['MoSold'].astype(str)
2、接下来 LabelEncoder,对文本类别的特征进行编号。
from sklearn.preprocessing import LabelEncoder cols = ('FireplaceQu', 'BsmtQual', 'BsmtCond', 'GarageQual', 'GarageCond', 'ExterQual', 'ExterCond','HeatingQC', 'PoolQC', 'KitchenQual', 'BsmtFinType1', 'BsmtFinType2', 'Functional', 'Fence', 'BsmtExposure', 'GarageFinish', 'LandSlope', 'LotShape', 'PavedDrive', 'Street', 'Alley', 'CentralAir', 'MSSubClass', 'OverallCond', 'YrSold', 'MoSold') # process columns, apply LabelEncoder to categorical features for c in cols: lbl = LabelEncoder() lbl.fit(list(all_data[c].values)) all_data[c] = lbl.transform(list(all_data[c].values)) # shape print('Shape all_data: {}'.format(all_data.shape))
3、接下来添加一个重要的特征,因为我们实际在购买房子的时候会考虑总面积的大小,但是此数据集中并没有包含此数据。总面积等于地下室面积+1层面积+2层面积。
# Adding total sqfootage feature all_data['TotalSF'] = all_data['TotalBsmtSF'] + all_data['1stFlrSF'] + all_data['2ndFlrSF']
4、我们对房价进行分析,不符合正态分布我们将其log转换,使其符合正态分布。那么偏离正态分布太多的特征我们也对它进行转化:
numeric_feats = all_data.dtypes[all_data.dtypes != "object"].index # Check the skew of all numerical features skewed_feats = all_data[numeric_feats].apply(lambda x: skew(x.dropna())).sort_values(ascending=False) print("\nSkew in numerical 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 skewed_features = skewness.index lam = 0.15 for feat in skewed_features: #all_data[feat] += 1 all_data[feat] = boxcox1p(all_data[feat], lam)
5、将类别特征进行哑变量转化:
all_data = pd.get_dummies(all_data) print(all_data.shape)
至此,我们的特征工程已经处理完毕。
模型选择
导入库:from sklearn.linear_model import ElasticNet, Lasso, BayesianRidge, LassoLarsIC from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor from sklearn.kernel_ridge import KernelRidge from sklearn.pipeline import make_pipeline from sklearn.preprocessing import RobustScaler from sklearn.base import BaseEstimator, TransformerMixin, RegressorMixin, clone from sklearn.model_selection import KFold, cross_val_score, train_test_split from sklearn.metrics import mean_squared_error import xgboost as xgb import lightgbm as lgb
我们使用Sklearn的cross_val_score函数。然而这个函数没有shuffle方法,我们添加了一行代码,为了在交叉验证之前shuffle数据集。
#Validation function 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)
Score的方法为MSE,求出几个模型的Baseline:
LASSO Regression : This model may be very sensitive to outliers. So we need to made it more robust on them. For that we use the sklearn's Robustscaler() method on pipeline In [43]: lasso = make_pipeline(RobustScaler(), Lasso(alpha =0.0005, random_state=1)) Elastic Net Regression : again made robust to outliers In [44]: ENet = make_pipeline(RobustScaler(), ElasticNet(alpha=0.0005, l1_ratio=.9, random_state=3)) Kernel Ridge Regression : In [45]: KRR = KernelRidge(alpha=0.6, kernel='polynomial', degree=2, coef0=2.5) Gradient Boosting Regression : With huber loss that makes it robust to outliers In [46]: GBoost = GradientBoostingRegressor(n_estimators=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 : In [47]: 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 : In [48]: 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) print("\nLasso score: {:.4f} ({:.4f})\n".format(score.mean(), score.std())) Lasso score: 0.1115 (0.0074) In [50]: score = rmsle_cv(ENet) print("ElasticNet score: {:.4f} ({:.4f})\n".format(score.mean(), score.std())) ElasticNet score: 0.1116 (0.0074) In [51]: score = rmsle_cv(KRR) print("Kernel Ridge score: {:.4f} ({:.4f})\n".format(score.mean(), score.std())) Kernel Ridge score: 0.1153 (0.0075) In [52]: score = rmsle_cv(GBoost) print("Gradient Boosting score: {:.4f} ({:.4f})\n".format(score.mean(), score.std())) Gradient Boosting score: 0.1177 (0.0080) In [53]: score = rmsle_cv(model_xgb) print("Xgboost score: {:.4f} ({:.4f})\n".format(score.mean(), score.std())) Xgboost score: 0.1161 (0.0079) In [54]: score = rmsle_cv(model_lgb) print("LGBM score: {:.4f} ({:.4f})\n" .format(score.mean(), score.std())) LGBM score: 0.1148 (0.0069)
模型融合
Stacking模型融合:Average-Stacking:
我们从最简单的平均基本模型的Stacking方法开始模型融合。建立一个新的类来扩展scikit模型融合方法:
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)
平均四个模型ENet,GBoost,KRR和lasso。利用上面重写的方法,我们可以轻松地添加更多的模型:
averaged_models = AveragingModels(models = (ENet, GBoost, KRR, lasso)) score = rmsle_cv(averaged_models) print(" Averaged base models score: {:.4f} ({:.4f})\n".format(score.mean(), score.std())) Averaged base models score: 0.1091 (0.0075)
可以看到,均方误差比单独使用几个模型有所下降,这还是最简单的模型融合,这鼓励我们向着更深的模型融合的方向继续努力。
Meta-model Stacking:
算法:
1、将整个训练集分解成两个不相交的集合(这里是train和.holdout)。
2、在第一部分(train)上训练几个基本模型。
3、在第二个部分(holdout)上测试这些基本模型。
4、使用(3)中的预测(称为 out-of-fold 预测)作为输入,并将正确的标签(目标变量)作为输出来训练更高层次的学习模型称为元模型。
前三个步骤是迭代完成的。例如,如果我们采取5倍的fold,我们首先将训练数据分成5次。然后我们会做5次迭代。在每次迭代中,我们训练每个基础模型4倍,并预测剩余的fold(holdout fold)。
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)
测试Meta-model Stacking结果:
tacked_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())) Stacking Averaged models score: 0.1085 (0.0074)
我们得到了一个更好的结果!
然后为了得到最后提交的结果,我们将StackedRegressor、XGBoost和LightGBM进行融合,得到rmsle的结果。
def rmsle(y, y_pred): return np.sqrt(mean_squared_error(y, y_pred))
最终的训练和预测:
StackedRegressor: In [60]: stacked_averaged_models.fit(train.values, y_train) stacked_train_pred = stacked_averaged_models.predict(train.values) stacked_pred = np.expm1(stacked_averaged_models.predict(test.values)) print(rmsle(y_train, stacked_train_pred)) 0.0781571937916 XGBoost: In [61]: model_xgb.fit(train, y_train) xgb_train_pred = model_xgb.predict(train) xgb_pred = np.expm1(model_xgb.predict(test)) print(rmsle(y_train, xgb_train_pred)) 0.0785165142425 LightGBM: In [62]: model_lgb.fit(train, y_train) lgb_train_pred = model_lgb.predict(train) lgb_pred = np.expm1(model_lgb.predict(test.values)) print(rmsle(y_train, lgb_train_pred)) 0.0719406222196 In [63]: '''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 )) RMSLE score on train data: 0.0752452023077
将三者进行融合,然后得到Ensemble prediction:
ensemble = stacked_pred*0.70 + xgb_pred*0.15 + lgb_pred*0.15
得到待提交的CSV文件:
sub = pd.DataFrame() sub['Id'] = test_ID sub['SalePrice'] = ensemble sub.to_csv('submission.csv',index=False)
提交结果
共有2408提交结果,排名191,还需要继续探索数据,探索模型融合的方法。相关文章推荐
- Kaggle房价预测数据观察和处理入门学习
- kaggle实战房价预测(二)
- kaggle竞赛入门Titanic生存预测
- Kaggle房价预测进阶版/bagging/boosting/AdaBoost/XGBoost
- Kaggle入门之泰坦尼克号生还率预测
- 详解 Kaggle 房价预测竞赛优胜方案:用 Python 进行全面数据探索
- NLP中kaggle比赛实例《每日新闻对股票市场的预测》基础版
- Kaggle竞赛 —— 房价预测 (House Prices)
- Kaggle 入门(NLP)——基于新闻语料预测股票涨跌
- Kaggle竞赛(lecture 1-2 入门)Titanic生存预测
- Kaggle房价预测:随机森林方法
- Udacity机器学习入门项目5:预测波士顿房价
- NLP中kaggle比赛实例《每日新闻对股票市场的预测》进阶版
- 贝叶斯分类实例(Kaggle比赛之『旧金山犯罪分类预测』)
- Kaggle入门第一课-Titanic实例解析
- Kaggle房价预测案例分享
- Kaggle房价预测-Lasso线性规划代入学习
- kaggle房价预测/Ridge/RandomForest/cross_validation
- kaggle 房价预测经典文章
- Story 2---Kaggle房价预测