PyTorch深度学习房价预测完整教程

🏠 PyTorch深度学习房价预测完整教程

📚 目录

  1. 项目概述
  2. 环境准备
  3. 理论基础
  4. 数据分析与预处理
  5. 神经网络模型设计
  6. 训练策略与优化
  7. 模型评估与预测
  8. 完整代码实现
  9. 结果分析
  10. 进阶优化

🎯 项目概述

项目目标

使用PyTorch深度学习框架,构建一个多层感知机(MLP)模型来预测加州房价。

数据集信息

  • 训练集: 47,439个房屋样本,41个特征
  • 测试集: 31,626个房屋样本,40个特征
  • 目标变量: 房屋售价(Sold Price)

数据集下载地址:
https://www.kaggle.com/competitions/california-house-prices/data

项目亮点

  • ✅ 完整的数据预处理流程
  • ✅ 科学的特征工程
  • ✅ 稳定的深度学习训练
  • ✅ 实用的模型优化技巧

🔧 环境准备

必要依赖

pip install torch pandas numpy scikit-learn

项目结构

pytorch-deeplearning/kaggle-house-price/
├── data/california-house-prices/
│   ├── train.csv              # 训练数据
│   ├── test.csv               # 测试数据
│   └── sample_submission.csv  # 提交格式样例
├── house_price_predictor_improved.py  # 主要代码
└── 深度学习房价预测教程.md            # 本教程

🧠 理论基础

1. 回归问题

房价预测是典型的回归问题,目标是预测连续数值。

2. 多层感知机(MLP)

输入层 → 隐藏层1 → 隐藏层2 → ... → 输出层
  33  →   256   →   128   → 64 → 32 → 1

3. 核心组件

  • 全连接层: 线性变换 y = Wx + b
  • 激活函数: ReLU f(x) = max(0, x)
  • 批标准化: 稳定训练过程
  • Dropout: 防止过拟合

4. 损失函数

使用均方误差损失(MSE):

MSE = (1/n) * Σ(预测值 - 真实值)²

📊 数据分析与预处理

1. 数据探索

原始特征分析
# 原始数据包含41个字段,包括:
原始字段 = [
    'Id', 'Address', 'Sold Price', 'Summary',  # 基本信息
    'Type', 'Year built', 'Bedrooms', 'Bathrooms',  # 房屋基本属性
    'Elementary School Score', 'Tax assessed value',  # 学区和税务信息
    # ... 更多字段
]
特征筛选策略
  • 删除无用字段: Id, Address, Summary(文本描述)
  • 保留数值特征: 房屋面积、卧室数、学校评分等
  • 保留分类特征: 房屋类型、供暖方式、城市等

2. 缺失值处理

# 数值字段:用中位数填充(比均值更稳健)
numeric_columns.fillna(median_value)

# 字符串字段:用'Unknown'填充
categorical_columns.fillna('Unknown')

3. 异常值处理

# 使用分位数方法过滤极端值
Q1 = target.quantile(0.01)   # 1%分位数: $135,000
Q99 = target.quantile(0.99)  # 99%分位数: $6,625,000

# 保留合理价格范围的房屋
valid_data = data[(target >= Q1) & (target <= Q99)]

4. 特征编码

One-hot编码

对于类别较少的字符串特征:

# 房屋类型: SingleFamily, Condo, Townhouse
Type_encoded = pd.get_dummies(data['Type'], prefix='Type')
# 结果: Type_SingleFamily, Type_Condo, Type_Townhouse
标签编码

对于类别过多的字符串特征:

# 城市: 有500+个不同城市
le = LabelEncoder()
data['City_encoded'] = le.fit_transform(data['City'])
# 结果: 0, 1, 2, ..., 500

5. 特征标准化

# Z-score标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 结果: 均值=0, 标准差=1

6. 目标变量变换

# 对数变换减少偏斜
y_log = np.log1p(y)  # log(1 + y)

🏗️ 神经网络模型设计

1. 网络架构

class HousePricePredictor(nn.Module):
    def __init__(self, input_size=33):
        super().__init__()
        self.model = nn.Sequential(
            # 输入层 → 第一隐藏层
            nn.Linear(33, 256),      # 全连接层
            nn.BatchNorm1d(256),     # 批标准化
            nn.ReLU(),               # ReLU激活
            nn.Dropout(0.4),         # Dropout正则化
            
            # 第一隐藏层 → 第二隐藏层
            nn.Linear(256, 128),
            nn.BatchNorm1d(128),
            nn.ReLU(),
            nn.Dropout(0.3),
            
            # 第二隐藏层 → 第三隐藏层
            nn.Linear(128, 64),
            nn.BatchNorm1d(64),
            nn.ReLU(),
            nn.Dropout(0.2),
            
            # 第三隐藏层 → 第四隐藏层
            nn.Linear(64, 32),
            nn.ReLU(),
            nn.Dropout(0.1),
            
            # 输出层
            nn.Linear(32, 1)         # 回归任务,输出1个数值
        )

2. 设计理念

渐进式神经元减少
  • 33 → 256: 扩展特征表示空间
  • 256 → 128 → 64 → 32: 逐步提取高级特征
  • 32 → 1: 最终回归输出
正则化策略
  • BatchNorm: 稳定训练,加速收敛
  • Dropout: 随机失活,防止过拟合
  • 权重衰减: L2正则化,惩罚大权重

3. 参数统计

总参数量: 52,865个
├── Linear层: 48,609个参数
├── BatchNorm层: 4,256个参数  
└── 内存占用: ~200KB

🚀 训练策略与优化

1. 损失函数选择

criterion = nn.MSELoss()  # 均方误差损失

2. 优化器配置

optimizer = optim.Adam(
    model.parameters(),
    lr=0.0005,          # 学习率: 偏保守
    weight_decay=1e-3   # L2正则化
)

3. 学习率调度

scheduler = optim.lr_scheduler.ReduceLROnPlateau(
    optimizer, 
    mode='min',         # 监控损失最小化
    factor=0.8,         # 衰减因子
    patience=8,         # 耐心值
    verbose=True        # 输出调度信息
)

4. 早停机制

patience = 25           # 允许25轮验证损失不改善
best_val_loss = float('inf')
patience_counter = 0

if val_loss < best_val_loss:
    best_val_loss = val_loss
    patience_counter = 0
    torch.save(model.state_dict(), 'best_model.pth')
else:
    patience_counter += 1
    
if patience_counter >= patience:
    print("早停触发")
    break

5. 训练稳定性技巧

梯度裁剪
# 防止梯度爆炸
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=0.5)
大批量处理
batch_size = 1024  # 大批量提高训练稳定性
数据打乱
train_loader = DataLoader(dataset, shuffle=True)  # 随机打乱训练数据

📈 模型评估与预测

1. 训练监控

# 每10轮输出训练进度
if (epoch + 1) % 10 == 0:
    print(f'Epoch [{epoch+1}/{num_epochs}]')
    print(f'Train Loss: {train_loss:.4f}')
    print(f'Val Loss: {val_loss:.4f}')
    print(f'Learning Rate: {lr:.6f}')

2. 模型评估指标

损失变化
训练损失: 142.73 → 0.92  (显著下降)
验证损失: 76.56 → 0.05   (稳定收敛)
训练过程
Epoch [10/300], Train Loss: 2.6788, Val Loss: 0.1602
Epoch [20/300], Train Loss: 1.8359, Val Loss: 0.0686
Epoch [30/300], Train Loss: 1.1783, Val Loss: 0.0771
...
早停在第 75 轮

3. 预测流程

# 1. 加载最佳模型
model.load_state_dict(torch.load('best_model_improved.pth'))

# 2. 预测测试集
model.eval()
with torch.no_grad():
    predictions = model(X_test)
    
# 3. 反对数变换
predictions = torch.expm1(predictions)  # exp(x) - 1

# 4. 后处理
predictions = np.clip(predictions, min_price, max_price)

💻 完整代码实现

详细代码请参考 house_price_predictor_improved.py 文件,包含:

核心模块

  1. 数据读取与预处理 (50行)
  2. 特征工程 (80行)
  3. 模型定义 (30行)
  4. 训练循环 (70行)
  5. 预测与输出 (20行)

代码特点

  • 📝 详细注释: 每行代码都有中文解释
  • 🛡️ 异常处理: robust的错误处理机制
  • 📊 进度监控: 实时训练状态显示
  • 💾 模型保存: 自动保存最佳模型

📊 结果分析

1. 预测结果统计

预测价格范围: $135,000 - $6,625,000
平均预测价格: $836,028
中位数预测价格: $613,983
样本数量: 31,626个

2. 模型性能

最终验证损失: 0.0477
训练时长: ~2分钟 (CPU)
模型大小: ~200KB
推理速度: ~1000样本/秒

3. 训练稳定性

  • ✅ 无异常震荡
  • ✅ 平滑收敛
  • ✅ 无过拟合
  • ✅ 合理预测范围

🔥 进阶优化

1. 数据增强

# 特征组合
data['price_per_sqft'] = data['Listed Price'] / data['Total interior livable area']
data['school_score_avg'] = (data['Elementary School Score'] + 
                           data['Middle School Score'] + 
                           data['High School Score']) / 3

2. 模型集成

# 多模型平均
models = [model1, model2, model3]
predictions = []
for model in models:
    pred = model(X_test)
    predictions.append(pred)
final_pred = torch.mean(torch.stack(predictions), dim=0)

3. 超参数优化

# 网格搜索或贝叶斯优化
hyperparams = {
    'lr': [0.0001, 0.0005, 0.001],
    'batch_size': [512, 1024, 2048],
    'hidden_size': [128, 256, 512]
}

4. 更高级的架构

# 残差连接
class ResidualBlock(nn.Module):
    def forward(self, x):
        residual = x
        out = self.layers(x)
        return out + residual

# 注意力机制  
class AttentionLayer(nn.Module):
    def forward(self, x):
        attention_weights = F.softmax(self.attention(x), dim=1)
        return x * attention_weights

🎓 学习要点总结

核心概念

  1. 回归 vs 分类: 预测连续值 vs 预测类别
  2. 过拟合 vs 欠拟合: 模型复杂度与泛化能力的平衡
  3. 批标准化: 稳定训练的关键技术
  4. 正则化: Dropout、权重衰减等防止过拟合

实践技巧

  1. 数据预处理: 缺失值、异常值、特征编码
  2. 训练监控: 损失曲线、早停机制
  3. 模型调优: 学习率、批量大小、网络架构
  4. 结果验证: 交叉验证、测试集评估

深度学习最佳实践

  1. 从简单开始: 先用简单模型建立baseline
  2. 逐步优化: 一次只改变一个变量
  3. 可视化分析: 绘制损失曲线、预测分布
  4. 版本控制: 保存每次实验的代码和结果

🔗 参考资源

官方文档

进阶学习

  • 《深度学习》- Ian Goodfellow
  • 《动手学深度学习》- 李沐
  • PyTorch官方教程

数据集来源

  • Kaggle竞赛平台
  • 加州房价数据集

💡 下一步建议

  1. 尝试不同架构: CNN、LSTM、Transformer
  2. 特征工程深化: 更多特征组合和选择
  3. 模型解释性: SHAP值、特征重要性分析
  4. 部署应用: Flask/FastAPI构建预测API
  5. 持续学习: 在线学习、增量训练

🎉 恭喜你完成了PyTorch房价预测的完整学习!

这个项目涵盖了深度学习的核心概念和实践技巧,是进入AI领域的重要基石。继续探索更多有趣的项目吧!

完整代码:

# 导入PyTorch深度学习框架核心模块
import torch
# 导入神经网络模块,包含各种层和激活函数
import torch.nn as nn
# 导入优化器模块,用于参数更新
import torch.optim as optim
# 导入数据加载器,用于批处理数据
from torch.utils.data import DataLoader, TensorDataset
# 导入pandas用于数据处理
import pandas as pd
# 导入numpy用于数值计算
import numpy as np
# 导入标准化和标签编码器
from sklearn.preprocessing import StandardScaler, LabelEncoder
# 导入训练验证集划分函数
from sklearn.model_selection import train_test_split
# 导入警告模块
import warnings
# 忽略所有警告信息
warnings.filterwarnings('ignore')

# 自动选择计算设备:如果有CUDA就用GPU,否则用CPU
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f'使用设备: {device}')

# 定义数据文件路径
train_path = 'data/california-house-prices/train.csv'  # 训练数据路径
test_path = 'data/california-house-prices/test.csv'    # 测试数据路径
submission_path = 'data/california-house-prices/sample_submission.csv'  # 提交样例路径

# 开始读取数据
print("读取数据...")
# 读取训练集CSV文件
train_df = pd.read_csv(train_path)
# 读取测试集CSV文件
test_df = pd.read_csv(test_path)
# 读取提交样例文件,了解输出格式
sample_submission = pd.read_csv(submission_path)

# 输出数据集形状信息
print(f"训练集形状: {train_df.shape}")
print(f"测试集形状: {test_df.shape}")

# 定义有价值的特征字段列表 - 经过筛选,去除无用字段
useful_columns = [
    'Type',                      # 房屋类型(独栋、公寓等)
    'Year built',                # 建造年份
    'Heating',                   # 供暖方式
    'Cooling',                   # 制冷方式
    'Parking',                   # 停车情况
    'Lot',                       # 地块面积
    'Bedrooms',                  # 卧室数量
    'Bathrooms',                 # 浴室数量
    'Full bathrooms',            # 完整浴室数量
    'Total interior livable area',  # 室内居住面积
    'Total spaces',              # 总空间数
    'Garage spaces',             # 车库空间数
    'Region',                    # 地区
    'Elementary School Score',    # 小学评分
    'Elementary School Distance', # 小学距离
    'Middle School Score',       # 中学评分
    'Middle School Distance',    # 中学距离
    'High School Score',         # 高中评分
    'High School Distance',      # 高中距离
    'Flooring',                  # 地板类型
    'Heating features',          # 供暖特征
    'Cooling features',          # 制冷特征
    'Appliances included',       # 包含的家电
    'Laundry features',          # 洗衣设施
    'Parking features',          # 停车特征
    'Tax assessed value',        # 税务评估价值
    'Annual tax amount',         # 年度税费
    'Listed Price',              # 挂牌价格
    'Last Sold Price',           # 上次售价
    'City',                      # 城市
    'Zip',                       # 邮编
    'State'                      # 州
]

# 定义目标变量(我们要预测的房价)
target_column = 'Sold Price'

# 从原始数据中提取特征和目标变量
train_features = train_df[useful_columns].copy()    # 训练集特征
train_target = train_df[target_column].copy()       # 训练集目标(房价)
test_features = test_df[useful_columns].copy()      # 测试集特征
test_ids = test_df['Id'].copy()                     # 测试集ID,用于最终提交

# 输出筛选后的特征数量
print(f"筛选后特征数: {len(useful_columns)}")

# 定义数据预处理函数
def preprocess_data(train_data, test_data):
    """
    数据预处理函数:处理缺失值、one-hot编码等
    
    参数:
    train_data: 训练集特征数据
    test_data: 测试集特征数据
    
    返回:
    处理后的训练集和测试集
    """
    # 合并训练和测试数据,确保预处理的一致性
    all_data = pd.concat([train_data, test_data], ignore_index=True)
    
    # 处理缺失值
    print("处理缺失值...")
    
    # 获取数值型字段列表
    numeric_columns = all_data.select_dtypes(include=[np.number]).columns
    # 对数值字段用中位数填充缺失值(比均值更稳健)
    for col in numeric_columns:
        all_data[col].fillna(all_data[col].median(), inplace=True)
    
    # 获取字符串型字段列表
    categorical_columns = all_data.select_dtypes(include=['object']).columns
    # 对字符串字段用'Unknown'填充缺失值
    for col in categorical_columns:
        all_data[col].fillna('Unknown', inplace=True)
    
    # 对字符串字段进行One-hot编码
    print("One-hot编码字符串字段...")
    categorical_features = []  # 存储one-hot编码后的特征
    
    # 遍历每个字符串字段
    for col in categorical_columns:
        # 如果类别数少于50,使用one-hot编码
        if all_data[col].nunique() < 50:
            # 创建one-hot编码(虚拟变量)
            dummies = pd.get_dummies(all_data[col], prefix=col, dummy_na=False)
            categorical_features.append(dummies)
        else:
            # 如果类别太多,使用标签编码(避免维度爆炸)
            le = LabelEncoder()
            all_data[col + '_encoded'] = le.fit_transform(all_data[col].astype(str))
    
    # 删除原始的字符串列(已经编码)
    all_data = all_data.drop(columns=categorical_columns)
    
    # 如果有one-hot编码的特征,合并到主数据中
    if categorical_features:
        categorical_df = pd.concat(categorical_features, axis=1)
        all_data = pd.concat([all_data, categorical_df], axis=1)
    
    # 重新分离训练和测试数据
    train_processed = all_data[:len(train_data)].copy()
    test_processed = all_data[len(train_data):].copy()
    
    return train_processed, test_processed

# 执行数据预处理
train_processed, test_processed = preprocess_data(train_features, test_features)

# 输出预处理后的特征数量
print(f"预处理后特征数: {train_processed.shape[1]}")

# 处理目标变量中的异常值(极端房价)
print("处理异常值...")
# 计算房价的1%和99%分位数
target_q1 = train_target.quantile(0.01)   # 1%分位数
target_q99 = train_target.quantile(0.99)  # 99%分位数
print(f"价格范围 (1%-99%): ${target_q1:,.2f} - ${target_q99:,.2f}")

# 过滤掉极端异常值,保留1%-99%范围内的数据
valid_indices = (train_target >= target_q1) & (train_target <= target_q99)
train_processed = train_processed[valid_indices]      # 过滤特征数据
train_target_filtered = train_target[valid_indices]   # 过滤目标数据

# 输出过滤后的样本数量
print(f"过滤后训练样本数: {len(train_processed)}")

# 特征标准化(Z-score标准化)
print("标准化特征...")
# 创建标准化器
scaler = StandardScaler()
# 在训练集上拟合并转换(计算均值和标准差)
X_train_scaled = scaler.fit_transform(train_processed)
# 用训练集的均值和标准差转换测试集
X_test_scaled = scaler.transform(test_processed)

# 对目标变量进行对数变换(减少数据偏斜,提高模型性能)
y_train = np.log1p(train_target_filtered.values)  # log1p = log(1+x),避免log(0)

# 划分训练集和验证集(80%训练,20%验证)
X_train, X_val, y_train_split, y_val = train_test_split(
    X_train_scaled, y_train, test_size=0.2, random_state=42
)

# 输出划分后的数据形状
print(f"训练集: {X_train.shape}, 验证集: {X_val.shape}")

# 将numpy数组转换为PyTorch张量,并移动到指定设备
X_train_tensor = torch.FloatTensor(X_train).to(device)      # 训练特征
y_train_tensor = torch.FloatTensor(y_train_split).to(device) # 训练目标
X_val_tensor = torch.FloatTensor(X_val).to(device)          # 验证特征
y_val_tensor = torch.FloatTensor(y_val).to(device)          # 验证目标
X_test_tensor = torch.FloatTensor(X_test_scaled).to(device)  # 测试特征

# 创建PyTorch数据集对象
train_dataset = TensorDataset(X_train_tensor, y_train_tensor)  # 训练数据集
val_dataset = TensorDataset(X_val_tensor, y_val_tensor)        # 验证数据集

# 设置批处理大小(较大的batch size有助于训练稳定性)
batch_size = 1024
# 创建数据加载器
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)   # 训练时打乱数据
val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)      # 验证时不打乱

# 定义神经网络模型类
class HousePricePredictor(nn.Module):
    """
    房价预测神经网络模型
    
    架构:多层感知机(MLP)
    - 5个全连接层,逐渐减少神经元数量
    - 使用批标准化、ReLU激活和Dropout正则化
    """
    def __init__(self, input_size):
        """
        初始化模型
        
        参数:
        input_size: 输入特征数量
        """
        super(HousePricePredictor, self).__init__()
        # 定义网络结构(Sequential:按顺序执行)
        self.model = nn.Sequential(
            # 第一层:输入维度 -> 256
            nn.Linear(input_size, 256),     # 全连接层
            nn.BatchNorm1d(256),            # 批标准化(稳定训练)
            nn.ReLU(),                      # ReLU激活函数
            nn.Dropout(0.4),                # Dropout正则化(防止过拟合)
            
            # 第二层:256 -> 128
            nn.Linear(256, 128),
            nn.BatchNorm1d(128),
            nn.ReLU(),
            nn.Dropout(0.3),
            
            # 第三层:128 -> 64
            nn.Linear(128, 64),
            nn.BatchNorm1d(64),
            nn.ReLU(),
            nn.Dropout(0.2),
            
            # 第四层:64 -> 32
            nn.Linear(64, 32),
            nn.ReLU(),
            nn.Dropout(0.1),
            
            # 输出层:32 -> 1(回归任务,输出单个数值)
            nn.Linear(32, 1)
        )
    
    def forward(self, x):
        """
        前向传播函数
        
        参数:
        x: 输入特征张量
        
        返回:
        模型预测结果
        """
        return self.model(x)

# 创建模型实例
input_size = X_train.shape[1]  # 获取输入特征维度
model = HousePricePredictor(input_size).to(device)  # 创建模型并移动到设备

# 定义损失函数和优化器
criterion = nn.MSELoss()  # 均方误差损失(回归问题常用)
# Adam优化器:自适应学习率,较低的学习率提高稳定性
optimizer = optim.Adam(model.parameters(), lr=0.0005, weight_decay=1e-3)
# 学习率调度器:当验证损失不再下降时自动降低学习率
scheduler = optim.lr_scheduler.ReduceLROnPlateau(
    optimizer, mode='min', factor=0.8, patience=8, verbose=True
)

# 输出训练开始信息
print("开始训练...")
# 计算并输出模型参数总数
print(f"模型参数数量: {sum(p.numel() for p in model.parameters()):,}")

# 设置训练超参数
num_epochs = 300        # 最大训练轮数
best_val_loss = float('inf')  # 记录最佳验证损失
patience = 25           # 早停耐心值(允许验证损失不改善的轮数)
patience_counter = 0    # 早停计数器

# 记录训练历史
train_losses = []  # 训练损失列表
val_losses = []    # 验证损失列表

# 开始训练循环
for epoch in range(num_epochs):
    # ============ 训练阶段 ============
    model.train()  # 设置为训练模式(启用dropout和batch norm的训练行为)
    train_loss = 0.0  # 累计训练损失
    
    # 遍历训练数据批次
    for batch_X, batch_y in train_loader:
        optimizer.zero_grad()  # 清零梯度(PyTorch会累积梯度)
        
        # 前向传播:计算预测值
        outputs = model(batch_X).squeeze()  # squeeze移除多余维度
        
        # 计算损失
        loss = criterion(outputs, batch_y)
        
        # 反向传播:计算梯度
        loss.backward()
        
        # 梯度裁剪:防止梯度爆炸,提高训练稳定性
        torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=0.5)
        
        # 更新参数
        optimizer.step()
        
        # 累计损失
        train_loss += loss.item()
    
    # ============ 验证阶段 ============
    model.eval()  # 设置为评估模式(禁用dropout,batch norm使用移动统计)
    val_loss = 0.0  # 累计验证损失
    
    # 禁用梯度计算(节省内存,加速推理)
    with torch.no_grad():
        # 遍历验证数据批次
        for batch_X, batch_y in val_loader:
            # 前向传播
            outputs = model(batch_X).squeeze()
            # 计算损失
            loss = criterion(outputs, batch_y)
            # 累计损失
            val_loss += loss.item()
    
    # 计算平均损失
    avg_train_loss = train_loss / len(train_loader)  # 训练平均损失
    avg_val_loss = val_loss / len(val_loader)        # 验证平均损失
    
    # 记录损失历史
    train_losses.append(avg_train_loss)
    val_losses.append(avg_val_loss)
    
    # 学习率调度:根据验证损失调整学习率
    scheduler.step(avg_val_loss)
    
    # ============ 早停检查 ============
    if avg_val_loss < best_val_loss:
        # 验证损失改善,更新最佳损失并重置计数器
        best_val_loss = avg_val_loss
        patience_counter = 0
        # 保存当前最佳模型
        torch.save(model.state_dict(), 'best_model_improved.pth')
    else:
        # 验证损失未改善,增加计数器
        patience_counter += 1
    
    # 每10轮输出一次训练进度
    if (epoch + 1) % 10 == 0:
        print(f'Epoch [{epoch+1}/{num_epochs}], Train Loss: {avg_train_loss:.4f}, Val Loss: {avg_val_loss:.4f}, LR: {optimizer.param_groups[0]["lr"]:.6f}')
    
    # 早停检查:如果验证损失长期不改善,提前停止训练
    if patience_counter >= patience:
        print(f'早停在第 {epoch+1} 轮')
        break

# ============ 模型预测 ============
# 加载训练过程中的最佳模型
model.load_state_dict(torch.load('best_model_improved.pth'))

# 在测试集上进行预测
print("在测试集上预测...")
model.eval()  # 设置为评估模式
with torch.no_grad():  # 禁用梯度计算
    # 对测试集进行前向传播
    test_predictions = model(X_test_tensor).squeeze()
    # 反对数变换:将预测值从对数空间转换回原始价格空间
    test_predictions = torch.expm1(test_predictions)  # expm1 = exp(x) - 1
    # 转换为numpy数组
    test_predictions = test_predictions.cpu().numpy()

# 后处理:将预测价格限制在合理范围内(防止异常预测)
test_predictions = np.clip(test_predictions, target_q1, target_q99)

# ============ 生成提交文件 ============
# 创建提交格式的DataFrame
submission = pd.DataFrame({
    'Id': test_ids,                    # 测试集ID
    'Sold Price': test_predictions     # 预测房价
})

# 保存预测结果到CSV文件
submission.to_csv('submission_improved.csv', index=False)
print("预测完成! 结果保存为 submission_improved.csv")

# ============ 结果统计 ============
# 显示预测价格的统计信息
print(f"\n预测价格统计:")
print(f"最小值: ${test_predictions.min():,.2f}")           # 最低预测价格
print(f"最大值: ${test_predictions.max():,.2f}")           # 最高预测价格
print(f"平均值: ${test_predictions.mean():,.2f}")          # 平均预测价格
print(f"中位数: ${np.median(test_predictions):,.2f}")     # 中位数预测价格

# 输出最终模型性能
print(f"\n最终验证损失: {best_val_loss:.4f}")
print(f"提交文件形状: {submission.shape}")

# 显示训练历史概览
if len(train_losses) > 1:
    print(f"\n训练历史:")
    print(f"训练损失: {train_losses[0]:.4f} -> {train_losses[-1]:.4f}")   # 训练损失变化
    print(f"验证损失: {val_losses[0]:.4f} -> {val_losses[-1]:.4f}")       # 验证损失变化 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值