1. 项目概述当机器学习遇上医疗数据不平衡在医疗健康数据分析尤其是像糖尿病这样的慢性病风险预测领域我们常常会碰到一个令人头疼的问题数据不平衡。简单来说就是数据集中“健康”的样本多数类远远多于“患病”的样本少数类。这就像在一个100人的班级里只有5个学生得了流感你要训练一个模型来精准识别出这5个“流感学生”。如果直接用原始数据训练模型很容易“偷懒”——它只要把所有学生都预测为“健康”就能获得95%的准确率听起来很高但对找出那5个真正的病人毫无用处。这就是类别不平衡问题在现实中的残酷体现。我最近深入研读并复现了S. Perveen等人的一项研究他们聚焦于代谢综合征MetS与糖尿病发展的关联并尝试用机器学习模型进行预测。这项研究的核心挑战和亮点就在于如何优雅地处理这种不平衡数据。传统上大家会想到随机欠采样随机扔掉一些健康样本或随机过采样随机复制一些病人样本但这两种方法各有各的“坑”。欠采样可能把一些关键的、有代表性的健康样本信息给丢掉了而过采样简单复制又容易让模型过度关注那些重复的、甚至可能是噪声的少数类样本导致过拟合——在训练集上表现完美一遇到新数据就“露馅”。这项研究引入并验证了K-medoids采样技术结合朴素贝叶斯Naïve Bayes分类器在糖尿病预测任务上取得了比传统方法更优的性能。这不仅仅是一个技术选型的胜利更是为小样本、高不平衡的医疗数据场景提供了一套更可靠、更稳健的分析思路。对于从事医疗数据分析、风险预测模型开发或是任何面临类别不平衡问题的数据科学家来说这项研究中的技术路径和实操细节都具有很高的参考价值。接下来我将结合自己的理解和实践拆解这项技术的原理、实现以及那些论文里不会写的“踩坑”经验。2. 核心挑战与方案选型为什么是K-medoids在动手之前我们必须先搞清楚敌人是谁以及为什么选择这件武器。2.1 医疗数据中的类别不平衡困境在糖尿病预测场景中数据不平衡是常态而非例外。基于社区或电子病历的队列研究中糖尿病的年发病率通常在1%-5%之间。这意味着在一个万人的数据集中可能只有几百个阳性患病案例。直接在这样的数据上训练分类模型模型会严重偏向于多数类未患病。注意评估不平衡数据集上的模型绝不能只看整体准确率Accuracy。一个将所有人都预测为健康的“傻瓜模型”就能获得超过95%的准确率但这毫无意义。必须关注召回率Recall又称灵敏度、精确率Precision、F1-score特别是针对少数类的AUC-ROC曲线下面积。传统采样方法主要有两大流派但各有缺陷随机过采样Random Over-sampling随机复制少数类样本直到两类数量平衡。问题在于它只是简单复制现有样本没有创造新信息。这会导致模型过度学习这些重复样本的特定特征甚至包括其中的噪声从而在训练集上产生过于乐观的估计泛化能力差。随机欠采样Random Under-sampling随机丢弃多数类样本直到数量与少数类持平。这种方法最直接的风险是信息丢失。那些被随机丢弃的多数类样本中可能包含了许多界定“健康”与“患病”边界的宝贵信息。丢失这些信息相当于让模型在一个有偏的、不完整的“健康”画像上进行学习其判别能力自然会下降。2.2 K-medoids采样的核心思想与优势K-medoids采样技术的核心在于“智能”而非“随机”。它属于原型选择Prototype Selection的一种其目标是从原始数据集中选出一个最具代表性的子集。K-medoids算法本身是聚类算法的一种可以看作是K-means的一个更稳健的变体。K-means使用簇内样本的均值作为簇中心对异常值非常敏感。而K-medoids则选择簇内实际存在的一个样本点作为中心medoid这个中心点是簇内到其他所有点距离之和最小的那个点因此对噪声和异常值的鲁棒性更强。将K-medoids思想用于采样其流程可以理解为对于欠采样我们希望从庞大的多数类中筛选出最具代表性的一个子集。我们可以对多数类样本运行K-medoids聚类设定K值等于少数类的样本数或一个相近的数值。聚类完成后每个簇的medoid中心点就被选为代表该簇的样本。这些medoid构成的集合理论上能够最大程度地保留多数类的整体分布结构和边界信息同时大幅减少样本数量。对于过采样我们希望为少数类生成有意义的“新”样本。同样可以对少数类进行K-medoids聚类但此时K值可以设得小一些。然后不是简单复制中心点而是可以以每个medoid为中心在其邻近的同类样本之间进行插值如SMOTE算法的思想生成合成样本。不过在原论文中更侧重于使用K-medoids进行欠采样来平衡数据。为什么它优于随机方法信息保留最大化通过选择中心点medoids作为代表它试图用最少的样本覆盖原始数据的主要分布模式减少了随机丢弃带来的信息损失风险。抑制噪声影响由于medoid是实际存在的、到簇内其他点距离之和最小的点它天然地远离噪声点因此选出的代表性子集更“干净”。边界保持在分类问题中两类样本的决策边界附近的点最为关键。K-medoids聚类后位于边界区域的簇其中心点很可能也靠近边界从而保留了这些关键的判别信息。在原研究中作者将K-medoids采样与朴素贝叶斯分类器结合正是看中了朴素贝叶斯计算高效、在小样本上也能工作的特点与K-medoids提供的“高质量、平衡化”训练集相得益彰最终在糖尿病预测任务上实现了更优的、特别是针对少数类糖尿病患者的识别性能。3. 技术实现细节与实操步骤理论讲清楚了我们来点实际的。如何将K-medoids采样应用到糖尿病预测项目中下面是我基于研究复现和扩展的详细步骤。3.1 数据准备与特征工程任何机器学习项目都始于数据。对于糖尿病预测数据通常包含人口统计学信息年龄、性别、临床指标BMI、血压、血液检测结果血糖、血脂等。关键步骤数据清洗处理缺失值。对于医疗数据直接删除缺失过多的样本或特征需谨慎。可以采用中位数/众数填充、或使用模型预测填充。对于类别型变量如性别进行编码如Label Encoding或One-Hot Encoding。特征选择本研究聚焦于代谢综合征MetS的相关风险因素。通常包括腰围或BMI空腹血糖血压收缩压/舒张压甘油三酯TG高密度脂蛋白胆固醇HDL-C 根据NHLBI/AHA的标准具备其中三项及以上即可判定为MetS。这些指标是预测糖尿病的强相关特征。数据标准化由于K-medoids算法依赖于距离计算常用欧氏距离或曼哈顿距离必须对连续型特征进行标准化如Z-score标准化消除量纲影响确保个特征在距离计算中贡献均衡。# 示例代码数据预处理核心步骤 import pandas as pd from sklearn.preprocessing import StandardScaler, LabelEncoder from sklearn.model_selection import train_test_split # 假设 df 是包含‘diabetes’标签0/1的DataFrame # 1. 分离特征和标签 X df.drop(diabetes, axis1) y df[diabetes] # 2. 分割训练集和测试集务必先分割再对训练集做采样 X_train_raw, X_test, y_train_raw, y_test train_test_split(X, y, test_size0.2, stratifyy, random_state42) # 3. 对训练集特征进行标准化 (拟合scaler只用训练集数据) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train_raw) X_test_scaled scaler.transform(X_test) # 测试集用同样的scaler转换 # 将数组转回DataFrame以便后续操作保留列名 X_train pd.DataFrame(X_train_scaled, columnsX_train_raw.columns, indexX_train_raw.index)实操心得train_test_split中的stratifyy参数至关重要。它确保在训练集和测试集中糖尿病患者的比例即类别分布与原始数据集保持一致。这是评估模型处理不平衡问题能力的公平前提。3.2 K-medoids欠采样实战实现Scikit-learn库没有直接提供K-medoids但我们可以使用scikit-learn-extra库中的KMedoids或者使用相似度矩阵来实现。方案一使用scikit-learn-extra# 安装 pip install scikit-learn-extra import numpy as np from sklearn_extra.cluster import KMedoids from imblearn.under_sampling import ClusterCentroids # 假设我们已获得训练集 X_train, y_train # 分离多数类和少数类 X_majority X_train[y_train 0] y_majority y_train[y_train 0] X_minority X_train[y_train 1] y_minority y_train[y_train 1] # 设定目标使多数类样本数降至与少数类相同 n_clusters len(X_minority) # 初始化KMedoids使用‘euclidean’距离并增加初始化次数以获得稳定结果 kmedoids KMedoids(n_clustersn_clusters, metriceuclidean, initheuristic, random_state42) kmedoids.fit(X_majority) # KMedoids的cluster_centers_属性返回的是中心点的索引我们需要根据索引取出样本 majority_indices kmedoids.medoid_indices_ X_majority_sampled X_majority.iloc[majority_indices] y_majority_sampled y_majority.iloc[majority_indices] # 组合平衡后的训练集 X_train_balanced pd.concat([X_majority_sampled, X_minority], axis0) y_train_balanced pd.concat([y_majority_sampled, y_minority], axis0) # 打乱顺序可选但通常是个好习惯 shuffle_idx np.random.permutation(len(X_train_balanced)) X_train_balanced X_train_balanced.iloc[shuffle_idx].reset_index(dropTrue) y_train_balanced y_train_balanced.iloc[shuffle_idx].reset_index(dropTrue)方案二使用自定义距离矩阵与PAM算法K-medoids的标准实现是PAMPartitioning Around Medoids算法。我们可以通过计算距离矩阵来更精细地控制过程。from sklearn.metrics.pairwise import pairwise_distances # 计算多数类样本间的距离矩阵 distance_matrix pairwise_distances(X_majority, metriceuclidean) # 实现一个简化的PAM算法这里仅示意核心逻辑完整PAM较复杂 # 1. 随机选择n_clusters个初始medoids # 2. 将每个点分配到最近的medoid所在的簇 # 3. 对于每个簇尝试用非medoid点替换当前medoid如果总距离减小则替换 # 4. 重复2-3直到medoids不再变化 # 在实际中建议使用现成库或参考成熟实现。注意事项K-medoids算法的计算复杂度比K-means高因为它需要在每次迭代中计算所有点到所有候选中心点的距离。当多数类样本量极大时例如10000直接对整个多数类进行聚类可能会比较慢。此时可以考虑先使用一次快速的K-means进行粗聚类减少候选点数量或者对多数类先进行一次随机欠采样降到5000-10000样本再进行K-medoids精采样。3.3 模型训练与评估获得平衡的训练集后就可以训练分类器了。原研究对比了朴素贝叶斯和J48决策树这里我们以朴素贝叶斯为例。from sklearn.naive_bayes import GaussianNB from sklearn.metrics import classification_report, confusion_matrix, roc_auc_score # 初始化朴素贝叶斯分类器 nb_classifier GaussianNB() # 在平衡后的训练集上训练 nb_classifier.fit(X_train_balanced, y_train_balanced) # 在未经过采样的原始测试集上进行评估这是检验泛化能力的关键。 y_pred nb_classifier.predict(X_test_scaled) y_pred_proba nb_classifier.predict_proba(X_test_scaled)[:, 1] # 获取预测为1的概率 print(测试集分类报告) print(classification_report(y_test, y_pred, target_names[非糖尿病, 糖尿病])) print(\n混淆矩阵) print(confusion_matrix(y_test, y_pred)) print(f\nAUC-ROC分数 {roc_auc_score(y_test, y_pred_proba):.4f})评估重点解读分类报告重点关注糖尿病少数类的精确率Precision、召回率Recall和F1-score。召回率灵敏度尤其重要它表示实际患病的人中被模型正确找出的比例在疾病筛查中宁可误报一些也不能漏报。混淆矩阵直观展示真阴性TN、假阳性FP、假阴性FN、真阳性TP的数量。一个好的模型应在主对角线TN和TP上有高值。AUC-ROC这个指标对类别不平衡不敏感它衡量的是模型将正例排在负例前面的能力。值越接近1越好0.5表示没有判别能力。3.4 与传统方法的对比实验为了复现研究结论你需要设计对比实验基准模型直接在原始不平衡训练集上训练朴素贝叶斯。随机过采样使用imblearn.over_sampling的RandomOverSampler。随机欠采样使用imblearn.under_sampling的RandomUnderSampler。K-medoids欠采样如上文实现。使用相同的训练/测试集划分对每种采样方法训练同一个朴素贝叶斯分类器并在同一个测试集上评估。记录并比较各项指标特别是少数类的召回率和AUC-ROC。4. 深入解析K-medoids采样的参数与调优像所有机器学习方法一样K-medoids采样也有其需要精心调节的参数和细节这些往往决定了最终的成败。4.1 核心参数K值的选择与距离度量K值聚类数量这是最重要的参数。在欠采样场景下通常将K设置为少数类的样本数量。这样做的目标是得到一个完全平衡的数据集两类样本数相等。这是最直接的做法。更灵活的策略完全1:1的平衡未必是最优的。有时多数类需要保留更多信息。可以尝试将K设为少数类样本数的倍数如1.5倍或2倍得到一个轻微不平衡但信息更丰富的训练集。这需要通过交叉验证来寻找最佳比例。确定方法可以绘制不同K值下模型在验证集上特别是少数类F1或AUC的性能曲线选择一个性能饱和或最佳的K值。距离度量K-medoids的核心是距离计算。欧氏距离最常用适用于连续型特征且各维度重要性相当时。前提是数据必须标准化。曼哈顿距离对异常比欧氏距离稍不敏感。余弦相似度如果更关注特征向量的方向而非绝对距离可以考虑使用。但在医疗数据中欧氏距离通常是默认选择。自定义距离对于混合型数据连续分类可能需要定义自定义距函数。这是一个高级话题需要深厚的领域知识。# 示例尝试不同的K值 from sklearn.model_selection import cross_val_score minority_count sum(y_train_raw 1) k_values [minority_count, int(minority_count * 1.5), minority_count * 2] auc_scores [] for k in k_values: if k len(X_majority): # K不能大于多数类样本数 k len(X_majority) # 执行K-medoids采样此处省略采样代码复用3.2节函数 X_bal, y_bal kmedoids_undersample(X_train, y_train, n_clustersk) # 使用交叉验证评估 scores cross_val_score(GaussianNB(), X_bal, y_bal, cv5, scoringroc_auc) auc_scores.append(scores.mean()) print(fK{k}, 平均AUC: {scores.mean():.4f}) # 选择AUC最高的K值4.2 初始化策略与算法稳定性K-medoids对初始中心点的选择敏感可能导致不同的局部最优解。‘heuristic’初始化sklearn_extra.cluster.KMedoids中的initheuristic是一种常用且较快的方法它先选取数据空间中距离最远的点作为初始中心通常效果不错。多次随机初始化设置initrandom并配合一个较大的max_iter和不同的random_state多次运行算法选择目标函数到中心点的总距离最小的那次结果作为最终采样。这能增加找到更好解的几率但会增加计算成本。‘k-medoids’类似于K-means这是一种概率化的初始化方法旨在使初始中心点彼此远离。虽然不是所有库都直接支持但可以自己实现或寻找相关实现。实操心得在医疗数据这种“小数据”场景下我建议采用initheuristic并固定random_state以保证结果可复现。如果追求极致性能可以尝试多次随机初始化并取平均效果但要注意计算开销。4.3 与过采样技术的结合SMOTE K-medoids原研究主要探讨了K-medoids用于欠采样。但在实践中还有一种强大的思路是混合采样。对少数类使用SMOTESynthetic Minority Over-sampling Technique进行过采样SMOTE不是简单复制而是在少数类样本的k近邻之间线性插值生成“新”的合成样本能有效增加少数类的多样性。对多数类使用K-medoids进行欠采样在SMOTE增加了少数类样本后多数类可能仍然占优。此时再用K-medoids对多数类进行智能欠采样最终得到一个平衡且高质量的数据集。这种“SMOTE K-medoids”的组合拳既能丰富少数类的表征又能精炼多数类的信息往往能取得比单一方法更好的效果。可以使用imblearn库的SMOTE和自定义的K-medoids采样器通过Pipeline组合实现。5. 项目复现中的常见问题与解决方案在实际复现这项研究或类似项目时我遇到并总结了一些典型问题这里分享给大家。5.1 问题一计算效率低下特别是样本量大时现象当多数类样本数超过1万时K-medoids聚类速度非常慢甚至内存溢出。根因K-medoids算法需要计算和存储所有样本点两两之间的距离矩阵其时间和空间复杂度均为O(n²)n为样本数。解决方案两阶段采样先对多数类进行一次快速的随机欠采样将样本量减少到一个可管理的规模如5000再对这个子集运行K-medoids。虽然会损失一些信息但权衡了效率与效果。使用更快的算法变体CLARAClustering LARge Applications算法是PAM的扩展它不计算整个数据集的距离矩阵而是多次随机抽取子样本进行PAM聚类然后取最佳结果。sklearn_extra的KMedoids似乎没有直接实现CLARA但你可以手动实现这个思路。近似最近邻搜索在计算距离和分配簇时可以使用诸如Ball Tree、KD-Tree或近似最近邻库如annoy,faiss来加速但这需要修改算法底层实现难度较高。硬件与并行确保使用了多核CPU并检查使用的库是否支持并行计算。5.2 问题二采样后模型在测试集上表现不稳定现象每次运行K-medoids采样即使random_state固定得到的训练集略有不同导致最终模型在测试集上的指标有波动。根因K-medoids算法可能收敛到不同的局部最优解特别是当数据分布没有非常清晰的簇结构时。此外数据分割的随机性也会带来影响。解决方案增加算法稳定性如前所述使用initheuristic或进行多次随机初始化取最优。集成学习思路不要只做一次采样、训练一个模型。可以Bagging式采样对多数类进行多次K-medoids采样每次可采样不同数量的中心点或使用不同的随机种子初始化每次采样后与全部少数类组合训练一个基分类器如朴素贝叶斯。最后用这些基分类器进行投票或平均概率形成最终预测。这能有效降低方差。交叉验证集成在训练集内进行交叉验证每一折都对训练部分进行K-medoids采样并训练模型最终模型可以是这些折外模型Out-of-Fold的集成或者用所有数据重新采样训练一个模型但用交叉验证来确定最佳参数。固定所有随机种子从数据分割(train_test_split)、K-medoids初始化(random_state)、到模型训练如果模型有随机性全部设置固定的随机种子确保实验完全可复现。5.3 问题三如何确定K-medoids是否比随机采样有“显著”提升现象对比实验显示K-medoids的AUC比随机欠采样高了0.02这算显著改进吗解决方案需要进行统计检验。重复实验将整个“数据分割 - 采样 - 训练 - 评估”流程重复N次例如30次或50次每次使用不同的随机种子进行数据分割。这样你会得到两个方法各自的N个性能指标如AUC列表。统计检验使用配对样本t检验如果指标近似正态分布或非参数的Wilcoxon符号秩检验来比较这两个指标列表是否存在统计学上的显著差异通常p-value 0.05。报告结果不仅要报告平均性能还要报告标准差以及统计检验的p-value。例如“经过50次重复实验K-medoids采样方法的平均AUC为0.892 (±0.012)显著高于随机欠采样的0.876 (±0.015) (p 0.01)。” 这样的结论才令人信服。5.4 问题四特征空间与距离度量的适配性现象数据中包含分类特征如吸烟史是/否直接使用欧氏距离进行K-medoids聚类不合理。解决方案特征编码与距离定义对于分类特征必须进行合适的编码如One-Hot。然后需要定义一个混合距离度量。例如对于连续特征用标准化后的欧氏距离对于One-Hot编码后的分类特征用汉明距离不同则为1相同则为0最后加权求和。scikit-learn的pairwise_distances函数支持自定义度量函数。预处理与降维如果特征维度很高距离计算会变得低效且“维度灾难”会使距离失去意义。可以考虑先使用主成分分析PCA或t-SNE等降维技术在低维空间进行K-medoids聚类。但要注意降维可能会损失一些判别信息。领域知识指导在医疗数据中某些特征可能比其他特征更重要。可以在定义距离时根据特征与目标变量的相关性如通过互信息或卡方检验获得赋予不同的权重。这需要深入的领域知识。处理医疗数据中的类别不平衡问题远不止是调用一个API那么简单。K-medoids采样技术提供了一种更聪明、更数据驱动的方式来提炼多数类信息。它通寻找最具代表性的原型点在压缩数据量的同时力求保留关键的分布结构特别是决策边界附近的信息。这项技术与朴素贝叶斯等简单高效的分类器结合在糖尿病预测这类小样本、高不平衡的场景中展现出了独特优势。从我个人的复现经验来看成功的关键在于细节数据预处理的严谨性、K值和距离度量的慎重选择、对算法稳定性的把控以及严谨的评估与对比方法。它可能不是一颗“银弹”在数据量极大时计算成本较高但当数据珍贵且不平衡问题严重时它无疑是工具箱里一件值得深入打磨的利器。最后一个小建议在实际项目中不妨将K-medoids与SMOTE等过采样技术结合或者融入集成学习框架往往能碰撞出更稳健、更强大的火花。