0.导包
导入本次比赛所需要用到所有包。
import numpy as np
import pandas as pd
import torch
from torch import nn
from d2l import torch as d2l
1.获取数据
先从Kaggle上下载对应的数据包(trian和test),然后利用pd.read_csv读取所需要的数据信息。
train_data = pd.read_csv("C:\\Users\\97332\\Desktop\\kaggle数据集\\house_datas\\house_train.csv")
test_data = pd.read_csv("C:\\Users\\97332\\Desktop\\kaggle数据集\\house_datas\\house_test.csv")
2.数据清洗(处理数据)
2.1合并特征
把训练集和测试集的特征全部取出来,并去掉“Id”和训练集里的“label”。
all_features = pd.concat((train_data.iloc[:,1:-1],test_data.iloc[:,1:]))
2.2数据处理
在本次比赛中,决定房子最终售价的因素有很多,数据表格中有数值,类别等等,所以在建立我们的训练模型之前,我们需要对数据进行预处理。
对于数值类的特征,我们统一将其缩放为零均值和单位方差来标准化数据。缺失值用0来代替。
对于文本类别的特征,采用one-hot编码将其转变为数值变量。缺失值利用“Dummy_na = True”将缺失值视为有效的特征值,并为其创建指示符特征。【这里我还不太理解,就是感觉用了这个就可以把缺失值处理好了】
#把全部特征中的数字特征提取出来
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()))
#把缺失的数字特征换0补全
all_features[numeric_features] = all_features[numeric_features].fillna(0)
#处理文本类的那些离散值和缺失值
all_features = pd.get_dummies(all_features,dummy_na= True)
2.3修改数据类型
把训练特征值,训练目标值以及测试特征值的数据从Pandas格式中提取Numpy格式,并将其转换为张量(Tensor)表示用于训练。
num_train = train_data.shape[0]
train_features = torch.tensor(all_features[:num_train].values,dtype = torch.float32)
test_features = torch.tensor(all_features[num_train:].values,dtype = torch.float32)
train_labels = torch.tensor(train_data.SalePrice.values.reshape(-1,1),dtype = torch.float32)
3.建立训练模型
我们所建立的模型,主要包括两点:1.损失函数——用于对比预测值和真实值,从而对我们的权重和偏差进行更新。2.用什么样的一个模型来装我们所要训练的特征值(输入)。
#损失函数,用nn自带的MSELoss
loss = nn.MSELoss()
#Sequential是一个特殊的容器,这里的代码指的是容器里就只有一个线性回归层用于训练
def get_net():
net = nn.Sequential(nn.Linear(test_features.shape[1],1))
return net
4.设定误差算法
对于我们最后训练出来的模型,我们需要有一个标准去衡量这个模型到底是训练的好还是坏。在这里采用价格预测的对数来衡量差异(Kaggle也用的这个方法哦)。
#返回一个模型的误差(高精度)
def log_rmse(net, features, labels):
clipped_preds = torch.max(net(features), torch.tensor(1.0))
rmse = torch.sqrt(loss(clipped_preds.log(), labels.log()))
return rmse.item()
5.训练方法的设定
模型:神经网络(net)
训练内容:训练集特征(train_features)、训练集标签(train_labels)、测试集特征(test_features)、测试集标签(test_labels)
参数:训练周期(num_epochs)、学习率(learning_rate)、权重衰减(weight_decay)、单次数据大小(batch_size)
将训练误差和测试误差先设为0(这里就是空集合),按设定好的batch_size大小将数据集传入train_iter中,然后利用Adam优化算法(一种平滑的SGD)对数据进行更新。利用损失,梯度求导不断更新权重和偏差。最后将训练好的模型进行误差计算输出。
#返回训练集的误差和测试集误差
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(params=net.parameters(), lr=learning_rate, weight_decay=weight_decay)
for epoch in range(num_epochs):
for X, y in train_iter:
optimizer.zero_grad()
l = loss(net(X), y)
l.backward()
optimizer.step()
train_ls.append(log_rmse(net, train_features, train_labels))
if test_labels is not None:
test_ls.append(log_rmse(net, test_features, test_labels))
return train_ls, test_ls
6.K折交叉验证
6.1设定划分比例
这里写的算法逻辑我实在没有看懂(看视频的时候大家都说沐沐大神的代码写的太漂亮了),最后反正就返回了当前这一折的训练集和验证集。
def get_k_fold_data(k, i, X, y):
# 返回第i折交叉验证时所需要的训练和验证数据
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), dim=0)
y_train = torch.cat((y_train, y_part), dim=0)
return X_train, y_train, X_valid, y_valid
6.2计算每折的误差并求平均
划分好了训练集和验证集之后,就利用之前设定好的模型对每一折的训练集和验证集进行训练,最后分别计算训练集和验证集的误差平均值。
值得一提的是,我们这里最关注的还是平均验证误差。
#返回k折训练集误差的均值和k折验证集误差的均值
def k_fold(k, X_train, y_train, num_epochs,learning_rate, weight_decay, batch_size):
train_l_sum, valid_l_sum = 0, 0
for i in range(k):
data = get_k_fold_data(k, i, X_train, y_train)
net = get_net()
train_ls, valid_ls = train(net, *data, num_epochs, learning_rate,weight_decay, batch_size)
train_l_sum += train_ls[-1]
valid_l_sum += valid_ls[-1]
print(f"折{i+1}:训练误差:{float(train_ls[-1]):f}," f"验证误差:{float(valid_ls[-1]):f}")
return train_l_sum / k, valid_l_sum / k
7.实例化模型训练
7.1利用K折交叉验证调整参数
这一步,我们需要确定上述定义模型中的那些超参数具体要取值多少(这些超参数的取值就决定了你模型的精度)。然后根据所得到的误差值我们又反过去调整超参数的设置,直至调至自己觉得最优的结果,此时所设定的超参数就是本次模型的参数。
注:在这里的训练我们只涉及训练集,不去考虑测试集。
k,num_epochs,lr,weight_decay,batch_size = 6,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}折交叉验证:平均训练误差:{float(train_l):f}," f"平均验证误差:{float(valid_l):f}")
7.2利用定好的参数再次训练训练集
net = get_net()
train_ls, _ =train(net, train_features, train_labels, None,None,
num_epochs, lr, weight_decay, batch_size)
print(f"训练误差:{float(train_ls[-1]):f}")
8.训练测试集
将训练好的网络应用于测试集,得到测试集每一个房子最终的卖价,并将结果转换成csv格式,储存在文件中,方便提交至Kaggle。
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)
9.最终成绩(完结撒花)