逻辑回归实战:基于鸢尾花数据集的sklearn与自定义梯度下降实现对比

1次阅读
没有评论

共计 3574 个字符,预计需要花费 9 分钟才能阅读完成。

image.webp

逻辑回归与鸢尾花数据集简介

逻辑回归(Logistic Regression)是解决二分类问题的经典算法,通过 sigmoid 函数将线性回归结果映射到 [0,1] 区间,表示样本属于某一类的概率。选择鸢尾花数据集(Iris dataset)的前两个特征(花萼长度和宽度)进行实验,主要基于以下考虑:

逻辑回归实战:基于鸢尾花数据集的 sklearn 与自定义梯度下降实现对比

  • 降低可视化难度:二维特征可以方便地用平面图展示决策边界
  • 教学演示友好:减少特征维度能更清晰地展示算法原理
  • 保留足够信息:前两个特征已能较好区分 setosa(0)和 versicolor(1)类别

数据准备

首先加载并预处理数据:

from sklearn.datasets import load_iris
import numpy as np

# 加载数据,只保留前两个特征和类别 0 /1
iris = load_iris()
X = iris.data[:100, :2]  # 前 100 个样本(类别 0 和 1),前两个特征
y = iris.target[:100]    # 对应的标签

# 训练测试集分割
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

方法一:sklearn 实现

1. 使用 Pipeline 构建模型

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression

# 构建包含特征标准化和逻辑回归的 pipeline
lr_pipe = Pipeline([('scaler', StandardScaler()),  # 特征标准化
    ('lr', LogisticRegression(penalty='none'))  # 无正则化
])

# 训练模型
lr_pipe.fit(X_train, y_train)

# 评估模型
train_acc = lr_pipe.score(X_train, y_train)
test_acc = lr_pipe.score(X_test, y_test)
print(f'Train accuracy: {train_acc:.2f}, Test accuracy: {test_acc:.2f}')

2. 决策边界可视化

import matplotlib.pyplot as plt

# 创建网格点用于绘制决策边界
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 = lr_pipe.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)

# 绘制决策边界和散点图
plt.contourf(xx, yy, Z, alpha=0.4)
plt.scatter(X[:, 0], X[:, 1], c=y, edgecolor='k')
plt.xlabel('Sepal length')
plt.ylabel('Sepal width')
plt.title('sklearn Logistic Regression Decision Boundary')
plt.show()

方法二:自定义梯度下降实现

1. 数学原理

逻辑回归的假设函数和损失函数:

$$
h_\theta(x) = \frac{1}{1 + e^{-\theta^Tx}}
$$

$$
J(\theta) = -\frac{1}{m}\sum_{i=1}^m [y^{(i)}\log(h_\theta(x^{(i)})) + (1-y^{(i)})\log(1-h_\theta(x^{(i)}))]
$$

参数更新公式(梯度下降):

$$
\theta_j := \theta_j – \alpha \frac{\partial J(\theta)}{\partial \theta_j}
$$

2. Python 实现

class CustomLogisticRegression:
    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(self, X):
        linear_model = np.dot(X, self.weights) + self.bias
        y_pred = self._sigmoid(linear_model)
        return (y_pred > 0.5).astype(int)

3. 模型训练与评估

# 特征标准化(对梯度下降非常重要)from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 训练自定义模型
custom_lr = CustomLogisticRegression(learning_rate=0.1, n_iters=1000)
custom_lr.fit(X_train_scaled, y_train)

# 评估模型
def accuracy(y_true, y_pred):
    return np.sum(y_true == y_pred) / len(y_true)

train_pred = custom_lr.predict(X_train_scaled)
test_pred = custom_lr.predict(X_test_scaled)

train_acc = accuracy(y_train, train_pred)
test_acc = accuracy(y_test, test_pred)
print(f'Custom Model - Train accuracy: {train_acc:.2f}, Test accuracy: {test_acc:.2f}')

两种方法对比

1. 决策边界对比

# 自定义模型的决策边界可视化(代码类似 sklearn 部分,略)

2. 量化指标对比

指标 sklearn 实现 自定义实现
训练集准确率 1.00 1.00
测试集准确率 1.00 1.00
训练时间(秒) 0.002 0.015
代码复杂度
可定制性 有限

避坑指南

  1. 学习率选择
  2. 过大:可能导致震荡甚至发散
  3. 过小:收敛速度慢
  4. 建议:尝试 0.001, 0.01, 0.1 等值,观察损失函数下降曲线

  5. 特征缩放

  6. 对 sklearn 实现:LogisticRegression 默认会进行特征缩放
  7. 对自定义实现:必须手动进行标准化

  8. 类别不平衡

  9. sklearn 中可设置 class_weight 参数
  10. 自定义实现可对损失函数添加样本权重

思考题

  1. 当特征维度增加到 10 个时,梯度下降法的训练时间会如何变化?sklearn 的实现又会怎样?
  2. 如何在自定义实现中加入 L2 正则化项?需要修改哪些部分的代码?

总结

通过本次实验,我们对比了 sklearn 内置 LogisticRegression 和自定义梯度下降实现的差异。对于大多数实际应用场景,推荐使用 sklearn 实现,因为它经过高度优化且稳定可靠。但当需要特殊定制(如修改损失函数、添加特殊正则化项)时,自定义实现提供了更大的灵活性。理解两种方法的底层原理,能帮助我们在不同场景下做出更合适的选择。

正文完
 0
评论(没有评论)