简介:本资源是一份面向数据科学初学者与Kaggle入门者的完整房价预测竞赛实战代码包,聚焦机器学习建模全流程,解决结构化数值回归问题。压缩包共13个文件,含7个CSV数据集(train/test/submit等)、5个Python脚本(覆盖随机森林、集成+Boosting、Stacking及神经网络四大模型实现)和1份README.md说明文档,整体仅274KB,轻量易读,便于逐模块调试与复现。已有250人下载学习,适合希望系统掌握特征工程、多模型对比、交叉验证与结果提交流程的学习者。代码结构清晰,以HousePrice_predict-master为根目录,分src子模块组织不同算法方案,每个py文件均包含数据加载、预处理、训练、评估与预测完整逻辑,附带可直接运行的端到端示例,是理解Kaggle经典赛题落地实践的优质参考。
1. 这不是一份“抄就能跑通”的房价预测代码包,而是 Kaggle 新手绕不开的建模闭环训练场
如果你刚下载了kaggle房价预测比赛代码.zip,打开发现一堆.py文件、train.csv和submission.csv模板,却卡在“为什么 RandomForest 的 score 比线性回归还低?”“为什么用 Neural Networks 训练 20 分钟结果反而过拟合?”——这不是代码写错了,而是你正站在 Kaggle 房价预测赛(House Prices - Advanced Regression Techniques)的真实入口:它不考算法炫技,而考特征工程深度、缺失值逻辑一致性、异常值业务可解释性、以及 ensemble stacking 的权重稳定性。这个 ZIP 包里最值得细读的,从来不是model.fit()那一行,而是feature_engineering.py中对LotFrontage的插补策略、preprocessing.py里对MSSubClass的类别编码方式、以及stacking.py中XGBoost与LightGBM预测结果的校准方法。它适合两类人:想用波士顿房价预测练手但发现数据太干净、缺乏真实噪声的初学者;以及已会调参但提交后 LB 排名总比 CV 低 5% 的进阶者——因为这里每一步都暴露着 Kaggle 竞赛最典型的 gap:CV 可控,LB 不可控。
2. 从原始 CSV 到可训练 DataFrame:特征工程不是“标准化+one-hot”,而是重建业务因果链
Kaggle 房价预测赛的数据集(Ames Housing Dataset)有 79 个特征,其中 43 个是类别型,15 个含显著缺失值(如PoolQC缺失率 99.5%),且大量特征存在隐式层级关系(如OverallQual是 1–10 的有序整数,但直接当数值用会丢失“差→中→优”的非线性跃迁)。常见错误是直接pd.get_dummies()+StandardScaler(),结果模型在 LB 上掉点。真正有效的做法,是分三类处理特征,并为每一类绑定业务逻辑。
2.1 数值型特征的“分段归因”处理:拒绝一刀切标准化
GrLivArea(地上生活面积)和YearBuilt(建造年份)都是数值型,但物理意义完全不同。前者服从近似对数正态分布,后者需转换为“房龄”并考虑折旧非线性(例如 1980 年建的房子,2023 年房龄 43 年,但其价值衰减曲线在 20–30 年区间最陡)。正确做法是:
# 对面积类特征取 log,缓解右偏 df['GrLivArea_log'] = np.log1p(df['GrLivArea']) df['TotalBsmtSF_log'] = np.log1p(df['TotalBsmtSF']) # 对年份类特征构造房龄,并添加“是否翻新”交叉项 df['Age'] = 2023 - df['YearBuilt'] df['IsRemodeled'] = (df['YearRemodAdd'] > df['YearBuilt']).astype(int) df['Age_Remod'] = df['Age'] * df['IsRemodeled'] # 翻新后的实际有效年龄提示:
np.log1p(x)比np.log(x)更安全,避免x=0时报错;YearRemodAdd为 0 表示未翻新,需先用fillna(0)处理,否则> YearBuilt会产生 NaN。
2.2 类别型特征的“语义分层”编码:把“None”当作有效状态,而非缺失
GarageType有Attchd,Detchd,BuiltIn,CarPort,Basement,2Types,NA7 个取值。注意:这里的NA不代表“数据缺失”,而是“该房屋无车库”——这是 Ames 数据集明确标注的业务含义。若用sklearn的SimpleImputer填充most_frequent,会把NA错误地等同于其他值,破坏语义。正确流程是:
# 显式声明 NA 为合法类别(共 7 类),再做 OrdinalEncoder garage_categories = ['NA', 'CarPort', 'Basement', 'Attchd', 'Detchd', '2Types', 'BuiltIn'] encoder = OrdinalEncoder(categories=[garage_categories], handle_unknown='use_encoded_value', unknown_value=-1) df['GarageType_enc'] = encoder.fit_transform(df[['GarageType']]) # 对高基数类别(如 Neighborhood)用目标编码(Target Encoding),但需用 CV 折内均值防泄露 def target_encode_cv(df, col, target, cv_folds=5): kf = KFold(n_splits=cv_folds, shuffle=True, random_state=42) encoded = np.zeros(len(df)) for train_idx, val_idx in kf.split(df): train_mean = df.iloc[train_idx].groupby(col)[target].mean() encoded[val_idx] = df.iloc[val_idx][col].map(train_mean).fillna(df[target].mean()) return encoded df['Neighborhood_enc'] = target_encode_cv(df, 'Neighborhood', 'SalePrice')注意:
OrdinalEncoder的categories参数必须显式传入完整有序列表,否则NA可能被排到末尾,导致模型误判其为“最高级车库类型”。
2.3 缺失值的“业务驱动”填充:用领域知识替代统计值
LotFrontage(临街宽度)缺失 16%。简单用median填充会导致同一街区的房屋临街宽度差异过大,违背地理一致性。Ames 数据集文档指出:LotFrontage与LotArea(地块面积)和LotConfig(地块形状)强相关。因此应按Neighborhood+LotConfig分组填充:
# 构造分组键:Neighborhood 决定区域均价,LotConfig 决定几何约束 df['LotFrontage_fill_key'] = df['Neighborhood'] + '_' + df['LotConfig'] # 每组内用 LotArea 的线性回归预测 LotFrontage(因 LotArea 已知,且二者理论正相关) fill_map = {} for key, group in df.groupby('LotFrontage_fill_key'): if len(group) > 5 and group['LotFrontage'].notna().sum() > 3: # 仅对样本足够、有有效值的组建模 valid = group[group['LotFrontage'].notna()] model = LinearRegression() model.fit(valid[['LotArea']], valid['LotFrontage']) fill_map[key] = model # 应用预测 def predict_lotfrontage(row): key = row['LotFrontage_fill_key'] if key in fill_map and pd.isna(row['LotFrontage']): return fill_map[key].predict([[row['LotArea']]])[0] return row['LotFrontage'] df['LotFrontage'] = df.apply(predict_lotfrontage, axis=1)关键点:此方法比
KNNImputer更稳定,因后者在高维稀疏类别特征下易受噪声干扰;且避免了IterativeImputer的收敛不确定性。
3. 模型选型不是堆砌算法,而是构建误差互补的基学习器组合
Kaggle 房价预测赛的 LB 分数天花板约 0.12(RMSLE),单一模型很难突破 0.125。random_forest在 CV 上表现稳健但 LB 波动大;Neural Networks对特征缩放极度敏感,且小数据量下易过拟合;真正有效的策略是让三类模型覆盖不同误差模式:树模型捕获非线性交互,线性模型提供全局趋势锚点,梯度提升模型拟合残差细节。ensem_stacking不是简单平均,而是用元模型学习各基模型的置信度边界。
3.1 基学习器设计:控制方差、暴露偏差,为 stacking 提供差异化信号
每个基模型必须满足:相同 CV 折划分、相同预处理管道、输出一致维度的预测值。以下为三个核心基模型的最小可行配置:
# 1. LightGBM:控制过拟合,强调 feature importance 稳定性 lgb_params = { 'objective': 'regression', 'metric': 'rmse', 'learning_rate': 0.03, 'num_leaves': 31, 'min_data_in_leaf': 20, # 关键!防止单个叶子仅含 1–2 样本 'feature_fraction': 0.8, # 每轮随机选 80% 特征,增强泛化 'bagging_fraction': 0.9, # 行采样,进一步降方差 'seed': 42 } # 2. Ridge Regression:作为线性基准,暴露非线性残差 ridge = Ridge(alpha=10.0) # alpha 越大,L2 正则越强,系数越平滑 # 3. Random Forest:限制深度,避免记忆训练集噪声 rf = RandomForestRegressor( n_estimators=200, max_depth=12, # 深度 >15 时 CV 开始过拟合 min_samples_split=10, # 防止在稀疏叶节点上分裂 random_state=42 )参数说明:
min_data_in_leaf=20是 LightGBM 在房价数据上的经验阈值,低于此值模型开始拟合噪声;max_depth=12对应约 4000 个叶子节点,足够捕获交互但不过载;alpha=10.0的 Ridge 在标准化后使大部分系数趋近于 0.01–0.1,保留主效应同时抑制共线性放大。
3.2 Stacking 元模型训练:用 CV 预测值作为新特征,杜绝数据泄露
Stacking 的致命错误是直接用model.predict(X_train)生成 meta-feature——这导致元模型看到“未来信息”。正确做法是用cross_val_predict在每折上训练基模型并预测验证集:
from sklearn.model_selection import cross_val_predict # 初始化 meta-features 矩阵(n_samples × n_models) meta_X = np.zeros((len(X_train), 3)) # 对每个基模型,用 5 折 CV 生成 out-of-fold 预测 meta_X[:, 0] = cross_val_predict(lgb, X_train, y_train, cv=5, n_jobs=-1) meta_X[:, 1] = cross_val_predict(ridge, X_train, y_train, cv=5, n_jobs=-1) meta_X[:, 2] = cross_val_predict(rf, X_train, y_train, cv=5, n_jobs=-1) # 元模型:用 ElasticNet 平衡 Ridge 和 Lasso,自动筛选有效基模型 meta_model = ElasticNet(alpha=0.1, l1_ratio=0.5, random_state=42) meta_model.fit(meta_X, y_train)逻辑说明:
cross_val_predict内部自动完成“训练折 fit → 验证折 predict”,确保每个样本的 meta-feature 仅由其他 4 折数据训练得到;ElasticNet的l1_ratio=0.5使其兼具 Ridge 的稳定性与 Lasso 的稀疏性,若某基模型在 CV 中表现不稳定(如 RF 的预测值方差大),其系数会被自动压缩至接近 0。
3.3 提交前校准:用训练集分布修正测试集预测偏差
Stacking 后常出现整体预测偏高或偏低(如所有预测值比真实值高 5%)。这是因为元模型在训练集上优化 RMSLE,但测试集分布略有漂移。简单有效的校准是:
# 计算训练集上 meta_model 预测 vs 真实值的 ratio 分布 train_pred = meta_model.predict(meta_X) ratio = y_train / train_pred # 注意:此处用除法,因 RMSLE 对数尺度下 ratio 更稳定 calibration_factor = np.median(ratio) # 用中位数抗离群点 # 应用校准 test_meta_X = np.column_stack([ lgb.predict(X_test), ridge.predict(X_test), rf.predict(X_test) ]) test_pred = meta_model.predict(test_meta_X) * calibration_factor # 输出 submission.csv(注意:Kaggle 要求 SalePrice > 0,且用 RMSLE 评估) submission = pd.DataFrame({'Id': test_ids, 'SalePrice': np.expm1(test_pred)}) submission.to_csv('submission.csv', index=False)关键点:
np.expm1(x)是exp(x) - 1,对应训练时对SalePrice做的log1p反变换;calibration_factor用中位数而非均值,因房价分布右偏,均值易被高价房拉高。
4. 验证 stacking 稳定性的 3 个硬指标:不只是看 CV 分数
一个看似 CV 得分很高的 stacking pipeline,可能在 LB 上崩盘。真正可靠的验证,必须检查以下三个不可见但决定成败的指标。它们无法直接从score()方法获取,需手动计算。
4.1 基模型预测的相关系数矩阵:确保误差正交性
如果LightGBM和RandomForest的预测高度相关(|r| > 0.95),stacking 就只是加权平均,无法提升上限。计算并可视化:
import seaborn as sns base_preds = { 'LGB': cross_val_predict(lgb, X_train, y_train, cv=5), 'Ridge': cross_val_predict(ridge, X_train, y_train, cv=5), 'RF': cross_val_predict(rf, X_train, y_train, cv=5) } corr_matrix = pd.DataFrame(base_preds).corr(method='spearman') # Spearman 更鲁棒 plt.figure(figsize=(5, 4)) sns.heatmap(corr_matrix, annot=True, cmap='RdBu_r', center=0, square=True, cbar_kws={"shrink": .8}) plt.title('Base Models Prediction Correlation (Spearman)') plt.show()| 指标 | 合格阈值 | 问题表现 | 修复方向 |
|---|---|---|---|
LGBvsRidge相关系数 | < 0.7 | 两者都过度拟合线性趋势 | 给 Ridge 加更强正则(alpha=50),或给 LGB 加feature_fraction=0.6 |
RFvsLGB相关系数 | < 0.85 | 树模型结构相似 | 降低 RFmax_depth至 8,或提高 LGBmin_data_in_leaf至 30 |
| 所有两两相关系数均值 | < 0.75 | 整体多样性不足 | 引入第四个基模型,如CatBoost(对类别特征原生支持) |
4.2 meta-feature 的分布偏移检测:识别训练/测试集不匹配
用KS-test检验每个 meta-feature 在训练集和测试集上的分布是否一致:
from scipy.stats import kstest for i, name in enumerate(['LGB', 'Ridge', 'RF']): stat, p_value = kstest(meta_X[:, i], test_meta_X[:, i]) print(f"{name} meta-feature KS test: statistic={stat:.4f}, p-value={p_value:.4f}") # p-value < 0.05 表示分布显著不同,需检查该模型在测试集上的特征工程一致性典型问题:若
Ridge的 meta-feature p-value = 0.002,说明测试集上Ridge预测值整体右偏——大概率是测试集Neighborhood_enc目标编码时用了全局均值,而非 CV 折内均值,导致编码泄露。
4.3 单模型 LB-CV Gap 分析:定位最大拖累项
对每个基模型单独提交,记录 LB 分数与 CV 分数的差值(Gap):
| Model | CV RMSLE | LB RMSLE | Gap | 主要原因 |
|---|---|---|---|---|
| LightGBM | 0.1221 | 0.1248 | +0.0027 | categorical_feature未显式声明,导致类别特征被当数值处理 |
| Ridge | 0.1315 | 0.1302 | -0.0013 | 线性模型对测试集长尾价格更鲁棒 |
| RandomForest | 0.1238 | 0.1285 | +0.0047 | max_features='sqrt'导致特征随机性过高,CV 过于乐观 |
行动项:Gap > +0.003 的模型,必须回查其
fit()时的参数和predict()时的输入特征 dtype——90% 的 case 是测试集某列被误转为float64(如MSSubClass本应为category)。
5. 用kaggle submit命令行提交前的 5 个必检项:避免因格式失败丢分
Kaggle 提交系统对submission.csv极其严格。即使模型完美,一个空格或 ID 顺序错乱都会导致Submission failed: Invalid format。以下是本地验证脚本,运行后无输出即表示合格:
# 保存为 validate_submission.sh,chmod +x 后执行 #!/bin/bash SUB_FILE="submission.csv" TRAIN_FILE="../input/train.csv" # 假设训练集在同一目录 # 1. 检查列名是否精确为 Id,SalePrice(逗号分隔,无空格) if ! head -1 "$SUB_FILE" | grep -q "^Id,SalePrice$"; then echo "ERROR: Header must be exactly 'Id,SalePrice'" exit 1 fi # 2. 检查行数是否等于测试集样本数(Kaggle 测试集固定 1459 行) TEST_ROWS=$(wc -l < "$SUB_FILE") if [ "$TEST_ROWS" -ne "1460" ]; then # +1 for header echo "ERROR: Expected 1460 lines (1 header + 1459 data), got $TEST_ROWS" exit 1 fi # 3. 检查 Id 是否严格递增且无重复 if ! awk -F, 'NR>1 {if ($1 <= prev) exit 1; prev=$1}' "$SUB_FILE"; then echo "ERROR: Id column not strictly increasing" exit 1 fi # 4. 检查 SalePrice 是否全为正数且无 NaN if ! awk -F, 'NR>1 {if ($2 <= 0 || $2 != $2) exit 1}' "$SUB_FILE"; then echo "ERROR: SalePrice must be > 0 and not NaN" exit 1 fi # 5. 检查是否与训练集 Id 无重叠(Kaggle 测试集 Id 从 1461 开始) TRAIN_IDS=$(awk -F, 'NR>1 {print $1}' "$TRAIN_FILE" | sort -n | tail -1) if [ "$TRAIN_IDS" -ge "1461" ]; then echo "WARNING: Training set max Id ($TRAIN_IDS) >= 1461, may indicate data leak" fi echo "✅ Submission file validation passed."执行命令:
bash validate_submission.sh && kaggle competitions submit -c house-prices-advanced-regression-techniques -f submission.csv -m "stacking_v3_lgb_ridge_rf"
注意:-c后必须是竞赛短名house-prices-advanced-regression-techniques,可在 Kaggle 竞赛页 URL 中找到;-m的消息不要含空格或特殊字符,否则 CLI 解析失败。
最后,真正的竞争力不来自 ZIP 包里的某行代码,而来自你修改feature_engineering.py后重新跑通整个 pipeline 的 7 次迭代——第 1 次解决缺失值,第 3 次调平基模型相关性,第 5 次校准分布偏移,第 7 次才让 LB 与 CV gap 缩小到 0.0015 以内。
本文还有配套的精品资源,点击获取