飞桨AI Studio之加州房价预测——机器学习的Hello world




飞桨AI Studio之加州房子预测——机器学习的Hello world

目录

飞桨AI Studio之加州房子预测(上)——机器学习的Hello world

前言

一、比赛分析

二、数据分析及处理

导入库

读取数据

数据预处理

网络定义

RMSE定义

训练函数

超参数定义和训练

生成提交文件

 提交结果

总结





前言

房价预测这个比赛,我个人认为是比MNIST更加适合新手入门的一个项目,而且Kaggle上有现成的比赛,可以通过这个平台去检验自己的一些成果。

这个比赛主要还是锻炼对于数据处理的能力,对于搭建模型没有什么特别大的需求,比较适合新手。

我这里只是使用了飞浆的平台,但比赛还是在Kaggle上比的,具体网址如下。

House Prices - Advanced Regression Techniquesw

我以下的所有代码都是在飞浆的AI Studio中运行的

notebook链接




一、比赛分析

简单的说,比赛提供了有关一些加州房子的信息,其中包括其买入价格,房子的修建情况等79项信息,其中的信息有数值信息也有包含字符的信息,我们需要根据这些信息去预测test数据集中得到房价。

在这个房价预测中,我们的评价指标是RMSE,且数据采用的是log形式(因为房价差距会很大,取log防止因为这个房价的定位影响结果)

 我们需要提交的形式是csv格式下,房子的Id和其销售价格




二、数据分析及处理

我这里因为只是作为一个baseline,主要是对数据做一个可视化和将其简单的处理使其可以训练。

导入库

import pandas as pd
import numpy as np
import paddle
import paddle.nn as nn
import paddle.nn.functional as F
import paddle.optimizer as optimizer
import matplotlib.pyplot as plt

读取数据

# 读取文件
train_path = '/home/aistudio/data/data109189/train.csv'
test_path = '/home/aistudio/data/data109189/test.csv'
train_dataset = pd.read_csv(train_path)
test_dataset = pd.read_csv(test_path)
print(train_dataset.shape)
print(test_dataset.shape)
train_dataset.head()

"""
(1460, 81)
(1459, 80)
"""
 
 
Id MSSubClass MSZoning LotFrontage LotArea Street Alley LotShape LandContour Utilities ... PoolArea PoolQC Fence MiscFeature MiscVal MoSold YrSold SaleType SaleCondition SalePrice
0 1 60 RL 65.0 8450 Pave NaN Reg Lvl AllPub ... 0 NaN NaN NaN 0 2 2008 WD Normal 208500
1 2 20 RL 80.0 9600 Pave NaN Reg Lvl AllPub ... 0 NaN NaN NaN 0 5 2007 WD Normal 181500
2 3 60 RL 68.0 11250 Pave NaN IR1 Lvl AllPub ... 0 NaN NaN NaN 0 9 2008 WD Normal 223500
3 4 70 RL 60.0 9550 Pave NaN IR1 Lvl AllPub ... 0 NaN NaN NaN 0 2 2006 WD Abnorml 140000
4 5 60 RL 84.0 14260 Pave NaN IR1 Lvl AllPub ... 0 NaN NaN NaN 0 12 2008 WD Normal 250000

5 rows × 81 columns

 可以看到数据训练集包含1460个,其中除去ID和saleprice有79个特征。

测试集和1459个,不包含saleprice这一栏故有80列。

其中特征包含数字,文本,还有许多数据丢失。

数据预处理

这里数据的预处理主要是针对将数值数据作归一化,将字符数据做one-hot编码,对缺失数据进行处理和划分训练集和验证集

这里说一下,为什么要把训练集和测试集的特征放到一起处理

一是因为做one-hot编码时,训练集中的字符信息测试集不一定有,不一起处理,会导致总的特征数不一样。

二是因为一起处理更加方便

# 先把训练集的label取出来
train_label = train_dataset['SalePrice']
# 合并一下测试集和训练集的信息
train_test_data = pd.concat([train_dataset.iloc[:, 1:-1], test_dataset.iloc[:, 1:]])
# 获取数值特征
numeric_features = train_test_data.dtypes[train_test_data.dtypes != 'object'].index
# 均一化所有数值特征
train_test_data[numeric_features] = train_test_data[numeric_features].apply(lambda x: (x - x.mean())/ (x.std()) )
# 将所有丢失的数据变为0,因为上一步整个数据已经变为均值是0了,对整体没有影响
train_test_data[numeric_features] = train_test_data[numeric_features].fillna(0)
# 将非数值的特征转为one-hot编码
train_test_data = pd.get_dummies(train_test_data, dummy_na=True)
# 再把训练集和测试集重新划分出来
train_num = train_dataset.shape[0]
train_dataset = train_test_data[: train_num]
test_dataset = train_test_data[train_num: ]
print(train_dataset.shape)
print(test_dataset.shape)
# 简单划分一下训练集和验证集
num_train = round(1460*0.8)
training_data = np.array(train_dataset.iloc[0: num_train])
training_label = np.array(train_label.iloc[0: num_train])
val_data = np.array(train_dataset.iloc[num_train: ])
val_label = np.array(train_label.iloc[num_train: ])

"""
处理完后,特征变成331个
(1460, 331)
(1459, 331)
"""

我这里因为后面训练直接把训练和验证集写在一个函数里的关系,所以要做一下合并

# 增加一个维度方便拼接
training_label = training_label[None]
val_label = val_label[None]
training_data = np.concatenate((training_data, training_label.T), axis=1)
val_data = np.concatenate((val_data, val_label.T), axis=1)
dataloader = {'train':training_data, 'val':val_data}

网络定义

这里简单的使用一个单全连接网络作为一个线性回归模型

class Net(nn.Layer):

    # self代表类的实例自身
    def __init__(self):
        # 初始化父类中的一些参数
        super(Net, self).__init__()
        
        # 定义一层全连接层,输入维度是331,输出维度是1
        self.fc1 = nn.Linear(in_features=331, out_features=1024)
    # 网络的前向计算
    def forward(self, inputs):
        x = self.fc1(inputs)
        return x

RMSE定义

没有现成的定义RMSE,手写一个

def log_rmse(preds, labels):
    # 为了在取对数时进⼀步稳定该值,将⼩于1的值设置为1
    preds = paddle.fluid.layers.clip(preds, 1, float('inf'))
    # 同时取log
    preds = paddle.log(preds)
    labels = paddle.log(labels)
    # 做MSE
    loss = F.square_error_cost(preds, label=labels)
    loss = loss.mean()
    # 取sqrt
    rmse = paddle.sqrt(loss)
    return rmse.item()

训练函数

比较普通的训练函数,把训练和验证放的一起了

def train_model(modle, epoch_num, dataloader, batch_size, opt):
    # 记录最小的损失
    min_loss = 0
    # 用来画图,分别记录
    total_loss = {"train": [], "val": []}
    for epoch in range(epoch_num):
        # 输出格式
        print('Epoch {}/{}'.format(epoch, epoch_num - 1))
        print('-' * 10)
        # 选择训练模式或者测试模式
        for phase in ['train', 'val']:
            if phase == 'train':
                model.train()  # Set model to training mode
            else:
                model.eval()  # Set model to evaluate mode
            # 随机打乱对于数据
            np.random.shuffle(dataloader[phase])
            mini_batches = [dataloader[phase][k:k+batch_size] for k in range(0, len(dataloader[phase]), batch_size)]
            running_loss = 0
            for index, mini_batch in enumerate(mini_batches):
                mini_batch = np.array(mini_batch).astype('float32')
                x = mini_batch[:, :-1]
                y = mini_batch[:, -1:]
                
                # 转换成paddle格式
                x = paddle.to_tensor(x)
                y = paddle.to_tensor(y)
                # 前行传播
                preds = modle(x)
                # 计算损失
                loss = F.square_error_cost(preds, label=y)
                avg_loss = loss.mean()
                train_ls = log_rmse(preds, y)
                running_loss += train_ls
                
                # 反向传播
                avg_loss.backward()
                # 梯度更新
                opt.step()
                # 梯度请0
                opt.clear_grad()
            running_loss = running_loss / index
            total_loss[phase].append(running_loss)
            print('{} Loss: {:.4f}'.format(phase, running_loss))
    
    plt.figure()
    epoch = np.arange(epoch_num)
    for phase in ['train', 'val']:
       plt.plot(epoch, total_loss[phase], label=phase)
    plt.show()

超参数定义和训练

# 定义超参数
model = Net()
model.train()
loss = nn.MSELoss()
opt = optimizer.SGD(learning_rate=0.01, parameters=model.parameters())
epoch_num = 100
batch_size = 64

train_model(model, epoch_num , dataloader, batch_size , opt)

生成提交文件

# 提交测试集预测和生产kaggle提交文件csv
test_dataset = np.array(test_dataset).astype('float32')
test_dataset = paddle.to_tensor(test_dataset)
test_preds = model(test_dataset).detach().numpy()
test_sub = {'Id':None, 'SalePrice':None}
test_sub['SalePrice'] = pd.Series(test_preds.reshape(1, -1)[0])
test_sub['SalePrice'].to_csv('submission.csv')

这里生成完之后,将表头改为Id和SalePrices,Id序号要求从1461开始,自己修改一下

 提交结果

将csv文件提交

最后得分是0.15877,作为一个baseline来说还行了




总结

后面根据前人大佬的一些数据处理来做一些改进

猜你喜欢

转载自blog.csdn.net/lzzzzzzm/article/details/120408416