实战python:Kaggle房价预测 + 课程竞赛:加州2020年房价预测

实战:Kaggle房价预测 + 课程竞赛:加州2020年房价预测

实战Kaggle比赛:预测房价

直接使用即可

import hashlib
import os
import tarfile
import zipfile
import requests
import numpy as np
import pandas as pd
import torch
from torch import nn
from d2l import torch as d2l
import matplotlib.pyplot as plt
# @save
DATA_HUB = dict()
DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/'


def download(name, cache_dir=os.path.join('..', 'data')):  # @save
    """下载一个DATA_HUB中的文件,返回本地文件名"""
    assert name in DATA_HUB, f"{name} 不存在于 {DATA_HUB}"
    url, sha1_hash = DATA_HUB[name]
    os.makedirs(cache_dir, exist_ok=True)
    fname = os.path.join(cache_dir, url.split('/')[-1])
    if os.path.exists(fname):
        sha1 = hashlib.sha1()
        with open(fname, 'rb') as f:
            while True:
                data = f.read(1048576)
                if not data:
                    break
                sha1.update(data)
        if sha1.hexdigest() == sha1_hash:
            return fname  # 命中缓存
    print(f'正在从{url}下载{fname}...')
    r = requests.get(url, stream=True, verify=True)
    with open(fname, 'wb') as f:
        f.write(r.content)
    return fname


def download_extract(name, folder=None):  # @save
    """下载并解压zip/tar文件"""
    fname = download(name)
    base_dir = os.path.dirname(fname)
    data_dir, ext = os.path.splitext(fname)
    if ext == '.zip':
        fp = zipfile.ZipFile(fname, 'r')
    elif ext in ('.tar', '.gz'):
        fp = tarfile.open(fname, 'r')
    else:
        assert False, '只有zip/tar文件可以被解压缩'
    fp.extractall(base_dir)
    return os.path.join(base_dir, folder) if folder else data_dir


def download_all():  # @save
    """下载DATA_HUB中的所有文件"""
    for name in DATA_HUB:
        download(name)

# 下载数据集
DATA_HUB['kaggle_house_train'] = (  # @save
    DATA_URL + 'kaggle_house_pred_train.csv',
    '585e9cc93e70b39160e7921475f9bcd7d31219ce')

DATA_HUB['kaggle_house_test'] = (  # @save
    DATA_URL + 'kaggle_house_pred_test.csv',
    'fa19780a7b011d9b009e8bff8e99922a8ee2eb90')

#假设解压后的数据位于../../data/kaggle_house/目录,它包括两个csv文件。下面使用pandas读取这两个文件。
# 读取数据集
train_data = pd.read_csv(download('kaggle_house_train'))
test_data = pd.read_csv(download('kaggle_house_test'))

#训练数据集包括1460个样本、80个特征和1个标签。
print(train_data.shape)
print(test_data.shape)
print(train_data.iloc[0:4, [0, 1, 2, 3, -3, -2, -1]])# 打印数据集中前4行的正数4列和倒数3列
#可以看到第一个特征是Id,它能帮助模型记住每个训练样本,但难以推广到测试样本,所以我们不使用它来训练。我们将所有的训练数据和测试数据的79个特征按样本连结。
# 第一列特征是ID,不携带任何用于预测的信息,删除样本中的第一列和最后一列(SalePrice)删掉id和label
all_features = pd.concat((train_data.iloc[:, 1:-1], test_data.iloc[:, 1:]))
print(all_features.shape)

# 找出那些是数值的列
numeric_features = all_features.dtypes[all_features.dtypes != 'object'].index
print(numeric_features)

'''
(1460, 81)
(1459, 80)
   Id  MSSubClass MSZoning  LotFrontage SaleType SaleCondition  SalePrice
0   1          60       RL         65.0       WD        Normal     208500
1   2          20       RL         80.0       WD        Normal     181500
2   3          60       RL         68.0       WD        Normal     223500
3   4          70       RL         60.0       WD       Abnorml     140000
(2919, 79)
Index(['MSSubClass', 'LotFrontage', 'LotArea', 'OverallQual', 'OverallCond',
       'YearBuilt', 'YearRemodAdd', 'MasVnrArea', 'BsmtFinSF1', 'BsmtFinSF2',
       'BsmtUnfSF', 'TotalBsmtSF', '1stFlrSF', '2ndFlrSF', 'LowQualFinSF',
       'GrLivArea', 'BsmtFullBath', 'BsmtHalfBath', 'FullBath', 'HalfBath',
       'BedroomAbvGr', 'KitchenAbvGr', 'TotRmsAbvGrd', 'Fireplaces',
       'GarageYrBlt', 'GarageCars', 'GarageArea', 'WoodDeckSF', 'OpenPorchSF',
       'EnclosedPorch', '3SsnPorch', 'ScreenPorch', 'PoolArea', 'MiscVal',
       'MoSold', 'YrSold'],
      dtype='object')

'''

# 若无法获得测试数据,则可根据训练数据计算均值和标准差
all_features[numeric_features] = all_features[numeric_features].apply(
    lambda x: (x - x.mean()) / (x.std()))
# 在标准化数据之后,所有均值消失,因此我们可以将缺失值设置为0
#将所有缺失的值替换为相应特征的平均值,通过将特征重新缩放到零均值和单位方差来标准化数据。
all_features[numeric_features] = all_features[numeric_features].fillna(0)
# “Dummy_na=True”将“na”(缺失值)视为有效的特征值,并为其创建指示符特征
all_features = pd.get_dummies(all_features, dummy_na=True)
print(all_features.shape)#(2919,330)可以看到此转换会将特征的总数量从79个增加到331个
#处理离散值。 这包括诸如“MSZoning”之类的特征。 我们用独热编码替换它们, 方法与前面将多类别标签转换为向量的方式相同 。
#例如,“MSZoning”包含值“RL”和“Rm”。 我们将创建两个新的指示器特征“MSZoning_RL”和“MSZoning_RM”,其值为0或1。 根据独热编码,如果“MSZoning”的原始值为“RL”, 则:“MSZoning_RL”为1,“MSZoning_RM”为0。 pandas软件包会自动为我们实现这一点。
#使用pandas中的get_dummies()函数将所有非数值类型的特征转换为指示符变量.这个函数会对all_features中的所有非数值类型的特征进行转换,具体来说,就是为每一个非数值类型的特征创建多个新的特征,每个新特征都是一个二元变量,表示原始特征的一个取值是否出现。如果一个样本在原始特征中的取值是该新特征对应的取值,那么新特征就是1,否则是0。

n_train = train_data.shape[0]
train_features = torch.tensor(all_features[:n_train].astype('float32').values, dtype=torch.float32)
test_features = torch.tensor(all_features[n_train:].astype('float32').values, dtype=torch.float32)
train_labels = torch.tensor(
    train_data.SalePrice.values.reshape(-1, 1), dtype=torch.float32)
#将训练数据和测试数据分别转换成了pytorch张量。 首先,使用shape属性获取train_data中样本数量,赋值给n_train。 接着,使用torch.tensor()函数将all_features中的前n_train行转换成了train_features张量,将all_features中剩下的行转换成了test_features张量。 最后,使用torch.tensor()函数将train_data中SalePrice列对应的值转换成了train_labels张量。 .values 属性用来得到pandas DataFrame 和 Series 的数组表示。reshape(-1, 1) 用来将一维的标签转化为二维的张量,这样才能在之后的模型中使用

loss = nn.MSELoss()
in_features = train_features.shape[1]
#定义了一个线性回归模型,使用了均方误差损失函数,并设置了输入特征的数量,以便在之后的训练中使用。
def get_net():
    #net = nn.Sequential(nn.Linear(in_features,1))
    net=nn.Sequential(nn.Linear(in_features,50),nn.ReLU(),nn.Linear(50,1))
    #dropout1, dropout2 = 0.2, 0.5
    #net = nn.Sequential(nn.Flatten(), nn.Linear(in_features, 50), nn.ReLU(),
                        # 在第一个全连接层之后添加一个dropout层
                       # nn.Dropout(dropout1), nn.Linear(50, 50), nn.ReLU(),
                        # 在第二个全连接层之后添加一个dropout层
                       # nn.Dropout(dropout2), nn.Linear(50, 1))
    return net

#net = nn.Sequential(nn.Flatten(), nn.Linear(784, 256), nn.ReLU(),#3D的需要展平flatten后,线性层+relu
                    #nn.Linear(256, 10))


def log_rmse(net, features, labels):
    # 为了在取对数时进一步稳定该值,将小于1的值设置为1,数值太大就用log
    clipped_preds = torch.clamp(net(features), 1, float('inf'))
    rmse = torch.sqrt(loss(torch.log(clipped_preds),
                           torch.log(labels)))
    return rmse.item()

#使用torch.optim.Adam()函数定义了优化器optimizer,该优化器使用Adam算法,学习率为learning_rate,权重衰减系数为weight_decay。
def train(net, train_features, train_labels, test_features, test_labels,
          num_epochs, learning_rate, weight_decay, batch_size):
    train_ls, test_ls = [], []
    train_iter = d2l.load_array((train_features, train_labels), batch_size) # 加载训练数据
    # 这里使用的是Adam优化算法,类似于sgd
    optimizer = torch.optim.Adam(net.parameters(),
                                 lr = learning_rate,
                                 weight_decay = weight_decay)
    for epoch in range(num_epochs):
        for X, y in train_iter:#标准模块
            optimizer.zero_grad()
            l = loss(net(X), y)
            l.backward()
            optimizer.step()
        train_ls.append(log_rmse(net, train_features, train_labels))
        if test_labels is not None:
            test_ls.append(log_rmse(net, test_features, test_labels))
    return train_ls, test_ls

#K折交叉验证, 它有助于模型选择和超参数调整。 我们首先需要定义一个函数,在K折交叉验证过程中返回第i折的数据。
# 具体地说,它选择第个切片作为验证数据,其余部分作为训练数据。 注意,这并不是处理数据的最有效方法,如果我们的数据集大得多,会有其他解决办法。
def get_k_fold_data(k, i, X, y):
    assert k > 1
    fold_size = X.shape[0] // k
    X_train, y_train = None, None
    for j in range(k):
        idx = slice(j * fold_size, (j + 1) * fold_size)
        X_part, y_part = X[idx, :], y[idx]
        if j == i:#作为验证集,其他k-1测试集
            X_valid, y_valid = X_part, y_part
        elif X_train is None:
            X_train, y_train = X_part, y_part
        else:
            X_train = torch.cat([X_train, X_part], 0)
            y_train = torch.cat([y_train, y_part], 0)
    return X_train, y_train, X_valid, y_valid#返回训练集和验证集


#在K折交叉验证中训练K次后,返回训练和验证误差的平均值
def k_fold(k, X_train, y_train, num_epochs, learning_rate, weight_decay,
           batch_size):
    train_l_sum, valid_l_sum = 0, 0  # 用于存储训练误差和验证误差的总和
    for i in range(k):
        data = get_k_fold_data(k, i, X_train, y_train)
        net = get_net() #选择模型
        train_ls, valid_ls = train(net, *data, num_epochs, learning_rate,
                                   weight_decay, batch_size) # 训练模型
        train_l_sum += train_ls[-1] # 将当前训练误差的最后一个值累加到train_l_sum变量中
        valid_l_sum += valid_ls[-1]
        if i == 0: # 第一次循环
            d2l.plot(list(range(1, num_epochs + 1)), [train_ls, valid_ls],
                     xlabel='epoch', ylabel='rmse', xlim=[1, num_epochs],
                     legend=['train', 'valid'], yscale='log')
        print(f'折{i + 1},训练log rmse{float(train_ls[-1]):f}, '
              f'验证log rmse{float(valid_ls[-1]):f}')
    return train_l_sum / k, valid_l_sum / k

k, num_epochs, lr, weight_decay, batch_size = 5, 100, 5, 4, 64
#train_l, valid_l = k_fold(k, train_features, train_labels, num_epochs, lr, weight_decay, batch_size)
#print('%d-fold validation: avg train rmse %f, avg valid rmse %f' % (k, train_l, valid_l))
#plt.show()
'''
折1,训练log rmse0.169707, 验证log rmse0.156568
折2,训练log rmse0.162140, 验证log rmse0.190571
折3,训练log rmse0.163900, 验证log rmse0.168265
折4,训练log rmse0.168909, 验证log rmse0.154888
折5,训练log rmse0.163091, 验证log rmse0.182573
5-fold validation: avg train rmse 0.165549, avg valid rmse 0.170573
10-fold validation: avg train rmse 0.164510, avg valid rmse 0.167979
开始炼丹
MLP:5-fold validation: avg train rmse 0.105464, avg valid rmse 0.164013,改变net=nn.Sequential(nn.Linear(in_features,50),nn.ReLU(),nn.Linear(50,1)),训练集比验证集好
avg valid rmse=0.14多
1000epoch会慢很多,注重看验证集结果。5-fold validation: avg train rmse 0.086880, avg valid rmse 0.170675
加一个dropout 5-fold validation: avg train rmse 0.937441, avg valid rmse 1.022778

linear-relu-linear比较好l=5,wd=4,5-fold validation: avg train rmse 0.124102, avg valid rmse 0.144513


'''

def train_and_pred(train_features, test_features, train_labels, test_data,
                   num_epochs, lr, weight_decay, batch_size):
    net = get_net()
    train_ls, _ = train(net, train_features, train_labels, None, None,
                        num_epochs, lr, weight_decay, batch_size)
    d2l.plot(np.arange(1, num_epochs + 1), [train_ls], xlabel='epoch',
             ylabel='log rmse', xlim=[1, num_epochs], yscale='log')
    print(f'训练log rmse:{float(train_ls[-1]):f}')
    # 将网络应用于测试集。
    preds = net(test_features).detach().numpy()
    # 将其重新格式化以导出到Kaggle
    test_data['SalePrice'] = pd.Series(preds.reshape(1, -1)[0])
    submission = pd.concat([test_data['Id'], test_data['SalePrice']], axis=1)
    submission.to_csv('submission.csv', index=False)


train_and_pred(train_features, test_features, train_labels, test_data, 100, 5, 4, 64)#训练log rmse:0.086266还行吧
plt.show()

在这个网址提交csv文件https://www.kaggle.com/competitions/house-prices-advanced-regression-techniques/submissions#
Score:0.13642,好像很低
用其他博主几年前的也有0.4得分

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Bug

d2l.plot(np.arange(1, num_epochs + 1), [train_ls], xlabel='epoch',
             ylabel='log rmse', xlim=[1, num_epochs], yscale='log')

这是正确画图形式,不使用semilog

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值