机器学习从入门到精通 - 逻辑回归为什么是分类之王?深入决策边界与概率校准
机器学习从入门到精通 - 逻辑回归为什么是分类之王?深入决策边界与概率校准
想象一下这个场景:你刚拿到一份数据集,任务是把客户分成“会买”和“不会买”两类。你兴奋地打开工具库,看着琳琅满目的算法——支持向量机闪着寒光,随机森林像一片茂密的未知丛林,神经网络更是深不见底的黑匣子… 这时候,我敢打赌,一个名字朴实无华的老伙计往往会先跳进你的脑海:逻辑回归。别小看它!这家伙能在分类任务的江湖里稳坐“入门必学”和“工业常青树”两把交椅,靠的可不只是资历。今天咱们就掰开揉碎了聊聊,为什么逻辑回归配得上“分类之王”的名号,尤其是它那清晰得发亮的决策边界和精准得吓人的概率校准能力。对了,我还会把当年踩得鼻青脸肿的那些坑,一个个亮给你看!
一、起点:从线性到非线性,分类的基石为什么是它?
咱别一上来就扔公式把人砸晕 —— 先说个最容易踩的坑!很多新手一看“回归”俩字,立马把逻辑回归和预测房价的线性回归划等号,然后困惑它怎么就能分类了?这误会可深了!
核心区别在于目标输出:
- 线性回归: 预测一个连续的数值,比如明天气温 25.3℃、股票价格 102.4 元。它的输出理论上可以是负无穷到正无穷之间的任何数。
- 逻辑回归: 预测一个样本属于某个类别的概率,这个概率值严格限制在 0 到 1 之间。最终我们根据这个概率是否超过某个阈值(通常是 0.5)来决定把它分到哪一类(0 或 1)。
那它是怎么把“任意实数”变成“0~1 概率”的呢? 靠的就是那个神奇的 Sigmoid 函数(也叫 Logistic 函数)!
它的数学表达式是:
g(z)=11+e−z g(z) = \frac{1}{1 + e^{-z}} g(z)=1+e−z1
这个公式得揉碎了看:
z
:这就是线性回归的输出!z = θ₀ + θ₁x₁ + θ₂x₂ + ... + θₙxₙ
。θ
(theta
) 是我们要学习的参数(权重),x
是特征值。e
:自然常数,约等于 2.71828。g(z)
:输出的就是样本属于正类(通常标记为 1)的概率P(y=1 | x)
。
Sigmoid 函数干了啥? 你看它的曲线图:
- 当
z
非常大(趋向正无穷)时,e^{-z}
趋近于 0,所以g(z)
趋近于1 / (1 + 0) = 1
。 - 当
z
非常小(趋向负无穷)时,e^{-z}
趋近于正无穷,所以g(z)
趋近于1 / (非常大的数) ≈ 0
。 - 当
z = 0
时,g(z) = 1 / (1+1) = 0.5
。
妙处就在这里! 它把线性回归计算出来的那个可以上天入地的 z
值,平滑地、非线性地压缩到了 (0, 1) 区间内,完美地满足了概率的定义。输出的 g(z)
值直接代表“是正类的可能性有多大”。
二、决策边界:模型划下的那道“楚河汉界”
明白了逻辑回归输出的是概率,分类决策怎么做?简单!设定一个阈值(Threshold),比如 0.5:
- 如果
g(z) >= 0.5
,模型就预测y = 1
(正类)。 - 如果
g(z) < 0.5
,模型就预测y = 0
(负类)。
关键问题来了:g(z) >= 0.5
对应着 z
满足什么条件? 咱们代回 Sigmoid 函数看看:
g(z)=11+e−z≥0.5 g(z) = \frac{1}{1 + e^{-z}} \geq 0.5 g(z)=1+e−z1≥0.5
解这个不等式:
1 / (1 + e^{-z}) >= 1/2
- 两边取倒数(注意不等号方向要变):
1 + e^{-z} <= 2
- 移项:
e^{-z} <= 1
- 两边取自然对数(
ln
是单调递增函数,不等号方向不变):-z <= ln(1) = 0
- 因此:
z >= 0
结论令人拍案叫绝! g(z) >= 0.5
等价于 z >= 0
。代入 z
的表达式:
θ0+θ1x1+θ2x2+...+θnxn≥0 \theta_0 + \theta_1x_1 + \theta_2x_2 + ... + \theta_nx_n \geq 0 θ0+θ1x1+θ2x2+...+θnxn≥0
这个方程 θ₀ + θ₁x₁ + θ₂x₂ + ... + θₙxₙ = 0
定义的玩意儿,就是逻辑回归的决策边界(Decision Boundary)!
-
在二维平面(两个特征 x1, x2)上,这个方程就是一条直线。直线的一侧预测 y=1,另一侧预测 y=0。如下图:
graph LR A[Input Features X] --> B[Calculate z = θᵀX] B --> C{Apply Sigmoid<br> g(z) = 1/(1+e⁻ᶻ)} C --> D[Output Probability<br> P(y=1|X)] D --> E{Threshold?<br> e.g., >= 0.5} E -- Yes --> F[Predict Class 1] E -- No --> G[Predict Class 0]
-
在更高维空间,它是一个超平面(Hyperplane)。
决策边界的威力:
- 可视化理解模型: 它能让我们直观地看到模型是如何把不同的类别区分开来的。这比看一堆数字权重
θ
要直观得多! - 模型复杂度的体现: 逻辑回归的决策边界天生是线性的(或者通过特征工程变成线性)。这个特性 —— 我得强调 —— 既是它最大的优点(简单、稳定、易解释),也是它的局限(无法直接处理非常复杂的非线性关系)。想让它处理更复杂的边界?咱们后面讲特征工程和正则化时会说。
举个栗子(用 Python): 我们用经典的鸢尾花数据集,只取两个特征(萼片长度和花瓣长度)和两个类别(Setosa 和 Versicolor),训练一个逻辑回归模型,画出它的决策边界。
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.linear_model import LogisticRegression# 加载数据,只取前两个特征和前两个类别
iris = datasets.load_iris()
X = iris.data[:, :2] # 只取萼片长度和萼片宽度
y = iris.target
# 只取类别0 (Setosa) 和类别1 (Versicolor)
X = X[y < 2]
y = y[y < 2]# 创建逻辑回归模型并训练
model = LogisticRegression(C=1e5) # 先用个大C(小λ)避免默认正则化干扰
model.fit(X, y)# 创建网格点用于绘制决策边界
x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),np.arange(y_min, y_max, 0.02))# 预测网格点上每个点的类别概率
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)# 绘制决策边界和训练样本
plt.figure(figsize=(10, 6))
plt.contourf(xx, yy, Z, alpha=0.3) # 填充决策区域
plt.scatter(X[:, 0], X[:, 1], c=y, edgecolors='k', cmap=plt.cm.Paired)
plt.xlabel('Sepal Length (cm)')
plt.ylabel('Sepal Width (cm)')
plt.title('Logistic Regression Decision Boundary (Iris Setosa vs Versicolor)')
plt.show()
运行这段代码,你会看到一条清晰的直线(决策边界)把Setosa(一类点)和Versicolor(另一类点)完美地分开了。这就是线性决策边界的直观体现!
三、代价函数与梯度下降:模型是怎么“学会”的?
好,模型结构有了(Sigmoid+线性组合),决策边界也清楚了。现在核心问题是:模型参数 θ
怎么学出来? 这里就有个超级大坑!
为什么不能用线性回归的均方误差(MSE)?
线性回归的代价函数是 MSE:J(θ) = (1/2m) * Σ (hθ(x⁽ⁱ⁾) - y⁽ⁱ⁾)²
。其中 hθ(x)
是预测值(连续值)。如果我们把这个代价函数直接用在逻辑回归上(hθ(x)
现在是 Sigmoid 函数输出,即概率),会发生什么?
问题出在 Sigmoid 函数上!把它和平方误差组合起来,画出的代价函数 J(θ)
相对于参数 θ
的图像会变成一个非凸函数(Non-convex function) —— 表面坑坑洼洼,有好多局部最低点(Local Minima)。想象你下山找最低点,结果掉进一个小坑里就以为到底了,其实下面还有万丈深渊!梯度下降算法在这种地形上很容易卡在局部最优解里出不来,根本找不到全局最优解。
逻辑回归的专属代价函数:交叉熵(Cross-Entropy)
为了解决非凸问题,我们需要为逻辑回归量身定做一个凸的代价函数。这个函数就是 二元交叉熵(Binary Cross-Entropy Loss):
J(θ)=−1m∑i=1m[y(i)log(hθ(x(i)))+(1−y(i))log(1−hθ(x(i)))] J(\theta) = -\frac{1}{m} \sum_{i=1}^{m} \left[ y^{(i)} \log(h_\theta(x^{(i)})) + (1 - y^{(i)}) \log(1 - h_\theta(x^{(i)})) \right] J(θ)=−m1i=1∑m[y(i)log(hθ(x(i)))+(1−y(i))log(1−hθ(x(i)))]
这个公式看着唬人,拆开了巨好理解!它按样本的真实标签 y⁽ⁱ⁾
分成了两部分:
- 当真实标签
y⁽ⁱ⁾ = 1
(正样本):- 我们希望模型预测的概率
hθ(x⁽ⁱ⁾)
越接近 1 越好。 - 此时代价函数只剩下前半部分:
-log(hθ(x⁽ⁱ⁾))
。 - 如果模型预测
hθ(x⁽ⁱ⁾)
很接近 1,那么log(接近1)
接近 0,代价-0=0
很小。 - 如果模型预测
hθ(x⁽ⁱ⁾)
很小(比如接近0),那么log(小值)
是一个很大的负数,代价-(大的负数)
= 一个很大的正数!惩罚很重!
- 我们希望模型预测的概率
- 当真实标签
y⁽ⁱ⁾ = 0
(负样本):- 我们希望模型预测的概率
hθ(x⁽ⁱ⁾)
越接近 0 越好。 - 此时代价函数只剩下后半部分:
-log(1 - hθ(x⁽ⁱ⁾))
。 - 如果模型预测
hθ(x⁽ⁱ⁾)
很接近 0,那么1 - hθ(x⁽ⁱ⁾)
接近 1,log(接近1)
接近0,代价-0=0
很小。 - 如果模型预测
hθ(x⁽ⁱ⁾)
很大(比如接近1),那么1 - hθ(x⁽ⁱ⁾)
很小,log(小值)
是一个很大的负数,代价-(大的负数)
= 一个很大的正数!惩罚同样很重!
- 我们希望模型预测的概率
交叉熵代价函数的精髓: 它对“预测概率离真实标签的差距”进行了不对称但极其合理的惩罚。预测错得越离谱(该是1你预测接近0,或者该是0你预测接近1),代价就飙升得越高!而且 —— 最重要的是 —— 它在逻辑回归的设定下是凸函数,用梯度下降就能比较靠谱地找到全局最优解。
梯度下降:参数更新的引擎
有了凸的代价函数 J(θ)
,我们就可以用梯度下降(Gradient Descent)来迭代更新参数 θ
,让它一步步走向最优解。梯度下降的更新规则是:
θj:=θj−α∂∂θjJ(θ) \theta_j := \theta_j - \alpha \frac{\partial}{\partial \theta_j} J(\theta) θj:=θj−α∂θj∂J(θ)
其中 α
是学习率(Learning Rate),控制每次更新的步长。核心在于计算代价函数 J(θ)
对每个参数 θⱼ
的偏导数 ∂J(θ)/∂θⱼ
。
推导梯度(关键步骤):
-
Sigmoid 函数的导数有个很好的性质(务必记住):
g'(z) = g(z)(1 - g(z))
证明:
g(z) = 1 / (1 + e⁻ᶻ) = (1 + e⁻ᶻ)⁻¹
g'(z) = -1 * (1 + e⁻ᶻ)⁻² * (-e⁻ᶻ) = e⁻ᶻ / (1 + e⁻ᶻ)²
分子分母同乘eᶻ
:
= 1 / (eᶻ * (1 + e⁻ᶻ)²)
这不对…
正确拆解:
g'(z) = [e⁻ᶻ] / [(1 + e⁻ᶻ)²]
(由上式直接来)
= [1 / (1 + e⁻ᶻ)] * [e⁻ᶻ / (1 + e⁻ᶻ)]
= g(z) * [(1 + e⁻ᶻ - 1) / (1 + e⁻ᶻ)]
(技巧:分子e⁻ᶻ = (1 + e⁻ᶻ) - 1
)
= g(z) * [1 - 1/(1 + e⁻ᶻ)]
= g(z) * (1 - g(z))
证毕! -
计算单个样本的损失对
z
的偏导:
定义单个样本的损失:Loss = - [y log(h) + (1-y) log(1-h)]
,其中h = g(z) = g(θᵀx)
。
∂Loss / ∂z = ∂Loss / ∂h * ∂h / ∂z
- 先算
∂Loss / ∂h = - [ y * (1/h) + (1-y) * (1/(1-h)) * (-1) ] = - [ y/h - (1-y)/(1-h) ]
- 再算
∂h / ∂z = h(1 - h)
(由Sigmoid导数性质) - 所以:
∂Loss / ∂z = [ - (y/h - (1-y)/(1-h)) ] * [h(1-h)]
= - [y(1-h) - (1-y)h]
(展开并约掉h和(1-h)? )
仔细展开:
= - [ (y/h - (1-y)/(1-h)) ] * h(1-h)
= - [ y(1-h) - (1-y)h ]
(因为(y/h)*h(1-h) = y(1-h)
,[-(1-y)/(1-h)]*h(1-h) = -(1-y)h
)
= - [ y - yh - h + yh ]
= - [y - h]
= h - y
太棒了!结果异常简洁:
∂Loss / ∂z = hθ(x) - y
! - 先算
-
计算
∂Loss / ∂θⱼ
:
因为z = θ₀x₀ + θ₁x₁ + ... + θⱼxⱼ + ... + θₙxₙ
(其中x₀=1
对应截距项),所以∂z / ∂θⱼ = xⱼ
。
根据链式法则:∂Loss / ∂θⱼ = (∂Loss / ∂z) * (∂z / ∂θⱼ) = (hθ(x) - y) * xⱼ
。 -
计算整个代价函数
J(θ)
的梯度∂J(θ)/∂θⱼ
:
J(θ) = (1/m) Σ Loss⁽ⁱ⁾
∂J(θ)/∂θⱼ = (1/m) Σ [∂Loss⁽ⁱ⁾ / ∂θⱼ] = (1/m) Σ [(hθ(x⁽ⁱ⁾) - y⁽ⁱ⁾) * xⱼ⁽ⁱ⁾]
最终梯度下降更新公式:
θj:=θj−α1m∑i=1m(hθ(x(i))−y(i))xj(i)
\theta_j := \theta_j - \alpha \frac{1}{m} \sum_{i=1}^{m} (h_\theta(x^{(i)}) - y^{(i)}) x_j^{(i)}
θj:=θj−αm1i=1∑m(hθ(x(i))−y(i))xj(i)
看这个更新公式!