本文用于对kaggel项目:House Prices - Advanced Regression Techniques 的学习

一.参考资料

Regularized Linear Models

二.notebook解读

原文:这个notebook不用 XGBoost,不用随机森林,不用集成学习,只用正则化线性回归(Lasso / Ridge)

嗯其实他都用了

1.库导入,数据加载

# 1. 导入数据分析必备库
import pandas as pd    # 表格处理神器
import numpy as np     # 数值计算
import seaborn as sns  # 画图(好看)
import matplotlib      # 画图基础库

import matplotlib.pyplot as plt  # 画图主工具
from scipy.stats import skew     # 计算偏度(看数据是否正态)
from scipy.stats.stats import pearsonr  # 计算相关系数


# 让图片高清显示
%config InlineBackend.figure_format = 'retina'
# 让图表直接在 notebook 里展示
%matplotlib inline

# 2. 读取训练集、测试集数据
train = pd.read_csv("../input/train.csv")  # 带房价的训练数据
test = pd.read_csv("../input/test.csv")    # 不带房价的测试数据

# 3. 展示训练集前5行,看看数据长啥样
train.head()

# 4. 合并数据集
all_data = pd.concat((train.loc[:,'MSSubClass':'SaleCondition'],
                      test.loc[:,'MSSubClass':'SaleCondition']))

  1. train = pd.read_csv("../input/train.csv") 这里的文件输入方式值得学习,以前我一直使用的绝对路径避免出错
  2. train.head()官方默认显示前五

  3. 为什么要合并数据集?因为要让两套数据经过完全一样的预处理

    1. 独热编码要一致

    2. 缺失值填充/标准化要一致

    3. 特征工程要一致

2.数据预处理

原文:我们这里不做花里胡哨的操作,只做 3 件事:

1. 对偏态的数值特征做 log(特征 + 1) 变换 → 让数据分布更接近正态分布

2. 对分类特征创建哑变量(独热编码) → 把文字变成数字

3. 用均值填充数值型特征的缺失值

因变量处理

# 1. 设置全局图表的大小:宽度12,高度6(让图更大更清晰)
matplotlib.rcParams['figure.figsize'] = (12.0, 6.0)

# 2. 创建一个新的DataFrame,存放【原始房价】和【log变换后的房价】
#    方便我们画直方图对比分布变化
prices = pd.DataFrame({
    "price": train["SalePrice"],                # 第一列:原始房价(未处理,偏态)
    "log(price + 1)": np.log1p(train["SalePrice"])  # 第二列:log(房价+1),正态化处理
})

# 3. 对上面两列数据画直方图(左右并排对比)
#    左边:原始价格分布
#    右边:log变换后的价格分布
prices.hist()
out:array([[<AxesSubplot:title={'center':'price'}>,
        <AxesSubplot:title={'center':'log(price + 1)'}>]], dtype=object)

Q1:.hist()结合之前学习的

图表名称 代码常见写法 核心作用(一句话记住)
1. 直方图 Histogram .hist() 看单列数据分布、是否正态、偏不偏
2. 散点图 Scatter plt.scatter() 看两个变量有没有线性相关关系
3. 箱线图 Boxplot .boxplot() 快速找异常值、看数据四分位范围
4. 热力图 Heatmap sns.heatmap() 看多个变量之间的相关系数强弱

Q2:np.log1p和pd.DataFrame的作用:

  • np.log1p(x) == np.log(x + 1)对数变换
  • pd.DataFrame(字典)是创建一个新表格,方便画图、对比、查看数据;字典格式: { "列名1": 数据列1, "列名2": 数据列2, "列名3": 数据列3 }

对数值型特征作log1p正态化处理

#log transform the target:
# 对预测目标【房价标签】做对数正态化修正
train["SalePrice"] = np.log1p(train["SalePrice"])

#log transform skewed numeric features:
# 筛选出所有【数值型特征】列名(排除字符串object类型)
numeric_feats = all_data.dtypes[all_data.dtypes != "object"].index

# 遍历所有数值特征:先删掉缺失值,再计算每一列的偏度skew
skewed_feats = train[numeric_feats].apply(lambda x: skew(x.dropna())) 

# 过滤保留:偏度大于0.75、明显右偏的特征
skewed_feats = skewed_feats[skewed_feats > 0.75]

# 只取出筛选后这些偏态特征的列名索引
skewed_feats = skewed_feats.index

# 在合并后的全量数据集上,对所有高偏度特征统一做log1p对数变换
all_data[skewed_feats] = np.log1p(all_data[skewed_feats])

Q1:解读skewed_feats = train[numeric_feats].apply(lambda x: skew(x.dropna()))

  • .apply( )对每一列分别执行后面的函数
  • lambda x表示当前正在处理的某一列数据,对这一列做下面的操作
  • x.dropna()把这一列里的空值 NaN 删掉,因为计算偏度时不能有空值
  • skew( )计算这一列数据的偏度

Q2:为什么大于0.75是严重右偏,以及其他判断依据?

  • 0.75是Kaggle 房价赛经验阈值
  • 数据分析通用规则:
    • Skew = 0:完美正态分布,完全对称
    • 0 < Skew < 0.5:轻微右偏,几乎不用处理
    • 0.5 ≤ Skew < 0.75:中度右偏,可选x = np.sqrt(x)微调,也可以不处理
    • Skew ≥ 0.75:重度强右偏 → 必须 log 变换

独热编码处理分类特征

# 把文字类型(如街道类型、建材风格)转换成 0/1 数字矩阵
all_data = pd.get_dummies(all_data)

缺失值填充

# 用【每一列自己的平均值】填充该列的缺失值(NaN)
all_data = all_data.fillna(all_data.mean())

数据划分

# 为 sklearn 机器学习模型创建特征矩阵(数据格式转换)
X_train = all_data[:train.shape[0]]  # 取前 训练集行数 的数据 → 训练集特征
X_test = all_data[train.shape[0]:]   # 取 训练集行数之后 的数据 → 测试集特征
y = train.SalePrice                 # 训练集的目标变量(房价)

Q1:这里为什么不需要把y转成竖着的二维张量tensor?

因为这里用的是 scikit-learn(sklearn)模型,不是 PyTorch / TensorFlow 深度学习模型

sklearn 接受的输入就是:

  • X:DataFrame 或 numpy 数组
  • y:一维 numpy 数组 /pandas Series

Q2. 什么时候才需要转 tensor?

  • 用深度学习框架时才需要如PyTorch,TensorFlow / Keras,CNN、RNN、Transformer 等神经网络
  • 它们要求:必须是 tensor,维度必须严格匹配,常常需要 y.reshape(-1,1)unsqueeze

Q3:为什么 sklearn 不用?

  • 因为 sklearn 是传统机器学习:线性回归,随机森林,XGBoost,岭回归 / Lasso
  • 它们原生支持 DataFrame /numpy自动处理维度,不需要手动调整 y 的形状

3.sklearn模型搭建(重点)

原文:现在我们要使用 scikit-learn 里的 正则化线性回归模型。 我会尝试两种:

  1. L1 正则化(Lasso)
  2. L2 正则化(Ridge)

我还会定义一个函数,用来返回 交叉验证的 RMSE 误差。 这样我们就能评估模型,并且选出最好的超参数。

验证函数

# 导入需要的机器学习模型(正则化线性回归)
from sklearn.linear_model import Ridge, RidgeCV, ElasticNet, LassoCV, LassoLarsCV

# 导入交叉验证工具(用来评估模型)
from sklearn.model_selection import cross_val_score

# 定义一个函数:输入模型,输出 5折交叉验证的 RMSE 误差
# RMSE 越小,模型越好
def rmse_cv(model):
    # 计算交叉验证分数 → 转成 RMSE
    rmse = np.sqrt(-cross_val_score(model, X_train, y, scoring="neg_mean_squared_error", cv=5))
    # 返回 RMSE 结果
    return rmse

Q1:np.sqrt(-cross_val_score(model, X_train, y, scoring="neg_mean_squared_error", cv=5))解读

  • cross_val_score:5 折交叉验证
  • scoring="neg_mean_squared_error":用负均方误差打分
  • - 号、开根号 np.sqrt → 变成 RMSE

Q2:RMSE是啥?怎么算?

  •   均方误差 MSE开根号

Ridge 模型

# 初始化 Ridge 回归模型(L2 正则化线性回归)
model_ridge = Ridge()

原文:Ridge 模型最核心的调节参数叫做 alpha,alpha 是一个**正则化强度参数**,用来控制模型的灵活程度,alpha 越大 → 正则化越强 → 模型越简单 → 越不容易过拟合,但是 alpha 太大会让模型变得太简单,可能学不到数据里的规律(欠拟合)

# 定义一组待测试的正则化参数 alpha 值
alphas = [0.05, 0.1, 0.3, 1, 3, 5, 10, 15, 30, 50, 75]

# 遍历每个 alpha,训练 Ridge 模型,计算 5 折交叉验证 RMSE 的平均值
cv_ridge = [rmse_cv(Ridge(alpha = alpha)).mean() 
            for alpha in alphas]
  • cv_ridge = [...]这是一个列表推导式,作用是循环测试每一个 alpha。

  • Ridge(alpha = alpha)用当前循环到的 alpha 创建一个 Ridge 模型。

  • rmse_cv(...)把模型放进你之前定义的函数里,做5 折交叉验证,得到一组 RMSE。

  • .mean()对 5 次验证的 RMSE 取平均值,让结果更稳定。

# 把交叉验证得到的RMSE结果转换成Pandas序列,索引就是对应的alpha值
cv_ridge = pd.Series(cv_ridge, index = alphas)

# 绘制折线图,展示不同alpha对应的RMSE变化,标题为"Validation - Just Do It"
cv_ridge.plot(title = "Validation - Just Do It")

# 设置X轴(横轴)标签为alpha
plt.xlabel("alpha")

# 设置Y轴(纵轴)标签为rmse
plt.ylabel("rmse")
out:Text(0, 0.5, 'rmse')

  • cv_ridge = pd.Series(cv_ridge, index = alphas)

    • 将之前计算好的平均 RMSE 列表转换成 Pandas 序列
    • alphas作为索引,实现alpha 值 ↔ RMSE 值一一对应
  • cv_ridge.plot(...)

    • 自动绘制折线图
    • X 轴:alpha(正则化参数)
    • Y 轴:RMSE(模型误差,越小越好)
    • 作用:可视化找最优 alpha(RMSE 最低点对应的 alpha)
  • plt.xlabel/ylabel

    • 给坐标轴加文字说明,让图表更易读

从图中能看出:alpha=10 处在 U 型最低点,误差最小,是最优参数

# 求出刚才所有 alpha 对应的 RMSE 中【最小的那个值】
cv_ridge.min()
out:0.12733734668670768

Lasso 模型

原文:刚才 Ridge 模型的最优结果:RMSE 大约 0.127(非常优秀),现在我们试试 Lasso 模型,这次用不一样的方法:直接用 LassoCV 自动找最优 alpha。重点:LassoCV 里的 alpha 和 Ridge 是反过来的。

总结:Ridge vs Lasso 参数完全相反

  • Ridge:alpha=10 → 正则化强
  • LassoCV:alpha=0.001 → 正则化强(Lasso 的 alpha 很小 = 惩罚很强)

为什么用 LassoCV?

因为它全自动:不用自己写循环试 alphas,不用画图。直接算出最好的 alpha,还直接训练好模型

# 初始化 LassoCV 模型(自动交叉验证选最优 alpha)
# 传入一组待测试的 alpha 参数
model_lasso = LassoCV(alphas = [1, 0.1, 0.001, 0.0005]).fit(X_train, y)

1.LassoCV(alphas=[...])

  • LassoCV:自带交叉验证自动选参的 Lasso 模型
  • 不用自己循环、不用画图
  • 自动帮你试每个 alpha,找出RMSE 最小的那个

2. alphas = [1, 0.1, 0.001, 0.0005]

  • Lasso 专用的参数列表
  • 数字越小,正则化越强
  • 所以 Lasso 一般都用 0.001、0.0005 这种极小值

3. .fit(X_train, y)

  • 用训练集数据训练模型

  • 训练过程中:自动尝试所有 alpha,自动做交叉验证,自动保存最优模型

rmse_cv(model_lasso).mean()
out:0.12256735885048131

原文:太好了!Lasso 模型表现得更好,所以我们直接用它来在测试集上做预测,Lasso 还有一个很棒的优点:它会自动帮你做特征选择,不用人工删特征,模型自己判断哪些有用、哪些没用。把它认为不重要的特征的系数直接设为 0,我们来看看这些系数。

# 把模型训练好的系数 拿出来,做成带特征名的 Series
coef = pd.Series(model_lasso.coef_, index = X_train.columns)

# 打印:Lasso 选了多少个有用特征,删了多少个没用特征
print("Lasso picked " + str(sum(coef != 0)) + " variables and eliminated the other " +  str(sum(coef == 0)) + " variables")
out:Lasso picked 110 variables and eliminated the other 178 variables

原文:干得漂亮,Lasso!(夸它自动删了 178 个没用特征),但是有一点要注意:Lasso 选出来的特征,不一定就是 100% 绝对正确的。特别是因为数据里有很多共线性特征一个可以尝试的思路:在不同的抽样数据上多跑几次 Lasso,看看特征选择稳不稳定。

Q1:model_lasso.coef和.columns

训练完成后,模型自动生成:

  • model_lasso.coef_ → 所有特征的系数
  • model_lasso.alpha_ → 最优 alpha
  • model_lasso.intercept_ → 截距

这些都是 scikit-learn 模型训练完自带的属性

.columns → 获取所有列的名字

Q2:思维扩展1(见后文)

# 取出系数排序后 最小的10个 + 最大的10个,合并成重要系数
imp_coef = pd.concat([coef.sort_values().head(10),
                     coef.sort_values().tail(10)])

# 设置图表大小:宽度8,高度10
matplotlib.rcParams['figure.figsize'] = (8.0, 10.0)

# 画水平条形图,展示Lasso模型的重要特征
imp_coef.plot(kind = "barh")

# 设置图表标题
plt.title("Coefficients in the Lasso Model")
out:Text(0.5, 1.0, 'Coefficients in the Lasso Model')

1. coef.sort_values()把所有特征的系数从小到大排序

2. .head(10)取系数最小(最负)的 10 个特征

3. .tail(10)取系数最大(最正)的 10 个特征

4. pd.concat([...])把正负各 10 个特征拼在一起,画出最有影响力的特征

5. plot(kind="barh")画水平条形图,能清楚看到:#没有kind默认是折线图,跟之前一样

  • 条形越长 → 特征对房价影响越大
  • 向右 → 正向影响(抬高房价)
  • 向左 → 负向影响(拉低房价)

原文:最重要的正向特征是GrLivArea—— 地面以上的居住总面积(平方英尺)。这完全符合现实逻辑。其余一些地段、房屋品质类特征,也对房价起到正向推动作用。部分负向系数的特征逻辑很难解释,值得进一步排查;这大概率来自分布不均衡的类别特征。类别特征如果某一类样本极少(数据不平衡),模型会学到奇怪的负相关规律,不具备实际业务意义。还要注意:和随机森林的特征重要性不同,Lasso 输出的是真实可解释的模型系数。因此你能精准拆解:模型为什么预测出这个房价。唯一的问题:我们对目标值(房价)和数值特征都做了对数变换,导致系数大小很难直接按原始金额解释。

极简核心总结:

  1. 强正向特征GrLivArea符合买房常识;异常负向特征大概率是类别数据不平衡导致;
  2. Lasso 系数比随机森林重要性更可精准溯源解释;
  3. 对数变换:破坏了原始线性加法逻辑,变成乘法关系,降低了系数的直观解读性。
# 我们也来看看残差(模型预测的误差分布)
#let's look at the residuals as well:

# 设置图表大小为 6x6
matplotlib.rcParams['figure.figsize'] = (6.0, 6.0)

# 1. 创建一个表格:存放【模型预测值】和【真实值】
preds = pd.DataFrame({
    "preds": model_lasso.predict(X_train),  # 模型预测的房价
    "true": y                               # 真实的房价(已经取过log)
})

# 2. 计算【残差】= 真实值 - 预测值
# 残差 = 模型没预测准的部分(误差)
preds["residuals"] = preds["true"] - preds["preds"]

# 3. 画散点图:x轴是预测值,y轴是残差
preds.plot(x = "preds", y = "residuals", kind = "scatter")

误差随机分布,没有明显规律,说明模型很健康、没有大问题,可以放心拿去做最终预测。

4.加入XGBoost 模型

原文:我们在线性模型(Lasso)的基础上,再加入一个 XGBoost 模型,看看能不能让分数更高、预测更准!

xgb.cv

import xgboost as xgb

# 把训练数据转换成 XGBoost 专用格式(更快、更稳定)
dtrain = xgb.DMatrix(X_train, label = y)
dtest = xgb.DMatrix(X_test)

# 设置 XGBoost 核心参数
params = {"max_depth":2, "eta":0.1}

# 交叉验证(自动试模型,找最优训练轮数)
model = xgb.cv(params, dtrain,  num_boost_round=500, early_stopping_rounds=100)

# 画出训练集 & 测试集的误差曲线
model.loc[30:,["test-rmse-mean", "train-rmse-mean"]].plot()
out:<AxesSubplot:>

Q1:代码解读

1. dtrain = xgb.DMatrix(...)

  • XGBoost 有自己专用的数据格式 DMatrix
  • 作用:加速训练、支持缺失值、效率更高

2. params = {"max_depth":2, "eta":0.1}

  • max_depth=2:树的深度(越小越简单,防止过拟合)
  • eta=0.1:学习率(越小学得越精细)

3.model = xgb.cv(...) cv = 交叉验证:

  • 自动在训练集里反复切分验证
  • 自动找最优迭代次数
  • 自动在误差不再下降时停止(early_stopping)
  • 防止过拟合

4.model.loc[30:].plot()

  • model 是 xgb.cv () 运行完输出的表格,里面记录了:
    • 每一轮迭代的训练集误差
    • 每一轮迭代的测试集误差
  • loc [30:]从第 30 行开始,取后面所有行,前 30 轮模型还没学好,误差很大,画出来图不好看。.loc 是 pandas DataFrame(表格)最常用的选取工具
  • [30:, ["test-rmse-mean", "train-rmse-mean"]]取两列
  • .plot ()画折线图

Q2;图像结果分析

1.关键趋势解读

  • 前 50 轮:两条线都急剧下降 → 模型在快速学习,误差大幅缩小
  • 50~500 轮
  • 蓝色线(验证集):先缓慢下降,之后趋于平稳,几乎不再变化
  • 橙色线(训练集):持续缓慢下降,和蓝色线的差距逐渐拉大
  • 终点(500 轮)
  • 验证集 RMSE ≈ 0.125
  • 训练集 RMSE ≈ 0.06
  • 两者差距约 0.065

2.模型状态判断 

  • 没有明显过拟合:验证集误差没有后期上升,说明模型没有 “背答案”
  • 收敛稳定:验证集在约 100 轮后就基本平稳,继续训练几乎不再提升
  • 存在轻微过拟合倾向:训练集误差持续低于验证集,且差距缓慢扩大,说明模型在逐渐 “记住” 训练数据细节

3. 接下来该怎么做

  • 最优迭代轮数:验证集在 100~200 轮 就已经收敛,没必要跑到 500 轮。实际训练时可以取 num_boost_round=200 左右,既保证效果又节省时间。
  • 参数调整建议:
    • 若想缩小训练 / 验证集的差距(减轻过拟合):
    • 调小 max_depth(比如从 2 改到 1)
    • 调小 eta(比如从 0.1 改到 0.05)
    • 增加 subsample/colsample_bytree(随机采样特征 / 样本)

Q3:如何添加subsample/colsample_bytree(可选)

  • params = {
        "max_depth": 2,
        "eta": 0.1,
        "subsample": 0.7,          # 每棵树用70%样本
        "colsample_bytree": 0.7    # 每棵树用70%特征
    }

通用值subsample: 0.7 ~ 0.8 colsample_bytree: 0.7 ~ 0.8

xgb.XGBRegressor

# 定义 XGBoost 回归模型,传入通过交叉验证(xgb.cv)调整好的参数
model_xgb = xgb.XGBRegressor(n_estimators=360, max_depth=2, learning_rate=0.1) #the params were tuned using xgb.cv

# 使用训练集数据 (X_train, y) 训练模型
model_xgb.fit(X_train, y)
out:XGBRegressor(base_score=0.5, booster='gbtree', callbacks=None,
             colsample_bylevel=1, colsample_bynode=1, colsample_bytree=1,
             early_stopping_rounds=None, enable_categorical=False,
             eval_metric=None, gamma=0, gpu_id=-1, grow_policy='depthwise',
             importance_type=None, interaction_constraints='',
             learning_rate=0.1, max_bin=256, max_cat_to_onehot=4,
             max_delta_step=0, max_depth=2, max_leaves=0, min_child_weight=1,
             missing=nan, monotone_constraints='()', n_estimators=360, n_jobs=0,
             num_parallel_tree=1, predictor='auto', random_state=0, reg_alpha=0,
             reg_lambda=1, ...)

Q1:代码解读

1.model_xgb = xgb.XGBRegressor(...)

  • 创建一个 XGBoost 回归模型,Regressor = 回归器,因为是回归预测任务
  • n_estimators=360:迭代次数(树的数量),从之前交叉验证曲线图中选出最优值,n_estimators是不是和num_boost_round一样都是轮数,只不过模型接口的叫法不同
  • max_depth=2:树的最大深度,控制模型复杂度
  • learning_rate=0.1:学习率,控制每一步更新的幅度
  • 注释说明:这些参数都是通过 xgb.cv 调试出来的最佳参数

2.model_xgb.fit(X_train, y)

  • 用训练集特征 X_train 和训练集标签 y 训练模型
  • 训练完成后,model_xgb 就可以用于预测

Q2:我能不能理解成XGBRegressor就是专门的预测模型,那为什么还要重新用预测模型来再训练一次train.data?

  • 前面的 xgb.cv () 只是 “测试、调参”,没有生成真正能用的模型,只是为了帮你找到最好的参数:n_estimators, max_depth, learning_rate
  • 现在的XGBRegressor.fit () = 正式训练(产生可预测模型)

5.预测并提交

# XGBoost模型预测测试集,并对对数化的结果进行指数还原
xgb_preds = np.expm1(model_xgb.predict(X_test))

# Lasso模型预测测试集,并对对数化的结果进行指数还原
lasso_preds = np.expm1(model_lasso.predict(X_test))

# 将两个模型的预测结果保存到DataFrame中
predictions = pd.DataFrame({"xgb":xgb_preds, "lasso":lasso_preds})

# 绘制散点图,查看两个模型预测结果的相关性
predictions.plot(x = "xgb", y = "lasso", kind = "scatter")
<AxesSubplot:xlabel='xgb', ylabel='lasso'>
out:<AxesSubplot:xlabel='xgb', ylabel='lasso'>

Q1:代码层面

  • fit() = 学习 / 训练
  • .predict() = 预测 / 做题

Q2:图该怎么看

  • 点越贴近对角线 → 两个模型预测越一致
  • 点分散 → 两个模型有互补性,融合后分数会更高

原文:多数情况下,对相关性低的模型预测结果做加权平均融合是很合理的做法;这通常能提升模型分数,不过在当前数据集里,融合带来的提升并不会特别大。这里我们只用一个随手选定的固定权重来做加权平均。我们其实也可以用交叉验证网格搜索,来选出最优融合权重。

# 模型融合:对 Lasso 和 XGBoost 的预测结果做加权平均
# 70% 权重给 Lasso,30% 权重给 XGBoost
preds = 0.7*lasso_preds + 0.3*xgb_preds

# 构造提交文件:包含 房屋ID 和 最终预测房价
solution = pd.DataFrame({"id":test.Id, "SalePrice":preds})

# 保存为 CSV 文件,用于上传 Kaggle 排行榜
solution.to_csv("ridge_sol.csv", index = False)

Q1;思维扩展2(见后文)

三.思维扩展

思维扩展1:lasso模型岂不是可以拿来做EDA,如何保证lasso不错误的删除有用的特征,删除逻辑是啥?

1. Lasso 删除特征的真正逻辑

Lasso 的核心公式:

损失函数 = 预测误差 + alpha * |所有特征权重之和|

① 尽量让预测准

② 同时尽量让特征权重变小(越小越好,最好变成 0)

当权重变成 0 = 特征被删除

Lasso 删特征的真实规则:

规则 1:两个特征高度相关 → 只留一个,删另一个

比如:地上面积,居住面积

两个几乎一样 → Lasso 随便删一个,不是因为没用,是重复了。

规则 2:特征对预测没帮助 → 直接删

比如 ID、乱码、无关列。

规则 3:噪声特征、随机波动特征 → 强制删

规则 4:alpha 越大 → 删得越狠

那 Lasso 会不会误删有用特征?

会!而且非常容易!主要 2 个原因:

  1. 特征共线性(最常见)A 和 B 高度相关 → Lasso 删一个,不是它没用,是重复了。

  2. alpha 选得太大正则太强 → 删太多 → 欠拟合。

如何保证 Lasso 不错误删除有用特征?

方法 1:用 LassoCV,不要手动设 alpha(最重要)

model_lasso = LassoCV(alphas=[...]).fit(X_train, y)

LassoCV 自动选最温和、最不容易误删的 alpha→ 这是防误删第一神器

方法 2:看系数稳定性(不要只跑一次)

跑多次 Lasso,看:每次都保留 → 真重要,每次都变 → 共线特征

方法 3:不要只看 “删不删”,要看权重大小

  • 权重接近 0 → 不重要

  • 权重大 → 重要

  • 权重 = 0 → 被删除

方法 4:结合业务知识判断(EDA 核心)

比如:Lasso 删了 “卧室数”,但常识知道卧室数很重要 →说明它和面积共线了

方法 5:特征先做标准化(必须做)

Lasso 对量纲敏感不标准化 → 容易误删数值小的特征

方法 6:用 Stability Selection / 自助采样(高级防误删)

多次抽样、多次跑 Lasso:

  • 出现频率高 → 真重要

  • 偶尔出现 → 共线 / 不稳定

4. 最关键的一句话总结

Lasso 删特征不是看 “有没有用”,而是看 “在所有特征里,这个特征是否带来增量信息”。

重复的、没用的、噪声的 → 都会被删。只要用 LassoCV + 观察稳定性,就能避免 99% 的误删。

思维扩展2:如何用CV Grid Search(交叉验证网格搜索)自动找最优融合权重

# 导入数值计算库,用于数组、数学计算
import numpy as np
# 导入表格处理库,用于数据整理
import pandas as pd
# 导入网格搜索 + 交叉验证工具,用来自动找最优参数
from sklearn.model_selection import GridSearchCV
# 导入自定义模型必须继承的两个基础类(固定写法)
from sklearn.base import BaseEstimator, RegressorMixin

# ---------------------------
# 1. 定义一个融合模型(专门用来网格搜索)
# ---------------------------
# 自定义一个“模型融合类”,继承 sklearn 的标准格式,才能被 GridSearchCV 使用
class BlendModel(BaseEstimator, RegressorMixin):
    
    # 初始化函数:创建模型时会自动运行
    # alpha 是 Lasso 的权重,默认 0.5
    def __init__(self, alpha=0.5):
        self.alpha = alpha  # 把权重 alpha 存到模型里,后面网格搜索会自动修改它
    
    # 训练函数:sklearn 要求必须有,但我们这里只是融合,不需要训练
    def fit(self, X, y=None):
        # 什么都不用做,直接返回自己就行(固定写法)
        return self
    
    # 预测函数:真正执行 模型融合 计算的地方
    def predict(self, X):
        # X 是一个二维数组
        # X[:,0] = 第一列 → Lasso 模型的预测值
        lasso = X[:, 0]
        # X[:,1] = 第二列 → XGBoost 模型的预测值
        xgb = X[:, 1]
        
        # 核心公式:加权平均融合
        # 最终预测 = alpha * Lasso预测 + (1-alpha) * XGB预测
        return self.alpha * lasso + (1 - self.alpha) * xgb

# ---------------------------
# 2. 构造输入:把两个模型的预测结果拼在一起
# ---------------------------
# 注意:这里必须用【训练集的交叉验证预测值】,绝对不能用测试集!
# 你需要提前生成:
# oof_lasso = Lasso 在训练集上做交叉验证得到的预测值
# oof_xgb   = XGBoost 在训练集上做交叉验证得到的预测值

# 把两个模型的预测值 左右拼起来,变成 2列 的数据
# 第一列:Lasso预测
# 第二列:XGB预测
X_blend = np.column_stack((oof_lasso, oof_xgb))

# 真实的房价标签(训练集的y,已经log处理过)
y_true = y  

# ---------------------------
# 3. 网格搜索最优 alpha
# ---------------------------
# 定义要搜索的参数范围:
# alpha 从 0.1 到 0.9,每隔 0.05 取一个值试一遍
param_grid = {"alpha": np.arange(0.1, 1.0, 0.05)}  

# 创建网格搜索对象
grid = GridSearchCV(
    estimator=BlendModel(),    # 要搜索的模型:我们自定义的融合模型
    param_grid=param_grid,     # 要搜索的参数范围
    cv=5,                      # 5折交叉验证(把数据分成5份,轮流验证)
    scoring="neg_mean_squared_error",  # 用均方误差评估模型好坏
    n_jobs=-1                  # 使用所有CPU核心,加速计算
)

# 开始暴力搜索:遍历所有alpha,自动计算哪个分数最好
grid.fit(X_blend, y_true)

# ---------------------------
# 4. 输出最优权重
# ---------------------------
# 输出网格搜索找到的最优 alpha 值
print("最优 alpha:", grid.best_params_['alpha'])
# 输出最优模型的交叉验证误差(RMSE),越小越好
print("最优交叉验证误差:", np.sqrt(-grid.best_score_))

四.总结

原来搭建模型训练比我想象的要简单的多,全都是封装好的接口函数,重要的是模型的选择,经验和思路,简单的模型,比如本文中的,用的好的话照样能有不错的效果,其次,可以用多个模型进行预测,然后交叉验证融合模型,可以进一步减小loss提升分数。

Logo

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

更多推荐