逻辑回归(Logistic Regression)是机器学习中用于解决二分类或多分类问题的经典算法。虽然名称中包含”回归”,但它实际上是一种分类算法,特别适用于预测概率输出。
逻辑回归基础概念
想象一下线性回归:y = wx + b
。它输出一个连续的、可以是任意大小的数值。如果我们想用它来预测一个类别(例如:0 或 1,是或否),直接使用它的输出是不合适的。
因此,我们要想办法找一个函数,将线性回归的结果映射到一个介于 0 和 1 之间的值,这里记作 p。当 p>0.5 时判定为 1,反之判定为 0。如此一来,就可以实现训练模型-输入数据-获得分类结果。
通常情况下,逻辑回归是一个线性模型,当然,也可以调整成非线性的,本文只讨论线性模型的情况,且直接讨论通用形式,不对二维情形做单独的讨论。
逻辑回归数学原理
Sigmoid 函数
sigmoid 函数的公式如下:
$$\sigma (z) = \frac{1}{{1 + {e^{ – z}}}}$$
它的图像是一个优美的”S”型曲线:
- 当$z \to + \infty $时,输出$\sigma (z)$ 无限接近 1。
- 当$z \to – \infty $时,输出$\sigma (z)$ 无限接近 0。
- 当$ z=0 $时,$\sigma (z) = 0.5$

这个特性完美地将任何实数转换成了(0,1)区间的值,我们可以将其解释为概率。
逻辑回归模型
在逻辑回归中,我们将线性回归的输出 $z = {\omega _0} + {\omega _1}{x_1} + {\omega _2}{x_2} + \cdots + {\omega _n}{x_n}$作为 Sigmoid 函数的输入。
令${\underline \omega ^T} = \left[ {{\omega _0},{\omega _1},{\omega _2}, \cdots ,{\omega _n}} \right]$,$\underline x = \left[ {1,{x_0},{x_1}, \cdots ,{x_n}} \right]$,则$z = {\underline \omega ^T}\underline x $
之后,将 z 代入 Sigmoid 函数,得到预测概率
$$\hat y = P\left( {\left. {y = 1} \right|\underline x } \right) = \frac{1}{{1 + {e^{ – \left( {{{\underline \omega }^T}\underline x } \right)}}}}$$
决策边界
我们得到了一个概率,如何最终做出分类决策呢?我们需要设定一个阈值,通常默认为 0.5:
- $\hat y \ge 0.5$,预测为 1
- $\hat y < 0.5$,预测为 0
由于 Sigmoid 函数在 $ z=0 $时输出 0.5,所以这个决策规则等价于:
- ${\underline \omega ^T}\underline x \ge 0$,预测为 1
- ${\underline \omega ^T}\underline x < 0$,预测为 0
这个${\underline \omega ^T}\underline x $就是一个决策边界,在二维空间中是一条直线,在三维空间中是一个平面,在高维空间中是一个超平面。逻辑回归的本质就是通过学习参数$\underline \omega $来找到这个最佳决策边界。
损失函数
模型需要学习,就必须有一个衡量预测好坏的指标,这就是损失函数。线性回归用的是均方误差,但它在逻辑回归中不是一个好的选择(会导致非凸优化问题,容易陷入局部最优)。
逻辑回归使用 交叉熵损失函数。
对于单个样本,其损失函数定义为:
$$L\left( {\hat y,y} \right) = – \left[ {y\log \left( {\hat y} \right) + \left( {1 – y} \right)\log \left( {1 – \hat y} \right)} \right]$$
- 当真实标$y=1$ 时,损失函数变为$- {\log \left( {\hat y} \right)}$。如果预测概率${\hat y}$越接近 1,损失 越$- {\log \left( {\hat y} \right)}$接近 0(预测正确);如果 ${\hat y}$ 越接近 0,损失 $- {\log \left( {\hat y} \right)}$会变得非常大(惩罚预测错误)。
- 当真实标签 $y=0$ 时,损失函数变为 ${-\log \left( {1 – \hat y} \right)}$。如果预测概率$ \hat y$ 越接近 0,损失越接近 0;如果$ \hat y$ 越接近 1,损失会变得非常大。
代价函数
定义平均交叉熵损失(代价函数):
$$J(w,b)=\frac{1}{m}\sum_{i=1}^mL(\hat{y}^{(i)},y^{(i)})$$
其中$L(\hat{y}^{(i)},y^{(i)})=-[y^{(i)}\log(\hat{y}^{(i)})+(1-y^{(i)})\log(1-\hat{y}^{(i)})]$是单个样本的损失,$\hat{y}^{(i)}=\sigma(z^{(i)})=\frac{1}{1+e^{-z^{(i)}}}$是模型的预测输出。
参数学习:梯度下降法
我们的目标是找到$\underline \omega ^T$,使得总的代价函数(平均交叉熵损失)最小化。
这个过程通常通过梯度下降算法来实现。其核心思想是:
- 初始化参数 $\underline \omega ^T$
- 计算代价函数对每个参数的梯度(偏导数)。
- 沿着梯度的反方向(即下降最快的方向)更新参数。
- 重复步骤 2 和 3,直到代价函数收敛或达到预设的迭代次数。
参数更新公式如下:
$$w_j:=w_j-\alpha\frac{\partial J(\mathbf{w},b)}{\partial w_j}$$
其中 $\alpha$是学习率,控制每次更新的步长。

逻辑回归矩阵运算实现
矩阵运算一般情形
假设有 $m$ 个样本,每个样本有 $n$ 个特征变量 $x_1, x_2, \dots, x_n$,加上偏置项 $x_0 = 1$。
矩阵定义
特征矩阵 $X$:维度 $m \times (n+1)$
$$X = \begin{bmatrix} 1 & x_{1,1} & x_{1,2} & \dots & x_{1,n} \\ 1 & x_{2,1} & x_{2,2} & \dots & x_{2,n} \\ \vdots & \vdots & \vdots & & \vdots \\ 1 & x_{m,1} & x_{m,2} & \dots & x_{m,n} \end{bmatrix}$$
参数向量 $\theta$:维度 $(n+1) \times 1$
$$\theta = \begin{bmatrix} \theta_0 \\ \theta_1 \\ \vdots \\ \theta_n \end{bmatrix}$$
标签向量 $y$:维度 $m \times 1$
$$y = \begin{bmatrix} y^{(1)} \\ y^{(2)} \\ \vdots \\ y^{(m)} \end{bmatrix}, \quad y^{(i)} \in \{0,1\}$$
假设函数
对每个样本的预测:
$$h_\theta(x^{(i)}) = \sigma(x^{(i)} \theta) = \frac{1}{1 + e^{-x^{(i)} \theta}}$$
向量化(对整个数据集):
$$z = X \theta \quad (m \times 1)$$
$$h = \sigma(z) = \frac{1}{1 + e^{-z}} \quad \text{(逐元素运算)}$$
这里 $h$ 是 $m \times 1$ 向量,表示每个样本的预测概率 $P(y=1|x;\theta)$。
代价函数
$$J(\theta) = -\frac{1}{m} \left[ y^T \log(h) + (1 – y)^T \log(1 – h) \right] + \frac{\lambda}{2m} \theta_{1:n}^T \theta_{1:n}$$
其中 $\log(\cdot)$ 逐元素取自然对数,$\theta_{1:n}$ 表示去掉 $\theta_0$ 的 $n \times 1$ 向量,$\lambda$ 是正则化参数。
梯度下降迭代
梯度:
$$\frac{\partial J}{\partial \theta} = \frac{1}{m} X^T (h – y) + \frac{\lambda}{m} \begin{bmatrix} 0 \\ \theta_1 \\ \vdots \\ \theta_n \end{bmatrix}$$
迭代更新:
$$\theta := \theta – \alpha \frac{\partial J}{\partial \theta}$$
其中 $\alpha$ 是学习率。
展开写:
$$\theta := \theta – \alpha \left[ \frac{1}{m} X^T (h – y) + \frac{\lambda}{m} \begin{bmatrix} 0 \\ \theta_1 \\ \vdots \\ \theta_n \end{bmatrix} \right]$$
迭代过程算法步骤
- 初始化 $\theta = \mathbf{0}_{(n+1)\times 1}$
- 重复直到收敛:
- 计算 $z = X \theta$ ($m \times 1$)
- 计算 $h = \sigma(z) = 1 ./ (1 + \exp(-z))$ (逐元素,$m \times 1$)
- 计算误差 $\text{error} = h – y$ ($m \times 1$)
- 计算梯度: $$\text{grad} = \frac{1}{m} X^T \cdot \text{error}$$ 如果正则化: $$\text{grad} = \text{grad} + \frac{\lambda}{m} \begin{bmatrix} 0 \\ \theta_1 \\ \vdots \\ \theta_n \end{bmatrix}$$
- 更新参数: $$\theta := \theta – \alpha \cdot \text{grad}$$
- 检查收敛条件
矩阵维度小结
- $X$: $m \times (n+1)$
- $\theta$: $(n+1) \times 1$
- $y$: $m \times 1$
- $z = X\theta$: $m \times 1$
- $h = \sigma(z)$: $m \times 1$
- $X^T(h-y)$: $(n+1) \times 1$
逻辑回归 Python 实现
具体案例分析
假设我们需要根据学生的两项成绩,来判断他们是否通过期末考核。目前我们手上有三个学生的历史数据,将其转化为特征矩阵 :
$$X=\begin{bmatrix} 1 & 85 & 78 \\ 1 & 62 & 65 \\ 1 & 92 & 88 \end{bmatrix}$$
- 第 1 列:偏置项(始终为 1)
- 第 2 列:数学成绩(满分 100)
- 第 3 列:英语成绩(满分 100)
标签向量 y(1=通过,0=未通过):
$$y = {\left[ {1,0,1} \right]^T}$$
初始参数:
$$w=\begin{bmatrix} w_0 \\ w_1 \\ w_2 \end{bmatrix}=\begin{bmatrix} 0 \\ 0 \\ 0 \end{bmatrix}$$
且令$\alpha=0.2$。
以第一次迭代过程为例:
- 计算线性输出 $z$: $$z = Xw = \begin{bmatrix} 1 & 85 & 78 \\ 1 & 62 & 65 \\ 1 & 92 & 88 \end{bmatrix} \begin{bmatrix} 0 \\ 0 \\ 0 \end{bmatrix} = \begin{bmatrix} 0 \\ 0 \\ 0 \end{bmatrix}$$
- 计算预测概率 $\hat{y}$(Sigmoid 函数): $$\hat{y} = \sigma(z) = \frac{1}{1 + e^{-z}} = \begin{bmatrix} \sigma(0) \\ \sigma(0) \\ \sigma(0) \end{bmatrix} = \begin{bmatrix} 0.5 \\ 0.5 \\ 0.5 \end{bmatrix}$$
- 计算预测误差: $$\hat{y} – y = \begin{bmatrix} 0.5 \\ 0.5 \\ 0.5 \end{bmatrix} – \begin{bmatrix} 1 \\ 0 \\ 1 \end{bmatrix} = \begin{bmatrix} -0.5 \\ 0.5 \\ -0.5 \end{bmatrix}$$ 计算梯度 $\nabla_w J$: $$\nabla_w J = \frac{1}{3} \begin{bmatrix} 1 & 1 & 1 \\ 85 & 62 & 92 \\ 78 & 65 & 88 \end{bmatrix} \begin{bmatrix} -0.5 \\ 0.5 \\ -0.5 \end{bmatrix} = \frac{1}{3} \begin{bmatrix} -0.5 \\ -37.5 \\ -30.5 \end{bmatrix} = \begin{bmatrix} -0.1667 \\ -12.5 \\ -10.1667 \end{bmatrix}$$
- 更新参数 $w$: $$w := w – \alpha \nabla_w J = \begin{bmatrix} 0 \\ 0 \\ 0 \end{bmatrix} – 0.1 \times \begin{bmatrix} -0.1667 \\ -12.5 \\ -10.1667 \end{bmatrix} = \begin{bmatrix} 0.01667 \\ 1.25 \\ 1.01667 \end{bmatrix}$$
之后周而复始,就可以逐渐逼近最佳参数了。
Python 代码实现
import numpy as np
# 逻辑回归梯度下降实现
class LogisticRegression:
def __init__(self, learning_rate=0.1, max_iters=1000, tol=1e-4):
self.learning_rate = learning_rate
self.max_iters = max_iters
self.tol = tol
self.w = None
self.loss_history = []
def sigmoid(self, z):
return 1 / (1 + np.exp(-z))
def compute_loss(self, y_true, y_pred):
# 交叉熵损失
return -np.mean(y_true * np.log(y_pred + 1e-8) + (1 - y_true) * np.log(1 - y_pred + 1e-8))
def fit(self, X, y):
# 添加偏置项
X = np.column_stack([np.ones(X.shape[0]), X])
# 初始化参数
self.w = np.zeros(X.shape[1])
m = len(y)
print("开始训练...")
print(f"初始参数: w = {self.w}")
print(f"学习率: {self.learning_rate}")
print("-" * 50)
for i in range(self.max_iters):
# 前向传播
z = X @ self.w
y_pred = self.sigmoid(z)
# 计算损失
loss = self.compute_loss(y, y_pred)
self.loss_history.append(loss)
# 计算梯度
gradient = (1/m) * X.T @ (y_pred - y)
# 更新参数
w_prev = self.w.copy()
self.w -= self.learning_rate * gradient
# 打印迭代信息
if i < 3 or i % 100 == 0:
print(f"迭代 {i+1}:")
print(f" 预测值: {y_pred.round(4)}")
print(f" 损失: {loss:.6f}")
print(f" 梯度: {gradient.round(6)}")
print(f" 参数: w = {self.w.round(6)}")
print("-" * 30)
# 检查收敛
if np.linalg.norm(self.w - w_prev) < self.tol:
print(f"在第 {i+1} 次迭代收敛")
break
return self
def predict_proba(self, X):
X = np.column_stack([np.ones(X.shape[0]), X])
return self.sigmoid(X @ self.w)
def predict(self, X, threshold=0.5):
return (self.predict_proba(X) >= threshold).astype(int)
# 示例数据
X = np.array([
[85, 78], # 学生 1: 数学 85, 英语 78 → 通过
[62, 65], # 学生 2: 数学 62, 英语 65 → 未通过
[92, 88] # 学生 3: 数学 92, 英语 88 → 通过
])
y = np.array([1, 0, 1])
# 创建并训练模型
model = LogisticRegression(learning_rate=0.1, max_iters=1000)
model.fit(X, y)
# 预测
print("\n=== 预测结果 ===")
y_pred_proba = model.predict_proba(X)
y_pred = model.predict(X)
for i in range(len(X)):
print(f"学生{i+1}: 数学{X[i,0]}, 英语{X[i,1]} → "
f"预测概率: {y_pred_proba[i]:.4f} → "
f"预测类别: {y_pred[i]} (真实: {y[i]})")
print(f"\n 最终参数: w0 = {model.w[0]:.6f}, w1 = {model.w[1]:.6f}, w2 = {model.w[2]:.6f}")