以波士顿房价预测为例,演示过拟合问题和解决办法

以波士顿房价预测为例,演示过拟合问题和解决办法

简介:以波士顿房价预测为例,演示过拟合问题和解决办法

  • 作者简介:一名后端开发人员,每天分享后端开发以及人工智能相关技术,行业前沿信息,面试宝典。
  • 座右铭:未来是不可确定的,慢慢来是最快的。
  • 个人主页极客李华-CSDN博客
  • 合作方式:私聊+
  • 这个专栏内容:用最低价格鼓励和博主一起在寒假打卡高频大厂算法题,连续一个月,提升自己的算法实力,为了算法比赛或者春招。
  • 我的CSDN社区:https://bbs.csdn.net/forums/99eb3042821a4432868bb5bfc4d513a8
  • 微信公众号,抖音,b站等平台统一叫做:极客李华,加入微信公众号领取各种编程资料,加入抖音,b站学习面试技巧,职业规划

首先需要在jupyter中安装对应的环境。

pip install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install sklearn -i https://pypi.tuna.tsinghua.edu.cn/simple

1. 数据集介绍

使用Scikit-Learn库中的波士顿房价数据集,该数据集包含了房屋的各种特征以及相应的房价。

2. 模型选择

将使用简单的线性回归模型作为演示的基础模型,并尝试增加模型的复杂度以观察过拟合的情况。

3. 线性回归模型

线性回归模型的数学公式如下:

y = w 0 + w 1 x 1 + w 2 x 2 + . . . + w n x n y = w_0 + w_1x_1 + w_2x_2 + ... + w_nx_n y=w0+w1x1+w2x2+...+wnxn

其中, y y y 是预测的房价, x i x_i xi 是房屋的特征, w i w_i wi 是特征的权重。

4. 实验步骤

a. 加载数据集并拆分训练集和测试集

from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split

# 加载波士顿房价数据集
boston = load_boston()
X = boston.data
y = boston.target

# 将数据集分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

b. 拟合线性回归模型

from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

# 创建并拟合线性回归模型
model = LinearRegression()
model.fit(X_train, y_train)

# 在训练集和测试集上进行预测
train_predictions = model.predict(X_train)
test_predictions = model.predict(X_test)

# 计算均方误差(MSE)
train_mse = mean_squared_error(y_train, train_predictions)
test_mse = mean_squared_error(y_test, test_predictions)

print("训练集 MSE:", train_mse)
print("测试集 MSE:", test_mse)

c. 增加模型复杂度

为了演示过拟合的情况,我们可以增加模型的复杂度,比如使用多项式回归模型。

from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline

# 创建一个带有多项式特征的线性回归模型
degree = 10  # 可以尝试不同的多项式次数
model = make_pipeline(PolynomialFeatures(degree), LinearRegression())

# 拟合模型
model.fit(X_train, y_train)

# 在训练集和测试集上进行预测
train_predictions = model.predict(X_train)
test_predictions = model.predict(X_test)

# 计算均方误差(MSE)
train_mse = mean_squared_error(y_train, train_predictions)
test_mse = mean_squared_error(y_test, test_predictions)

print("训练集 MSE:", train_mse)
print("测试集 MSE:", test_mse)

完整代码

from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline

# 加载波士顿房价数据集
boston = load_boston()
X = boston.data
y = boston.target

# 将数据集分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 创建并拟合线性回归模型
model = LinearRegression()
model.fit(X_train, y_train)

# 在训练集和测试集上进行预测
train_predictions = model.predict(X_train)
test_predictions = model.predict(X_test)

# 计算均方误差(MSE)
train_mse = mean_squared_error(y_train, train_predictions)
test_mse = mean_squared_error(y_test, test_predictions)

print("线性回归模型 - 训练集 MSE:", train_mse)
print("线性回归模型 - 测试集 MSE:", test_mse)

# 创建一个带有多项式特征的线性回归模型
degree = 10  # 可以尝试不同的多项式次数
model_poly = make_pipeline(PolynomialFeatures(degree), LinearRegression())

# 拟合多项式回归模型
model_poly.fit(X_train, y_train)

# 在训练集和测试集上进行预测
train_predictions_poly = model_poly.predict(X_train)
test_predictions_poly = model_poly.predict(X_test)

# 计算多项式回归模型的均方误差(MSE)
train_mse_poly = mean_squared_error(y_train, train_predictions_poly)
test_mse_poly = mean_squared_error(y_test, test_predictions_poly)

print("\n多项式回归模型 - 训练集 MSE:", train_mse_poly)
print("多项式回归模型 - 测试集 MSE:", test_mse_poly)


import matplotlib.pyplot as plt

# 绘制线性回归模型的预测结果与真实结果对比图
plt.figure(figsize=(10, 6))
plt.scatter(y_test, test_predictions, color='blue', label='预测结果')
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=2, color='red', label='理想结果')
plt.xlabel('真实房价')
plt.ylabel('预测房价')
plt.title('线性回归模型预测结果对比图')
plt.legend()
plt.grid(True)
plt.show()

# 绘制多项式回归模型的预测结果与真实结果对比图
plt.figure(figsize=(10, 6))
plt.scatter(y_test, test_predictions_poly, color='blue', label='预测结果')
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=2, color='red', label='理想结果')
plt.xlabel('真实房价')
plt.ylabel('预测房价')
plt.title('多项式回归模型预测结果对比图')
plt.legend()
plt.grid(True)
plt.show()

  • 运行结果
    在这里插入图片描述

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

  • 运行结果分析
  1. 训练集MSE和测试集MSE之间的差异:

    • 在线性回归模型中,训练集MSE和测试集MSE的差异不大,而在多项式回归模型中,训练集MSE非常小,但测试集MSE却非常大。
    • 多项式回归模型在训练集上的MSE接近零,这表明模型可以完美地拟合训练数据,但在测试集上的MSE非常大,这表明模型在未见过的数据上表现很差,这是典型的过拟合现象。
  2. 多项式回归模型的测试集MSE异常地大:

    • 测试集MSE达到了数百万的级别,这说明模型在测试集上的预测结果与真实值之间存在很大的偏差,模型的泛化能力非常差。
  3. 训练集MSE接近零:

    • 多项式回归模型在训练集上的MSE非常接近零,这表明模型可以完美地拟合训练数据,甚至可能过度拟合了训练数据中的噪声和细节。

多项式回归模型在训练集上表现得很好,但在测试集上的表现非常糟糕,这是典型的过拟合现象。这种情况下,模型在训练集上过度拟合了数据,失去了泛化能力,不能很好地适应新的数据。为了解决过拟合问题,可以尝试降低模型的复杂度、增加数据量、进行特征选择或使用正则化等方法。

解决问题代码

常见的方法之一是使用正则化技术,特别是岭回归(Ridge Regression)或Lasso回归(Lasso Regression)。这些方法可以限制模型的复杂度,从而减少过拟合的风险。

岭回归模型和普通的线性回归模型之间的主要区别在于岭回归模型引入了L2正则化项,而普通的线性回归模型没有正则化项。

具体来说,岭回归模型在损失函数中添加了一个L2范数惩罚项,用于惩罚模型系数的大小。这个惩罚项可以防止模型过度拟合训练数据,因为它会使得模型系数更加稳定,减少模型对数据中噪声的敏感性。

岭回归模型的损失函数定义如下:

Loss = MSE + α ∑ i = 1 n β i 2 \text{Loss} = \text{MSE} + \alpha \sum_{i=1}^{n} \beta_i^2 Loss=MSE+αi=1nβi2
其中,MSE表示均方误差, α \alpha α是正则化参数,用于控制正则化的强度, β i \beta_i βi 表示模型的系数。

相比之下,普通的线性回归模型只优化MSE,没有引入正则化项。因此,在训练数据足够多的情况下,普通的线性回归模型可能会出现过拟合的问题。

岭回归模型相对于普通线性回归模型来说,更加稳健,能够更好地处理高维数据和多重共线性,减少模型的过拟合风险。

下面是改进后的代码,采用了岭回归来解决过拟合问题:

from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline

import matplotlib.pyplot as plt

# 加载波士顿房价数据集
boston = load_boston()
X = boston.data
y = boston.target

# 将数据集分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 创建并拟合岭回归模型
alpha = 1.0  # 正则化参数,可以调整
model_ridge = Ridge(alpha=alpha)
model_ridge.fit(X_train, y_train)

# 在训练集和测试集上进行预测
train_predictions_ridge = model_ridge.predict(X_train)
test_predictions_ridge = model_ridge.predict(X_test)

# 计算岭回归模型的均方误差(MSE)
train_mse_ridge = mean_squared_error(y_train, train_predictions_ridge)
test_mse_ridge = mean_squared_error(y_test, test_predictions_ridge)

print("岭回归模型 - 训练集 MSE:", train_mse_ridge)
print("岭回归模型 - 测试集 MSE:", test_mse_ridge)

# 绘制岭回归模型的预测结果与真实结果对比图
plt.figure(figsize=(10, 6))
plt.scatter(y_test, test_predictions_ridge, color='blue', label='预测结果')
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=2, color='red', label='理想结果')
plt.xlabel('真实房价')
plt.ylabel('预测房价')
plt.title('岭回归模型预测结果对比图')
plt.legend()
plt.grid(True)
plt.show()
  • 运行结果
    在这里插入图片描述

  • 运行结果分析

根据您提供的运行结果分析如下:

  • 训练集 MSE: 21.82
  • 测试集 MSE: 24.48

这些结果表明岭回归模型在训练集和测试集上的表现相对接近,测试集的 MSE 稍高于训练集,但差异不大。

  • 训练集 MSE 略低于测试集 MSE,这表明模型在训练集上的预测效果略好于测试集,但差异不大,说明模型具有一定的泛化能力。
    模型的表现是相对一致的,没有出现明显的过拟合或欠拟合现象,岭回归模型在该数据集上表现稳定。
<think>嗯,用户让我用PyTorch构建一个Dataset并实现波士顿房价预测模型训练。首先,我需要确定波士顿房价数据集的结构。波士顿房价数据集通常是一个结构化的数据集,包含多个特征,比如犯罪率、房间数量等,以及目标变量房价。 首先,我应该检查PyTorch是否内置了这个数据集。记得PyTorch的torchvision主要处理图像数据,而波士顿房价可能在别的地方。哦,对,PyTorch本身没有直接提供,但可以通过sklearn的datasets模块获取。所以,可能需要用sklearn加载数据,然后转换成PyTorch的Dataset。 接下来,构建自定义Dataset类。需要继承torch.utils.data.Dataset,并实现__len____getitem__方法。数据预处理部分可能需要标准化,因为不同特征的尺度差异大,这对神经网络训练很重要。标准化可以使用sklearn的StandardScaler来处理特征标签吗?可能特征需要标准化,而标签是否需要?或者可能只对特征标准化,标签可以保持原样或者也标准化,视情况而定。通常在回归问题中,对特征进行标准化,标签可以不做,但有时候标准化标签可能有助于训练。不过预测时需要反标准化,可能麻烦。这里可能用户希望简单处理,所以先对特征做标准化,标签暂时不做。 然后,数据要分割成训练集测试集。使用sklearn的train_test_split,比如按8:2的比分割。分割后,分别创建训练集测试集的Dataset,再用DataLoader加载,设置batch_size,是否要shuffle训练集。 模型结构方面,波士顿房价预测是一个回归问题,所以输出层一个神经元,不使用激活函数。中间层可能需要几个全连接层,比如一个隐藏层,比如13个输入特征,中间层64个神经元,激活函数用ReLU,然后输出层1个神经元。或者更深一些?不过波士顿数据集样本量不大,可能简单的网络更好,防止过拟合。 损失函数选择均方误差MSE,优化器用Adam或者SGD。学习率可能需要调整,比如0.001。 训练循环部分,迭代多个epoch,每个epoch中遍历DataLoader,前向传播,计算loss,反向传播,优化。同时,可以记录训练损失测试损失,观察是否收敛。 验证部分,需要在测试集上评估模型,计算MSE或者其他指标,比如平均绝对误差(MAE)。 可能需要考虑数据预处理的正确性,比如正确地对训练集测试集应用相同的标准化参数,避免数据泄漏。也就是说,应该用训练集的均值标准差来标准化测试集,而不是整个数据集。所以在代码中,应该先分割数据,再对训练数据进行fit_transform,对测试数据只做transform。 然后,自定义Dataset类在初始化时接收特征标签,并在__getitem__中返回对应的张量。要注意转换为float类型,因为房价是连续值。 可能的代码结构: 1. 导入必要的库:torch, sklearn.datasets, train_test_split, StandardScaler, DataLoader等。 2. 加载数据:boston = load_boston(),但可能现在sklearn的load_boston已经被弃用,可能需要从其他来源获取,比如使用pandas或其他库。这时候需要注意,用户可能遇到这个问题,需要提示。如,sklearn的版本问题,可能需要改用fetch_openml('boston', version=1)或者其他方法。这部分可能需要处理一下,避免代码出错。 比如: from sklearn.datasets import fetch_openml data = fetch_openml(name='boston', version=1) X = data.data y = data.target 但需要确认数据的形状类型是否正确。可能X是DataFrame,需要转换为numpy数组,再转换为float32的tensor。 3. 数据预处理:分割为训练集测试集,然后标准化特征。如: X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) 然后创建scaler,用X_train的均值标准差来标准化X_trainX_test: scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) 同样,对标签是否需要标准化?可能不必要,但也可以尝试。比如,如果标准化标签,那么模型预测后需要用scaler.inverse_transform得到原值。这里可能用户希望直接预测原始价格,所以可能不需要。或者如果损失函数较大,可以考虑对标签进行标准化。不过可能保持原样更直观。 4. 创建自定义Dataset: class BostonDataset(Dataset): def __init__(self, features, labels): self.features = torch.tensor(features, dtype=torch.float32) self.labels = torch.tensor(labels, dtype=torch.float32) def __len__(self): return len(self.labels) def __getitem__(self, idx): return self.features[idx], self.labels[idx] 然后,用处理后的数据创建数据集: train_dataset = BostonDataset(X_train_scaled, y_train) test_dataset = BostonDataset(X_test_scaled, y_test) 然后创建DataLoader,设置batch_size,比如32或64。 5. 定义模型: class BostonModel(nn.Module): def __init__(self, input_dim): super().__init__() self.net = nn.Sequential( nn.Linear(input_dim, 64), nn.ReLU(), nn.Linear(64, 32), nn.ReLU(), nn.Linear(32, 1) ) def forward(self, x): return self.net(x) 这里输入维度是特征数量,比如13。可以动态获取。 但需要确保输入维度的正确。比如,data.data.shape[1]是特征数。 6. 初始化模型、损失函数、优化器: model = BostonModel(input_dim=X_train_scaled.shape[1]) criterion = nn.MSELoss() optimizer = torch.optim.Adam(model.parameters(), lr=0.001) 7. 训练循环: num_epochs = 200 train_losses = [] test_losses = [] for epoch in range(num_epochs): model.train() epoch_train_loss = 0 for features, labels in train_loader: optimizer.zero_grad() outputs = model(features) loss = criterion(outputs, labels.unsqueeze(1)) # 因为labels的形状是(batch_size,),而outputs是(batch_size,1) loss.backward() optimizer.step() epoch_train_loss += loss.item() * features.size(0) epoch_train_loss /= len(train_dataset) train_losses.append(epoch_train_loss) model.eval() epoch_test_loss = 0 with torch.no_grad(): for features, labels in test_loader: outputs = model(features) loss = criterion(outputs, labels.unsqueeze(1)) epoch_test_loss += loss.item() * features.size(0) epoch_test_loss /= len(test_dataset) test_losses.append(epoch_test_loss) if (epoch+1) % 10 == 0: print(f'Epoch {epoch+1}/{num_epochs}, Train Loss: {epoch_train_loss:.4f}, Test Loss: {epoch_test_loss:.4f}') 这里需要注意,标签可能需要调整形状,因为模型输出是(batch_size, 1),而原始标签是(batch_size,),所以需要unsqueeze(1)来匹配维度,避免形状不匹配的错误。 8. 最后,评估模型,可能计算在测试集上的MAE或者R²分数。或者在训练过程中记录损失,绘制损失曲线。 可能还需要考虑的问题:数据加载是否正确,数据预处理是否正确,模型是否足够复杂或过于复杂导致过拟合,学习率是否合适,训练epoch是否足够。 此外,波士顿房价数据集可能存在伦理问题,因为该数据集包含可能导致歧视的特征,但用户可能只是需要技术实现,所以可能不需要深入讨论这一点,但可能需要简单提及。 现在需要将这些步骤整理成清晰的步骤,并写出对应的代码,同时解释各部分的作用。可能用户是刚开始学习PyTorch,所以需要详细解释每一步,比如Dataset的构建、DataLoader的使用、模型定义、训练循环等。</think>以下是使用PyTorch实现波士顿房价预测的完整流程,包含数据加载、模型构建训练过程: ```python import torch import torch.nn as nn from torch.utils.data import Dataset, DataLoader from sklearn.datasets import fetch_openml from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # 1. 数据加载与预处理 boston = fetch_openml(name='boston', version=1) X, y = boston.data, boston.target.astype('float32') # 数据集分割 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) # 特征标准化 scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # 2. 自定义Dataset类 class BostonDataset(Dataset): def __init__(self, features, targets): self.features = torch.tensor(features, dtype=torch.float32) self.targets = torch.tensor(targets, dtype=torch.float32) def __len__(self): return len(self.targets) def __getitem__(self, idx): return self.features[idx], self.targets[idx] # 创建DatasetDataLoader train_dataset = BostonDataset(X_train_scaled, y_train.values) test_dataset = BostonDataset(X_test_scaled, y_test.values) batch_size = 32 train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True) test_loader = DataLoader(test_dataset, batch_size=batch_size) # 3. 定义神经网络模型 class HousePricePredictor(nn.Module): def __init__(self, input_dim): super().__init__() self.net = nn.Sequential( nn.Linear(input_dim, 64), nn.ReLU(), nn.Linear(64, 32), nn.ReLU(), nn.Linear(32, 1) ) def forward(self, x): return self.net(x) # 4. 模型初始化 input_dim = X_train_scaled.shape[1] model = HousePricePredictor(input_dim) criterion = nn.MSELoss() optimizer = torch.optim.Adam(model.parameters(), lr=0.001) # 5. 训练循环 num_epochs = 200 best_test_loss = float('inf') for epoch in range(num_epochs): # 训练阶段 model.train() train_loss = 0 for features, labels in train_loader: optimizer.zero_grad() outputs = model(features) loss = criterion(outputs, labels.view(-1, 1)) # 调整labels维度 loss.backward() optimizer.step() train_loss += loss.item() # 验证阶段 model.eval() test_loss = 0 with torch.no_grad(): for features, labels in test_loader: outputs = model(features) loss = criterion(outputs, labels.view(-1, 1)) test_loss += loss.item() # 计算平均损失 train_loss = train_loss / len(train_loader) test_loss = test_loss / len(test_loader) # 保存最佳模型 if test_loss < best_test_loss: best_test_loss = test_loss torch.save(model.state_dict(), 'best_model.pth') # 打印训练进度 if (epoch+1) % 20 == 0: print(f'Epoch [{epoch+1}/{num_epochs}] | ' f'Train Loss: {train_loss:.4f} | ' f'Test Loss: {test_loss:.4f}') # 6. 加载最佳模型进行预测 model.load_state_dict(torch.load('best_model.pth')) model.eval() with torch.no_grad(): sample = torch.tensor(X_test_scaled[0], dtype=torch.float32) prediction = model(sample) print(f"\n预测:") print(f"真实价格: {y_test.values[0]:.1f}k$") print(f"预测价格: {prediction.item():.1f}k$") ``` **代码说明:** 1. **数据准备** - 使用`fetch_openml`加载波士顿房价数据集 - 按8:2比划分训练集/测试集 - 使用`StandardScaler`进行特征标准化(注意:仅用训练集数据拟合scaler) 2. **Dataset构建** - 自定义Dataset类将数据转换为PyTorch张量 - 创建DataLoader实现批量数据加载 3. **网络结构** - 3层全连接网络(13 → 64 → 32 → 1) - 使用ReLU激活函数增强非线性表达能力 - 输出层直接返回预测值(回归任务) 4. **训练配置** - 使用均方误差损失(MSE)作为损失函数 - 采用Adam优化器(学习率0.001) - 保存验证集表现最好的模型参数 5. **训练过程** - 训练200个epoch - 每20个epoch输出一次损失值 - 动态显示训练集测试集的损失变化 6. **模型验证** - 加载最佳模型参数 - 随机选取测试样本进行预测演示 **注意事项:** 1. 实际使用中可能需要调整网络结构(层数、神经元数量) 2. 可以尝试添加正则化(Dropout/L2正则)防止过拟合 3. 学习率批量大小等超参数需要根据实际情况调整 4. 波士顿数据集存在伦理争议,实际商业应用中建议使用其他替代数据集 **扩展改进建议:** - 添加早停(Early Stopping)机制 - 实现交叉验证 - 增加特征工程处理 - 尝试不同的归一化方法 - 添加可视化损失曲线功能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

极客李华

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

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

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

打赏作者

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

抵扣说明:

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

余额充值