本文参考了李沐老师的b站深度学习课程 课程链接,使用了线性回归模型,特别适合深度学习初学者。通过阅读本文,你将学会如何用PyTorch训练模型,并掌握一些实用的训练技巧。希望这些内容能对你的深度学习学习有所帮助。
安装pytorch
在命令行输入下面这段指令
pip install pytorch torchvision -i https://pypi.tuna.tsinghua.edu.cn/simple
导入数据集
数据集来自kaggle一个比赛(比赛链接),可以通过链接自己下载数据集:
训练数据集 http://d2l-data.s3-accelerate.amazonaws.com/kaggle_house_pred_train.csv
测试数据集 http://d2l-data.s3-accelerate.amazonaws.com/kaggle_house_pred_test.csv
也可以通过一下这段代码下载
import hashlib
import os
import tarfile
import zipfile
import requests
#@save
DATA_HUB = dict()
DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/'
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)
通过pandas处理数据
import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
train_data = pd.read_csv('/kaggle/input/kaggle_house_pred_train.csv')
test_data = pd.read_csv('/kaggle/input/kaggle_house_pred_test.csv')
train_data是训练集包含47439条数据,40个feature,还有一个是房价(label) 测试集只有也是47439条,40个feature。
在开始建模之前,我们需要对数据进行预处理。 首先,我们将所有缺失的值替换为相应特征的平均值。然后,为了将所有特征放在一个共同的尺度上, 我们通过将特征重新缩放到零均值和单位方差来标准化数据:
中μ和σ分别表示均值和标准差。 现在,这些特征具有零均值和单位方差,即
直观地说,我们标准化数据有两个原因: 首先,它方便优化。 其次,因为我们不知道哪些特征是相关的, 所以我们不想让惩罚分配给一个特征的系数比分配给其他任何特征的系数更大。
将缺失值替换为0,因为已经在上一步中进行了标准化,0在这个上下文中相当于均值
# train_data.loc[:, train_data.columns != 'Sold Price'] # 这行代码用于提取除'Sold Price'外的其他列
# 合并训练数据和测试数据,排除“Sold Price”列,因为这是我们预测的目标变量
all_features = pd.concat((train_data.loc[:, train_data.columns != 'Sold Price'], test_data.iloc[:, 1:]))
# 查看合并后的数据信息,以了解数据的整体情况
all_features.info()
# 将所有缺失的值替换为相应特征的平均值。通过将特征重新缩放到零均值和单位方差来标准化数据
# 首先,确定哪些特征是数值型的,因为我们将对这些特征进行标准化
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,因为已经在上一步中进行了标准化,0在这个上下文中相当于均值
all_features[numeric_features] = all_features[numeric_features].fillna(0)
all_features = all_features[numeric_features[1:]] # 原本第一列是Id,去掉
all_features.info()
<class 'pandas.core.frame.DataFrame'>
Index: 79065 entries, 0 to 31625
Data columns (total 18 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Year built 79065 non-null float64
1 Lot 79065 non-null float64
2 Bathrooms 79065 non-null float64
3 Full bathrooms 79065 non-null float64
4 Total interior livable area 79065 non-null float64
5 Total spaces 79065 non-null float64
6 Garage spaces 79065 non-null float64
7 Elementary School Score 79065 non-null float64
8 Elementary School Distance 79065 non-null float64
9 Middle School Score 79065 non-null float64
10 Middle School Distance 79065 non-null float64
11 High School Score 79065 non-null float64
12 High School Distance 79065 non-null float64
13 Tax assessed value 79065 non-null float64
14 Annual tax amount 79065 non-null float64
15 Listed Price 79065 non-null float64
16 Last Sold Price 79065 non-null float64
17 Zip 79065 non-null float64
dtypes: float64(18)
memory usage: 11.5 MB
将numpy 转换成tensor
# 从pandas格式中提取NumPy格式,并将其转换为张量表示
n_train = train_data.shape[0]#shape获取行、列数,只取行数————获取训练集行数
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['Sold Price'].values.reshape(-1, 1),
dtype=torch.float32)
设计模型
from torch import nn
from torch.utils.data import dataset, DataLoader, TensorDataset
from torch import optim
# 定义一个继承自nn.Module的模型类
class model(nn.Module):
def __init__(self, in_features):
super(model, self).__init__()
### 定义模型 [b,40] ==> [b,1]
self.net = nn.Sequential(nn.Linear(in_features, 1))
def forward(self, x):
##只有一层所以不需要激活函数
return self.net(x)
获取数据集,实例化模型
# 将训练数据和标签封装为数据集
train_datasets = TensorDataset(train_features, train_labels)
# 创建数据加载器,用于迭代加载数据集中的数据
train_data = DataLoader(train_datasets, batch_size=64, shuffle=True)
# 同样的操作应用于测试数据
test_datasets = TensorDataset(test_features)
test_data = DataLoader(test_datasets, batch_size=64, shuffle=True)
# 获取输入特征的数量
in_features = train_features.shape[1]
# 实例化模型
model = model(in_features)
# 定义均方误差损失函数
loss = nn.MSELoss()
# 定义优化器,使用Adam算法
optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=0.01)
# 再次获取输入特征的数量,用于下面的函数
in_features = train_features.shape[1]
定义相关函数
1. 定义获取K折交叉验证数据的函数:它有助于模型选择和超参数调整。 我们首先需要定义一个函数,在K折交叉验证过程中返回第i折的数据(将一个数据集分成k折,k-1折作为训练级 ,1折作为验证集)。 具体地说,它选择第i个切片作为验证数据,其余部分作为训练数据。 注意,这并不是处理数据的最有效方法,如果我们的数据集大得多,会有其他解决办法。
# 定义获取K折交叉验证数据的函数
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
2.房价就像股票价格一样,我们关心的是相对数量,而不是绝对数量。 因此,我们更关心相对误差, 而不是绝对误差。 例如,如果我们在估计一栋房子的价格时, 假设我们的预测偏差了10万美元, 然而那里一栋典型的房子的价值是12.5万美元, 那么模型可能做得很糟糕。 另一方面,如果我们在加州豪宅区的预测出现同样的10万美元的偏差, (在那里,房价中位数超过400万美元) 这可能是一个不错的预测。
解决这个问题的一种方法是用价格预测的对数来衡量差异。 事实上,这也是比赛中官方用来评价提交质量的误差指标。 即将δ for |logy−logy^|≤δ 转换为e−δ≤y^y≤eδ。 这使得预测价格的对数与真实标签价格的对数之间出现以下均方根误差:
# 定义对数均方根误差函数
def log_rmse(preds, labels):
clipped_preds = torch.clamp(preds, 1, float('inf'))
rmse = torch.sqrt(loss(torch.log(clipped_preds), torch.log(labels)))
return rmse.item()
训练模型
# 训练模型
for epochs in range(10):
for batch_id, (x, y) in enumerate(train_data):
# 获取K折交叉验证数据
x_train, y_train, x_test, y_test = get_k_fold_data(5, 0, x, y)
# 前向传播得到预测值
pred = model(x_train)
# 对预测值进行裁剪,确保其值在1到正无穷之间
clipped_preds = torch.clamp(pred, 1, float('inf'))
# 计算损失
l = loss(torch.log(clipped_preds), torch.log(y_train))
# 清零梯度
optimizer.zero_grad()
# 反向传播
l.backward()
# 更新参数
optimizer.step()
# 在测试数据上进行预测
pred = model(x_test)
# 打印当前批次的训练情况
print(f'epoch {epochs + 1}, batch {batch_id}, valid log rmse {log_rmse(pred,y_test):f}')
# 在测试集上进行最终预测
pred = model(test_features)
结语
非常感谢您的阅读!我衷心希望这篇关于使用PyTorch进行线性回归模型训练的博客文章能够对您有所帮助。