共计 3074 个字符,预计需要花费 8 分钟才能阅读完成。
背景介绍:为什么需要逻辑回归
线性回归是机器学习中最基础的算法之一,它通过拟合一条直线来预测连续值。然而,当我们面对分类问题时,线性回归就显得力不从心了。想象一下,如果我们用线性回归来预测一个客户是否会购买产品(0 表示不购买,1 表示购买),模型的预测值可能会超出 [0,1] 的范围,这显然不符合概率的定义。

逻辑回归正是为了解决这类二分类问题而设计的。虽然名字中有 ” 回归 ”,但它实际上是一种分类算法。逻辑回归通过引入 sigmoid 函数,将线性回归的输出映射到 (0,1) 区间,从而可以解释为概率。
核心概念解析
sigmoid 函数的数学奥秘
sigmoid 函数是逻辑回归的核心,其数学表达式为:
σ(z) = 1 / (1 + e^-z)
这个函数的特性非常有趣:
- 它将任何实数输入映射到 (0,1) 区间
- 当 z = 0 时,σ(z)=0.5
- 函数在 z = 0 附近变化最快,两端逐渐平缓
- 导数可以用 σ(z)(1-σ(z))表示,这个性质在梯度下降中非常有用
最大似然估计:寻找最佳参数
逻辑回归使用最大似然估计 (MLE) 来确定模型参数。其基本思想是:找到一组参数,使得在这组参数下,观察到当前数据的概率最大。对于二分类问题,我们可以写出似然函数:
L(θ) = ∏[p(x_i)^y_i * (1-p(x_i))^(1-y_i)]
为了简化计算,我们通常取对数得到对数似然函数:
l(θ) = Σ[y_ilog(p(x_i)) + (1-y_i)log(1-p(x_i))]
我们的目标就是最大化这个对数似然函数。
实现对比:手动实现 vs scikit-learn
手动实现梯度下降
让我们先看看如何从零开始实现逻辑回归。以下是用 Python 实现的关键步骤:
- 定义 sigmoid 函数
- 计算损失函数(负对数似然)
- 实现梯度计算
- 执行梯度下降更新参数
import numpy as np
class LogisticRegression:
def __init__(self, learning_rate=0.01, n_iterations=1000):
self.learning_rate = learning_rate
self.n_iterations = n_iterations
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_iterations):
linear_model = np.dot(X, self.weights) + self.bias
predictions = self._sigmoid(linear_model)
# 计算梯度
dw = (1/n_samples) * np.dot(X.T, (predictions - y))
db = (1/n_samples) * np.sum(predictions - y)
# 更新参数
self.weights -= self.learning_rate * dw
self.bias -= self.learning_rate * db
def predict(self, X):
linear_model = np.dot(X, self.weights) + self.bias
y_pred = self._sigmoid(linear_model)
return [1 if i > 0.5 else 0 for i in y_pred]
使用 scikit-learn 实现
相比之下,使用 scikit-learn 实现逻辑回归就简单多了:
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# 数据预处理
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 划分训练测试集
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2)
# 创建并训练模型
model = LogisticRegression()
model.fit(X_train, y_train)
# 评估模型
accuracy = model.score(X_test, y_test)
print(f"模型准确率: {accuracy:.2f}")
性能考量与优化
学习率选择
学习率是梯度下降中最重要的超参数之一。学习率太大会导致无法收敛,太小则训练速度过慢。通常的做法是:
- 从 0.01 开始尝试
- 观察损失函数下降曲线
- 如果损失震荡,降低学习率
- 如果下降太慢,适当增加学习率
特征缩放的重要性
逻辑回归虽然不像 KNN 或 SVM 那样对特征尺度敏感,但进行特征缩放仍然有助于:
- 加速梯度下降收敛
- 提高数值稳定性
- 使正则化对各个特征公平
常用的缩放方法包括标准化 (StandardScaler) 和归一化(MinMaxScaler)。
预防过拟合
逻辑回归容易在小样本高维数据上过拟合。预防方法包括:
- 使用 L1 或 L2 正则化
- 增加训练数据量
- 特征选择减少维度
- 早停法(Early Stopping)
在 scikit-learn 中,可以通过设置 penalty 参数来添加正则化:
# L1 正则化
model = LogisticRegression(penalty='l1', solver='liblinear')
# L2 正则化(默认)model = LogisticRegression(penalty='l2')
避坑指南
数据未标准化
这是初学者常犯的错误。未标准化的数据会导致:
- 梯度下降收敛缓慢
- 数值不稳定
- 模型性能下降
解决方案:始终在训练前进行特征缩放。
类别不平衡问题
当正负样本比例严重失衡时(如 99:1),模型可能会倾向于预测多数类。解决方法包括:
- 使用 class_weight 参数调整类别权重
- 采用过采样或欠采样技术
- 使用不同的评估指标(如 F1-score, ROC-AUC)
# 调整类别权重
model = LogisticRegression(class_weight='balanced')
延伸思考:多分类问题
虽然逻辑回归本质上是二分类算法,但可以通过以下方式扩展到多分类:
- OvR(One-vs-Rest):为每个类别训练一个二分类器
- OvO(One-vs-One):为每对类别训练一个分类器
- Multinomial Logistic Regression:直接使用 softmax 函数
scikit-learn 默认使用 OvR 策略:
# 多分类逻辑回归
model = LogisticRegression(multi_class='ovr')
# 或
model = LogisticRegression(multi_class='multinomial', solver='lbfgs')
总结
逻辑回归虽然简单,但在实际应用中非常强大。理解其背后的数学原理对于调优和问题诊断至关重要。通过本文,你应该已经掌握了:
- sigmoid 函数如何将线性回归转换为分类模型
- 最大似然估计的原理和实现
- 手动实现和使用库的区别
- 性能优化的关键点
- 常见问题的解决方案
逻辑回归是学习更复杂模型的基础,深入理解它将对你的机器学习之旅大有裨益。在实际项目中,建议先尝试简单的逻辑回归作为基线模型,再考虑更复杂的算法。
