You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kaggle房价预测模型数据集处理异常,RMSE输出始终为负求助

Kaggle房价预测训练异常问题排查与修复

核心问题分析

你遇到的训练后RMSE显示为-0.000000的问题,根源是代码混淆了回归任务和分类任务的核心逻辑,同时存在损失计算、指标计算的错误:

1. 损失函数误用

房价预测是连续值回归任务,你却使用了CrossEntropyLoss——这是用于多分类任务的损失函数,完全不匹配回归场景。该损失在回归数据上会出现异常值,甚至导致计算出负的损失值(显示为-0.000000)。

2. 验证逻辑错误

Trainer类的validate方法用了分类任务的准确率计算逻辑(torch.max(outputs,1)、统计预测等于标签的数量),回归任务不存在"分类正确"的概念,这个指标完全无效。

3. RMSE计算缺失

你打印的"train rmse"直接用了损失函数的输出值,但回归任务的RMSE需要对MSE损失开平方,当前代码没有实现这一步。

4. 模型输出与数据维度不匹配

当outputSize=1时,模型输出是形状为(batch_size,1)的张量,但CrossEntropyLoss要求输入形状为(batch_size, num_classes),且标签是类别索引,这会导致维度不兼容的隐性错误。


修复后的代码

以下是修正后的完整代码,针对上述问题逐一调整:

import torch
import torch.nn as nn
from torch.utils.data import TensorDataset, DataLoader, random_split

class MLP(nn.Module):
    def __init__(self, inputSize=3, outputSize=1, lr=0.01, weight_decay=0):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Flatten(),
            nn.Linear(inputSize, 64),
            nn.ReLU(),
            nn.Linear(64, 32),
            nn.ReLU(),
            nn.Linear(32, outputSize),
        )
        self.lr = lr
        self.weight_decay = weight_decay

    def forward(self, X):
        return self.layers(X).squeeze(dim=1)  # 压缩维度,让输出和标签形状匹配

    def loss(self, y_hat, y):
        fn = nn.MSELoss()  # 回归任务用均方误差损失
        return fn(y_hat, y)

    def configure_optimizers(self):
        return torch.optim.Adam(self.parameters(), lr=self.lr, weight_decay=self.weight_decay)

class Trainer:
    def __init__(self, n_epochs=3):
        self.max_epochs = n_epochs    
        self.epoch_losses = []
        self.val_losses = []

    def fit(self, model, train_loader, val_loader):
        self.data = train_loader
        self.optimizer = model.configure_optimizers()
        self.model = model

        for epoch in range(self.max_epochs):
            avr_epoch_loss = self.fit_epoch()
            print(f'Epoch {epoch} average train loss: {avr_epoch_loss:.4f}')
            self.epoch_losses.append(avr_epoch_loss)

            val_loss = self.validate(model, val_loader)
            print(f'Epoch {epoch} validation loss: {val_loss:.4f}')
            self.val_losses.append(val_loss)

        print("Training process has finished")

    def fit_epoch(self):
        self.model.train()  # 训练模式,启用 dropout/batchnorm 等
        current_loss = 0.0
        all_losses = []
        for i, data in enumerate(self.data):
            inputs, target = data
            self.optimizer.zero_grad()
            outputs = self.model(inputs)
            loss = self.model.loss(outputs, target)
            all_losses.append(loss.item())
            loss.backward()
            self.optimizer.step()
            current_loss += loss.item()
            if i % 500 == 499:
                print('Loss after mini-batch %5d: %.3f' %(i + 1, current_loss / 500))
                current_loss = 0.0

        return sum(all_losses)/len(all_losses)

    def validate(self, model, val_loader):
        model.eval()
        total_loss = 0.0
        total_samples = 0
        with torch.no_grad():
            for data in val_loader:
                inputs, targets = data
                outputs = model(inputs)
                loss = model.loss(outputs, targets)
                total_loss += loss.item() * inputs.size(0)
                total_samples += inputs.size(0)
        return total_loss / total_samples  # 返回验证集平均MSE损失

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.concat((X_train, X_part), dim=0)
            y_train = torch.concat((y_train, y_part), dim=0)
    return X_train, y_train, X_valid, y_valid

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):
        X_train_fold, y_train_fold, X_valid_fold, y_valid_fold = get_k_fold_data(k, i, X_train, y_train)
        mlp_model = MLP(inputSize=X_train.shape[1], outputSize=1, lr=learning_rate, weight_decay=weight_decay)
        trainer = Trainer(num_epochs)

        trainset = TensorDataset(X_train_fold, y_train_fold)
        train_size = int(0.8 * len(trainset))
        val_size = len(trainset) - train_size
        train_dataset, val_dataset = random_split(trainset, [train_size, val_size])

        train_loader = DataLoader(train_dataset, batch_size, shuffle=True, num_workers=1)
        val_loader = DataLoader(val_dataset, batch_size, shuffle=False, num_workers=1)  # 验证集无需 shuffle
        trainer.fit(mlp_model, train_loader, val_loader)

        # 计算RMSE
        with torch.no_grad():
            mlp_model.eval()
            y_train_pred = mlp_model(X_train_fold)
            train_mse = mlp_model.loss(y_train_pred, y_train_fold)
            train_rmse = torch.sqrt(train_mse).item()

            y_valid_pred = mlp_model(X_valid_fold)
            valid_mse = mlp_model.loss(y_valid_pred, y_valid_fold)
            valid_rmse = torch.sqrt(valid_mse).item()

            train_l_sum += train_rmse
            valid_l_sum += valid_rmse

            print(f'fold {i}, train rmse: {train_rmse:.6f}, valid rmse: {valid_rmse:.6f}')

    return train_l_sum / k, valid_l_sum / k

额外优化建议

  • 数据归一化/标准化:房价预测的特征(如面积、房间数)数值范围差异大,必须做标准化处理(例如用sklearn.preprocessing.StandardScaler),否则模型难以收敛。
  • 学习率调整:如果训练损失下降缓慢,可以尝试调整学习率(比如从0.01降到0.001)。
  • 模型结构调整:根据特征数量适当增减隐藏层神经元数量,避免过拟合或欠拟合。

内容的提问来源于stack exchange,提问作者K Y

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 20:52:21