☰
慢性胃炎数据挖掘:K-means聚类与Resblock-BP预测模型
2026/10/21 16:00:49 网站建设 项目流程

1. 项目全貌:从病历数据到多模型联合分析的思路

手里拿到一批慢性胃炎的病历数据,要做的事情其实很明确:用数据挖掘的方法,把散落在病历里的规律找出来,再做分类预测。这个项目标题看起来长,拆开看就是三条主线——聚类分析(K-means和AGNES)、关联规则挖掘,以及用Resblock优化后的BP神经网络做预测。前面还加了SPSS和Python两个工具,说明整个流程不是单一工具走到底,而是各取所长。

先说这个项目到底解决了什么问题。病历数据是典型的表格型数据,但和普通业务数据不一样的地方在于:字段极其复杂,既有性别、年龄、病程这类结构化字段,又有症状描述、证型判断、用药方案这类半结构化甚至文本型字段。直接用神经网络往里面灌,效果往往很差。所以整个项目的思路是分阶段递进:先用聚类把人群分群,看看有没有规律性的亚组;再用关联规则挖掘症状和证型之间的共现规律;最后才用神经网络做预测建模。而神经网络部分之所以要用Resblock优化,是因为普通的BP网络在层数加深之后会出现梯度消失和退化问题,我们后面会详细讲。

这个项目适合谁来参考?如果你是做医学数据分析的入门者,或者是想了解传统统计工具(SPSS)如何与Python建模工具配合使用的从业者,又或者你正在纠结“聚类结果和预测模型到底怎么串联起来用”,这篇内容应该能给你一个完整的实操参考。

我习惯先把整个分析链条画出来再动手:数据清洗与标准化 → SPSS做描述统计和差异性检验 → Python做K-means和AGNES聚类 → Apriori关联规则 → Resblock-BP神经网络建模与评估。每一步的输出都是下一步的输入,比如聚类得到的类别标签可以作为神经网络的辅助特征,关联规则发现的高频组合可以作为特征工程的重要依据。这种串行流程的好处是每一步的结果都可解释、可验证,不是黑箱。

2. 数据准备:SPSS与Python的分工配合

2.1 SPSS先行的原因:快速掌握数据全貌

很多人一上来就用Python跑模型,这其实是个常见的误区。SPSS这个老牌统计工具在医学数据分析里至今仍有不可替代的位置,原因是它能在几分钟内给出变量的分布、缺失情况、组间差异的显著性检验结果,这些信息能帮我们快速判断数据质量,决定后续建模的方向。

我拿到慢性胃炎病历数据后,第一件事是先用SPSS做频率分析和描述统计。看什么?看核心变量的分布是否合理,比如年龄段是否集中在某个区间、性别比例是否均衡、病程长短的离散程度如何。这些都是后续分析的基础。举个例子,如果病程这个变量严重右偏,那在做聚类之前要考虑取对数或者做标准化处理,否则极端值会主导距离计算。

SPSS里做这些非常方便,菜单操作点几下就能出结果。但它的短板也很明显:做不了深度的机器学习建模,聚类算法也只有K-means的简化版,层次聚类虽然能用但参数控制不够精细。所以我的策略是SPSS负责“摸底”,Python负责“深挖”。

2.2 数据清洗和标准化的实操要点

病历数据的清洗是最花时间的环节,没有之一。常见的坑有这么几个:一是字段命名不统一,有的叫“诊断”,有的叫“诊断结果”,有的叫“中医诊断”,合并前必须手动核对字典;二是缺失值处理,比如内镜表现这个字段经常是空的,这时候要看缺失比例,超过40%就要考虑是否弃用或者单独作为一档“未知”;三是文本字段的清洗,像症状描述这种,里面会有“无明显不适”、“偶感”、“轻度”这类程度词,处理时需要做分词和词表映射。

清洗做完就要标准化。K-means是基于距离的算法,如果变量量纲不同,比如年龄(40~70)和胃镜评分(0~10)放在一起算欧氏距离,年龄会完全主导。这里我建议用Z-score标准化,也就是(x - mean) / std,把每个变量都变成均值为0、标准差为1的分布。SPSS里“描述统计”菜单直接能算Z分数,但更推荐在Python里用StandardScaler,因为后续建模可以封装进Pipeline,避免数据泄漏。

数据清洗完了还要做一次完整性校验。我的习惯是分三步检查:一是各字段缺失率是否达到可接受阈值;二是关键变量(如年龄、性别、诊断类型)有没有逻辑错误,比如年龄为负值这种明显问题;三是数据总量是否满足后续建模需求。一般来讲,做聚类分析样本量不能太少,少于100例的聚类结果稳定性很差,如果只有几十例,建议不要强行上AGNES,改用K-means试效果会更实际。

3. 聚类分析:K-means与AGNES的双路对照

3.1 K-means聚类的核心参数与K值确定

K-means是聚类里最常用的算法,原理简单粗暴:随机选K个中心点,迭代更新直到收敛。但真正决定效果的不是算法本身,而是三个前置步骤。

第一步是选特征。不是所有变量都适合放进聚类模型。比如性别这种二分类变量,放进K-means效果不好,因为距离计算对类别型变量不友好,一般建议在聚类前先剔除或者做one-hot编码。慢性胃炎数据里我最终选用的特征是:年龄、病程、胃镜评分、Hp感染状态(编码为0/1/2)、主要症状的数量,一共5个维度。

第二步是定K值。这里我用了“肘部法则+轮廓系数”双确认。肘部法则的做法是跑K从2到8的K-means,记录每个K对应的簇内误差平方和(SSE),画折线图,找拐点。轮廓系数的取值范围在[-1,1],越接近1说明聚类效果越好,一般0.5以上就说明有一定聚合度。我当时跑出来K=3时轮廓系数是0.52,K=4是0.47,结合SSE的拐点位置,最终确定K=3。

第三步是必须设置random_state。K-means的初始点是随机选的,不固定随机种子的话每次结果都可能不同,这在写论文或做汇报时是大忌。设置random_state=42这类固定值,才能保证结果可复现。

3.2 AGNES层次聚类的适用场景与结果解读

AGNES(自下而上的层次聚类)和K-means的最大区别是:K-means要预先指定K,AGNES不需要,它把每个样本当作一个簇,然后逐步合并距离最近的两个簇,直到所有样本合并成一个簇,这个过程可以用树状图完整呈现。

那什么情况下该用AGNES而不用K-means?我的经验是两个判断标准:第一,数据量不大(几百例),AGNES的时间复杂度是O(n²),数据量过万就跑不动了;第二,你想“看”聚类的过程,而不只是拿最终分组结果。医学数据其实很适合这种探索方式,因为医生希望看到人群是怎么一层层分出来的。

AGNES里有个关键参数是连接方式(linkage)。我推荐用Ward方法,因为它最小化合并后的簇内方差,效果最接近K-means的优化目标,生成的分簇比较紧凑。如果用了single linkage,容易出现“链条效应”——两个真正不同的大类被一根细线连起来,结果很不可靠。

我们当时跑完AGNES后看了树状图,发现其实有两三个样本非常独立,离所有其他样本都很远。这类离群点用K-means是看不出来的,K-means会硬把它们分到某个簇里,影响整个簇的质心。所以我把这两三个样本单独标记出来,在后续建模时做了剔除,净效果是所有模型的评估指标都往上走了一截。

3.3 两类聚类结果如何互相验证

很多人会问:K-means和AGNES都跑一遍,到底以哪个结果为准?我的答案是:不用二选一,而是看两个结果是否“合拍”。如果同一个数据点,K-means把它分到簇1,AGNES也把它分到簇1,那这个分簇可信度就很高;如果两边结果打架,说明这个样本处于聚类边界,本身就不适合硬分类。

实际操作中,我用交叉表做了两组聚类的比对,算了一下一致性比例,大概在85%左右。这一步的价值在于为后续给每个簇“贴标签”提供了信心。比如慢性胃炎数据聚类之后,我们可以试着给每个簇命名:簇1是“年轻轻度组”(病程短、症状少、Hp感染率低),簇2是“中老年Hp阳性组”,簇3是“重度症状组”。这些标签在医生看来有临床意义,在建模角度看就是天然的样本分层依据。

聚类结果给神经网络用的时候,还有一个小技巧:把簇标签作为类别特征拼进原始特征矩阵里。这等于让模型“白嫖”了无监督学习发现的规律,实测下来对最终预测准确率有1~3个百分点的提升。

4. 关联规则挖掘:从症状组合中找到强规律

4.1 Apriori算法的核心逻辑与参数设置

关联规则挖掘在这个项目里的定位,是做症状和证型之间的“组合规律发现”。通俗讲就是找“什么症状经常一起出现”“什么证型往往伴随什么主诉”,这种知识对于辅助诊断和病历质控都有参考价值。

Apriori算法的核心思想其实很简单:如果某个项集是频繁的,那么它的所有子集也必须是频繁的。反之,如果某个项集不频繁,它的超集也不需要再考虑。这种“先剪枝再计数”的思路能大幅减少计算量。算法流程是:扫描数据 → 生成频繁1项集 → 连接生成候选2项集 → 剪枝 → 扫描计数 → 得到频繁2项集 → 一直迭代到没有新的频繁项集产生。

这里有两个关键参数:最小支持度(min_support)和最小置信度(min_confidence)。支持度是“这个组合在全部病历中出现的概率”,置信度是“出现A的前提下出现B的概率”。我在这个项目里把min_support设为0.05,min_confidence设为0.6。为什么这么设?因为病历数据里的症状字段非常稀疏,患者之间的个体差异大,如果把支持度设到0.1,很多有价值的低频组合会被过滤掉;低于0.05又会生成大量无意义的规则,噪声太多。置信度0.6以上保证规则有一定可靠性,但也不要设到0.8,那样剩下的规则太少,探索性变差。

4.2 关联规则结果筛选与医学解释

Apriori会一次性生成大量规则,需要我们做筛选。除了支持度和置信度,还有一个重要指标叫提升度(Lift)。提升度大于1说明A和B正相关,等于1说明独立,小于1说明负相关。筛选规则时我的经验是保留提升度大于1.2的规则,因为很多强置信度的规则可能是由于某个症状本身非常普遍,比如“腹胀”在几乎所有慢性胃炎病历里都有,那它在规则里作为前项或后项的意义就很弱。

我们最终得到的几条有价值的规则是这样的:如果主诉包含“胃脘隐痛”且“纳差”,那么被判断为“脾胃气虚证”的概率显著提升;如果“反酸”和“烧心”同时出现,则和“肝胃不和证”的关联较强。这类规则从医学角度是能被解释的——症状组合本身就有病理生理基础,挖掘结果本质上是把这些隐含规律显式化了。这一步的意义在于,它能帮助验证后续BP神经网络的学习结果是否有临床合理性,模型学到的模式如果跟关联规则高度一致,说明模型是真学会了规律而不是死记硬背。

要注意的是,关联规则挖掘只负责发现问题,不负责证明因果关系。症状和证型之间是互相关联的关系,不是“症状导致证型”的因果推断,写报告和做解读的时候措辞上要有边界感。

5. 神经网络预测:Resblock优化的BP网络构建全流程

5.1 为什么标准BP网络需要Resblock优化

这是整个项目技术含量最高的部分。标准的BP神经网络(反向传播网络)结构通常是:输入层 → 若干隐藏层 → 输出层,用链式法则求梯度,再更新权重。但有一个问题随着层数加深会越来越明显——梯度消失。在反向传播过程中,梯度要从输出层逐层往回传,每经过一层就要乘以激活函数的导数。像sigmoid这类激活函数的导数最大值才0.25,多乘几次梯度就趋近于0了,前面的层几乎学不到东西。

还有一种情况是退化问题:网络层数加深后,训练集上的误差不降反升。这不是过拟合,而是因为深层网络在优化时更难找到最优解。

Resblock的解决思路是从计算机视觉领域借鉴来的。ResNet在2015年提出时,核心就是引入了“跳层连接”(skip connection):让某一层的输入直接加到几层之后的输出上。公式表达是y = F(x) + x,其中F(x)是经过几层变换后的结果。这样一来,梯度可以从后面的层直接“抄近道”传到前面,即使F(x)里的梯度消失,还有一条x的直连通道保证梯度不会为0。更深层的网络因此变得可训练了。

在这个项目里,我把这个思路移植到了BP网络结构上。标准BP隐藏层是线性堆叠,每层输出是上一层的非线性变换。加入Resblock后,变成:h = ReLU(BN(W2 · ReLU(BN(W1 · x)) ) ) + x,其中BN是批归一化层。这个结构的好处是既缓解了梯度问题,又加快了收敛速度。

5.2 网络结构与关键参数配置

我使用的最终网络结构如下:

  • 输入层:15个特征(从聚类、关联规则、原始结构化字段综合出来的特征)
  • 第一个Resblock:全连接层(64) → BN → ReLU → 全连接层(64) → BN → ReLU → 与输入残差相加
  • 第二个Resblock:全连接层(32) → BN → ReLU → 全连接层(32) → BN → ReLU → 与输入残差相加
  • 输出层:根据任务类型选择激活函数(分类用Softmax,回归用线性激活)

这里有几个参数要特别说明。第一是激活函数,隐藏层我统一用ReLU,原因很简单:ReLU在正区间梯度恒为1,不像sigmoid那样存在梯度饱和问题。第二是BN层的加入,它能对每一层的输入做归一化,让数据分布更稳定,这样我们可以用更大的学习率而不担心震荡。第三是隐藏层宽度,64和32的宽度是针对这个数据规模(几百例样本)设计的,太宽容易过拟合,太窄学习能力不够。样本量只有几百条的话,网络宽度不宜超过128,这是经验值。

训练参数方面,我用的优化器是Adam,初始学习率0.001,batch_size=32,epochs=200,early stopping的patience设为20。早停的机制是:当验证集上的损失连续20个epoch不下降,就停止训练,模型回滚到验证损失最低的权重。这个机制必须加,否则很可能会在训练集上过拟合。

5.3 训练过程的关键决策:验证集划分与数据泄漏防范

深度学习里最容易被忽视的是数据划分方式。这个项目里我反复强调一个原则:所有数据预处理步骤必须在训练集上完成后再应用到验证集和测试集,也就是要用sklearn的Pipeline来组织流程。否则,如果先在全数据集上做了标准化再划分,验证集的信息已经通过均值和方差悄悄泄漏进了训练过程,得到的评估指标会虚高,部署后效果立刻现形。

我们用的划分比例是训练集:验证集:测试集 = 7:2:1。有人会问为什么测试集只留10%?原因是样本总量本身不大,如果测试集占比太高,训练数据就不够了。但10%的测试集要保证类别分布和总体一致,这里用到了分层抽样(stratify),否则某一类的样本可能全部掉进测试集,导致训练时完全没见过这一类的样本。

看一下训练过程记录:第一个epoch训练集准确率只有62%,验证集54%,显然没学到东西。到第30个epoch,训练集到了78%,验证集在72%左右,开始分化;第60个epoch左右训练集已经到88%,验证集卡在78%附近波动——这时候我知道过拟合开始了。早停机制在epoch≈85的地方触发了,最终模型的验证集准确率定格在81.4%,测试集准确率79.8%。

对比一个没有Resblock的普通3层BP网络,在完全相同的数据上,验证集准确率大约在73%左右,训练曲线明显更陡地走向过拟合。所以说Resblock不是玄学,在数据量不大、特征存在非线性交互的表格数据上,它确实能带来实打实的增益。

5.4 Resblock在数据量较小时会不会过拟合

这是我在项目推进中反复被问到的问题。在视觉领域,ResNet动辄在千万张图片上训练,Resblock的强表达能力天然占优。但医疗病历数据通常只有几百例,很多人担心残差结构会不会更容易过拟合。

我的实测结论是:有风险,但可控。关键是配套措施要跟上。第一,每个Resblock后面接BN和Dropout(我设置的dropout比率是0.2),这些正则化手段能有效抑制过拟合。第二,早停策略必不可少。第三,模型复杂度需要刻意控制——不要盲目加宽或加深,我最终的结构其实只有两个Resblock,总参数量在几千这个量级,远低于标准ResNet动辄百万级的体量。

还有一招是数据增强思路的变体,在表格数据里可以用“特征扰动”来等效实现:对输入特征添加少量高斯噪声(标准差取原特征标准差的5%),相当于给模型看了更多略有变化的样本,也能提升泛化能力。不过这个方法要慎用,加了噪声之后特征的可解释性会变弱,如果项目更看重分析结论而非纯预测效果,不建议加。

6. 常见问题与排查技巧实录

6.1 聚类结果不稳定怎么办

K-means最让人头疼的问题是对初始中心敏感。如果你发现每次跑出来的簇中心坐标差很多,原因可能有三个:一是没设random_state;二是数据没有标准化,某个维度的方差过大导致聚类结果被这个维度带着跑;三是数据本身的聚类结构不明显,属于无天然分群的数据。

解决办法:固定随机种子、加强标准化、用多次运行取投票结果的方式(比如跑50次K-means,每个样本以出现最多的簇标签作为最终标签)。AGNES相比之下是确定性的,结果不会受随机种子影响,适合作为“仲裁者”来验证K-means的可信度。

6.2 Apriori规则太多或太少怎么调参

规则太多的时候,优先调高min_support,这是最直接的手段。比如从0.05调到0.08,规则数量经常能减少一半以上。如果规则太少,又需要先检查数据格式是否正确,Apriori要求输入是交易型数据格式(每一行是一个患者的全部症状集合),很多人犯错的原因是字段里多个症状被逗号分隔在一个单元格里,没有做拆分处理。

Python里处理这个很顺手,用Pandas的str.split('、', expand=True)配合stack()就能把一条多症状记录转成多行单项记录,再用transactionEncoder.fit_transform()转成布尔矩阵。如果用了SPSS Modeler做Apriori,要注意它要求的输入格式是“标志型字段”,每个症状各占一列,值为0/1。

6.3 BP网络训练不收敛或过拟合早发的排查清单

我把训练中遇到过的典型状况整理成了一份速查清单,每次新项目我都会对照着过一遍:

现象可能原因处理方案
训练损失完全不下降学习率太大或太小尝试0.01、0.003、0.001三个量级
训练集准但验证集远低于训练集过拟合已发生增大Dropout,提前早停patience
损失震荡不降BN缺失或batch太小加上BN层,增大batch_size到64
准确率一直徘徊在某个值数据类别不均衡先检查样本分布,需要时用类别权重

还有一个非常隐蔽的坑:验证集损失在下降过程中出现平台期,然后突然掉下去。这种情况往往不是模型变好了,而是步长过大导致跳出了较好的解区间后又幸运地跳回来了。判断方法是看验证集准确率是否同步上升,以及训练曲线的平滑度。遇到这种情况,我建议调低学习率重跑一遍确认,不要急着采信这个结果。

6.4 特征重要性与结果解释怎么做

模型建完后还有一个关键步骤:解释特征的重要性。我用的是排列重要性(Permutation Importance)方法,做法很简单:把某一列特征的值随机打乱,看模型预测准确率下降多少。下降得越多说明这个特征越重要。实测下来,Hp感染状态、胃镜评分、病程长度排在前三位,年龄和症状数量排在后面。这个结论和临床经验基本一致,说明模型学到的东西有现实依据。

要注意的是,排列重要性存在一个局限:如果两个特征高度相关,打乱其中一个时模型还能通过另一个获得相同信息,导致的准确性下降就被低估了。如果发现两个变量在交叉表里分布高度重合,建议手动删掉其中一个再做一遍排列重要性,验证结果的稳定性。

SPSS和Python在这个项目里的配合让我有一个很深的体会:工具不在多,关键是在每一个环节用对工具。SPSS的优势在于快速出统计结果、适合给不懂代码的协作者展示中间结果;Python的优势在于灵活性、可复现性和深度建模能力。两者都用上的项目流程,比只用其中任何一个工具都要顺畅。

踩过几次坑之后,我再做类似的数据挖掘项目时,都会把“先聚类分群 → 再规则挖掘 → 最后深度建模”这个流程作为基本框架。这不是说每个项目都必须三件套全上,而是每一步的结果都可能成为下一步的输入,形成一个互相验证、互相增强的分析闭环。如果你的病历数据同样面临人群异质性大、规律不明显的问题,建议从聚类这一步开始,先把人群分层搞清楚了,后面做什么都会轻松不少。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询