当前位置: 首页 > news >正文

李沐 《动手学深度学习》 | 实战Kaggle比赛:预测房价

文章目录

    • 1.下载和缓存数据集
    • 2.数据预处理
      • 读取样本
      • 预处理样本
        • 数值型特征处理
        • 特征标准化的好处
        • 离散值处理
        • 转换为张量表示
      • 训练
      • K折交叉验证
      • 模型选择
      • 最终模型确认及结果预测
      • 代码总结
      • 提交到Kaggle

房价预测比赛链接:https://www.kaggle.com/c/house-prices-advanced-regression-techniques

1.下载和缓存数据集

我们建立字典DATA_HUB, 它可以将数据集名称的字符串映射到数据集相关的二元组上, 这个二元组包含数据集的url和验证文件完整性的sha-1密钥。 所有类似的数据集都托管在地址为DATA_URL的站点上(这里只托管了小一点的数据集,大的数据集还是需要去官网下载)。

import hashlib # Python的哈希库,用于文件校验(计算SHA1、MD5等哈希值)
import os # 操作系统接口库 用于处理文件路径、目录创建等操作
import tarfile # 导入tar文件处理库 用于解压缩.tar、.tar.gz、.tar.bz2等归档文件
import zipfile # 导入zip文件处理库 用于解压缩.zip格式的压缩文件
import requests # 导入HTTP请求库 提供简洁的API用于网络文件下载DATA_HUB = dict()
DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/'

一般情况需要自己去Kaggle将数据集下载到本地目录。这里使用一个download函数用来下载数据集, 将数据集缓存在本地目录(默认情况下为../data)中, 并返回下载文件的名称。 如果缓存目录中已经存在此数据集文件,并且其sha-1与存储在DATA_HUB中的相匹配, 我们将使用缓存的文件,以避免重复的下载。

def download(name, cache_dir=os.path.join('..', 'data')):"""下载一个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:breaksha1.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

竞赛数据分为训练集和测试集。 每条记录都包括房屋的属性和属性值,如街道类型、施工年份、屋顶类型、地下室状况等, 这些特征由各种数据类型组成。 例如,建筑年份由整数表示,屋顶类型由离散类别表示,其他特征由浮点数表示。 这就是现实让事情变得复杂的地方:例如,一些数据完全丢失了,缺失值被简单地标记为“NA”。

每套房子的价格只出现在训练集中,我们将划分训练集以创建验证集,但是在将预测结果上传到Kaggle之后, 我们只能在官方测试集中评估我们的模型。

训练集拆分 训练集+验证集 , 测试集用于评估模型。

方法1:Kaggle 比赛网站中下载数据

加入比赛后,可以在Data下载数据集。

方法2:代码远程下载

使用上面定义的脚本下载并缓存Kaggle房屋数据集。

import numpy as np
import pandas as pd
import torch
from torch import nn
from d2l import torch as d2lDATA_HUB['kaggle_house_train'] = (  DATA_URL + 'kaggle_house_pred_train.csv','585e9cc93e70b39160e7921475f9bcd7d31219ce')DATA_HUB['kaggle_house_test'] = ( DATA_URL + 'kaggle_house_pred_test.csv','fa19780a7b011d9b009e8bff8e99922a8ee2eb90')train_data = pd.read_csv(download('kaggle_house_train'))
test_data = pd.read_csv(download('kaggle_house_test'))

2.数据预处理

读取样本

训练数据集包括1460个样本,每个样本80个特征和1个标签, 而测试数据集包含1459个样本,每个样本80个特征。

# 使用pandas分别加载包含训练数据和测试数据的两个CSV文件。
train_data = pd.read_csv(download('kaggle_house_train'))
test_data = pd.read_csv(download('kaggle_house_test'))
print(train_data.shape) #(1460,81)
print(test_data.shape) #(1459,80)

查看一下前4个样本的前四个特征以及最后二个特征以及相应标签(房价)

# train_data 是一个Pandas DataFrame对象 通常包含表格数据(行是样本/记录,列是特征/字段)
# iloc 按位置索引选择数据的属性,第一个参数为行选择器,第二个参数为列选择器
print(train_data.iloc[0:4, [0, 1, 2, 3, -3, -2, -1]])

在每个样本中,第一个特征是ID, 它不携带任何用于预测的信息。 因此,在将数据提供给模型之前,我们将其从数据集中删除。

# train_data.iloc[:, 1:-1] 选择所有行,选取第二列和倒数第二列  排除训练集的第一列(通常是ID)和最后一列(通常是标签/目标变量)
# test_data.iloc[:, 1:] 测试集选取从第2列开始到最后一列的所有列
# 参数2接着参数1,将多个DataFrame连接成一个新的DataFrame
all_features = pd.concat((train_data.iloc[:, 1:-1], test_data.iloc[:, 1:]))

预处理样本

数值型特征处理
  1. 将数据中所有缺失的值替换为相应特征的平均值。
  2. 为了将所有特征放在一个共同的尺度上, 我们通过将特征重新缩放到零均值和单位方差来标准化数据 x 标准 = x − μ σ x_{标准}=\frac{x−μ}{σ} x标准=σxμ

其中 μ μ μ σ σ σ分别表示特征 x x x均值和标准差。 这些特征具有零均值和单位方差,即 E [ x − μ σ ] = μ − μ σ = 0 E[\frac{x−μ}{σ}] = \frac{μ-μ}{σ}=0 E[σxμ]=σμμ=0 E [ ( x − μ ) 2 ] = ( σ 2 + μ 2 ) − 2 μ 2 + μ 2 = σ 2 E[(x−μ)^2]=(σ^2+μ^2)−2μ^2+μ^2=σ^2 E[(xμ)2]=(σ2+μ2)2μ2+μ2=σ2

all_features.dtypes返回pandas Series对象,包含索引index:列名 值values:对应的数据类型。

all_features.dtypes != 'object'比较的存储在Series中的实际值(values),返回布尔Series

布尔Series机制

在 pandas 中,当您使用一个布尔 Series 来索引另一个 Series 时,pandas 会选择布尔 Series 中值为 True 的所有对应元素。

  1. 对齐索引:首先 pandas 会根据索引名称对齐两个 Series
  2. 选择元素:然后只保留布尔 Series 中值为 True 的位置对应的元素
  3. 返回结果:返回一个新的 Series,包含所有被选中的元素
# numeric_features包含所有数值型特征列名的列表
numeric_features = all_features.dtypes[all_features.dtypes != 'object'].index

all_features[numeric_features]是根据列名选择all_featuresDataFrame子集。

DataFrame.apply会遍历每一列,每次取一列数据作为Series传入lambda函数进行标准化 x 标准 = x − μ σ x_{标准}=\frac{x−μ}{σ} x标准=σxμ并收集每次lambda的返回值(这里使用了Pandas的广播机制),组合所有结果返回新的DataFrame

这里的均值是包含数据集和测试集的,实际情况不一定有测试集,可能只能在数据集上求均值。

# 所有数值特征的标准化
all_features[numeric_features] = all_features[numeric_features].apply(lambda x: (x - x.mean()) / (x.std()))

numeric_features选中数值列,但有些数值列的值为NaN,最后需要将数值特征中的所有缺失值替换为0,并返回新的DataFrame。这里替换为0因为标准化后所有特征的均值为0。

# 在标准化数据之后,所有均值消失,因此我们可以将缺失值设置为0
all_features[numeric_features] = all_features[numeric_features].fillna(0)
特征标准化的好处
  1. 方便优化

假设预测房价有两个特征,房间数(1-5)和房屋面积(50-300),对每个特征求梯度。发现面积方向的梯度是房间数方向梯度的几十倍!

大梯度需要小的学习率抑制梯度爆炸,而大的梯度需要大的学习率去加速收敛,没有一个合适的学习率可以兼容。

如果采取标准化,假设房间_std=(房间数-3)/1.5 ,面积_std =(面积-175)/125,新梯度之间差异不会太大

# 标准化变换:
房间_std = (房间数 - 3)/1.5   # 范围[-1.33,1.33]
面积_std = (面积 - 175)/125   # 范围[-1,1]# 新梯度:
∂Loss/∂w_房间 = -2(y-ŷ)×房间_std ≈ -2(y-ŷ)×0 (平均)
∂Loss/∂w_面积 = -2(y-ŷ)×面积_std ≈ -2(y-ŷ)×0 (平均)
→ 梯度大小比例恢复11
  1. 正则化惩罚公平

正则项 λ 2 ∣ ∣ w ∣ ∣ 2 \frac{\lambda}{2}||w||^2 2λ∣∣w2惩罚 权重的平方和,这里超参数 λ \lambda λ控制了正则项的重要程度, λ \lambda λ设置的越大,对大的权重施加的惩罚就越重,没办法选出一个合适的值。

离散值处理

MSZoning的类型是一个object之类的离散值,使用one-hot编码处理。假设这个特征(这一列)有5个不一样的值,那我们就使用5个分量来表示。

pd.get_dummies会处理所有字符串类型(object)或分类类型(category)的列,将其转换为可用的数值格式(one-hot编码),在DataFrame结构中以多列方式呈现,生成新的列 列原列名_值,返回新的DataFrame。

参数dummy_na=True表示将NaN视为一个独立有效的类别,因为默认情况会忽略缺失值。

# “Dummy_na=True”将“na”(缺失值)视为有效的特征值,并为其创建指示符特征
# 处理字符串类型或分类类型
all_features = pd.get_dummies(all_features,dummy_na=True)
print(all_features.shape)  #(2919,330)

可以发现此转换之后,特征的总数量从79个增加到330个。

这里原始的列被剔除了!

print(all_features.select_dtypes(include='object').columns)
# Index(['MSZoning', 'Street', 'Alley', 'LotShape', 'LandContour', 'Utilities',
#      'LotConfig', 'LandSlope', 'Neighborhood', 'Condition1', 'Condition2',
#       'BldgType', 'HouseStyle', 'RoofStyle', 'RoofMatl', 'Exterior1st',
#       'Exterior2nd', 'MasVnrType', 'ExterQual', 'ExterCond', 'Foundation',
#       'BsmtQual', 'BsmtCond', 'BsmtExposure', 'BsmtFinType1', 'BsmtFinType2',
#       'Heating', 'HeatingQC', 'CentralAir', 'Electrical', 'KitchenQual',
#       'Functional', 'FireplaceQu', 'GarageType', 'GarageFinish', 'GarageQual',
#       'GarageCond', 'PavedDrive', 'PoolQC', 'Fence', 'MiscFeature',
#       'SaleType', 'SaleCondition'],
#      dtype='object')
all_features = pd.get_dummies(all_features,dummy_na=True,dtype=np.uint8)
print(all_features.select_dtypes(include='object').columns)
# Index([], dtype='object')

大模型的解释:


**问题:**can’t convert np.ndarray of type numpy.object_. The only supported types are: float64, float32, float16, complex64, complex128, int64, int32, int16, int8, uint64, uint32, uint16, uint8, and bool

尝试将一个包含 numpy.object_** **类型数据 的 NumPy 数组转换为其他类型(如 PyTorch Tensor 或特定数值类型),但操作仅支持特定数据类型。

评论解答

get_dummies函数在pandas1.6.0版本之前返回numpy.uint8,无符号八位整数,在1.6.0版本开始更改为返回numpy.bool_,numpy布尔值。

# “Dummy_na=True”将“na”(缺失值)视为有效的特征值,并为其创建指示符特征
# 处理字符串类型或分类类型 只控制新的列的类型
all_features = pd.get_dummies(all_features,dummy_na=True,dtype=int)
print(all_features.shape)  #(2919,310)

这里确实可以解决问题,但是我测试了之后发现布尔类型是不会报错的??

转换为张量表示

pandas格式中提取NumPy格式,并将其转换为张量表示用于训练。

all_features[:n_train]表示前n_train行数据,只包含数据行,不包含列名,.values表示返回NumPy数组。torch,tensor表示将NumPy数组转换为PyTorch张量,并且指定格式为32位浮点数

train_data.SalePrice.values通过列名SalePrice获取到该列的值,输出是一个一维数组,形状为一维数组(n_train,)。

将其重塑数组二维数组形状为(n_train, 1),最后转换为PyTorch张量。

n_train = train_data.shape[0] #获取训练数据集的样本数train_features = torch.tensor(all_features[:n_train].values, dtype=torch.float32)
test_features = torch.tensor(all_features[n_train:].values, dtype=torch.float32)
train_labels = torch.tensor(train_data.SalePrice.values.reshape(-1, 1), dtype=torch.float32)

训练

损失函数使用均方误差损失 1 n ∑ i = 1 n ( y i − y ^ i ) 2 \frac{1}{n} \sum_{i=1}^n(y_i−\hat y_i)^2 n1i=1n(yiy^i)2

loss = nn.MSELoss()

我们训练一个带有损失平方的线性模型。 显然线性模型很难让我们在竞赛中获胜,但线性模型提供了一种健全性检查, 以查看数据中是否存在有意义的信息。

如果我们在这里不能做得比随机猜测更好,那么我们很可能存在数据处理错误。 如果一切顺利,线性模型将作为基线(baseline)模型, 让我们直观地知道最好的模型有超出简单的模型多少。

in_features = train_features.shape[1] # 特征数量def get_net():net = nn.Sequential(nn.Linear(in_features,1))return net

对于真实值减去误差值来说,我们更关心相对误差 y − y ^ y \frac{y-\hat y}{y} yyy^,对于房价来说不同的房子价格相差较大,有10w的有100w的。例如,如果我们在俄亥俄州农村地区估计一栋房子的价格时, 假设我们的预测偏差了10万美元, 然而那里一栋典型的房子的价值是12.5万美元, 那么模型可能做得很糟糕。 另一方面,如果我们在加州豪宅区的预测出现同样的10万美元的偏差, (在那里,房价中位数超过400万美元) 这可能是一个不错的预测。

对数变换(log transformation)这种方法被广泛应用于Kaggle等数据科学竞赛中。我们评估模型时使用 1 n ∑ i = 1 n ( l o g ( y i ) − l o g ( y ^ i ) ) 2 \sqrt {\frac{1}{n}\sum_{i=1}^n (log(y_i)−log(\hat y_i))^2} n1i=1n(log(yi)log(y^i))2 等价于 1 n ∑ i = 1 n ( l o g y i y ^ i ) 2 \sqrt {\frac{1}{n}\sum_{i=1}^n (log \frac {y_i}{\hat y_i})^2} n1i=1n(logy^iyi)2 。这个评估值越小越好。

torch.clamp(input, min, max, *, out=None) -> Tensor将张量中的元素限制在指定范围,min表示下限值,max表示上限值。这里将最低值限制为1,防止出现负数和为0的情况影响对数的取值。

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

这里训练使用Adam优化器(后续会讲),主要优势是对于初始学习率不那么敏感。

  1. 从train_iter中按batch_size取出数据集
  2. 将optimizer管理的所有参数的梯度归零,防止每一batch_size的梯度累加错误更新
  3. 前向传播与损失计算。训练中使用均方误差损失,这里y和net(X)的形状在之前的步骤已经统一
  4. 反向传播计算梯度,自动微分计算梯度。
  5. 使用优化器更新参数。

每轮结束后评估整个训练集的性能,将每轮结果放入train_ls中,如果提供了测试标签test_labels,评估模型在测试集上的表现。

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优化算法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:# 每次返回一个(X_batch, y_batch)元组optimizer.zero_grad()l = loss(net(X), y) # 输出形状 net(X)=[batch_size, 1]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折交叉验证来帮助模型选和超参数调整。

我们首先需要定义一个函数,在 K K K折交叉验证过程中返回第 i i i折的数据。 具体地说,它选择第 i i i个切片作为验证数据,其余部分作为训练数据。

注意,这并不是处理数据的最有效方法,如果我们的数据集大得多,会有其他解决办法。

返回训练和验证误差的平均值

# k总折数,i当前折数索引
def get_k_fold_data(k, i, X, y):assert k > 1 # 如果 k≤1,抛出 AssertionError#计算每折大小,整除会舍弃余数,少量样本不被包含在任何折中fold_size = X.shape[0] // k X_train, y_train = None, Nonefor j in range(k): # slice(start, stop, step):slice对象是用于表示切片操作的特殊对象# 以下代码相当于创建了一个切片对象:# idx = [j * fold_size : (j + 1) * fold_size]idx = slice(j * fold_size, (j + 1) * fold_size)X_part, y_part = X[idx, :], y[idx]if j == i: # 验证集X_valid, y_valid = X_part, y_partelif X_train is None: # 如果不是验证集且训练集尚未初始化 - 第一个训练集X_train, y_train = X_part, y_partelse: # 将其余训练集拼接在一起# torch.cat():沿指定维度连接张量,0表示垂直堆叠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份后,每次使用第i份作为验证集,其余作为训练集。

下面的代码独立训练K次模型,每一次都创建一个全新初始化的模型,每次都有新的训练数据与验证数据。复用模型会使用上一次训练后参数信息,

交叉验证目标是评估最终模型性能,每一次训练模型都取模型最后一轮的评估误差。

def k_fold(k, X_train, y_train, num_epochs, learning_rate, weight_decay,batch_size):train_l_sum, valid_l_sum = 0, 0for i in range(k):data = get_k_fold_data(k, i, X_train, y_train)net = get_net()#         解包为X_train, y_train, X_valid, y_validtrain_ls, valid_ls = train(net, *data, num_epochs, learning_rate,weight_decay, batch_size)# 只取训练完成时(最后一轮)的误差train_l_sum += train_ls[-1]valid_l_sum += valid_ls[-1]# K折的学习曲线通常相似,所以这里选择只绘制第一折if i == 0:d2l.plot(list(range(1, num_epochs + 1)),  # X轴:训练轮次[1, num_epochs][train_ls, valid_ls],           # Y轴数据:[训练误差列表, 验证误差列表]xlabel='epoch',                 # X轴标签ylabel='rmse',                 # Y轴标签(均方根误差)xlim=[1, num_epochs],           # X轴显示范围legend=['train', 'valid'],      # 图例说明yscale='log'                    # Y轴使用对数刻度)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 K K折交叉验证的误差要高得多, 这表明模型过拟合了。 在整个训练过程中,我们希望监控训练误差和验证误差这两个数字。

k, num_epochs, lr, weight_decay, batch_size = 5, 100, 5, 0, 64
train_l, valid_l = k_fold(k, train_features, train_labels, num_epochs, lr,weight_decay, batch_size)
print(f'{k}-折验证: 平均训练log rmse: {float(train_l):f}, 'f'平均验证log rmse: {float(valid_l):f}')

代码总结

  1. 初始化一组超参数
  2. 进行 K K K折交叉验证,对于每一折 i i i过程执行如下的步骤
    1. 将训练集划分为k份,其中第i份作为验证集,其余k-1份作为训练集
    2. 初始化一个新的模型
    3. 使用当前超参数、训练集和验证集训练模型(训练该模型的参数)
    4. 累计训练完成的评估误差 - 训练集的评估误差与验证集的评估误差
  3. 返回折数的平均评估误差

通过不断调整超参数重复上述过程,选择出最优的超参数。

最终模型确认及结果预测

  1. 通过之前的步骤,我们已经选择出了一组超参数。现在使用这些超参数和全部的训练数据来训练最终模型。
  2. 将最终模型应用于测试集,将预测结果保存在CSV文件中。

在预测阶段不需要知道梯度信息了,更关心内存的优化,所以通常会将预测结果从计算图中分离datach出来创建一个新的不含梯度信息的张量。然后将PyTorch张量转换为NumPy数组。

将模型预测结果转换为Pandas DataFrame中的格式,preds.reshape(1, -1)表示将数组转换为形状为(1,N)的二维数组,取出二维数组中的唯一元素(一维数组)转换为Pandas Series对象。

def train_and_pred(train_features, test_features, train_labels, test_data,num_epochs, lr, weight_decay, batch_size):# 1. 初始化模型net = get_net() # # 2. 完整训练(不使用验证集)train_ls, _ = train(net, train_features, train_labels, None, None,num_epochs, lr, weight_decay, batch_size)# 3. 可视化训练过程d2l.plot(np.arange(1, num_epochs + 1), [train_ls], xlabel='epoch',ylabel='log rmse', xlim=[1, num_epochs], yscale='log')# 4. 打印最终训练误差print(f'训练log rmse:{float(train_ls[-1]):f}')# 5. 测试集预测preds = net(test_features).detach().numpy()# 6. 格式化预测结果test_data['SalePrice'] = pd.Series(preds.reshape(1, -1)[0])

Kaggle提交规范

1.文件命名:通常要求固定文件名。

2.首列必须为id,次列为预测值列,列名大小写敏感。

    # 7. 创建提交文件# 将测试集id和模型预测的房价水平拼接形成新的DataFrame# Id	SalePrice# 1461	181000# 1462	179500# 将DataFrame转换为CSV格式(Kaggle标准提交格式)submission = pd.concat([test_data['Id'], test_data['SalePrice']], axis=1)submission.to_csv('submission.csv', # 输出的文件名index=False	# 禁止写入行索引)

如果测试集上的预测与 K K K倍交叉验证过程中的预测相似, 那就是时候把它们上传到Kaggle了。 下面的代码将生成一个名为submission.csv的文件。

train_and_pred(train_features, test_features, train_labels, test_data,num_epochs, lr, weight_decay, batch_size)

代码总结

import hashlib # Python的哈希库,用于文件校验(计算SHA1、MD5等哈希值)
import os # 操作系统接口库 用于处理文件路径、目录创建等操作
import tarfile # 导入tar文件处理库 用于解压缩.tar、.tar.gz、.tar.bz2等归档文件
import zipfile # 导入zip文件处理库 用于解压缩.zip格式的压缩文件
import requests # 导入HTTP请求库 提供简洁的API用于网络文件下载
import numpy as np
import pandas as pd
import torch
from torch import nn
from d2l import torch as d2lDATA_HUB = dict()
DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/'def download(name, cache_dir=os.path.join('..', 'data')):"""下载一个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:breaksha1.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# 数据获取
DATA_HUB['kaggle_house_train'] = (  DATA_URL + 'kaggle_house_pred_train.csv','585e9cc93e70b39160e7921475f9bcd7d31219ce')DATA_HUB['kaggle_house_test'] = ( DATA_URL + 'kaggle_house_pred_test.csv','fa19780a7b011d9b009e8bff8e99922a8ee2eb90')train_data = pd.read_csv(download('kaggle_house_train'))
test_data = pd.read_csv(download('kaggle_house_test'))# 数据处理
all_features = pd.concat((train_data.iloc[:, 1:-1], test_data.iloc[:, 1:]))
numeric_features = all_features.dtypes[all_features.dtypes != 'object'].index
all_features[numeric_features] = all_features[numeric_features].apply(lambda x: (x - x.mean()) / (x.std()))
all_features[numeric_features] = all_features[numeric_features].fillna(0)all_features = pd.get_dummies(all_features,dummy_na=True,dtype=np.uint8)
n_train = train_data.shape[0] #获取训练数据集的样本数
train_features = torch.tensor(all_features[:n_train].values, dtype=torch.float32)
test_features = torch.tensor(all_features[n_train:].values, dtype=torch.float32)
train_labels = torch.tensor(train_data.SalePrice.values.reshape(-1, 1), dtype=torch.float32)# 模型的训练
loss = nn.MSELoss()
in_features = train_features.shape[1] # 特征数量
# 训练损失
def get_net():net = nn.Sequential(nn.Linear(in_features,1))return net
# 评估误差
def log_rmse(net, features, labels):# 为了在取对数时进一步稳定该值,将小于1的值设置为1clipped_preds = torch.clamp(net(features), 1, float('inf'))rmse = torch.sqrt(loss(torch.log(clipped_preds),torch.log(labels)))return rmse.item() # 从张量中提取python标量值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优化算法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:# 每次返回一个(X_batch, y_batch)元组optimizer.zero_grad()l = loss(net(X), y) # 输出形状 net(X)=[batch_size, 1]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折交叉验证
# 获取训练集和数据集
def get_k_fold_data(k, i, X, y):assert k > 1 # 如果 k≤1,抛出 AssertionError#计算每折大小,整除会舍弃余数,少量样本不被包含在任何折中fold_size = X.shape[0] // k X_train, y_train = None, Nonefor j in range(k): # slice(start, stop, step):slice对象是用于表示切片操作的特殊对象# 以下代码相当于创建了一个切片对象:# idx = [j * fold_size : (j + 1) * fold_size]idx = slice(j * fold_size, (j + 1) * fold_size)X_part, y_part = X[idx, :], y[idx]if j == i: # 验证集X_valid, y_valid = X_part, y_partelif X_train is None: # 如果不是验证集且训练集尚未初始化 - 第一个训练集X_train, y_train = X_part, y_partelse: # 将其余训练集拼接在一起# torch.cat():沿指定维度连接张量,0表示垂直堆叠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_validdef k_fold(k, X_train, y_train, num_epochs, learning_rate, weight_decay,batch_size):train_l_sum, valid_l_sum = 0, 0for i in range(k):data = get_k_fold_data(k, i, X_train, y_train)net = get_net()#         解包为X_train, y_train, X_valid, y_validtrain_ls, valid_ls = train(net, *data, num_epochs, learning_rate,weight_decay, batch_size)# 只取训练完成时(最后一轮)的误差train_l_sum += train_ls[-1]valid_l_sum += valid_ls[-1]# K折的学习曲线通常相似,所以这里选择只绘制第一折if i == 0:d2l.plot(list(range(1, num_epochs + 1)),  # X轴:训练轮次[1, num_epochs][train_ls, valid_ls],           # Y轴数据:[训练误差列表, 验证误差列表]xlabel='epoch',                 # X轴标签ylabel='rmse',                 # Y轴标签(均方根误差)xlim=[1, num_epochs],           # X轴显示范围legend=['train', 'valid'],      # 图例说明yscale='log'                    # Y轴使用对数刻度)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, 0, 64
train_l, valid_l = k_fold(k, train_features, train_labels, num_epochs, lr,weight_decay, batch_size)
print(f'{k}-折验证: 平均训练log rmse: {float(train_l):f}, 'f'平均验证log rmse: {float(valid_l):f}')def train_and_pred(train_features, test_features, train_labels, test_data,num_epochs, lr, weight_decay, batch_size):# 1. 初始化模型net = get_net() # # 2. 完整训练(不使用验证集)train_ls, _ = train(net, train_features, train_labels, None, None,num_epochs, lr, weight_decay, batch_size)# 3. 可视化训练过程d2l.plot(np.arange(1, num_epochs + 1), [train_ls], xlabel='epoch',ylabel='log rmse', xlim=[1, num_epochs], yscale='log')# 4. 打印最终训练误差print(f'训练log rmse:{float(train_ls[-1]):f}')# 5. 测试集预测preds = net(test_features).detach().numpy()# 6. 格式化预测结果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,num_epochs, lr, weight_decay, batch_size)

提交到Kaggle

http://www.xdnf.cn/news/979165.html

相关文章:

  • 【PhysUnits】17.7 readme.md更新
  • 从代码学习深度强学习 - Dyna-Q 算法 PyTorch版
  • Android Gson工具类
  • 免下载苹果 IPA 文件重签名工具:快速更换应用名称和 BID的教程
  • xcode中project.pbxproj点开为空白问题
  • Linux Swap分区应该禁用吗?深入辨析其作用与性能优化
  • Spring @Qualifier,@Primary
  • 接收rabbitmq消息
  • 记录下three.js学习过程中不理解问题----材质(material)⑤
  • 在前端元素中,点击当前元素,但是却选中其他元素的文字的问题
  • Cesium圆锥渐变色实现:融合顶点着色器、Canvas动态贴图与静态纹理的多方案整合
  • 深度剖析:UI 设计怎样为小程序构建极致轻量体验
  • 在 Windows 上安装和配置 Redis 及可视化工具指南
  • C#接口代码记录
  • 物联网基础概述【一】
  • 容器化部署案例一wordpress
  • 递归法解N叉树的后序遍历
  • 若依微服务Openfeign接口调用超时问题
  • Java面向对象编程(OOP)深度学习解析
  • Flutter布局系统全面解析:从基础组件到复杂界面构建
  • ttyd:安全地通过网络共享您的 Linux 终端
  • Cpp 知识3
  • github action推送-构建准备步骤获取私有dockerhub镜像仓库镜像的一系列错误尝试
  • Solidity 开发从入门到精通:语法特性与实战指南
  • 在Linux下使用vscode使用交叉编译工具链的gdb对core文件进行堆栈、变量查看
  • Ubuntu下编译安装DLib的GPU版本并实现人脸检测和人脸关键点检测
  • “十五五”时期智慧城市赋能全国一体化数据市场建设:战略路径与政策建议[ 注:本建议基于公开政策文件与行业实践研究,数据引用截至2025年6月11日。]
  • 商品中心—3.商品可采可补可售的技术文档下
  • 前端面试宝典---事件循环面试题
  • 小白学Pinia状态管理