写在前面:
这篇文章旨在梳理kaggle回归问题的一个基本流程。博主只是一个数据分析刚入门的新手,有些错漏之处还请批评指正。很遗憾这个项目最后提交的Private Score只达到了排行榜的TOP13%,我目前也还没有更好的方法去进一步提高分数,不过整个项目做完之后对kaggle回归预测项目的解题思路有了一套比较完整清楚的认识,总结出来和大家分享,欢迎共同探讨。
完整的代码放在github:kaggle房价预测完整代码

1.项目背景

问题陈述

房价预测是kaggle的一个经典Data Science项目,作为数据分析的新手,这是一个很好的入门练习项目。
任务很明确,就是要根据给出的79个特征,预测对应的房价,这些特征包括房子的类型、临街宽度、各层的面积等等。
数据可以在以下链接下载:
Kaggle: House Price
给出的数据包括四份文件:
· ‘train.csv’:训练数据
· ‘test.csv’:测试数据
· ‘data_description.txt’:说明各个特征的文档
· ‘sample_submission.csv’:预测结果提交的示例

评价指标

Kaggle给出的评价指标是回归问题中常用的均方误差(RMSE):
RMSE=1n∑i=1n(yi−yi^)2RMSE = \sqrt{\frac{1}{n}\displaystyle\sum_{i=1}^n(y_i-\hat{y_i})^2} RMSE=n1​i=1∑n​(yi​−yi​^​)2​

2.数据处理

数据探索

俗话说,知己知彼 百战不殆。拿到数据之后要做的第一件事就是了解你手中的这份数据。

导入所需的库

首先导入必要的库:

import numpy as npimport pandas as pd
pd.set_option('display.float_format', lambda x: '{:.2f}'.format(x))import seaborn as sns
color = sns.color_palette()
sns.set_style('darkgrid')import matplotlib.pyplot as plt
%matplotlib inlinefrom scipy import stats
from scipy.special import boxcox1p
from scipy.stats import norm, skew#忽略警告
import warnings
def ignore_warn(*args, **kwargs):pass
warnings.warn = ignore_warnfrom sklearn.preprocessing import LabelEncoder

查看数据集

先来看看训练集:

train = pd.read_csv('train.csv')
print('The shape of training data:', train.shape)
train.head()

The shape of training data: (1460, 81)


可以看到训练数据的大小是1460*81,也就是说训练数据总共有1460条,81列,其中最后一列是我们的预测目标:SalePrice。(训练数据的表格因为太长,我这里没有全部放出来)
再来看看测试数据:

test = pd.read_csv('test.csv')
print('The shape of testing data:', test.shape)
test.head()

The shape of testing data: (1459, 80)

测试数据一共是1459条,80列。
注意到Id这一列是直接从1顺次排到2919的,训练数据取的是1 ~ 1460,测试数据取的是1461 ~ 2919,说明Id和房价没有任何关系,所以直接去掉这一列:

#ID列没有用,直接删掉
train.drop('Id', axis=1, inplace=True)
test.drop('Id', axis=1, inplace=True)
print('The shape of training data:', train.shape)
print('The shape of testing data:', test.shape)

The shape of training data: (1460, 80)
The shape of testing data: (1459, 79)

去掉之后训练数据大小是146080,测试数据是145979。

目标值分析

要了解整个数据,我们首先得了解要预测的目标值,包括两方面:目标值的分布、其他特征与目标值的关系。
我们先来看看目标值的分布:

#绘制目标值分布
sns.distplot(train['SalePrice'])


明显的右偏分布,这就意味着我们之后要对目标值做一些处理,因为回归模型在正态分布的数据集上表现更好。
再看看目标值的统计值:

train['SalePrice'].describe()

count 1460.00
mean 180921.20
std 79442.50
min 34900.00
25% 129975.00
50% 163000.00
75% 214000.00
max 755000.00
Name: SalePrice, dtype: float64

最大值和均值之间差距比较大,可能会存在异常值。
这里有一个小trick:把类别特征和数字特征分离开来,在处理的时候会比较方便。

#分离数字特征和类别特征
num_features = []
cate_features = []
for col in test.columns:if test[col].dtype == 'object':cate_features.append(col)else:num_features.append(col)
print('number of numeric features:', len(num_features))
print('number of categorical features:', len(cate_features))

number of numeric features: 36
number of categorical features: 43

总共有36个数字特征,43个类别特征。
查看目标值和数字特征之间的关系(查看数字特征通常采用散点图):

#查看数字特征与目标值的关系
plt.figure(figsize=(16, 20))
plt.subplots_adjust(hspace=0.3, wspace=0.3)
for i, feature in enumerate(num_features):plt.subplot(9, 4, i+1)sns.scatterplot(x=feature, y='SalePrice', data=train, alpha=0.5)plt.xlabel(feature)plt.ylabel('SalePrice')
plt.show()


可以看到,‘TotalBsmtSF’、'GrLiveArea’与目标值之间有明显的线性关系,那么这两个值对目标值的预测应该会有很大的帮助,这就是我们要重点关注的特征。
在类别特征中,凭直觉来看,'Neighborhood’这个特征应该是很重要的,房子的房价往往和周围的房价是差不多的,为了验证这个想法,我们来看看不同类型的’Neighborhood’房价的分布情况(查看类别特征通常采用箱线图):

#查看‘Neighborhood’与目标值的关系
plt.figure(figsize=(16, 12))
sns.boxplot(x='Neighborhood', y='SalePrice', data=train)
plt.xlabel('Neighborhood', fontsize=14)
plt.ylabel('SalePrice', fontsize=14)
plt.xticks(rotation=90, fontsize=12)


不同的’Neighborhood’类型房价的分布区间是明显不同的,这验证了我们的猜想。
在逛kaggle的kernel的时候,有人提到不同年份的房价会不会跟通胀有关系,我觉得这是个很有意思的想法,于是用同样的方法来看一下出售年份’YrSold’和房价的关系:

plt.figure(figsize=(16, 10))
sns.boxplot(x='YrSold', y='SalePrice', data=train)
plt.xlabel('YrSold', fontsize=14)
plt.ylabel('SalePrice', fontsize=14)
plt.xticks(rotation=90, fontsize=12)


看起来不同的出售年份房价的分布状态差不多,所以我们就不考虑把各个年份的通胀数据加进来了。

多变量分析

单个特征分析完了,我们来看看所有特征之间的相关关系:

corrs = train.corr()
plt.figure(figsize=(16, 16))
sns.heatmap(corrs)


由于特征比较多,我们可以取与目标值相关度最高的十个特征再次绘制相关度热图:

#分析与目标值相关度最高的十个变量
cols_10 = corrs.nlargest(10, 'SalePrice')['SalePrice'].index
corrs_10 = train[cols_10].corr()
plt.figure(figsize=(6, 6))
sns.heatmap(corrs_10, annot=True)


并绘制这十个特征两两之间的散点图:

g = sns.PairGrid(train[cols_10])
g.map_diag(plt.hist)
g.map_offdiag(plt.scatter)


到这里,对数据的探索就基本完成了。目前可以看出,‘TotalBsmtSF’、‘GrLiveArea’、'Neighborhood’这几个是我们要重点关注的特征。

特征工程

异常值处理

观察’TotalBsmtSF’、'GrLiveArea’与目标值的散点图,可以看到存在异常值:

sns.scatterplot(x='TotalBsmtSF', y='SalePrice', data=train)


‘TotalBsmtSF’-'SalePrice’基本呈线性关系,右下角有一个明显的异常值。我们去掉这个异常值,并重新绘图检查以下:

#处理掉右下的明显异常值
train = train.drop(train[(train['TotalBsmtSF']>6000) & (train['SalePrice']<200000)].index)sns.scatterplot(x='TotalBsmtSF', y='SalePrice', data=train)


此时异常值已经没有了。
对’GrLiveArea’进行同样的处理:

sns.scatterplot(x='GrLivArea', y='SalePrice', data=train)


也是右下角存在异常值,同样删去:

#处理掉右下的异常值
train = train.drop(train[(train['GrLivArea']>4000) & (train['SalePrice']<200000)].index)sns.scatterplot(x='GrLivArea', y='SalePrice', data=train)

缺失值处理

注意不能把训练数据和测试数据连接在一起处理,会造成数据泄露,应该分步处理,先处理训练数据,再同样地处理测试数据。
先查看训练数据各个特征中的缺失值个数:

#查看训练集中各特征的数据缺失个数
print('The shape of training data:', train.shape)
train_missing = train.isnull().sum()
train_missing = train_missing.drop(train_missing[train_missing==0].index).sort_values(ascending=False)
train_missing

The shape of training data: (1458, 80)
PoolQC 1452
MiscFeature 1404
Alley 1367
Fence 1177
FireplaceQu 690
LotFrontage 259
GarageYrBlt 81
GarageType 81
GarageFinish 81
GarageQual 81
GarageCond 81
BsmtFinType2 38
BsmtExposure 38
BsmtFinType1 37
BsmtCond 37
BsmtQual 37
MasVnrArea 8
MasVnrType 8
Electrical 1
dtype: int64

再查看测试数据各个特征中的缺失值个数:

#查看测试集中各特征的数据缺失个数
print('The shape of testing data:', test.shape)
test_missing = test.isnull().sum()
test_missing = test_missing.drop(test_missing[test_missing==0].index).sort_values(ascending=False)
test_missing

The shape of testing data: (1459, 79)
PoolQC 1456
MiscFeature 1408
Alley 1352
Fence 1169
FireplaceQu 730
LotFrontage 227
GarageYrBlt 78
GarageCond 78
GarageQual 78
GarageFinish 78
GarageType 76
BsmtCond 45
BsmtExposure 44
BsmtQual 44
BsmtFinType1 42
BsmtFinType2 42
MasVnrType 16
MasVnrArea 15
MSZoning 4
BsmtFullBath 2
BsmtHalfBath 2
Utilities 2
Functional 2
Exterior2nd 1
Exterior1st 1
SaleType 1
BsmtFinSF1 1
BsmtFinSF2 1
BsmtUnfSF 1
KitchenQual 1
GarageCars 1
GarageArea 1
TotalBsmtSF 1
dtype: int64

测试数据中存在缺失值的特征会更多一些。

类别特征处理

先来处理类别特征,这些特征缺失值的处理有以下几种方法:

· 补充为’None’

根据特征说明文档,以下特征缺失代表没有,所以直接补充为’None’就可以了:

none_lists = ['PoolQC', 'MiscFeature', 'Alley', 'Fence', 'FireplaceQu', 'GarageType', 'GarageFinish', 'GarageQual', 'GarageCond', 'BsmtFinType1','BsmtFinType2', 'BsmtCond', 'BsmtExposure', 'BsmtQual', 'MasVnrType']
for col in none_lists:train[col] = train[col].fillna('None')test[col] = test[col].fillna('None')
· 补充出现频率最高的一类

根据文档,以下特征缺失不代表没有,只是说明数据丢失了,所以对缺失值补充该特征中出现次数最多的值:

most_lists = ['MSZoning', 'Exterior1st', 'Exterior2nd', 'SaleType', 'KitchenQual', 'Electrical']
for col in most_lists:train[col] = train[col].fillna(train[col].mode()[0])test[col] = test[col].fillna(train[col].mode()[0])    #注意这里补充的是训练集中出现最多的类别

注意处理测试数据时,我们仍然是向其中补充和训练数据一样的值,而不用测试数据中出现次数最多的值。
不同的是,‘Functional’这个特征,文档中说缺失值都看作是’Typ’,所以直接填入’Typ’。另外,‘Utilities’在训练集中全为’AllPub’,除了两个’NoSeWa’,而在测试集中有两处缺失,其他全为’AllPub’,说明这个特征对测试集的房价预测没有帮助,所以删掉该特征:

train['Functional'] = train['Functional'].fillna('Typ')
test['Functional'] = test['Functional'].fillna('Typ')train.drop('Utilities', axis=1, inplace=True)
test.drop('Utilities', axis=1, inplace=True)

数字特征处理

· 补零

同样参照特征说明文档,对可能为零的特征,缺失值全部补零:

zero_lists = ['GarageYrBlt', 'MasVnrArea', 'BsmtFullBath', 'BsmtHalfBath', 'BsmtFinSF1', 'BsmtFinSF2', 'BsmtUnfSF', 'GarageCars', 'GarageArea','TotalBsmtSF']
for col in zero_lists:train[col] = train[col].fillna(0)test[col] = test[col].fillna(0)
· 补充中位数

对不能为零的特征,按’Neighborhood’分组,补充为同类’Neighborhood’中该特征的中位数:

train['LotFrontage'] = train.groupby('Neighborhood')['LotFrontage'].apply(lambda x: x.fillna(x.median()))
for ind in test['LotFrontage'][test['LotFrontage'].isnull().values==True].index:x = test['Neighborhood'].iloc[ind]test['LotFrontage'].iloc[ind] = train.groupby('Neighborhood')['LotFrontage'].median()[x]

注意测试数据中补充的是训练数据的中位数。
最后检查下是否还存在缺失值:

train.isnull().sum().any()

False

test.isnull().sum().any()

False

转换类别特征

类别特征不能直接输入模型,因此要对其进行编码,把它转换成数字。编码有两种方式,对于各个类别中可能存在顺序关系的,用LabelEncoder编码,对于不存在顺序关系的,用get_dummies。
先处理存在顺序关系的这部分特征:

#从存放类别特征的列表去掉'Utilities'
cate_features.remove('Utilities')
print('The number of categorical features:', len(cate_features))

The number of categorical features: 42

for col in cate_features:train[col] = train[col].astype(str)test[col] = test[col].astype(str)
le_features = ['Street', 'Alley', 'LotShape', 'LandContour', 'LandSlope', 'HouseStyle', 'RoofMatl', 'Exterior1st', 'Exterior2nd', 'ExterQual', 'ExterCond', 'Foundation', 'BsmtQual', 'BsmtCond', 'BsmtExposure', 'BsmtFinType1', 'BsmtFinType2', 'HeatingQC', 'CentralAir','KitchenQual', 'Functional', 'FireplaceQu', 'GarageFinish', 'GarageQual', 'GarageCond', 'PavedDrive', 'PoolQC', 'Fence']
for col in le_features:encoder = LabelEncoder()value_train = set(train[col].unique())value_test = set(test[col].unique())value_list = list(value_train | value_test)encoder.fit(value_list)train[col] = encoder.transform(train[col])test[col] = encoder.transform(test[col])

因为get_dummies需要对所有数据一起处理,所以放到最后。

处理偏斜特征

先获得偏斜度大于0.5的特征,我们要对这一部分进行处理:

skewness = train[num_features].apply(lambda x: skew(x)).sort_values(ascending=False)
skewness = skewness[skewness>0.5]
skew_features = skewness.index
skewness

MiscVal 24.43
PoolArea 15.93
LotArea 12.56
3SsnPorch 10.29
LowQualFinSF 9.00
KitchenAbvGr 4.48
BsmtFinSF2 4.25
ScreenPorch 4.11
BsmtHalfBath 4.10
EnclosedPorch 3.08
MasVnrArea 2.69
OpenPorchSF 2.34
LotFrontage 1.55
WoodDeckSF 1.54
MSSubClass 1.41
GrLivArea 1.01
BsmtUnfSF 0.92
1stFlrSF 0.89
2ndFlrSF 0.81
BsmtFinSF1 0.76
OverallCond 0.69
HalfBath 0.68
TotRmsAbvGrd 0.66
Fireplaces 0.63
BsmtFullBath 0.59
TotalBsmtSF 0.51
dtype: float64

由于数据中可能存在许多没有处理的异常值,为了增强模型对异常值的刚度,我们采用Box Cox转换来处理偏斜数据:

for col in skew_features:lam = stats.boxcox_normmax(train[col]+1)    #+1是为了保证输入大于零train[col] = boxcox1p(train[col], lam)test[col] = boxcox1p(test[col], lam)

构建新的特征

怎么构建新的特征是解决数据科学问题的关键,特征构造的好,就能够提高模型的得分上限,而这一点需要敏锐的直觉和长期的经验。
我们这里主要围绕与目标值相关度大的几个特征来构造新的特征:

train['IsRemod'] = 1
train['IsRemod'].loc[train['YearBuilt']==train['YearRemodAdd']] = 0  #是否翻新(翻新:1, 未翻新:0)
train['BltRemodDiff'] = train['YearRemodAdd'] - train['YearBuilt']  #翻新与建造的时间差(年)
train['BsmtUnfRatio'] = 0
train['BsmtUnfRatio'].loc[train['TotalBsmtSF']!=0] = train['BsmtUnfSF'] / train['TotalBsmtSF']  #Basement未完成占总面积的比例
train['TotalSF'] = train['TotalBsmtSF'] + train['1stFlrSF'] + train['2ndFlrSF']  #总面积
#对测试集做同样的处理
test['IsRemod'] = 1
test['IsRemod'].loc[test['YearBuilt']==test['YearRemodAdd']] = 0  #是否翻新(翻新:1, 未翻新:0)
test['BltRemodDiff'] = test['YearRemodAdd'] - test['YearBuilt']  #翻新与建造的时间差(年)
test['BsmtUnfRatio'] = 0
test['BsmtUnfRatio'].loc[test['TotalBsmtSF']!=0] = test['BsmtUnfSF'] / test['TotalBsmtSF']  #Basement未完成占总面积的比例
test['TotalSF'] = test['TotalBsmtSF'] + test['1stFlrSF'] + test['2ndFlrSF']  #总面积

处理其余的类别特征

先获得要处理的特征:

dummy_features = list(set(cate_features).difference(set(le_features)))
dummy_features

[‘SaleCondition’,
‘LotConfig’,
‘MSZoning’,
‘GarageType’,
‘RoofStyle’,
‘BldgType’,
‘MasVnrType’,
‘Condition1’,
‘Neighborhood’,
‘Electrical’,
‘MiscFeature’,
‘SaleType’,
‘Condition2’,
‘Heating’]

把训练数据和测试数据连接起来一起处理(因为独热编码不会产生数据泄露,所以可以放在一起处理):

all_data = pd.concat((train.drop('SalePrice', axis=1), test)).reset_index(drop=True)
all_data = pd.get_dummies(all_data, drop_first=True)  #注意独热编码生成的时候要去掉一个维度,保证剩下的变量都是相互独立的

保存处理后的训练数据和测试数据

数据处理的代码和模型预测的代码最好是分成两个文件,保存处理后的数据,这样处理后的数据可以随时读入模型中进行预测,不用每次测试新的模型的时候都把前面数据处理的代码重新跑一遍。
把连接起来的所有数据再拆分回训练集和测试集:

trainset = all_data[:1458]
y = train['SalePrice']
trainset['SalePrice'] = y.values
testset = all_data[1458:]
print('The shape of training data:', trainset.shape)
print('The shape of testing data:', testset.shape)

The shape of training data: (1458, 160)
The shape of testing data: (1459, 159)

保存处理后的数据:

trainset.to_csv('train_data.csv', index=False)
testset.to_csv('test_data.csv', index=False)

3.模型预测

导入所需的库

#基础
import numpy as np
import pandas as pd
import time#绘图
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline#模型
from sklearn.linear_model import Lasso, LassoCV, ElasticNet, ElasticNetCV, Ridge, RidgeCV
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor, StackingRegressor
from mlxtend.regressor import StackingCVRegressor
from sklearn.svm import SVR
import lightgbm as lgb
import xgboost as xgb#模型相关
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import RobustScaler
from sklearn.model_selection import KFold, cross_val_score
from sklearn.metrics import mean_squared_error#忽略警告
import warnings
def ignore_warn(*args, **kwargs):pass
warnings.warn = ignore_warn

读取数据,处理目标值

先读取训练集和测试集:

train = pd.read_csv('train_data.csv')
test = pd.read_csv('test_data.csv')
print('The shape of training data:', train.shape)
print('The shape of testing data:', test.shape)

在数据探索的时候,我们发现目标值是右偏斜的,所以对目标值取对数,将其处理成近似正态分布。
查看目标值的分布:

#查看目标值的斜度和峰度
from scipy.stats import skew, kurtosis, normy = train['SalePrice']
print('Skewness of target:', y.skew())
print('kurtosis of target:', y.kurtosis())
sns.distplot(y, fit=norm);

Skewness of target: 1.8812964895244009
kurtosis of target: 6.523066888485879


明显右偏,取对数:

y = np.log1p(y)
print('Skewness of target:', y.skew())
print('kurtosis of target:', y.kurtosis())
sns.distplot(y, fit=norm);

Skewness of target: 0.12157976050304879
kurtosis of target: 0.8047507917418972


此时目标值已经接近正态分布了。

定义交叉验证策略及评估方法

模型的验证有两种方法:留出法和交叉验证。因为训练集的数据较少,所以采用十折交叉验证:

#采用十折交叉验证
n_folds = 10def rmse_cv(model):kf = KFold(n_folds, shuffle=True, random_state=20)rmse = np.sqrt(-cross_val_score(model, train.values, y, scoring='neg_mean_squared_error', cv=kf))return(rmse)

单个模型参数设置

这里选择回归问题常用的六个模型:
· Lasso
· ElasticNet
· Ridge
· Gradient Boosting
· LightGBM
· XGBoost
kaggle的比赛中单个模型能做好,那么模型融合后的效果就不会差,因此,要反复地调参,找到模型的最佳参数。以下给出的参数设置是已经经过调参得到的,关于调参网上有很多经验分享,这里不另行赘述,调参部分的代码我也放在github中了。

#Lasso
lasso_alpha = [0.00005, 0.0001, 0.0002, 0.0005, 0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1.0]
lasso = make_pipeline(RobustScaler(), LassoCV(alphas=lasso_alpha, random_state=2))#ElasticNet
enet_beta = [0.1, 0.2, 0.5, 0.6, 0.8, 0.9]
enet_alpha = [0.00005, 0.0001, 0.0002, 0.0005, 0.001, 0.002, 0.005, 0.01]
ENet = make_pipeline(RobustScaler(), ElasticNetCV(l1_ratio=enet_beta, alphas=enet_alpha, random_state=12))#Ridge
rid_alpha = [0.00005, 0.0001, 0.0002, 0.0005, 0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1.0]
rid = make_pipeline(RobustScaler(), RidgeCV(alphas=rid_alpha))#Gradient Boosting
gbr_params = {'loss': 'huber','criterion': 'mse', 'learning_rate': 0.1,'n_estimators': 600, 'max_depth': 4,'subsample': 0.6,'min_samples_split': 20,'min_samples_leaf': 5,'max_features': 0.6,'random_state': 32,'alpha': 0.5}
gbr = GradientBoostingRegressor(**gbr_params)#LightGBM
lgbr_params = {'learning_rate': 0.01,'n_estimators': 1850, 'max_depth': 4,'num_leaves': 20,'subsample': 0.6,'colsample_bytree': 0.6,'min_child_weight': 0.001,'min_child_samples': 21,'random_state': 42,'reg_alpha': 0,'reg_lambda': 0.05}
lgbr = lgb.LGBMRegressor(**lgbr_params)#XGBoost
xgbr_params = {'learning_rate': 0.01,'n_estimators': 3000, 'max_depth': 5,'subsample': 0.6,'colsample_bytree': 0.7,'min_child_weight': 3,'seed': 52,'gamma': 0,'reg_alpha': 0,'reg_lambda': 1}
xgbr = xgb.XGBRegressor(**xgbr_params)

单个模型评估

用之前设定的评估方法进行评估:

models_name = ['Lasso', 'ElasticNet', 'Ridge', 'Gradient Boosting', 'LightGBM', 'XGBoost']
models = [lasso, ENet, rid, gbr, lgbr, xgbr]
for i, model in enumerate(models):score = rmse_cv(model)print('{} score: {}({})'.format(models_name[i], score.mean(), score.std()))

Lasso score: 0.11068147519566576(0.0073942264704033155)
ElasticNet score: 0.11091904371233648(0.007532173448331412)
Ridge score: 0.11119303368441777(0.00758400106291327)
Gradient Boosting score: 0.11865538871859024(0.009497984510023386)
LightGBM score: 0.118318792967013(0.010364808635531306)

Stacking

模型融合有Stacking和Blending两种方法,可以直接采用mlxtend库提供的StackingCVRegressor构建Stacking模型:

stack_model = StackingCVRegressor(regressors=(lasso, ENet, rid, gbr, lgbr, xgbr), meta_regressor=lasso, use_features_in_secondary=True)

在整个训练集上训练各个模型

#Lasso
lasso_trained = lasso.fit(np.array(train), np.array(y))#ElasticNet
ENet_trained = ENet.fit(np.array(train), np.array(y))#Ridge
rid_trained = rid.fit(np.array(train), np.array(y))#Gradient Boosting
gbr_trained = gbr.fit(np.array(train), np.array(y))#LightGBM
lgbr_trained = lgbr.fit(np.array(train), np.array(y))#XGBoost
xgbr_trained = xgbr.fit(np.array(train), np.array(y))#Stacking
stack_model_trained = stack_model.fit(np.array(train), np.array(y))

评估各个模型在训练集上的表现

先定义评估方法,采用kaggle规定的评估方法:

def rmse(y, y_preds):return np.sqrt(mean_squared_error(y, y_preds))

评估模型:

models.append(stack_model)
models_name.append('Stacking_model')
for i, model in enumerate(models):y_preds = model.predict(np.array(train))model_score = rmse(y, y_preds)print('RMSE of {}: {}'.format(models_name[i], model_score))

RMSE of Lasso: 0.0989275731446597
RMSE of ElasticNet: 0.09866027051143429
RMSE of Ridge: 0.09681908262749463
RMSE of Gradient Boosting: 0.06583064311955181
RMSE of LightGBM: 0.0641626832182992
RMSE of XGBoost: 0.02286417135304461
RMSE of Stacking_model: 0.09431153828814741

提交预测结果

sample_submission = pd.read_csv('sample_submission.csv')
for i, model in enumerate(models):preds = model.predict(np.array(test))submission = pd.DataFrame({'Id': sample_submission['Id'], 'SalePrice': np.expm1(preds)})submission.to_csv('House_Price_submission_'+models_name[i]+'_optimation.csv', index=False)print('{} finished.'.format(models_name[i]))

Lasso finished.
ElasticNet finished.
Ridge finished.
Gradient Boosting finished.
LightGBM finished.
XGBoost finished.
Stacking_model finished.

Blending

采用两种比较简单的融合方法:均值融合和加权融合。
均值融合:

#均值融合
preds_in_train = np.zeros((len(y), len(models)))
for i, model in enumerate(models):preds_in_train[:, i] = model.predict(np.array(train))
average_preds_in_train = preds_in_train.mean(axis=1)
average_score = rmse(y, average_preds_in_train)
print('RMSE of average model on training data:', average_score)

RMSE of average model on training data: 0.07286858974899155

#提交均值融合预测结果
preds_in_test = np.zeros((len(test), len(models)))
for i, model in enumerate(models):preds_in_test[:, i] = model.predict(np.array(test))
average_preds_in_test = preds_in_test.mean(axis=1)average_submission = pd.DataFrame({'Id': sample_submission['Id'], 'SalePrice': np.expm1(average_preds_in_test)})
average_submission.to_csv('House_Price_submission_average_model_optimation.csv', index=False)

加权融合,根据各模型的得分取合适的权重:

model_weights = [0.15, 0.12, 0.08, 0.08, 0.12, 0.15, 0.3]
weight_preds_in_train = np.matmul(preds_in_train, model_weights)weight_score = rmse(y, weight_preds_in_train)
print('RMSE of weight model on training data:', weight_score)

RMSE of weight model on training data: 0.07525884061133567

可以看到,加权融合比均值融合有更好的表现。

#提交权值融合预测结果
weight_preds_in_test = np.matmul(preds_in_test, model_weights)weight_submission = pd.DataFrame({'Id': sample_submission['Id'], 'SalePrice': np.expm1(weight_preds_in_test)})
weight_submission.to_csv('House_Price_submission_weight_model_optimation.csv', index=False)

可以把最后的结果保存起来,以便以后有更好的想法对这些结果进一步处理,提高得分。
提交到kaggle得到的Private Score如下图:

Kaggle经典项目——房价预测相关推荐

  1. 动手学深度学习:3.16 实战Kaggle比赛:房价预测

    3.16 实战Kaggle比赛:房价预测 作为深度学习基础篇章的总结,我们将对本章内容学以致用.下面,让我们动手实战一个Kaggle比赛:房价预测.本节将提供未经调优的数据的预处理.模型的设计和超参数 ...

  2. [Kaggle] Housing Prices 房价预测

    文章目录 1. Baseline 1. 特征选择 2. 异常值剔除 3. 建模预测 2. 待优化特征工程 房价预测 kaggle 地址 参考文章:kaggle比赛:房价预测(排名前4%) 1. Bas ...

  3. 超详解pytorch实战Kaggle比赛:房价预测

    详解pytorch实战Kaggle比赛:房价预测 教程名称 教程地址 机器学习/深度学习 [李宏毅]机器学习/深度学习国语教程(双语字幕) 生成对抗网络 [李宏毅]生成对抗网络国语教程(双语字幕) 目 ...

  4. kaggle简单实战——房价预测(xgboost实现)

    最近正在学习xgboost,因此在kaggle上用xgboost做了个简单的小项目--波士顿房价预测(https://www.kaggle.com/c/house-prices-advanced-re ...

  5. Kaggle实战之 房价预测案例

    房价预测案例(进阶版) 这是进阶版的notebook.主要是为了比较几种模型框架.所以前面的特征工程部分内容,我也并没有做任何改动,重点都在后面的模型建造section Step 1: 检视源数据集 ...

  6. kaggle小白入门——房价预测top2%~top1%

    入门第二战,达到了top1%的分数,有点小兴奋,不过也有可能为公分的提高使模型过拟合了,但入门赛貌似也只能追求公分的提高. 言归正传,开战. 一.导包 # 数据处理及可视化 import numpy ...

  7. 机器学习初级项目--房价预测案例

    项目背景: 运用回归模型进行房价预测. 影响房价的因素有很多,在本题的数据集中有79个变量几乎描述了爱荷华州艾姆斯(Ames,lowa)住宅的方方面面,要求预测最终的房价. 数据介绍: 我们要使用Ba ...

  8. 【问题3】:Kaggle练习题《房价预测》----分别采用的岭回归,随机森林,bagging模型,AdaBoost,XgBoost等。

    第一步:导入基本的模块, 并且加载数据. import pandas as pd import numpy as np import matplotlib.pyplot as plt# index_c ...

  9. kaggle机器学习作业(房价预测)

    来源:kaggle Machine Learning Micro-Course Home Page Recap Here's the code you've written so far. Start ...

最新文章

  1. 【HeadFirst 设计模式总结】1.策略模式
  2. 微信运动:抓住用户的小九九,一个都别跑!
  3. [网络安全自学篇] 五十九.Windows安全缺陷利用之MS08-067远程代码执行及深度防御解析
  4. Sqlite大数据量查询优化比较-转
  5. 网络协议:关于TCP/IP,必须知道的十个知识点
  6. windows下检測文件改变
  7. Java贪吃蛇小游戏
  8. Expressive TTS 相关论文阶段性总结
  9. Java里用Frame框架写流星雨
  10. java实验学校与教师_java实验
  11. 加拿大计算机硕士gpa不够,申请加拿大硕士课程有GPA不足的硬伤怎么办?
  12. dad my_【玩转英文绘本】My Dad!《我爸爸》
  13. 论职务犯罪案件侦查 z
  14. Git教程及常用命令
  15. js中的this及箭头函数
  16. 封杀太愚蠢,马斯克喊话解封特朗普推特账号!
  17. [Samba]源码安装samba
  18. 云队友丨技术狂李彦宏的二次创业
  19. 一种不太完善的OpenStreetMap字典汉化方法
  20. Live2D 性能优化

热门文章

  1. Android PNG图片像素检测及剪裁优化
  2. OCR-文本检测和文本识别的数据
  3. clappr:可扩展网页媒体播放器使用(在vue中的使用)
  4. 贪吃蛇游戏 c++代码 ↑,↓,→,←分别控制蛇的方向 增加蛇长 随机生成食物 吃食物 吃撞墙 撞到自己
  5. pool(二)——动手入门
  6. 一键收藏网页上的文章,浏览器书签方便日常查找和管理
  7. Android播放音效
  8. 【应急】应急信息“一张图”,福建省是这样用的
  9. 实验四 类和对象;类的继承和派生;多态性; 接口;构造器应用
  10. Java 应用程序,该程序中有 3 个类:Triangle、T rapezium 和 Circle,分别用来 表示三角形、梯形和圆形。