ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

从线性回归到对率回归:二分类问题的核心原理与实战实现

2026/8/29 10:04:59 拓冰建站 浏览量
从线性回归到对率回归:二分类问题的核心原理与实战实现 1. 从线性回归到对率回归一个分类问题的诞生在机器学习入门时我们接触的第一个模型往往是线性回归。它的目标很直观找到一条直线或超平面让预测值y w^T x b尽可能接近真实的连续数值标签。比如预测房价、预测销售额模型输出的是一个实数。但当我们面对“是”或“否”、“猫”或“狗”这样的二分类问题时线性回归就显得力不从心了。你总不能强行让模型输出“0.7只猫”或者“-1.2条狗”吧这既不符合逻辑也缺乏概率解释。对率回归Logistic Regression就是为了解决这个问题而生的。别看名字里有“回归”它可是一个地地道道的分类模型。它的核心思想是把线性回归的输出z w^T x b通过一个特殊的函数“挤压”到 (0, 1) 区间内将这个值解释为样本属于正类的概率。这个特殊的函数就是Sigmoid函数也叫Logistic函数。为什么叫“对率”呢这其实是对“Logistic”的一种翻译更学术一点的叫法是“逻辑斯蒂回归”但“对率”更贴近其数学本质——它处理的是几率Odds的对数Logit。简单来说几率是“事件发生概率”与“事件不发生概率”的比值。对率回归模型实际上是在用线性模型去拟合这个几率的对数。这个转换过程正是模型能够处理分类问题的关键。所以当你翻开《机器学习》西瓜书的3.3节看到“对率回归”时你首先要明白你正在学习的是解决二分类问题的基石模型。它不仅是很多算法面试的必考题更是理解神经网络中神经元激活、以及更复杂分类模型如最大熵模型的重要阶梯。接下来我们就抛开书本上严谨但略显抽象的数学推导从“为什么要这么做”以及“具体怎么实现”的角度把这个模型掰开揉碎了讲清楚。2. Sigmoid函数概率的“转换器”与决策边界对率回归的精髓全在于Sigmoid函数。我们先来看看它的样子σ(z) 1 / (1 e^{-z})其中z就是我们熟悉的线性组合z w^T x b。这个函数图像是一个优美的S型曲线关于点 (0, 0.5) 中心对称。2.1 函数特性与概率解释这个函数有几个至关重要的特性决定了它为何适合做分类值域为 (0, 1)无论z是正无穷大还是负无穷大σ(z)的输出都被严格限制在0和1之间。这完美契合了“概率”的定义。我们可以将σ(z)的输出直接解释为样本x属于正类例如类别1的概率即P(y1 | x; w, b) σ(z)。单调递增函数是单调递增的。这意味着当z增大时即w^T x b越大σ(z)也越大样本被预测为正类的概率就越高。这符合我们的直觉特征与正类越“正相关”它是正类的可能性就越大。临界点与决策当z 0时σ(z) 0.5。这是一个天然的决策阈值。通常我们设定一个规则如果σ(z) 0.5则预测为正类y_hat 1如果σ(z) 0.5则预测为负类y_hat 0。由于σ(z) 0.5等价于z 0这个决策规则又可以简化为看w^T x b是否大于等于0。2.2 决策边界的本质这个简化后的决策规则w^T x b 0揭示了对率回归一个非常强大的特性它的决策边界是线性的。在二维特征空间里w1*x1 w2*x2 b 0就是一条直线。在更高维空间里这就是一个超平面。模型学习的过程就是寻找这个超平面的参数w和b使得这个超平面能最好地将两类样本分开。注意这里说的“线性”指的是决策边界关于特征x是线性的而不是指模型本身是线性的。模型σ(w^T x b)显然是非线性的。这是一个常见的误解点。对率回归是一种广义线性模型。一个生动的类比你可以把Sigmoid函数想象成一个“软开关”或者“概率挤压器”。线性部分z像一个打分器根据特征给样本打分。分数z可正可负可大可小。Sigmoid函数则负责把这个可能范围无限的分数平滑地、非线性地转换成一个介于0和1之间的概率值。分数越高开关越倾向于“开”正类分数越低开关越倾向于“关”负类。而0.5就是那个开关切换的临界点。理解了这个“转换器”的工作原理我们就明白了模型如何输出概率。但模型怎么知道什么样的w和b才是好的呢这就需要定义一个目标来告诉模型什么是“好”什么是“坏”。这个目标就是损失函数。3. 交叉熵损失函数衡量概率预测的“差距”在线性回归中我们使用均方误差MSE作为损失函数它衡量的是预测值与真实值之间的平方距离。但在分类问题中特别是当我们输出的是概率时MSE并不是最优选择。它对于概率误差的惩罚不够“尖锐”且可能导致优化过程陷入局部最优或收敛缓慢。对率回归使用的是交叉熵损失函数它源于信息论用来衡量两个概率分布之间的差异。在我们的二分类场景中一个是模型预测的概率分布(y_hat, 1-y_hat)另一个是样本真实的标签分布对于正类样本是(1, 0)对于负类样本是(0, 1)。3.1 损失函数的推导与形式对于一个样本(x^(i), y^(i))其中y^(i)是真实标签0或1y_hat^(i) σ(z^(i))是模型预测为正类的概率。我们希望当y^(i)1时y_hat^(i)尽可能接近1。当y^(i)0时y_hat^(i)尽可能接近0。一个很自然的想法是使用对数似然。我们希望最大化所有样本的预测概率的联合似然也就是最大化Π [P(y1)^y * P(y0)^(1-y)]。取负对数将最大化似然转化为最小化损失就得到了交叉熵损失对于单个样本的形式L(y^(i), y_hat^(i)) - [ y^(i) * log(y_hat^(i)) (1 - y^(i)) * log(1 - y_hat^(i)) ]我们来分析一下这个公式如果y^(i)1损失变为-log(y_hat^(i))。预测概率y_hat^(i)越接近1-log(y_hat)越接近0因为 log(1)0预测概率越接近0-log(y_hat)会趋向正无穷。这意味着当真实标签是1时如果你预测它是0的概率很大损失会变得极其巨大惩罚非常严厉。如果y^(i)0损失变为-log(1 - y_hat^(i))。分析同理。对于包含m个样本的训练集我们的目标就是最小化所有样本损失的平均值即代价函数J(w, b) (1/m) * Σ_{i1}^{m} L(y^(i), y_hat^(i))3.2 为什么交叉熵比MSE更好我们可以从梯度的角度来理解。对交叉熵损失函数求关于参数w_j的梯度你会得到一个非常简洁优美的形式∂J/∂w_j (1/m) * Σ_{i1}^{m} (y_hat^(i) - y^(i)) * x_j^(i)这个梯度表达式(预测值 - 真实值) * 特征值非常干净没有包含Sigmoid函数的导数σ(z)。而如果我们使用MSE损失梯度中会包含σ(z)。Sigmoid函数的导数σ(z) σ(z)(1-σ(z))有一个特性当预测值σ(z)接近0或1时即模型很“自信”时σ(z)会接近0。这意味着如果使用MSE在模型预测接近正确但尚未完全正确时比如真实为1预测为0.9梯度会非常小导致参数更新缓慢学习效率低下。这被称为“梯度饱和”或“梯度消失”。而交叉熵损失函数巧妙地避开了这个问题它的梯度大小与(预测值 - 真实值)成正比误差越大梯度越大更新越快误差越小更新越精细。这使得模型训练更加高效稳定。实操心得在实现神经网络时对于二分类问题的输出层几乎总是将Sigmoid激活函数与交叉熵损失函数配对使用。这被视为一个“黄金组合”。如果你在TensorFlow或PyTorch里看到BCELoss(Binary Cross Entropy Loss)它就是这里讨论的损失函数。记住这个组合它能帮你省去很多调试的麻烦。4. 参数求解梯度下降的实战推演有了损失函数J(w, b)我们的目标就明确了找到一组参数(w, b)使得J(w, b)最小。由于对率回归的损失函数是凸函数对于线性可分或近似可分的数据我们可以使用梯度下降法来找到这个全局最优解或局部最优但凸函数保证了全局最优。4.1 梯度计算与更新公式我们前面已经得到了损失函数对单个参数w_j的偏导数。对于偏置项b其推导类似因为b可以看作是一个对应特征值恒为1的权重。因此∂J/∂w_j (1/m) * Σ_{i1}^{m} (y_hat^(i) - y^(i)) * x_j^(i)∂J/∂b (1/m) * Σ_{i1}^{m} (y_hat^(i) - y^(i))在代码实现中我们通常使用向量化操作来同时更新所有参数。设X为m x n的特征矩阵m个样本n个特征通常已添加了一列1以包含bY为m x 1的标签向量W为(n1) x 1的参数向量包含b。则向量化的梯度为dW (1/m) * X^T (A - Y)其中A σ(X * W)是模型对所有样本的预测向量。梯度下降的更新规则为W W - α * dW其中α是学习率控制着每次参数更新的步长。4.2 代码实现框架与细节下面我们用Python和NumPy来勾勒一个简易的对率回归训练框架并讨论关键细节import numpy as np class LogisticRegression: def __init__(self, learning_rate0.01, num_iterations1000): self.lr learning_rate self.num_iter num_iterations self.w None self.b None def _sigmoid(self, z): # 防止数值溢出对输入进行裁剪 z np.clip(z, -500, 500) return 1 / (1 np.exp(-z)) def _initialize_parameters(self, dim): # 权重初始化通常使用小的随机数偏置初始化为0 self.w np.random.randn(dim) * 0.01 self.b 0.0 def _propagate(self, X, Y): m X.shape[0] # 前向传播 Z np.dot(X, self.w) self.b A self._sigmoid(Z) # 预测值 A # 计算损失交叉熵 cost - (1/m) * np.sum(Y * np.log(A) (1-Y) * np.log(1-A)) # 反向传播计算梯度 dw (1/m) * np.dot(X.T, (A - Y)) db (1/m) * np.sum(A - Y) grads {dw: dw, db: db} return grads, cost def fit(self, X, Y): # 初始化参数 self._initialize_parameters(X.shape[1]) costs [] # 记录损失历史用于绘图检查收敛 for i in range(self.num_iter): # 计算梯度和当前损失 grads, cost self._propagate(X, Y) dw grads[dw] db grads[db] # 更新参数 self.w self.w - self.lr * dw self.b self.b - self.lr * db # 每100次迭代记录一次损失 if i % 100 0: costs.append(cost) print(fIteration {i}: cost {cost}) return self def predict_proba(self, X): # 输出概率 Z np.dot(X, self.w) self.b A self._sigmoid(Z) return A def predict(self, X, threshold0.5): # 输出类别标签 proba self.predict_proba(X) return (proba threshold).astype(int)关键细节与避坑指南数值稳定性在_sigmoid函数中我们对z进行了裁剪 (np.clip)。这是因为当z的绝对值非常大时np.exp(-z)可能会溢出变成无穷大或0导致计算错误。裁剪到一个安全范围如[-500, 500]是常见的做法。更鲁棒的做法是在计算交叉熵损失时直接将对数计算与Sigmoid输出结合使用np.logaddexp等函数但裁剪法对于理解原理足够直观。参数初始化权重w不能初始化为0。如果所有权重初始为0那么每个神经元这里就一个输出神经元的计算结果相同梯度也相同参数更新会对称阻碍了有效的学习。通常使用小的随机高斯噪声初始化如np.random.randn() * 0.01。偏置b初始化为0是可以的。学习率选择学习率α是最重要的超参数之一。太大可能导致损失震荡甚至发散太小则收敛缓慢。常见的策略是从一个标准值开始如0.01观察损失曲线。如果损失下降很慢可以适当增大如果损失震荡或不降反升必须减小。也可以使用学习率衰减策略。迭代终止我们设定了固定的迭代次数num_iterations。更好的做法是设置一个收敛条件例如当连续两次迭代的损失值变化小于某个阈值ε时就停止迭代。在实际中固定迭代次数并观察损失曲线是否已平稳也是一种实用方法。特征缩放虽然对率回归不像基于距离的模型如KNN、SVM那样严格要求特征缩放但进行标准化零均值、单位方差或归一化缩放到[0,1]通常能加速梯度下降的收敛过程因为所有特征都在相近的尺度上梯度更新方向会更直接地指向最优点。5. 从二分类到多分类OvR与Softmax回归标准的对率回归是二分类器。但现实世界的问题往往是多类的例如识别手写数字0-9。如何将对率回归扩展到多分类主要有两种策略OvR和Softmax回归。5.1 一对多策略一对多One-vs-Rest, OvR是最直接、最常用的扩展方法。假设有K个类别。训练我们训练K个独立的二分类对率回归模型。对于第k个模型我们将类别k的样本作为正类标签1将所有其他类别的样本作为负类标签0。预测对于一个新样本我们让这K个模型都对其进行预测每个模型输出一个属于其对应正类的概率P(yk | x)。然后我们选择概率最高的那个类别作为最终预测结果prediction argmax_{k} P(yk | x)。优点简单易于理解和实现。只需要训练K个二分类模型计算相对可控。每个模型的训练可以使用全部数据。缺点当类别数量K很大时需要训练很多模型。可能存在“类别不平衡”问题对于第k个模型正类样本只有第k类负类样本包含其他所有K-1类负样本通常远多于正样本。由于每个分类器是独立训练的其输出的概率值P(yk | x)并不是在一个归一化的概率框架下产生的它们的和不一定为1虽然取最大值通常可行但严格来说这些概率值不可直接比较。不过在实践中这往往不是大问题。5.2 Softmax回归更优雅的归一化多分类Softmax回归是对率回归在多分类问题上的自然推广有时直接被称为多类对率回归。它直接输出一个样本属于每个类别的概率分布且所有类别的概率之和为1。模型修改对于K个类别我们需要K组参数(w_1, b_1), (w_2, b_2), ..., (w_K, b_K)。对于输入x我们为每个类别计算一个“得分”z_k w_k^T x b_k。Softmax函数Softmax函数扮演了Sigmoid在多分类中的角色。它将K个得分转换成一个概率分布P(yk | x) e^{z_k} / Σ_{j1}^{K} e^{z_j}这个公式确保了所有P(yk | x)非负且和为1。损失函数使用交叉熵损失的多分类版本。对于真实标签为k的样本其损失为L -log(P(yk | x))。模型的目标是最小化所有训练样本的平均交叉熵损失。Softmax回归 vs. OvR输出Softmax直接输出归一化的概率分布更符合概率解释。OvR输出的是K个独立的、未归一化的“置信度”。训练Softmax回归是端到端一次训练所有参数共同优化考虑到了类别之间的竞争关系。OvR是独立训练K个模型。适用性对于类别互斥一个样本只属于一个类的问题Softmax回归通常更受青睐尤其是作为神经网络的输出层。对于类别可能有重叠或样本可能属于多个类的问题多标签分类OvR策略更合适。实操选择在很多机器学习库中如scikit-learn的LogisticRegression当设置multi_classovr时使用OvR策略设置multi_classmultinomial时则使用Softmax回归并配合solverlbfgs或newton-cg等支持它的优化器。对于大多数互斥多分类问题直接使用multi_classmultinomial是更好的默认选择。6. 正则化应对过拟合的利器对率回归模型同样会面临过拟合的问题特别是当特征维度很高或特征之间存在多重共线性时。过拟合的模型在训练集上表现很好但在未见过的测试集上表现糟糕。正则化是解决过拟合的核心技术通过对损失函数添加一个惩罚项来约束模型参数的大小鼓励模型更简单。6.1 L1与L2正则化最常用的两种正则化是对率回归损失函数中加入L1范数或L2范数惩罚。L2正则化岭回归在损失函数中加入所有权重w的平方和L2范数乘以一个正则化系数λ。J(w, b) (1/m) * Σ L(...) (λ / (2m)) * Σ w_j^2L2正则化会让权重整体趋向于变小但通常不会完全变为0。它倾向于让模型的所有特征都贡献一点信息而不是依赖少数几个特征。这使得模型更加稳定抗干扰能力更强。L1正则化Lasso回归在损失函数中加入所有权重w的绝对值之和L1范数乘以一个正则化系数λ。J(w, b) (1/m) * Σ L(...) (λ / m) * Σ |w_j|L1正则化有一个非凡的特性它会产生稀疏解。这意味着在优化过程中许多不重要的特征的权重会被精确地压缩到0。因此L1正则化天然具有特征选择的功能。如果你怀疑很多特征是不相关的使用L1正则化可以帮助你自动识别出重要的特征。6.2 正则化的影响与超参数调优添加正则化项后梯度下降的更新公式也需要相应修改。以L2正则化为例梯度更新变为w_j w_j - α * [ (∂J/∂w_j)_原始 (λ/m) * w_j ]可以看到每次更新时权重w_j会额外减去(αλ/m) * w_j这相当于在每次迭代中都让权重“衰减”一点从而防止其变得过大。正则化系数λ的选择λ是一个超参数需要手动调整例如通过交叉验证。λ 0没有正则化模型可能过拟合。λ太大正则化惩罚过强所有权重被过度压缩模型可能变得过于简单导致欠拟合高偏差。模型可能会忽略数据中的有效模式。调优实践通常的做法是将数据分为训练集、验证集和测试集。在训练集上使用不同的λ值例如[0, 0.001, 0.01, 0.1, 1, 10]训练多个模型然后在验证集上评估它们的性能如准确率、F1分数。选择在验证集上性能最好的那个λ值。最后用这个λ在完整的训练集验证集上重新训练模型并在测试集上做最终评估。注意正则化通常只惩罚权重w而不惩罚偏置项b。因为b只是控制决策边界的偏移其大小与模型复杂度关系不大。在计算梯度时对b的更新保持不变。7. 模型评估、局限性与实战思考训练好一个对率回归模型后我们如何评价它它又有什么局限性7.1 超越准确率全面的评估指标对于分类问题不能只看准确率尤其是当数据类别不平衡时例如99%的样本是负类1%是正类。一个把所有样本都预测为负类的“笨”模型准确率也能达到99%但它毫无用处。更全面的评估需要混淆矩阵以及由此衍生的指标精确率在所有被预测为正类的样本中真正为正类的比例。Precision TP / (TP FP)。关注预测的“准不准”。召回率在所有真实为正类的样本中被正确预测出来的比例。Recall TP / (TP FN)。关注“找得全不全”。F1分数精确率和召回率的调和平均数F1 2 * (Precision * Recall) / (Precision Recall)。是综合衡量指标。ROC曲线与AUC通过不断调整分类阈值默认0.5计算真正例率和假正例率绘制出的曲线。曲线下的面积AUC衡量的是模型整体上的排序能力将正样本排在负样本前面的能力对类别不平衡不敏感是一个非常鲁棒的指标。对于对率回归由于它输出的是概率我们可以很方便地通过调整阈值来权衡精确率和召回率以满足不同的业务需求例如在疾病筛查中我们可能更看重召回率宁错勿漏在垃圾邮件过滤中可能更看重精确率宁漏勿错。7.2 对率回归的局限性尽管强大且经典对率回归也有其天花板决策边界线性这是其最大的限制。对率回归的决策边界始终是一个超平面。这意味着它只能处理线性可分或近似线性可分的数据。对于像“异或”问题这样简单的非线性可分数据对率回归无能为力。特征需要独立虽然模型本身不要求特征完全独立但如果特征之间存在高度多重共线性会导致参数估计不稳定标准误增大使得解释模型变得困难。正则化可以在一定程度上缓解这个问题。容易受极端值影响虽然比线性回归稳健但极端特征值仍然可能对Sigmoid函数的输出和决策边界产生较大影响。7.3 突破局限特征工程与核方法如何让对率回归处理非线性问题答案不在模型本身而在数据表示上。特征工程这是最实用、最有效的方法。通过人工或自动化的方式构造新的特征。例如对于原始特征x1, x2我们可以添加多项式特征x1^2, x2^2, x1*x2等。这样在原始空间非线性可分的数据在特征变换后的高维空间可能就变得线性可分了。对率回归在这个新的特征空间里仍然学习一个线性决策边界但这个边界映射回原始空间就变成了非线性的。核方法类似于支持向量机理论上可以对率回归“核化”即使用核函数隐式地将数据映射到高维空间在高维空间进行线性分类。这被称为核逻辑回归。但在实际中由于其计算复杂度较高远不如带核的SVM或基于特征工程的方法常用。最后的个人体会对率回归远不止一个简单的分类算法。它是我理解整个统计机器学习范式的起点。从它的概率解释、交叉熵损失、到梯度下降优化这套“建模-定义损失-优化求解”的流程是深度学习乃至许多现代机器学习模型的通用范式。在实际项目中它常常作为强基线模型首先被建立。它的结果具有可解释性权重的大小和正负代表了特征的影响方向和程度这对于需要模型解释性的领域如金融风控、医疗诊断至关重要。虽然现在深度学习风头正劲但对率回归因其简单、高效、可靠在工业界仍然占据着不可替代的一席之地。吃透它绝对是稳赚不赔的投资。