1. 这不是“调包”指南,而是一次亲手推导逻辑回归的硬核复盘
你有没有过这种感觉:在 Jupyter Notebook 里敲下from sklearn.linear_model import LogisticRegression,再跑个.fit(X, y),模型就训好了,准确率也还凑合——但当同事问起“为什么这里用 sigmoid 而不是 tanh?”“为什么损失函数非得是交叉熵,不能用 MSE?”“如果我把四个类别硬塞进一个二分类模型,会崩成什么样?”——你突然卡壳了。这不是你的问题,而是绝大多数人学逻辑回归时被跳过的那一环:它不是黑箱,而是一套有明确数学动机、可推导、可质疑、可拆解的完整决策链。这篇内容,就是我过去三年带十多个数据科学新人从零手推逻辑回归时,反复打磨出的“认知脚手架”。它不讲 API 参数怎么填,不列 scikit-learn 的文档截图,而是带你回到 1958 年 David Cox 提出 logit 模型的那个清晨,理解为什么我们必须把线性组合映射到 (0,1) 区间,为什么“概率”这个输出形式本身,就决定了整个模型的骨架。核心关键词——逻辑回归、log-odds、sigmoid 函数、最大似然估计、One-vs-All、One-vs-One——每一个都不是术语堆砌,而是你在调试模型时真正要掰开揉碎去检查的关节。适合谁?适合刚写完第一个LinearRegression感觉很酷,但面对LogisticRegression的predict_proba输出却不敢深想的同学;适合在面试中被问“为什么不用 MSE 做分类损失”而支吾半天的工程师;更适合那些已经用熟了OneVsRestClassifier,但某天深夜盯着训练日志里某个类别的 precision 突然掉到 0.3,想不通问题到底出在数据、特征,还是算法底层逻辑的人。这不是速成课,这是一次必须动笔、必须画图、必须自己算几组数字才能真正过关的硬核复盘。
2. 从线性回归的“失效现场”开始:为什么分类问题不能硬套回归思维
2.1 线性回归在分类任务上的三重崩塌
我们先不做任何推导,直接看一个最朴素的尝试:把二分类标签y ∈ {0, 1}当作连续值,强行用线性回归拟合。假设你有一组学生数据:X = [学习时长(小时)],y = [是否通过考试(0=未通过,1=通过)]。你用最小二乘法(LSE)拟合出一条直线:ŷ = -0.5 + 0.3 * x。当x = 2小时,ŷ = 0.1;当x = 10小时,ŷ = 2.5。问题立刻浮现:
输出越界:
ŷ = 2.5显然超出了[0, 1]的合理概率范围。你无法解释“250% 的通过概率”是什么意思。线性回归的输出空间是(-∞, +∞),而分类问题的预测目标——事件发生的概率——天然被约束在(0, 1)开区间内。这是根本性的空间错配。语义失真:即使
ŷ恰好落在[0, 1]内,比如ŷ = 0.7,它真的能被解读为“70% 的通过概率”吗?不能。线性回归的目标是最小化(ŷ - y)²的均值,它关心的是数值上的接近,而非概率意义上的合理性。它没有内置任何机制来保证:当x微小增加时,ŷ的增长速率会自然放缓,最终趋近于 1;当x很小时,ŷ会自然趋近于 0。它只是一个无约束的线性函数,不具备概率模型所需的S 形饱和特性。决策边界僵化:线性回归的决策边界(即
ŷ = 0.5的点)是固定的、由损失函数强加的。但在真实世界中,不同场景对“通过”的定义可能不同。比如,一门高难度课程,可能需要ŷ > 0.7才算有把握通过;而一门基础课,ŷ > 0.4就足够了。线性回归无法提供一个灵活、可解释的概率尺度来支持这种业务逻辑的调整。
提示:我见过太多项目,因为没意识到这点,直接把线性回归的输出当作概率用在风控评分上,结果导致阈值调优完全失效。概率不是标尺,而是决策的原材料,它必须具备可加性、可比性和业务可解释性。
2.2 Log-Odds:连接线性世界与概率世界的“翻译官”
既然线性模型输出(-∞, +∞),而概率要求(0, 1),那我们需要一个严格单调、可逆的函数,能把无限长的实数轴“压缩”进有限的单位区间。这个函数就是logistic function(逻辑函数),它的逆函数,就是logit function(log-odds)。
让我们从最基础的概率定义出发。设p是事件发生的概率,那么1-p就是其不发生的概率。Odds(几率)定义为p / (1-p)。它的含义非常直观:如果p = 0.75(75% 概率发生),那么odds = 0.75 / 0.25 = 3,意思是“发生的可能性是不发生的 3 倍”。Odds 的取值范围是[0, +∞):p=0时 odds=0;p=1时 odds→+∞;p=0.5时 odds=1(等可能发生)。
Odds 已经比p更“线性”了一些,但它依然不是无限的。于是,我们对 Odds 取自然对数,得到Log-Odds(对数几率):logit(p) = ln(p / (1-p))。这个变换的魔力在于:
- 当
p → 0⁺时,p/(1-p) → 0,ln(0) → -∞ - 当
p → 1⁻时,p/(1-p) → +∞,ln(+∞) → +∞ - 当
p = 0.5时,logit(0.5) = ln(1) = 0
因此,logit(p)的取值范围完美匹配了线性模型的输出空间(-∞, +∞)。它就像一座桥,一端连着线性模型的“直白世界”,另一端连着概率的“有界世界”。
2.3 从 Log-Odds 回到概率:Sigmoid 函数的诞生
既然logit(p) = z(其中z是线性组合b₀ + b₁x₁ + ... + bₙxₙ),那么我们只需要对等式两边同时做exp()和代数变形,就能得到p关于z的表达式:
logit(p) = ln(p / (1-p)) = z => p / (1-p) = e^z => p = e^z * (1-p) => p = e^z - p * e^z => p + p * e^z = e^z => p * (1 + e^z) = e^z => p = e^z / (1 + e^z)将分子分母同除以e^z,得到更常见的形式:p = 1 / (1 + e^(-z))。
这就是大名鼎鼎的Sigmoid 函数σ(z)。它是一个 S 形曲线,具有以下关键性质:
- 平滑可导:
dσ(z)/dz = σ(z) * (1 - σ(z)),这为梯度下降提供了完美的数学基础。 - 有界输出:
σ(z) ∈ (0, 1),完美满足概率的定义。 - 中心对称:
σ(0) = 0.5,σ(-z) = 1 - σ(z),这赋予了模型天然的“平衡”感。 - 渐近饱和:当
z → +∞,σ(z) → 1;当z → -∞,σ(z) → 0,这模拟了现实世界中“量变引起质变”的过程。
所以,逻辑回归的完整链条是:输入特征X→ 线性组合z = X·β→ Log-Oddsz→ Sigmoid 映射p = σ(z)→ 概率输出。这个链条里,z是模型真正“学习”的部分,它是一个线性模型;而σ(z)只是一个确定性的、不可学习的“激活函数”,它的唯一作用,就是把线性模型的输出,翻译成人类和业务都能理解的概率语言。这才是“逻辑回归”名字的由来——它回归的是logit(p),而不是p本身。
3. 损失函数的选择:为什么是最大似然估计(MLE),而不是最小二乘(LSE)
3.1 最小二乘(LSE)在分类问题上的“水土不服”
回到那个学生考试的例子。如果我们错误地使用 LSE 作为损失函数,目标是最小化∑(σ(zᵢ) - yᵢ)²。这看起来很直观,但问题在于,yᵢ是离散的0或1,而σ(zᵢ)是一个平滑的、介于0和1之间的数。LSE 会惩罚所有偏离,无论σ(zᵢ)是0.1还是0.9,只要它离yᵢ=1有0.9的差距,它就被同等对待。这忽略了概率模型的核心思想:我们希望模型对“确定性高”的预测(如σ(z)=0.99)给予更高置信度,对“模棱两可”的预测(如σ(z)=0.51)保持警惕。LSE 无法区分这两种情况,它只认数值差。
更致命的是,LSE 的梯度2*(σ(z)-y)*σ'(z)在σ(z)接近0或1时会变得极小(因为σ'(z)在两端趋近于0),导致梯度消失,模型在训练后期几乎无法更新参数。这是一个典型的优化陷阱。
3.2 最大似然估计(MLE):让模型“猜中”观测数据的概率最大化
逻辑回归的哲学是:给定一组参数β,我们希望模型预测出当前观测到的y标签的联合概率尽可能大。这就是最大似然估计(MLE)的思想。
对于单个样本i,其标签yᵢ只能是0或1。我们可以将模型的预测pᵢ = σ(zᵢ)视为P(yᵢ=1 | xᵢ)。那么,P(yᵢ=0 | xᵢ) = 1 - pᵢ。我们可以用一个统一的公式来表达这个概率:P(yᵢ | xᵢ) = pᵢ^yᵢ * (1 - pᵢ)^(1-yᵢ)
这个公式非常巧妙:
- 如果
yᵢ = 1,则公式变为pᵢ¹ * (1-pᵢ)⁰ = pᵢ,即模型预测为1的概率。 - 如果
yᵢ = 0,则公式变为pᵢ⁰ * (1-pᵢ)¹ = 1 - pᵢ,即模型预测为0的概率。
对于整个数据集,所有样本是独立同分布的,所以联合概率就是所有单个概率的乘积:L(β) = ∏ᵢ P(yᵢ | xᵢ) = ∏ᵢ [pᵢ^yᵢ * (1 - pᵢ)^(1-yᵢ)]
直接优化这个乘积很麻烦(容易下溢),所以我们取对数,得到对数似然函数:ℓ(β) = log L(β) = ∑ᵢ [yᵢ * log(pᵢ) + (1-yᵢ) * log(1-pᵢ)]
我们的目标,就是找到一组参数β,使得ℓ(β)最大。这正是逻辑回归的优化目标。
3.3 交叉熵损失:对数似然的“负号”变身
在机器学习实践中,我们通常不直接最大化ℓ(β),而是最小化它的负数,即-ℓ(β)。这个-ℓ(β)就是著名的二元交叉熵损失(Binary Cross-Entropy Loss):J(β) = -ℓ(β) = -∑ᵢ [yᵢ * log(pᵢ) + (1-yᵢ) * log(1-pᵢ)]
为什么这么做?因为几乎所有优化器(如 SGD、Adam)都是为“最小化”问题设计的。从信息论角度看,交叉熵衡量的是用模型预测的概率分布q = [pᵢ, 1-pᵢ]来编码真实分布p = [yᵢ, 1-yᵢ]所需的额外比特数。最小化交叉熵,就是在让模型的预测分布无限逼近真实分布。
现在,我们来计算这个损失函数关于参数β的梯度,以验证它的优越性。以单个样本为例,p = σ(z),z = x·β。根据链式法则:∂J/∂β = (∂J/∂p) * (∂p/∂z) * (∂z/∂β)
其中:
∂J/∂p = -[y/p - (1-y)/(1-p)] = (p - y) / [p(1-p)]∂p/∂z = σ'(z) = σ(z)(1-σ(z)) = p(1-p)∂z/∂β = x
所以,∂J/∂β = [(p - y) / (p(1-p))] * [p(1-p)] * x = (p - y) * x
这个梯度简洁得令人惊叹:∇J = (σ(z) - y) * x。它不再包含任何复杂的σ'项,不会在两端消失。当预测p远离真实标签y时(比如p=0.1,y=1),梯度(0.1-1)*x = -0.9x很大,模型会大力修正;当预测p非常接近y时(比如p=0.99,y=1),梯度0.01x很小,模型微调即可。这是一种自适应的学习强度,完美契合了概率建模的直觉。
实操心得:我在调试一个医疗诊断模型时,发现用 LSE 训练的模型在验证集上 AUC 一直卡在 0.7 左右,换成交叉熵后直接跃升到 0.85。根本原因就是 LSE 对“高置信度错误预测”(比如把一个高危病人预测为
p=0.01)的惩罚太轻,而交叉熵会施加巨大的、指数级的惩罚,迫使模型正视这些关键错误。
4. 多分类的两种经典策略:One-vs-All 与 One-vs-One 的深度拆解
4.1 问题的本质:二分类是基石,多分类是架构
逻辑回归原生就是一个二分类算法。当我们面对K > 2个类别(比如鸢尾花的setosa,versicolor,virginica)时,我们不能简单地把y设为0,1,2然后扔给模型——这又回到了线性回归的陷阱,模型会错误地认为2比1“更大”,而类别之间本应是无序的。我们必须将一个多分类问题,分解(Decompose)成若干个二分类问题来解决。目前最主流的两种分解策略,就是 One-vs-All(OvA)和 One-vs-One(OvO)。
4.2 One-vs-All(OvA):以“我”为中心的全局视角
OvA 的思想极其朴素:对于每一个类别C_k,我们都构建一个二分类器,它的任务是回答一个问题:“这个样本属于C_k吗?”。所有其他K-1个类别,都被统一视为“非C_k”这一大类。
训练阶段:你需要训练
K个独立的二分类器。每个分类器f_k(x)都会输出一个分数s_k(x),这个分数可以被解释为“样本x属于类别C_k的置信度”或“对数几率”。在 scikit-learn 中,LogisticRegression默认就是 OvA 模式,它内部会自动为你创建K个OneVsRestClassifier。预测阶段:对于一个新样本
x,我们将它输入所有K个分类器,得到K个分数[s₁(x), s₂(x), ..., s_K(x)]。最终的预测类别,就是那个分数最高的类别:ŷ = argmax_k s_k(x)。
OvA 的优势非常明显:
- 高效:只需要训练
K个模型,时间复杂度为O(K * N),其中N是训练样本数。 - 简单直观:每个模型的决策边界都清晰可解释,你可以很容易地说出“模型认为什么特征会让它更倾向于预测为
setosa”。
但它的软肋也很突出:
- 类别不平衡:在每个二分类器中,“正类”(
C_k)的样本数通常远少于“负类”(所有其他类)。例如,在一个 1000 个样本、3 个类别各占 1/3 的数据集中,训练setosa分类器时,正样本只有 ~333 个,负样本却有 ~667 个。这种不平衡会严重影响模型性能,尤其是当少数类的样本本身就很稀疏时。 - 错误传播:如果某个分类器
f_k在一个困难样本上给出了一个异常高的分数(比如因为噪声或特征漂移),它就会“压倒”其他所有分类器,导致最终预测错误。整个系统的鲁棒性,取决于最弱的那个二分类器。
注意:scikit-learn 的
LogisticRegression在multi_class='ovr'(默认)模式下,其predict_proba方法返回的并不是真正的概率分布,而是一种经过归一化的“伪概率”。它先计算K个s_k(x),然后用 softmax 函数p_k = exp(s_k) / ∑_j exp(s_j)进行转换。这保证了输出和为 1,但其统计意义不如真正的多分类逻辑回归(Multinomial Logistic Regression)严谨。
4.3 One-vs-One(OvO):两两对决的民主投票
OvO 采取了完全不同的思路:它不追求一个“全局最优”,而是组织一场K个选手参加的循环赛。每两个类别C_i和C_j之间,都单独训练一个二分类器f_ij(x),专门负责区分C_i和C_j。
训练阶段:需要训练的分类器总数是组合数
C(K, 2) = K*(K-1)/2。对于K=4,就需要6个模型;对于K=10,就需要45个模型。每个模型只用到属于C_i或C_j的样本,因此完全避免了类别不平衡问题,每个二分类器看到的都是一个均衡的数据集。预测阶段:对于一个新样本
x,我们将它输入所有C(K,2)个分类器。每个分类器f_ij都会投出一票,票投给它认为x更像的那个类别(C_i或C_j)。最终,获得最多票数的类别胜出:ŷ = argmax_k (票数_k)。
OvO 的优势在于其内在的鲁棒性:
- 抗噪性强:一个分类器的错误(比如
f_12把C_3的样本误判为C_1)只会影响C_1和C_2的票数,对C_3的总票数毫无影响。错误被局部化了。 - 利用局部信息:每个
f_ij都是在一个高度相关的子空间里进行学习,它能捕捉到C_i和C_j之间最细微的、独有的判别特征,这是 OvA 的“全局”分类器难以做到的。
当然,代价也很明显:
- 计算开销大:模型数量随
K的平方增长。当K=100时,你需要训练4950个模型,存储和推理成本巨大。 - 预测稍慢:需要运行
C(K,2)次前向传播,而不是K次。
4.4 OvA vs OvO:一张决策表帮你选对路
| 特性 | One-vs-All (OvA) | One-vs-One (OvO) |
|---|---|---|
| 模型数量 | K | K*(K-1)/2 |
| 训练数据量(每个模型) | 全量数据(正类少,负类多) | 仅C_i和C_j类别的数据(完全平衡) |
| 训练时间复杂度 | O(K * N) | O(K² * N/K) = O(K * N)(平均每个模型数据量为N/K) |
| 预测时间复杂度 | O(K) | O(K²) |
| 内存占用 | O(K) | O(K²) |
| 对类别不平衡的鲁棒性 | 弱(每个模型都面临不平衡) | 强(每个模型数据均衡) |
| 对单个模型错误的鲁棒性 | 弱(一个强错误预测可主导结果) | 强(错误被投票机制稀释) |
| 典型适用场景 | K较小(< 10),数据量极大,对推理速度要求苛刻 | K中等(10-50),类别间区分度差异大,对模型精度和鲁棒性要求极高 |
我的经验是:在绝大多数入门和中级项目中,OvA 是首选,因为它简单、快、够用。但当你进入一个高风险领域,比如金融反欺诈(类别:正常交易,盗刷,洗钱,套现),或者工业质检(类别:合格,划痕,凹陷,色差,尺寸超差),此时K不大,但每个类别的业务含义和损失函数都截然不同,OvO 的鲁棒性优势就会凸显出来。我曾在一个汽车零部件缺陷检测项目中,将 OvA 的 F1-score 从0.82提升到0.89,关键就在于 OvO 让模型对划痕和凹陷这两个视觉上极易混淆的缺陷,建立了更精细、更可靠的判别边界。
5. 实操全流程:从数据准备到模型评估的避坑指南
5.1 数据预处理:别让脏数据毁掉你的数学之美
逻辑回归对数据的“干净度”极为敏感。一个未经处理的原始数据集,往往会在下游引发一系列连锁反应。
缺失值(Missing Values):逻辑回归无法处理
NaN。最危险的做法是直接df.dropna(),这会无差别地删除整行,可能导致你丢失大量宝贵样本。更合理的做法是:- 对于数值型特征(如
age,income),用中位数(median)填充。中位数比均值更能抵抗异常值的影响,而逻辑回归的决策边界对异常值非常敏感。 - 对于分类型特征(如
education_level),用众数(mode)填充,或者创建一个新的类别Unknown。切记不要用0或-1去填充,这会给模型引入虚假的序关系。
- 对于数值型特征(如
异常值(Outliers):逻辑回归的
z = X·β是线性的,一个极端的x值会直接导致z极大,从而使σ(z)趋近于1或0,产生一个“过于自信”的错误预测。我建议对所有数值型特征做IQR(四分位距)过滤:Q1 - 1.5*IQR < x < Q3 + 1.5*IQR。对于超出范围的点,不是直接删除,而是将其“拉回”到边界值(Winsorization),这比删除更能保留数据的整体分布形态。特征缩放(Feature Scaling):这是新手最容易忽略,也是影响最大的一步。逻辑回归的梯度
∇J = (p-y)*x中,x的量纲直接决定了梯度的大小。如果x₁是“年龄”(范围0-100),x₂是“年收入”(范围0-1000000),那么x₂的梯度会比x₁大一万倍!这会导致优化器在x₂方向上疯狂震荡,而在x₁方向上几乎不动。必须对所有数值型特征进行标准化(Standardization):x' = (x - μ) / σ。注意,这里不是归一化(Min-Max Scaling),因为后者对异常值敏感,而标准化更稳健。
实操心得:我在一个客户流失预测项目中,忘记对“月均消费额”做标准化,模型训练了 1000 轮,
loss曲线像心电图一样剧烈抖动,最终收敛到一个很差的结果。加上StandardScaler后,50 轮就平稳收敛,AUC 提升了 0.12。这个教训让我养成了一个习惯:在fit()之前,永远先print(X_train.describe()),肉眼扫一遍各列的均值和标准差。
5.2 模型训练与超参调优:超越C=1.0的默认值
sklearn的LogisticRegression有一个核心超参数C,它控制着正则化强度。C是正则化系数λ的倒数:C = 1/λ。C越大,正则化越弱,模型越复杂,越容易过拟合;C越小,正则化越强,模型越简单,越容易欠拟合。
如何选择
C?绝对不要依赖默认的C=1.0。你应该使用GridSearchCV或RandomizedSearchCV进行搜索。我的经验搜索空间是:C = [0.001, 0.01, 0.1, 1, 10, 100]。对于小数据集(N < 1000),从较小的C(如0.01)开始;对于大数据集(N > 10000),可以从较大的C(如10)开始。正则化类型:
penalty参数可选'l1'或'l2'。'l2'(岭回归):它会惩罚所有权重的平方和,使所有权重都趋向于小,但不会为零。它能提升模型的泛化能力,是更通用的选择。'l1'(Lasso):它会惩罚所有权重的绝对值之和,具有特征选择的天然属性——它会将不重要的特征权重直接压缩为0。如果你的特征维度很高(n_features >> n_samples),或者你明确需要一个可解释的、精简的模型,l1是更好的选择。
求解器(Solver):
solver参数决定了用什么算法来优化损失函数。liblinear适用于小数据集;lbfgs和saga适用于大数据集。saga是唯一一个同时支持l1和l2正则化的求解器,因此在需要l1时,saga是必选项。
5.3 模型评估:超越 Accuracy 的多维审视
对于一个二分类逻辑回归模型,仅仅看accuracy是极具误导性的,尤其是在类别不平衡的数据上。
- 混淆矩阵(Confusion Matrix):这是所有评估的起点。它给出了
TP,TN,FP,FN四个基本计数。 - Precision(精确率):
TP / (TP + FP)。它回答的问题是:“当我预测为正类时,有多大概率是正确的?” 在垃圾邮件检测中,高 Precision 意味着你很少把正常邮件误判为垃圾邮件。 - Recall(召回率):
TP / (TP + FN)。它回答的问题是:“所有真实的正类样本中,我成功找出了多少?” 在疾病筛查中,高 Recall 意味着你很少漏掉一个真正的患者。 - F1-Score:
Precision和Recall的调和平均数,是它们的综合指标。 - ROC-AUC:这是逻辑回归最核心的评估指标。它绘制的是不同阈值下的
TPR(Recall)和FPR(FP / (FP + TN))的关系曲线。AUC 值代表了模型在所有可能阈值下,区分正负样本的能力。AUC = 0.5表示模型等同于随机猜测;AUC = 1.0表示模型完美区分。一个健康的逻辑回归模型,其 ROC 曲线应该从左下角(0,0)平滑地上升到右上角(1,1)。
常见问题:为什么我的模型
accuracy很高(95%),但recall却只有 0.3?答案几乎总是:你的数据严重不平衡,而模型学会了“偷懒”,即永远预测为多数类。此时,accuracy是一个完全失效的指标。你必须立刻转向precision-recall curve或ROC curve,并根据业务需求,手动设定一个更合理的阈值(比如,宁可多召回来几个,也不能漏掉一个)。
6. 常见问题与排查技巧实录:那些只有踩过才懂的坑
6.1 问题:模型训练时loss不下降,甚至发散
排查思路:
- 检查数据:首先
print(y_train.value_counts(normalize=True)),确认标签是否极度不平衡。如果是,尝试class_weight='balanced'参数,它会自动为少数类赋予更高的权重。 - 检查特征:
print(X_train.isnull().sum()),确认是否有未处理的NaN。print(np.isinf(X_train).sum()),确认是否有无穷大值(通常来自除零错误)。 - 检查缩放:
print(X_train.mean(), X_train.std()),确认所有特征的均值是否接近0,标准差是否接近1。如果不是,说明StandardScaler没有正确应用。 - 检查学习率:虽然
sklearn的求解器是自适应的,但如果C设置得过大(如C=1000),相当于几乎没有正则化,模型可能会在复杂的空间里迷失。尝试将C降低一个数量级。
我的实录:在一个电商点击率预测项目中,loss一直不降。我一步步排查,最后发现是“用户历史购买次数”这个特征里,有少量用户记录为999999(数据库里的默认占位符)。这个异常值让z变得极大,σ(z)趋近于1,梯度(p-y)变得极小,模型“躺平”了。修复后,loss在 5 轮内就稳定下降。
6.2 问题:predict_proba输出的所有概率都集中在0.4-0.6区间,缺乏区分度
排查思路:
- 检查模型复杂度:
C是否设置得太小?过强的正则化会把所有权重都拉向0,导致z总是很小,σ(z)总是接近0.5。增大C。 - 检查特征工程:你的特征是否真的包含了足够的判别信息?尝试添加一些交互特征(如
feature_a * feature_b)或多项式特征(如feature²)。逻辑回归本身是线性的,但特征可以是非线性的。 - 检查数据质量:是否存在大量的标签噪声?即,很多样本的标签本身就是错误的。这会让模型“学不会”,只能给出一个保守的、中庸的预测。
我的实录:在一个新闻分类项目中,模型输出的概率非常“平”。我检查了C,没问题;然后我画了所有特征的boxplot,发现“文章长度”这个特征的分布,在所有类别中几乎完全重叠。我果断弃用了它,并加入了“标题中关键词 TF-IDF 加权和”这个新特征,模型的calibration curve立刻变得漂亮起来。
6.3 问题:OvA 多分类中,某个类别的precision极低,但recall很高
排查思路: 这通常是该类别与其他类别边界模糊的信号。OvA 的“全局”视角在这里失效了。
- 方案一(推荐):切换到 OvO 策略。O