很久之前,利用过传统的机器学习方法进行过房价的预测,但那时候的影响因素太少,而且还都是整数形式的,这次咱试一下有几十个影响因素的房价预测。
首先咱看看数据,截图如下:
数据链接:https://pan.baidu.com/s/1kq7l3JQpafxdkupO54Oebw 提取码:ziv2
在整个流程中,我觉得最重要的还是数据的预处理,因为这个数据,我们可以看见很多字符串,我们必须把这些字符串转化为数字,这样才能将数据输入到网络中,数据处理的代码如下:
'''author:nike hu'''
import pandas as pd
import numpy as np
from torch.utils.data import DataLoader
batchsize = 64
def getData(train_file, test_file):
train = pd.read_csv(train_file) # 读取csv文件
test = pd.read_csv(test_file)
# print(train.head()) # 这里是将整个数据输出,包括第一行对数据的命名
all_features = pd.concat((train.loc[:, 'MSSubClass':'SaleCondition'],
test.loc[:, 'MSSubClass':'SaleCondition']))
# train.loc[]这里面相当于是根据第一行的命名标签来截取数据了,值得注意的是,:号两侧截取的时候都要取到,不像NumPy这些
num_feats = all_features.dtypes[all_features.dtypes != 'object'].index
# 找出数据类型不是‘object’的列数,panda的'object'等于Python中的‘str',输出为满足要求的列的命名
# 减去均值,除以方差
all_features[num_feats] = all_features[num_feats].apply(lambda x : (x - x.mean()) / (x.std()))
all_features = pd.get_dummies(all_features, dummy_na=True)
# 这个函数是将矩阵中的字符串编码为数字,利用的是独热编码,使用之后维度一般会扩充
all_features = all_features.fillna(all_features.mean())
# 经过编码之后,可能会出现一些丢失数据na,我们这里将na的数值赋值为整个数据的均值
# 将数据转化为numpy形式
data = np.array(all_features, dtype=np.float32) # 转化为numpy数据
train_data = data[:train.shape[0]]
test_data = data[train.shape[0]:]
# print(train_data.shape)
# print(test_data.shape)
label_train_data = np.array(train.SalePrice, dtype=np.float32)
# 注意数据类型的转化
label_test_data = np.array(test.SalePrice, dtype=np.float32)
# print(label_train_data.shape, label_test_data.shape)
'''下面是将数据处理为迭代模式,正式训练网络的时候一次性提取batchsize个数据到网络'''
trainXLoader = DataLoader(train_data, batch_size=batchsize, shuffle=True)
trainYLoader = DataLoader(label_train_data, batch_size=batchsize, shuffle=True)
testXLoader = DataLoader(test_data, batch_size=batchsize, shuffle=True)
testYLoader = DataLoader(label_test_data, batch_size=batchsize, shuffle=True)
return trainXLoader, trainYLoader, testXLoader, testYLoader
经过以上处理之后,我们就将那些字符串处理为我们想要的数字数据了,接下来,我们可以开始设计网络模型这些了。
整个网络模型很简单,直接几个全连接层就可以,这个大家随意设计,我的设计如下:
'''author:nike hu'''
class HouseModel(nn.Module):
def __init__(self, chin):
super(HouseModel, self).__init__()
self.net = nn.Sequential(
nn.Linear(chin, 100),
nn.ReLU(),
nn.Linear(100, 60),
nn.ReLU(),
nn.Linear(60, 20),
nn.ReLU(),
nn.Linear(20, 1),
)
def forward(self, z):
z = self.net(z)
return z
接下来,我么就要开始模型的训练了,在训练的过程中,我们也可以看看训练的效果。值得注意一下的就是,损失函数的设计,由于预测值和真实值动不动就几十万,所以我们这里先将预测和真实值进行Log处理,然后使用MSEloss,最后再开个根号,整个过程如下:
'''author:nike hu'''
# 构建可视化的操作
vis = visdom.Visdom()
vis.line([[0.], [0.]], [0.], win='test', opts=dict(title='test', legend=['process', 'first']))
def createAndUse():
trainXLoader, trainYLoader, testXLoader, testYLoader = getData('../data/dataset/train.csv', '../data/dataset/test.csv')
netdim = next(iter(trainXLoader)).shape[1] # 获取输入数据的维度,也就是输入节点的个数
net = HouseModel(netdim)
optimer = torch.optim.Adam(net.parameters(),lr=0.001)
crimiter = nn.MSELoss()
for i in range(100):
net.train() # 训练模式
for j in range(10):
for x, y in zip(trainXLoader, trainYLoader):
x = net(x)
loss = torch.sqrt(crimiter(torch.log(x), torch.log(y)))
# 最后的损失函数,是先对输出值进行log,然后mse,再sqrt,不是直接使用mse,那样设计,效果不好,因为最后的输出值都很大
optimer.zero_grad()
loss.backward()
optimer.step()
print('loss->', loss)
net.eval() # 转化为测试模型,不进行求导这些操作
count = 100 * i
for x, y in zip(testXLoader, testYLoader):
x = net(x)
x1 = (count + torch.arange(x.shape[0]))
x1 = x1.view(-1, 1)
for w in range(x.shape[0]): # 将图形画出来,由于每次只能传入一个点的坐标,而testXLoader这些每次迭代会有64个数据出来,所以这里循环处理
vis.line([[x[w].item(), y[w]]], [x1[w]], win='test', opts=dict(title='test'), update='append')
由于预测值和真实值太大,我不可能像预测类别那样来判断准确率,所以我这里就直接进行可视化操作了,直接将最后的预测值和真实值可视化来观察,关于房价预测的所有代码,这篇文章都上传了,大家有兴趣可以去改改参数,比如学习率这些,来看看最后的预测效果,我这边,可能是由于cpu训练次数不够加上数据量其实还是很小的原因,效果差强人意。
2020 4.21