1. 线性模型的设计思路拆解
1.1 为什么入门机器学习要先啃线性模型
拿到《机器学习》(西瓜书)第三章,开篇就是线性模型。我在第一次刷这本书的时候也有点困惑:深度学习、神经网络这么火,上来啃一个“线性的”东西,是不是有点过时了?后来踩了一圈工业界的坑才明白,线性模型真正牛的地方在于三个字:可解释。
你在实际业务里跑一个RNN或者Transformer,决策树一上就是几十层,模型是准了,但业务方问你“这个用户为什么被判定为高风险?”你说不出来,只能摊手。但换成线性模型,每个特征的权重w就是明晃晃的证据:收入每降低一个档位,风险分数上升0.3。这种透明度和可解释性,在金融风控、医疗诊断、电商信用分这些领域是不可让步的硬需求。这也是为什么我一直跟初学者说,别一上来就追深度学习,先把线性模型吃透,后面看神经网络你会突然发现,很多结构就是在线性模型外面套了一层非线性激活函数。
线性回归作为线性模型里最基础的成员,用途极广。它做预测、做基线、做特征筛选,甚至在深度学习项目里也经常被拿来做baseline——如果线性模型已经能跑出95%的效果,那你上复杂模型之前就得先掂量一下,那5%的提升值不值得付出数倍的训练和调参成本。这章节的内容不只是讲公式,而是在教你一套“如何用最简单的数学结构描述数据规律”的思维方式。
1.2 基本形式里的数学直觉
线性模型的基本形式写出来非常简洁:
f(x) = w1*x1 + w2*x2 + ... + wd*xd + b
其中x是样本的特征向量,w是权重(weight),b是偏置(bias)。写成向量形式就是:
f(x) = w^T * x + b
我第一次看到这个式子,觉得它简单到有点“不够看”。但后来在做特征工程的时候才意识到,这个形式本身就包含了巨大的设计空间。它把复杂的预测问题分解成了“每个特征的独立贡献叠加”,每个w的绝对值大小直接反映了该特征对最终结果的影响强度。特征之间的交互项统统没有,这意味着模型只关注“主效应”。
举一个生活化的例子:你要估算一套房子的价格。线性模型学的就是——地段评分每涨1分,房价涨5万;面积每增加1平米,房价涨0.8万;房龄每增加1年,房价跌1.2万。最终价格就是这些影响叠加起来,再加上一个基础价格b。没有“好地段的大户型会不会有溢价”这种交互效应。这既是线性模型的局限,也是它轻便、不易过拟合、容易解释的根源。
在西瓜书里,作者用一个相当简洁的框架把这章的内容串起来:线性回归解决“连续值预测”,对数几率回归解决“二分类”,线性判别分析则是从“降维+分类”的角度切入,最后再讨论多分类学习与类别不平衡问题。本章的知识密度相当高,值得逐字推敲。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 线性回归:从最小二乘到矩阵解法
2.1 损失函数到底在做什么
线性回归最经典的目标函数是均方误差(Mean Squared Error, MSE)。公式是:
E(w, b) = Σ(yi - f(xi))²
为什么要用“差的平方”而不是“差的绝对值”?我在给新人讲这个点时,会问一个看似无关的问题:你更在意预测差5块还是差10块?差别当然是5块。但如果一组预测的总误差相同,平方损失会惩罚那些“偏差特别大”的个体——差1和差9,平方后是1和81,绝对值和是10,而平方和是82。模型会拼命把那个差9的样本拉回来,因为它的“代价”更大。
从统计学的角度更严谨地说,如果我们假设噪声服从高斯分布,那么最小化均方误差就是最大似然估计的等价形式。这一点是西瓜书的推导核心之一。用大白话讲:在“误差是随机、独立、服从正太分布”的前提下,你去最小化平方误差,得到的w和b就是“最可能产生这批观测数据”的参数组合。数学上绕了一圈,最终殊途同归。
具体求解过程有两种路线。一种是解析解——直接用最小二乘法闭式求解;另一种是迭代解——用梯度下降一步步逼近。我建议初学者两条路线都亲手推一遍,因为前者让你看清问题的本质,后者让你理解深度学习时代所有优化算法的起点。
2.2 矩阵形式的推导与代码实现
当特征维度变成多个,也就是多元线性回归时,我们可以把所有样本堆叠成矩阵:
X = [[x11, x12, ..., x1d, 1], [x21, x22, ..., x2d, 1], ..., [xm1, xm2, ..., xmd, 1]]
注意这里最后一列加了一个1,目的就是把偏置b吸收到权重向量里,把问题简化成w^T*x的形式。这个trick在后续很多模型里都能看到,非常常用。
目标函数写成矩阵形式:
min ||Xw - y||²
对w求导并令导数为零,得到:
w* = (X^T X)^(-1) X^T y
这里的关键问题是X^TX是否可逆。在特征维度大于样本数或特征之间存在多重共线性时,X^TX是奇异矩阵,无法求逆。这个问题在实际业务里极其常见。我处理过一份营销数据集,十几个特征之间高度相关(比如“上月消费金额”和“季度总消费金额”),直接求逆就会出现数值爆炸。解决方案后面会讲到岭回归,本质上是在X^TX上加一个λI。
用Python实现一个闭式解的线性回归,十几行代码就够:
python复制import numpy as np
class LinearRegressionClosedForm:
def __init__(self):
self.w = None
def fit(self, X, y):
# 添加偏置项,将b吸收进w
X_with_bias = np.c_[X, np.ones(X.shape[0])]
# 闭式解 w* = (X^T X)^-1 X^T y
XTX = X_with_bias.T @ X_with_bias
if np.linalg.det(XTX) == 0:
raise ValueError("X^T X 不可逆,请检查特征是否存在多重共线性")
self.w = np.linalg.inv(XTX) @ X_with_bias.T @ y
return self
def predict(self, X):
X_with_bias = np.c_[X, np.ones(X.shape[0])]
return X_with_bias @ self.w
实测过一个小数据集,闭式解和sklearn的LinearRegression结果完全一致,但sklearn内部使用的是SVD分解而不是直接求逆,数值稳定性更好。所以实际项目里我建议直接调库,手推公式是为了让你理解发生了什么。
2.3 梯度下降什么时候才用得着
闭式解虽然优雅,但有一个致命问题:X^TX的求逆运算复杂度是O(n³)。当特征维度上万、样本量上百万时,内存和算力都扛不住。工业界用梯度下降更多,它属于迭代法,每轮只算一个梯度方向,更新参数:
w ← w - η * (∂E / ∂w)
其中η是学习率(learning rate)。
我自己第一次实现梯度下降时候踩过一个坑:忘记特征缩放。当时的特征有两个维度,一个是“年龄”(20~60),另一个是“年收入”(20000~2000000)。这导致损失函数的等高线被拉成了极长的椭圆,梯度下降在窄的方向上来回震荡,learning rate调大就发散,调小就半天不动。后来做了标准化,每个特征减去均值除以标准差,收敛速度肉眼可见地提升了。
分批量处理常见的是三种模式:批量梯度下降(Batch GD)、随机梯度下降(SGD)、小批量梯度下降(Mini-batch GD)。三者本质都是算梯度更新参数,区别在于每次更新用的样本量。SGD每看到一个样本就更新一次,参数震荡厉害但能跳出局部极小;Mini-batch GD在工业界用得最多,原因就是它能在GPU上高效地做矩阵运算,同时引入一定噪声避免陷入鞍点。线性回归是凸优化问题,不存在“局部极小”的困扰,所以用Mini-batch GD主要是图它快。
3. 对数几率回归:从回归到分类的跳跃
3.1 为什么线性回归不能直接做分类
在线性回归的框架下,输出是一个实数值。但二分类问题的输出是0或1。有人问:能不能直接把标签当作y,跑一个线性回归?比如把“好瓜”记为1,“坏瓜”记为0,然后预测分数大于0.5就判为好瓜?
理论上可以,但效果极差。原因有二:
第一,线性回归拟合的是实数输出,它允许预测值跑到0以下或1以上,而分类标签的取值范围是离散的0/1,这两者的“量纲”不对齐。
第二,训练样本分布不均衡时,线性回归的决策边界会被带偏。举个极端例子:好瓜样本有100个,坏瓜样本只有10个,线性回归做回归拟合时,为了让整体平方误差最小,决策面会严重偏向那个只有10个样本的类别。
所以从线性回归到分类,需要一个“映射函数”把实数输出压缩到[0, 1]区间,并赋予概率含义。西瓜书这里引出了“单位阶跃函数”,但阶跃函数不连续、不可导,不利于优化。实际使用的是sigmoid函数:
y = 1 / (1 + e^(-z))
其中z就是线性模型的输出 w^T x + b。这个函数在z趋于正无穷时y趋近1,z趋于负无穷时y趋近0,z等于0时y等于0.5,正好是“决策边界”的位置。
3.2 对数几率与决策边界的几何意义
为什么管它叫“对数几率回归”?因为把sigmoid函数做个变形:
ln(y / (1 - y)) = w^T x + b
这里的 y/(1-y) 叫作“几率”(odds),它衡量的是“正类概率”与“负类概率”的比值。取对数之后,线性模型的输出就成了“对数几率”(logit)。换句话说,逻辑回归并不是在直接预测类别,而是在预测“对数几率”这个连续的实数,然后再通过sigmoid函数映射成概率。
这个视角在金融风控领域非常实用。我做过一个信用卡违约预测项目,线上系统需要的不是“这个用户违约/不会违约”的二值结论,而是一个风险评分。逻辑回归输出的概率直接映射成1~100的风险分数,阈值由业务方根据资金成本动态调整。50分以下直接通过,70分以上拒绝,中间分数走人工审核。这就是对数几率回归相比SVM、树模型的一个天然优势:它输出的是概率,不是硬分类。
决策边界从哪里来?令 z = w^T x + b = 0,这是一个线性超平面。因为sigmoid是单调函数,z的正负直接决定概率是否大于0.5。所以在二维平面上,逻辑回归学到的决策边界永远是一条直线。如果你看到有人在二维平面上画了一个逻辑回归模型却有弯弯曲曲的边界,那一定是做了特征变换(比如多项式特征),模型本身仍然是“线性的”。
3.3 损失函数为什么不能沿用均方误差
线性回归用MSE没问题,但到了逻辑回归,如果沿用MSE,损失函数会变成一个非凸函数,里面有大量局部极小值。梯度下降很容易被困住,收敛效果极差。另外从概率角度讲,MSE并不是伯努利分布下的最大似然估计的合理损失。
逻辑回归用的是交叉熵损失(Cross Entropy Loss),二分类形式为:
Loss = -[ y * ln(p) + (1-y) * ln(1-p) ]
其中p是模型输出的正类概率。这个公式的直觉是:如果真实标签y=1,我们希望p尽量接近1;如果y=0,我们希望p尽量接近0。用对数函数做惩罚,预测概率离真实标签越远,损失增长越快——特别是当模型极度自信却错了的时候,损失会趋向无穷大,这种“重罚”特性让模型不容易出现过度自信的错误判断。
化简后你会得到一个非常漂亮的结果:交叉熵损失对参数向量w的梯度是:
∂Loss / ∂w = (p - y) * x
这个形式和线性回归的梯度惊人地一致,区别只在于线性回归的“预测值”是实数值,逻辑回归的“预测值”是压缩后的概率。正因如此,逻辑回归的梯度下降实现和线性回归几乎一模一样,只需要把预测输出套一个sigmoid即可。
我自己实践时通常用学习率0.1起步,配合L2正则化(后面细说),用Adam优化器可以直接从0.01开始。不过如果追求的是纯工业级稳定,直接用sklearn的LogisticRegression,设置penalty='l2',solver='lbfgs',训练速度和稳定性都很好。
4. 线性判别分析:从降维到分类的几何桥
4.1 LDA 的核心思想
线性判别分析(Linear Discriminant Analysis, LDA)和逻辑回归不同,它走的是另一条路线:把样本投影到一条直线上,让同类样本的投影点尽可能接近,异类样本的投影点尽可能远离。新样本投影后,看它离哪一类中心更近,就判为哪一类。
这个思路很像是在做“监督版的PCA”:PCA找的是方差最大的方向,不关心类别;LDA找的是“类间离散度 / 类内离散度”最大的方向。两者一个是为“表达数据”,一个是为“区分类别”。
为了衡量“接近”和“远离”,西瓜书里给出了两个散度矩阵的定义:
类内散度矩阵 Sw = Σ0 + Σ1,衡量每个类内部样本的分散程度。
类间散度矩阵 Sb = (μ0 - μ1)(μ0 - μ1)^T,衡量两个类中心之间的距离。
LDA的目标函数就是最大化:
J = (w^T Sb w) / (w^T Sw w)
这个式子有点像“信噪比”:分子是信号(类间差异),分母是噪声(类内差异),我们希望信号大、噪声小。
4.2 数学推导与求解过程
对J求w偏导并令其为零,经过整理可以推出:
w = Sw^(-1) (μ0 - μ1)
也就是投影方向只由“类内散度矩阵的逆”和“两个类均值之差”决定。推导中有一个关键细节:矩阵求导后得到的式子含有标量因子,但这个因子不影响方向,所以可以直接整理成这个简洁的形式。
这个结果非常优雅。编程实现也就几十行:
python复制import numpy as np
class LDA:
def fit(self, X, y):
# 假设二分类,标签为0和1
X0 = X[y == 0]
X1 = X[y == 1]
mu0 = X0.mean(axis=0)
mu1 = X1.mean(axis=0)
Sw = np.cov(X0.T) + np.cov(X1.T)
# 注意:这里是按样本数量加权更规范,直接用np.cov相当于无偏估计,但符号推导不变
self.w = np.linalg.inv(Sw + 1e-6 * np.eye(Sw.shape[0])) @ (mu0 - mu1)
return self
def project(self, X):
return X @ self.w
实际使用中有个细节:Sw在特征维度高、样本少时很可能不可逆,需要加一个很小的单位阵扰动作正则,或者先用PCA降维再跑LDA。
4.3 LDA和逻辑回归的适用场景
LDA和逻辑回归都能做二分类,但性格不同。
LDA对数据分布有更强的假设——它假设每个类内部服从高斯分布且有相同的协方差矩阵。如果数据真的符合这个假设,LDA在“小样本”场景下表现往往优于逻辑回归,因为它把“分布的期望”直接纳入了建模。逻辑回归不关心分布形态,它只关心决策边界。换句话说,逻辑回归更“懒”,不需要对数据做严格假设,但需要更多样本才能学得好。
我在实际使用中有一个经验法则:如果样本量小(几百级别)、且各类别特征分布接近正态,优先用LDA;如果样本量大、特征形态复杂、甚至有一些离群点,用逻辑回归更稳。当然在深度学习时代,LDA更常见的角色是“降维预处理”——把高维特征投影到低维空间,再喂给分类器,能有效降低过拟合风险。
5. 多分类学习与类别不平衡处理
5.1 OvO、OvR、MvM 三种策略拆解
现实业务里很少是干净的二分类,更多是三分类、五分类甚至上千分类。把二分类模型扩展到多分类,最基本的是两类拆分策略。
OvO(一对一):把N个类别两两配对,训练 N*(N-1)/2 个分类器。每个分类器只管区分其中两个类别。预测时让所有分类器投票,得票最多的类别获胜。优点是每个分类器只用在两个类的小样本上训练,速度快;缺点是分类器数量随类别数平方增长,当类别数上百时,训练成本会爆炸。
OvR(一对多):每次把一个类当作正类,其余所有类当作负类,训练N个分类器。预测时选取“置信度”最高的那个分类器对应的类别。缺点是每次训练都用到全部样本,类别多时算得慢;而且正负样本极不均衡(1: N-1),会影响训练效果。
MvM(多对多)在工业界效果更好,其中一种典型是“纠错输出码”(ECOC)。它把类别编码成二值码字,每个分类器学一个码位,最后用海明距离或欧氏距离找最近的类别。这一步有纠错能力:个别分类器出错,整体仍然能正确识别。我在一个文本多分类项目里试过ECOC,比纯OvR准确率提升了近2%,代价是训练时间增加,但可接受。
5.2 类别不平衡的问题与解决
类别不平衡在真实业务中极其常见:信用卡欺诈样本只有0.1%,疾病检测中患病样本只有5%,推荐系统里点击样本占比不到1%。如果直接训练模型,分类器会倾向于把所有样本都判为负类,因为整体准确率照样可以达到95%以上。
西瓜书给出的三种基本策略是:
欠采样(undersampling):从多数类中随机去掉一些样本,让正负类比例接近。缺点是丢数据,会损失信息。更高级的做法是“EasyEnsemble”——把多数类切成多份,每份和少数类组成一个训练集,训练多个模型再集成,既缓解了数据丢失,又提升了稳定性。
过采样(oversampling):复制少数类样本或者合成新样本。简单复制容易导致过拟合,实际操作中更推荐SMOTE算法——在少数类样本的近邻之间插值合成新样本。我在一个信贷违约项目中用过SMOTE,正样本召回率从18%提升到64%,效果拔群。
阈值移动(threshold moving):如果是二元分类,可以训练时不用刻意调整数据,保持原始分布,到预测时把决策阈值从0.5往多数类方向移动。比如偏重召回的场景,把阈值降到0.2,只要模型输出概率超过0.2就判为正类。这种方法最灵活,不用重训练,可以随时按业务需求调阈值。
实际操作中,我通常先跑一个“什么都不做”的基线模型,看它的混淆矩阵,再决定用哪种策略。如果“重采样”能让指标明显提升,就说明模型的偏差主要来自数据分布;如果重采样没什么帮助,那问题可能在特征本身需要重新做特征工程。
5.3 线性模型损失函数里的正则化参数
线性模型在工业界用得长,一个关键因素就是正则化参数好调。逻辑回归或线性回归的损失函数加上L2正则:
Loss = 原始损失 + λ * ||w||²
这里的λ控制惩罚力度。λ小,模型倾向于拟合训练数据,容易出现权重很大、过拟合;λ大,权重被压得很小,模型变得简单,但可能欠拟合。L1正则则倾向于把不重要的特征的权重压成0,天然起到了特征选择的作用。
我调参的一个习惯是:先用标准化的特征跑一组λ在1e-4到1e2之间的网格搜索,观察训练集和验证集损失曲线。当训练损失下降而验证损失上升时,说明开始过拟合了,此时离这段区域最近的λ就是可选值。在sklearn里直接可以用LogisticRegressionCV帮我们自动搜索,但对炼丹工程师来说,理解λ的物理意义比自动搜索更重要。
6. 常见问题与实操心得整理
6.1 特征归一化:线性模型的命门
线性模型对特征尺度极其敏感。原因在梯度下降部分提过,当特征的量纲不一致时,损失函数的等高线会被拉成椭圆形,收敛极慢。更重要的是,如果不做归一化,各个特征的权重w直接取决于特征的数值范围——身高用“米”和“厘米”两个单位,训练出来的w会差100倍,解释性就乱套了。
我通常用两种归一化:Z-score标准化(减去均值除以标准差)和Min-Max缩放(缩放到0~1)。注意一个坑:必须在训练集上计算均值、标准差,再应用到验证集和测试集,不能在全体数据上先算好均值再切分。否则会造成数据泄漏(data leakage),测试结果会虚高。
6.2 多元共线性怎么查、怎么治
做线性回归最怕多元共线性。判断方法可以看方差膨胀因子VIF,经验阈值是VIF大于10就认为存在严重共线性。有次我在一个房价预测任务里发现“建筑面积”和“使用面积”两个特征VIF高达48,模型权重忽正忽负,极其不稳定。处理办法要么删除其中一个,要么用岭回归加L2正则,让权重分布稳定下来。
第三个选择是主成分分析(PCA),把原始特征映射到正交的主成分方向上再回归。但这样会牺牲可解释性——主成分是特征的线性组合,没法直接说“某个原始特征提高了多少房价”。所以业务上要求解释的时候,我会优先考虑删特征和岭回归。
6.3 学习率选择的经验法则
对逻辑回归和线性回归的梯度下降,学习率初始值我一般从0.1开始试,观察损失曲线。如果损失震荡,就把学习率除以3;如果损失半天不动,就把学习率乘3。这个“三分法”看起来粗糙,实际操作比网格搜索高效得多。
一个更稳的进阶技巧是使用学习率调度:前10个epoch用较大的学习率快速下降,之后每N个epoch降为原来的0.9倍。配合Mini-batch梯度下降,基本能解决多数线性模型训练的收敛问题。从数据角度看,批量大小选32或64在当前硬件上效率最高,再大并不会节省太多训练时间,反而可能因为批次噪声减小而陷入不好的局部极小。
6.4 线性模型到非线性:怎么扩展
线性模型的表达能力确实有限,但扩展方式非常丰富。第一种是手写特征交叉,比如“收入×年龄”,相当于人为加入非线性;第二种是使用核方法,比如线性SVM换成核SVM,等价于在高维空间做线性划分;第三种是多项式特征扩展,将原始特征做二次、三次组合,再用正则化控制复杂度。
我的建议是:先从一个纯线性模型开始,记录基线效果;再逐步加特征变换和交互项,每加一步都做交叉验证。这样你能清楚地看到哪些特征交互对模型效果提升最大。跳过这一步直接上XGBoost、LightGBM,很容易迷失在调参里,反而说不清楚模型学到了什么。
6.5 线性模型在深度学习时代的定位
现在很多人问我,深度学习都这么强了,还有必要学线性模型吗?我的回答是:不仅要学,还要学扎实。线性模型是理解一切复杂模型的基石。多层神经网络去掉激活函数压缩成一层,本质上就是一个线性模型;Batch Norm、Dropout这些技巧的出发点,多少都能跟线性回归里的正则化、归一化对得上号。在实习和面试中,能讲清楚逻辑回归的损失函数推导、LDA的投影方向求解,通常比背出一堆Transformer结构更让面试官认可你的基础功。
实测下来,我在做深度学习项目时,也经常从线性baseline开始,用它的误差来对标复杂模型的最低收益。如果一个数据集连逻辑回归都能跑到AUC 0.9,说明特征已经很强了,模型复杂度带来的收益可能很有限;反之,如果线性模型AUC只有0.6,而复杂的树模型能飙到0.85,才说明这个任务的提升空间在模型侧。这种判断逻辑,只有真正把线性模型的内功修炼到位之后,才能用得顺手。
