机器学习-逻辑回归

发布于:2025-04-15 ⋅ 阅读:(23) ⋅ 点赞:(0)

自己做笔记用,后续可能会继续添加内容。

逻辑回归(Logistic Regression) 是一种广泛应用于分类问题的统计学习方法,尤其适用于二分类问题。尽管名字中有“回归”,但它实际上是一种分类算法。逻辑回归通过拟合数据,预测样本属于某一类别的概率。

一、理论介绍

1. 基本思想

逻辑回归的核心思想是通过一个逻辑函数(Logistic Function),将线性回归的输出映射到 [0,1][0,1] 区间,表示样

2. 数学模型

(1)线性部分

逻辑回归首先对输入特征进行线性组合:

z = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + \dots + \beta_n x_n

其中:

  • x_1, x_2, \dots, x_n 是输入特征。

  • beta_0, \beta_1, \dots, \beta_n 是模型参数(回归系数)。

  • z 是线性组合的结果。

(2)逻辑函数(Sigmoid 函数)

将线性组合的结果 zz 通过 Sigmoid 函数映射到 [0,1]区间:

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

函数图像:

Sigmoid 函数的输出表示样本属于正类(类别 1)的概率:

P(y=1 | X) = \sigma(z)

(3)分类规则

根据概率值进行分类:

  • 如果P(y=1 | X) \geq 0.5,则预测为正类(类别 1)。

  • 如果 P(y=1 | X) < 0.5,则预测为负类(类别 0)。

3. 参数估计

逻辑回归通过最大似然估计(Maximum Likelihood Estimation, MLE)来求解模型参数\beta。具体步骤如下:

  1. 定义似然函数:

    L(\beta) = \prod_{i=1}^N P(y_i | X_i)
  2. 取对数似然函数:

    \log L(\beta) = \sum_{i=1}^N \left[ y_i \log(P(y_i=1 | X_i)) + (1-y_i) \log(1 - P(y_i=1 | X_i)) \right]
  3. 最大化对数似然函数,等价于最小化损失函数 J(\beta)

通常使用梯度下降法或其他优化算法来求解参数 \beta

4. 损失函数

(1)两种方法求得损失函数

逻辑回归的损失函数推导可以基于以下两种方法获得,殊途同归:

1. 可以基于上述参数估计中的对数似然函数取负数(因为损失函数一般要取到最小的正直)并除以N得到;

2.可以基于熵的概念推导出损失函数。

推导角度 极大似然估计 交叉熵
核心思想 最大化数据出现的概率 最小化两个分布的差异
数学形式 负对数似然函数 交叉熵损失函数
理论依据 概率论(伯努利分布假设) 信息论(分布差异度量)
优化目标 找到最可能生成数据的参数 β 让预测分布逼近真实分布

(2)利用熵求得损失函数

通常我们用熵(entropy)来表示随机变量不确定性的度量,或者说系统混乱程度、信息混乱程度。熵的计算公式如下:

H(X) = -\sum^N_{i=1}p(x_i)log(p(x_i))

交叉熵(Cross-Entropy):衡量两个分布 P(真实分布)和 QQ模型预测分布)之间的差异:

cross\_entropy(P,Q) = -\sum ^n_{i=1}P(x_i)log(Q(x_i))

(3)最终损失函数

逻辑回归使用对数损失函数(Log Loss)来衡量预测概率与真实标签之间的差异。对于二分类问题,损失函数为:

J(\beta) = -\frac{1}{N} \sum_{i=1}^N \left[ y_i \log(P(y_i=1 | X_i)) + (1-y_i) \log(1 - P(y_i=1 | X_i)) \right]

其中:

  • y_i是样本的真实标签(0 或 1)。

  • P(y_i=1 | X_i) 是模型预测的概率。

损失函数的直观理解

  • 当真实标签y_i = 1 时,损失函数为-\log(P(y_i=1 | X_i)。如果预测概率P(y_i=1 | X_i) 接近 1,则损失接近 0;如果预测概率接近 0,则损失会很大。

  • 当真实标签 y_i = 0 时,损失函数为 -\log(1 - P(y_i=1 | X_i)。如果预测概率 P(y_i=1 | X_i)接近 0,则损失接近 0;如果预测概率接近 1,则损失会很大。

5. 多分类问题

逻辑回归可以通过以下方式扩展到多分类问题:

  • One-vs-Rest (OvR):训练多个二分类器,每个分类器区分一个类别与其他类别。

  • Softmax 回归:直接推广到多分类,使用 Softmax 函数计算每个类别的概率。

6. 优缺

(1)优点

  • 简单高效,计算速度快。

  • 输出具有概率意义,易于解释。

  • 可以处理线性可分或近似线性可分的数据。

(2)缺点

  • 只能处理线性决策边界(除非引入特征变换)。

  • 对异常值和多重共线性敏感。

  • 在特征空间较大时容易过拟合。

二、代码实现

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, confusion_matrix
from sklearn.preprocessing import StandardScaler

# ======================
# 1. 数据生成与预处理
# ======================
# 生成二分类数据集
X, y = make_classification(
    n_samples=1000,  # 样本数量
    n_features=2,    # 特征数量
    n_redundant=0,   # 冗余特征
    n_clusters_per_class=1,
    random_state=42
)

# 数据标准化
scaler = StandardScaler()
X = scaler.fit_transform(X)

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 添加偏置项 (intercept term)
X_train = np.hstack([np.ones((X_train.shape[0], 1)), X_train])
X_test = np.hstack([np.ones((X_test.shape[0], 1)), X_test])

# ======================
# 2. 逻辑回归模型实现
# ======================
class LogisticRegression:
    def __init__(self, lr=0.01, n_iters=1000):
        self.lr = lr          # 学习率
        self.n_iters = n_iters  # 迭代次数
        self.weights = None   # 模型参数
        self.loss_history = []  # 损失记录

    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)

        # 梯度下降
        for _ in range(self.n_iters):
            # 计算预测值
            linear_model = np.dot(X, self.weights)
            y_pred = self.sigmoid(linear_model)

            # 计算梯度
            gradient = np.dot(X.T, (y_pred - y)) / n_samples
            # 更新参数
            self.weights -= self.lr * gradient

            # 记录损失
            loss = self._compute_loss(y, y_pred)
            self.loss_history.append(loss)

    def _compute_loss(self, y, y_pred):
        # 交叉熵损失
        epsilon = 1e-15  # 防止 log(0)
        y_pred = np.clip(y_pred, epsilon, 1 - epsilon)
        return -np.mean(y * np.log(y_pred) + (1 - y) * np.log(1 - y_pred))

    def predict(self, X):
        linear_model = np.dot(X, self.weights)
        y_pred = self.sigmoid(linear_model)
        return (y_pred >= 0.5).astype(int)

# ======================
# 3. 模型训练与预测
# ======================
# 初始化模型
model = LogisticRegression(lr=0.1, n_iters=1000)
model.fit(X_train, y_train)

# 预测
y_train_pred = model.predict(X_train)
y_test_pred = model.predict(X_test)

# ======================
# 4. 结果评估
# ======================
# 准确率
train_acc = accuracy_score(y_train, y_train_pred)
test_acc = accuracy_score(y_test, y_test_pred)
print(f"Train Accuracy: {train_acc:.4f}")
print(f"Test Accuracy: {test_acc:.4f}")

# 混淆矩阵
print("\nConfusion Matrix (Test Set):")
print(confusion_matrix(y_test, y_test_pred))

# ======================
# 5. 可视化
# ======================
# 损失曲线
plt.figure(figsize=(10, 4))
plt.plot(model.loss_history)
plt.title("Training Loss Curve")
plt.xlabel("Iterations")
plt.ylabel("Cross-Entropy Loss")
plt.grid(True)
plt.show()

# 决策边界
def plot_decision_boundary(X, y, model):
    x_min, x_max = X[:, 1].min() - 1, X[:, 1].max() + 1
    y_min, y_max = X[:, 2].min() - 1, X[:, 2].max() + 1
    xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.01),
                         np.arange(y_min, y_max, 0.01))
    
    Z = model.predict(np.c_[np.ones((xx.ravel().shape[0], 1)), xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    
    plt.figure(figsize=(8, 6))
    plt.contourf(xx, yy, Z, alpha=0.4)
    plt.scatter(X[:, 1], X[:, 2], c=y, s=20, edgecolor='k')
    plt.title("Decision Boundary")
    plt.xlabel("Feature 1")
    plt.ylabel("Feature 2")
    plt.show()

plot_decision_boundary(X_train, y_train, model)

# ======================
# 6. 对比scikit-learn实现
# ======================
from sklearn.linear_model import LogisticRegression as SKLogisticRegression

sk_model = SKLogisticRegression()
sk_model.fit(X_train[:, 1:], y_train)  # 移除添加的偏置项
sk_acc = sk_model.score(X_test[:, 1:], y_test)
print(f"\nscikit-learn Test Accuracy: {sk_acc:.4f}")

Train Accuracy: 0.9038
Test Accuracy: 0.9000

Confusion Matrix (Test Set):
[[97  7]
 [13 83]]

scikit-learn Test Accuracy: 0.9000

关键实现说明

  1. 数据生成

    • 使用 make_classification 生成线性可分数据集。

    • 数据标准化 (StandardScaler) 加速梯度下降收敛。

  2. 模型核心方法

    • sigmoid: 将线性输出映射到 [0,1] 区间。

    • fit: 通过梯度下降更新参数,记录损失历史。

    • predict: 根据阈值 0.5 输出分类结果。

  3. 评估与可视化

    • 损失曲线:监控训练过程是否收敛。

    • 决策边界:直观展示分类效果。

    • 与 scikit-learn 对比:验证实现正确性。

  4. 其他扩展内容

    • 正则化:在梯度计算中加入 L1/L2 正则化项。

    • 随机梯度下降:修改 fit 方法实现小批量更新。

    • 多分类支持:改用 Softmax 函数和交叉熵损失。


网站公告

今日签到

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