简介这份资源面向机器学习入门者与需要完成回归预测实战的开发者围绕经典波士顿房价数据集用Python从零实现BP神经网络完成房价预测。压缩包共2个文件包含1个py脚本与1个csv数据文件整体约8KB其中csv提供14维房屋特征与中位房价目标变量py脚本承载网络搭建与训练全流程。代码覆盖数据读取与预处理、输入层到隐藏层再到输出层的前向传播、以方差为损失函数的误差计算以及基于梯度下降的反向传播权重更新并设置迭代训练与测试集评估环节可输出均方误差、决定系数等指标也支持输入新特征进行价格预测。已有21735人学习下载适合希望理解神经网络底层原理、亲手推导前向与后向传播细节的读者可作为课程作业或入门项目的参考实现。1. 房价预测的BP神经网络实现从一份Excel到能跑的Python代码手里有一份带标签的房价数据表想用BP神经网络跑出预测值这是很多做数据分析的工程师都会碰到的场景。标题里的“房价预测的BP神经网络实现_python代码”核心就三件事把房价这个连续值回归问题建模成神经网络、用反向传播把误差压下去、用Python把整条链路跑通。它适合两类人一类是刚学完神经网络理论、想找一个完整回归案例练手的开发者另一类是在业务侧需要快速搭一个房价估价基线、不追求SOTA但要求可解释可复现的工程师。我见过太多人卡在第一步——数据里混着“120万”“3室2厅”这种字符串直接喂给模型就是一场翻车。这篇笔记按“数据怎么洗、网络怎么搭、参数怎么调、坑在哪”的顺序讲代码可以直接抄参数可以按你的数据规模改。2. 房价数据预处理把脏表格变成网络能吃的浮点矩阵BP神经网络只认数值房价数据里最常见的字段是面积、房间数、楼层、地段评分、成交价。原始表里往往有缺失值、量纲差异巨大面积100多房间数3还有“满五唯一”这种文本标签。不做处理直接训练结果就是损失不收敛或者收敛到一个荒谬的值。2.1 缺失值与异常值的处理顺序先处理异常值再补缺失顺序反了会把异常值当成正常分布去填充。我一般用IQR四分位距法标记异常再决定是截断还是删除。房价数据里“面积0”或“单价99999”这种明显是录入错误直接删比补更安全。import pandas as pd import numpy as np # 读取原始数据假设列名为中文 df pd.read_excel(house_raw.xlsx) # 1. 删除面积或单价为0或负数的记录 df df[(df[面积] 0) (df[单价] 0)] # 2. 用IQR标记面积异常值 Q1 df[面积].quantile(0.25) Q3 df[面积].quantile(0.75) IQR Q3 - Q1 lower Q1 - 1.5 * IQR upper Q3 1.5 * IQR # 超出边界的先打印看看数量再决定删还是截断 outliers df[(df[面积] lower) | (df[面积] upper)] print(f面积异常值数量: {len(outliers)}) df df[(df[面积] lower) (df[面积] upper)] # 3. 数值列缺失用中位数填充类别列缺失用众数填充 num_cols [面积, 房间数, 楼层, 地段评分] cat_cols [装修情况, 是否有电梯] for col in num_cols: df[col] df[col].fillna(df[col].median()) for col in cat_cols: df[col] df[col].fillna(df[col].mode()[0])这段代码的逻辑是先保证数值合理性再处理缺失。IQR的1.5倍是经典阈值如果你的数据分布偏态严重可以放宽到3倍。中位数填充比均值填充更抗异常值这是血泪经验——曾经用均值填充面积结果一套5000平的别墅把整体均值拉高模型对普通住宅的预测全部偏高。2.2 类别特征编码与量纲统一“装修情况”这种文本列用独热编码One-Hot比标签编码更安全因为装修等级之间没有大小关系。量纲统一用StandardScaler把每列变成均值0方差1。注意目标值房价也要缩放否则梯度爆炸。from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.compose import ColumnTransformer # 独热编码类别列标准化数值列 preprocessor ColumnTransformer( transformers[ (num, StandardScaler(), num_cols), (cat, OneHotEncoder(sparse_outputFalse), cat_cols) ]) # 分离特征和目标 X df[num_cols cat_cols] y df[成交价].values.reshape(-1, 1) # 先划分训练测试集再fit防止数据泄露 from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42) X_train_processed preprocessor.fit_transform(X_train) X_test_processed preprocessor.transform(X_test) # 目标值单独标准化 y_scaler StandardScaler() y_train_scaled y_scaler.fit_transform(y_train) y_test_scaled y_scaler.transform(y_test)这里的关键参数是sparse_outputFalse因为后续要转成PyTorch张量稀疏矩阵不支持。random_state42是为了复现你换成别的数字结果会不同但趋势一致。目标值标准化后预测时要用y_scaler.inverse_transform还原回真实房价这一步漏了的话输出就是标准分数完全没法解释。3. 用PyTorch搭一个能收敛的BP网络层数、激活、损失函数怎么选BP神经网络做回归结构不需要太深。房价预测的特征维度通常几十到几百两层隐藏层足够。我见过有人堆到10层结果梯度消失损失卡在0.5下不去。这一章把网络定义、训练循环、早停策略拆开讲。3.1 网络结构定义与初始化输入维度等于预处理后的特征数输出维度是1房价。隐藏层神经元数量按“输入维度×2/3”起步再微调。激活函数用ReLU输出层不加激活回归任务。权重初始化用Kaiming比默认的均匀分布收敛快。import torch import torch.nn as nn class HousePriceNet(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) # 回归输出不加激活 ) # 手动Kaiming初始化 for m in self.net: if isinstance(m, nn.Linear): nn.init.kaiming_normal_(m.weight, nonlinearityrelu) nn.init.zeros_(m.bias) def forward(self, x): return self.net(x) input_dim X_train_processed.shape[1] model HousePriceNet(input_dim) print(f输入维度: {input_dim}, 参数量: {sum(p.numel() for p in model.parameters())})kaiming_normal_的nonlinearityrelu要和实际激活函数一致用错初始化方式会让前几轮损失震荡。参数量打印出来心里有数如果超过训练样本数的十分之一就要考虑减层或加正则。3.2 训练循环与早停策略损失函数用MSELoss优化器用Adam学习率1e-3起步。早停的patience设10到20监控验证集损失。房价数据噪声大训练集损失一直降但验证集反弹是常态早停能省下大量无效训练时间。from torch.utils.data import TensorDataset, DataLoader # 转张量 X_train_t torch.tensor(X_train_processed, dtypetorch.float32) y_train_t torch.tensor(y_train_scaled, dtypetorch.float32) X_test_t torch.tensor(X_test_processed, dtypetorch.float32) y_test_t torch.tensor(y_test_scaled, dtypetorch.float32) train_ds TensorDataset(X_train_t, y_train_t) train_loader DataLoader(train_ds, batch_size32, shuffleTrue) criterion nn.MSELoss() optimizer torch.optim.Adam(model.parameters(), lr1e-3, weight_decay1e-5) best_val_loss float(inf) patience 15 counter 0 for epoch in range(500): model.train() for xb, yb in train_loader: optimizer.zero_grad() pred model(xb) loss criterion(pred, yb) loss.backward() optimizer.step() # 验证 model.eval() with torch.no_grad(): val_pred model(X_test_t) val_loss criterion(val_pred, y_test_t).item() if val_loss best_val_loss: best_val_loss val_loss torch.save(model.state_dict(), best_model.pth) counter 0 else: counter 1 if counter patience: print(f早停于第{epoch}轮, 最佳验证损失: {best_val_loss:.4f}) break if epoch % 50 0: print(fEpoch {epoch}, Val Loss: {val_loss:.4f})weight_decay1e-5是L2正则防止过拟合。batch_size32适合几千条样本样本量过万可以调到64或128。早停保存的是最佳模型不是最后一轮模型这个细节很多人忽略导致最终用的是过拟合版本。3.3 预测结果还原与评估指标训练完加载最佳权重预测测试集用inverse_transform还原成真实房价再算MAE和R²。MAE比RMSE更直观单位是元R²看解释方差比例。model.load_state_dict(torch.load(best_model.pth)) model.eval() with torch.no_grad(): pred_scaled model(X_test_t).numpy() # 还原真实尺度 pred_real y_scaler.inverse_transform(pred_scaled) y_test_real y_scaler.inverse_transform(y_test_scaled) from sklearn.metrics import mean_absolute_error, r2_score mae mean_absolute_error(y_test_real, pred_real) r2 r2_score(y_test_real, pred_real) print(fMAE: {mae:.0f} 元, R²: {r2:.4f})如果R²低于0.6先别调网络回头查数据里有没有泄露特征比如“成交总价”混进了输入或者类别编码是否合理。我一般会画一张预测值vs真实值的散点图看是否集中在45度线附近。4. 房价预测BP网络的避坑与排查5个真实翻车现场这一章记录我在不同数据集上踩过的坑每条按现象、原因、解决写。你如果训练时遇到类似情况直接对号入座。4.1 损失降到某个值就不动了现象训练损失卡在0.3左右验证损失更高加层加神经元都没用。原因目标值没有标准化房价数值在百万级别MSE梯度太大导致权重更新步长失控。解决对y做StandardScaler训练完再逆变换。这个坑我踩过两次第二次是因为换了数据集忘了改。4.2 验证损失比训练损失低很多现象训练损失0.5验证损失0.2看起来模型“泛化很好”。原因训练集和验证集分布不一致常见于按时间划分数据时训练集是去年低价房验证集是今年高价房。解决改用随机划分或者对时间特征做归一化。如果业务要求按时间外推那就要接受验证损失更高的事实别被数字骗了。4.3 预测值全部偏向均值现象不管输入什么特征输出都在均价附近。原因网络欠拟合可能是学习率太小或训练轮数不够。解决先把学习率调到1e-2试几轮看损失是否下降如果下降后震荡再降到1e-3。另外检查ReLU是否把太多神经元置零可以换成LeakyReLU试试。4.4 独热编码后维度爆炸现象地段评分有200个类别独热后特征维度从20变成220训练极慢且过拟合。原因高基数类别特征不适合独热。解决改用目标编码Target Encoding或嵌入层Embedding。目标编码要注意在训练集上计算均值再映射到验证集防止泄露。4.5 早停太早导致欠拟合现象第20轮就早停验证损失还在下降趋势中。原因patience设太小或者验证集太小导致损失波动大。解决patience调到30验证集至少占总样本15%。如果数据量实在少用K折交叉验证取平均验证损失而不是单次划分。5. 让房价预测BP网络更稳的进阶技巧从能跑到好用基础版跑通后想让模型在业务里真正可用还有几个技巧值得试。第一个是特征工程把“房间数/面积”做成一个新特征“户均面积”比单独喂两个特征效果更好。第二个是学习率调度用ReduceLROnPlateau验证损失不降时自动减半比固定学习率稳。第三个是集成训练5个不同初始化的网络预测值取平均MAE通常能降3%到5%。# 学习率调度示例 scheduler torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, modemin, factor0.5, patience5) # 在验证后调用 scheduler.step(val_loss)验证方法上除了MAE和R²我习惯按房价区间分段看误差。比如低价房低于中位数和高价房分别算MAE如果高价房误差明显更大说明模型对高价样本欠拟合可以在损失函数里给高价样本更高权重。具体做法是自定义加权MSE权重与房价成正比。# 加权MSE让高价样本贡献更大梯度 def weighted_mse(pred, target, weight_factor0.5): weights 1.0 weight_factor * torch.abs(target) return torch.mean(weights * (pred - target) ** 2)weight_factor从0.5开始试太大反而会让低价房预测崩掉。这个技巧在房价数据上特别有用因为高价房样本少但业务关注度高。最后说一个我自己的习惯每次调完参数把验证集预测结果和真实值按行导出成CSV人工看前20条。模型指标再好如果预测出一套“面积50平、房间数5”的房子价格是500万那肯定是特征编码出了问题。指标是黑匣子肉眼抽查是后悔药。希望帮到你。本文还有配套的精品资源点击获取