实战:通过简单的机器学习实现房价预测

概述

在本文中,我们将从一个专注于机器学习的网站Kaggle上获取一个有关房价的数据集,并通过房子的各个特征完成房价的预测。

关键词: 预测、回归、机器学习、特征处理

数据概览

下载数据

点击这里前往数据页面,注册用户后转到 Data 页面,之后下拉页面直到找到 Data Explorer 对话框,点击 Download All 按钮即可现在所有数据,数据共有四个文件,其中:

  • train.csv: 训练集,包含所有特征及你要预测的结果。
  • test.csv: 测试集,仅包含特征,你需要根据这些特征预测结果。
  • sample_submission.csv: 测试结果提交格式。
  • data_description.txt: 数据简介,说明了每一列是啥意思。

查看数据

本文章使用 Jupyter Notebook 进行数据处理与模型训练,这是一个为数据工作者打造的开发环境。当然读者使用其它环境也是不影响的。
将数据解压并拷贝到工作目录的 \data 文件夹,导入 pandas 库加载文件,并查看数据形状。

import pandas as pd

data = pd.read_csv(r"data\train.csv")
data.shape

运行结果为 (1460, 81),说明给出的数据共有1460行,每行数据都有80个特征(每行有81列,但最后一列是要预测的结果)通过给出的 data_description.txt 文件,我们可以理解每一个特征表示什么含义。举例前几个特征含义如下:

特证名称 含义
MSSubClass 建筑类别
MSZoning 一般分区分类
LotFrontage 与房产相连的街道长度
LotArea 地块面积(平方英尺)

数据预处理

加载数据

完成数据读取后,我们需要将数据分为 xy 两部分,其中 x 是特征矩阵,y 是预测结果。
由于最后一列是预测结果,我们将前80列作为 x,最后一列作为 y 即可。

x = data.iloc[:, 0:79]
y = data.iloc[:, 80:81]

处理标签

由于部分数据是以文本标签形式给出,而我们后续用到的训练器不支持接受文本型变量,所以我们需要将这些标签变量进行预处理。清洗数据是个又脏又累的活,这篇文章中我们不追求特别高的准确率,所以直接偷个懒,将文本型变量进行编码即可。

使用序数编码器 OrdinalEncoder 进行全文本标签编码:

from sklearn.preprocessing import OrdinalEncoder

le = OrdinalEncoder()
x = le.fit_transform(x)

现在 x 中的所有标签均被编码为了数字。

填补空缺

完成数字编码后,查看 x 矩阵,发现 #6、#25、#57、#72、#73、#74列的缺失率均达到了30%以上,将这几个特征排除在外,不予考虑。

import numpy as np

x = np.delete(x, [6, 25, 57, 72, 73, 74], axis=1)

下面对 x 的有少量残余的特征进行处理,它们是#3、#24、#28、#29、#30、#31、#33、#40、#55、#57、#60、#61。其中#24、#40缺失少于1%,又考虑到#28、#29、#30、#31四列为同步缺失,总缺失量不超过3%,且#55、#56、#57、#60、#61为同步缺失,缺失量不超过6%,预计总损失小于10%,可以接受。因此对这十一列进行空行删除处理。同时将特征矩阵的第0列删掉,因为这列是索引,与房价没有任何关系,并且会影响后续的填补,

k = [0, 23, 24, 28, 55]
mask = ~np.isnan(x[:, k]).any(axis=1)
x = x[mask]

过滤完成后的 x.shape[0] 的结果为1338,也就是说在一顿删之还剩下1338行数据,数据损失率为 \frac{1460-1338}{1460}=8.36\%,完全可接受。
之后将其转换为 DataFrame 备用。这里对 y 进行一次强转的原因是去掉残留的索引,避免合并时发生混乱。

x = np.delete(x, [0], axis=1)
x = pd.DataFrame(x)
y = pd.DataFrame(np.array(y))

最后来处理 #3,该列缺失数据较多,需要进行填补。考虑到数据规模不宜采用K-近邻(KNN)算法,遂使用随机森林回归算法。以其余72列特征为 x',缺失数据为 y' 行缺失数据预测。在代码中我们用 x_i 表示 x',用 y_i 表示 y',下面划分训练集与测试集。

remain = x.iloc[:, x.columns != 2]
x_i = np.array(pd.concat([remain, y], axis=1))
y_i = x.iloc[:, 2]

y_i_train = y_i[y_i.notnull()]
y_i_test = y_i[y_i.isnull()]
x_i_train = x_i[y_i_train.index, :]
x_i_test = x_i[y_i_test.index, :]

这里我们将特征矩阵和预测结果拼接为 x' ,之后用 y' 的是否空行为依据划分出了训练集与测试集。(其实这里的测试集的真正作用是预测,也可以叫预测集)根据上述数据,构建随机森林回归器预测空缺失值,并将其填回特征矩阵原位。

from sklearn.ensemble import RandomForestRegressor
rfc = RandomForestRegressor()
rfc = rfc.fit(x_i_train, y_i_train)
y_i_predict = rfc.predict(x_i_test)

x.loc[x.iloc[:, 2].isnull(), 2] = y_i_predict

运行 x.isnull().any().any() ,结果为 False 。至此,对数据缺失值的处理已全部结束,接下来我们筛选数据特征,保证后续预测的高效和准确。

特征工程

方差过滤

对一组数据 x_1,x_2,...,x_n 而言,设 \bar{x} = \frac{x_1+\dots+x_n}{n},则统计量 s^2 = (x_1-\bar{x})^2 + \dots + (x_n-\bar{x})^2 为方差。方差越小,说明数据的波动程度越小,数据几乎不波动就跟完全没有类似,对预测没有任何意义。

使用方差过滤器 VarianceThreshold 进行方差检验,并筛选掉方差小于0.01的数据。

from sklearn.feature_selection import VarianceThreshold

selector = VarianceThreshold(threshold=.01)
x = selector.fit_transform(x)

筛选过后 x 的特征数为70,我们将其降了两维。

方差分析

方差检验(AVONA)用于估计标签与特征间的线性关系,其中零假设H_0为不存在显著的线性关系。设因子 Ar 个水平 A_1,\dots A_r,在水平 A_i 下进行了 n_i 次重复试验,数据分别为 y_{ij} 。令 n = \sum n_i\bar{y}=\frac{1}{n} \sum_{i=1}^r \sum_{j=1}^{n_i}y_{ij}y_{i·}=\frac{1}{n_i}\sum_{j=1}^{n_i}y_{ij} ,此时 S_T^2 = \sum_{i=1}^r \sum_{j=1}^{n_i}(y_{ij}-\bar{y})^2S_e^2= \sum_{i=1}^r \sum_{j=1}^{n_i}(y_{ij}-\bar{y_{i·}})^2,由残差平方和分解很容易得到 S_A^2=\sum_{i=1}^r=n_i(\bar{y_{i·}}-\bar{y})^2。构造 F=\frac{S_A^2/(r-1)}{S_e^2/(n-r)},查F分布表即可得显著性水平。
在这里我们令显著性水平 \alpha = 0.10 进行筛选,筛选出的特征可以认为有90%的把握与结果相关。

from sklearn.feature_selection import SelectFpr, f_classif

y = np.array(y).ravel()
selector = SelectFpr(score_func=f_classif, alpha=0.1)
x = selector.fit_transform(x, y)

筛选过后 x 的特征数为41,我们保留了最有用的41个特征。

模型训练

初步训练

由于数据集要求的是预测任务,且预测的结果是连续型随机变量。所以采用随机森林回归算法进行训练。先看一下基础状况如何。直接将元数据的70%作为训练集,30%作为测试集,训练并在测试集上打分。

from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(x, y,test_size=0.3)
rfc.fit(x_train, y_train)
print(f"Accuracy: {rfc.score(x_test, y_test) * 100:.2f}%")

运行结果为 Accuracy: 83.13% ,说明我们根据这些数据,预测房价的准确率已经达到了83.13%.相对来说这不是一个很差的成绩,下面我们通过调节超参数来试试看能不能进一步提高准确率。

调整参数

首先,我们对森林中随机状态 random_state 进行调整,首先在大范围1~200内以步长为5进行搜索,如下图A,可以看到一个波峰的极大值位于20 ~ 35之间,之后对该区间进行小范围搜索,如下图B,确认一个极大值在25处,准确率为85.99%,优于刚才的结果。

随机状态搜索

接下来令随机状态始终为25,对森林中树木的的数量 n_estimators 进行遍历搜索。发现在10 ~ 35范围内有一个峰值,具体来看 n_estimators 的值为24。其峰值与上述基本相同,但运算量减少了\frac{3}{4}

树数量搜索

由于数据普遍相关性较弱,因此使用MAE作为损失函数,经过交叉验证,确定在测试集上,该模型有至少 85% 的准确性。

为什么模型的表现并不令人相当满意?因为在前面的数据清洗和处理中,我们仅仅通过几种简单的方式进行处理,这必然导致重要信息的丢失。各位读者如果有精力对数据进行进一步处理,必定能够得出更高的准确度。

总结

本文通过Kaggle房价预测案例,系统性地展示了机器学习项目的完整流程。我们采用随机森林回归算法作为核心模型,从数据加载、预处理到特征工程层层递进,最终实现了85%以上的预测准确率。

在数据预处理阶段,我们面对现实数据中常见的缺失值问题,运用乐、了随机森林自身进行缺失值预测填充,同时通过合理的阈值设定(如方差过滤0.01、ANOVA检验α=0.1)将特征维度从80维压缩至41维,既保留了关键信息又提升了运算效率。超参数调优环节中,通过常用的双阶段网格搜索手法(先大范围后小范围)使模型性能提升近3个百分点。

事实上,当前结果仍有改进空间,特别是文本型特征的简单序数编码可能损失部分语义信息,建议后续可尝试更精细的特征工程(如目标编码、嵌入表示)或引入梯度提升树等先进算法。全文体现了数据科学项目中权衡计算成本与模型性能的典型思路,为读者提供了可复现的实战范本。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • """1.个性化消息: 将用户的姓名存到一个变量中,并向该用户显示一条消息。显示的消息应非常简单,如“Hello ...
    她即我命阅读 3,313评论 0 5
  • 为了让我有一个更快速、更精彩、更辉煌的成长,我将开始这段刻骨铭心的自我蜕变之旅!从今天开始,我将每天坚持阅...
    李薇帆阅读 1,951评论 0 3
  • 似乎最近一直都在路上,每次出来走的时候感受都会很不一样。 1、感恩一直遇到好心人,很幸运。在路上总是...
    时间里的花Lily阅读 1,403评论 0 2
  • 1、expected an indented block 冒号后面是要写上一定的内容的(新手容易遗忘这一点); 缩...
    庵下桃花仙阅读 546评论 0 1
  • 一、工具箱(多种工具共用一个快捷键的可同时按【Shift】加此快捷键选取)矩形、椭圆选框工具 【M】移动工具 【V...
    墨雅丫阅读 545评论 0 0