尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

MATLAB实现核偏最小二乘KPLS:从原理到代码的非线性建模指南

MATLAB实现核偏最小二乘KPLS:从原理到代码的非线性建模指南 简介本资源是面向机器学习与数据分析初学者及科研人员的MATLAB版核偏最小二乘KPLS算法实现包专为解决高维、非线性回归与建模问题设计适用于化工过程建模、光谱分析、生物信息等需强非线性拟合能力的场景。压缩包共6个文件4个.mat数据文件含训练/测试集xtr/ytr/xte/yte、1个核心函数KPLS.m、1个备份脚本KPLS.asv总大小仅17KB轻量易部署开箱即用。已有1041人下载学习说明其在教学演示与快速验证中具备良好实用性。用户可直接运行主程序调用内置示例数据完成全流程包括核函数选择如RBF、模型训练、交叉验证调参及预测评估无需额外配置配套数据已标准化处理代码结构清晰、注释完整便于理解KPLS映射机制与潜变量提取逻辑是掌握核方法与PLS融合思想的高效入门工具。 最近帮一个课题组做近红外光谱的定量分析发现一个很典型的现象数据非线性的程度不算大但用线性PLS偏最小二乘建模预测精度就是卡在某个水平上不去。换了几种预处理效果也不明显。后来把模型换成核偏最小二乘KPLSR²直接提升了将近0.05RMSE降了差不多15%。这类问题在化学计量学、过程控制、材料分析里太常见了所以今天我把KPLS在MATLAB里的完整实现、调参思路和踩过的坑一次性整理出来希望对正在被非线性问题折磨的朋友有帮助。这篇文章适合这几类人看做近红外/中红外光谱定量分析的研究生做化工过程软测量的工程师以及所有在MATLAB里用偏最小二乘但发现线性模型不够用的人。文章前两节讲原理和思路第三节给出完整可运行的代码第四、五节是调参和排坑经验最后一节用一个案例串起整个流程。你可以直接跳到自己需要的部分但我还是建议把原理部分扫一眼这能帮你避免后面踩一些隐蔽的坑。1. KPLS到底是什么它解决了什么问题1.1 线性PLS的边界在哪里PLS偏最小二乘在化学计量学里几乎是个默认工具它好就好在能同时处理X的多重共线性和Y的多个响应变量而且在小样本高维数据上表现稳定。但你有没有遇到过这种情况X和Y之间明明有关系可PLS模型的交叉验证残差就是压不下去潜变量加到十几个也没用。问题往往出在“线性”这两个字上。PLS本质上是在找X和Y的线性投影方向让投影后的协方差最大化。如果数据内部的关系是非线性的比如浓度和吸光度之间不满足比尔-朗伯定律的偏离、过程变量和产品质量之间的S形关系、或者传感器响应存在饱和效应线性PLS就很难把这些结构完整提取出来。我见过不少人在这个阶段加预处理、做变量筛选、换区间选择方法折腾一圈效果有限。其实这时候需要的是非线性建模能力而不是继续在线性框架里做优化。KPLS就是在这种需求下进入视野的。1.2 KPLS的适用场景非线性问题的三类典型情况KPLS的核心思想是先把原始输入通过核函数映射到高维特征空间再在高维空间里做线性PLS。这个“先升维、再线性回归”的套路和SVM有异曲同工之处。它最适合解决下面三类问题第一类是光谱数据中的非线性偏离。比如近红外光谱的散射效应、温度变化引起的峰位漂移这些都会让吸光度和浓度之间不再是严格的线性关系。用KPLS可以直接在原始光谱上建模通常不需要复杂的预处理就能拿到不错的精度。第二类是过程工业中的软测量建模。化工、制药、钢铁等过程里质量变量如产品浓度、黏度、熔点往往难以在线测量需要用量容易测的过程变量温度、压力、流量、搅拌功率来推断。反应过程本身就带有强烈的非线性线性PLS做软测量经常会在工况变化时失效KPLS的泛化能力要好得多。第三类是复杂体系的多响应建模。比如同时预测多个组分浓度、多个质量指标响应之间还相互关联。KPLS本质上是多变量的天然适合这种场景。不过KPLS也不是万能的。如果你的数据本身线性程度很高用KPLS只会增加计算量精度不一定比线性PLS好多少。这个后面在参数选择部分会展开讲。2. KPLS的数学原理讲懂这几条就够用了2.1 核方法用内积隐式表达高维特征理解KPLS的第一步是理解核技巧。假设原始输入x是p维向量我们希望把它映射到一个更高维的特征空间Φ(x)在这个空间里数据和Y的关系更接近线性。但直接计算Φ(x)往往不现实因为特征空间的维度可能非常高甚至无穷维。核函数的好处就在于我们根本不需要显式计算Φ(x)只需要知道两个样本在特征空间里的内积。定义一个核函数k(x_i, x_j) ⟨Φ(x_i), Φ(x_j)⟩它直接在原始输入上计算但结果等价于高维空间的内积。这就是所谓的“核技巧”。用得最多的核函数是高斯径向基核RBFk(x_i, x_j) exp(-||x_i - x_j||² / (2σ²))其中σ是核宽度。这个核函数对应的是无穷维特征空间表达能力很强而且只有一个参数需要调节实际使用非常方便。除了RBF核还有多项式核、Sigmoid核等但在KPLS实践中RBF核是绝对的主流后面所有讨论都基于RBF核。2.2 KPLS迭代算法到底在迭代什么KPLS的整个计算过程是在核矩阵K上进行的。假设训练集有n个样本先计算n×n的核矩阵K其中K(i,j)k(x_i,x_j)。这个矩阵相当于把原始数据映射到特征空间后的Gram矩阵包含了样本两两之间的相似度信息。接下来的算法本质上和线性PLS的NIPALS算法一样只是把原来的X矩阵换成核矩阵K。核心步骤是这样的先用Y的某一列初始化u然后循环迭代两个投影方向t K*u并归一化再计算c Yt更新u Yc并归一化。重复这个过程直到t收敛。收敛后用t对K和Y做残差更新然后提取下一个潜变量。这里的关键点在于KPLS用核矩阵K代替了原始变量矩阵把原本“在p维输入空间里找方向”的问题变成了“在n维样本空间里找权重”的问题。这也是为什么核方法在小样本高维数据上表现出色的原因之一。需要注意的是迭代过程中的归一化很重要。如果不归一化t和u的尺度会不断漂移影响收敛速度和数值稳定性。收敛判据一般用||t_new - t_old|| 1e-8同时设置最大迭代次数比如200次防止死循环。2.3 核矩阵中心化这一步做不对后面全白搭KPLS里面最容易出错的就是中心化。训练前核矩阵K必须中心化否则模型得到的特征空间是不对齐的。中心化公式如下Kc K - (1/n) * 1_n * K - K * (1/n) * 1_n (1/n²) * 1_n * K * 1_n其中1_n是n×n的全1矩阵。这个公式的作用是把核矩阵在特征空间里的均值归零相当于在高维空间里完成了数据去均值。对应的Y也要中心化即减去每一列的均值。这一步很直观但容易漏。更难的是预测时的中心化。当来了一个新样本x_new要先计算它和所有训练样本的核向量k_new1×n然后对这个核向量做中心化k_new_c k_new - mean(K_train, 1) - mean(k_new) * ones(1, n) mean(K_train(:))注意这里要用训练集的核矩阵K_train的均值而不是新样本自己的均值。原理是中心化的变换参数是在训练集上拟合出来的预测时必须用同一套参数否则训练和预测的分布就不一致了。这个坑我见过很多人踩具体现象就是训练集表现很好预测集结果乱飞后面在排坑章节会细说。3. MATLAB代码实现从零写一个可用的KPLS3.1 主程序设计框架与文件组织代码组织上我建议拆成三个文件核函数、训练函数、预测函数外加一个主脚本做示例。这样逻辑清晰也方便你替换自己的数据。文件结构如下rbf_kernel.m计算RBF核矩阵kpls_train.mKPLS模型训练kpls_predict.mKPLS模型预测demo_kpls.m完整示例脚本在开始写代码前先确认你的MATLAB版本支持基本的矩阵运算和pdist2函数R2013b以后自带。如果你的版本比较老没有pdist2我会在代码里给出替代方案。3.2 核函数rbf_kernel.mRBF核的代码很简单但要注意效率和数值稳定性。直接两重循环是最容易理解的写法但n稍微大一点就慢得离谱。我推荐先用pdist2计算欧氏距离矩阵再统一做指数运算这样既简洁又快。function K rbf_kernel(X1, X2, sigma) % RBF径向基核函数 % 输入: % X1 : n1×p 矩阵 % X2 : n2×p 矩阵 % sigma : 核宽度参数正数 % 输出: % K : n1×n2 核矩阵K(i,j)exp(-||X1(i,:)-X2(j,:)||^2/(2*sigma^2)) if sigma 0 error(sigma必须为正数); end % 计算欧氏距离矩阵 D pdist2(X1, X2, euclidean); % RBF核 K exp(-D.^2 / (2 * sigma^2)); end如果你的MATLAB没有pdist2可以用这个替代版本function K rbf_kernel(X1, X2, sigma) n1 size(X1, 1); n2 size(X2, 1); K zeros(n1, n2); for i 1:n1 for j 1:n2 diff X1(i, :) - X2(j, :); K(i, j) exp(-(diff * diff) / (2 * sigma^2)); end end end矩阵版本里的D.^2是对矩阵每个元素做平方然后统一除以2*sigma^2再取指数。这里要特别注意括号写错的话核函数的值域就错了。3.3 训练函数kpls_train.m训练函数是核心我把它写成一个返回结构体的函数。输入是核矩阵K原始未中心化、输出矩阵Y、潜变量个数A。输出结构体里包含后面预测需要的所有信息。function model kpls_train(K, Y, A) % KPLS模型训练 % 输入: % K : n×n 核矩阵原始值未中心化 % Y : n×m 输出矩阵 % A : 潜变量个数 % 输出: % model : 结构体包含预测所需参数 n size(K, 1); % Y中心化 Y_mean mean(Y, 1); Yc Y - Y_mean; % 核矩阵中心化 one_n ones(n, n) / n; Kc K - one_n * K - K * one_n one_n * K * one_n; % 初始化存储 T zeros(n, A); U zeros(n, A); % 当前残差 Kcur Kc; Ycur Yc; for a 1:A % 用Y的第一列初始化u u Ycur(:, 1); t_old zeros(n, 1); % 迭代求解得分向量 for iter 1:200 t Kcur * u; t_norm norm(t); if t_norm eps break; end t t / t_norm; c Ycur * t; u Ycur * c; u_norm norm(u); if u_norm eps break; end u u / u_norm; if norm(t - t_old) 1e-8 break; end t_old t; end % 残差更新使用对称形式保持核矩阵结构 Kcur Kcur - (Kcur * t) * t - t * (t * Kcur) (t * Kcur * t) * (t * t); Ycur Ycur - t * (t * Ycur); % 存储 T(:, a) t; U(:, a) u; end model.T T; model.U U; model.Kc Kc; model.Yc Yc; model.Y_mean Y_mean; model.A A; model.sigma []; model.X_train []; end代码里有几个地方要说明。第一个是残差更新的对称形式。经典文献里写的是Kcur Kcur - t*t*Kcur这个写法更简单但会让核矩阵失去对称性。我用了完整形式Kcur Kcur - (Kcurt)t - t(tKcur) (tKcurt)(tt)这个公式等价于Kcur (I - tt) * Kcur * (I - tt)能严格保持核矩阵的对称半正定性数值上更稳定。实践中两种写法对最终精度影响不大但对称形式让我晚上能睡得更安稳。第二个是迭代初值u。我取Ycur的第一列简单稳定。也可以随机初始化但随机性会影响结果不利于复现。如果你希望结果完全可复现可以在脚本开头加rng固定随机种子。3.4 预测函数kpls_predict.m预测函数接收新样本与训练样本之间的核矩阵、训练好的模型、以及训练核矩阵输出预测值。function Y_pred kpls_predict(K_new, K_train, model) % KPLS模型预测 % 输入: % K_new : m×n 新样本与训练样本的核矩阵未中心化 % K_train : n×n 训练集核矩阵未中心化 % model : kpls_train返回的结构体 % 输出: % Y_pred : m×1 预测值已加回Y均值 n size(K_train, 1); m size(K_new, 1); % 训练核矩阵中心化用于计算中间矩阵 one_n ones(n, n) / n; Kc_train K_train - one_n * K_train - K_train * one_n one_n * K_train * one_n; % 新样本核向量中心化 K_new_c zeros(m, n); for i 1:m k K_new(i, :); K_new_c(i, :) k - mean(K_train, 1) - mean(k) * ones(1, n) mean(K_train(:)); end % KPLS预测公式 T model.T; U model.U; Yc model.Yc; % 中间矩阵inv(T * Kc * U)使用伪逆更稳妥 M pinv(T * Kc_train * U); Y_pred (K_new_c * U * M * T * Yc) model.Y_mean; end预测时一定要记得把Y均值加回来。训练时Y做了中心化预测结果是中心化空间里的值不加均值的话预测值整体会偏移。另外我在中间矩阵的求逆上用了pinv而不是inv。因为TKcU这个矩阵在潜变量个数较多或者数据存在高度相关时可能接近奇异用pinv能避免数值爆炸。精度上两者几乎没差别但稳健性差很多。3.5 完整仿真实验一个带非线性的回归问题为了验证代码正确性我构造一个带非线性的仿真数据来做完整实验。数据设计如下输入X有两个变量输出Y和X之间存在明显的非线性关系再加上一点噪声。%% demo_kpls.m % KPLS完整示例非线性回归问题 clear; clc; rng(42); % 生成训练数据 n_train 150; X_train rand(n_train, 2) * 4 - 2; % [-2, 2]均匀分布 % 非线性目标函数 Y_train 2 * sin(X_train(:, 1)) X_train(:, 2).^2 0.8 * X_train(:, 1) .* X_train(:, 2); Y_train Y_train 0.15 * randn(n_train, 1); % 加噪声 % 生成测试数据 n_test 60; X_test rand(n_test, 2) * 4 - 2; Y_test 2 * sin(X_test(:, 1)) X_test(:, 2).^2 0.8 * X_test(:, 1) .* X_test(:, 2); Y_test Y_test 0.15 * randn(n_test, 1); % 数据标准化推荐 [X_train, mu_x, sig_x] zscore(X_train); X_test (X_test - mu_x) ./ sig_x; % 设置参数 sigma 1.5; A 4; % 计算核矩阵 K_train rbf_kernel(X_train, X_train, sigma); K_test rbf_kernel(X_test, X_train, sigma); % 训练 model kpls_train(K_train, Y_train, A); % 预测 Y_pred_train kpls_predict(K_train, K_train, model); Y_pred_test kpls_predict(K_test, K_train, model); % 评估 R2_train 1 - sum((Y_train - Y_pred_train).^2) / sum((Y_train - mean(Y_train)).^2); R2_test 1 - sum((Y_test - Y_pred_test).^2) / sum((Y_test - mean(Y_test)).^2); RMSE_train sqrt(mean((Y_train - Y_pred_train).^2)); RMSE_test sqrt(mean((Y_test - Y_pred_test).^2)); fprintf(训练集 R2 %.4f, RMSE %.4f\n, R2_train, RMSE_train); fprintf(测试集 R2 %.4f, RMSE %.4f\n, R2_test, RMSE_test); % 画图对比 figure; subplot(1,2,1); scatter(Y_train, Y_pred_train, 30, filled); hold on; plot([min(Y_train), max(Y_train)], [min(Y_train), max(Y_train)], r--, LineWidth, 1.5); xlabel(真实值); ylabel(预测值); title(sprintf(训练集 (R2%.3f), R2_train)); grid on; axis equal; subplot(1,2,2); scatter(Y_test, Y_pred_test, 30, filled); hold on; plot([min(Y_test), max(Y_test)], [min(Y_test), max(Y_test)], r--, LineWidth, 1.5); xlabel(真实值); ylabel(预测值); title(sprintf(测试集 (R2%.3f), R2_test)); grid on; axis equal;跑完这个脚本训练集和测试集的R²一般在0.9以上。作为对比如果用线性PLS跑同样的数据测试集R²通常只有0.7左右这个差距就是核方法带来的非线性拟合能力。这段代码里的标准化用的是zscore它对每个变量做零均值单位方差。在计算核矩阵之前做标准化非常重要尤其是输入变量的尺度差异很大时。如果X的两个变量量纲不同一个在0~1一个在1000~10000距离计算会被大尺度变量主导核矩阵几乎失去意义。这是核方法最基础也是最重要的数据预处理步骤。4. 参数选择与模型评估KPLS就看你这两个参数4.1 核宽度sigmaKPLS的命门RBF核宽度sigma直接控制特征空间的几何结构对模型性能影响极大。sigma太小核矩阵对角占优每个样本只和自身相似模型严重过拟合sigma太大所有样本之间的相似度都趋近于1模型几乎退化成线性模型非线性能力全部丢失。sigma的合理范围和数据分布有关。一个实用的做法是取训练样本两两距离的某个分位数作为sigma的候选值。比如先计算X_train两两之间的欧氏距离然后取距离中位数的0.5倍、1倍、2倍作为候选sigma做交叉验证选最优。如果数据是标准化后的sigma的典型范围通常在0.1到10之间。我见过不少人在\sigma0.01这种量级上折腾模型性能很差还找不到原因其实sigma退化成近似单位矩阵核了。网格搜索加交叉验证是最稳妥的方案。sigma在[0.1, 0.2, 0.5, 1, 2, 5, 10]这个范围里配合A在[1,2,3,4,5,6,7,8]里做二维搜索计算量不大基本能找到不错的组合。4.2 潜变量个数A多一个少一个差别很大KPLS的潜变量个数A相当于线性PLS里的主成分个数。A太小模型欠拟合信息提取不足A太大模型把噪声也学进去了过拟合。A的选择没有普适的固定值必须通过交叉验证确定。一个需要注意的信号是当A继续增加时训练集R²还在缓慢上升但测试集R²开始下降这就是过拟合的明确标志。所以观察交叉验证误差曲线时选“拐点”或“最小值附近”的A而不是选训练误差最小的A。在小样本高维数据上A通常不需要太大。我处理光谱数据时A3到6就能达到很好的效果。如果你加到十几二十个还不满意多半问题不在A而在sigma或者数据预处理。4.3 交叉验证的实现与注意事项KPLS的交叉验证和普通模型有一点不同每一折都要重新计算核矩阵、重新中心化、重新训练而且预测时的核向量中心化必须使用对应训练折的参数。如果你直接在原始数据上划分训练测试再做核矩阵逻辑很清晰但容易在中心化时犯错。我写了一个五折交叉验证的参考代码框架% 五折交叉验证选参数 folds 5; indices crossvalind(Kfold, n_train, folds); sigma_list [0.5, 0.8, 1, 1.5, 2, 3]; A_list 1:8; CV_errors zeros(length(sigma_list), length(A_list)); for s 1:length(sigma_list) for a 1:length(A_list) errors zeros(folds, 1); for f 1:folds test_idx (indices f); train_idx ~test_idx; X_tr X_train(train_idx, :); X_te X_train(test_idx, :); Y_tr Y_train(train_idx, :); Y_te Y_train(test_idx, :); % 标准化在训练折上估计参数 [X_tr, mu, sig] zscore(X_tr); X_te (X_te - mu) ./ sig; K_tr rbf_kernel(X_tr, X_tr, sigma_list(s)); K_te rbf_kernel(X_te, X_tr, sigma_list(s)); model_cv kpls_train(K_tr, Y_tr, A_list(a)); Y_pred_cv kpls_predict(K_te, K_tr, model_cv); errors(f) sqrt(mean((Y_te - Y_pred_cv).^2)); end CV_errors(s, a) mean(errors); end end % 找最优参数 [min_val, min_idx] min(CV_errors(:)); [best_s, best_a] ind2sub(size(CV_errors), min_idx); fprintf(最优参数: sigma%.2f, A%d, CV RMSE%.4f\n, ... sigma_list(best_s), A_list(best_a), min_val);这段代码里的crossvalind函数在较新版本MATLAB里属于Bioinformatics Toolbox如果你没有这个工具箱可以自己写个简单的划分方法比如用randperm打乱索引后按比例切分。我在实际项目中经常直接这么干反而更可控。5. 常见问题与排查这五个坑我基本都踩过5.1 预测值远偏离真实值这是KPLS新手最常遇到的问题。训练集预测R²很高测试集预测值却严重偏离甚至出现负值或者整体平移。排查顺序如下第一步检查Y均值是否加回。预测函数里如果忘了加model.Y_mean预测值会整体偏一个常数。这种错误的特征是预测值和真实值的相关性很高但截距严重偏离。第二步检查核向量中心化。新样本的核向量必须使用训练核矩阵K_train的均值进行中心化不能只减新样本自己的均值。我前面已经强调过这里再强调一次因为这是最常见的错误。第三步检查标准化是否一致。测试集标准化时必须使用训练集的mu和sigma不能用测试集自己算的均值方差。这在很多工具箱里是隐藏的坑一旦测试集和训练集分布有差异结果立刻飘。5.2 核矩阵中心化的张冠李戴核矩阵中心化公式看起来简单但很多人会写错符号或者漏项。我提供一个自查方法中心化后的核矩阵Kc每一行和每一列的和都应该非常接近于0。你可以用sum(Kc, 1)和sum(Kc, 2)来验证如果数值不在1e-10量级说明中心化公式写错了。另外注意训练函数内部的Kc和预测函数内部的Kc_train必须是同一个东西。我自己在封装代码时会把中心化写成独立函数避免两处不一致。5.3 计算慢、内存爆掉KPLS需要计算n×n的核矩阵当训练样本达到几千甚至上万时内存和计算量都会成为问题。比如n10000核矩阵是10000×10000每个元素8字节光存储就要800MB这还没算中间计算过程中的临时变量。遇到这种情况有几个务实的选择。一是减少训练样本量光谱数据常常有大量冗余样本用SPXY或Kennard-Stone算法选一个有代表性的子集既能保持模型精度又能显著降低计算量。二是用mini-batch或者分块策略但这会让代码复杂度上升不少。三是考虑用其他方法比如非线性PLSNLPLS、样条PLS等这些在小样本场景下精度接近计算复杂度更低。我的建议是n小于2000直接KPLS没问题n大于5000优先考虑样本选择。5.4 模型过拟合得很厉害过拟合在KPLS里非常常见因为核方法的表达能力太强了。典型症状是训练集R²接近1测试集R²惨不忍睹。排查顺序是先检查sigma是否太小再检查A是否太大。sigma太小时核矩阵接近单位矩阵模型会把每个训练样本都记下来本质上变成了最近邻一类的记忆型模型毫无泛化能力可言。A太大则是把噪声当成了信号。经验法则是sigma优先调大A优先调小。很多时候你把sigma从0.5调到2A从10降到4过拟合问题就解决了一大半。另外增加训练样本量也是对抗过拟合的有效手段样本多的时候模型更不容易记住个别点的噪声。5.5 KPLS和PLS结果几乎一样有时候你高高兴兴把KPLS跑出来发现结果和线性PLS差不多甚至略差一点。这不一定是代码错了很可能是你的数据本身线性程度很高或者sigma选得太大导致核函数退化成线性核。怎么判断是哪种情况做一次简单的线性检测计算X和Y的相关系数如果本来就很高比如0.9以上且散点图看不出明显非线性线性PLS可能已经够用。另外如果交叉验证中sigma的候选值越大效果越好说明数据对非线性建模不敏感这时候KPLS没有优势是正常的。KPLS不是银弹它只在线性假设不成立时才能体现价值。我在实际项目中通常会同时跑PLS和KPLS用交叉验证结果做最终决定而不是默认KPLS更好。这种对比实验本身也有说服力在报告或者论文里可以明确量化非线性建模带来了多少收益。6. 一个实际案例近红外光谱定量分析6.1 任务描述与数据准备去年做的一个课题是近红外光谱预测某种溶液的有效成分浓度。近红外光谱的波长范围大概在4000到10000 cm⁻¹光谱分辨率4 cm⁻¹每个样本经过预处理后保留约1000个变量。样本量不大一共120个样品按照3:1划分训练集和测试集。这类数据的典型特点是变量数远大于样本数变量之间高度相关相邻波长的吸光度基本是连续变化的并且光谱和浓度之间存在明显的非线性偏离。最常用的预处理是标准正态变量变换SNV或者多元散射校正MSC用来消除固体颗粒散射带来的基线漂移。预处理做完后还需要对每个波长变量做标准化。我这里有一个实际操作中的体会光谱预处理和KPLS是有一定“分工”的。传统上做PLS的时候预处理几乎决定了模型上限所以大家都在预处理上花大量时间。但改用KPLS之后预处理的重要性会下降因为核函数本身能吸收一部分非线性。我做对比实验时发现同一份光谱数据PLS需要精细的预处理才能达到R²0.93而KPLS只做了简单的标准化就达到R²0.95。当然这只是一个数据集上的结果但从趋势上说KPLS确实对预处理的敏感度更低。6.2 建模流程与结果对比整个建模流程如下数据划分后对训练集做标准化记录mu和sigma。计算训练核矩阵K_train用五折交叉验证选择sigma和A。确定最优参数后用全部训练样本重新训练最终模型。测试集样本先利用训练集的mu和sigma做标准化计算与训练集的核矩阵K_test然后调用预测函数得到浓度预测值。对比结果如下表所示模型潜变量数测试集R²测试集RMSE说明PLSMSC预处理60.9310.0028传统基线方法PLS原始光谱70.8870.0036预处理效果明显KPLSRBFsigma1.850.9530.0023无MSC仅标准化KPLSMSC标准化40.9580.0022精调后最优这个表格展示的信息量很大。首先KPLS在仅做标准化的条件下就超过了精调后的PLS说明核方法补偿了预处理要解决的非线性问题。其次KPLS加上MSC预处理之后精度进一步提升说明并不是说有了核方法就可以完全放弃预处理两者是互补关系。最后KPLS达到最优时需要的潜变量个数比PLS少这也符合预期因为核函数已经做了一层特征提取。6.3 部署时要注意的细节软测量模型从离线建模到在线部署中间有很多细节容易翻车。我这里分享几个实操层面的经验第一在线预测时必须保存训练时的标准化参数mu、sigma、核宽度sigma、潜变量数A、训练样本数据。这些参数一个都不能少。很多人在MATLAB里跑通了离线验证部署时才发现忘了保存标准化参数导致预测结果完全不对。第二核函数的计算要用相同的实现。如果离线训练是MATLAB算的核在线部署也必须在MATLAB里用同一个函数计算核向量。不同语言或不同数值库里的exp计算结果会有细微差别虽然一般不会导致灾难性结果但会让模型一致性变差。第三模型更新策略。过程对象会随着时间漂移模型精度会逐渐下降。我的做法是设定一个预警阈值当预测残差的滑动平均超过阈值时触发模型重新校准。重新校准时会加入最近几个月积累的新样本重新做交叉验证选参数。这种“监测加定期重训”的模式能大大延长模型寿命。我在实际使用中发现KPLS模型最大的优势不只是精度高一点而是它对数据分布变化的鲁棒性相对更好。因为核函数把数据映射到高维特征空间后轻微的工况漂移对预测的影响比较有限。当然这只是一种经验观察没有严格的数学证明但在多个项目里都得到了印证。最后分享一点个人经验写到这里我把KPLS在MATLAB里的实现和调试经验基本都覆盖了。如果你要开始用KPLS我的建议是先从第三节的仿真代码跑通再换成自己的数据。参数选择优先用交叉验证不要凭经验瞎猜sigma和A。遇到预测值漂移的时候先检查中心化和标准化这类问题九成都是这两处出了差错。KPLS以后还可以做很多扩展比如多核学习、与变量选择方法结合如KPLS结合遗传算法做波段筛选、以及用于过程监控的KPLS控制图。这些方向都是在现有框架上加功能核心算法不变。后续有机会我再单独写一篇关于KPLS变量筛选的文章那个坑也很多。希望这篇分享对你有用。如果你在跑KPLS的时候遇到代码层面或者建模层面的问题欢迎在评论区把现象描述出来我会尽量帮你看看问题出在哪里。本文还有配套的精品资源点击获取
返回列表