逻辑回归实战:用Python从零构建一个二分类模型(附完整代码)

在机器学习领域,逻辑回归堪称是"第一课"般的存在。这个看似简单的算法,却在实际业务场景中展现出惊人的实用性。不同于深度学习模型的黑箱特性,逻辑回归以其高度可解释性计算效率,成为金融风控、医疗诊断、营销响应等领域的常青树算法。本文将带您从零开始,用Python完整实现一个逻辑回归分类器,不仅包含代码实现,更会深入剖析每个步骤背后的数学原理和工程考量。

1. 环境准备与数据生成

在开始建模之前,我们需要搭建合适的开发环境。推荐使用Python 3.8+版本,并安装以下核心库:

pip install numpy pandas scikit-learn matplotlib seaborn

为什么选择这些库? NumPy提供高效的数值计算,pandas用于数据整理,scikit-learn包含现成的机器学习实现,而matplotlib和seaborn则是可视化利器。对于初学者,建议使用Jupyter Notebook进行交互式开发。

接下来,我们生成模拟的二分类数据集:

from sklearn.datasets import make_classification
import pandas as pd

# 生成1000个样本,每个样本20个特征
X, y = make_classification(
    n_samples=1000,
    n_features=20,
    n_informative=15,  # 实际有预测能力的特征
    n_redundant=3,     # 冗余特征(与其他特征线性相关)
    n_classes=2,
    random_state=42
)

# 转换为DataFrame便于查看
df = pd.DataFrame(X, columns=[f"feature_{i}" for i in range(X.shape[1])])
df["target"] = y
print(df.head())

生成数据时需要注意几个关键参数:

  • n_informative:真正影响分类结果的特征数量
  • n_redundant:与其他特征线性相关的冗余特征
  • flip_y:可以添加随机噪声的比例

提示:在实际项目中,数据探索(EDA)应占整个流程60%以上的时间。建议使用df.describe()seaborn.pairplot()对数据分布进行可视化分析。

2. 模型构建与数学原理

逻辑回归的核心在于将线性回归的输出通过Sigmoid函数映射到[0,1]区间。让我们用NumPy手动实现这一过程:

import numpy as np

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

class LogisticRegression:
    def __init__(self, learning_rate=0.01, n_iter=1000):
        self.lr = learning_rate
        self.n_iter = n_iter
        self.weights = None
        self.bias = None
    
    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_iter):
            linear_pred = np.dot(X, self.weights) + self.bias
            predictions = sigmoid(linear_pred)
            
            # 计算梯度
            dw = (1/n_samples) * np.dot(X.T, (predictions - y))
            db = (1/n_samples) * np.sum(predictions - y)
            
            # 更新参数
            self.weights -= self.lr * dw
            self.bias -= self.lr * db
    
    def predict(self, X, threshold=0.5):
        linear_pred = np.dot(X, self.weights) + self.bias
        y_pred = sigmoid(linear_pred)
        return [1 if i > threshold else 0 for i in y_pred]

关键数学原理体现在梯度计算环节:

  • 交叉熵损失函数:$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}$

注意:学习率(learning_rate)的选择至关重要。过大可能导致震荡,过小则收敛缓慢。建议从0.01开始尝试。

3. 模型训练与评估

将数据集划分为训练集和测试集是避免过拟合的关键步骤:

from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report

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_iter=1000)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)

# 评估指标
print(f"Accuracy: {accuracy_score(y_test, y_pred):.2f}")
print("\nConfusion Matrix:")
print(confusion_matrix(y_test, y_pred))
print("\nClassification Report:")
print(classification_report(y_test, y_pred))

对于分类问题,准确率(Accuracy)只是最基础的指标。我们更应该关注:

  • 精确率(Precision):预测为正的样本中实际为正的比例
  • 召回率(Recall):实际为正的样本中被正确预测的比例
  • F1分数:精确率和召回率的调和平均

当类别不平衡时(如欺诈检测中正样本极少),建议使用PR曲线而非ROC曲线评估模型。

4. 使用scikit-learn优化实现

虽然手动实现有助于理解原理,但在实际项目中我们更推荐使用scikit-learn的优化实现:

from sklearn.linear_model import LogisticRegression as SklearnLogReg
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline

# 创建包含标准化的管道
pipe = make_pipeline(
    StandardScaler(),
    SklearnLogReg(penalty='l2', C=1.0, solver='lbfgs', max_iter=1000)
)

pipe.fit(X_train, y_train)
y_pred_sk = pipe.predict(X_test)

print(f"Sklearn Accuracy: {accuracy_score(y_test, y_pred_sk):.2f}")

scikit-learn提供了更多高级功能:

  • 正则化:通过penalty参数选择L1或L2正则化
  • 多分类:设置multi_class='multinomial'支持多类别
  • 不同求解器:对小数据集使用lbfgs,大数据集用sagsaga

参数调优示例:

from sklearn.model_selection import GridSearchCV

param_grid = {
    'logisticregression__C': [0.001, 0.01, 0.1, 1, 10, 100],
    'logisticregression__penalty': ['l1', 'l2'],
    'logisticregression__solver': ['liblinear', 'saga']
}

grid = GridSearchCV(pipe, param_grid, cv=5, scoring='accuracy')
grid.fit(X_train, y_train)

print(f"Best params: {grid.best_params_}")
print(f"Best CV accuracy: {grid.best_score_:.2f}")

5. 模型解释与业务应用

逻辑回归最大的优势在于其可解释性。我们可以分析每个特征对预测结果的贡献:

# 获取特征重要性
feature_importance = pd.DataFrame({
    'feature': [f"feature_{i}" for i in range(X.shape[1])],
    'coefficient': pipe.named_steps['logisticregression'].coef_[0]
}).sort_values('coefficient', ascending=False)

import matplotlib.pyplot as plt

plt.figure(figsize=(10, 6))
plt.barh(feature_importance['feature'], feature_importance['coefficient'])
plt.title('Feature Importance')
plt.show()

在实际业务中,逻辑回归模型可以输出概率而不仅是类别预测。例如在信贷审批中,我们可以设置不同的阈值:

  • 高风险客户:概率>0.8直接拒绝
  • 中等风险:0.4-0.8需要人工审核
  • 低风险:<0.4自动通过

这种灵活的应用方式使逻辑回归成为业务规则与机器学习之间的完美桥梁。

6. 常见问题与进阶技巧

在项目实践中,有几个关键问题需要特别注意:

类别不平衡处理

# 在类权重中考虑样本不平衡
model = SklearnLogReg(class_weight='balanced')

特征工程技巧

  • 对连续特征进行分箱(binning)
  • 创建有业务意义的交叉特征
  • 对高度相关的特征进行PCA降维

模型部署优化

# 导出模型系数用于其他语言部署
coef = pipe.named_steps['logisticregression'].coef_
intercept = pipe.named_steps['logisticregression'].intercept_

# 在C++中实现预测
def cpp_predict(features):
    z = intercept[0]
    for i in range(len(features)):
        z += features[i] * coef[0][i]
    return 1 / (1 + exp(-z))

逻辑回归虽然简单,但在特征工程到位的情况下,其性能往往能媲美更复杂的模型。我曾在一个电商用户流失预测项目中,经过精心特征工程后,逻辑回归的AUC达到0.92,远高于随机森林和XGBoost的初期表现。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐