BP神经网络做房价预测-Top20%
- 一.数据预处理
- 1.数据清洗(Data Cleaning)
- 2.特征工程(Feature Engineering)
- 3.PCA降维
- 二.网络搭建
- 三.结果展示
- 四.总结
一直在学习图像分类任务,预测方面较为薄弱。本次参考网上的数据预处理代码,用BP神经网络整合实现预测,特此记录。
比赛页面:https://www.kaggle.com/c/house-prices-advanced-regression-techniques数据预处理:完整代码整合:https://github.com/wulewule/neural/blob/master/house.py
一.数据预处理
1.数据清洗(Data Cleaning)
清洗掉NaN值,用其他的值补全,补全方式如下:
① 用0填补:特征为数字,0有实际意义时。如地下室面积,0代表没有地下室。
② 用None填补:特征为抽象,None有实际意义时。如游泳池质量,None代表没有游泳池。
③ 用众数填补:特征为分类,如住宅类型(学区房)等。
④ 剩余特征视具体情况而定,如根据其他特征进行分组后,用中位数填补。
# 用None填补,特征多为抽象
N_list = ["PoolQC" , "MiscFeature", "Alley", "Fence", "FireplaceQu", "GarageQual", "GarageCond", "GarageFinish",
"GarageYrBlt", "GarageType","BsmtExposure", "BsmtCond", "BsmtQual", "BsmtFinType2", "BsmtFinType1", "MasVnrType"]
for n in N_list:
data[n].fillna("None", inplace=True)
# 用0填补,特征多为数字
Z_list=["MasVnrArea", "BsmtUnfSF", "TotalBsmtSF", "GarageCars", "BsmtFinSF2", "BsmtFinSF1", "GarageArea"]
for z in Z_list:
data[z].fillna(0, inplace=True)
# 其它特征用众数填补,[0]为取多个众数的第一个
M_list = ["MSZoning", "BsmtHalfBath", "BsmtFullBath", "Utilities", "Functional", "Electrical", "KitchenQual", "SaleType","Exterior1st", "Exterior2nd"]
for m in M_list:
data[m].fillna(data[m].mode()[0], inplace=True)
# 按照数字大小,十个为一组将特征划分,目的是防止下面取中位数后仍为NaN
data['LotAreaCut'] = pd.qcut(data['LotArea'], 10)
# 按照LotAreaCut和Neighborhood分组后的中位数进行填补NaN
data['LotFrontage'] = data.groupby(['LotAreaCut', 'Neighborhood'])['LotFrontage'].transform(lambda x: x.fillna(x.median()))
data['LotFrontage'] = data.groupby(['LotAreaCut'])['LotFrontage'].transform(lambda x:x.fillna(x.median()))
2.特征工程(Feature Engineering)
从原始数据中提取特征以供使用:
① 将一部分离散型的数字特征转为字符串,筛选条件是该特征数值无具体数量含义,或与房价没有很强烈的正相关关系。
② 手动分类不适合用LabelEncoder处理的特征,比如特征的每个值对应的价格平均数相差很大,则适合手动分类。
③ LabelEncoder处理特征,比如年份,越老的房子相对越便宜,价格曲线波动较小。
④ 特征组合,先用Lasso进行特征筛选,选出较重要的特征,再进行组合测试
(②③④可以根据最终效果进行更改)
⑤ 数据平滑处理,增添虚拟变量。平滑处理指的是使特征的峰度更符合高斯分布,虚拟变量指的是通过get_dummies使所有特征转为数值变量,也就是one-hot特征。
# 将一部分离散的数字特征转为字符串
NumStr = ["MSSubClass", "BsmtFullBath", "BsmtHalfBath", "HalfBath", "BedroomAbvGr", "KitchenAbvGr", "MoSold",
"YrSold", "YearBuilt", "YearRemodAdd", "LowQualFinSF", "GarageYrBlt"]
for col in NumStr:
data[col] = data[col].astype(str)
# 手动分类,用于处理不能直接用LabelEncoder的特征
data.groupby(['MSSubClass'])[['SalePrice']].agg(['mean','median','count'])
data = map_values(data)
print('after map_value:', data.shape)
# 删掉无作用的两个特征
data.drop("LotAreaCut", axis=1, inplace=True)
data.drop(['SalePrice'], axis=1, inplace=True)
# 封装处理步骤,并处理数据 (1.标准化标签 2.数据平滑处理,增填虚拟变量)
pipe = Pipeline([('labenc', labelenc()), ('skew_dummies', skew_dummies(skew=1))])
copy = data.copy()
data_pipe = pipe.fit_transform(copy)
print('after pipe1:', data_pipe.shape)
3.PCA降维
在以前的博客已有介绍,这里的主要目的是,降低新增加的特征和以前特征的相关性,而不是降维加快运算。(基于Tensorflow实现BP神经网络+PCA降维)
二.网络搭建
网络整体结构和跑MNIST时大致相同,改动如下:
1. 去掉了dropout,选择L2正则化。
2. loss函数选择了均方根误差来进行训练。
训练代码如下:
# 网络搭建
input_size = 410
num_classes = 1
weight_decay = 0.05 # 权重衰减系数
hidden_units_size = 2*input_size + 1
X = tf.placeholder(tf.float32, shape = [None, input_size], name='x')
Y = tf.placeholder(tf.float32, shape = [None, num_classes], name='y')
W1 = tf.get_variable("W1", shape=[input_size, hidden_units_size], initializer=tf.contrib.layers.xavier_initializer())
B1 = tf.Variable(tf.constant (0.1), [hidden_units_size])
W2 = tf.get_variable("W2", shape=[hidden_units_size, num_classes], initializer=tf.contrib.layers.xavier_initializer())
B2 = tf.Variable(tf.constant (0.1), [num_classes])
hidden_opt = tf.matmul(X, W1) + B1 # 输入层到隐藏层正向传播
hidden_opt = tf.nn.relu(hidden_opt) # 激活函数,用于计算节点输出值
final_opt = tf.matmul(hidden_opt, W2) + B2 # 隐藏层到输出层正向传播
tf.add_to_collection('pred_network', final_opt)
loss = tf.reduce_mean(tf.losses.mean_squared_error(Y, final_opt))
l2_loss = weight_decay * tf.add_n([tf.nn.l2_loss(tf.cast(v, tf.float32)) for v in tf.trainable_variables()])
tf.summary.scalar('l2_loss', l2_loss)
loss = loss + l2_loss
train_step = tf.train.AdamOptimizer(learning_rate=0.01).minimize(loss)
sess = tf.Session()
sess.run(tf.global_variables_initializer())
NEPOCH = 10000
for i in range(NEPOCH):
train_loss = sess.run([final_opt, train_step], feed_dict={X: train_x_data, Y: train_y_data, keep_prob:0.1})
test_loss = sess.run(final_opt, feed_dict={X: test_x_data, keep_prob:1})
if i%100 == 0:
print('step:', i, 'train_mean_squared_error:', mean_squared_error(train_loss[0], train_y_data), 'test_mean_squared_error:', mean_squared_error(test_loss, test_y_data))
if mean_squared_error(test_loss, test_y_data) < 0.01:
break
三.结果展示
数据shape变化:
训练结果:
kaggle提交评分:
四.总结
大部分学习遇到的困难不是网络搭建,而是数据处理。好的数据代表着好的结果,这需要大量的经验和耐心,这次是预测任务的一个开端,接下来会尝试自己处理数据集来训练,来日方长,加油。