ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

线性模型核心公式推导:最小二乘、岭回归、Lasso与逻辑回归

线性模型核心公式推导:最小二乘、岭回归、Lasso与逻辑回归 线性模型几乎是每个搞机器学习的人真正动手写的第一个模型我带了几年新人第一课基本都是它。原因很现实机器学习里一大堆听起来很唬人的名词——损失函数、正则化、梯度下降、极大似然、过拟合、泛化——你在一条直线上就能把它们全部看一遍而且看得清清楚楚不用被神经网络的几百个维度先劝退。这篇文章就把线性回归、岭回归、Lasso、逻辑回归这四个东西的核心公式从头推一遍最小二乘的正规方程到底怎么来的、为什么加一个 λI 矩阵就突然可逆了、Lasso 的软阈值解是从哪个绝对值里蹦出来的、逻辑回归的梯度为什么长得跟线性回归一模一样。适合刚入门想搞懂原理的同学也适合期末前想快速把推导串一遍的人。1. 线性模型的定位与整体设计思路1.1 它到底在解决一个什么问题说人话线性模型干的事情就是给一堆输入特征找一个权重组合让输出尽量贴近真实标签。数学上写成 $f(x) w^T x b$其中 $w$ 是权重向量$b$ 是偏置项。这一个式子背后藏着两个假设第一特征对输出的影响是可以叠加的第二每个特征的影响是线性比例的你多给一个单位的 $x_j$输出就固定多 $w_j$。听起来很天真对吧现实世界哪有这么简单。但恰恰是这个天真让线性模型在工业界活了几十年还没被淘汰。你去看搜索广告的点击率预估、风控评分卡、销量预测这类场景大量线上模型的第一层还是线性项深度模型也要专门加一个 wide 分支去记线性关系。原因在于它的可解释性——$w_j$ 直接告诉你第 j 个特征每变动一个单位输出变动多少这种直接可读的性质在很多需要给人解释的场景里是刚需。1.2 为什么先学它而不是先学神经网络我见过不少新手一上来就啃神经网络调包调得挺顺但一问为什么损失函数要用交叉熵不用 MSE答不上来。问题就出在跳过了线性模型这一层。线性模型是理解机器学习的最小可运行系统。你在这个系统里能同时看到四件事假设空间怎么定义线性函数族、损失函数怎么选平方损失、对数似然、优化怎么解闭式解或迭代法、泛化怎么控制正则化。这四个环节在神经网络里一个都不少只是被包装得更复杂。先把简单的版本吃透后面遇到复杂的你才知道每个模块为什么存在。而且线性模型的公式是真的能推到底的。神经网络你很难手推一个解析解但线性回归的正规方程你可以从损失函数一路推到 $w (X^TX)^{-1}X^Ty$每一步都站得住脚。这种完整推一遍的经历对建立数学直觉非常重要。1.3 本文的组织方式与前置知识后面几节我按这个顺序展开先推最小二乘的解析解第 2 节再把它扩展到正则化版本第 3 节然后换一个损失函数推出逻辑回归第 4 节接着讲怎么手写实现和调参第 5 节最后是踩坑记录第 6 节。前置知识只需要三样基本的矩阵乘法、标量求导、一点概率里的极大似然。矩阵求导如果不太熟也没关系我在推导里会同时给出分量形式和矩阵形式两条路径你可以挑自己顺的那条看。我个人的建议是第一次看矩阵形式卡住了就退回分量形式把每个下标写清楚慢慢就顺了。2. 最小二乘法的完整公式推导2.1 损失函数为什么选平方先把问题形式化。我们有 $n$ 个样本每个样本 $d$ 维特征。把数据排成矩阵 $X \in \mathbb{R}^{n \times d}$标签排成向量 $y \in \mathbb{R}^{n}$。为了把偏置 $b$ 一起处理掉习惯上给 $X$ 最前面加一列全 1这样 $w$ 的第一个分量就自动是 $b$公式可以统一写成 $Xw$。预测值和真实值的差叫残差第 $i$ 个样本的残差是 $y_i - x_i^T w$。最自然的想法是让所有残差加起来尽量小。但残差有正有负直接加会互相抵消一个预测得离谱的模型可能总残差是 0。所以要么取绝对值要么取平方。取绝对值得到的优化问题在零点不可导后面不好处理取平方就光滑多了处处可导而且对大误差惩罚更重。于是损失函数写成$$L(w) \sum_{i1}^{n}(y_i - x_i^T w)^2 |y - Xw|_2^2$$这里用二范数平方的写法是为了后面推导方便。注意这个损失对 $w$ 是凸的——它本质上是一个二次型二次型只要矩阵半正定就是凸的而 $X^TX$ 天然半正定所以这个问题有全局最优解不存在陷入局部极小值的烦恼。2.2 从分量求导到正规方程现在开始推。把二范数展开$$L(w) (y - Xw)^T(y - Xw) y^Ty - y^TXw - w^TX^Ty w^TX^TXw$$中间两项其实是标量而且 $y^TXw (X^Ty)^Tw w^TX^Ty$所以它们相等合并后$$L(w) y^Ty - 2w^TX^Ty w^TX^TXw$$到这里就是一个标准的二次型了。对 $w$ 求梯度用两条规则$\nabla_w (a^Tw) a$$\nabla_w (w^TAw) 2Aw$$A$ 对称时。所以$$\nabla_w L -2X^Ty 2X^TXw$$令梯度为零$$X^TXw X^Ty$$这个式子就叫正规方程。如果 $X^TX$ 可逆直接得到解析解$$w^* (X^TX)^{-1}X^Ty$$如果你对矩阵求导不放心走一遍分量形式。记第 $i$ 个样本的预测为 $\hat{y}i \sum_j x{ij}w_j$损失 $L \sum_i (y_i - \hat{y}_i)^2$。对某个 $w_k$ 求偏导$$\frac{\partial L}{\partial w_k} \sum_i 2(y_i - \hat{y}i)\cdot(-x{ik}) -2\sum_i x_{ik}(y_i - \hat{y}_i)$$把所有 $k$ 合起来写成向量$-2X^T(y - Xw)$和矩阵形式一致。两条路殊途同归我更推荐初学者先走分量这条路心不慌。2.3 几何视角投影与伪逆正规方程还有一个特别漂亮的几何解释理解了它你会对整个最小二乘有完全不同的感觉。$Xw$ 的所有可能取值构成一个空间叫 $X$ 的列空间记作 $\text{Col}(X)$。我们要在这个空间里找一个向量离 $y$ 最近。几何上最近点就是 $y$ 在 $\text{Col}(X)$ 上的正交投影。投影的几何特征是残差 $y - Xw$ 必须垂直于这个空间的每一列也就是 $X^T(y - Xw) 0$展开正是 $X^TXw X^Ty$。所以最小二乘本质上就是在做正交投影。这也顺带解释了为什么当 $X$ 列满秩时解唯一——列空间维度等于 $d$投影点唯一如果列不满秩比如两个特征完全线性相关列空间维度小于 $d$就有无穷多组 $w$ 给出同一个投影点这时候 $X^TX$ 不可逆得用伪逆$$w^* X^ y$$其中 $X^$ 是 Moore-Penrose 伪逆。实际计算里numpy.linalg.lstsq和scipy.linalg.lstsq默认就是用 SVD 求伪逆稳定得多我强烈建议遇到共线性数据时直接用它们不要硬求逆。注意直接算 $(X^TX)^{-1}$ 在数值上是个坏习惯。条件数会被平方放大float64 大约 16 位有效数字条件数超过 $10^8$ 你就开始丢精度了超过 $10^{16}$ 结果基本是垃圾。用solve而不是inv或者干脆用lstsq。2.4 一个手算例子把推导落地为了不让推导飘在空中我们手算一个最小规模的问题。假设只有两个样本一个特征且已经加了全 1 列$$X \begin{bmatrix}1 1\ 1 2\end{bmatrix}, \quad y \begin{bmatrix}1\ 3\end{bmatrix}$$先算 $X^TX$$$X^TX \begin{bmatrix}11\12\end{bmatrix}\begin{bmatrix}11\12\end{bmatrix} \begin{bmatrix}23\35\end{bmatrix}$$再算 $X^Ty \begin{bmatrix}11\12\end{bmatrix}\begin{bmatrix}1\3\end{bmatrix} \begin{bmatrix}4\7\end{bmatrix}$。解方程组 $\begin{bmatrix}23\35\end{bmatrix}w \begin{bmatrix}4\7\end{bmatrix}$。行列式 $2\times5 - 3\times3 1$逆矩阵是 $\begin{bmatrix}5-3\-32\end{bmatrix}$乘以右边得到 $w \begin{bmatrix}20-21\-1214\end{bmatrix} \begin{bmatrix}-1\2\end{bmatrix}$。验算一下$w_0 -1$ 是截距$w_1 2$ 是斜率。预测值是 $-1 2\times1 1$ 和 $-12\times2 3$两个样本都完全命中残差为 0。两个点确定一条直线结果合理。这个例子虽然简单但它把整条计算链路走通了你换成一百维、一万个样本流程一模一样只是矩阵大一点。3. 正则化岭回归与 Lasso 的推导3.1 岭回归为什么能让矩阵可逆上一节留了个隐患$X^TX$ 可能不可逆。实践中这件事比你想的常见——特征之间高度相关比如身高厘米和身高英尺同时入模、样本数少于特征数$n d$基因数据里太常见了都会让 $X^TX$ 奇异或接近奇异。岭回归的做法是在损失里加一项 $\lambda|w|_2^2$$$L(w) |y - Xw|_2^2 \lambda|w|_2^2$$同样求梯度令其为零$$-2X^Ty 2X^TXw 2\lambda w 0$$整理得$$(X^TX \lambda I)w X^Ty \quad\Rightarrow\quad w^* (X^TX \lambda I)^{-1}X^Ty$$关键在于为什么加了 $\lambda I$ 就可逆了。$X^TX$ 是实对称半正定矩阵特征值 $\mu_1 \ge \mu_2 \ge \dots \ge \mu_d \ge 0$。加上 $\lambda I$ 之后特征值全部变成 $\mu_j \lambda$。只要 $\lambda 0$所有特征值严格大于 0矩阵满秩必然可逆。这个操作的另一个名字叫吉洪诺夫正则化在数值分析里用了很多年。它还有一个统计解释等价于给 $w$ 加上一个高斯先验 $w \sim \mathcal{N}(0, \frac{1}{2\lambda}I)$然后做最大后验估计。也就是说岭回归不是硬把权重压小而是在说我事先相信权重应该靠近零数据如果强烈反对它还是会给出较大的权重只是反对的代价变高了。$\lambda$ 的取值直接决定压缩强度。$\lambda \to 0$ 退化成普通最小二乘$\lambda \to \infty$ 时 $w$ 全部趋向 0。实际调参走交叉验证通常在对数尺度上扫像[0.001, 0.01, 0.1, 1, 10, 100]这样。我个人习惯先扫三个数量级定位大致范围再在附近细扫。3.2 Lasso 的软阈值解是怎么冒出来的Lasso 把正则项换成 $\ell_1$ 范数$$L(w) |y - Xw|_2^2 \lambda|w|_1 \sum_i (y_i - x_i^Tw)^2 \lambda\sum_j |w_j|$$这个改动带来的性质完全不同Lasso 会把一部分权重精确压到 0产生稀疏解所以它同时干了特征选择的事。代价是 $|w_j|$ 在 0 点不可导没法直接求梯度得用次梯度或者坐标下降。先看一维问题把它看透了你就能理解整个 Lasso。设目标是最小化 $(y - w)^2 \lambda|w|$这里 $y$ 是当前的残差目标值。分三种情况当 $w 0$ 时$|w| w$求导得 $2(w - y) \lambda 0$即 $w y - \lambda/2$。这个解要成立需要 $y \lambda/2$。当 $w 0$ 时$|w| -w$求导得 $2(w - y) - \lambda 0$即 $w y \lambda/2$。需要 $y -\lambda/2$。当 $|y| \le \lambda/2$ 时0 点满足次梯度条件最优解就是 $w 0$。三种情况合起来写成$$w^* \text{sign}(y)\max(|y| - \lambda/2,\ 0)$$这就是软阈值算子。它的直观含义是把所有小于阈值的信号直接砍成零大于阈值的则整体往零的方向平移一个固定量。对比一下硬阈值小于阈值砍零大于的保持原值软阈值虽然引入了偏差但它是连续的处理起来稳定得多。把一维结论推广到多维用坐标下降每次只优化一个 $w_j$固定其他维度。设 $r_j y - \sum_{k \ne j}x_kw_k$ 是去掉第 j 维贡献后的残差那么 $w_j$ 的最优更新是$$w_j \leftarrow \frac{S(x_j^Tr_j,\ \lambda/2)}{|x_j|_2^2}$$其中 $S$ 就是上面的软阈值函数。实际实现时通常会先把每列特征标准化到 $|x_j|_2 1$这样分母恒为 1更新变成单纯的软阈值代码能短一大截。标准化的另一个好处是让 $\lambda$ 对所有特征一视同仁否则量纲大的特征天然占便宜惩罚力度不公平。3.3 岭回归和 Lasso 该怎么选这两个不是谁替代谁的关系选哪个取决于你的目标。我把常见判断整理成一张表维度岭回归Lasso正则项$\ell_2$$\ell_1$解的性质权重整体缩小不为零部分权重精确为零是否做特征选择否是特征高度相关时倾向于均分权重随机留一个丢掉其他闭式解有无需迭代适合场景特征都有用只想防过拟合特征多且稀疏想挑出关键项计算复杂度一次矩阵分解多轮迭代实践中还有一个折中方案叫弹性网把两个正则项一起加$$L(w) |y - Xw|_2^2 \lambda_1|w|_1 \lambda_2|w|_2^2$$它在特征成组相关的时候表现比纯 Lasso 稳因为 Lasso 在相关特征里随机挑一个的行为有时候挺让人抓狂的——换个随机种子选出来的特征就变了。弹性网会倾向于把相关的一组特征一起保留或一起剔除重复实验的结果更可复现。我做过一个用户行为预测的项目几百个特征里大量高度相关的衍生变量用纯 Lasso 每次跑出的特征集都不一样换成弹性网之后就稳定多了。提示用 Lasso 之前一定要做标准化。$\lambda$ 的量纲和特征量纲绑定不标准化的话同一个 $\lambda$ 对不同特征的实际惩罚强度完全不一样调参调出来的结果没有参考价值。4. 逻辑回归换一个损失函数的完整推导4.1 从回归到分类的关键一步线性回归输出的是实数直接拿来做二分类有个明显问题输出可以是任意实数没法解释成概率而且对异常值敏感。我们想要的是把 $z w^Tx$ 映射到 $(0,1)$ 区间。sigmoid 函数就是这个映射$$\sigma(z) \frac{1}{1 e^{-z}}$$它的性质很讨喜单调递增、值域 $(0,1)$、关于 $(0, 0.5)$ 中心对称而且导数形式特别干净$$\sigma(z) \sigma(z)(1 - \sigma(z))$$这个导数式子是后面所有推导能化简的关键一定要记住。验证一下对 $\sigma(z) (1e^{-z})^{-1}$ 求导用链式法则得 $-1e^{-z})^{-2}\cdot(-e^{-z}) \frac{e^{-z}}{(1e^{-z})^2}$。而 $\sigma(z)(1-\sigma(z)) \frac{1}{1e^{-z}}\cdot\frac{e^{-z}}{1e^{-z}}$正好相等。那这个函数是怎么来的不是随便拍脑袋选的。把它放到广义线性模型的框架里就很自然了GLM 假设 $y$ 服从某个指数族分布然后通过一个链接函数把均值 $\mu E[y]$ 和线性预测子 $z$ 连起来。二分类里 $y$ 服从伯努利分布它的自然参数恰好就是 logit 函数 $\ln\frac{\mu}{1-\mu}$。令这个等于 $z$反解出 $\mu \frac{1}{1e^{-z}}$sigmoid 就出来了。换句话说sigmoid 是伯努利分布对应的标准答案不是审美选择。4.2 极大似然估计的完整推导有了概率输出参数怎么估用极大似然。对第 $i$ 个样本模型给出 $p_i P(y_i 1|x_i)$那么观测到标签 $y_i$ 的概率可以统一写成$$P(y_i|x_i) p_i^{y_i}(1-p_i)^{1-y_i}$$当 $y_i 1$ 时右边是 $p_i$当 $y_i 0$ 时是 $1-p_i$一个式子覆盖两种情况。假设样本独立似然函数是连乘$$L(w) \prod_{i1}^{n}p_i^{y_i}(1-p_i)^{1-y_i}$$连乘容易下溢取对数变成求和得到对数似然$$\ell(w) \sum_{i1}^{n}\left[y_i\ln p_i (1-y_i)\ln(1-p_i)\right]$$我们要最大化它等价于最小化负对数似然后者就是常说的交叉熵损失。现在对 $w$ 求梯度。对单个样本把 $\ell_i$ 对 $z_i$ 求导$$\frac{\partial \ell_i}{\partial z_i} y_i\frac{1}{p_i}\cdot p_i(1-p_i) (1-y_i)\frac{-1}{1-p_i}\cdot p_i(1-p_i)$$第一项化简为 $y_i(1-p_i)$第二项化简为 $-(1-y_i)p_i$合并$$\frac{\partial \ell_i}{\partial z_i} y_i - p_i$$再乘上 $\frac{\partial z_i}{\partial w} x_i$得到$$\frac{\partial \ell}{\partial w} \sum_{i1}^{n}(y_i - p_i)x_i X^T(y - p)$$注意这个结果的形式——残差乘特征和线性回归的梯度 $X^T(y - \hat{y})$ 结构完全一样。区别只在于线性回归里残差是 $y - Xw$逻辑回归里是 $y - \sigma(Xw)$。这个统一性不是巧合背后是广义线性模型的共同结构几乎所有 GLM 的梯度都是这个形式。理解了这一点你看其他模型泊松回归、Softmax 回归时会发现全是一个套路。4.3 梯度下降与牛顿法的迭代公式逻辑回归没有闭式解因为 $X^T(y-p) 0$ 里的 $p$ 是 $w$ 的非线性函数解不出来。只能用迭代法。梯度下降最直接更新式是$$w^{(t1)} w^{(t)} \eta X^T(y - p^{(t)})$$注意这里是加号因为我们在最大化对数似然。如果写成最小化负对数似然就是减号。$\eta$ 是学习率。牛顿-拉弗森法收敛快得多它用二阶信息$$w^{(t1)} w^{(t)} - H^{-1}\nabla\ell$$其中 $H$ 是海森矩阵。对逻辑回归可以推出漂亮的闭式$$H \frac{\partial^2 \ell}{\partial w\partial w^T} -X^TSX$$其中 $S \text{diag}(p_i(1-p_i))$ 是对角矩阵。代入更新式$$w^{(t1)} w^{(t)} (X^TSX)^{-1}X^T(y - p)$$这个算法有名字叫迭代重加权最小二乘IRLS。为什么叫这个名字因为每次迭代都在解一个加权最小二乘问题权重就是 $S$样本的权重随 $p_i$ 变化——预测越接近 0.5 的样本模型越不确定权重越大。这个自适应机制让牛顿法通常五到十次迭代就能收敛到很高精度而梯度下降可能要几百上千次。代价是每次迭代要算 $d \times d$ 矩阵的逆复杂度 $O(d^3)$。特征维度上千时牛顿法就太重了实际中常用拟牛顿法L-BFGS近似sklearn 的LogisticRegression默认求解器lbfgs就是这个路子。维度上万的时候通常换 SGD 类的一阶方法用随机小批量降低单次迭代成本。方法单次迭代复杂度典型迭代次数适用维度梯度下降$O(nd)$数百到数千任意SGD/小批量$O(bd)$$b$ 为批大小数万但每次极快大规模稀疏牛顿法/IRLS$O(nd^2 d^3)$5 到 15$d$ 较小L-BFGS$O(nd md)$数十$d$ 中等4.4 多分类的 Softmax 推广二分类要推广到 $K$ 类做法是把 sigmoid 换成 softmax$$p_{ik} \frac{\exp(z_{ik})}{\sum_{c1}^{K}\exp(z_{ic})}, \quad z_{ik} w_k^Tx_i$$损失同样用负对数似然$$\ell \sum_{i1}^{n}\sum_{k1}^{K}y_{ik}\ln p_{ik}$$梯度还是那个熟悉的形式$$\frac{\partial \ell}{\partial w_k} \sum_i (y_{ik} - p_{ik})x_i$$又一次真实值减预测值。所以我一直说机器学习里一堆模型长得不同梯度长得很像抓住这个共性记忆负担能小很多。另外 softmax 有个细节要注意直接算 $\exp(z)$ 在 $z$ 很大时会溢出标准做法是先减去每行的最大值再取指数数学上等价但数值上安全得多。这个技巧叫 log-sum-exp 稳定化写代码时别忘了。5. 从公式到手写实现的完整流程5.1 数据准备与标准化推导讲完了现在动手。我建议先用自己生成的数据验证可控性强出问题好排查。用numpy造一份带噪声的线性数据import numpy as np rng np.random.default_rng(42) n, d 500, 5 X rng.normal(size(n, d)) true_w np.array([2.5, -1.2, 0.0, 3.7, 0.8]) y X true_w 0.3 * rng.normal(sizen)注意true_w里我特意放了一个 0这样后面能直观看到 Lasso 能不能把它干掉。接下来是标准化这一步千万别省mu X.mean(axis0) sigma X.std(axis0) sigma[sigma 0] 1.0 Xs (X - mu) / sigma Xs np.hstack([np.ones((n, 1)), Xs])最后一行给特征矩阵加了全 1 列当作截距项。这里有个容易踩的坑截距列不要参与正则化。很多新手的实现里把全 1 列也一起惩罚了结果就是 $\lambda$ 一大截距被压到 0预测整体平移误差莫名其妙变大。正确做法是在计算正则项时跳过第 0 个权重分量我下面的实现都用reg_w w.copy(); reg_w[0] 0这种写法处理。5.2 三个核心函数的实现与验证正规方程、岭回归、梯度下降写起来都很短def fit_ols(X, y): return np.linalg.lstsq(X, y, rcondNone)[0] def fit_ridge(X, y, lam): n, d X.shape reg lam * np.eye(d) reg[0, 0] 0.0 # 不惩罚截距 return np.linalg.solve(X.T X reg, X.T y) def fit_gd(X, y, lr0.05, epochs3000): n, d X.shape w np.zeros(d) for _ in range(epochs): g 2.0 / n * X.T (X w - y) w - lr * g return w验证一下一致性。上面生成数据的时候真实权重加上截距是[0, 2.5, -1.2, 0, 3.7, 0.8]第一个是截距因为我没加偏置所以是 0。跑一遍三个函数正规方程出来的结果应该在真实值附近摆动因为标准化之后特征均值为 0、方差为 1权重尺度可以直接对比。梯度下降的结果会稍微糙一点因为它是迭代逼近但跑到几千轮之后和正规方程的差距应该在 $10^{-3}$ 量级。如果两者的差距远大于这个量级先查两件事一是有没有忘记标准化二是学习率是不是太大导致震荡。判断震荡有个简单办法把每轮损失打印出来如果损失上下跳而不是单调下降就是学习率超了。逻辑回归的梯度下降版本def fit_logreg(X, y, lr0.1, epochs3000): n, d X.shape w np.zeros(d) for _ in range(epochs): z X w p 1.0 / (1.0 np.exp(-np.clip(z, -30, 30))) g X.T (p - y) / n w - lr * g return w那个np.clip是实践中必须的。$z$ 特别小的时候np.exp(-z)会溢出成inf导致结果是 0 或者 nan。裁剪到 $[-30, 30]$ 完全不影响数学结果但能救你一命。类似地自己实现 softmax 或者对数似然的时候一定要加这个防护。5.3 用 sklearn 对照与调参自己写完用 sklearn 验证一下能帮你确认实现没错from sklearn.linear_model import LinearRegression, Ridge, Lasso, LogisticRegression from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline from sklearn.model_selection import GridSearchCV pipe make_pipeline(StandardScaler(), Ridge()) param_grid {ridge__alpha: [0.001, 0.01, 0.1, 1.0, 10.0, 100.0]} gs GridSearchCV(pipe, param_grid, cv5, scoringneg_mean_squared_error) gs.fit(X, y) print(gs.best_params_, -gs.best_score_)这里alpha就是前面推导里的 $\lambda$。注意 sklearn 的Ridge默认fit_interceptTrue它内部会自动处理截距不惩罚的问题所以用它的管道时不用自己加全 1 列反而要记得不要手动加否则会出现两列常数StandardScaler还会把其中一列的标准差算成 0触发除以零的警告。另外GridSearchCV的cv参数别调太小。5 折是常规起点样本量少于 200 的时候我一般会换成LeaveOneOut或者 10 折因为 5 折下每折验证集太小方差大选出来的超参数不稳。样本量很大时可以降到 3 折省时间影响不大。关于学习率的选取有个粗略的经验区间可以救命特征标准化之后二阶目标函数的曲率上界大致对应最大特征值 $\mu_{max}$为保证收敛学习率应满足 $\eta 2/\mu_{max}$。你可以用np.linalg.eigvalsh(X.T X / n)把最大特征值算出来直接定一个安全的学习率。我做过一次实验标准化前最大特征值约 $10^6$标准化后降到个位数对应的安全学习率差了好几个数量级这就是为什么同样的学习率不标准化时会直接发散。提示如果你发现损失出现 nan按这个顺序排查——特征里有没有 inf/nan、学习率是不是过大、有没有出现log(0)、exp有没有溢出。这四类几乎能覆盖九成以上的数值问题。6. 实操踩坑与常见问题排查6.1 常见问题速查表把我在实际项目里遇到过的典型问题整理成表方便对照现象可能原因排查手段处理方式训练损失 nan学习率过大或特征未标准化打印每轮损失缩小学习率先标准化损失震荡不下降学习率超过 $2/\mu_{max}$算最大特征值学习率减半或更小正规方程报奇异矩阵特征共线或 $n d$查条件数np.linalg.cond改用lstsq或加岭正则系数方向与业务不符多重共线性导致符号翻转查特征相关系数矩阵剔除冗余特征或用岭回归测试集远差于训练集过拟合对比两者误差加大 $\lambda$ 或减特征Lasso 选出的特征不稳定相关特征间随机竞争换随机种子多跑几次改用弹性网逻辑回归在训练集上精度 100%数据可分导致权重发散看权重范数是否巨大加 $\ell_2$ 正则sklearn 默认已加预测概率全是 0 或 1权重过拟合或未校准画出概率直方图加正则、做 Platt 校准这个表里我特别想强调共线性导致系数符号翻转这一条。很多人看到某个特征的系数是负的但业务上明明是正相关第一反应是数据有问题。其实很可能是因为这个特征和另一个特征高度相关模型把正向的贡献记在了另一个特征头上这个特征只能拿一个负系数来补偿。判断方法很简单算一下特征之间的相关系数矩阵如果有超过 0.9 的配对基本就是这个问题。处理办法是删掉冗余的那个或者上岭回归让它自动均分。6.2 我自己踩过的几个坑第一个坑是忘了把截距排除在正则之外。当时做一个销量预测数据量不大我用 Lasso 做特征选择发现 $\lambda$ 稍微调大一点预测值整体就往下掉。折腾了半天才反应过来截距被惩罚压小了而销量基数本身是几万量级截距一动就是系统性偏移。后来在正则项里把第 0 维置零问题立刻消失。第二个坑是标准化时用了全量数据的统计量。这个在离线实验里看不出问题因为你是先标准化再切分数据的测试集的信息泄露到了训练过程里。上线之后换成流式处理标准化参数只能从历史数据估计效果就掉了一截。正确做法是把切分放在最前面StandardScaler只在训练集上fit然后transform验证集和测试集。用 sklearn 的Pipeline能自动帮你做对这件事所以我强烈建议把标准化和模型包在管道里。第三个坑是条件数被忽略。有一次处理的数据里一个特征以万元为单位另一个以元为单位差了四个数量级。正规方程跑出来的结果和 sklearn 差得离谱我以为是自己的推导错了重新检查了一遍公式才发现是数值问题。np.linalg.cond(X.T X)算出来是 $10^{10}$ 量级精度已经丢了一大半。标准化之后条件数降到两位数结果立刻对齐。从那以后我养成了习惯建模前先看一眼条件数超过 $10^6$ 就老老实实做预处理。第四个坑是逻辑回归完全可分。用一份特征区分度很高的数据训练训练精度刷到 100% 看着很爽但权重范数大得吓人换个测试集就崩。原因是对数似然在数据线性可分时没有有限最大值权重会一直往无穷大跑。解决办法就是加 $\ell_2$ 正则sklearn 的LogisticRegression默认C1.0C是 $\lambda$ 的倒数$\lambda 1/C$已经帮你兜住了。但如果你自己手写实现且不加正则就一定会遇到这个问题。我第一次手写的时候就是权重跑到上千exp直接溢出。6.3 一些能省时间的实操心得关于数据预处理连续特征标准化几乎是必须的类别特征做独热编码之后如果类别数很多可以考虑低频类别合并否则维度爆炸还容易过拟合。对于偏态很严重的连续特征比如收入、点击次数取对数变换往往比直接标准化效果更好因为它能把长尾压一压让线性关系更容易成立。关于特征工程的方向线性模型对非线性关系无能为力但你可以手工造非线性特征比如把 $x_1x_2$ 作为一个新特征加进去模型就能捕捉交互效应把 $x^2$ 加进去就能拟合抛物线。这叫多项式特征扩展配合岭回归用效果还不错缺点是维度增长很快$d$ 维的二次扩展有 $O(d^2)$ 项$d$ 上百之后就得慎重。关于评估指标的选取回归任务别只看 MSE如果数据里有离群点MAE 更能反映典型误差水平。分类任务在类别不平衡时别只看准确率一个 99% 是负类的数据集全预测负类就有 99% 准确率。这时候看 AUC 或者 F1 更靠谱。顺便说一句逻辑回归输出的概率在类别不平衡时会偏向多数类需要做阈值调整或概率校准。关于特征选择的流程我一般先用方差过滤掉常数列再用相关系数剔除共线性严重的最后才上 Lasso 做精细筛选。反过来先上 Lasso 的话冗余特征会稀释掉重要特征的权重选出来的结果反而不准。这个顺序看着是细节实际影响挺大。最后再分享一个小技巧就是用残差图诊断模型。把预测值做横轴、残差做纵轴画散点如果呈现明显的漏斗形预测越大残差越分散说明存在异方差可以考虑对目标做对数变换如果呈现 U 形说明有非线性关系没被捕捉该加多项式特征了。这个小图我每次建模都会画比看一堆指标直观得多花不了两分钟但经常能发现指标上看不出来的问题。
返回列表