逻辑回归在医疗数据分析中的应用:从威斯康星乳腺癌数据集看模型构建与解释

📅 2026/8/3 12:57:27 👁️ 阅读次数 📝 编程学习
逻辑回归在医疗数据分析中的应用:从威斯康星乳腺癌数据集看模型构建与解释

1. 从一次误诊说起:为什么逻辑回归是医疗数据分析的“第一道防线”

几年前,我参与过一个医疗数据分析的辅助项目,当时团队拿到了一批初步的病理指标数据,目标是快速筛选出高风险人群。有人提议直接用当时很火的复杂模型,比如随机森林甚至神经网络。但项目负责人,一位经验丰富的临床研究员,坚持让我们先跑一遍逻辑回归。结果呢?逻辑回归模型用几个核心指标(比如细胞核的均匀度、纹理)构建的简单线性边界,其初步筛查的准确率就达到了92%,并且每个特征对结果的影响一目了然——比如“最坏周长”这个指标,系数为正且显著,直接告诉我们这个值越大,恶性风险越高。这个清晰、可解释的结果,为后续更深入的检查和复杂模型分析提供了坚实、可信的基线。最终,那个花里胡哨的神经网络模型,准确率只提升了不到2个百分点,但解释成本却高了好几个数量级。

这个故事我想说明的是,在机器学习的实战中,尤其是在像医疗诊断这样要求高可靠性、高可解释性的领域,逻辑回归远不是一个“过时”或“简单”的算法。它是你探索数据、建立认知、构建可靠基线模型的瑞士军刀。而威斯康星州乳腺癌数据集(Wisconsin Diagnostic Breast Cancer, WDBC)正是练习这把“军刀”的绝佳“磨刀石”。这个数据集包含了569个样本,每个样本有30个从乳腺肿块的数字化图像中计算出的特征(如半径、纹理、周长、面积、光滑度等),目标变量是诊断结果:恶性(M)或良性(B)。

今天,我就以这个经典数据集为例,带你完整走一遍逻辑回归模型从数据理解、预处理、建模、评估到解释的全过程。你会发现,即便是一个“入门级”算法,要把它用对、用好、用到生产级别的可靠,里面需要注意的细节和坑一点也不少。我们不止要得到预测结果,更要理解数据如何说话,模型为何做出某个判断,这才是数据科学的核心价值。

2. 数据初探与核心特征工程:不只是加载数据那么简单

拿到任何数据集,尤其是像医疗数据这样敏感且重要的数据,直接丢进模型fit是最大的忌讳。你的模型效果不好,很可能第一步就出了问题。

2.1 理解数据字典:每个数字背后的医学意义

首先,我们得知道自己在处理什么。WDBC数据集的30个特征并非天书,它们都有明确的物理和医学意义。这30个特征实际上是10个核心特征的三种统计量:均值(mean)、标准差(standard error)和最差值(worst)。

这10个核心特征是:

  1. 半径(radius):从中心到周边点的平均距离。
  2. 纹理(texture):灰度值的标准偏差,可以理解为图像灰度变化的不均匀程度。
  3. 周长(perimeter):细胞核的周长。
  4. 面积(area):细胞核的面积。
  5. 光滑度(smoothness):半径长度的局部变化,值越小表面越光滑。
  6. 紧密度(compactness):计算公式为周长^2 / 面积 - 1.0,衡量形状的紧凑程度。
  7. 凹度(concavity):轮廓凹陷部分的严重程度。
  8. 凹点(concave points):轮廓上凹陷点的数量。
  9. 对称性(symmetry):细胞核的对称程度。
  10. 分形维数(fractal dimension):海岸线近似度,描述轮廓的复杂程度。“最差值”指的是这10个特征在所有细胞核中最大的那个值,通常对恶性肿瘤的指示性更强。

理解这一点至关重要。它意味着我们的30维特征之间存在天然的分组和多重共线性。例如,radius_mean,radius_se,radius_worst这三个特征都描述半径,只是统计角度不同。直接使用所有特征,可能会让模型陷入冗余信息的干扰,并增加过拟合的风险。

2.2 数据清洗与异常值处理:信任,但要验证

尽管WDBC是一个清洗过的经典数据集,但我们依然要养成数据质量检查的习惯。

import pandas as pd import numpy as np from sklearn.datasets import load_breast_cancer # 加载数据 data = load_breast_cancer() df = pd.DataFrame(data.data, columns=data.feature_names) df['target'] = data.target # 0: 恶性(M), 1: 良性(B) # 1. 检查缺失值 print("缺失值统计:") print(df.isnull().sum()) # 2. 检查基本统计信息,关注异常值 print("\n数值型特征描述(重点关注min, max, 50%):") print(df.describe().loc[['min', '50%', 'max']].T.head(15))

在我的经验中,即使数据源声称“无缺失”,用.isnull().sum()快速扫一遍也是成本极低的好习惯。对于数值型特征,重点关注描述性统计中的最小值和最大值。例如,如果某个特征的max值比其他值高出好几个数量级,它可能是一个输入错误或极端异常值。对于逻辑回归这种基于距离(通过sigmoid函数转换)的模型,异常值会对系数估计产生不小的拉扯效应。

对于这个数据集,通常没有明显需要剔除的异常值。但我们可以通过箱线图或3σ原则进行筛查。一个更稳健的做法是使用中位数和四分位距(IQR)进行盖帽处理(Winsorization),而不是直接删除,以保留数据完整性。

# 示例:使用IQR方法检测并处理极端异常值(可选,本例中通常不需要) def cap_outliers(series): Q1 = series.quantile(0.25) Q3 = series.quantile(0.75) IQR = Q3 - Q1 lower_bound = Q1 - 3 * IQR # 使用3倍IQR,标准更宽松 upper_bound = Q3 + 3 * IQR return series.clip(lower_bound, upper_bound) # 对数值特征进行盖帽处理(谨慎使用,需结合业务理解) # for col in df.columns[:-1]: # 不对target列操作 # df[col] = cap_outliers(df[col])

注意:在医疗数据中,盲目处理“异常值”是危险的。一个特别大的“最坏面积”值,很可能就是一个非常危险的恶性肿瘤指征,而不是数据错误。因此,处理前必须结合领域知识,或与领域专家确认。在缺乏先验知识时,更安全的做法是保留它们,并在模型评估时关注其影响。

2.3 特征缩放:为什么逻辑回归也需要它?

这是一个常见的误区:逻辑回归不是基于距离的模型吗?为什么需要特征缩放?逻辑回归的损失函数(如对数损失)本身不受特征尺度影响。但是,其优化算法(如梯度下降)的收敛速度和效果会受到特征尺度的巨大影响。

想象一下,area_worst(面积最差值)的范围可能在100-2500之间,而smoothness_worst(光滑度最差值)的范围在0.05-0.25之间。如果不进行缩放,优化算法在更新权重时,area_worst的微小变化就会对损失函数产生巨大影响,导致优化路径震荡,难以找到最优解。同时,正则化项(如L1/L2)也是对系数本身进行惩罚,如果特征尺度不一,惩罚就会不公平,大尺度的特征会“被迫”拥有小系数,这扭曲了我们希望特征选择或收缩的本意。

因此,我们几乎总是需要对特征进行标准化(StandardScaler)或归一化(MinMaxScaler)。对于逻辑回归,标准化(使均值为0,方差为1)通常是首选,因为它能更好地处理可能存在的异常值,并且产生的系数可以近似解释为特征重要性(在标准化后,系数大小可直接比较)。

from sklearn.preprocessing import StandardScaler # 分离特征和目标 X = df.drop('target', axis=1) y = df['target'] # 初始化标准化器,并在训练集上拟合 scaler = StandardScaler() X_scaled = scaler.fit_transform(X) # 注意:在实际中,应先分割训练测试集再fit!

这里埋了一个关键的坑:上面代码中,我使用了fit_transform(X),这是错误的示范。在实际项目中,我们必须先划分训练集和测试集,然后只用训练集的数据来fit标准化器,再用这个拟合好的转换器去转换训练集和测试集。否则,我们就“数据泄露”了测试集的信息到训练过程中,会导致模型评估结果过于乐观。正确的做法我们会在下一节详述。

3. 模型构建的核心:划分、训练与正则化的艺术

数据准备好了,现在进入核心环节。这里每一步的选择都直接影响模型的最终性能和可靠性。

3.1 数据分割:防止信息泄露的铁律

这是建模过程中最容易犯错,也最致命的一步。我们必须模拟真实场景:模型只能从“过去”(训练集)学习,然后去预测“未来”(测试集)。任何让测试集信息“污染”训练过程的行为,都叫数据泄露。

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, stratify=y) # 第二步:在训练集上拟合标准化器,并转换训练集和测试集 scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) # fit 只在训练集上! X_test_scaled = scaler.transform(X_test) # transform 用于测试集

注意两个关键参数:

  • random_state=42:设置随机种子,保证每次运行分割结果一致,这对于结果复现至关重要。
  • stratify=y:这是分类任务中极其重要的参数。它确保训练集和测试集中,恶性与良性样本的比例与原始数据集保持一致。乳腺癌数据中恶性样本约占37%,如果不分层抽样,有可能某个集合中恶性样本比例畸高或畸低,导致评估偏差。

3.2 逻辑回归的“内核”:从概率到决策

逻辑回归的本质是用线性回归的输出去拟合对数几率(Logit)。公式如下:

logit(p) = log(p / (1-p)) = w0 + w1*x1 + ... + wn*xn

其中p是样本属于正类(例如恶性)的概率。通过sigmoid函数σ(z) = 1 / (1 + e^{-z}),我们可以将线性组合z映射到(0, 1)的概率区间。

scikit-learn中,我们使用LogisticRegression。但直接model = LogisticRegression()然后fit,你可能得不到最优解,甚至可能得到警告。

from sklearn.linear_model import LogisticRegression # 基础模型 model_basic = LogisticRegression(random_state=42) model_basic.fit(X_train_scaled, y_train) print(f"基础模型训练集准确率:{model_basic.score(X_train_scaled, y_train):.4f}") print(f"基础模型测试集准确率:{model_basic.score(X_test_scaled, y_test):.4f}")

运行后你可能会发现两个现象:1. 训练集准确率可能非常高(接近100%)。2. 控制台可能会输出一个ConvergenceWarning,提示算法没有完全收敛。

第一个现象提示我们可能过拟合了——模型在训练集上表现太好,学习了过多的噪声和细节。第二个现象是因为默认的最大迭代次数(max_iter=100)可能不够。但盲目增加迭代次数治标不治本,我们需要引入模型泛化的守护神:正则化

3.3 正则化:控制模型复杂度的“刹车”

逻辑回归默认使用L2正则化(参数penalty='l2')。正则化通过在损失函数中添加一个惩罚项(C * 损失函数 + 惩罚项)来限制系数的大小,防止它们变得过大,从而降低模型复杂度,缓解过拟合。

这里有一个关键且容易混淆的点:参数CC是正则化强度的倒数C值越小,正则化力度越强,系数会被压缩得越接近零。C值越大,正则化力度越弱,模型更倾向于拟合训练数据。

# 尝试不同的正则化强度C for C_value in [100, 1, 0.01, 0.001]: model = LogisticRegression(C=C_value, max_iter=5000, random_state=42) model.fit(X_train_scaled, y_train) train_acc = model.score(X_train_scaled, y_train) test_acc = model.score(X_test_scaled, y_test) print(f"C={C_value:<7} | 训练准确率:{train_acc:.4f} | 测试准确率:{test_acc:.4f} | 系数平均绝对值:{np.mean(np.abs(model.coef_)):.4f}")

你会观察到,当C很大(如100)时,训练集准确率很高,但测试集准确率可能略低,且系数绝对值较大。当C很小(如0.001)时,训练集和测试集准确率都下降,系数被严重压缩。我们需要找到一个平衡点,通常C=1附近是一个不错的起点。

此外,我们还可以使用L1正则化(penalty='l1')。L1正则化倾向于产生稀疏解,即把许多不重要的特征的系数直接压缩为0,从而实现特征选择。这在特征维度高、存在大量冗余时非常有用。

# 使用L1正则化,并观察系数稀疏性 model_l1 = LogisticRegression(penalty='l1', C=0.1, solver='saga', max_iter=5000, random_state=42) # 注意solver需支持l1 model_l1.fit(X_train_scaled, y_train) # 统计非零系数的数量 non_zero_coef = np.sum(model_l1.coef_ != 0) print(f"L1正则化后,非零系数数量:{non_zero_coef} / {X_train_scaled.shape[1]}")

实操心得:在实际项目中,我通常会用一个网格搜索(GridSearchCV)来系统性地寻找最优的Cpenalty参数。但在此之前,手动尝试几个数量级不同的C值,观察模型在训练集和验证集上表现的变化趋势,能帮你快速建立直觉,理解正则化是如何起作用的。记住,我们的目标不是训练集上的完美分数,而是测试集上稳定且可泛化的性能。

4. 超越准确率:全面评估模型性能

模型训练好了,准确率(Accuracy)有95%,是不是就大功告成了?远远不是。在像医疗诊断这样的不平衡或代价敏感的场景中,准确率是一个具有严重误导性的指标。

4.1 混淆矩阵:一切评估的基石

假设我们有100个样本,其中90个良性,10个恶性。如果一个模型把所有样本都预测为良性,它的准确率是90%!但这对于那10个恶性患者来说是灾难性的。因此,我们必须看混淆矩阵。

from sklearn.metrics import confusion_matrix, classification_report, ConfusionMatrixDisplay import matplotlib.pyplot as plt # 使用最佳模型进行预测 best_model = LogisticRegression(C=1, max_iter=5000, random_state=42) best_model.fit(X_train_scaled, y_train) y_pred = best_model.predict(X_test_scaled) # 计算混淆矩阵 cm = confusion_matrix(y_test, y_pred) disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=['恶性 (M)', '良性 (B)']) disp.plot(cmap='Blues') plt.title('逻辑回归模型混淆矩阵') plt.show()

混淆矩阵会给出四个值:

  • 真阴性(TN):实际是良性,预测也是良性。
  • 假阳性(FP):实际是良性,预测为恶性(误报)。
  • 假阴性(FN):实际是恶性,预测为良性(漏报)。
  • 真阳性(TP):实际是恶性,预测也是恶性。

对于癌症诊断,假阴性(FN)的代价远高于假阳性(FP)。漏诊一个癌症患者(FN)可能导致延误治疗,后果严重。而误诊为癌症(FP)虽然会给患者带来不必要的心理压力和后续检查,但仍有纠正的机会。因此,我们的模型应该优先保证高的召回率(Recall)

4.2 关键指标:精确率、召回率与F1分数

  • 精确率(Precision):在所有预测为恶性的样本中,真正是恶性的比例。Precision = TP / (TP + FP)。它衡量的是“预测的准不准”。
  • 召回率(Recall/ Sensitivity):在所有实际为恶性的样本中,被模型正确预测出来的比例。Recall = TP / (TP + FN)。它衡量的是“查的全不全”。
  • F1分数(F1-Score):精确率和召回率的调和平均数。F1 = 2 * (Precision * Recall) / (Precision + Recall)。它是一个综合平衡的指标。

我们可以通过classification_report来全面查看:

print(classification_report(y_test, y_pred, target_names=['恶性 (M)', '良性 (B)']))

报告会分别给出恶性类和良性类的精确率、召回率、F1分数以及支持数(样本数量)。请重点关注“恶性”类别的召回率。一个优秀的癌症筛查模型,恶性类别的召回率应力争接近100%,即使这可能会略微降低精确率(即增加一些假阳性)。

4.3 ROC曲线与AUC:衡量模型整体排序能力

有时,我们不一定直接使用0.5作为分类阈值。比如,为了进一步提高召回率(减少漏诊),我们可以降低阈值,例如将预测概率大于0.3就判为恶性。ROC曲线描绘了当分类阈值从1到0变化时,真正例率(TPR,即召回率)假正例率(FPR)的变化情况。

from sklearn.metrics import roc_curve, auc y_pred_proba = best_model.predict_proba(X_test_scaled)[:, 1] # 获取预测为恶性的概率 fpr, tpr, thresholds = roc_curve(y_test, y_pred_proba) roc_auc = auc(fpr, tpr) plt.figure() plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC曲线 (AUC = {roc_auc:.2f})') plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--', label='随机猜测') plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.05]) plt.xlabel('假正例率 (FPR)') plt.ylabel('真正例率 (TPR)') plt.title('乳腺癌分类逻辑回归模型ROC曲线') plt.legend(loc="lower right") plt.show()

AUC(曲线下面积)值越接近1,说明模型整体区分能力越好。AUC=0.5相当于随机猜测。一个好的模型AUC通常在0.8以上。ROC-AUC的优势在于它不受类别不平衡和具体阈值选择的影响,给出了一个模型整体性能的概括。

经验之谈:在项目报告中,不要只展示一个准确率数字。必须呈现混淆矩阵、分类报告(尤其是召回率)和ROC-AUC。向业务方(比如医生)解释时,可以这样说:“我们的模型在测试集上整体准确率是XX%,但更重要的是,对于真正的恶性肿瘤,它能识别出其中YY%(召回率)。这意味着每100个癌症患者,它能成功预警YY个。当然,这也会带来约ZZ%的健康人被误判为高风险(假阳性率),需要进一步检查确认。”这样的表述既专业又务实。

5. 模型解释与特征洞察:打开“黑箱”

逻辑回归最大的优势之一就是可解释性。我们可以通过模型的系数来理解每个特征对预测结果的贡献方向和大小。

5.1 解读系数:影响力与方向

在特征经过标准化之后,系数的绝对值大小可以近似衡量特征的重要性,系数的正负号表示影响方向。

# 获取特征名和系数 feature_names = X.columns coefficients = best_model.coef_[0] # 因为我们是二分类,coef_形状为(1, n_features) # 创建系数DataFrame并按绝对值排序 coef_df = pd.DataFrame({'特征': feature_names, '系数': coefficients}) coef_df['系数绝对值'] = np.abs(coef_df['系数']) coef_df = coef_df.sort_values(by='系数绝对值', ascending=False) print("特征影响力排序(标准化后数据):") print(coef_df.head(10))

输出结果可能会显示,例如worst perimeter(最坏周长)、worst radius(最坏半径)、worst area(最坏面积)等“最坏”系列的系数绝对值最大且为正。这完全符合医学直觉:这些指标的值越大,细胞核形态越不规则、越大,是恶性肿瘤的强指征。而像fractal dimension se(分形维数标准误)等特征系数可能很小,说明其影响力较弱。

5.2 从系数到实际影响:几率比(Odds Ratio)

对于业务人员来说,“系数增加0.5”可能难以理解。我们可以将其转换为几率比,这提供了更直观的解释。

几率比 =exp(系数)它表示:在其他特征不变的情况下,该特征增加一个单位(由于我们标准化了,这里的一个单位是1个标准差),样本被诊断为恶性的几率(Odds)变为原来的多少倍。

# 计算几率比 coef_df['几率比 (exp(系数))'] = np.exp(coef_df['系数']) print("\n特征几率比(标准化后,增加1个标准差的影响):") print(coef_df[['特征', '系数', '几率比 (exp(系数))']].head(10))

假设worst perimeter的系数是2.5,那么其几率比exp(2.5) ≈ 12.2。这意味着,在最坏周长这个特征上,数值每增加一个标准差,该样本是恶性的几率就变为原来的约12.2倍。这种解释方式对临床医生或决策者来说非常有力且直观。

5.3 处理多重共线性:VIF与特征筛选

前面提到,我们的特征存在分组共线性。这会导致系数估计不稳定,标准误增大,解释变得困难。我们可以使用方差膨胀因子(VIF)来诊断。

from statsmodels.stats.outliers_influence import variance_inflation_factor from statsmodels.tools.tools import add_constant # 计算VIF需要常数项 X_with_const = add_constant(pd.DataFrame(X_train_scaled, columns=feature_names)) vif_data = pd.DataFrame() vif_data["特征"] = X_with_const.columns vif_data["VIF"] = [variance_inflation_factor(X_with_const.values, i) for i in range(X_with_const.shape[1])] vif_data = vif_data[vif_data['特征'] != 'const'] # 排除常数项 vif_data = vif_data.sort_values(by='VIF', ascending=False) print("\n特征方差膨胀因子(VIF):") print(vif_data.head(15))

通常,VIF大于5或10(阈值可调整)就认为存在较严重的多重共线性。你会发现,同一组的特征(如radius_mean,radius_se,radius_worst)VIF值会非常高。

怎么办?

  1. 领域知识筛选:根据医学意义,优先保留“最坏(worst)”系列特征,因为它们通常包含最具判别力的信息。可以尝试只使用10个“最坏”特征来建模,对比效果。
  2. 主成分分析(PCA):对高度相关的特征组进行PCA降维,用主成分作为新特征。但这会损失可解释性。
  3. 正则化:L1正则化(Lasso)本身可以起到特征选择的作用,将冗余特征的系数压缩至0。这是我们之前已经尝试过的方法。
  4. 手动剔除:计算特征间的相关系数矩阵,从高相关性的特征对中,根据业务理解或模型性能剔除一个。

在我的实践中,对于这个数据集,“最坏特征+L1正则化”的组合是一个很好的起点。它既利用了最具判别力的信息,又通过稀疏化自动处理了组内冗余。

6. 实战中的进阶考量与部署准备

一个能在Jupyter Notebook里跑通的模型,离真正可用还有距离。以下是几个必须考虑的进阶问题。

6.1 类别不平衡处理:我们真的需要它吗?

乳腺癌数据集的类别并不算严重不平衡(恶性:良性 ≈ 37:63)。对于轻度不平衡的数据,逻辑回归本身通常能处理得很好。但如果你发现模型对少数类(恶性)的召回率始终很低,可以考虑以下方法:

  • 调整类别权重LogisticRegression有一个class_weight参数。设置为'balanced'后,算法会自动根据类别频率调整损失函数中的权重,让模型更关注少数类。
    model_balanced = LogisticRegression(C=1, class_weight='balanced', max_iter=5000, random_state=42) model_balanced.fit(X_train_scaled, y_train) # 重新评估,重点关注恶性类别的召回率是否提升
  • 调整决策阈值:默认阈值是0.5。我们可以通过ROC曲线或精确率-召回率曲线,选择一个能提高召回率的更低阈值(如0.3)。
    from sklearn.metrics import precision_recall_curve precision, recall, thresholds = precision_recall_curve(y_test, y_pred_proba) # 寻找满足最低召回率要求(如0.95)下的阈值 target_recall = 0.95 idx = np.argmax(recall >= target_recall) custom_threshold = thresholds[idx] print(f"要达到{target_recall:.2f}的召回率,建议阈值设为:{custom_threshold:.3f}") # 使用新阈值进行预测 y_pred_custom = (y_pred_proba >= custom_threshold).astype(int)

踩坑提醒:不要盲目使用过采样技术(如SMOTE)来处理像医疗数据这样的轻度不平衡。过采样会人为制造数据,可能引入噪声并扭曲特征分布的真实性,在严谨的医疗场景下需要格外谨慎。优先尝试调整类别权重或决策阈值这些更“安全”的方法。

6.2 模型持久化与部署:从Notebook到生产

模型训练和评估完成后,我们需要将其保存下来,以便在新的数据上使用,而无需重新训练。

import joblib # 或使用 pickle # 保存模型和标准化器 model_pipeline = { 'scaler': scaler, 'model': best_model } joblib.dump(model_pipeline, 'breast_cancer_lr_pipeline.pkl') # 加载并使用 loaded_pipeline = joblib.load('breast_cancer_lr_pipeline.pkl') new_scaler = loaded_pipeline['scaler'] new_model = loaded_pipeline['model'] # 假设有一条新数据(需要是原始30维特征) new_data = np.array([[...]]) # 你的新数据 new_data_scaled = new_scaler.transform(new_data.reshape(1, -1)) prediction = new_model.predict(new_data_scaled) prediction_proba = new_model.predict_proba(new_data_scaled) print(f"预测类别:{prediction[0]} (0:恶性, 1:良性)") print(f"预测概率:[恶性概率:{prediction_proba[0][0]:.3f}, 良性概率:{prediction_proba[0][1]:.3f}]")

关键点:必须将标准化器(scaler)和模型一起保存和加载!因为新来的数据必须用与训练数据完全相同的方式进行预处理,否则预测结果将毫无意义。

6.3 持续验证与监控:模型不是一劳永逸的

模型部署上线后,工作并未结束。必须建立监控机制:

  1. 预测分布监控:定期检查模型对新数据预测结果的概率分布是否与训练时相似。如果出现大幅偏移(如预测概率普遍变得非常接近0或1),可能意味着数据分布发生了变化。
  2. 性能衰减监控:如果可能,收集新数据的真实标签,定期(如每月)计算模型在新数据上的准确率、召回率等指标,观察是否有下降。
  3. 特征稳定性监控:监控输入特征的统计特性(如均值、标准差、缺失率)是否稳定。

机器学习模型是建立在“历史数据分布代表未来数据分布”的假设上的。在医疗领域,检测设备更新、诊断标准变化、人群特征变迁都可能打破这个假设。因此,模型的定期回顾、重新评估乃至再训练,是保证其长期有效性的必要环节。

通过以上六个部分的拆解,我们从数据的一个细胞核特征开始,逐步构建、评估、解释并思考了一个逻辑回归模型的完整生命周期。它不仅仅是一个from sklearn.linear_model import LogisticRegression的简单调用,而是一套结合了统计学思想、领域知识和工程实践的完整方法论。掌握这套方法,你就能让逻辑回归这个“古老”而强大的工具,在包括医疗在内的众多关键领域,持续、可靠、透明地发挥价值。