自己做笔记用,后续可能会继续添加内容。
逻辑回归(Logistic Regression) 是一种广泛应用于分类问题的统计学习方法,尤其适用于二分类问题。尽管名字中有“回归”,但它实际上是一种分类算法。逻辑回归通过拟合数据,预测样本属于某一类别的概率。
一、理论介绍
1. 基本思想
逻辑回归的核心思想是通过一个逻辑函数(Logistic Function),将线性回归的输出映射到 [0,1][0,1] 区间,表示样
2. 数学模型
(1)线性部分
逻辑回归首先对输入特征进行线性组合:
其中:
是输入特征。
是模型参数(回归系数)。
是线性组合的结果。
(2)逻辑函数(Sigmoid 函数)
将线性组合的结果 zz 通过 Sigmoid 函数映射到 [0,1]区间:
函数图像:
Sigmoid 函数的输出表示样本属于正类(类别 1)的概率:
(3)分类规则
根据概率值进行分类:
如果
,则预测为正类(类别 1)。
如果
,则预测为负类(类别 0)。
3. 参数估计
逻辑回归通过最大似然估计(Maximum Likelihood Estimation, MLE)来求解模型参数。具体步骤如下:
定义似然函数:
取对数似然函数:
最大化对数似然函数,等价于最小化损失函数
。
通常使用梯度下降法或其他优化算法来求解参数 。
4. 损失函数
(1)两种方法求得损失函数
逻辑回归的损失函数推导可以基于以下两种方法获得,殊途同归:
1. 可以基于上述参数估计中的对数似然函数取负数(因为损失函数一般要取到最小的正直)并除以N得到;
2.可以基于熵的概念推导出损失函数。
推导角度 | 极大似然估计 | 交叉熵 |
---|---|---|
核心思想 | 最大化数据出现的概率 | 最小化两个分布的差异 |
数学形式 | 负对数似然函数 | 交叉熵损失函数 |
理论依据 | 概率论(伯努利分布假设) | 信息论(分布差异度量) |
优化目标 | 找到最可能生成数据的参数 β | 让预测分布逼近真实分布 |
(2)利用熵求得损失函数
通常我们用熵(entropy)来表示随机变量不确定性的度量,或者说系统混乱程度、信息混乱程度。熵的计算公式如下:
交叉熵(Cross-Entropy):衡量两个分布 P(真实分布)和 QQ模型预测分布)之间的差异:
(3)最终损失函数
逻辑回归使用对数损失函数(Log Loss)来衡量预测概率与真实标签之间的差异。对于二分类问题,损失函数为:
其中:
是样本的真实标签(0 或 1)。
是模型预测的概率。
损失函数的直观理解
当真实标签
时,损失函数为
。如果预测概率
接近 1,则损失接近 0;如果预测概率接近 0,则损失会很大。
当真实标签
时,损失函数为
。如果预测概率
接近 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
关键实现说明
数据生成
使用
make_classification
生成线性可分数据集。数据标准化 (
StandardScaler
) 加速梯度下降收敛。
模型核心方法
sigmoid
: 将线性输出映射到 [0,1] 区间。fit
: 通过梯度下降更新参数,记录损失历史。predict
: 根据阈值 0.5 输出分类结果。
评估与可视化
损失曲线:监控训练过程是否收敛。
决策边界:直观展示分类效果。
与 scikit-learn 对比:验证实现正确性。
其他扩展内容
正则化:在梯度计算中加入 L1/L2 正则化项。
随机梯度下降:修改
fit
方法实现小批量更新。多分类支持:改用 Softmax 函数和交叉熵损失。