从零构建逻辑回归: sklearn 与自定义实现对比

发布于:2025-03-11 ⋅ 阅读:(18) ⋅ 点赞:(0)


尽管名字中有“回归”二字,逻辑回归(Logistic Regression)实际上是一种分类方法,主要用于二分类问题。它通过一个线性模型与 Sigmoid 函数的结合,将问题转化为一个概率预测问题,并基于该概率来做分类决策。下面将详细讲解逻辑回归的工作原理、损失函数、梯度推导、以及如何通过梯度下降来优化模型。


这篇文章也写的不错:
Python实现逻辑回归(Logistic Regression)


理论基础

1. 逻辑回归模型

逻辑回归的核心是 Sigmoid 函数,也称为 逻辑函数。它可以将任何实数值映射到区间 [0, 1] 内,因此非常适合用于概率预测。Sigmoid 函数的形式为:

σ ( z ) = 1 1 + e − z \sigma(z) = \frac{1}{1 + e^{-z}} σ(z)=1+ez1

其中 (z) 是线性回归的输出,可以表示为:

z = w T x + b z = w^T x + b z=wTx+b

这里:

  • w w w 是权重向量(模型参数之一),
  • x x x 是输入特征向量,
  • b b b 是偏置项。

因此,Sigmoid 函数的输出 y ^ = σ ( z ) \hat{y} = \sigma(z) y^=σ(z) 表示样本属于正类(即 y = 1 y = 1 y=1)的概率。对于二分类问题,模型的目标是预测输入样本属于类别1的概率:

P ( y = 1 ∣ x ) = σ ( w T x + b ) P(y = 1 | x) = \sigma(w^T x + b) P(y=1∣x)=σ(wTx+b)

如果 y ^ ≥ 0.5 \hat{y} \geq 0.5 y^0.5,则预测样本为正类 y = 1 y = 1 y=1,否则预测为负类 y = 0 y = 0 y=0


2. 损失函数

逻辑回归使用 交叉熵损失函数(Cross-Entropy Loss)来衡量模型的预测与真实标签之间的差异。交叉熵损失函数的形式如下:

L ( y , y ^ ) = − [ y log ⁡ ( y ^ ) + ( 1 − y ) log ⁡ ( 1 − y ^ ) ] L(y, \hat{y}) = - \left[ y \log(\hat{y}) + (1 - y) \log(1 - \hat{y}) \right] L(y,y^)=[ylog(y^)+(1y)log(1y^)]

其中:

  • y y y 是真实标签(0 或 1),
  • y ^ \hat{y} y^ 是模型预测的概率。

对于整个训练集,损失函数 J ( w , b ) J(w, b) J(w,b) 是所有样本损失的平均值,具体形式为:

J ( w , b ) = − 1 m ∑ i = 1 m [ y ( i ) log ⁡ ( y ^ ( i ) ) + ( 1 − y ( i ) ) log ⁡ ( 1 − y ^ ( i ) ) ] J(w, b) = -\frac{1}{m} \sum_{i=1}^m \left[ y^{(i)} \log(\hat{y}^{(i)}) + (1 - y^{(i)}) \log(1 - \hat{y}^{(i)}) \right] J(w,b)=m1i=1m[y(i)log(y^(i))+(1y(i))log(1y^(i))]

其中 m m m 是训练集中的样本数量。


3. 梯度推导

为了优化逻辑回归模型,我们需要通过梯度下降算法最小化损失函数。首先,我们需要计算损失函数相对于模型参数 w w w b b b 的梯度。

(1) 计算 ∂ L ∂ y ^ \frac{\partial L}{\partial \hat{y}} y^L

损失函数对预测值的导数为:

∂ L ∂ y ^ = − ( y y ^ − 1 − y 1 − y ^ ) \frac{\partial L}{\partial \hat{y}} = -\left( \frac{y}{\hat{y}} - \frac{1 - y}{1 - \hat{y}} \right) y^L=(y^y1y^1y)

(2) 计算 ∂ y ^ ∂ z \frac{\partial \hat{y}}{\partial z} zy^

Sigmoid 函数的导数为:

∂ y ^ ∂ z = y ^ ( 1 − y ^ ) \frac{\partial \hat{y}}{\partial z} = \hat{y} (1 - \hat{y}) zy^=y^(1y^)

(3) 计算 ∂ L ∂ z \frac{\partial L}{\partial z} zL

根据链式法则,可以得到:

∂ L ∂ z = ∂ L ∂ y ^ ⋅ ∂ y ^ ∂ z = y ^ − y \frac{\partial L}{\partial z} = \frac{\partial L}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z} = \hat{y} - y zL=y^Lzy^=y^y

(4) 计算 ∂ z ∂ w \frac{\partial z}{\partial w} wz ∂ z ∂ b \frac{\partial z}{\partial b} bz

线性回归的输出对权重和偏置的导数为:

∂ z ∂ w = x , ∂ z ∂ b = 1 \frac{\partial z}{\partial w} = x, \quad \frac{\partial z}{\partial b} = 1 wz=x,bz=1

(5) 计算 ∂ L ∂ w \frac{\partial L}{\partial w} wL ∂ L ∂ b \frac{\partial L}{\partial b} bL

根据链式法则:

∂ L ∂ w = ( y ^ − y ) ⋅ x \frac{\partial L}{\partial w} = (\hat{y} - y) \cdot x wL=(y^y)x

∂ L ∂ b = y ^ − y \frac{\partial L}{\partial b} = \hat{y} - y bL=y^y

对于整个训练集,梯度是所有样本梯度的平均值:

∂ J ∂ w = 1 m ∑ i = 1 m ( y ^ ( i ) − y ( i ) ) ⋅ x ( i ) \frac{\partial J}{\partial w} = \frac{1}{m} \sum_{i=1}^m (\hat{y}^{(i)} - y^{(i)}) \cdot x^{(i)} wJ=m1i=1m(y^(i)y(i))x(i)

∂ J ∂ b = 1 m ∑ i = 1 m ( y ^ ( i ) − y ( i ) ) \frac{\partial J}{\partial b} = \frac{1}{m} \sum_{i=1}^m (\hat{y}^{(i)} - y^{(i)}) bJ=m1i=1m(y^(i)y(i))


4. 梯度下降

梯度下降是一种优化算法,用于最小化损失函数并学习最佳的模型参数。梯度下降的更新规则如下:

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

其中, α \alpha α 是学习率,控制每次更新的步长。

梯度下降的执行步骤:
  1. 初始化参数 w w w b b b
  2. 计算损失函数 J ( w , b ) J(w, b) J(w,b)
  3. 计算梯度 ∂ J ∂ w \frac{\partial J}{\partial w} wJ ∂ J ∂ b \frac{\partial J}{\partial b} bJ
  4. 更新参数:
    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
  5. 重复步骤 2-4,直到损失函数收敛或达到最大迭代次数。

5. 总结

逻辑回归通过将线性回归的结果通过 Sigmoid 函数转化为概率,并使用交叉熵损失函数来衡量模型的误差。通过反向传播计算梯度,并利用梯度下降优化参数,逻辑回归模型可以逐步学习到最优的分类边界,适用于二分类问题。


项目结构

project_root/
├── my_sklearn/
│   ├── linear_model/
│   │   ├── __init__.py
│   │   └── logistic_regression.py
│   ├── metrics/
│   │   ├── __init__.py
│   │   └── accuracy.py
│   └── __init__.py
└── main.py

代码实现

1. my_sklearn/linear_model/logistic_regression.py — 自定义逻辑回归实现
# my_sklearn/linear_model/logistic_regression.py
# 包含逻辑回归模型的实现

import numpy as np


class MyLogisticRegression:
    def __init__(self, learning_rate=0.01, epochs=1000):
        self.learning_rate = learning_rate  # 学习率
        self.epochs = epochs  # 迭代次数
        self.weights = None  # 权重
        self.bias = None  # 偏置

    # Sigmoid 激活函数
    def sigmoid(self, linear_output):
        result = 1 / (1 + np.exp(-linear_output))
        return result
    
    # 损失函数:交叉熵损失
    def compute_loss(self, X, y):
        m = len(y)
        predictions = self.sigmoid(np.dot(X, self.weights) + self.bias)
        loss = -1/m * np.sum(y * np.log(predictions) + (1 - y) * np.log(1 - predictions))
        return loss

    # 训练模型
    def fit(self, X, y):
        m, n = X.shape
        self.weights = np.zeros(n)
        self.bias = 0
        
        for epoch in range(self.epochs):
            # 前向传播
            z = np.dot(X, self.weights) + self.bias  # 计算线性组合
            predictions = self.sigmoid(z)  # 通过sigmoid函数得到概率
            
            # 反向传播
            dw = (1/m) * np.dot(X.T, (predictions - y))
            db = (1/m) * np.sum(predictions - y)
            
            # 更新参数
            self.weights -= self.learning_rate * dw
            self.bias -= self.learning_rate * db
            
            # 每100次迭代输出一次损失
            if epoch % 100 == 0:
                loss = self.compute_loss(X, y)
                print(f"Epoch {epoch}: Loss = {loss}")

    # 预测
    def predict(self, X):
        z = np.dot(X, self.weights) + self.bias
        predictions = self.sigmoid(z)
        class_predictions = np.where(predictions >= 0.5, 1, 0)
        return class_predictions

2. my_sklearn/metrics/accuracy.py — 自定义准确率计算
# my_sklearn/metrics/accuracy.py

def my_accuracy_score(y_true, y_pred):
    """
    计算准确率:正确预测的样本数 / 总样本数
    
    参数:
    y_true -- 真实标签
    y_pred -- 预测标签
    
    返回:
    accuracy -- 准确率
    """
    correct = sum(y_true == y_pred)
    total = len(y_true)
    accuracy = correct / total
    return accuracy

3. my_sklearn/linear_model/__init__.py — 导出 MyLogisticRegression
# my_sklearn/linear_model/__init__.py
# linear_model子包初始化文件

# 从logistic_regression模块导入类
from .logistic_regression import MyLogisticRegression

# 导出的类列表
__all__ = ['MyLogisticRegression'] 

4. my_sklearn/metrics/__init__.py — 导出准确率函数
# my_sklearn/metrics/__init__.py
# metrics子包初始化文件

# 从accuracy模块导入函数
from .accuracy import my_accuracy_score

# 导出的函数列表
__all__ = ['my_accuracy_score'] 

5. my_sklearn/__init__.py — 主包初始化
# my_sklearn/__init__.py
# 主包初始化文件

# 导入子模块,使它们可以通过my_sklearn.xxx直接访问
from . import metrics
from . import linear_model 

6. main.py — 主程序文件,包含数据处理和模型对比
# main.py

import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression as SklearnLogisticRegression
from sklearn.metrics import accuracy_score  # 导入sklearn的准确率计算函数

# 导入自定义模块
from my_sklearn.linear_model import MyLogisticRegression  # 导入自定义逻辑回归模型
from my_sklearn.metrics import my_accuracy_score  # 导入自定义准确率计算函数

# 1. 加载鸢尾花数据集
iris = datasets.load_iris()
X = iris.data
y = iris.target

# 只选择两类进行二分类
X = X[y != 2]
y = y[y != 2]

# 2. 数据预处理(标准化)
scaler = StandardScaler()
X = scaler.fit_transform(X)

# 3. 切分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 4. 使用自定义实现的逻辑回归
my_lr = MyLogisticRegression(learning_rate=0.01, epochs=1000)
my_lr.fit(X_train, y_train)

# 5. 使用 sklearn 实现的逻辑回归
sklearn_lr = SklearnLogisticRegression(max_iter=1000)
sklearn_lr.fit(X_train, y_train)

# 6. 对比自定义模型和 sklearn 模型的预测准确度
my_pred = my_lr.predict(X_test)
sklearn_pred = sklearn_lr.predict(X_test)

# 使用自定义函数计算自定义模型的准确率
my_accuracy = my_accuracy_score(y_test, my_pred)
# 使用sklearn的函数计算sklearn模型的准确率
sklearn_accuracy = accuracy_score(y_test, sklearn_pred)

print(f"My Logistic Regression Accuracy: {my_accuracy * 100:.2f}%")
print(f"Sklearn Logistic Regression Accuracy: {sklearn_accuracy * 100:.2f}%")


项目使用说明

  1. 安装依赖
    由于此项目只使用了 numpysklearn 库,你可以通过以下命令安装它们:

    pip install numpy scikit-learn
    
  2. 运行项目
    运行 main.py 文件来加载数据集、训练模型并对比自定义模型和 sklearn 版本的准确度:

    python main.py
    

总结

这个项目实现了自定义的逻辑回归模型,并与 sklearn 的实现进行了对比。结构上,模仿了 sklearn 的模块化方式,将功能分为 linear_modelmetrics 两个子模块。


网站公告

今日签到

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