基于Keras的深圳租房建模
本文是针对深圳租房数据的第3次分析,文章主要内容:
导入库
1 | import numpy as np |
数据基本信息
读取数据
1 | df = pd.read_excel("leyoujia.xls") |
形状shape
In [3]:
1 |
|
Out[3]:
1 | (2020, 12) |
字段类型
In [4]:
1 |
|
Out[4]:
大部分都是字符串类型,只有money,就是最终的预测值是数值型
1 | name object |
In [5]:
1 |
|
Out[5]:
1 | name 0 |
缺失值处理
找出缺失值
在time字段中存在缺失值,找出缺失值所在的行数据信息:
填充缺失值
缺失值的填充有多种方法:
- 填充具体值
- 填充现有数据的某个统计值,比如均值、众数等
- 填充前后项的值等
本文直接在网上找到每个小区的具体时间进行填充:
In [7]:
1 | # 2019 2003 2004 2019 2019 2020 |
In [8]:
1 | df.isnull().sum() |
Out[8]:
现在没有缺失值:
1 | name 0 |
预处理
针对不同的字段做预处理工作
name
小区的姓名name直接删除
In [9]:
1 | df.drop("name",axis=1,inplace=True) |
layout
layout分成3个具体的属性:室、厅、卫;当layout为"商铺"时,直接删除!!!
In [10]:
1 | df[df["layout"] == "商铺"] |
提取出几室几厅几卫:
1 | df1 = df["layout"].str.extract(r'(?P<shi>\d)室(?P<ting>\d)厅(?P<wei>\d)卫') |
1 | # 合并到原数据 |
删除3个字段中的空值:
1 | # 基于3个字段删除空值 |
location
统计每个location下面出现的数量:
In [14]:
1 | df["location"].value_counts() |
Out[14]:
1 | 朝南 552 |
In [15]:
不同朝向下租金的价格分布:
1 | fig = px.violin(df,y="money",color="location") |
按照常识,房子在南北朝向的时候价格比东西朝向会更贵。在这里我们取每个朝向下的最大值:
1 | # 不同朝向下的均值: |
注意:然后根据朝向的索引号大小进行编码:朝东西-0;朝东北-1;朝南北-9
这点是和第二次分析中不同的点。第二次分析根据价格分布的小提琴图自定义的编码顺序
1 | # 第二弹中编码自定义: location = ["朝东西","朝东北","朝西","朝西北","朝东","朝西南","朝东南","朝南","朝北","朝南北"] |
size和sizeInside
建筑面积和套内面积的处理
In [18]:
1 | df.dtypes |
Out[18]:
1 | shi object |
In [19]:
1 | # 1、通过切割的方式来提取 |
1 | # 2、使用正则的方式提取 |
zhuangxiu
装修方式的不同通过自定义的硬编码。
In [21]:
1 | df["zhuangxiu"].value_counts() |
Out[21]:
1 | 精装 1172 |
主观意义上的思路:毛坯的等级最低,豪装最高,所以在这里直接采用自定义的硬编码方式:
In [22]:
1 | # 硬编码 |
Out[22]:
1 | {'毛坯': 1, '普装': 2, '精装': 3, '豪装': 4} |
In [23]:
1 | df["zhuangxiu"] = df["zhuangxiu"].map(zhuangxiu) |
numberFloor
中低高楼层和价格money之间的关系
In [24]:
1 | # 提取中低高楼层 |
1 | # 中低高楼层和价格money之间的关系 |
采用独热码的形式进行编码:通过get_dummies实现
1 | # 中高低楼层采用独热码的形式 |
time
小区房子的建成时间处理:
In [30]:
1 | df["time"].value_counts() |
从原数据中提取出具体的年份信息:
转成数值型,并且求出和2022年的时间间隔:
1 | # time转成数值型 |
zone+position
行政区域和具体的位置对价格的影响
In [33]:
1 | df["zone"].value_counts() |
Out[33]:
1 | 龙岗 548 |
In [34]:
1 | fig = px.violin(df,y="money",color="zone") |
将zone和position进行合并,并且计算每个具体位置下的价格均值,根据均值的大小进行编码:
1 | df["zone_position"] = df["zone"] + "_" + df["position"] |
way
不同的租房方式,在这里只提取两个信息:整租与合租
In [39]:
1 | fig = px.violin(df,y="money",color="way") |
不同租房方式对价格的影响:很明显押一付一和押二付一是最常见的方式
提取整租和合租,并实施类型编码:
1 | # 整租-合租 |
我们发现:绝大部分的都是整租,极少数是合租。这两类样本是不均衡的,后面会实施采样处理。
采样处理
上面提到整租与合租的样本数是极不均衡的,在这里实施上采样,增加合租的数量,保证二者相同:
采样前:
采样后:
类型转换
上面对不同字段进行了预处理和编码,发现有些字段的类型需要转换:
1 | col1 = ["shi","ting","wei","time"] |
全部变成了数值型:
建模
下面是对数据smoted_df数据进行建模:
特征和标签
1 | X = smoted_df.drop("money",axis=1) # 特征 |
切分数据
1 | from sklearn.model_selection import train_test_split |
数据标准化
1 | mean = X_train.mean(axis=0) |
一般神经网路中的数据都要求比较小,在这里对同因变量统一转成以万为单位的数据:
In [58]:
1 | y_train = y_train / 10000 |
Out[58]:
1 | 3201 0.6000 |
构建网络
训练集的样本总共是3000+,我们在这里采用了一个很小的网络,只有两个隐藏层,每层64个单元。
网络的最后一层只有一个单元,没有其他激活函数,是一个纯粹的线性层。
In [59]:
1 | model = models.Sequential() |
编译网络
在这个建模中,损失函数loss采用的是mse:均方误差mean squared error,预测值和目标实际值的差的平方,$(y_{pred} - y_{test})^2$
监控的指标为mae:平均绝对误差mean absolute error,表示的是预测值和目标实际值之差的绝对值,$|y_{pred}- y_{test}|$
In [60]:
1 | model.compile(optimizer="rmsprop", # 优化器 |
网络架构
查看整个网络的基本架构
In [61]:
训练网络
引入5折交叉验证:从训练数据集中划分出一定的验证数据集
In [62]:
1 | k = 5 |
网络指标
1 | # 每个轮次中的平均值 |
loss和mae的整体均值如下:
1 | # 整体均值 |
模型评估
通过evaluate函数对模型进行评估,传入测试集中的数据:
In [65]:
1 | model.evaluate(X_test, y_test) |
Out[65]:
1 | [0.06960742175579071, 0.12945905327796936] |
可以看到loss的取值0.0696,mae取值约为0.1295,表示预测值和实际值之间相差0.1295万元,大约1295元
loss-mae可视化
1 | # 损失绘图 |
引入正则
主要是两个方面:添加L1或者L2正则项(本文采用)、添加Dropout层和实施早停EarlyStopping策略。
1 | mae的均值: 0.11811128027737142 |
新生成的loss-mae可视化:
1 | # 损失绘图 |
重新预测
In [69]:
1 | model.evaluate(X_test, y_test) |
Out[69]:
1 | [0.06338492780923843, 0.110066257417202] |
在引入正则项之后,模型得到优化:loss和mae都有一定的下降。mae变成了0.11,预测值和真实值相差约1100元。
小结
本文从一份网络爬取的租房数据出发,从数据基本信息探索、缺失值处理、特征工程、样本不均衡处理、基于Keras的深度学习模型搭建以及优化等多个步骤进行建模分析,完成了对租房数据价格的预测分析,并且最终的误差控制在了1100元左右。
总结
在写3篇文章的总结之前先贴上爬虫的代码,只需要修改请求头即可运行:
1、爬虫代码
下面提供两种不同方式的租房数据爬虫源码:
1 | import pandas as pd |
1、基于xpath的爬虫代码(全网爬取100页)
笔者近期再次调试可以直接运行
2、基于正则解析的爬取(单页爬取并解析)
1 | import pandas as pd |
解析不同的字段
其他字段的正则解析表达式:
总结
从2020年11月发表第一篇深圳租房数据分析的文章,到这篇基于深度模型Keras的建模分析和预测,在此谈谈3篇文章的特点:
1、第一篇
写于2020年11月,笔者作为一名数据分析师,学习了Python、SQL、爬虫、可视化以及部分机器学习的常见算法和模型,所以在第一篇文章中的重点是:统计与可视化分析。看过文章的小伙伴应该都知道,里面出现了许多还算漂亮的可视化图表(下面是部分图)。
一图胜千言,从统计和可视化图表的角度能够很快速且直观地看到数据分布和变化趋势。文章中使用可视化库是Plotly,一个非常棒的动态可视化库,强烈推荐学习~
2、第二篇
写于2022年3月,笔者仍然是一名数据分析师。从2020年末到2022年初,一年左右的时间,笔者接触和学习了更多的是机器学习的算法、特征工程以及模型的可解释性等知识点。
在这篇文章中,笔者花费了大量的工作来做10个字段的预处理和特征工程工作,重点是如何做编码工作,便于后续输入到我们的回归模型中。
最后,笔者对模型的可解释性进行了探索,主要基于目前一个流行的可解释库:SHAP。
SHAP将所有的特征都视为“贡献者”。 对于每个预测样本,模型都产生一个对应的预测值,SHAP value就是该样本中每个特征所分配到的数值
关于特征工程的学习,笔者推荐一本书:《特征工程入门与实践》。
3、第三篇
写第3篇(也就是看到的这篇)的时候,笔者依旧是一名数据分析师。今年的学习重点转移到了深度学习和kaggle比赛,最近一段时间学习一些DL的基础和Keras框架对于分类和回归问题的建模,从网络模型搭建、编译、训练网络等步骤完成整个建模过程。