【沐神课程 - 动手学深度学习】实战一详解之 Kaggle比赛:预测房价

参考与前言

  1. 课程链接:跟李沐学AI的个人空间_哔哩哔哩_bilibili
  2. 课程主页:https://zh-v2.d2l.ai/chapter_multilayer-perceptrons/kaggle-house-price.html
  3. 相关代码参考:https://www.kaggle.com/alexandreazhang/using-dl-to-predict-housing-prices/notebook

本部分主要用于记录自己做第一次作业的一些感受 主要是emmm 也是第一次看这么仔细,虽然最后还是不如沐神的十行代码 hhhh 但是还是总结一下较好,本来是想弄一弄懂注意力那个算法是咋个计算方式 还有最近看的GRU那几个点,但是沐神太吸引人了 hhh 所以就实战了一下 基本上也算是做了一天左右 emmm

首先遇到的问题:

  1. one hot后太多feature了
  2. learning_rate 一开始直接成0了
  3. 后面有遇到梯度爆炸情况,虽然有weight_decay 但是emm 好像哪里不太对

放张图感受一下 内心一起爆炸

提醒用了wandb 非常好用的一个好东西,这是最后比较好的结果,大概几分钟就训完了,大概就是上面那个爆炸前 因为我设了中间保存模型的点,大概是350处的模型进行评估的:

在这里插入图片描述

全部完整代码在这里:https://gitee.com/kin_zhang/d2l 点进去选择即可

1. 模型

MLP简单版

import torch
import pandas as pd
import torch.nn as nn
from torch.nn import functional as F
from tqdm import tqdm
import numpy as np
from torch.utils import data
import wandb

class MLP(nn.Module):
    def __init__(self, in_features):
        super().__init__()
        self.layer1 = nn.Linear(in_features,256)
        self.layer2 = nn.Linear(256,64)
        self.out = nn.Linear(64,1)
        
    def forward(self, X):
        X = F.relu(self.layer1(X))
        X = F.relu(self.layer2(X))
        return self.out(X)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

注意因为我大概看了我的in_features 最后470多 512不太合适 所以就256了

其他就是forward里面加relu,说实话我最近才意识到 原来激活函数是非线性化的,而relu是其中一个计算开销最小的激活函数。恕我之前都是 直接用就完事了 resnet yyds 😂

Loss选取

和ppt选的一样 但是自己看的时候以kaggle上面说的rmse来看的哈

criterion = nn.MSELoss()
def load_array(data_arrays, batch_size, is_train=True):  #@save
    """Construct a PyTorch data iterator."""
    dataset = data.TensorDataset(*data_arrays)
    return data.DataLoader(dataset, batch_size, shuffle=is_train)

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

2. 数据处理

导入数据

test_data = pd.read_csv('test.csv')
train_data = pd.read_csv('train.csv')
print("train_data and test_data shape",train_data.shape,test_data.shape)

这一步没啥好讲的 emmm 就是这样 read_csv一下就行了

预处理

# 去掉冗余数据
redundant_cols = ['Address', 'Summary', 'City', 'State']
for c in redundant_cols:
    del test_data[c], train_data[c]
    
# 数据预处理
large_vel_cols = ['Lot', 'Total interior livable area', 'Tax assessed value', 'Annual tax amount', 'Listed Price', 'Last Sold Price']
for c in large_vel_cols:
    train_data[c] = np.log(train_data[c]+1)
    test_data[c] = np.log(test_data[c]+1)

预处理是因为有些数字比较大 所以需要取一下log进行数据处理,虽然我并没有对最终的Sold Price进行处理 因为好像试了一下效果不好?

# 查询数字列 ->缺失数据赋0 -> 归一化
numeric_features = all_features.dtypes[all_features.dtypes == 'float64'].index
all_features = all_features.fillna(method='bfill', axis=0).fillna(0)
all_features[numeric_features] = all_features[numeric_features].apply(lambda x: (x - x.mean()) / (x.std()))

首先在one-hot之前,我们需要看一下非数字的object都有哪些并且他们大概有多少个不同的类别:

for in_object in all_features.dtypes[all_features.dtypes=='object'].index:
    print(in_object.ljust(20),len(all_features[in_object].unique()))

输出为:

Type                 174
Heating              2660
Cooling              911
Parking              9913
Bedrooms             278
Region               1259
Elementary School    3568
Middle School        809
High School          922
Flooring             1740
Heating features     1763
Cooling features     596
Appliances included  11290
Laundry features     3031
Parking features     9695

所以呢,我大概就只选了前两个个

features = list(numeric_features)
# 加上类别数相对较少的Type
features.extend(['Type','Bedrooms'])
all_features = all_features[features]

one-hot

这样进入one-hot后的feature也不会让内存爆炸了,是的… 我一开始只顾着删除地址和summary,其他我以为都可以one-hot,直到看到参考的链接才发现噢 原来有这么多类型 emmm:

print('before one hot code',all_features.shape)
all_features = pd.get_dummies(all_features,dummy_na=True)
print('after one hot code',all_features.shape)

n_train = train_data.shape[0]
train_features = torch.tensor(all_features[:n_train].values, dtype=torch.float)
print('train feature shape:', train_features.shape)
test_features = torch.tensor(all_features[n_train:].values, dtype=torch.float)
print('test feature shape:', test_features.shape)
train_labels = torch.tensor(train_data['Sold Price'].values.reshape(-1, 1), dtype=torch.float)
print('train label shape:', train_labels.shape)

输出:

before one hot code (79065, 19)
after one hot code (79065, 470)
train feature shape: torch.Size([47439, 470])
test feature shape: torch.Size([31626, 470])
train label shape: torch.Size([47439, 1])

3. 训练步骤

train function

in_features = train_features.shape[1]
net = MLP(in_features).to(device)

def train(net, train_features, train_labels, test_features, test_labels,
          num_epochs, learning_rate, weight_decay, batch_size):
    wandb.watch(net)
    train_ls, test_ls = [], []
    train_iter = 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 tqdm(range(num_epochs)):
        for X, y in train_iter:
            X, y = X.to(device), y.to(device)
            optimizer.zero_grad()
            outputs = net(X)
            loss = criterion(outputs, y)
            loss.backward()
            optimizer.step()
        record_loss = log_rmse(net.to('cpu'), train_features, train_labels)
        wandb.log({'loss': record_loss,'epoch': epoch})
        train_ls.append(record_loss)
        if (epoch%NUM_SAVE==0 and epoch!=0) or (epoch==num_epochs-1):
            torch.save(net.state_dict(),'checkpoint_'+str(epoch))
            print('save checkpoints on:', epoch, 'rmse loss value is:', record_loss)
        del X, y
        net.to(device)
    wandb.finish()
    return train_ls, test_ls

# 初始化wandb 进行记录
num_epochs, lr, weight_decay, batch_size = 500, 0.005, 0.05, 256
wandb.init(project="kaggle_predict",
           config={ "learning_rate": lr,
                    "weight_decay": weight_decay,
                    "batch_size": batch_size,
                    "total_run": num_epochs,
                    "network": net_list}
          )
print("network:",net)

train

train_ls, valid_ls = train(net, train_features,train_labels,None,None, num_epochs, lr, weight_decay, batch_size)

# 使用现有训练好的net
net.to('cpu')
# 将网络应用于测试集。
preds = net(test_features).detach().numpy()

# 将其重新格式化以导出到Kaggle
test_data['Sold Price'] = pd.Series(preds.reshape(1, -1)[0])
submission = pd.concat([test_data['Id'], test_data['Sold Price']], axis=1)
submission.to_csv('submission.csv', index=False)

4. 结果

正如一开始的图那样,可以看到在400之前 一切都还挺正常,而直接使用 350处的模型文件进行predict,比如这样:就可以大概得到差不多的

# 读取网络参数应用于测试集
net = []
net = MLP(test_features.shape[1])
net.load_state_dict(torch.load('checkpoint_350'))
net.to('cpu')
preds = net(test_features).detach().numpy()
# 将其重新格式化以导出到Kaggle
test_data['Sold Price'] = pd.Series(preds.reshape(1, -1)[0])
submission = pd.concat([test_data['Id'], test_data['Sold Price']], axis=1)
submission.to_csv('submission.csv', index=False)

提交kaggle submission file后即:

在这里插入图片描述

碎碎念

虽然并没有超过… 沐神十行代码的 但是 我觉得是因为沐神用了AutoML 都不用调参,真就啥啥不需要,详情见:10行代码战胜90%数据科学家?_哔哩哔哩_bilibili

不过我感觉差不多就这样了 后面也MLP 也就是再多搞点特征进来,或者融一下transfomer进行解读文本信息等 emmm

  • 21
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 23
    评论
以下是一个完整的kaggle房价预测比赛的代码示例。注意,这只是一个示例,并不是唯一的解决方案。 首先,我们需要导入必要的库: ```python import numpy as np import pandas as pd from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.ensemble import RandomForestRegressor from xgboost import XGBRegressor from sklearn.metrics import mean_squared_error ``` 接下来,我们可以读取训练和测试数据: ```python train_data = pd.read_csv("train.csv") test_data = pd.read_csv("test.csv") ``` 然后,我们可以进行一些基本的数据探索和清理: ```python # 检查缺失值 print(train_data.isnull().sum()) # 删除缺失值过多的列和无关列 train_data.drop(["Alley", "PoolQC", "MiscFeature", "Id"], axis=1, inplace=True) test_data.drop(["Alley", "PoolQC", "MiscFeature", "Id"], axis=1, inplace=True) # 用中位数替换缺失值 train_data["LotFrontage"].fillna(train_data["LotFrontage"].median(), inplace=True) test_data["LotFrontage"].fillna(test_data["LotFrontage"].median(), inplace=True) train_data["MasVnrArea"].fillna(train_data["MasVnrArea"].median(), inplace=True) test_data["MasVnrArea"].fillna(test_data["MasVnrArea"].median(), inplace=True) train_data["GarageYrBlt"].fillna(train_data["GarageYrBlt"].median(), inplace=True) test_data["GarageYrBlt"].fillna(test_data["GarageYrBlt"].median(), inplace=True) # 对于其他缺失值,用众数替换 for column in train_data.columns: if train_data[column].dtype == "object": train_data[column].fillna(train_data[column].mode()[0], inplace=True) test_data[column].fillna(test_data[column].mode()[0], inplace=True) else: train_data[column].fillna(train_data[column].median(), inplace=True) test_data[column].fillna(test_data[column].median(), inplace=True) # 对于分类变量,进行独热编码 train_data = pd.get_dummies(train_data) test_data = pd.get_dummies(test_data) # 确保训练和测试数据的列数相同 train_labels = train_data["SalePrice"] train_data, test_data = train_data.align(test_data, join="inner", axis=1) ``` 接下来,我们可以对数据进行标准化: ```python scaler = StandardScaler() train_data = scaler.fit_transform(train_data) test_data = scaler.transform(test_data) ``` 然后,我们可以将数据拆分为训练和验证集: ```python X_train, X_val, y_train, y_val = train_test_split(train_data, train_labels, test_size=0.2, random_state=42) ``` 接下来,我们可以训练和评估几个不同的模型: ```python # 线性回归模型 lr = LinearRegression() lr.fit(X_train, y_train) lr_preds = lr.predict(X_val) print("Linear Regression RMSE:", np.sqrt(mean_squared_error(y_val, lr_preds))) # 随机森林模型 rf = RandomForestRegressor(n_estimators=100, random_state=42) rf.fit(X_train, y_train) rf_preds = rf.predict(X_val) print("Random Forest RMSE:", np.sqrt(mean_squared_error(y_val, rf_preds))) # XGBoost模型 xgb = XGBRegressor(n_estimators=100, learning_rate=0.1, random_state=42) xgb.fit(X_train, y_train) xgb_preds = xgb.predict(X_val) print("XGBoost RMSE:", np.sqrt(mean_squared_error(y_val, xgb_preds))) ``` 最后,我们可以用最佳模型生成提交文件: ```python # 训练最佳模型 best_model = XGBRegressor(n_estimators=1000, learning_rate=0.05, random_state=42) best_model.fit(train_data, train_labels) # 生成提交文件 test_preds = best_model.predict(test_data) submission = pd.DataFrame({"Id": test["Id"], "SalePrice": test_preds}) submission.to_csv("submission.csv", index=False) ``` 这就是完整的代码示例。请注意,这只是一个可能的解决方案,你可以根据自己的需求和数据进行调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kin-Zhang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值