李沐 《动手学深度学习》 | 实战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.pswp.cn/news/909073.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一键部署Prometheus+Grafana+alertmanager对网站状态进行监控

在建设监控体系的过程中,针对一个系统的监控是多维度的,除了服务器资源状态、中间件状态、应用状态外,对系统访问状态的监控也是很有必要,可以在系统访问出现异常时第一时间通知到我们。本文介绍使用 Docker-compose 方式一键部署…

康谋方案 | 高精LiDAR+神经渲染3DGS的完美融合实践

目录 一、从点云到高精地图的重建 1、数据采集 2、点云聚合 3、高精地图建模 4、三维建模与装饰 二、颠覆性革新:NeRF 与 3DGS 重建 1、仅需数日,完成街景重建 2、进一步消除 Domain gap,场景逼真如实地拍摄 3、降本增效&#xff0c…

MySQL-事务(TRANSACTION-ACID)管理

目录 一、什么是事务? 1.1.事务的定义 1.2.事务的基本语句 1.3.事务的四大特性(ACID) 二、数据库的并发控制 2.1.什么是并发及并发操作带来的影响? 2.2.并发操作带来的隔离级别 三、使用事务的场景 3.1.银行转账场景示例 3.2.模拟…

centos系统docker配置milvus教程

本人使用的是京东云服务器配置milvus 参考教程:https://blog.csdn.net/withme977/article/details/137270087 首先确保安装了docker 、docker compose docker -- version docker-compose --version创建milvus工作目录 mkdir milvus # 进入到新建的目录 cd milvu…

什么是JSON ?从核心语法到编辑器

一、什么是JSON ? JSON,即 JavaScript 对象表示法,是一种轻量级、跨语言、纯文本的数据交换格式 。它诞生于 JavaScript 生态,但如今已成为所有编程语言通用的 “数据普通话”—— 无论前端、后端,还是 Python、Java&…

计算机网络(7)——物理层

1.数据通信基础 1.1 物理层基本概念 物理层(Physical Layer)是所有网络通信的物理基础,它定义了在物理介质上传输原始比特流(0和1)所需的机械、电气、功能、过程和规程特性 1.2 数据通信系统模型 信源:生成原始数据的终端设备,常见形态包括…

深度学习基础知识总结

1.BatchNorm2d 加速收敛:Batch Normalization 可以使每层的输入保持较稳定的分布(接近标准正态分布),减少梯度更新时的震荡问题,从而加快模型训练速度。 减轻过拟合:批归一化引入了轻微的正则化效果&#…

iOS 抖音首页头部滑动标签的实现

抖音首页的头部滑动标签(通常称为"Segmented Control"或"Tab Bar")是一个常见的UI组件,可以通过以下几种方式实现: 1. 使用UISegmentedControl 最简单的实现方式是使用系统自带的UISegmentedControl: let segmentedCo…

ThreadLocal实现原理

ThreadLocal 是 Java 中实现线程封闭(Thread Confinement)的核心机制,它通过为每个线程创建变量的独立副本来解决多线程环境下的线程安全问题。 Thread └── ThreadLocalMap (threadLocals) // 每个线程持有的专属Map├── Entry[] tab…

【笔记】结合 Conda任意创建和配置不同 Python 版本的双轨隔离的 Poetry 虚拟环境

如何结合 Conda 任意创建和配置不同 Python 版本的双轨隔离的Poetry 虚拟环境? 在 Python 开发中,为不同项目配置独立且适配的虚拟环境至关重要。结合 Conda 和 Poetry 工具,能高效创建不同 Python 版本的 Poetry 虚拟环境,接下来…

defineAsyncComponent

下面,我们来系统的梳理关于 defineAsyncComponent 懒加载 的基本知识点: 一、异步组件核心概念 1.1 什么是异步组件? 异步组件是 Vue 中一种按需加载组件的机制,允许将组件代码拆分为独立的 chunk,在需要时再从服务器加载。这种技术能显著提升应用初始加载速度。 1.2 为…

ANeko v1.0.3 | 在手机里养只宠物猫 实时互动 动画细腻

ANeko是一款专为喜欢猫咪的用户设计的互动养宠应用。它让你在手机屏幕上拥有一只可爱的猫咪动画,这只猫咪会实时跟随你的手指触摸轨迹,带来生动有趣的互动体验。该应用不仅保留了用户熟悉的交互式猫动画,还结合了现代高清图形技术&#xff0c…

人工智能AI

AI 简介 AI 使我们能够生成可以改进卫生保健的出色软件,让人能够克服生理上的不便,改进智能基础结构,创造令人惊叹的娱乐体验,甚至拯救地球! 什么是 AI? 简而言之,AI 就是一种模仿人类行为和能力的软件。 关键工作负载包括: 机器学习 - 它通常是 AI 系统的基础,也是…

Vue 中 data 选项:对象 vs 函数

Vue 中 data 选项&#xff1a;对象 vs 函数 在 Vue 开发中&#xff0c;data 选项可以使用对象或函数形式&#xff0c;了解它们的使用场景非常重要。下面我将通过一个直观的示例来展示两者的区别和适用场景。 <!DOCTYPE html> <html lang"zh-CN"> <h…

python打卡第49天

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 CBAM 注意力模块介绍 从 SE 到 CBAM&#xff1a;注意力机制的演进 之前我们介绍了 SE&#xff08;Squeeze-and-Excitation&#xff09;通道注意力模块&#xff0c;其本质是对特征进行增强处理。现在&#…

iOS和桌面双端抓包实战经验总结:Sniffmaster与常见工具组合解析

近几年&#xff0c;移动端和桌面端的网络调试工作变得越来越“棘手”。过去一个代理证书搞定的场景&#xff0c;现在常常被HTTPS加密、双向验证、App安全策略给难住。特别是涉及到iOS平台时&#xff0c;很多传统抓包方案都不再适用。作为一名在多个平台开发和测试的程序员&…

cloudstudio腾讯云:matplotlib 设置中文字体

检查可用字体&#xff1a; import matplotlib.font_manager as fm fonts [f.name for f in fm.fontManager.ttflist] print(fonts) # 查看系统中可用的字体列表# 列出所有中文字体文件 !fc-list :langzh没有中文字体&#xff0c;需要下载 !sudo apt-get install fonts-wqy-m…

Django中的ORM的使用步骤----以MySQL为例

1 以纯Python的形式创建项目虚拟环境 2 命令安装Django 3 在当前虚拟环境目录下命令创建Django项目 4 命令创建app 注&#xff1a; 若想将创建的子应用存放到指定目录&#xff0c;如app&#xff0c; 那么需要先手动创建app目录&#xff0c;再手动创建子应用目录&#xff0c;如o…

Rust 学习笔记:通过 Send 和 Sync trait 实现可扩展并发性

Rust 学习笔记&#xff1a;通过 Send 和 Sync trait 实现可扩展并发性 Rust 学习笔记&#xff1a;通过 Send 和 Sync trait 实现可扩展并发性Send trait&#xff1a;允许在线程之间转移所有权Sync trait&#xff1a;允许多线程访问手动实现 Send 和 Sync 是不安全的练习题 Rust…

【C++】第十一节—一文详解vector(使用+杨辉三角+深度剖析+模拟实现+细节详细补充)

Hi&#xff0c;我是云边有个稻草人&#xff0c;偶尔中二的C领域博主^(*&#xffe3;(oo)&#xffe3;)^&#xff0c;与你分享专业知识—— C_本篇博客所属专栏—持续更新中—欢迎订阅喔 目录 一、vector的介绍及使用 1.1 vector的介绍 1.2 vector的使用 &#xff08;1&…