用Python从零实现逻辑回归:以乳腺癌数据集为例,手把手教你构建癌细胞识别模型

在医疗诊断领域,机器学习正发挥着越来越重要的作用。其中,逻辑回归作为一种经典的二分类算法,因其模型简单、可解释性强,常被用于疾病预测和诊断辅助。本文将带您从零开始,用Python完整实现一个基于逻辑回归的乳腺癌识别模型,不仅会展示代码编写过程,更会深入剖析背后的数学原理和工程实践细节。

1. 环境准备与数据理解

在开始编码之前,我们需要准备好开发环境并充分理解数据集。推荐使用Python 3.8+版本,并安装以下依赖库:

pip install numpy pandas matplotlib scikit-learn

乳腺癌数据集包含569个样本,每个样本有30个特征和1个标签。特征包括半径、纹理、周长等细胞核特征,标签为0(良性)或1(恶性)。让我们先加载并探索数据:

from sklearn.datasets import load_breast_cancer
import pandas as pd

# 加载数据集
data = load_breast_cancer()
df = pd.DataFrame(data.data, columns=data.feature_names)
df['target'] = data.target

# 查看数据概况
print(f"数据集形状: {df.shape}")
print(f"特征名称: {data.feature_names}")
print(f"类别分布:\n{df['target'].value_counts()}")

注意:在实际医疗应用中,数据质量至关重要。建议在建模前进行详细的数据质量检查,包括缺失值、异常值等。

2. 逻辑回归的数学基础

逻辑回归虽然名字中有"回归",但它实际上是一种分类算法。其核心是通过sigmoid函数将线性回归的输出映射到(0,1)区间,表示样本属于正类的概率。

sigmoid函数定义: $$ \sigma(z) = \frac{1}{1+e^{-z}} $$

其中$z = w^Tx + b$是线性组合。这个函数的特性是:

  • 当$z \to +\infty$时,$\sigma(z) \to 1$
  • 当$z \to -\infty$时,$\sigma(z) \to 0$
  • 在$z=0$处,$\sigma(z)=0.5$

损失函数采用交叉熵损失: $$ J(w,b) = -\frac{1}{m}\sum_{i=1}^m [y^{(i)}\log(a^{(i)}) + (1-y^{(i)})\log(1-a^{(i)})] $$

其中$a^{(i)} = \sigma(z^{(i)})$是预测概率。

3. 从零实现逻辑回归模型

现在,我们开始实现逻辑回归的核心组件。首先实现sigmoid函数:

import numpy as np

def sigmoid(x):
    """计算sigmoid函数值"""
    return 1 / (1 + np.exp(-x))

# 测试sigmoid函数
x = np.linspace(-10, 10, 100)
y = sigmoid(x)

接下来实现梯度下降算法来优化模型参数:

def compute_gradient(X, y, w, b):
    """
    计算梯度
    :param X: 特征矩阵 (m,n)
    :param y: 标签向量 (m,)
    :param w: 权重向量 (n,)
    :param b: 偏置标量
    :return: dw, db
    """
    m = X.shape[0]
    A = sigmoid(np.dot(X, w) + b)
    dz = A - y
    dw = (1/m) * np.dot(X.T, dz)
    db = (1/m) * np.sum(dz)
    return dw, db

def gradient_descent(X, y, w_init, b_init, learning_rate, num_iters):
    """
    梯度下降算法
    :param X: 特征矩阵
    :param y: 标签向量
    :param w_init: 初始权重
    :param b_init: 初始偏置
    :param learning_rate: 学习率
    :param num_iters: 迭代次数
    :return: w, b, costs
    """
    w = w_init.copy()
    b = b_init
    costs = []
    
    for i in range(num_iters):
        dw, db = compute_gradient(X, y, w, b)
        w -= learning_rate * dw
        b -= learning_rate * db
        
        # 计算损失
        A = sigmoid(np.dot(X, w) + b)
        cost = -np.mean(y * np.log(A) + (1-y) * np.log(1-A))
        costs.append(cost)
        
        if i % 100 == 0:
            print(f"迭代 {i}: 损失 {cost:.4f}")
    
    return w, b, costs

4. 模型训练与评估

现在,我们使用乳腺癌数据集来训练和评估我们的模型。首先需要预处理数据:

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

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

# 特征标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 添加偏置项x0=1
X_train_final = np.c_[np.ones(X_train_scaled.shape[0]), X_train_scaled]
X_test_final = np.c_[np.ones(X_test_scaled.shape[0]), X_test_scaled]

# 初始化参数
w_init = np.zeros(X_train_final.shape[1])
b_init = 0

# 训练模型
w_final, b_final, costs = gradient_descent(
    X_train_final, y_train, w_init, b_init, 
    learning_rate=0.01, num_iters=2000)

训练完成后,我们需要评估模型性能:

def predict(X, w, b, threshold=0.5):
    """预测函数"""
    A = sigmoid(np.dot(X, w) + b)
    return (A >= threshold).astype(int)

# 训练集和测试集预测
y_train_pred = predict(X_train_final, w_final, b_final)
y_test_pred = predict(X_test_final, w_final, b_final)

# 计算准确率
train_accuracy = np.mean(y_train_pred == y_train)
test_accuracy = np.mean(y_test_pred == y_test)

print(f"训练集准确率: {train_accuracy:.4f}")
print(f"测试集准确率: {test_accuracy:.4f}")

5. 模型优化与调试技巧

在实际应用中,我们常常需要调优模型以获得更好的性能。以下是几个关键的优化方向:

学习率选择

  • 学习率过大可能导致震荡甚至发散
  • 学习率过小则收敛缓慢
  • 建议尝试0.001、0.003、0.01、0.03、0.1等值

特征工程

  • 检查特征相关性
  • 考虑特征组合或多项式特征
  • 处理类别不平衡问题

正则化

  • L2正则化可以防止过拟合
  • 修改损失函数为: $$J(w,b) = -\frac{1}{m}\sum_{i=1}^m [y^{(i)}\log(a^{(i)}) + (1-y^{(i)})\log(1-a^{(i)})] + \frac{\lambda}{2m}|w|^2_2$$

实现L2正则化的梯度下降:

def compute_gradient_with_regularization(X, y, w, b, lambda_):
    """带L2正则化的梯度计算"""
    m = X.shape[0]
    A = sigmoid(np.dot(X, w) + b)
    dz = A - y
    dw = (1/m) * np.dot(X.T, dz) + (lambda_/m) * w
    db = (1/m) * np.sum(dz)
    return dw, db

收敛诊断

  • 绘制损失函数曲线
  • 监控训练集和验证集准确率
  • 早停(Early Stopping)策略

6. 模型部署与应用

训练好的模型可以保存并集成到医疗诊断系统中:

import pickle

# 保存模型
model = {
    'weights': w_final,
    'bias': b_final,
    'scaler': scaler
}

with open('breast_cancer_model.pkl', 'wb') as f:
    pickle.dump(model, f)

# 加载模型
with open('breast_cancer_model.pkl', 'rb') as f:
    loaded_model = pickle.load(f)

# 使用模型进行预测
def predict_new_sample(sample, model):
    """预测新样本"""
    sample_scaled = model['scaler'].transform(sample.reshape(1, -1))
    sample_final = np.c_[1, sample_scaled]
    prob = sigmoid(np.dot(sample_final, model['weights']) + model['bias'])
    return '恶性' if prob >= 0.5 else '良性'

在实际部署时,还需要考虑:

  • 模型性能监控
  • 数据漂移检测
  • 模型版本管理
  • 解释性报告生成

更多推荐