地址:House Prices - Advanced Regression Techniques | Kaggle

一、任务要求

数据集包含79个特征,你的任务是预测每套房子的售价。对于测试集中的每个Id,你必须预测SalePrice变量的值。

二、模型评估指标

提交的评估基于预测价值的对数与观测销售价格的对数之间的均方根误差(RMSE)。(取对数意味着预测昂贵和便宜房屋的误差对结果的影响是相等的。)

三、数据集描述

train.csv - 训练集

test.csv - 测试集

data_description.txt - 特征属性的完整介绍

sample_submission.csv - 提交文件实例

四、数据属性解释

SalePrice - 房产的销售价格(美元)。这是您试图预测的目标变量。
MSSubClass: 建筑类别
MSZoning: 一般分区分类
LotFrontage: 与房产相连的街道线性英尺数
LotArea: 地块大小(平方英尺)
Street: 道路通行类型
Alley: 小巷通行类型
LotShape: 房产的一般形状
LandContour: 房产的平整度
Utilities: 可用公用设施类型
LotConfig: 地块配置
LandSlope: 房产坡度
Neighborhood: 埃姆斯市范围内的物理位置
Condition1: 靠近主干道或铁路的程度
Condition2: 靠近主干道或铁路的程度(如果有第二条)
BldgType: 住宅类型
HouseStyle: 住宅风格
OverallQual: 整体材料和装修质量
OverallCond: 整体状况评级
YearBuilt: 原始建造日期
YearRemodAdd: 翻新日期
RoofStyle: 屋顶类型
RoofMatl: 屋顶材料
Exterior1st: 房屋外部覆盖材料
Exterior2nd: 房屋外部覆盖材料(如果多于一种材料)
MasVnrType: 砌体饰面类型
MasVnrArea: 砌体饰面面积(平方英尺)
ExterQual: 外部材料质量
ExterCond: 外部材料的现状
Foundation: 地基类型
BsmtQual: 地下室高度
BsmtCond: 地下室一般状况
BsmtExposure: 走出式或花园层地下室墙壁
BsmtFinType1: 地下室装修区域的质量
BsmtFinSF1: 1 类装修平方英尺数
BsmtFinType2: 第二类装修区域的质量(如果存在)
BsmtFinSF2: 2 类装修平方英尺数
BsmtUnfSF: 地下室未装修区域的平方英尺数
TotalBsmtSF: 地下室总平方英尺数
Heating: 供暖类型
HeatingQC: 供暖质量和状况
CentralAir: 中央空调
Electrical: 电气系统
1stFlrSF: 一楼平方英尺数
2ndFlrSF: 二楼平方英尺数
LowQualFinSF: 低质量装修平方英尺数(所有楼层)
GrLivArea: 地上(地面)居住面积平方英尺数
BsmtFullBath: 地下室全浴室数量
BsmtHalfBath: 地下室半浴室数量
FullBath: 地上全浴室数量
HalfBath: 地上半浴室数量
Bedroom: 地下室层以上的卧室数量
Kitchen: 厨房数量
KitchenQual: 厨房质量
TotRmsAbvGrd: 地上房间总数(不包括浴室)
Functional: 房屋功能评级
Fireplaces: 壁炉数量
FireplaceQu: 壁炉质量
GarageType: 车库位置
GarageYrBlt: 车库建造年份
GarageFinish: 车库内部装修
GarageCars: 车库容量(车辆数)
GarageArea: 车库大小(平方英尺)
GarageQual: 车库质量
GarageCond: 车库状况
PavedDrive: 铺砌的车道
WoodDeckSF: 木制露台面积(平方英尺)
OpenPorchSF: 开放式门廊面积(平方英尺)
EnclosedPorch: 封闭式门廊面积(平方英尺)
3SsnPorch: 三季门廊面积(平方英尺)
ScreenPorch: 纱窗门廊面积(平方英尺)
PoolArea: 游泳池面积(平方英尺)
PoolQC: 游泳池质量
Fence: 围栏质量
MiscFeature: 其他类别未涵盖的杂项特征
MiscVal: 杂项特征的价值(美元)
MoSold: 售出月份
YrSold: 售出年份
SaleType: 销售类型
SaleCondition: 销售状况

五、数据预处理和数据建模

1、数据预处理

①数据变换房屋价格呈现右偏分布,进行对数变换。

②分离目标变量和自变量。

③剔除ID这个无意义属性。

④对模型质量提升最高的是开展特征工程,根据已有特征构造有业务价值的新特征。构建诸如房屋总面积(地下室、一楼、二楼等特征)、卫浴指标(全浴室数量、半浴室数量、地上全浴室数量、地下半浴室数量)、房龄(建造时间、售卖时间)、翻新时间(建造时间、翻新时间)等特征。

⑤缺失值处理,可根据数据类型、数据分布、数据意义填充。这里数值型数据可以直接填充中位数,中位数对异常值不敏感,均值在房价数据集这种存在异常值较多的数据集中容易拉高缺失值,引入偏差。分类数据的缺失值代表“无”这一个业务逻辑,如无独立卫浴、无庭院灯,单独将缺失值给定一个新的类别值“None”

⑥偏态修正,计算了所有数值特征的偏度(Skewness),并设定0.75作为阈值,对于高于阈值的采用boxcox1p方法变换。处理的原因在于,第一很多算法假设数据是正态分布,第二房价、面积、地块大小等数据通常是右偏的。

⑦对分类变量进行独热编码。

⑧前面将给定的测试集和预测集统一进行预处理,现在需要按原来的状态进行拆分即可。预测集没有要预测的目标值这一列,也不涉及数据泄露。

⑨特征缩放,选用RobustScaler。StandardScaler标准化使用均值和标准差。如果数据里有极端的异常值,比如某一个面积写了100000平方英尺,均值会被拉偏,导致所有正常数据都被压缩得很小。而RobustScaler鲁棒缩放使用中位数和四分位数(IQR)。它对异常值不敏感。上面做了偏态修正,但数据中可能仍存在离群点。使用RobustScaler可以保证这些异常值不会破坏缩放的尺度,让模型更稳健。缩放后的数据只给了线性模型使用,树模型不需要缩放,因为它们是基于节点分裂的,缩放不会改变分裂点的位置。

2、数据建模

2.1 多模型加权平均融合

构建多模型融合体系,通过经验分配模型在预测中的权重。此权重分配方式的实际效果要优于一般stacking的方法。

正则化线性模型:岭回归、拉索回归、弹性网络回归。抗过拟合,在特征维度很高的情况下,比如独热编码之后,线性模型能通过正则化项L1、L2抑制噪声,提供稳定的基准预测。

核方法:核岭回归。非线性拟合,通过核技巧将数据映射到高维空间,能捕捉线性模型无法处理的非线性关系,通常在小样本或复杂分布下表现优异。

梯度提升树:XGBoost、LightBGM、CatBoost。擅长处理非线性、特征交互,且鲁棒性极强,效果一般都很好,所以给予的权重较高。

2.2 K折交叉检验

这里选用5折,每个模型都会被训练5次,每次用不同的子集验证,能最大程度地利用有限的数据,并评估模型的稳定性。

2.3 预测并还原

做 KFold 的平均预测,前面对要预测的房价做了np.log1p的对数变换运算,现在需要用np.expm1方法将预测值还原。最后生成提交的预测文件。

import pandas as pd
import numpy as np

from sklearn.model_selection import KFold
from sklearn.preprocessing import RobustScaler
from sklearn.metrics import mean_squared_error

from sklearn.linear_model import Ridge, Lasso, ElasticNet
from sklearn.kernel_ridge import KernelRidge

from scipy.stats import skew
from scipy.special import boxcox1p

from xgboost import XGBRegressor
import lightgbm as lgb
from catboost import CatBoostRegressor
# 读取数据
train = pd.read_csv('/kaggle/input/competitions/house-prices-advanced-regression-techniques/train.csv')
test = pd.read_csv('/kaggle/input/competitions/house-prices-advanced-regression-techniques/test.csv')

test_id = test["Id"]

y = np.log1p(train["SalePrice"])

train.drop(["SalePrice"], axis=1, inplace=True)

all_data = pd.concat([train, test])
# 删除Id
all_data.drop("Id", axis=1, inplace=True)
# 特征工程
all_data["TotalSF"] = (
    all_data["TotalBsmtSF"]
    + all_data["1stFlrSF"]
    + all_data["2ndFlrSF"]
)

all_data["TotalBath"] = (
    all_data["FullBath"]
    + 0.5 * all_data["HalfBath"]
    + all_data["BsmtFullBath"]
    + 0.5 * all_data["BsmtHalfBath"]
)

all_data["HouseAge"] = all_data["YrSold"] - all_data["YearBuilt"]
all_data["RemodAge"] = all_data["YrSold"] - all_data["YearRemodAdd"]

all_data["HasGarage"] = (all_data["GarageArea"] > 0).astype(int)
all_data["HasBsmt"] = (all_data["TotalBsmtSF"] > 0).astype(int)
all_data["HasFireplace"] = (all_data["Fireplaces"] > 0).astype(int)
# 缺失值处理
num_cols = all_data.select_dtypes(include=["int64","float64"]).columns
cat_cols = all_data.select_dtypes(include=["object"]).columns

all_data[num_cols] = all_data[num_cols].fillna(all_data[num_cols].median())
all_data[cat_cols] = all_data[cat_cols].fillna("None")
# 偏态修正
skewed = all_data[num_cols].apply(lambda x: skew(x))
skewed = skewed[abs(skewed) > 0.75]

for col in skewed.index:
    all_data[col] = boxcox1p(all_data[col], 0.15)

    
# One-Hot Encoding
all_data = pd.get_dummies(all_data)

# 拆回 train / test
X_train = all_data.iloc[:len(train)]
X_test = all_data.iloc[len(train):]

# 特征缩放(给线性模型)
scaler = RobustScaler()

X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 定义模型
ridge = Ridge(alpha=15)
lasso = Lasso(alpha=0.0005)
elastic = ElasticNet(alpha=0.0005, l1_ratio=0.9)
krr = KernelRidge(alpha=0.6, kernel="polynomial", degree=2)
xgb = XGBRegressor(
    n_estimators=5000,
    learning_rate=0.02,
    max_depth=4,
    subsample=0.7,
    colsample_bytree=0.7,
    reg_alpha=0.0005,
    random_state=42
)
lgb_model = lgb.LGBMRegressor(
    n_estimators=5000,
    learning_rate=0.02,
    num_leaves=32,
    subsample=0.7,
    colsample_bytree=0.7,
    random_state=42
)
cat = CatBoostRegressor(
    iterations=3000,
    learning_rate=0.03,
    depth=6,
    verbose=False
)

# KFold Stacking
kf = KFold(n_splits=5, shuffle=True, random_state=42)

pred_test = np.zeros(len(X_test))

for train_idx, val_idx in kf.split(X_train):

    X_tr = X_train.iloc[train_idx]
    y_tr = y.iloc[train_idx]

    X_val = X_train.iloc[val_idx]
    y_val = y.iloc[val_idx]

    ridge.fit(X_train_scaled[train_idx], y_tr)
    lasso.fit(X_train_scaled[train_idx], y_tr)
    elastic.fit(X_train_scaled[train_idx], y_tr)
    krr.fit(X_train_scaled[train_idx], y_tr)

    xgb.fit(X_tr, y_tr)
    lgb_model.fit(X_tr, y_tr)
    cat.fit(X_tr, y_tr)

    pred = (
        0.15 * ridge.predict(X_test_scaled)
        + 0.15 * lasso.predict(X_test_scaled)
        + 0.10 * elastic.predict(X_test_scaled)
        + 0.10 * krr.predict(X_test_scaled)
        + 0.20 * xgb.predict(X_test)
        + 0.20 * lgb_model.predict(X_test)
        + 0.10 * cat.predict(X_test)
    )

    pred_test += pred / kf.n_splits
    

# 还原预测
pred_test = np.expm1(pred_test)

# 生成提交文件
submission = pd.DataFrame({
    "Id": test_id,
    "SalePrice": pred_test
})

submission.to_csv("submission.csv", index=False)

2.4 模型得分

Public Score  0.11934

平均验证集 RMSE: 0.12204 (+/- 0.02161)
平均验证集 R²:   0.90108
------------------------------
原始价格尺度的 RMSE: $31,213.05

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐