深度学习--------------Kaggle房价预测

news2024/11/13 9:22:55

目录

  • 下载和缓存数据集
  • 访问和读取数据集
    • 总代码
  • 数据预处理
  • 训练
  • K折交叉验证
  • 模型选择
  • 总代码
  • 提交你的Kaggle预测
  • 提交Kaggle

下载和缓存数据集

import hashlib
import os
import tarfile
import zipfile
import requests
 

# download传递的参数分别是数据集的名称、缓存文件夹的路径
def download(name, cache_dir=os.path.join('..', 'data')):  # @save
    """下载一个DATA_HUB中的文件,返回本地文件名"""
    # 检查变量name是否存在于名为DATA_HUB的字典中,如果是异常则给出消息提示
    assert name in DATA_HUB, f"{name} 不存在于 {DATA_HUB}"
    # 检查与name相对应的元组并解包赋值给url和sha1_hash
    url, sha1_hash = DATA_HUB[name]
    os.makedirs(cache_dir, exist_ok=True)
    # 缓存的路径和目标目录,[-1]:url指向文件名
    fname = os.path.join(cache_dir, url.split('/')[-1])
    if os.path.exists(fname): # 检查文件是否存在
        sha1 = hashlib.sha1() # 计算文件的哈希值,初始化哈希对象
        # with确保文件在使用后正确关闭
        with open(fname, 'rb') as f: # 读取文件内容
            while True: # 循环读取文件内容并更新哈希对象
                data = f.read(1048576) # 读取1MB到data
                if not data: # 为真跳出
                    break
                # 将读取到的数据块更新到哈希对象中
                sha1.update(data)
        # 检查计算出的哈希值是否与预期的哈希值匹配
        if sha1.hexdigest() == sha1_hash:
            return fname  # 命中缓存
    print(f'正在从{url}下载{fname}...')
    # 发送get请求并设置stream=True支持流式传输
    r = requests.get(url, stream=True, verify=True)
    with open(fname, 'wb') as f: # 写入模式
        f.write(r.content)
    return fname


# folder=None是指解压后数据应存放的文件名,如未指定则默认使用去除扩展名后的文件名作为文件夹名
def download_extract(name, folder=None):  # @save
    """下载并解压zip/tar文件"""
    # 下载文件,并返回下载的完整路径fname
    fname = download(name)
    # 获取下载文件所在的基本目录
    base_dir = os.path.dirname(fname)
    # 从文件名中分离出扩展名exe和数据目录
    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文件可以被解压缩'
    # 使用extractall方法将文件解压到基本目录,如果指定了folder参数,则返回base_dir和folder组合的路径
    # 否则返回去除扩展名后的文件名作为路径
    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)

# @save
DATA_HUB = dict() # 这是一个字典,用来存储数据集的信息。
DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/' # 指向一个存储了多个数据集文件的服务器




访问和读取数据集

import numpy as np
import pandas as pd
import torch
from torch import nn
from d2l import torch as d2l

下载并缓存Kaggle房屋数据集

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')

使用pandas分别加载包含训练数据和测试数据的两个CSV文件

train_data = pd.read_csv(download('kaggle_house_train'))
test_data = pd.read_csv(download('kaggle_house_test'))

输出:

在这里插入图片描述


训练数据集包括1460个样本,每个样本80个特征和1个标签, 而测试数据集包含1459个样本,每个样本80个特征。
print(train_data.shape)
print(test_data.shape)

输出:
在这里插入图片描述


查看前四个最后两个特征,以及相应标签(房价)。

print(train_data.iloc[0:4, [0, 1, 2, 3, -3, -2, -1]])

输出:
在这里插入图片描述

在每个样本中,第一个特征是ID, 这有助于模型识别每个训练样本。 虽然这很方便,但它不携带任何用于预测的信息。 因此,在将数据提供给模型之前,(我们将其从数据集中删除)。

# 删除train_data的第一列ID和最后一列房价;删除test_data的第一列索引
all_features = pd.concat((train_data.iloc[:, 1:-1], test_data.iloc[:, 1:]))
# 查看一下
print(all_features)

输出:

在这里插入图片描述




总代码

import hashlib
import os
import tarfile
import zipfile
import requests
import pandas as pd

# download传递的参数分别是数据集的名称、缓存文件夹的路径
def download(name, cache_dir=os.path.join('..', 'data')):  # @save
    """下载一个DATA_HUB中的文件,返回本地文件名"""
    # 检查变量name是否存在于名为DATA_HUB的字典中,如果是异常则给出消息提示
    assert name in DATA_HUB, f"{name} 不存在于 {DATA_HUB}"
    # 检查与name相对应的元组并解包赋值给url和sha1_hash
    url, sha1_hash = DATA_HUB[name]
    os.makedirs(cache_dir, exist_ok=True)
    # 缓存的路径和目标目录,[-1]:url指向文件名
    fname = os.path.join(cache_dir, url.split('/')[-1])
    if os.path.exists(fname): # 检查文件是否存在
        sha1 = hashlib.sha1() # 计算文件的哈希值,初始化哈希对象
        # with确保文件在使用后正确关闭
        with open(fname, 'rb') as f: # 读取文件内容
            while True: # 循环读取文件内容并更新哈希对象
                data = f.read(1048576) # 读取1MB到data
                if not data: # 为真跳出
                    break
                # 将读取到的数据块更新到哈希对象中
                sha1.update(data)
        # 检查计算出的哈希值是否与预期的哈希值匹配
        if sha1.hexdigest() == sha1_hash:
            return fname  # 命中缓存
    print(f'正在从{url}下载{fname}...')
    # 发送get请求并设置stream=True支持流式传输
    r = requests.get(url, stream=True, verify=True)
    with open(fname, 'wb') as f: # 写入模式
        f.write(r.content)
    return fname


# folder=None是指解压后数据应存放的文件名,如未指定则默认使用去除扩展名后的文件名作为文件夹名
def download_extract(name, folder=None):  # @save
    """下载并解压zip/tar文件"""
    # 下载文件,并返回下载的完整路径fname
    fname = download(name)
    # 获取下载文件所在的基本目录
    base_dir = os.path.dirname(fname)
    # 从文件名中分离出扩展名exe和数据目录
    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文件可以被解压缩'
    # 使用extractall方法将文件解压到基本目录,如果指定了folder参数,则返回base_dir和folder组合的路径
    # 否则返回去除扩展名后的文件名作为路径
    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)


# @save
DATA_HUB = dict() # 这是一个字典,用来存储数据集的信息。
DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/' # 指向一个存储了多个数据集文件的服务器
# 第二个参数:表示数据集文件的哈希值,用哈希值验证文件的完整性
DATA_HUB['kaggle_house_train'] = (DATA_URL + 'kaggle_house_pred_train.csv','585e9cc9370b9160e7921475fbcd7d31219ce')
DATA_HUB['kaggle_house_test'] = (DATA_URL + 'kaggle_house_pred_test.csv', 'fal9780a7b011d9b009e8bff8e99922a8ee2eb90')
# 读取路径指向的csv文件
train_data = pd.read_csv(download('kaggle_house_train'))
test_data = pd.read_csv(download('kaggle_house_test'))

print(train_data.shape) # 1460个样本,80个特征,1个标号label
print(test_data.shape) # 测试样本没有标号label
print(train_data.iloc[0:4,[0,1,2,3,-3,-2,-1]]) # 前面四行的某些列特征

输出:
在这里插入图片描述




数据预处理

如上所述,我们有各种各样的数据类型。 在开始建模之前,我们需要对数据进行预处理。 首先,我们将所有缺失的值替换为相应特征的平均值。然后,为了将所有特征放在一个共同的尺度上, 我们通过将特征重新缩放到零均值和单位方差来标准化数据在这里插入图片描述其中 𝜇 和 𝜎 分别表示均值和标准差。 现在,这些特征具有零均值和单位方差,即 在这里插入图片描述在这里插入图片描述
直观地说,我们标准化数据有两个原因: 首先,它方便优化。 其次,因为我们不知道哪些特征是相关的, 所以我们不想让惩罚分配给一个特征的系数比分配给其他任何特征的系数更大。

# 若无法获得测试数据,则可根据训练数据计算均值和标准差
# all_features.dtypes 获取每个特征的数据类型
# all_features.dtypes != 'object' 返回一个布尔值的 Series,其中为 True 的位置表示对应的特征不是对象类型(即数值类型)
# .index 提取数值类型特征的索引,并在numeric_features中存储
print(all_features.dtypes) # 可以知道每一列分别为什么类型特征
numeric_features = all_features.dtypes[all_features.dtypes != 'object'].index
print(numeric_features)
# 对all_features中所有数值类型特征进行标准化处理
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)

输出:
在这里插入图片描述




接下来,我们处理离散值。 这包括诸如“MSZoning”之类的特征。我们用独热编码替换它们), 例如,“MSZoning”包含值“RL”和“Rm”。 我们将创建两个新的指示器特征“MSZoning_RL”和“MSZoning_RM”,其值为01。 根据独热编码,如果“MSZoning”的原始值为“RL”, 则:“MSZoning_RL”为1,“MSZoning_RM”为0。 pandas软件包会自动为我们实现这一点。

# “Dummy_na=True”将“na”(缺失值)视为有效的特征值,并为其创建指示符特征
# 未指定column时,pd.get_dummies函数默认转换所有的分类列,包括MSZoning、SaleType、SaleCondition
# pd.get_dummies每个分类变量都会被转换成多列,每个唯一值对应一列
# 理解意思:某列有五类值,就将此列等价于变成五列,然后对应的列置1,不存在的列置0
all_features = pd.get_dummies(all_features, dummy_na=True)

未改变时的数据特征:

# 这里仅验证看一下特征的形状
print(all_features.shape)

输出:
在这里插入图片描述


改变后:

all_features = pd.get_dummies(all_features, dummy_na=True)
print(all_features.shape)

输出:
在这里插入图片描述
可以看到此转换会将特征的总数量从79个(去掉ID)增加到331个。



最后,通过values属性,我们可以从pandas格式中提取NumPy格式,并将其转换为张量表示用于训练。

# 获取训练集的行数,即训练样本数量,并将其存储在n_train中
n_train = train_data.shape[0]
# [:n_train].values是选择前n_train作为训练集的特征将数据转换为Numpy数组
train_features = torch.tensor(all_features[:n_train].values, dtype=torch.float32)
test_features = torch.tensor(all_features[n_train:].values, dtype=torch.float32)
# 将SalePrice列单独提取为标签
train_labels = torch.tensor(
    train_data.SalePrice.values.reshape(-1, 1), dtype=torch.float32)



训练

首先,我们训练一个带有损失平方的线性模型。 显然线性模型很难让我们在竞赛中获胜,但线性模型提供了一种健全性检查,以查看数据中是否存在有意义的信息。 如果我们在这里不能做得比随机猜测更好,那么我们很可能存在数据处理错误。 如果一切顺利,线性模型将作为基线(baseline)模型, 让我们直观地知道最好的模型有超出简单的模型多少。

loss = nn.MSELoss()
in_features = train_features.shape[1]
 
def get_net():
    net = nn.Sequential(nn.Linear(in_features,1))
    return net

房价就像股票价格一样,我们关心的是相对数量,而不是绝对数量。因此,我们更关心相对误差,而不是绝对误差 。 例如,如果我们在俄亥俄州农村地区估计一栋房子的价格时,假设我们的预测偏差了10万美元,然而那里一栋典型的房子的价值是12.5万美元,那么模型可能做得很糟糕。另一方面,如果我们在加州豪宅区的预测出现同样的10万美元的偏差,(在那里,房价中位数超过400万美元) 这可能是一个不错的预测。

(解决这个问题的一种方法是用价格预测的对数来衡量差异)。 事实上,这也是比赛中官方用来评价提交质量的误差指标。 即将在这里插入图片描述转换为在这里插入图片描述。 这使得预测价格的对数真实标签价格的对数之间出现以下均方根误差
在这里插入图片描述

# rmse=Root Mean Squared Error,对数均方根误差
def log_rmse(net, features, labels):
	# 通过神经网络net对输入特征features进行前向传播得到预测值
    # 然后为了在取对数时进一步稳定该值,用torch.clamp()函数将小于1的值设置为1
    # 第三个参数:不设置上限,即预测可以无限大
    clipped_preds = torch.clamp(net(features), 1, float('inf'))
    rmse = torch.sqrt(loss(torch.log(clipped_preds),
                           torch.log(labels)))
    # .item() 方法将rmse转换为Python标量返回
    return rmse.item()

我们的训练函数将借助Adam优化器,Adam优化器的主要吸引力在于它对初始学习率不那么敏感

# weight_decay权重衰退
def train(net, train_features, train_labels, test_features, test_labels,
          num_epochs, learning_rate, weight_decay, batch_size):
    # 用于存储每一轮训练和测试集上的log rmse值
    train_ls, test_ls = [], []
    train_iter = d2l.load_array((train_features, train_labels), batch_size)
    # 这里使用的是Adam优化算法,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:
        	# 清空过往梯度
            optimizer.zero_grad()
            # 通过前向传播计算损失值,然后计算预测值与真实值差异
            l = loss(net(X), y)
            l.backward() # 反向传播计算当前梯度
            optimizer.step() # 更新模型参数
        # 将log_rmse函数返回的误差值追加到train_ls列表中就记录了每一轮迭代
        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折交叉验证有助于模型选择和超参数调整。定义一个函数,在 𝐾 折交叉验证过程中返回第 𝑖 折的数据。具体地说,它选择第 𝑖 个切片作为验证数据其余部分作为训练数据。注意,这并不是处理数据的最有效方法,如果我们的数据集大得多,会有其他解决办法。

# 四个参数:表示将数据集分为多少个子集、当前折的索引、特征数据集、标签数据集
def get_k_fold_data(k, i, X, y):
	# 断言确保折数大于1,因为至少两个子集才能进行交叉验证
    assert k > 1
    # 每个子集(折)的大小=数据集的总行数除以折数,余数在最后一个子集处理
    fold_size = X.shape[0] // k
    # 初始化训练集
    X_train, y_train = None, None
    for j in range(k):
        # 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]
        # 当前折的索引 j 与指定用于验证的索引 i 匹配,将该折的数据分配给验证集(X_valid 和 y_valid)
        if j == i:
        	# 将该折的数据分配给验证集
            X_valid, y_valid = X_part, y_part
        # 如不匹配且训练集为空(第一次看到),则将该折的数据分配给训练集(X_train 和 y_train)
        elif X_train is None:
            X_train, y_train = X_part, y_part
        # 如不匹配且训练集不为空,则将该折的数据追加到训练集(X_train 和 y_train)中
        else:
        	# 表示将X_train, X_part沿着第一个维度拼接起来
            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




在𝐾折交叉验证中训练𝐾次后,返回训练和验证误差的平均值

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):
    	# 做k次,每次拿到第i折,把数据data拿出来即拿到从训练集和验证集
        data = get_k_fold_data(k, i, X_train, y_train)
        # 每次循环都会创建一个新的模型实例,确保每折的评估是独立的
        net = get_net()
        # 调用train函数训练模型并返回训练损失列表和验证损失列表
        train_ls, valid_ls = train(net, *data, num_epochs, learning_rate,
                                   weight_decay, batch_size)
        # 索引[-1]获取最后一个epoch的损失,即当前折的训练损失累加到train_l_sum 
        train_l_sum += train_ls[-1]
        valid_l_sum += valid_ls[-1]
        if i == 0: # 如果当前是第i折
        	# 第一个参数:用作x轴数据
        	# 第二个参数:一个列表储存了每一轮训练后的训练损失和验证损失
            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折交叉验证往往对多次测试具有相当的稳定性。然而,如果我们尝试了不合理的超参数,我们可能会发现验证效果不再代表真正的误差。

#数据集被分成了5份,然后执行5次训练和验证过程,每次过程中,4份数据用于训练模型,剩下的一份用于
k, num_epochs, lr, weight_decay, batch_size = 5, 100, 5, 0, 64
# 分别包含每次迭代的训练集和验证集的log rmse值
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}')

我们关心的是平均验证log rmse




总代码

import hashlib
import os
import tarfile
import zipfile
import requests
import pandas as pd
import torch
from torch import nn
from d2l import torch as d2l


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)


def get_net():
    net = nn.Sequential(nn.Linear(in_features, 1))
    return net


# rmse=Root Mean Squared Error,对数均方根误差
def log_rmse(net, features, labels):
    # 通过神经网络net对输入特征features进行前向传播得到预测值
    # 然后为了在取对数时进一步稳定该值,用torch.clamp()函数将小于1的值设置为1
    # 第三个参数:不设置上限,即预测可以无限大
    clipped_preds = torch.clamp(net(features), 1, float('inf'))
    rmse = torch.sqrt(loss(torch.log(clipped_preds),
                           torch.log(labels)))
    # .item() 方法将rmse转换为Python标量返回
    return rmse.item()


# weight_decay权重衰退
def train(net, train_features, train_labels, test_features, test_labels,
          num_epochs, learning_rate, weight_decay, batch_size):
    # 用于存储每一轮训练和测试集上的log rmse值
    train_ls, test_ls = [], []
    train_iter = d2l.load_array((train_features, train_labels), batch_size)
    # 这里使用的是Adam优化算法,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:
            # 清空过往梯度
            optimizer.zero_grad()
            # 通过前向传播计算损失值,然后计算预测值与真实值差异
            l = loss(net(X), y)
            l.backward()  # 反向传播计算当前梯度
            optimizer.step()  # 更新模型参数
        # 将log_rmse函数返回的误差值追加到train_ls列表中就记录了每一轮迭代
        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


# 四个参数:表示将数据集分为多少个子集、当前折的索引、特征数据集、标签数据集
def get_k_fold_data(k, i, X, y):
    # 断言确保折数大于1,因为至少两个子集才能进行交叉验证
    assert k > 1
    # 每个子集(折)的大小=数据集的总行数除以折数,余数在最后一个子集处理
    fold_size = X.shape[0] // k
    # 初始化训练集
    X_train, y_train = None, None
    for j in range(k):
        # 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]
        # 当前折的索引 j 与指定用于验证的索引 i 匹配,将该折的数据分配给验证集(X_valid 和 y_valid)
        if j == i:
            # 将该折的数据分配给验证集
            X_valid, y_valid = X_part, y_part
        # 如不匹配且训练集为空(第一次看到),则将该折的数据分配给训练集(X_train 和 y_train)
        elif X_train is None:
            X_train, y_train = X_part, y_part
        # 如不匹配且训练集不为空,则将该折的数据追加到训练集(X_train 和 y_train)中
        else:
            # 表示将X_train, X_part沿着第一个维度拼接起来
            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


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):
        # 做k次,每次拿到第i折,把数据data拿出来即拿到从训练集和验证集
        data = get_k_fold_data(k, i, X_train, y_train)
        # 每次循环都会创建一个新的模型实例,确保每折的评估是独立的
        net = get_net()
        # 调用train函数训练模型并返回训练损失列表和验证损失列表
        train_ls, valid_ls = train(net, *data, num_epochs, learning_rate,
                                   weight_decay, batch_size)
        # 索引[-1]获取最后一个epoch的损失,即当前折的训练损失累加到train_l_sum
        train_l_sum += train_ls[-1]
        valid_l_sum += valid_ls[-1]
        if i == 0:  # 如果当前是第i折
            # 第一个参数:用作x轴数据
            # 第二个参数:一个列表储存了每一轮训练后的训练损失和验证损失
            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


# @save
DATA_HUB = dict()
DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/'
DATA_HUB['kaggle_house_train'] = (DATA_URL + 'kaggle_house_pred_train.csv', '585e9cc9370b9160e7921475fbcd7d31219ce')
DATA_HUB['kaggle_house_test'] = (DATA_URL + 'kaggle_house_pred_test.csv', 'fal9780a7b011d9b009e8bff8e99922a8ee2eb90')
train_data = pd.read_csv(download('kaggle_house_train'))
test_data = pd.read_csv(download('kaggle_house_test'))

# print(train_data.shape) # 1460个样本,80个特征,1个标号label
# print(test_data.shape) # 测试样本没有标号label
# print(train_data.iloc[0:4,[0,1,2,3,-3,-2,-1]]) # 前面四行的某些列特征
all_features = pd.concat((train_data.iloc[:, 1:-1], test_data.iloc[:, 1:]))
# print(all_features.shape)


# 若无法获得测试数据,则可根据训练数据计算均值和标准差
# all_features.dtypes 获取每个特征的数据类型
# all_features.dtypes != 'object' 返回一个布尔值的 Series,其中为 True 的位置表示对应的特征不是对象类型(即数值类型)
# .index 提取数值类型特征的索引,并在numeric_features中存储
# print(all_features.dtypes)  # 可以知道每一列分别为什么类型特征
numeric_features = all_features.dtypes[all_features.dtypes != 'object'].index
# print(numeric_features)
# 对all_features中所有数值类型特征进行标准化处理
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”(缺失值)视为有效的特征值,并为其创建指示符特征
# 未指定column时,pd.get_dummies函数默认转换所有的分类列,包括MSZoning、SaleType、SaleCondition
# pd.get_dummies每个分类变量都会被转换成多列,每个唯一值对应一列
# 理解意思:某列有五类值,就将此列等价于变成五列,然后对应的列置1,不存在的列置0
all_features = pd.get_dummies(all_features, dummy_na=True)

# 获取训练集的行数,即训练样本数量,并将其存储在n_train中
n_train = train_data.shape[0]
# [:n_train].values是选择前n_train作为训练集的特征将数据转换为Numpy数组
train_features = torch.tensor(all_features[:n_train].values, dtype=torch.float32)
test_features = torch.tensor(all_features[n_train:].values, dtype=torch.float32)
# 将SalePrice列单独提取为标签
train_labels = torch.tensor(
    train_data.SalePrice.values.reshape(-1, 1), dtype=torch.float32)
loss = nn.MSELoss()
in_features = train_features.shape[1]

# 数据集被分成了5份,然后执行5次训练和验证过程,每次过程中,4份数据用于训练模型,剩下的一份用于
k, num_epochs, lr, weight_decay, batch_size = 5, 100, 5, 0, 64
# 分别包含每次迭代的训练集和验证集的log rmse值
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}')

输出:

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




提交你的Kaggle预测

既然我们知道应该选择什么样的超参数, 我们不妨使用所有数据对其进行训练 (而不是仅使用交叉验证中使用的 1− 1 K \frac 1K K1 的数据)。 然后,我们通过这种方式获得的模型可以应用于测试集。 将预测保存在CSV文件中可以简化将结果上传到Kaggle的过程。

def train_and_pred(train_features, test_features, train_labels, test_data,
                   num_epochs, lr, weight_decay, batch_size):
    net = get_net()
    # 返回两个值,一个包含每个epoch训练,log mose的列表,一个未在代码片段中使用的值
    train_ls, _ = train(net, train_features, train_labels, None, None,
                        num_epochs, lr, weight_decay, batch_size)
    # 第一个参数和第二个参数:接收一个epoch数组和一个包含训练log rmse的列表作为输入
    d2l.plot(np.arange(1, num_epochs + 1), [train_ls], xlabel='epoch',
             ylabel='log rmse', xlim=[1, num_epochs], yscale='log')
    # 打印最后一个epoch训练log rmse来评估模型的最终训练性能
    print(f'训练log rmse:{float(train_ls[-1]):f}')
    # 将训练好的网络模型应用于测试特征
    preds = net(test_features).detach().numpy()
    # 将其重新格式化以导出到Kaggle
    # pd.Series()封装成一个Pandas的Series对象,以确保数据类型与DataFrame的列兼容。
    test_data['SalePrice'] = pd.Series(preds.reshape(1, -1)[0])
    submission = pd.concat([test_data['Id'], test_data['SalePrice']], axis=1)
    # 用于将DataFrame保存为csv文件
    submission.to_csv('submission.csv', index=False)

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

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

输出:
在这里插入图片描述

在这里插入图片描述

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


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)


def get_net():
    net = nn.Sequential(nn.Linear(in_features, 1))
    return net


# rmse=Root Mean Squared Error,对数均方根误差
def log_rmse(net, features, labels):
    # 通过神经网络net对输入特征features进行前向传播得到预测值
    # 然后为了在取对数时进一步稳定该值,用torch.clamp()函数将小于1的值设置为1
    # 第三个参数:不设置上限,即预测可以无限大
    clipped_preds = torch.clamp(net(features), 1, float('inf'))
    rmse = torch.sqrt(loss(torch.log(clipped_preds),
                           torch.log(labels)))
    # .item() 方法将rmse转换为Python标量返回
    return rmse.item()


# weight_decay权重衰退
def train(net, train_features, train_labels, test_features, test_labels,
          num_epochs, learning_rate, weight_decay, batch_size):
    # 用于存储每一轮训练和测试集上的log rmse值
    train_ls, test_ls = [], []
    train_iter = d2l.load_array((train_features, train_labels), batch_size)
    # 这里使用的是Adam优化算法,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:
            # 清空过往梯度
            optimizer.zero_grad()
            # 通过前向传播计算损失值,然后计算预测值与真实值差异
            l = loss(net(X), y)
            l.backward()  # 反向传播计算当前梯度
            optimizer.step()  # 更新模型参数
        # 将log_rmse函数返回的误差值追加到train_ls列表中就记录了每一轮迭代
        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


def train_and_pred(train_features, test_features, train_labels, test_data,
                   num_epochs, lr, weight_decay, batch_size):
    net = get_net()
    # 返回两个值,一个包含每个epoch训练,log mose的列表,一个未在代码片段中使用的值
    train_ls, _ = train(net, train_features, train_labels, None, None,
                        num_epochs, lr, weight_decay, batch_size)
    # 第一个参数和第二个参数:接收一个epoch数组和一个包含训练log rmse的列表作为输入
    d2l.plot(np.arange(1, num_epochs + 1), [train_ls], xlabel='epoch',
             ylabel='log rmse', xlim=[1, num_epochs], yscale='log')
    # 打印最后一个epoch训练log rmse来评估模型的最终训练性能
    print(f'训练log rmse:{float(train_ls[-1]):f}')
    # 将训练好的网络模型应用于测试特征
    preds = net(test_features).detach().numpy()
    # 将其重新格式化以导出到Kaggle
    # pd.Series()封装成一个Pandas的Series对象,以确保数据类型与DataFrame的列兼容。
    test_data['SalePrice'] = pd.Series(preds.reshape(1, -1)[0])
    submission = pd.concat([test_data['Id'], test_data['SalePrice']], axis=1)
    # 用于将DataFrame保存为csv文件
    submission.to_csv('submission.csv', index=False)


# @save
DATA_HUB = dict()
DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/'
DATA_HUB['kaggle_house_train'] = (DATA_URL + 'kaggle_house_pred_train.csv', '585e9cc9370b9160e7921475fbcd7d31219ce')
DATA_HUB['kaggle_house_test'] = (DATA_URL + 'kaggle_house_pred_test.csv', 'fal9780a7b011d9b009e8bff8e99922a8ee2eb90')
train_data = pd.read_csv(download('kaggle_house_train'))
test_data = pd.read_csv(download('kaggle_house_test'))

# print(train_data.shape) # 1460个样本,80个特征,1个标号label
# print(test_data.shape) # 测试样本没有标号label
# print(train_data.iloc[0:4,[0,1,2,3,-3,-2,-1]]) # 前面四行的某些列特征
all_features = pd.concat((train_data.iloc[:, 1:-1], test_data.iloc[:, 1:]))
# print(all_features.shape)


# 若无法获得测试数据,则可根据训练数据计算均值和标准差
# all_features.dtypes 获取每个特征的数据类型
# all_features.dtypes != 'object' 返回一个布尔值的 Series,其中为 True 的位置表示对应的特征不是对象类型(即数值类型)
# .index 提取数值类型特征的索引,并在numeric_features中存储
# print(all_features.dtypes)  # 可以知道每一列分别为什么类型特征
numeric_features = all_features.dtypes[all_features.dtypes != 'object'].index
# print(numeric_features)
# 对all_features中所有数值类型特征进行标准化处理
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”(缺失值)视为有效的特征值,并为其创建指示符特征
# 未指定column时,pd.get_dummies函数默认转换所有的分类列,包括MSZoning、SaleType、SaleCondition
# pd.get_dummies每个分类变量都会被转换成多列,每个唯一值对应一列
# 理解意思:某列有五类值,就将此列等价于变成五列,然后对应的列置1,不存在的列置0
all_features = pd.get_dummies(all_features, dummy_na=True)

# 获取训练集的行数,即训练样本数量,并将其存储在n_train中
n_train = train_data.shape[0]
# [:n_train].values是选择前n_train作为训练集的特征将数据转换为Numpy数组
train_features = torch.tensor(all_features[:n_train].values, dtype=torch.float32)
test_features = torch.tensor(all_features[n_train:].values, dtype=torch.float32)
# 将SalePrice列单独提取为标签
train_labels = torch.tensor(
    train_data.SalePrice.values.reshape(-1, 1), dtype=torch.float32)
loss = nn.MSELoss()
in_features = train_features.shape[1]

num_epochs, lr, weight_decay, batch_size = 100, 5, 0, 64
train_and_pred(train_features, test_features, train_labels, test_data,
               num_epochs, lr, weight_decay, batch_size)
d2l.plt.show()




提交Kaggle

我们可以提交预测到Kaggle上,并查看在测试集上的预测实际房价(标签)的比较情况。

 ①登录Kaggle网站,访问房价预测竞赛页面。
 ②点击“Submit Predictions”按钮。
 ③点击页面底部虚线框中的“Upload Submission File”按钮,选择要上传的预测文件。
 ④点击页面底部的“Submission”按钮,即可查看结果。

Kaggle注册链接

Kaggle登录链接

房价预测比赛页面(如下图 所示)的"Data"选项卡下可以找到数据集。我们可以通过下面的网址提交预测,并查看排名:

House Prices - Advanced Regression Techniques

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

这里是第一次提交(未优化)

在这里插入图片描述




1、用平均值替换缺失值总是好主意吗?提示:能构造一个不随机丢失值的情况吗?
    用平均值替换缺失值并不总是一个好主意,因为这可能会导致数据失真或者模型表现下降。替代缺失值的方法应该根据数据的特点和缺失值产生的原因来选择。
    比如,一个数据集中的某个属性代表的是某种物质的浓度,这种物质在某个特定温度下会分解,因此在这个温度下所有样本的该属性值都是缺失的。在这种情况下,如果直接用平均值替换缺失值,则可能会导致数据严重失真。


2、通过 𝐾 折交叉验证调整超参数,从而提高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


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)


def get_net():
    net = nn.Sequential(nn.Linear(in_features, 1))
    return net


# rmse=Root Mean Squared Error,对数均方根误差
def log_rmse(net, features, labels):
    # 通过神经网络net对输入特征features进行前向传播得到预测值
    # 然后为了在取对数时进一步稳定该值,用torch.clamp()函数将小于1的值设置为1
    # 第三个参数:不设置上限,即预测可以无限大
    clipped_preds = torch.clamp(net(features), 1, float('inf'))
    rmse = torch.sqrt(loss(torch.log(clipped_preds),
                           torch.log(labels)))
    # .item() 方法将rmse转换为Python标量返回
    return rmse.item()


# weight_decay权重衰退
def train(net, train_features, train_labels, test_features, test_labels,
          num_epochs, learning_rate, weight_decay, batch_size):
    # 用于存储每一轮训练和测试集上的log rmse值
    train_ls, test_ls = [], []
    train_iter = d2l.load_array((train_features, train_labels), batch_size)
    # 这里使用的是Adam优化算法,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:
            # 清空过往梯度
            optimizer.zero_grad()
            # 通过前向传播计算损失值,然后计算预测值与真实值差异
            l = loss(net(X), y)
            l.backward()  # 反向传播计算当前梯度
            optimizer.step()  # 更新模型参数
        # 将log_rmse函数返回的误差值追加到train_ls列表中就记录了每一轮迭代
        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


def train_and_pred(train_features, test_features, train_labels, test_data,
                   num_epochs, lr, weight_decay, batch_size):
    net = get_net()
    # 返回两个值,一个包含每个epoch训练,log mose的列表,一个未在代码片段中使用的值
    train_ls, _ = train(net, train_features, train_labels, None, None,
                        num_epochs, lr, weight_decay, batch_size)
    # 第一个参数和第二个参数:接收一个epoch数组和一个包含训练log rmse的列表作为输入
    d2l.plot(np.arange(1, num_epochs + 1), [train_ls], xlabel='epoch',
             ylabel='log rmse', xlim=[1, num_epochs], yscale='log')
    # 打印最后一个epoch训练log rmse来评估模型的最终训练性能
    print(f'训练log rmse:{float(train_ls[-1]):f}')
    # 将训练好的网络模型应用于测试特征
    preds = net(test_features).detach().numpy()
    # 将其重新格式化以导出到Kaggle
    # pd.Series()封装成一个Pandas的Series对象,以确保数据类型与DataFrame的列兼容。
    test_data['SalePrice'] = pd.Series(preds.reshape(1, -1)[0])
    submission = pd.concat([test_data['Id'], test_data['SalePrice']], axis=1)
    # 用于将DataFrame保存为csv文件
    submission.to_csv('submission.csv', index=False)


# 四个参数:表示将数据集分为多少个子集、当前折的索引、特征数据集、标签数据集
def get_k_fold_data(k, i, X, y):
    # 断言确保折数大于1,因为至少两个子集才能进行交叉验证
    assert k > 1
    # 每个子集(折)的大小=数据集的总行数除以折数,余数在最后一个子集处理
    fold_size = X.shape[0] // k
    # 初始化训练集
    X_train, y_train = None, None
    for j in range(k):
        # 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]
        # 当前折的索引 j 与指定用于验证的索引 i 匹配,将该折的数据分配给验证集(X_valid 和 y_valid)
        if j == i:
            # 将该折的数据分配给验证集
            X_valid, y_valid = X_part, y_part
        # 如不匹配且训练集为空(第一次看到),则将该折的数据分配给训练集(X_train 和 y_train)
        elif X_train is None:
            X_train, y_train = X_part, y_part
        # 如不匹配且训练集不为空,则将该折的数据追加到训练集(X_train 和 y_train)中
        else:
            # 表示将X_train, X_part沿着第一个维度拼接起来
            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


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):
        # 做k次,每次拿到第i折,把数据data拿出来即拿到从训练集和验证集
        data = get_k_fold_data(k, i, X_train, y_train)
        # 每次循环都会创建一个新的模型实例,确保每折的评估是独立的
        net = get_net()
        # 调用train函数训练模型并返回训练损失列表和验证损失列表
        train_ls, valid_ls = train(net, *data, num_epochs, learning_rate,
                                   weight_decay, batch_size)
        # 索引[-1]获取最后一个epoch的损失,即当前折的训练损失累加到train_l_sum
        train_l_sum += train_ls[-1]
        valid_l_sum += valid_ls[-1]
        if i == 0:  # 如果当前是第i折
            # 第一个参数:用作x轴数据
            # 第二个参数:一个列表储存了每一轮训练后的训练损失和验证损失
            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


# @save
DATA_HUB = dict()
DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/'
DATA_HUB['kaggle_house_train'] = (DATA_URL + 'kaggle_house_pred_train.csv', '585e9cc9370b9160e7921475fbcd7d31219ce')
DATA_HUB['kaggle_house_test'] = (DATA_URL + 'kaggle_house_pred_test.csv', 'fal9780a7b011d9b009e8bff8e99922a8ee2eb90')
train_data = pd.read_csv(download('kaggle_house_train'))
test_data = pd.read_csv(download('kaggle_house_test'))

# print(train_data.shape) # 1460个样本,80个特征,1个标号label
# print(test_data.shape) # 测试样本没有标号label
# print(train_data.iloc[0:4,[0,1,2,3,-3,-2,-1]]) # 前面四行的某些列特征
all_features = pd.concat((train_data.iloc[:, 1:-1], test_data.iloc[:, 1:]))
# print(all_features.shape)


# 若无法获得测试数据,则可根据训练数据计算均值和标准差
# all_features.dtypes 获取每个特征的数据类型
# all_features.dtypes != 'object' 返回一个布尔值的 Series,其中为 True 的位置表示对应的特征不是对象类型(即数值类型)
# .index 提取数值类型特征的索引,并在numeric_features中存储
# print(all_features.dtypes)  # 可以知道每一列分别为什么类型特征
numeric_features = all_features.dtypes[all_features.dtypes != 'object'].index
# print(numeric_features)
# 对all_features中所有数值类型特征进行标准化处理
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”(缺失值)视为有效的特征值,并为其创建指示符特征
# 未指定column时,pd.get_dummies函数默认转换所有的分类列,包括MSZoning、SaleType、SaleCondition
# pd.get_dummies每个分类变量都会被转换成多列,每个唯一值对应一列
# 理解意思:某列有五类值,就将此列等价于变成五列,然后对应的列置1,不存在的列置0
all_features = pd.get_dummies(all_features, dummy_na=True)

# 获取训练集的行数,即训练样本数量,并将其存储在n_train中
n_train = train_data.shape[0]
# [:n_train].values是选择前n_train作为训练集的特征将数据转换为Numpy数组
train_features = torch.tensor(all_features[:n_train].values, dtype=torch.float32)
test_features = torch.tensor(all_features[n_train:].values, dtype=torch.float32)
# 将SalePrice列单独提取为标签
train_labels = torch.tensor(
    train_data.SalePrice.values.reshape(-1, 1), dtype=torch.float32)
loss = nn.MSELoss()
in_features = train_features.shape[1]

# 数据集被分成了5份,然后执行5次训练和验证过程,每次过程中,4份数据用于训练模型,剩下的一份用于
k, num_epochs, lr, weight_decay, batch_size = 5, 100, 10, 0, 32
# 分别包含每次迭代的训练集和验证集的log rmse值
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}')
train_and_pred(train_features, test_features, train_labels, test_data,
               num_epochs, lr, weight_decay, batch_size)
d2l.plt.show()

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

在这里插入图片描述




3、通过改进模型(例如,层、权重衰减和dropout)来提高分数。

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


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)


def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)


def get_net():
    net = nn.Sequential(nn.Flatten(),
                        nn.Linear(in_features, 512),
                        nn.ReLU(),
                        nn.Linear(512, 1))
    net.apply(init_weights)
    return net


# rmse=Root Mean Squared Error,对数均方根误差
def log_rmse(net, features, labels):
    # 通过神经网络net对输入特征features进行前向传播得到预测值
    # 然后为了在取对数时进一步稳定该值,用torch.clamp()函数将小于1的值设置为1
    # 第三个参数:不设置上限,即预测可以无限大
    clipped_preds = torch.clamp(net(features), 1, float('inf'))
    rmse = torch.sqrt(loss(torch.log(clipped_preds),
                           torch.log(labels)))
    # .item() 方法将rmse转换为Python标量返回
    return rmse.item()


# weight_decay权重衰退
def train(net, train_features, train_labels, test_features, test_labels,
          num_epochs, learning_rate, weight_decay, batch_size):
    # 用于存储每一轮训练和测试集上的log rmse值
    train_ls, test_ls = [], []
    train_iter = d2l.load_array((train_features, train_labels), batch_size)
    # 这里使用的是Adam优化算法,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:
            # 清空过往梯度
            optimizer.zero_grad()
            # 通过前向传播计算损失值,然后计算预测值与真实值差异
            l = loss(net(X), y)
            l.backward()  # 反向传播计算当前梯度
            optimizer.step()  # 更新模型参数
        # 将log_rmse函数返回的误差值追加到train_ls列表中就记录了每一轮迭代
        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


def train_and_pred(train_features, test_features, train_labels, test_data,
                   num_epochs, lr, weight_decay, batch_size):
    net = get_net()
    # 返回两个值,一个包含每个epoch训练,log mose的列表,一个未在代码片段中使用的值
    train_ls, _ = train(net, train_features, train_labels, None, None,
                        num_epochs, lr, weight_decay, batch_size)
    # 第一个参数和第二个参数:接收一个epoch数组和一个包含训练log rmse的列表作为输入
    d2l.plot(np.arange(1, num_epochs + 1), [train_ls], xlabel='epoch',
             ylabel='log rmse', xlim=[1, num_epochs], yscale='log')
    # 打印最后一个epoch训练log rmse来评估模型的最终训练性能
    print(f'训练log rmse:{float(train_ls[-1]):f}')
    # 将训练好的网络模型应用于测试特征
    preds = net(test_features).detach().numpy()
    # 将其重新格式化以导出到Kaggle
    # pd.Series()封装成一个Pandas的Series对象,以确保数据类型与DataFrame的列兼容。
    test_data['SalePrice'] = pd.Series(preds.reshape(1, -1)[0])
    submission = pd.concat([test_data['Id'], test_data['SalePrice']], axis=1)
    # 用于将DataFrame保存为csv文件
    submission.to_csv('submission.csv', index=False)


# 四个参数:表示将数据集分为多少个子集、当前折的索引、特征数据集、标签数据集
def get_k_fold_data(k, i, X, y):
    # 断言确保折数大于1,因为至少两个子集才能进行交叉验证
    assert k > 1
    # 每个子集(折)的大小=数据集的总行数除以折数,余数在最后一个子集处理
    fold_size = X.shape[0] // k
    # 初始化训练集
    X_train, y_train = None, None
    for j in range(k):
        # 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]
        # 当前折的索引 j 与指定用于验证的索引 i 匹配,将该折的数据分配给验证集(X_valid 和 y_valid)
        if j == i:
            # 将该折的数据分配给验证集
            X_valid, y_valid = X_part, y_part
        # 如不匹配且训练集为空(第一次看到),则将该折的数据分配给训练集(X_train 和 y_train)
        elif X_train is None:
            X_train, y_train = X_part, y_part
        # 如不匹配且训练集不为空,则将该折的数据追加到训练集(X_train 和 y_train)中
        else:
            # 表示将X_train, X_part沿着第一个维度拼接起来
            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


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):
        # 做k次,每次拿到第i折,把数据data拿出来即拿到从训练集和验证集
        data = get_k_fold_data(k, i, X_train, y_train)
        # 每次循环都会创建一个新的模型实例,确保每折的评估是独立的
        net = get_net()
        # 调用train函数训练模型并返回训练损失列表和验证损失列表
        train_ls, valid_ls = train(net, *data, num_epochs, learning_rate,
                                   weight_decay, batch_size)
        # 索引[-1]获取最后一个epoch的损失,即当前折的训练损失累加到train_l_sum
        train_l_sum += train_ls[-1]
        valid_l_sum += valid_ls[-1]
        if i == 0:  # 如果当前是第i折
            # 第一个参数:用作x轴数据
            # 第二个参数:一个列表储存了每一轮训练后的训练损失和验证损失
            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


# @save
DATA_HUB = dict()
DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/'
DATA_HUB['kaggle_house_train'] = (DATA_URL + 'kaggle_house_pred_train.csv', '585e9cc9370b9160e7921475fbcd7d31219ce')
DATA_HUB['kaggle_house_test'] = (DATA_URL + 'kaggle_house_pred_test.csv', 'fal9780a7b011d9b009e8bff8e99922a8ee2eb90')
train_data = pd.read_csv(download('kaggle_house_train'))
test_data = pd.read_csv(download('kaggle_house_test'))

# print(train_data.shape) # 1460个样本,80个特征,1个标号label
# print(test_data.shape) # 测试样本没有标号label
# print(train_data.iloc[0:4,[0,1,2,3,-3,-2,-1]]) # 前面四行的某些列特征
all_features = pd.concat((train_data.iloc[:, 1:-1], test_data.iloc[:, 1:]))
# print(all_features.shape)


# 若无法获得测试数据,则可根据训练数据计算均值和标准差
# all_features.dtypes 获取每个特征的数据类型
# all_features.dtypes != 'object' 返回一个布尔值的 Series,其中为 True 的位置表示对应的特征不是对象类型(即数值类型)
# .index 提取数值类型特征的索引,并在numeric_features中存储
# print(all_features.dtypes)  # 可以知道每一列分别为什么类型特征
numeric_features = all_features.dtypes[all_features.dtypes != 'object'].index
# print(numeric_features)
# 对all_features中所有数值类型特征进行标准化处理
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”(缺失值)视为有效的特征值,并为其创建指示符特征
# 未指定column时,pd.get_dummies函数默认转换所有的分类列,包括MSZoning、SaleType、SaleCondition
# pd.get_dummies每个分类变量都会被转换成多列,每个唯一值对应一列
# 理解意思:某列有五类值,就将此列等价于变成五列,然后对应的列置1,不存在的列置0
all_features = pd.get_dummies(all_features, dummy_na=True)

# 获取训练集的行数,即训练样本数量,并将其存储在n_train中
n_train = train_data.shape[0]
# [:n_train].values是选择前n_train作为训练集的特征将数据转换为Numpy数组
train_features = torch.tensor(all_features[:n_train].values, dtype=torch.float32)
test_features = torch.tensor(all_features[n_train:].values, dtype=torch.float32)
# 将SalePrice列单独提取为标签
train_labels = torch.tensor(
    train_data.SalePrice.values.reshape(-1, 1), dtype=torch.float32)
loss = nn.MSELoss()
in_features = train_features.shape[1]

# 数据集被分成了5份,然后执行5次训练和验证过程,每次过程中,4份数据用于训练模型,剩下的一份用于
k, num_epochs, lr, weight_decay, batch_size = 5, 100, 0.01, 300, 32
# 分别包含每次迭代的训练集和验证集的log rmse值
train_l, valid_l = k_fold(k, train_features, train_labels, num_epochs, lr,
                          weight_decay, batch_size)

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

print(f'{k}-折验证: 平均训练log rmse: {float(train_l):f}, '
      f'平均验证log rmse: {float(valid_l):f}')
d2l.plt.show()

在这里插入图片描述

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




十行代码:

# 预测
import pandas as pd
from autogluon.tabular import TabularDataset, TabularPredictor

# 训练
train_data = TabularDataset('./data/California house price/house-prices-advanced-regression-techniques/train.csv')

id, label = 'Id', 'SalePrice'
predictor = TabularPredictor(label=label).fit(train_data.drop(columns=[id]))

test_data = TabularDataset('./data/California house price/house-prices-advanced-regression-techniques/test.csv')
preds = predictor.predict(test_data.drop(columns=[id]))
submission = pd.DataFrame({id: test_data[id], label: preds})
submission.to_csv('./kaggle_submission/submission_4.csv', index=False)

后面两个代码均为测试,发现并没有那么好。

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



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)


def init_weights(m):
    if isinstance(m, nn.Linear):
        nn.init.normal_(m.weight, std=0.01)


def get_net():
    net = nn.Sequential(
        nn.Flatten(),
        nn.Linear(in_features, 512),
        nn.ReLU(),
        nn.Dropout(p=0.5),  # 添加 Dropout 层
        nn.Linear(512, 256),
        nn.ReLU(),
        nn.Dropout(p=0.5),  # 添加 Dropout 层
        nn.Linear(256, 128),
        nn.ReLU(),
        nn.Dropout(p=0.5),  # 添加 Dropout 层
        nn.Linear(128, 1)
    )
    net.apply(init_weights)
    return net


def log_rmse(net, features, labels):
    clipped_preds = torch.clamp(net(features), 1, float('inf'))
    rmse = torch.sqrt(loss(torch.log(clipped_preds),
                           torch.log(labels)))
    return rmse.item()


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)
    optimizer = torch.optim.Adam(net.parameters(),
                                 lr=learning_rate,
                                 weight_decay=weight_decay)
    scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.7)

    for epoch in range(num_epochs):
        net.train()  # 确保模型处于训练模式
        for X, y in train_iter:
            optimizer.zero_grad()
            l = loss(net(X), y)
            l.backward()
            optimizer.step()

        scheduler.step()  # 更新学习率

        net.eval()  # 切换到评估模式
        with torch.no_grad():
            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


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}')

    net.eval()  # 切换到评估模式
    with torch.no_grad():
        preds = net(test_features).detach().numpy()

    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)


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:
            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


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]
        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


# @save
DATA_HUB = dict()
DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/'
DATA_HUB['kaggle_house_train'] = (DATA_URL + 'kaggle_house_pred_train.csv', '585e9cc9370b9160e7921475fbcd7d31219ce')
DATA_HUB['kaggle_house_test'] = (DATA_URL + 'kaggle_house_pred_test.csv', 'fal9780a7b011d9b009e8bff8e99922a8ee2eb90')
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)

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]

k, num_epochs, lr, weight_decay, batch_size = 5, 100, 0.01, 300, 32
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}')
train_and_pred(train_features, test_features, train_labels, test_data,
               num_epochs, lr, weight_decay, batch_size)
d2l.plt.show()


输出:

在这里插入图片描述

在这里插入图片描述




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


# 数据下载和提取功能
def download(name, cache_dir=os.path.join('..', 'data')):
    """下载一个DATA_HUB中的文件,返回本地文件名"""
    if name not in DATA_HUB:
        raise ValueError(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:
            for chunk in iter(lambda: f.read(1048576), b''):
                sha1.update(chunk)
        if sha1.hexdigest() == sha1_hash:
            return fname

    print(f'正在从 {url} 下载 {fname}...')
    response = requests.get(url, stream=True)
    response.raise_for_status()
    with open(fname, 'wb') as f:
        f.write(response.content)
    return fname


def download_extract(name, folder=None):
    """下载并解压zip/tar文件"""
    fname = download(name)
    base_dir = os.path.dirname(fname)
    ext = os.path.splitext(fname)[1]

    if ext == '.zip':
        with zipfile.ZipFile(fname, 'r') as zip_ref:
            zip_ref.extractall(base_dir)
    elif ext in ('.tar', '.gz'):
        with tarfile.open(fname, 'r') as tar_ref:
            tar_ref.extractall(base_dir)
    else:
        raise ValueError('仅支持zip和tar文件的解压缩')

    return os.path.join(base_dir, folder) if folder else base_dir


# 初始化权重
def init_weights(m):
    if isinstance(m, nn.Linear):
        nn.init.normal_(m.weight, std=0.01)


# 创建网络
def get_net():
    net = nn.Sequential(
        nn.Flatten(),
        nn.Linear(in_features, 1024),  # 增加神经元数量
        nn.ReLU(),
        nn.Dropout(0.3),  # 调整 Dropout 率
        nn.Linear(1024, 512),  # 增加神经元数量
        nn.ReLU(),
        nn.Dropout(0.3),  # 调整 Dropout 率
        nn.Linear(512, 256),
        nn.ReLU(),
        nn.Dropout(0.3),  # 调整 Dropout 率
        nn.Linear(256, 1)
    )
    net.apply(init_weights)
    return net


# 计算对数均方根误差
def log_rmse(net, features, labels):
    clipped_preds = torch.clamp(net(features), 1, float('inf'))
    rmse = torch.sqrt(loss(torch.log(clipped_preds), torch.log(labels)))
    return rmse.item()


# 训练模型
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)
    optimizer = torch.optim.Adam(net.parameters(), lr=learning_rate, weight_decay=weight_decay)
    scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=num_epochs)

    for epoch in range(num_epochs):
        net.train()
        for X, y in train_iter:
            optimizer.zero_grad()
            l = loss(net(X), y)
            l.backward()
            optimizer.step()

        scheduler.step()

        net.eval()
        with torch.no_grad():
            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


# 训练并预测
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:{train_ls[-1]:f}')

    net.eval()
    with torch.no_grad():
        preds = net(test_features).detach().numpy()

    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)


# K折交叉验证数据获取
def get_k_fold_data(k, i, X, y):
    assert k > 1, '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:
            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], dim=0)
            y_train = torch.cat([y_train, y_part], dim=0)

    return X_train, y_train, X_valid, y_valid


# 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]
        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: {train_ls[-1]:.6f}, 验证log rmse: {valid_ls[-1]:.6f}')

    return train_l_sum / k, valid_l_sum / k


# 数据准备
DATA_HUB = {
    'kaggle_house_train': (
    'http://d2l-data.s3-accelerate.amazonaws.com/kaggle_house_pred_train.csv', '585e9cc9370b9160e7921475fbcd7d31219ce'),
    'kaggle_house_test': ('http://d2l-data.s3-accelerate.amazonaws.com/kaggle_house_pred_test.csv',
                          'fal9780a7b011d9b009e8bff8e99922a8ee2eb90')
}
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()).fillna(0)
all_features = pd.get_dummies(all_features, dummy_na=True)

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]

# 设置训练参数
k, num_epochs, lr, weight_decay, batch_size = 5, 100, 0.01, 0, 32

# 执行K折交叉验证
train_l, valid_l = k_fold(k, train_features, train_labels, num_epochs, lr, weight_decay, batch_size)
print(f'{k}-折验证: 平均训练log rmse: {train_l:.6f}, 平均验证log rmse: {valid_l:.6f}')

# 训练模型并生成预测
train_and_pred(train_features, test_features, train_labels, test_data, num_epochs, lr, weight_decay, batch_size)
d2l.plt.tight_layout()  # 调整子图参数,使之填充整个图像区域
d2l.plt.show()

输出:
在这里插入图片描述

在这里插入图片描述




4、如果我们没有像本节所做的那样标准化连续的数值特征,会发生什么?

    可能导致模型训练不稳定、性能下降、泛化能力差:未标准化的特征可能具有不同的尺度,这可能导致某些特征在模型训练过程中权重过大或过小,使得模型的收敛速度变慢,甚至无法收敛,标准化可以使得以确保所有特征对模型的影响是均衡的,从而提高算法的性能、收敛速度以及泛化能力。
    可能导致数值问题:如果数据的特征值范围非常广泛,可能会导致数值溢出或者精度问题。标准化可以减少这些问题的发生,确保计算的稳定性和准确性。
    可能导致特征重要性误判:未标准化的特征可能会导致对特征重要性的误判。模型可能会错误地认为某些特征对目标变量的影响更大,而实际上这只是因为该特征的值范围较大而已。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1990977.html

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

相关文章

LabVIEW液压传动系统

开发了一种高效的液压传动系统,其特点在于采用LabVIEW软件与先进的硬件配合,实现能量的有效回收。此系统主要应用于工业机械中,如工程机械和船机械等,通过优化液压泵和马达的测试台设计,显著提高系统的能效和操作性能。…

华为OD机试 - 最长子字符串的长度(二) (Java 2024 D卷 100分)

华为OD机试 2024D卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试(JAVA)真题(D卷C卷A卷B卷)》。 刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华…

为什么要推荐R语言?欢迎订阅专栏《R 探索临床数据科学》

统计分析的强大支持: R语言最初是为统计分析而设计的,至今仍然在这方面保持领先地位。无论是基础统计、回归分析、时间序列分析还是高级统计建模,R都能提供丰富的函数和包,帮助我们轻松实现各种统计分析,很简单的代码就…

搭建个人博客需要做哪些事

文章目录 前言搭建步骤站点服务器站点域名注册域名ICP 备案公安备案域名解析 博客图床图床是什么图床搭建 博客站点搭建建站工具本地搭建博客部署 站点运营百度收录百度统计 总结 前言 花了几天时间,搭建了一个个人博客,也算是完成了年初立的一个flag&a…

VSCODE调试程序

1、打开 2、具体测试过程 (1)把路径改成真正执行的程序的绝对路径(${workspaceFolder}这个代表就是项目根目录) (2)然后先注释preLauchTask。 (3)重新编译一下文件,make…

全新神经网络:Kolmogorov-Arnold网络更具解释性,有望为物理学家提供新假设

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

FDE Solver 的 enabled 选项是不开放的

FDE Solver 的 enabled 选项是不开放的 正文正文 在 Mode 工程文件中,只能添加一个 FDE Solver,并且,不同于结构组件,对于结构组件,我们通常可以使用如下脚本将其设置为不启用状态。 比如,我们这里有一个三角型结构。 我们通过如下脚本设置其为不启用状态后, CAD 显示…

准确度与精密度:差异和示例

准确度与精密度:差异和示例 文章目录 一、说明二、准确性的定义三、精度的定义四、飞镖板上的准确度与精确度五、如何记住准确度与精确度六、如何测试准确度和精密度 一、说明 当您依赖数据得出结论时,准确度和精确度是测量的关键属性。这两个概念都适…

Git合并多笔提交为一笔

Git合并多笔提交为一笔 1. 背景 在实际项目开发中,我们会基于生产分支拉出很多需求分支,在需求分支开发完成后再将代码合到生产分支,但随着提交次数越来越多,我们在合到生产分支的时候就得一笔一笔的入库,特别麻烦&a…

day14-测试自动化之Selenium的元素操作、浏览器操作等

一、元素操作 1.1.为什么要学习操作元素的方法? 1).需要让脚本模拟用户给指定元素输入值 2).需要让脚本模拟人为删除元素的内容 3).需要让脚本模拟点击操作 1.2.元素常用操作方法 1).click()点击方法 2).send_keys(value) 输入方法 3).clear(…

手表运动报告生成以及手机展示

一.运动报告组成部分 一般一份运动健康的报告包括以下信息: 1.运动轨迹区。2.报告数据区。(运动总体概览,如距离,时长,训练表现等)3.曲线图表区。(心率曲线,海拔曲线,速度,配速曲线) 二.组成部…

PHP + Laravel + RabbitMQ + Redis 实现消息队列 (三) 消费队列在RabbitMQ和redis中的发布和订阅

发布订阅(Pub/Sub) 对于消息队列传统的模式来说,一个消费者消费一条消息,这条消息被消费之后就不会再次被其它的消费者消费。但是在发布订阅模式中,一条消息是可以被多个消费者消费的,这些消费者其实相当于…

SOMEIP_ETS_021:echoINT8

测试目的: 验证DUT在发送和接收INT8参数时,是否能够保持参数的值和顺序不变。 描述 本测试用例旨在检验DUT在处理包含INT8类型参数的SOME/IP消息时,是否能够正确地发送和接收这些参数,并且确保返回的方法响应消息中的INT8参数值…

QT(C#)-QTabWidget修改字体后Tab页显示不完整的解决方法

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1、 前言2、问题示例3、解决方法 1、 前言 最近一段时间学习QT的程序开发,遇到了如标题所说的问题,经过查询和摸索找到了解决方法&#xff…

CSP-J 复赛 模拟题6

1.大小写字母互换: 题目描述 由输入给定一个字符串,你的任务是将原字符串中的大写字母转换成其对应的小写字母,还要将原字符串中的小写字母转换成对应的大写字母,其余字符不变。 输出转换之后得到的新字符串。 输入格式 一行…

Flink开发语言大比拼:Java与Scala怎么选好?

在选择Apache Flink的开发语言时,Java和Scala各有优劣,最合适的选择取决于项目需求、团队技能和偏好。 Apache Flink是一个开源流处理框架,广泛应用于实时数据处理场景,如金融交易监控、网络流量分析和用户行为分析等。Flink支持J…

水泵性能参数详解

水泵性能参数之流量 水泵流量是指水泵单位时间内输送液体的体积或重量,用符号Q来表示,常用单位有m/h、m/s、L/s或t/h。 水泵铭牌上标注的流量是这台泵的设计流量,又称额定流量,水泵在额定流量下运行效率最高。 水泵流量计算公式为…

代码随想录训练营 Day23打卡 回溯算法part02 39. 组合总和 40. 组合总和II 131. 分割回文串

代码随想录训练营 Day23打卡 回溯算法part02 一、 力扣39. 组合总和 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回…

李飞飞亲自撰文,数十名科学家签署联名信,反对加州AI限制法案

AI真的已经危险到要如此监管的地步了吗? 点击访问我的技术博客https://ai.weoknow.comhttps://ai.weoknow.com 在创新的热土硅谷,李飞飞、吴恩达等 AI 科学家正在与监管部门展开一场关于安全与创新的拉锯战。 这场拉锯战的核心是一个名叫 SB-1047 的法案…

云平台部署 FunAudioLLM 语音天花板

FunAudioLLM FunAudioLLM 是阿里开源的语音处理模型,包含 SenseVoice 和 CosyVoice 两个模型。可以实现 5 种语言生成,以及 50 种语言无缝翻译,还能识别语音情绪。 FunAudioLLM:https://github.com/FunAudioLLM CosyVoice开源仓…