机器学习在乳腺癌生存预测中的应用与优化
·
1. 乳腺癌患者生存概率预测模型开发指南
在医疗数据分析领域,构建能够准确预测患者生存概率的模型具有重要价值。本文将详细介绍如何使用机器学习技术开发一个预测乳腺癌患者5年生存率的概率模型,特别针对数据集不平衡的情况进行优化处理。
1.1 项目背景与挑战
我们使用的Haberman数据集记录了1958-1970年间在芝加哥大学比林斯医院接受乳腺癌手术的306名患者的临床数据。该数据集存在几个显著特点:
- 类别不平衡 :生存患者(73.5%)远多于未生存患者(26.5%)
- 特征有限 :仅包含患者年龄、手术年份和阳性腋窝淋巴结数量
- 历史数据 :数据来自上世纪50-60年代,现代医疗条件下可能不完全适用
注意:本项目旨在演示不平衡数据集的概率建模方法,而非提供实际的医疗诊断建议。任何临床决策都应基于更全面、现代的医疗数据。
1.2 技术路线设计
我们的建模流程将分为以下几个关键阶段:
- 数据探索 :分析特征分布和类别不平衡情况
- 基准建立 :确定随机猜测的性能基准
- 模型评估 :测试多种概率预测算法
- 性能优化 :通过数据预处理提升模型表现
- 最终应用 :使用最佳模型进行新数据预测
2. 数据准备与探索分析
2.1 数据集加载与预处理
首先我们需要加载数据并进行基本处理:
from pandas import read_csv
from sklearn.preprocessing import LabelEncoder
def load_dataset(full_path):
# 加载CSV文件
data = read_csv(full_path, header=None)
data = data.values
# 分割特征和标签
X, y = data[:, :-1], data[:, -1]
# 将标签编码为0(生存)和1(未生存)
y = LabelEncoder().fit_transform(y)
return X, y
2.2 数据特征分析
数据集包含三个特征:
- 年龄 :患者手术时的年龄(30-83岁)
- 年份 :手术年份(1958-1969)
- 淋巴结 :阳性腋窝淋巴结数量(0-52个)
通过描述性统计可以看到:
| 特征 | 平均值 | 标准差 | 最小值 | 25%分位 | 中位数 | 75%分位 | 最大值 |
|---|---|---|---|---|---|---|---|
| 年龄 | 52.46 | 10.80 | 30 | 44 | 52 | 60.75 | 83 |
| 年份 | 62.85 | 3.25 | 58 | 60 | 63 | 65.75 | 69 |
| 淋巴结 | 4.03 | 7.19 | 0 | 0 | 1 | 4 | 52 |
2.3 类别分布可视化
from collections import Counter
from matplotlib import pyplot
# 加载数据
X, y = load_dataset('haberman.csv')
# 统计类别分布
counter = Counter(y)
for k, v in counter.items():
per = v / len(y) * 100
print(f'Class={k}, Count={v}, Percentage={per:.1f}%')
# 绘制饼图
labels = ['Survived', 'Not Survived']
sizes = [counter[0], counter[1]]
pyplot.pie(sizes, labels=labels, autopct='%1.1f%%')
pyplot.title('Class Distribution')
pyplot.show()
输出结果:
Class=0, Count=225, Percentage=73.5%
Class=1, Count=81, Percentage=26.5%
3. 模型评估框架构建
3.1 评估指标选择
对于概率预测问题,我们使用Brier Skill Score(BSS)作为主要评估指标:
from sklearn.metrics import brier_score_loss
def brier_skill_score(y_true, y_prob):
# 计算参考Brier分数(基于类别先验)
pos_prob = sum(y_true) / len(y_true)
ref_probs = [pos_prob for _ in range(len(y_true))]
bs_ref = brier_score_loss(y_true, ref_probs)
# 计算模型Brier分数
bs_model = brier_score_loss(y_true, y_prob)
# 计算技能分数
return 1.0 - (bs_model / bs_ref)
BSS解释:
- 0:模型等同于基准预测
- 1:完美预测
- <0:比基准预测更差
3.2 交叉验证策略
采用分层重复K折交叉验证:
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.model_selection import cross_val_score
def evaluate_model(X, y, model):
# 定义10折重复3次的验证策略
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# 使用BSS作为评分指标
metric = make_scorer(brier_skill_score, needs_proba=True)
# 评估模型
scores = cross_val_score(model, X, y, scoring=metric, cv=cv, n_jobs=-1)
return scores
3.3 基准模型建立
使用简单策略作为基准:
from sklearn.dummy import DummyClassifier
# 基准模型:总是预测类别先验概率
baseline = DummyClassifier(strategy='prior')
# 评估基准模型
baseline_scores = evaluate_model(X, y, baseline)
print(f'Baseline BSS: {mean(baseline_scores):.3f} (±{std(baseline_scores):.3f})')
预期输出:
Baseline BSS: 0.000 (±0.000)
4. 概率模型比较与选择
4.1 候选模型测试
我们评估以下6种概率模型:
from sklearn.linear_model import LogisticRegression
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis, QuadraticDiscriminantAnalysis
from sklearn.naive_bayes import GaussianNB, MultinomialNB
from sklearn.gaussian_process import GaussianProcessClassifier
models = [
LogisticRegression(solver='lbfgs'),
LinearDiscriminantAnalysis(),
QuadraticDiscriminantAnalysis(),
GaussianNB(),
MultinomialNB(),
GaussianProcessClassifier()
]
4.2 模型性能对比
执行评估并可视化结果:
results = []
names = []
for model in models:
# 获取模型简称
name = model.__class__.__name__[:7]
# 评估模型
scores = evaluate_model(X, y, model)
# 记录结果
results.append(scores)
names.append(name)
# 打印性能摘要
print(f'{name:>15}: {mean(scores):.3f} (±{std(scores):.3f})')
# 绘制箱线图
pyplot.figure(figsize=(10,6))
pyplot.boxplot(results, labels=names, showmeans=True)
pyplot.title('Model Comparison')
pyplot.ylabel('Brier Skill Score')
pyplot.show()
典型输出结果:
| 模型 | 平均BSS | 标准差 |
|---|---|---|
| LogisticRegression | 0.142 | 0.096 |
| LinearDiscriminantAnalysis | 0.138 | 0.098 |
| QuadraticDiscriminantAnalysis | 0.085 | 0.150 |
| GaussianNB | 0.092 | 0.137 |
| MultinomialNB | -0.042 | 0.086 |
| GaussianProcessClassifier | 0.123 | 0.114 |
4.3 结果分析
从测试结果可以看出:
- 逻辑回归 表现最佳,BSS达到0.142
- 线性判别分析 紧随其后,性能接近逻辑回归
- 多项式朴素贝叶斯 表现最差,甚至低于基准
- 所有模型的标准差较大,说明性能对数据划分敏感
经验分享:对于小型医疗数据集,简单的线性模型(如逻辑回归)往往比复杂模型表现更好,因为它们不容易过拟合。
5. 数据预处理优化
5.1 特征缩放测试
尝试标准化和归一化对模型的影响:
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.pipeline import Pipeline
# 定义预处理方法
preprocessors = [
('原始数据', None),
('标准化', StandardScaler()),
('归一化', MinMaxScaler())
]
# 测试不同预处理对最佳模型的影响
for name, scaler in preprocessors:
if scaler is None:
pipeline = LogisticRegression(solver='lbfgs')
else:
pipeline = Pipeline([('scaler', scaler),
('model', LogisticRegression(solver='lbfgs'))])
scores = evaluate_model(X, y, pipeline)
print(f'{name:>10}: {mean(scores):.3f} (±{std(scores):.3f})')
5.2 特征变换尝试
对偏态分布的特征进行幂变换:
from sklearn.preprocessing import PowerTransformer
# 应用Ye-Johnson幂变换
transformer = PowerTransformer(method='yeo-johnson')
pipeline = Pipeline([
('transform', transformer),
('model', LogisticRegression(solver='lbfgs'))
])
scores = evaluate_model(X, y, pipeline)
print(f'幂变换后: {mean(scores):.3f} (±{std(scores):.3f})')
5.3 优化结果
预处理方法比较:
| 预处理方法 | 平均BSS | 性能提升 |
|---|---|---|
| 原始数据 | 0.142 | 基准 |
| 标准化 | 0.145 | +2.1% |
| 归一化 | 0.147 | +3.5% |
| 幂变换 | 0.149 | +4.9% |
6. 最终模型与应用
6.1 模型训练与评估
选择最佳配置训练最终模型:
from sklearn.preprocessing import PowerTransformer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
# 创建最优管道
model = Pipeline([
('preprocess', PowerTransformer()),
('classifier', LogisticRegression(solver='lbfgs'))
])
# 在全数据集上训练
model.fit(X, y)
# 使用保留测试集评估
# 假设X_test, y_test是预留数据
y_prob = model.predict_proba(X_test)[:, 1]
bss = brier_skill_score(y_test, y_prob)
print(f'Final Model BSS: {bss:.3f}')
6.2 预测新数据
使用训练好的模型预测新患者生存概率:
def predict_survival(age, year, nodes):
# 准备输入数据
case = [[age, year, nodes]]
# 预测生存概率(返回的是未生存概率,需要取反)
prob_not_survive = model.predict_proba(case)[0][1]
prob_survive = 1 - prob_not_survive
return prob_survive
# 示例:预测50岁患者,1965年手术,3个阳性淋巴结的生存概率
probability = predict_survival(50, 65, 3)
print(f'5年生存概率: {probability:.1%}')
6.3 模型解释
对于逻辑回归模型,我们可以分析特征重要性:
# 获取训练后的逻辑回归系数
lr = model.named_steps['classifier']
feature_names = ['年龄', '年份', '淋巴结']
coefficients = pd.DataFrame({
'特征': feature_names,
'系数': lr.coef_[0],
'重要性': np.abs(lr.coef_[0])
}).sort_values('重要性', ascending=False)
print(coefficients)
典型输出:
| 特征 | 系数 | 重要性 |
|---|---|---|
| 淋巴结 | -0.12 | 0.12 |
| 年龄 | -0.05 | 0.05 |
| 年份 | 0.03 | 0.03 |
这表明阳性淋巴结数量是最重要的预测因素,与年龄呈负相关,而较晚的手术年份略微有利于生存。
7. 项目总结与经验分享
通过本项目,我们完成了从数据探索到模型部署的全流程,特别关注了不平衡数据集下的概率预测问题。以下是一些关键经验:
- 评估指标选择 :对于概率预测,Brier Skill Score比准确率更适合不平衡数据
- 模型选择 :简单线性模型在小型医疗数据上往往表现优异
- 数据预处理 :适当的特征变换可以小幅提升模型性能
- 可解释性 :医疗领域需要能够解释模型决策过程
实际应用中的注意事项:
- 历史数据的局限性:现代医疗条件下结果可能不同
- 特征有限性:临床决策需要更多维度的数据
- 不确定性沟通:向患者解释预测结果时应强调概率性质
这个项目展示了如何系统性地解决医疗领域的分类问题,其方法论可以扩展到其他类似的预测任务中。
更多推荐


所有评论(0)