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
相关产品推荐
相关产品推荐

