【问题3】:Kaggle练习题《房价预测》----分别采用的岭回归,随机森林,bagging模型,AdaBoost,XgBoost等。
第一步:导入基本的模块, 并且加载数据。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# index_col=0 将第0列作为行索引
train_df = pd.read_csv('data/home_price/train.csv', index_col=0)
test_df = pd.read_csv('data/home_price/test.csv', index_col=0)
# print(train_df.head())
# print(train_df.shape)
下面是读取出来的数据 数据集下载地址:https://www.kaggle.com/c/house-prices-advanced-regression-techniques
MSSubClass | MSZoning | LotFrontage | LotArea | Street | Alley | LotShape | LandContour | Utilities | LotConfig | ... | PoolArea | PoolQC | Fence | MiscFeature | MiscVal | MoSold | YrSold | SaleType | SaleCondition | SalePrice | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Id | |||||||||||||||||||||
1 | 60 | RL | 65.0 | 8450 | Pave | NaN | Reg | Lvl | AllPub | Inside | ... | 0 | NaN | NaN | NaN | 0 | 2 | 2008 | WD | Normal | 208500 |
2 | 20 | RL | 80.0 | 9600 | Pave | NaN | Reg | Lvl | AllPub | FR2 | ... | 0 | NaN | NaN | NaN | 0 | 5 | 2007 | WD | Normal | 181500 |
3 | 60 | RL | 68.0 | 11250 | Pave | NaN | IR1 | Lvl | AllPub | Inside | ... | 0 | NaN | NaN | NaN | 0 | 9 | 2008 | WD | Normal | 223500 |
4 | 70 | RL | 60.0 | 9550 | Pave | NaN | IR1 | Lvl | AllPub | Corner | ... | 0 | NaN | NaN | NaN | 0 | 2 | 2006 | WD | Abnorml | 140000 |
5 | 60 | RL | 84.0 | 14260 | Pave | NaN | IR1 | Lvl | AllPub | FR2 | ... | 0 | NaN | NaN | NaN | 0 | 12 | 2008 | WD | Normal | 250000 |
5 rows × 80 columns
kaggle平台提供了两张表,一张是带标签的数据(训练数据), 一张是不带标签的数据(测试数据)。接下来,我想做的就是将带有标签数据的标签列删除,它也就变成不带标签的数据。在将测试数据与其进行合并。这么做主要是为了统一进行数据预处理的时候更加方便。等所有的需要的预处理进行完之后,我们再把他们分隔开。
第二步:合并数据
prices = pd.DataFrame({'price': train_df['SalePrice'], 'log(price+1)': np.log1p(train_df['SalePrice'])})
prices.hist() # 画图 看一下标签是否平滑
plt.show()
y_train = np.log1p(train_df.pop('SalePrice')) # 将原来的标签删除 剩下log(price+1)列的数据
all_df = pd.concat((train_df, test_df), axis=0) # 将train_df, test_df合并
我们这里除了将标签取下来,还构造了一个新列log(price+1) 这样主要是为了不让数据倾斜。 也就是让数据尽可能服从高斯分布。log1p(x) 就是log(x+1)。 这里我们将数据进行了平滑,那么按“怎么来的怎么去”原则,最后的预测结果就需要expm1()。 也就是将预测结果变为真实的房价数。 log1p反运算就是expm1()
第三步:特征工程(处理缺失值等)
# 有些数据的取值只有四五,或者可数个。这类数据我们转为one_hot编码
# 发现MSSubClass值应该是分类值
print(all_df['MSSubClass'].dtypes) # int64
all_df['MSSubClass'] = all_df['MSSubClass'].astype(str)
print(all_df['MSSubClass'].value_counts())
# 我们将category的变量转变为numerical表达形式
# 当我们用numerical来表达categorical的时候,要注意,数字本身有大小。
# 不能乱指定大小,我们采用one_hot编码
# pandas自带的get_dummies方法可以一键做到one_hot
print(pd.get_dummies(all_df['MSSubClass'], prefix='MSSubClass').head())
# 此刻MSSubClass被我们分成了12个column,每一个代表一个category。是就是1,不是就是0。
# 同理,我们把所有的category数据都转化为One_hot
all_dummy_df = pd.get_dummies(all_df)
# 缺失值处理
# 统计每列缺失值情况
print(all_dummy_df.isnull().sum().sort_values(ascending=False).head())
# 可以看到,缺失最多的column是LotFrontage
# 处理这些缺失的信息,得靠好好审题。一般来说,数据集的描述里会写的很清楚,这些缺失都代表着什么。当然,如果实在没有的话,也只能靠自己的『想当然』。。
# 在这里,我们用平均值来填满这些空缺
# 我们用均值填充
mean_cols = all_dummy_df.mean()
all_dummy_df = all_dummy_df.fillna(mean_cols)
# 再检查一下是否有缺失值
print(all_dummy_df.isnull().sum().sum())
# 标准化数字型数据
# 标准化numerical数据¶
# 这一步并不是必要,但是得看你想要用的分类器是什么。一般来说,regression的分类器都比较傲娇,最好是把源数据给放在一个标准分布内。不要让数据间的差距太大。
# 这里,我们当然不需要把One-Hot的那些0/1数据给标准化。我们的目标应该是那些本来就是numerical的数据:
# 先找出数字型数据
numeric_cols = all_df.columns[all_df.dtypes != 'object']
# print(numeric_cols)
# 对其标准化
numeric_col_mean = all_dummy_df.loc[:, numeric_cols].mean()
numeric_col_std = all_dummy_df.loc[:, numeric_cols].std()
all_dummy_df.loc[:, numeric_cols] = (all_dummy_df.loc[:, numeric_cols]-numeric_col_mean) / numeric_col_std
# 将合并的数据此时进行拆分 分为训练数据和测试数据
dummy_train_df = all_dummy_df.loc[train_df.index]
dummy_test_df = all_dummy_df.loc[test_df.index]
这一步我们分别进行了:将训练数据和测试数据进行合并,主要是为了方便进行统一处理; 将category型的数据转化为one_hot编码; 对缺失值进行填充; 标准化数据; 将合并的数据又分开。
第四步:模型的建立,预测
模型1:岭回归
from sklearn.linear_model import Ridge
from sklearn.model_selection import cross_val_score
X_train = dummy_train_df.values
X_test = dummy_test_df.values
alphas = np.logspace(-3, 2, 50)
test_scores = []
for alpha in alphas:
clf = Ridge(alpha)
test_score = np.sqrt(-cross_val_score(clf, X_train, y_train, cv=10, scoring='neg_mean_squared_error'))
test_scores.append(np.mean(test_score))
# 看那个alpha下 模型预测的更好
plt.plot(alphas, test_scores)
plt.title('Alpha vs CV Error')
plt.show()
根据图像发现alpha值为15左右的时候,模型最佳。 均方误差值为:0.135左右
模型二:随机森林(基本模型我们设为岭回归。一般默认为回归树)
from sklearn.ensemble import RandomForestRegressor
max_features = [0.1, 0.3, 0.5, 0.7, 0.9, 0.99]
test_scores = []
for max_feat in max_features:
clf = RandomForestRegressor(n_estimators=200, max_features=max_feat)
test_score = np.sqrt(-cross_val_score(clf, X_train, y_train, cv=5, scoring='neg_mean_squared_error'))
test_scores.append(np.mean(test_score))
plt.plot(max_features, test_scores)
plt.title('Max Features vs CV Error')
plt.show()
max_features 代表的划分是考虑的最大特征数,可以是大于1的具体数字, 代表考虑多少个特征。 如果是小于1的数字,代表的是特征的百分之多少。
从上图中我们可以看到当max_feature为0.3左右的时候,均方误差最小。大概也是0.135
模型三:Stacking集成 (用stacking的思维来汲取两种或多种模型的优点)
# 我们用一个Stacking的思维来汲取两种或多种模型的优点
ridge = Ridge(alpha=15)
rf = RandomForestRegressor(n_estimators=500, max_features=0.3)
ridge.fit(X_train, y_train)
rf.fit(X_train, y_train)
# 上面提到了,因为最前面我们给label做了个log(1+x), 于是这里我们需要把predit的值给exp回去,并且减掉那个"1"
# 所以就是我们的expm1()函数。
y_ridge = np.expm1(ridge.predict(X_test))
y_rf = np.expm1(rf.predict(X_test))
y_final = (y_ridge + y_rf) / 2
submission_df = pd.DataFrame(data={'ID': test_df.index, 'SalePrice': y_final})
# print(submission_df)
submission_df 就是我们这场比赛需要提交的结果。也就是测试样本的预测值。
模型四: bagging模型
from sklearn.ensemble import BaggingRegressor
from sklearn.model_selection import cross_val_score
params = [1, 10, 15, 20, 25, 30, 40]
test_scores = []
for param in params:
clf = BaggingRegressor(n_estimators=param, base_estimator=ridge) # 基模型为岭回归
test_score = np.sqrt(-cross_val_score(clf, X_train, y_train, cv=10, scoring='neg_mean_squared_error'))
test_scores.append(np.mean(test_score))
plt.plot(params, test_scores)
plt.title('n_estimator vs CV Error')
plt.show()
bagging就是把多个小分类器放在一起,每个train随机选取一部分,然后把他们的最终结果综合起来(投票制)
分类器个数为10个的时候 均方误差最小,大概为0.132。 比前面的几种模型好
模型五:Adaboost
from sklearn.ensemble import AdaBoostRegressor
params = [10, 15, 20, 25, 30, 35, 40, 45, 50]
test_scores = []
for param in params:
clf = AdaBoostRegressor(n_estimators=param, base_estimator=ridge)
test_score = np.sqrt(-cross_val_score(clf, X_train, y_train, cv=10, scoring='neg_mean_squared_error'))
test_scores.append(np.mean(test_score))
plt.plot(params, test_scores)
plt.title("n_estimator vs CV Error")
这里的基本分类器依旧是岭回归 ,我们找的还是基分类器个数与均方误差的关系
Adaboos+Ridge, 用35个弱分类器的情况下,均方误差能降到0.141左右。。可以再增加基分类器个数 看图像的走势
模型六:Kaggle神器:XgBoost
from xgboost import XGBRegressor
# 用sklearn自带的cross validation方法来测试模型
params = [1, 2, 3, 4, 5, 6]
test_scores = []
for param in params:
clf = XGBRegressor(max_depth=param)
test_score = np.sqrt(-cross_val_score(clf, X_train, y_train, cv=10, scoring='neg_mean_squared_error'))
test_scores.append(np.mean(test_score))
plt.plot(params, test_scores)
plt.title('max_depth vs CV Error')
plt.show()
惊了,深度为5的时候,错误率缩小到0.127。。这就是为什么,浮躁的竞赛圈,人人都在用XGBoost