深度学习中的逻辑回归:从原理到Python实现

发布于:2025-07-05 ⋅ 阅读:(15) ⋅ 点赞:(0)

逻辑回归概述

逻辑回归是用于解决二分类问题的经典算法,尽管名称含"回归",实为分类模型。其核心思想是通过线性组合加非线性变换,将输入特征映射到[0,1]区间,表示样本属于正类的概率:

z = w T x + b z = \mathbf{w}^T \mathbf{x} + b z=wTx+b
y ^ = σ ( z ) = 1 1 + e − z \hat{y} = \sigma(z) = \frac{1}{1 + e^{-z}} y^=σ(z)=1+ez1

其中 σ ( z ) \sigma(z) σ(z)为sigmoid函数,其特性如下:

输入特征 x
线性变换 z = w·x + b
Sigmoid激活函数
概率输出 ŷ = σ(z)
sigmoid函数
sigmoid函数
计算示例
计算示例

逻辑回归损失函数

损失函数用域名衡量预测结构与真实值之间的误差。最简单的损失函数定义方式为平方差损失:
L ( y ^ , y ) = 1 2 ( y ^ − y ) 2 L(\hat{y},y)=\frac {1}{2}(\hat{y}-y)^2 L(y^,y)=21(y^y)2
逻辑回归一般使用 : L ( y ^ , y ) = − ( y l o g y ^ ) − ( 1 − y ) l o g ( 1 − y ^ ) L(\hat{y},y)=-(ylog\hat{y})-(1-y)log(1-\hat{y}) L(y^,y)=(ylogy^)(1y)log(1y^)
该式子的理解:

  • 如何 y = 1 y=1 y=1,损失为 − l o g y ^ -log\hat{y} logy^,那么要想损失越小, h a t y hat{y} haty的值必须越大,即越趋近于或者等于1
  • 如果 y = 0 y=0 y=0,损失为 − l o g ( 1 − y ^ ) -log(1-\hat{y}) log(1y^),那么要想损失越小,那么 y ^ \hat{y} y^的值越小,即趋近于或者等于0

损失函数是在单个训练样本中定义的,它衡量了在单个训练样本上的表现。代价函数衡量的是在全体训练样本上的表现,即衡量参数 w w w b b b的效果,所以训练样本的损失平均值为:
J ( w , b ) = 1 m Σ i = 1 m L ( y ^ ( i ) , y ( i ) ) J(w,b)=\frac {1}{m}\Sigma_{i=1}^{m}L(\hat{y}^{(i)},y^{(i)}) J(w,b)=m1Σi=1mL(y^(i),y(i))

梯度下降算法

梯度下降图示
梯度下降图示

可以看到,成本函数 J J J是一个凸函数,包含多个局部最低。
通过最小化损失函数来优化参数 w \mathbf{w} w b b b
w : = w − α ∂ J ∂ w \mathbf{w} := \mathbf{w} - \alpha \frac{\partial J}{\partial \mathbf{w}} w:=wαwJ
b : = b − α ∂ J ∂ b b := b - \alpha \frac{\partial J}{\partial b} b:=bαbJ

其中 α \alpha α表示学习速率,即每次更新的 w w w的步伐长度。当 w w w大于最优解 w ′ w^{'} w时,导数大于0,那么 w w w就会向更小的方向更新。反之当 w w w小于最优解 w ′ w^{'} w时,导数小于0,那么 w w w就会向更大的方向更新。迭代直到收敛。

向量化梯度计算

向量化梯度计算原理

在逻辑回归中,向量化梯度计算的核心思想是使用矩阵运算替代循环操作,大幅提升计算效率。其数学基础是:

给定:

  • 特征矩阵 X ∈ R m × n X \in \mathbb{R}^{m \times n} XRm×n(m个样本,n个特征)
  • 参数向量 w ∈ R n w \in \mathbb{R}^{n} wRn
  • 偏置 b ∈ R b \in \mathbb{R} bR
  • 预测值 y ^ = σ ( X w + b ) \hat{y} = \sigma(Xw + b) y^=σ(Xw+b)
  • 真实标签 y ∈ R m y \in \mathbb{R}^{m} yRm

梯度计算可向量化为:
∇ w J = 1 m X T ( y ^ − y ) \nabla_w J = \frac{1}{m} X^T (\hat{y} - y) wJ=m1XT(y^y)
∇ b J = 1 m ∑ i = 1 m ( y ^ ( i ) − y ( i ) ) \nabla_b J = \frac{1}{m} \sum_{i=1}^{m} (\hat{y}^{(i)} - y^{(i)}) bJ=m1i=1m(y^(i)y(i))

优势对比

方法 时间复杂度 代码简洁性 计算效率
循环迭代 O ( m × n ) O(m \times n) O(m×n)
向量化 O ( 1 ) O(1) O(1) 矩阵运算 高(加速10-100倍)

伪代码实现

# 输入:特征矩阵X,标签y,参数w,偏置b,样本数m
# 输出:权重梯度dw,偏置梯度db

前向传播:
  Z = X · w + b        # 矩阵乘法 (m×n)·(n×1) = (m×1)
  Y_hat = sigmoid(Z)   # 元素级sigmoid函数

计算梯度:
  error = Y_hat - y    # 预测误差 (m×1)
  dw = (1/m) * (Xᵀ · error)  # (n×m)·(m×1) = (n×1)
  db = (1/m) * sum(error)    # 标量

简单示例说明

假设我们有3个样本和2个特征:

输入数据

X = [[2, 3],   # 样本1
     [1, 4],   # 样本2
     [3, 1]]   # 样本3
     
y = [1, 0, 1]   # 真实标签

w = [0.5, -0.2] # 初始权重
b = 0.1         # 初始偏置

计算步骤

  1. 前向传播(向量化):

    Z = X·w + b = [[2*0.5 + 3*(-0.2) + 0.1],
                  [1*0.5 + 4*(-0.2) + 0.1],
                  [3*0.5 + 1*(-0.2) + 0.1]]
         = [0.5, -0.2, 1.4]
         
    Y_hat = sigmoid(Z) ≈ [0.622, 0.450, 0.802]
    
  2. 计算误差:

    error = Y_hat - y = [0.622-1, 0.450-0, 0.802-1]
                    = [-0.378, 0.450, -0.198]
    
  3. 向量化梯度计算:

    dw = (1/3) * Xᵀ · error
       = (1/3) * [[2, 1, 3],   # X转置
                  [3, 4, 1]] ·  [[-0.378],
                                 [ 0.450],
                                 [-0.198]]
       = (1/3) * [[2*(-0.378) + 1*0.450 + 3*(-0.198)],
                  [3*(-0.378) + 4*0.450 + 1*(-0.198)]]
       = (1/3) * [[-0.756 + 0.450 - 0.594],
                  [-1.134 + 1.800 - 0.198]]
       = (1/3) * [[-0.900], [0.468]]
       ≈ [[-0.300], [0.156]]
    
    db = (1/3) * sum(error)
       = (1/3) * (-0.378 + 0.450 - 0.198)
       = (1/3) * (-0.126)
       = -0.042
    

Python实现对比

循环实现(低效)
def compute_gradients_loop(X, y, w, b):
    m, n = X.shape
    dw = np.zeros(n)
    db = 0
    
    for i in range(m):
        z_i = np.dot(X[i], w) + b
        a_i = 1 / (1 + np.exp(-z_i))
        dz_i = a_i - y[i]
        
        for j in range(n):
            dw[j] += X[i, j] * dz_i
            
        db += dz_i
    
    dw /= m
    db /= m
    return dw, db
向量化实现(高效)
def compute_gradients_vectorized(X, y, w, b):
    m = X.shape[0]
    Z = np.dot(X, w) + b
    A = 1 / (1 + np.exp(-Z))
    dZ = A - y
    
    dw = (1/m) * np.dot(X.T, dZ)
    db = (1/m) * np.sum(dZ)
    
    return dw, db

性能对比测试

import time
import numpy as np

# 生成大数据集 (10,000样本, 100特征)
X_large = np.random.randn(10000, 100)
y_large = np.random.randint(0, 2, 10000)
w_large = np.random.randn(100)
b_large = 0

# 测试循环实现
start = time.time()
dw_loop, db_loop = compute_gradients_loop(X_large, y_large, w_large, b_large)
loop_time = time.time() - start

# 测试向量化实现
start = time.time()
dw_vec, db_vec = compute_gradients_vectorized(X_large, y_large, w_large, b_large)
vec_time = time.time() - start

print(f"循环实现时间: {loop_time:.4f}秒")
print(f"向量化实现时间: {vec_time:.4f}秒")
print(f"加速比: {loop_time/vec_time:.1f}x")
print(f"梯度差异: {np.linalg.norm(dw_loop - dw_vec):.6f}")

典型输出

输出示例
输出示例

向量化背后的数学原理

  1. 矩阵微分规则
    ∂ ∂ w ( X w ) = X T \frac{\partial}{\partial w}(Xw) = X^T w(Xw)=XT

  2. 链式法则的向量形式
    ∇ w J = ∂ J ∂ y ^ ⋅ ∂ y ^ ∂ z ⋅ ∂ z ∂ w \nabla_w J = \frac{\partial J}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z} \cdot \frac{\partial z}{\partial w} wJ=y^Jzy^wz
    其中:

    • ∂ J ∂ y ^ = y ^ − y y ^ ( 1 − y ^ ) \frac{\partial J}{\partial \hat{y}} = \frac{\hat{y} - y}{\hat{y}(1-\hat{y})} y^J=y^(1y^)y^y
    • ∂ y ^ ∂ z = y ^ ( 1 − y ^ ) \frac{\partial \hat{y}}{\partial z} = \hat{y}(1-\hat{y}) zy^=y^(1y^)
    • ∂ z ∂ w = X T \frac{\partial z}{\partial w} = X^T wz=XT

    简化后:
    ∇ w J = X T ( y ^ − y ) \nabla_w J = X^T (\hat{y} - y) wJ=XT(y^y)

  3. 广播机制

    # 标量b自动广播到向量运算中
    Z = X.dot(w) + b  # b被加到每个样本上
    

Python实现逻辑回归

完整代码

import numpy as np
import matplotlib.pyplot as plt

class LogisticRegression:
    def __init__(self, learning_rate=0.01, n_iters=1000):
        self.lr = learning_rate
        self.n_iters = n_iters
        self.weights = None
        self.bias = None
    
    def _sigmoid(self, z):
        return 1 / (1 + np.exp(-z))
    
    def fit(self, X, y):
        n_samples, n_features = X.shape
        self.weights = np.zeros(n_features)
        self.bias = 0
        
        # 梯度下降优化
        for _ in range(self.n_iters):
            # 前向传播
            linear_model = np.dot(X, self.weights) + self.bias
            y_pred = self._sigmoid(linear_model)
            
            # 反向传播(向量化计算梯度)
            dw = (1 / n_samples) * np.dot(X.T, (y_pred - y))
            db = (1 / n_samples) * np.sum(y_pred - y)
            
            # 参数更新
            self.weights -= self.lr * dw
            self.bias -= self.lr * db
    
    def predict_prob(self, X):
        linear_model = np.dot(X, self.weights) + self.bias
        return self._sigmoid(linear_model)
    
    def predict(self, X, threshold=0.5):
        return (self.predict_prob(X) >= threshold).astype(int)

# 测试示例
if __name__ == "__main__":
    # 生成示例数据
    from sklearn.datasets import make_classification
    X, y = make_classification(n_samples=100, n_features=2, n_redundant=0, 
                               n_informative=2, random_state=42)
    
    # 训练模型
    model = LogisticRegression(learning_rate=0.1, n_iters=1000)
    model.fit(X, y)
    predictions = model.predict(X)
    
    # 计算准确率
    accuracy = np.mean(predictions == y)
    print(f"Model accuracy: {accuracy:.4f}")
    
    # 可视化决策边界
    plt.figure(figsize=(10, 6))
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Paired, edgecolors='k')
    
    ax = plt.gca()
    xlim = ax.get_xlim()
    ylim = ax.get_ylim()
    
    # 创建网格以绘制决策边界
    xx, yy = np.meshgrid(np.linspace(xlim[0], xlim[1], 50),
                         np.linspace(ylim[0], ylim[1], 50))
    Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    
    plt.contourf(xx, yy, Z, alpha=0.3, cmap=plt.cm.Paired)
    plt.title("Logistic Regression Decision Boundary")
    plt.xlabel("Feature 1")
    plt.ylabel("Feature 2")
    plt.show()
  • 输出示例
完整代码输出示例
完整代码输出示例

代码结构详解

1. 导入必要的库
import numpy as np
import matplotlib.pyplot as plt
  • numpy:用于高效的数值计算和矩阵运算
  • matplotlib.pyplot:用于数据可视化,绘制决策边界
2. LogisticRegression类定义
2.1 初始化方法 __init__
def __init__(self, learning_rate=0.01, n_iters=1000):
    self.lr = learning_rate
    self.n_iters = n_iters
    self.weights = None
    self.bias = None
  • learning_rate:学习率,控制梯度下降的步长
  • n_iters:梯度下降的迭代次数
  • weights:特征权重向量(待训练)
  • bias:偏置项(待训练)
2.2 Sigmoid激活函数 _sigmoid
def _sigmoid(self, z):
    return 1 / (1 + np.exp(-z))
  • 将线性输出转换为概率值(0-1之间)
  • 公式: σ ( z ) = 1 1 + e − z \sigma(z) = \frac{1}{1 + e^{-z}} σ(z)=1+ez1
  • 函数曲线特性:
    • z → ∞ z \to \infty z 时, σ ( z ) → 1 \sigma(z) \to 1 σ(z)1
    • z → − ∞ z \to -\infty z 时, σ ( z ) → 0 \sigma(z) \to 0 σ(z)0
    • z = 0 z = 0 z=0 时, σ ( z ) = 0.5 \sigma(z) = 0.5 σ(z)=0.5
2.3 训练方法 fit
def fit(self, X, y):
    n_samples, n_features = X.shape
    self.weights = np.zeros(n_features)
    self.bias = 0
  • 输入
    • X:训练数据特征矩阵(形状:n_samples × n_features)
    • y:训练数据标签向量(形状:n_samples)
  • 初始化:权重初始化为零向量,偏置初始化为0
    # 梯度下降优化
    for _ in range(self.n_iters):
        # 前向传播
        linear_model = np.dot(X, self.weights) + self.bias
        y_pred = self._sigmoid(linear_model)
  • 前向传播
    • 计算线性部分: z = X ⋅ w + b z = X \cdot w + b z=Xw+b
    • 应用sigmoid激活: y ^ = σ ( z ) \hat{y} = \sigma(z) y^=σ(z)
        # 反向传播(向量化计算梯度)
        dw = (1 / n_samples) * np.dot(X.T, (y_pred - y))
        db = (1 / n_samples) * np.sum(y_pred - y)
  • 梯度计算(向量化实现):
    • 权重梯度: ∂ J ∂ w = 1 m X T ( y ^ − y ) \frac{\partial J}{\partial w} = \frac{1}{m} X^T (\hat{y} - y) wJ=m1XT(y^y)
    • 偏置梯度: ∂ J ∂ b = 1 m ∑ ( y ^ − y ) \frac{\partial J}{\partial b} = \frac{1}{m} \sum (\hat{y} - y) bJ=m1(y^y)
        # 参数更新
        self.weights -= self.lr * dw
        self.bias -= self.lr * db
  • 参数更新:使用梯度下降算法
    • w : = w − α ∂ J ∂ w w := w - \alpha \frac{\partial J}{\partial w} w:=wαwJ
    • b : = b − α ∂ J ∂ b b := b - \alpha \frac{\partial J}{\partial b} b:=bαbJ
2.4 预测方法
def predict_prob(self, X):
    linear_model = np.dot(X, self.weights) + self.bias
    return self._sigmoid(linear_model)
  • 返回样本属于正类的概率(0-1之间)
def predict(self, X, threshold=0.5):
    return (self.predict_prob(X) >= threshold).astype(int)
  • 根据阈值(默认0.5)将概率转换为类别预测
  • 概率 ≥ threshold 预测为正类(1),否则为负类(0)
3. 测试与可视化
3.1 生成示例数据
from sklearn.datasets import make_classification
X, y = make_classification(n_samples=100, n_features=2, n_redundant=0, 
                           n_informative=2, random_state=42)
  • 使用scikit-learn生成二分类数据集
  • 100个样本,2个特征,无冗余特征
  • random_state=42确保结果可复现
3.2 模型训练与评估
model = LogisticRegression(learning_rate=0.1, n_iters=1000)
model.fit(X, y)
predictions = model.predict(X)
accuracy = np.mean(predictions == y)
print(f"Model accuracy: {accuracy:.4f}")
  • 创建模型实例(学习率0.1,迭代1000次)
  • 训练模型
  • 在训练集上预测并计算准确率
3.3 决策边界可视化
plt.figure(figsize=(10, 6))
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Paired, edgecolors='k')
  • 创建散点图,不同颜色表示不同类别
ax = plt.gca()
xlim = ax.get_xlim()
ylim = ax.get_ylim()

# 创建网格点
xx, yy = np.meshgrid(np.linspace(xlim[0], xlim[1], 50),
                     np.linspace(ylim[0], ylim[1], 50))
  • 获取当前坐标轴范围
  • 创建覆盖整个绘图区域的网格点
# 预测网格点的类别
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)

# 绘制决策边界
plt.contourf(xx, yy, Z, alpha=0.3, cmap=plt.cm.Paired)
  • 将网格点转换为特征矩阵进行预测
  • 使用等高线填充绘制决策区域
plt.title("Logistic Regression Decision Boundary")
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
plt.show()
  • 添加标题和轴标签
  • 显示可视化结果

关键概念图解

1. 逻辑回归工作流程
输入特征
线性变换 z = w·x + b
Sigmoid激活 ŷ = σ(z)
概率输出 0-1
二元分类决策

2. 梯度下降优化过程

初始化参数
前向传播计算预测值
计算损失函数
反向传播计算梯度
更新参数
达到最大迭代次数?
输出最终模型

总结

逻辑回归作为深度学习的基础组件,具有以下特点:

  • 通过sigmoid函数实现概率输出
  • 使用交叉熵损失函数优化分类性能
  • 依赖梯度下降进行参数更新
  • 向量化实现显著提升计算效率

网站公告

今日签到

点亮在社区的每一天
去签到