恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
线性回归、岭回归与套索回归:原理、调参及实战选型指南
首页
资讯中心
/
线性回归、岭回归与套索回归:原理、调参及实战选型指南
线性回归、岭回归与套索回归:原理、调参及实战选型指南
发布时间:2026/10/15 20:16:43
1. 从一堆回归名词说起为什么我要把这几个东西放在一起讲线性回归、岭回归、套索回归这三个词放在一起很多刚接触机器学习或者数据分析的朋友第一反应是不都是回归吗有什么区别我一开始也是这么想的觉得无非就是加个正则化项的事能有多大差别。直到我在一个真实的房价预测项目上用普通线性回归跑出来的模型在测试集上表现一塌糊涂换成岭回归之后效果直接上了一个台阶我才意识到这里面的门道远比我想象的要多。这篇文章想做的事情很明确把线性回归、岭回归、套索回归这三个最常用的回归方法从原理到代码到调参到踩坑一次性讲透。不管你是刚入门的数据分析新手还是已经用过sklearn调过几个模型但说不清楚背后逻辑的进阶选手我相信你都能从里面找到对自己有用的东西。我会用最直白的话解释每个方法的适用场景和内在逻辑配上可以直接跑的代码再加上我自己在实际项目中踩过的坑和总结的经验。先说结论帮你建立整体认知普通线性回归适合特征少、样本多、特征之间没什么关联的场景岭回归适合特征多但每个特征都有点用的情况套索回归适合特征特别多但真正有用的就那么几个的情况。这三个方法不是互相替代的关系而是针对不同数据形态的不同工具。选错了工具不是模型跑不出来而是跑出来的结果你可能根本没法用。2. 普通线性回归最基础但也最容易翻车的起点2.1 线性回归到底在做什么线性回归的核心思想用一句话概括就是找到一条直线或者一个超平面让所有数据点到这条直线的距离平方和最小。这个“距离平方和最小”就是大名鼎鼎的最小二乘法。为什么是平方和而不是绝对值和这个问题我当初也纠结过。用平方的好处有两个一是平方函数处处可导求极值方便直接令导数为零就能得到解析解二是平方会放大较大误差的影响让模型更倾向于避免出现特别离谱的预测。坏处也很明显平方让模型对异常值特别敏感一个极端离群点就能把整条直线拽偏。数学上线性回归的解析解是import numpy as np # 假设 X 是特征矩阵y 是目标向量 # 解析解公式w (X^T X)^(-1) X^T y def linear_regression_closed_form(X, y): # 添加偏置项 X_with_bias np.hstack([np.ones((X.shape[0], 1)), X]) # 计算 (X^T X)^(-1) X^T y w np.linalg.inv(X_with_bias.T X_with_bias) X_with_bias.T y return w这段代码看起来很简单但里面藏着一个致命问题np.linalg.inv要求矩阵可逆。什么情况下X^T X不可逆当特征之间存在完全的线性关系也就是多重共线性时这个矩阵就是奇异的求逆会直接报错或者给出数值上极不稳定的结果。2.2 多重共线性线性回归的隐形杀手多重共线性这个词听起来很学术但用大白话解释就是你给模型的特征里面有几个特征携带的信息高度重叠。比如你要预测房价特征里同时有“面积平方米”和“面积平方英尺”这两个特征本质上是一个东西只是单位不同。模型看到这两个特征会非常困惑到底该把权重分配给谁我遇到过一个更隐蔽的例子。在一个用户流失预测项目中特征里同时有“最近30天登录次数”和“最近7天登录次数”。这两个特征相关性高达0.92。普通线性回归跑出来的系数是最近30天登录次数权重为-3.2最近7天登录次数权重为2.8。单看系数你会觉得“最近30天登录越多流失越少但最近7天登录越多流失越多”这完全说不通。实际上就是因为两个特征高度相关模型在训练集上找到了一组能拟合数据但毫无解释意义的系数。多重共线性带来的直接后果有三个系数估计方差变大训练集稍微变一点系数就剧烈波动系数的正负号可能和实际业务逻辑相反模型的泛化能力变差在新数据上表现不稳定。2.3 怎么判断有没有多重共线性最常用的方法是计算方差膨胀因子VIF。VIF衡量的是某个特征的系数方差因为共线性被放大了多少倍。经验规则是VIF大于10就说明存在严重的多重共线性大于5就值得警惕。from statsmodels.stats.outliers_influence import variance_inflation_factor import pandas as pd def calculate_vif(X_df): 计算每个特征的VIF值 vif_data pd.DataFrame() vif_data[feature] X_df.columns vif_data[VIF] [variance_inflation_factor(X_df.values, i) for i in range(X_df.shape[1])] return vif_data.sort_values(VIF, ascendingFalse)除了VIF相关系数矩阵也是一个快速筛查的工具。如果两个特征的皮尔逊相关系数绝对值超过0.8基本可以确定它们之间存在较强的线性关系。但要注意相关系数只能捕捉线性关系有些非线性关联它检测不出来所以VIF更全面一些。注意VIF的计算要求特征矩阵不能有常数项列比如全为1的偏置列否则那个常数项的VIF会无穷大。另外VIF只能检测特征之间的线性关系对于非线性关系无能为力。2.4 普通线性回归的适用边界说了这么多问题普通线性回归是不是就不能用了当然不是。在以下场景中普通线性回归依然是最优选择样本量远大于特征数比如1000个样本10个特征特征之间经过检验没有严重的多重共线性数据质量高异常值少你需要的是系数的可解释性而不是极致的预测精度在这些条件下普通线性回归给出的系数是无偏估计这是它最大的优势。岭回归和套索回归虽然能解决共线性问题但代价是引入了偏差。所以如果你的数据本身没问题没必要为了用高级方法而用高级方法。3. 岭回归给系数加一根“缰绳”3.1 岭回归的核心思想岭回归Ridge Regression解决多重共线性的思路非常直接既然系数会因为共线性变得很大且不稳定那我就在损失函数里加一个惩罚项专门惩罚过大的系数。具体来说岭回归的损失函数是损失函数 残差平方和 α × 系数平方和这里的α是一个超参数控制惩罚的力度。α越大惩罚越重系数被压缩得越厉害α等于0时岭回归退化成普通线性回归。为什么加的是系数平方和而不是绝对值因为平方项可导岭回归有解析解。这个解析解是def ridge_regression_closed_form(X, y, alpha): 岭回归的解析解 X_with_bias np.hstack([np.ones((X.shape[0], 1)), X]) n_features X_with_bias.shape[1] # 单位矩阵但不惩罚偏置项 I np.eye(n_features) I[0, 0] 0 # 通常不惩罚截距 w np.linalg.inv(X_with_bias.T X_with_bias alpha * I) X_with_bias.T y return w注意看这个公式和普通线性回归的区别在求逆之前对角线上加了α。这个操作保证了矩阵一定是可逆的因为即使X^T X是奇异的加上一个正数乘以单位矩阵之后所有特征值都至少是α矩阵就变成正定的了。这就是岭回归能解决共线性问题的数学本质。3.2 α的选择岭迹图是个好帮手α选多大合适这是岭回归最核心的调参问题。太小了解决不了共线性太大了所有系数都被压向零模型欠拟合。我常用的方法是画岭迹图。具体做法是让α从一个很小的值逐渐增大到一个很大的值对每个α都拟合一次岭回归然后把每个特征的系数随α变化的曲线画出来。import matplotlib.pyplot as plt from sklearn.linear_model import Ridge from sklearn.preprocessing import StandardScaler def plot_ridge_trace(X, y, alphasNone): 绘制岭迹图 if alphas is None: alphas np.logspace(-3, 4, 100) # 标准化非常重要 scaler StandardScaler() X_scaled scaler.fit_transform(X) coefs [] for a in alphas: ridge Ridge(alphaa) ridge.fit(X_scaled, y) coefs.append(ridge.coef_) coefs np.array(coefs) plt.figure(figsize(12, 6)) for i in range(coefs.shape[1]): plt.plot(np.log10(alphas), coefs[:, i], labelf特征 {i}) plt.xlabel(log10(alpha)) plt.ylabel(系数值) plt.title(岭迹图) plt.legend() plt.grid(True) plt.show()怎么看岭迹图理想的岭迹图应该是随着α增大所有系数都平稳地趋向于零没有哪个系数出现剧烈的震荡。如果某个系数在某个α区间内剧烈变化说明这个α区间不稳定应该避开。通常我会选择岭迹图趋于平稳的那个α值作为候选。3.3 标准化岭回归的前置必备步骤这里要特别强调一点做岭回归之前必须对特征做标准化。原因很简单岭回归的惩罚项是系数平方和而系数的大小和特征的量纲直接相关。如果一个特征的单位是“万元”另一个是“元”那前者的系数自然会比后者小很多惩罚项对它们的影响就不对等了。标准化的做法是让每个特征都变成均值为0、标准差为1的分布from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_scaled scaler.fit_transform(X) # 注意标准化要用训练集的均值和标准差 # 测试集要用同样的scaler进行transform而不是重新fit X_test_scaled scaler.transform(X_test)注意标准化一定要在划分训练集和测试集之后用训练集的统计量去转换测试集。如果先把整个数据集标准化再划分测试集的统计信息就泄露到了训练过程中会导致评估结果偏乐观。3.4 岭回归的实战表现回到我前面提到的房价预测项目。原始数据有80多个特征包括面积、房间数、地理位置的各种编码、周边设施距离等等。用普通线性回归跑出来的结果训练集R²是0.89测试集R²只有0.61典型的过拟合。而且系数的正负号有好几个和常识相反。换成岭回归之后经过5折交叉验证选择α10测试集R²提升到了0.78。更重要的是系数的正负号全部符合业务逻辑了。虽然训练集R²降到了0.84但测试集和训练集的差距从0.28缩小到了0.06说明模型的泛化能力大幅提升。这个案例让我深刻体会到在特征多且存在共线性的场景下岭回归几乎总是比普通线性回归更好的选择。代价是模型有偏了但方差降低带来的收益远大于偏差增加带来的损失。4. 套索回归自动做特征选择的回归4.1 套索回归和岭回归的本质区别套索回归Lasso Regression的损失函数和岭回归长得很像唯一的区别是惩罚项从系数平方和变成了系数绝对值之和损失函数 残差平方和 α × 系数绝对值之和这个看似微小的改动带来了一个非常关键的性质套索回归可以把某些系数精确地压缩到零。也就是说套索回归会自动帮你做特征选择把不重要的特征直接踢出模型。为什么绝对值惩罚能产生稀疏解而平方惩罚不能这个问题困扰了我很久。直观的解释是平方函数在零点附近的导数是趋近于零的所以系数越接近零受到的“往零拉”的力越小很难真正到达零。而绝对值函数在零点处有一个尖角导数不连续存在一个“死区”只要残差带来的梯度在这个死区内系数就会被牢牢按在零上。用几何的方式理解更直观岭回归的约束区域是一个圆二维情况下套索回归的约束区域是一个菱形。损失函数的等高线椭圆和约束区域相切时菱形因为有尖角切点更容易落在坐标轴上也就是某个系数为零的位置。4.2 套索回归的实操要点套索回归没有解析解需要用坐标下降法或者最小角回归LARS等迭代算法求解。sklearn里面用起来倒是很简单from sklearn.linear_model import Lasso, LassoCV from sklearn.preprocessing import StandardScaler # 标准化 scaler StandardScaler() X_scaled scaler.fit_transform(X) # 方法一手动指定alpha lasso Lasso(alpha0.1, max_iter10000) lasso.fit(X_scaled, y) print(f非零系数个数{np.sum(lasso.coef_ ! 0)}) # 方法二用交叉验证自动选alpha lasso_cv LassoCV(cv5, max_iter10000, random_state42) lasso_cv.fit(X_scaled, y) print(f最优alpha{lasso_cv.alpha_}) print(f非零系数个数{np.sum(lasso_cv.coef_ ! 0)})LassoCV会自动在一条alpha路径上做交叉验证选出均方误差最小的那个alpha。这个功能非常实用省去了手动调参的麻烦。4.3 套索回归的局限性套索回归虽然能做特征选择但它有几个需要注意的局限第一当特征之间存在高度相关时套索回归倾向于随机保留其中一个而把其他的压缩到零。这个“随机”意味着如果你把数据重新采样一遍保留下来的特征可能就变了。这在需要解释模型的时候是个麻烦。第二套索回归在特征数大于样本数的情况下最多只能选出样本数个非零系数。比如你有100个样本、1000个特征套索回归最多只能保留100个特征。这是由算法本身的几何性质决定的。第三套索回归的系数是有偏的。虽然它选出了重要特征但系数的大小被压缩了不能直接当作效应量来解释。4.4 弹性网络岭回归和套索回归的折中正是因为套索回归在处理相关特征时不够稳定弹性网络Elastic Net被提了出来。它的惩罚项是岭回归和套索回归惩罚项的加权组合损失函数 残差平方和 α × (ρ × 系数绝对值之和 (1-ρ) × 系数平方和)其中ρ控制两种惩罚的比例。ρ1时退化成套索回归ρ0时退化成岭回归。弹性网络既保留了套索回归的特征选择能力又在相关特征之间实现了“分组”效果——相关的特征要么一起被保留要么一起被剔除。from sklearn.linear_model import ElasticNetCV enet_cv ElasticNetCV( l1_ratio[0.1, 0.3, 0.5, 0.7, 0.9, 0.95, 1.0], cv5, max_iter10000, random_state42 ) enet_cv.fit(X_scaled, y) print(f最优alpha{enet_cv.alpha_}) print(f最优l1_ratio{enet_cv.l1_ratio_})在实际项目中如果特征之间相关性较高我通常会优先尝试弹性网络而不是纯套索回归。多花一点调参时间换来更稳定的特征选择结果是值得的。5. 三种方法的完整对比与选型指南5.1 核心差异一览对比维度线性回归岭回归套索回归损失函数仅残差平方和残差平方和 α×系数平方和残差平方和 α×系数绝对值之和能否处理共线性不能能能但相关特征只保留一个是否做特征选择否否是可将系数压缩为零是否有解析解有有无需迭代求解系数是否有偏无偏有偏有偏是否需要标准化不必须必须必须适用场景特征少、无共线性特征多、都有用特征多、稀疏5.2 选型决策流程根据我的经验选型可以按照以下流程来第一步先跑普通线性回归看VIF和测试集表现。如果VIF都小于5且测试集表现和训练集接近直接用线性回归没必要上正则化。第二步如果VIF很高或者测试集表现明显差于训练集换岭回归。岭回归是处理共线性的默认选择因为它稳定、有解析解、调参简单。第三步如果岭回归跑完之后发现很多特征的系数都很小但都不为零而你怀疑其中大部分特征其实没用换套索回归试试。套索回归会给你一个稀疏的模型方便解释。第四步如果套索回归选出来的特征在不同交叉验证折之间变化很大说明特征间相关性高换弹性网络。5.3 一个容易忽略的细节截距项的处理sklearn的Ridge和Lasso默认都会惩罚截距项吗答案是不会。sklearn在内部实现时对截距做了特殊处理不把截距纳入惩罚范围。这个设计是合理的因为截距反映的是目标变量的基准水平不应该被压缩。但如果你自己手写实现一定要注意把截距排除在惩罚项之外。我见过有人手写岭回归时把截距也惩罚了结果模型预测的均值严重偏离真实均值排查了半天才发现是这个问题。6. 实操中踩过的坑和排查技巧6.1 标准化顺序错误导致数据泄露这是我早期最常犯的错误。正确的流程是from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # 1. 先划分 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) # 2. 在训练集上fit scaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) # 3. 用同样的scaler转换测试集 X_test_scaled scaler.transform(X_test)如果反过来先标准化再划分测试集的均值和方差信息就泄露到了训练过程中。虽然对于标准化来说这种泄露的影响通常不大但在严格的项目评估中这是不可接受的。6.2 alpha过大导致模型欠拟合有一次我偷懒直接用了一个很大的alpha值1000结果所有系数都被压缩到接近零模型预测值几乎等于目标变量的均值。当时我还以为是数据有问题后来画了岭迹图才发现alpha选得太离谱了。正确的做法是用交叉验证来选alpha。sklearn提供了RidgeCV和LassoCV用起来很方便from sklearn.linear_model import RidgeCV alphas np.logspace(-3, 4, 100) ridge_cv RidgeCV(alphasalphas, cv5, scoringneg_mean_squared_error) ridge_cv.fit(X_train_scaled, y_train) print(f最优alpha{ridge_cv.alpha_})6.3 特征量纲不一致导致惩罚不公平这个问题在岭回归和套索回归中都会出现。如果特征没有标准化量纲大的特征系数自然小受到的惩罚也小量纲小的特征系数大受到的惩罚也大。这完全违背了正则化的初衷——正则化应该对所有特征一视同仁。我一般用StandardScaler做标准化但在某些场景下MinMaxScaler或者RobustScaler可能更合适。比如数据中有很多异常值时RobustScaler用中位数和四分位距来缩放比均值和标准差更稳健。6.4 常见问题速查表问题现象可能原因排查方法解决方案测试集R²远低于训练集过拟合或共线性计算VIF画学习曲线换岭回归/套索回归增大alpha系数正负号与业务逻辑相反多重共线性检查特征相关系数矩阵删除相关特征或换岭回归所有系数都接近零alpha过大画岭迹图减小alpha或用交叉验证选alpha套索回归选出的特征不稳定特征间高度相关多次交叉验证看特征选择结果换弹性网络模型预测值几乎等于均值alpha过大或特征无信息检查alpha值和特征与目标的相关性调整alpha检查特征工程手写实现结果和sklearn不一致截距被惩罚或标准化方式不同对比截距项和标准化参数确保截距不惩罚标准化方式一致6.5 一个实用的调参技巧如果你不确定该用岭回归还是套索回归可以先用弹性网络跑一遍看最优的l1_ratio落在哪里。如果l1_ratio接近1说明套索回归更合适如果接近0说明岭回归更合适如果在中间说明弹性网络确实比两者都好。另外我习惯在调参之前先做一个快速的基线测试用默认参数的岭回归和套索回归各跑一遍看测试集R²。如果两者差距不大优先选岭回归因为它的结果更稳定解释起来也更简单。如果套索回归明显更好且选出的特征数量合理再深入调套索回归。7. 从理论到落地一个完整的实战案例7.1 数据准备与探索假设我们有一个模拟的客户价值预测数据集包含500个样本和30个特征。特征包括客户的基本属性、历史行为统计、交互记录等。目标变量是客户未来一年的消费金额。import numpy as np import pandas as pd from sklearn.model_selection import train_test_split, cross_val_score from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LinearRegression, RidgeCV, LassoCV, ElasticNetCV from sklearn.metrics import r2_score, mean_squared_error # 模拟数据生成 np.random.seed(42) n_samples, n_features 500, 30 # 生成有共线性的特征 X np.random.randn(n_samples, n_features) # 让部分特征高度相关 X[:, 5] X[:, 0] * 0.9 np.random.randn(n_samples) * 0.1 X[:, 6] X[:, 1] * 0.85 np.random.randn(n_samples) * 0.15 X[:, 7] X[:, 2] * 0.8 np.random.randn(n_samples) * 0.2 # 生成目标变量只有前10个特征真正有影响 true_coef np.zeros(n_features) true_coef[:10] np.random.uniform(1, 5, 10) y X true_coef np.random.randn(n_samples) * 2 # 划分数据集 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) # 标准化 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)7.2 三种方法的对比实验# 普通线性回归 lr LinearRegression() lr.fit(X_train_scaled, y_train) lr_train_r2 r2_score(y_train, lr.predict(X_train_scaled)) lr_test_r2 r2_score(y_test, lr.predict(X_test_scaled)) # 岭回归 alphas np.logspace(-3, 4, 100) ridge RidgeCV(alphasalphas, cv5) ridge.fit(X_train_scaled, y_train) ridge_train_r2 r2_score(y_train, ridge.predict(X_train_scaled)) ridge_test_r2 r2_score(y_test, ridge.predict(X_test_scaled)) # 套索回归 lasso LassoCV(cv5, max_iter10000, random_state42) lasso.fit(X_train_scaled, y_train) lasso_train_r2 r2_score(y_train, lasso.predict(X_train_scaled)) lasso_test_r2 r2_score(y_test, lasso.predict(X_test_scaled)) # 弹性网络 enet ElasticNetCV(l1_ratio[0.1, 0.5, 0.7, 0.9, 0.95, 1.0], cv5, max_iter10000, random_state42) enet.fit(X_train_scaled, y_train) enet_train_r2 r2_score(y_train, enet.predict(X_train_scaled)) enet_test_r2 r2_score(y_test, enet.predict(X_test_scaled)) # 汇总结果 results pd.DataFrame({ 模型: [线性回归, 岭回归, 套索回归, 弹性网络], 训练集R²: [lr_train_r2, ridge_train_r2, lasso_train_r2, enet_train_r2], 测试集R²: [lr_test_r2, ridge_test_r2, lasso_test_r2, enet_test_r2], 非零系数数: [ np.sum(lr.coef_ ! 0), np.sum(ridge.coef_ ! 0), np.sum(lasso.coef_ ! 0), np.sum(enet.coef_ ! 0) ] }) print(results)在这个模拟数据上普通线性回归因为共线性的存在测试集表现会明显差于岭回归。套索回归会把大部分噪声特征的系数压缩为零只保留少数几个真正重要的特征。弹性网络的表现通常介于两者之间。7.3 结果解读与模型选择跑完对比实验之后怎么选最终模型我的判断标准是这样的如果业务方需要解释每个特征的影响大小优先选岭回归因为它保留了所有特征系数的大小反映了特征的重要性在标准化之后。虽然系数有偏但相对大小是有参考价值的。如果业务方只关心预测精度不关心用了哪些特征选测试集R²最高的那个。在这个模拟数据中通常是弹性网络或岭回归胜出。如果特征数量特别多比如上千个而且你怀疑大部分特征都是噪声选套索回归或弹性网络。它们给出的稀疏模型不仅预测效果好而且计算效率高部署时也轻量。提示无论选哪个模型最终都应该在独立的验证集上评估一次。交叉验证给出的分数是模型选择阶段的参考最终报告的性能应该来自一个从未参与过任何训练和调参的数据集。8. 一些零散但重要的经验补充关于正则化系数的尺度问题。sklearn中Ridge的alpha和Lasso的alpha含义不完全一样。Ridge的损失函数是||y - Xw||² alpha * ||w||²而Lasso是(1/(2*n_samples)) * ||y - Xw||² alpha * ||w||₁。注意Lasso的残差项前面有一个1/(2*n_samples)的系数这意味着同样的alpha值在Lasso中对应的惩罚力度和Ridge不同。比较两者时不要直接比较alpha的数值。关于normalize参数。sklearn早期版本的Ridge和Lasso有一个normalize参数可以在拟合时自动标准化。这个参数在新版本中已经被弃用了官方建议先用StandardScaler手动标准化。我强烈建议遵循这个建议因为手动标准化让你对数据的处理过程有完全的控制权也方便在测试集上做一致的转换。关于稀疏性的实际意义。套索回归把系数压缩为零意味着模型完全忽略那个特征。但在业务上一个特征被忽略不代表它不重要可能只是它和另一个被保留的特征高度相关信息被那个特征代表了。所以在向业务方解释套索回归的结果时不要简单地说“这个特征没用”而要说“在现有特征集下这个特征没有提供额外的信息”。关于计算效率。当特征数超过一万时套索回归的坐标下降法可能会比较慢。这时候可以考虑用SGDRegressor配合L1或L2惩罚它使用随机梯度下降适合大规模数据。或者用LassoLars它使用最小角回归算法在特征数远大于样本数时特别高效。关于交叉验证的策略。时间序列数据不能用普通的K折交叉验证因为会用到未来信息预测过去。这时候要用TimeSeriesSplit。分组数据比如同一个用户的多条记录要用GroupKFold确保同一个组的数据不会同时出现在训练集和测试集中。最后说一个我个人的习惯每次做回归分析我都会先跑一个普通线性回归作为基线然后跑岭回归和套索回归把三个模型的系数放在一起对比。如果三者的系数符号一致且大小接近说明数据质量好用哪个都行。如果差异很大说明存在共线性或异常值需要进一步排查。这个对比过程本身就能帮我发现很多数据问题比单纯看模型指标有用得多。