机器学习基础(8):逻辑回归
一、引言
在前面的学习中,我们已经掌握了线性回归、损失函数、反向传播以及激活函数等核心概念。但线性回归有一个明显的局限性:
它只能预测连续值,无法直接处理分类问题。
而在现实世界中,分类问题无处不在:
- 邮件是垃圾邮件还是正常邮件?
- 肿瘤是良性还是恶性?
- 客户是否会购买产品?
这些问题都需要模型输出离散类别,而非连续数值。
逻辑回归(Logistic Regression) 正是为解决这类问题而生。虽然名字里有"回归",但它实际上是一种分类算法。
二、从线性回归到分类问题
1. 为什么不能直接用线性回归做分类?
假设我们要预测"考试是否及格"(及格=1,不及格=0):
如果用线性回归:
问题很明显:
- 输出范围是 \((-\infty, +\infty)\),但分类结果只能是 0 或 1
- 预测值可能大于1或小于0,没有概率意义
2. 解决方案:引入 Sigmoid 函数
我们需要一个函数,将任意实数映射到 \((0, 1)\) 区间:
其中 \(z = w^Tx + b\) 是线性部分的结果。
Sigmoid 函数的特点:
- 当 \(z \to +\infty\) 时,\(\sigma(z) \to 1\)
- 当 \(z \to -\infty\) 时,\(\sigma(z) \to 0\)
- 当 \(z = 0\) 时,\(\sigma(z) = 0.5\)
这样,输出就可以解释为概率:
三、逻辑回归的数学原理
1. 模型定义
逻辑回归的完整表达式:
其中 \(\theta = [b, w_1, w_2, ..., w_n]^T\) 包含偏置项和所有权重。
2. 决策边界
通常设定阈值为 0.5:
- 如果 \(h_\theta(x) \geq 0.5\),预测为类别 1
- 如果 \(h_\theta(x) < 0.5\),预测为类别 0
这等价于:
决策边界就是 \(\theta^Tx = 0\) 这条线(或超平面)。
四、损失函数:交叉熵损失
1. 为什么不用均方误差(MSE)?
如果直接用 MSE:
由于 Sigmoid 函数的非线性,损失函数会变成非凸函数,存在很多局部最优,梯度下降容易陷入局部最小值。
2. 交叉熵损失(Cross-Entropy Loss)
对于二分类问题,定义:
直观理解:
- 当真实标签 \(y=1\) 时,损失为 \(-\log(h_\theta(x))\),预测越接近1,损失越小
- 当真实标签 \(y=0\) 时,损失为 \(-\log(1-h_\theta(x))\),预测越接近0,损失越小
交叉熵损失是凸函数,保证梯度下降能找到全局最优。
五、梯度下降与参数更新
1. 梯度推导
对交叉熵损失求导(过程略),得到:
神奇的是:这个形式和线性回归的梯度公式完全一样!
2. 参数更新
其中 \(\alpha\) 是学习率。
六、Scikit-learn 简介
在深入代码实现之前,先介绍一下 Scikit-learn(简称 sklearn)—— Python 中最流行的机器学习库之一。
1. 什么是 Scikit-learn?
Scikit-learn 是基于 NumPy、SciPy 和 Matplotlib 的开源机器学习库,提供了简单且高效的数据挖掘和数据分析工具。
主要特点:
- 简单易用:统一的 API 设计,所有模型都遵循
fit→predict→evaluate的流程 - 文档丰富:详尽的官方文档和示例
- 算法全面:涵盖分类、回归、聚类、降维等主流算法
- 集成性好:与 NumPy、Pandas、Matplotlib 无缝衔接
2. 安装方法
pip install scikit-learn
3. 基本使用流程
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# 1. 准备数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 2. 创建模型
model = LogisticRegression()
# 3. 训练模型
model.fit(X_train, y_train)
# 4. 预测
predictions = model.predict(X_test)
# 5. 评估
accuracy = accuracy_score(y_test, predictions)
七、代码实现
1. NumPy 实现逻辑回归
import numpy as np
import matplotlib.pyplot as plt
class LogisticRegression:
def __init__(self, learning_rate=0.01, n_iterations=1000):
self.lr = learning_rate
self.n_iter = n_iterations
self.weights = None
self.bias = None
self.losses = []
def _sigmoid(self, z):
"""Sigmoid激活函数"""
return 1 / (1 + np.exp(-z))
def _compute_loss(self, y_true, y_pred):
"""计算交叉熵损失"""
# 防止log(0)数值不稳定
epsilon = 1e-15
y_pred = np.clip(y_pred, epsilon, 1 - epsilon)
loss = -np.mean(y_true * np.log(y_pred) +
(1 - y_true) * np.log(1 - y_pred))
return loss
def fit(self, X, y):
"""训练模型"""
n_samples, n_features = X.shape
# 初始化参数
self.weights = np.zeros(n_features)
self.bias = 0
# 梯度下降
for i in range(self.n_iter):
# 前向传播
linear_model = np.dot(X, self.weights) + self.bias
y_pred = self._sigmoid(linear_model)
# 计算损失
loss = self._compute_loss(y, y_pred)
self.losses.append(loss)
# 计算梯度
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
# 每100次迭代打印一次损失
if (i + 1) % 100 == 0:
print(f"Iteration {i+1}/{self.n_iter}, Loss: {loss:.4f}")
def predict_proba(self, X):
"""预测概率"""
linear_model = np.dot(X, self.weights) + self.bias
return self._sigmoid(linear_model)
def predict(self, X, threshold=0.5):
"""预测类别"""
probabilities = self.predict_proba(X)
return (probabilities >= threshold).astype(int)
def accuracy(self, X, y):
"""计算准确率"""
predictions = self.predict(X)
return np.mean(predictions == y)
# ==================== 示例:使用逻辑回归进行分类 ====================
# 生成示例数据
np.random.seed(42)
# 类别0的数据(左下角)
X_class0 = np.random.randn(50, 2) + np.array([-2, -2])
y_class0 = np.zeros(50)
# 类别1的数据(右上角)
X_class1 = np.random.randn(50, 2) + np.array([2, 2])
y_class1 = np.ones(50)
# 合并数据
X = np.vstack([X_class0, X_class1])
y = np.hstack([y_class0, y_class1])
# 划分训练集和测试集
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)
# 创建并训练模型
model = LogisticRegression(learning_rate=0.1, n_iterations=1000)
model.fit(X_train, y_train)
# 评估模型
train_acc = model.accuracy(X_train, y_train)
test_acc = model.accuracy(X_test, y_test)
print(f"\n训练集准确率: {train_acc:.4f}")
print(f"测试集准确率: {test_acc:.4f}")
# 可视化结果
plt.figure(figsize=(12, 5))
# 子图1:数据分布和决策边界
plt.subplot(1, 2, 1)
plt.scatter(X_class0[:, 0], X_class0[:, 1], c='red', label='Class 0', alpha=0.6)
plt.scatter(X_class1[:, 0], X_class1[:, 1], c='blue', label='Class 1', alpha=0.6)
# 绘制决策边界
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 100),
np.linspace(y_min, y_max, 100))
Z = model.predict_proba(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z, levels=50, alpha=0.3, cmap='RdBu')
plt.contour(xx, yy, Z, levels=[0.5], colors='black', linestyles='--')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.title('Logistic Regression Decision Boundary')
plt.legend()
# 子图2:损失曲线
plt.subplot(1, 2, 2)
plt.plot(model.losses)
plt.xlabel('Iteration')
plt.ylabel('Loss')
plt.title('Training Loss Curve')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('logistic_regression_demo.png', dpi=150)
plt.show()
2. 使用 Scikit-learn
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
# 生成更复杂的数据集
X, y = make_classification(n_samples=1000, n_features=4, n_redundant=0,
n_informative=4, n_classes=2, random_state=42)
# 划分数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 创建模型(使用L2正则化)
model = LogisticRegression(penalty='l2', C=1.0, max_iter=1000, random_state=42)
# 训练
model.fit(X_train, y_train)
# 预测
y_pred = model.predict(X_test)
y_pred_proba = model.predict_proba(X_test)
# 评估
print("准确率:", accuracy_score(y_test, y_pred))
print("\n分类报告:")
print(classification_report(y_test, y_pred))
print("\n混淆矩阵:")
print(confusion_matrix(y_test, y_pred))
# 查看特征重要性(系数)
print("\n特征系数:")
for i, coef in enumerate(model.coef_[0]):
print(f"Feature {i+1}: {coef:.4f}")
3. 多分类逻辑回归(Softmax 回归)
import numpy as np
class SoftmaxRegression:
"""Softmax回归(多分类逻辑回归)"""
def __init__(self, learning_rate=0.01, n_iterations=1000):
self.lr = learning_rate
self.n_iter = n_iterations
self.weights = None
self.bias = None
def _softmax(self, z):
"""Softmax函数"""
# 减去最大值防止数值溢出
exp_z = np.exp(z - np.max(z, axis=1, keepdims=True))
return exp_z / np.sum(exp_z, axis=1, keepdims=True)
def fit(self, X, y):
"""训练模型"""
n_samples, n_features = X.shape
n_classes = len(np.unique(y))
# 将标签转换为one-hot编码
y_onehot = np.eye(n_classes)[y]
# 初始化参数
self.weights = np.random.randn(n_features, n_classes) * 0.01
self.bias = np.zeros((1, n_classes))
for i in range(self.n_iter):
# 前向传播
linear_model = np.dot(X, self.weights) + self.bias
y_pred = self._softmax(linear_model)
# 计算梯度
dw = (1 / n_samples) * np.dot(X.T, (y_pred - y_onehot))
db = (1 / n_samples) * np.sum(y_pred - y_onehot, axis=0, keepdims=True)
# 更新参数
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._softmax(linear_model)
return np.argmax(y_pred, axis=1)
# 使用示例
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# 生成三分类数据
X, y = make_classification(n_samples=300, n_features=2, n_redundant=0,
n_informative=2, n_classes=3, n_clusters_per_class=1,
random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 训练Softmax回归
model = SoftmaxRegression(learning_rate=0.1, n_iterations=1000)
model.fit(X_train, y_train)
# 预测并评估
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(f"Softmax回归准确率: {accuracy:.4f}")
八、逻辑回归的优缺点
优点
| 优点 | 说明 |
|---|---|
| 可解释性强 | 系数直接反映特征对结果的影响方向和程度 |
| 计算高效 | 训练和预测速度快,适合大规模数据 |
| 输出概率 | 不仅给出分类结果,还能提供置信度 |
| 不易过拟合 | 模型简单,配合正则化效果更好 |
| 扩展性好 | 容易扩展到多分类问题(Softmax) |
缺点
| 缺点 | 说明 |
|---|---|
| 线性决策边界 | 只能处理线性可分问题,复杂边界需要特征工程 |
| 对异常值敏感 | 异常值会显著影响模型参数 |
| 特征相关性 | 多重共线性会影响系数估计 |
九、应用场景
逻辑回归广泛应用于:
- 医疗诊断:疾病风险评估、肿瘤良恶性判断
- 金融风控:信用评分、欺诈检测
- 营销预测:客户流失预测、购买意愿分析
- 自然语言处理:情感分析、垃圾邮件识别
十、总结
逻辑回归是机器学习中最基础、最重要的分类算法之一:
核心思想:用 Sigmoid 函数将线性输出映射为概率,实现分类预测。
关键点回顾:
| 要点 | 内容 |
|---|---|
| 模型 | \(h_\theta(x) = \sigma(\theta^Tx) = \frac{1}{1+e^{-\theta^Tx}}\) |
| 损失函数 | 交叉熵损失(凸函数,优化友好) |
| 优化方法 | 梯度下降 |
| 扩展 | Softmax 回归处理多分类 |
逻辑回归虽然简单,但它是理解更复杂模型(如神经网络)的重要基础。掌握它,你就迈出了走向深度学习的关键一步。

浙公网安备 33010602011771号