ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

KECA核熵分析实战:训练集与测试集严格分离的降维与故障检测

KECA核熵分析实战:训练集与测试集严格分离的降维与故障检测 简介这份资源面向从事机器学习、数据降维与特征选择研究的同学及工程人员提供核熵成分分析KECA的MATLAB实现重点解决非线性高维数据在训练集与测试集分离条件下的降维与泛化评估问题。压缩包内共1个文件为m脚本整体约1KB可直接在MATLAB环境中加载数据、执行KECA算法并完成投影与结果查看。程序已考虑训练集与测试集的独立划分便于读者在训练阶段学习特征结构、在测试阶段检验模型泛化能力避免过拟合。结合核熵分析思路读者可理解如何通过熵值衡量特征信息量筛选对数据分布影响最大的成分从而简化数据结构、提升后续分类或建模效率。该资源已有327人学习下载适合希望快速上手KECA、对比降维效果或将其嵌入自身实验流程的读者参考使用。1. KECA 核熵分析训练集与测试集分开这件事为什么值得单独拎出来讲如果你手头有一批高维数据——光谱、振动信号、过程工业参数、图像特征——想做降维或者异常检测大概率绕不开 PCA 和 KPCA。但 PCA 只看方差方差大的方向未必包含最有判别力的信息KPCA 虽然引入了核函数处理非线性可它本质上还是在最大化方差。KECAKernel Entropy Component Analysis核熵成分分析换了一个判据它不追求方差最大而是让变换后数据的 Renyi 熵贡献最大化换句话说它挑出来的成分是让数据分布的信息量最集中的那些方向。这个区别在故障检测、过程监控、特征提取场景里非常实际——方差大的成分可能是噪声熵贡献大的成分才真正携带了数据的结构信息。但今天要聊的重点不是 KECA 的公式推导而是一个更容易被忽略、却直接决定结果可信度的工程细节训练集和测试集必须分开处理。我见过太多人把全部样本一起做核矩阵、一起做特征值分解然后随机切分报准确率——这种做法在 KECA 里是致命的因为核矩阵的构造和熵贡献的计算天然依赖全体样本的统计结构一旦测试样本参与了训练阶段的核参数估计和投影方向确定你报出来的任何指标都是虚高的。这个标题里特意标注了「训练、测试集分开」说明作者踩过这个坑或者至少意识到了它的重要性。下面我会把 KECA 的落地路径拆开从核矩阵构造、熵贡献排序、投影矩阵求解到训练/测试严格分离的实现方式再到参数怎么调、哪里容易翻车一步步讲清楚。2. KECA 的核矩阵与熵贡献从公式到能跑的代码2.1 为什么 KECA 的判据是 Renyi 熵而不是方差先把这个事情说透不然后面参数没法调。给定样本集 $X {x_1, x_2, ..., x_n}$$x_i \in \mathbb{R}^d$通过核函数映射到高维特征空间后数据的 Renyi 二次熵可以写成核矩阵的形式$$H(p) -\log \int p^2(x) dx -\log \frac{1}{n^2} \sum_{i1}^{n} \sum_{j1}^{n} K(x_i, x_j)$$其中 $K(x_i, x_j)$ 是核矩阵元素。KECA 的目标是找到一组投影方向使得投影后数据的熵贡献最大。对核矩阵做特征值分解 $K \Phi \Lambda \Phi^T$每个特征值 $\lambda_i$ 和对应的特征向量 $\phi_i$ 对熵的贡献是 $\sqrt{\lambda_i} \phi_i^T \mathbf{1}$$\mathbf{1}$ 是全 1 向量。按这个贡献值从大到小排序取前 $m$ 个成分就得到了 KECA 的降维结果。和 KPCA 的区别在这里KPCA 直接按 $\lambda_i$ 排序KECA 按 $\sqrt{\lambda_i} \phi_i^T \mathbf{1}$ 排序。这两个排序结果经常不一样尤其在数据分布偏斜或者多模态的时候KECA 选出的成分更能反映数据的聚类结构和判别信息。这就是它在故障检测里比 KPCA 好用的原因——故障往往不体现在方差最大的方向上而体现在熵贡献突出的方向上。2.2 训练集上构造核矩阵并求解投影方向下面这段代码是在训练集上独立完成的构造核矩阵、做特征值分解、计算熵贡献、排序、确定投影矩阵。注意所有统计量只来自训练集测试集此时完全不参与。import numpy as np from scipy.linalg import eigh def keca_fit(X_train, kernelrbf, sigma1.0, n_components10): 在训练集上拟合 KECA 模型。 X_train: (n_train, d) 训练样本 sigma: RBF 核带宽参数 n_components: 保留的熵贡献成分数 返回: 投影矩阵、训练集核矩阵、熵贡献排序 n X_train.shape[0] # 1. 构造 RBF 核矩阵只使用训练样本 # 计算样本间欧氏距离平方 sq_dists np.sum(X_train**2, axis1, keepdimsTrue) \ np.sum(X_train**2, axis1) - 2 * X_train X_train.T K np.exp(-sq_dists / (2 * sigma**2)) # 2. 中心化核矩阵在特征空间中中心化 one_n np.ones((n, n)) / n K_centered K - one_n K - K one_n one_n K one_n # 3. 特征值分解K_centered 是对称矩阵用 eigh 更稳 eigenvalues, eigenvectors eigh(K_centered) # 4. 按降序排列 idx np.argsort(eigenvalues)[::-1] eigenvalues eigenvalues[idx] eigenvectors eigenvectors[:, idx] # 5. 计算每个成分的熵贡献: sqrt(lambda) * phi^T * 1 ones_vec np.ones(n) entropy_contrib np.sqrt(np.maximum(eigenvalues, 0)) * (eigenvectors.T ones_vec) # 6. 按熵贡献绝对值降序排序 contrib_order np.argsort(np.abs(entropy_contrib))[::-1] # 7. 取前 n_components 个成分构造投影矩阵 selected contrib_order[:n_components] # 投影矩阵: 训练样本在特征空间中的表示 Phi_train eigenvectors[:, selected] * np.sqrt(np.maximum(eigenvalues[selected], 1e-12)) return { K_centered: K_centered, eigenvalues: eigenvalues, eigenvectors: eigenvectors, entropy_contrib: entropy_contrib, selected_idx: selected, Phi_train: Phi_train, sigma: sigma, X_train: X_train }逻辑说明第一步构造核矩阵时只用了X_train这是训练/测试分离的第一道关口。第二步中心化是必须的否则核矩阵的熵计算会被均值偏移污染。第三步用eigh而不是eig因为中心化后的核矩阵是对称半正定的eigh数值稳定性更好返回的特征值自动升序省得自己排。第五步的熵贡献公式是 KECA 的核心注意这里用的是sqrt(eigenvalues)而不是eigenvalues这是和 KPCA 排序的唯一区别但结果差异可能很大。参数说明sigma是 RBF 核带宽这个参数对结果影响极大后面单独讲怎么选。n_components决定保留几个成分一般看熵贡献的累计占比到 85%95% 就够了不用硬凑。2.3 测试集投影用训练阶段的参数不重新分解测试集的处理方式和训练集完全不同。测试样本不能重新构造核矩阵再做特征值分解而是用训练阶段确定的投影方向把测试样本映射过去。具体做法是计算测试样本与训练样本之间的核矩阵然后用训练阶段保存的特征向量和特征值做投影。def keca_transform(X_test, model): 将测试集投影到训练阶段确定的 KECA 空间中。 X_test: (n_test, d) 测试样本 model: keca_fit 返回的模型字典 X_train model[X_train] sigma model[sigma] selected model[selected_idx] eigenvectors model[eigenvectors] eigenvalues model[eigenvalues] n_train X_train.shape[0] # 1. 计算测试样本与训练样本之间的核矩阵 sq_dists np.sum(X_test**2, axis1, keepdimsTrue) \ np.sum(X_train**2, axis1) - 2 * X_test X_train.T K_test np.exp(-sq_dists / (2 * sigma**2)) # 2. 用训练阶段的中心化参数处理测试核矩阵 # 注意中心化用的均值必须来自训练集 one_n np.ones((n_train, n_train)) / n_train K_test_centered K_test - one_n K_test - K_test one_n one_n K_test one_n # 3. 投影到训练阶段选定的成分上 Phi_test K_test_centered eigenvectors[:, selected] / \ np.sqrt(np.maximum(eigenvalues[selected], 1e-12)) return Phi_test逻辑说明测试集投影的关键在于「不重新做特征值分解」。eigenvectors和eigenvalues全部来自训练阶段测试集只负责计算核矩阵然后投影。第二步的中心化操作容易出错——有人直接用测试集自己的均值做中心化这是错的必须用训练集的中心化结构。第三步的除法是 KPCA/KECA 投影的标准形式把核矩阵映射到特征向量张成的空间。参数说明这里没有新参数所有参数都继承自训练阶段。如果你发现测试集的投影结果和训练集分布差异很大先检查sigma是否一致再检查中心化步骤有没有用错均值。3. 训练/测试分离的工程实现从数据划分到结果验证3.1 数据划分策略什么时候该用分层采样训练/测试分离的第一步是划分数据。随机划分在类别均衡的数据集上没问题但如果你的数据有类别不平衡或者多工况随机划分可能导致测试集里某些类完全缺失。常见做法是分层采样stratified sampling保证训练集和测试集的类别比例一致。from sklearn.model_selection import train_test_split # 假设 X 是特征矩阵y 是标签 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, # 测试集占 30% stratifyy, # 按标签分层 random_state42 # 固定随机种子保证可复现 ) # 如果数据有工况标签也可以按工况分层 # stratify 可以传入组合标签比如工况故障类型的组合逻辑说明stratifyy保证训练集和测试集中各类别的比例与原始数据一致。random_state固定后每次运行划分结果相同方便调试和对比。如果你的数据有时间顺序比如过程工业的时序数据不能随机划分必须按时间切分——用前 70% 时间的数据训练后 30% 测试否则会引入未来信息泄漏。参数说明test_size一般取 0.20.3数据量少的时候可以取 0.30.4 保证测试集有足够样本。random_state建议固定不然每次跑出来的指标波动会让你怀疑人生。3.2 核参数 sigma 的选择训练集上交叉验证不碰测试集RBF 核的带宽sigma是 KECA 里最玄学的参数。太小会导致核矩阵接近单位矩阵所有样本都「孤立」熵贡献排序失去意义太大会让核矩阵接近全 1 矩阵所有样本都「混在一起」降维后区分度极低。常见做法是在训练集上做交叉验证用某个指标比如降维后的分类准确率、聚类轮廓系数、重构误差来选。from sklearn.model_selection import KFold from sklearn.svm import SVC from sklearn.metrics import accuracy_score def select_sigma(X_train, y_train, sigma_candidates, n_components10): 在训练集上用 5 折交叉验证选择最优 sigma。 注意整个过程只使用训练集测试集不参与。 kf KFold(n_splits5, shuffleTrue, random_state42) best_sigma None best_score -np.inf for sigma in sigma_candidates: scores [] for train_idx, val_idx in kf.split(X_train): X_tr, X_val X_train[train_idx], X_train[val_idx] y_tr, y_val y_train[train_idx], y_train[val_idx] # 在训练折上拟合 KECA model keca_fit(X_tr, sigmasigma, n_componentsn_components) # 投影验证折 Phi_val keca_transform(X_val, model) Phi_tr model[Phi_train] # 用最近邻分类器评估降维质量 # 这里只是示例实际可以用其他评估方式 from sklearn.neighbors import KNeighborsClassifier knn KNeighborsClassifier(n_neighbors3) knn.fit(Phi_tr, y_tr) y_pred knn.predict(Phi_val) scores.append(accuracy_score(y_val, y_pred)) mean_score np.mean(scores) if mean_score best_score: best_score mean_score best_sigma sigma return best_sigma, best_score逻辑说明交叉验证的每一折都在训练折上拟合 KECA然后在验证折上投影和评估。sigma的候选值一般取训练集样本间距离的中位数或者分位数比如[0.1*median_dist, 0.5*median_dist, 1.0*median_dist, 2.0*median_dist]。评估指标可以用降维后的分类准确率也可以用聚类指标取决于你的下游任务。参数说明sigma_candidates不要取太多58 个候选值就够了太多会过拟合验证集。n_components在选sigma时可以固定选完sigma后再单独调n_components。3.3 用 T² 和 SPE 统计量做故障检测的验证如果你的 KECA 是用在过程监控或故障检测上降维之后通常要构造 T² 和 SPE平方预测误差统计量来监控。T² 衡量样本在主成分子空间内的波动SPE 衡量残差子空间的变化。这两个统计量的控制限必须用训练集数据确定然后用测试集验证。def compute_monitoring_stats(Phi_train, Phi_test, n_components): 计算训练集和测试集的 T² 和 SPE 统计量。 Phi_train: 训练集投影 (n_train, n_components) Phi_test: 测试集投影 (n_test, n_components) # T² 统计量样本在主成分子空间中的马氏距离平方 cov_train np.cov(Phi_train, rowvarFalse) cov_inv np.linalg.pinv(cov_train) T2_train np.array([phi cov_inv phi.T for phi in Phi_train]) T2_test np.array([phi cov_inv phi.T for phi in Phi_test]) # SPE 统计量需要重构误差这里用投影前后的差异近似 # 实际实现中需要保留更多成分来计算残差 # 这里仅示意 T² 的计算方式 # 控制限用训练集的 99% 分位数 t2_limit np.percentile(T2_train, 99) return { T2_train: T2_train, T2_test: T2_test, t2_limit: t2_limit }逻辑说明T² 统计量的控制限来自训练集的 99% 分位数或者用 F 分布的理论值测试集的 T² 超过这个限就报警。SPE 的计算需要保留残差子空间实现上比 T² 复杂一些核心思路是用全部成分减去保留的成分得到残差。注意控制限只能用训练集确定用测试集确定控制限等于作弊。参数说明控制限的置信水平一般取 95% 或 99%取决于你对误报和漏报的容忍度。故障检测场景下99% 更常用因为误报的代价通常比漏报低。4. 避坑与排查KECA 训练/测试分离中最容易翻车的 5 个地方4.1 坑一核矩阵用了全体样本测试集信息泄漏现象训练集和测试集的降维结果看起来都很漂亮分类准确率 95% 以上但换一批新数据测试时性能断崖式下跌。原因构造核矩阵时用了X_all而不是X_train导致测试样本的统计信息参与了特征值分解和投影方向确定。这是最隐蔽的泄漏因为代码看起来「能跑」指标也「好看」。解决核矩阵构造、中心化、特征值分解、熵贡献排序这四步全部只用训练集。测试集只做投影不参与任何分解操作。检查代码里有没有np.concatenate或者X_all出现在keca_fit里。4.2 坑二中心化步骤用了测试集自己的均值现象测试集投影后的分布和训练集投影后的分布对不上T² 统计量在测试集上普遍偏高或偏低。原因测试集核矩阵的中心化用了测试集自己的均值而不是训练集的均值结构。核矩阵的中心化是一个全局操作训练阶段确定的中心化参数必须原样应用到测试集。解决在keca_fit里保存训练集的中心化矩阵或者保存训练核矩阵的行均值在keca_transform里用同样的参数处理测试核矩阵。不要重新计算测试集的均值。4.3 坑三sigma 在全体数据上选而不是训练集上选现象交叉验证选出来的sigma在训练集上表现很好但测试集上的降维效果很差。原因sigma的选择过程用了测试集数据导致参数过拟合到测试集。虽然最终模型没直接用测试集训练但参数选择阶段已经「偷看」了测试集。解决sigma的选择、n_components的确定、控制限的计算全部在训练集内部完成。测试集只在最终评估时用一次不要反复用测试集调参。4.4 坑四特征值分解用了 eig 而不是 eigh出现复数特征值现象np.linalg.eig返回的特征值里有复数排序和熵贡献计算全部乱掉。原因中心化后的核矩阵理论上是对称半正定的但数值误差可能让它出现极小的负特征值。eig不利用对称性可能返回复数结果。解决用scipy.linalg.eigh或者np.linalg.eigh这两个函数专门处理对称矩阵返回实数特征值。如果出现负特征值用np.maximum(eigenvalues, 0)截断。4.5 坑五测试集样本量太少核矩阵中心化不稳定现象测试集只有几十个样本时投影结果波动很大每次运行都不一样。原因测试集核矩阵的中心化需要用到训练集的均值结构但如果测试集样本太少核矩阵本身的估计就不稳定。解决测试集样本量至少是特征维度的 510 倍如果达不到考虑用滑动窗口或者 bootstrap 方法增加稳定性。另外测试集的投影不需要重新中心化直接用训练阶段的参数即可。5. 进阶技巧用熵贡献累计占比自动确定成分数5.1 熵贡献累计占比的计算与阈值选择n_components不用手动试可以用熵贡献的累计占比来自动确定。具体做法是计算所有成分的熵贡献绝对值排序后计算累计和取累计占比达到阈值比如 90%的最小成分数。def auto_select_components(entropy_contrib, threshold0.90): 根据熵贡献累计占比自动确定保留的成分数。 entropy_contrib: keca_fit 返回的熵贡献数组 threshold: 累计占比阈值默认 90% contrib_abs np.abs(entropy_contrib) sorted_contrib np.sort(contrib_abs)[::-1] cumulative np.cumsum(sorted_contrib) / np.sum(sorted_contrib) n_components np.searchsorted(cumulative, threshold) 1 return n_components, cumulative逻辑说明np.searchsorted找到累计占比首次超过阈值的位置加 1 是因为索引从 0 开始。返回的cumulative数组可以用来画累计贡献曲线直观看到需要多少成分。参数说明threshold一般取 0.850.95。故障检测场景下取 0.90 比较稳降维可视化场景下取 0.85 就够了。阈值越高保留的成分越多降维效果越好但计算量越大。5.2 和 KPCA 的对比验证什么时候 KECA 真的更好不是所有场景下 KECA 都优于 KPCA。我的一般做法是在训练集上同时跑 KECA 和 KPCA用同样的sigma和n_components比较降维后的分类准确率或者聚类指标。如果 KECA 的优势不明显差距小于 2%直接用 KPCA 就行毕竟 KPCA 的实现更成熟、计算更稳定。KECA 真正有优势的场景是数据分布明显非高斯、多模态或者故障信号在方差上不显著但在分布形状上有变化。比如化工过程的微小故障、轴承的早期损伤这些场景下 KECA 的熵判据比方差判据更敏感。5.3 我踩过的一个坑忘了保存训练阶段的模型参数最后说一个血泪教训。我第一次做 KECA 的时候训练完模型直接跑测试集结果发现测试集的投影结果完全不对。排查了半天才发现keca_fit返回的模型字典里没有保存sigma和中心化参数keca_transform里用的是默认值。后来我养成了一个习惯所有训练阶段的参数——sigma、selected_idx、eigenvectors、eigenvalues、训练集的均值结构——全部打包成一个字典或者用joblib保存到磁盘。测试的时候直接加载不重新计算任何训练阶段的统计量。这个习惯看起来简单但在实际项目里能省掉大量排查时间。尤其是当你需要复现几个月前的实验结果时没有保存模型参数基本等于重新做一遍。希望帮到你。本文还有配套的精品资源点击获取
返回列表