CARS变量选择算法MATLAB实现:从原理到代码实战

CARS变量选择算法MATLAB实现:从原理到代码实战 简介这是一份基于MATLAB的CARS相干反斯托克斯拉曼散射算法实现代码包非常适合从事光谱分析、化学计量学和生物医学成像的科研人员及研究生学习与使用。代码完整覆盖了光场模拟、信号处理、数据校正、图像重建与定量分析等关键环节并特别加入了与PLS偏最小二乘法结合的特征选择模块可直接用于变量筛选或建立光谱-性质关联模型。压缩包共38个文件以32个.m源代码为主同时配有使用说明文档doc/pdf、示例数据.mat和示意图整包仅438KB下载后即可快速查看代码结构。目前已有363人学习下载。对于希望复现CARS算法流程、学习MATLAB光谱处理实现或在化学计量学中应用PLS-CARS做特征选择的读者这份代码包提供了从基础功能到进阶变体的完整参考配合manual文档可有效降低上手门槛。1. 拿到CARS matlab 代码 - 副本.rar后先搞清楚CARS是什么如果你在化学计量学、近红外光谱建模或者高维数据特征工程里混过一段时间大概率见过这个压缩包。CARS 不是汽车而是 Competitive Adaptive Reweighted Sampling竞争性自适应重加权采样。简单说这是一种基于偏最小二乘PLS回归系数的变量选择方法最早是为了解决光谱数据里“变量个数远多于样本数”的稀疏建模问题。核心思路挺直白把每个变量想象成跑道上的选手用蒙特卡洛采样做多次随机“分组淘汰”最后留下来的就是和响应值相关度最高的那一小组变量。这份“副本.rar”里装的通常不是算法论文而是别人整理好的 MATLAB 函数集比如 carspls.m、ksspls.m、mccv_pls.m 这类辅助文件。适合的人群是刚接触 CARS、想直接跑通一个最小示例的研究生或算法工程师已经熟悉原理的人更需要关注的则是怎么把这段代码正确解压、把路径配好、把参数调成自己的数据形态。下面先花点时间把算法原理和常见实现讲清楚再带你一步步跑出一个能出图的结果。2. CARS算法原理与核心参数从PLS回归系数到变量筛选2.1 为什么用CARS而不是SPA、UVE或LASSO光谱建模里变量筛选方法非常多SPA连续投影算法和UVE无信息变量消除常被拿来跟CARS做对比。SPA从根本上是在找一组方差尽可能大的变量组合它关心的是变量之间的投影关系却不直接关心变量和响应值的相关性UVE则把人工噪声加入原始矩阵观察回归系数分布来剔除不过关的变量但它的计算量在变量维度极高时会明显上涨而且对噪声幅度的选择很敏感。LASSO属于压缩估计确实是端到端的稀疏解但它的筛选结果高度依赖惩罚参数并且要先把数据标准化否则变量尺度不同会让回归系数失去可比性。CARS走的是另一条路它把“变量选择”和“模型预测能力”放在同一套蒙特卡洛框架里评价。每次抽样时从原始样本集中随机抽取一部分样本通常是80%用这部分子样本建立PLS模型取回归系数绝对值的相对大小作为变量权重来源。然后借助指数衰减函数EDF决定当前要强制淘汰多少变量再用自适应重加权采样ARS从剩余变量里按概率抽取下一次要参与建模的变量子集。换句话说CARS不是一次性给出一个最优变量组合而是每次迭代产生一个新的子集最后从历史迭代中挑出RMSECV最小的那一次作为最优结果。相比SPA和UVECARS把“变量子集”和“交叉验证误差”直接关联起来非常适合高相关、多噪声的红外光谱数据。2.2 算法流程中的四个关键步骤从实现角度看CARS主循环通常包含四个反复出现的模块。第一个模块是蒙特卡洛采样设定总的采样次数N常见为50或100每次从样本集里不放回地随机抽取固定比例的样本构成建模集。第二个模块是建立PLS模型并提取回归系数向量b然后计算每个变量的权重权重与|b|成正比同时记录当前模型的RMSECV。第三个模块是淘汰使用指数衰减函数确定本轮的保留变量数。EDF的保留率大体上是一个对数衰减曲线前期淘汰速度快后期淘汰速度慢这样可以兼顾粗筛和细选。第四个模块是重加权采样根据权重从现有变量中随机抽取下一轮变量集合抽中的变量进入下一轮没抽中的则永久剔除。这四个模块循环往复直到保留变量数低于某个阈值或者达到预设的迭代次数。最终输出的是每个迭代轮次对应的RMSECV、保留变量数量以及变量序号。常见实现里还会额外用独立测试集验证所选变量的效果以便和全谱PLS模型做对比。需要特别说明的是CARS内部的蒙特卡洛随机性意味着两次运行产生的变量集不一定完全相同这也是后面讲参数时重点要处理的点。2.3 必须理解的两个核心参数蒙特卡洛采样次数N和交叉验证折数KCARS相关代码里最常见的输入参数是N蒙特卡洛采样次数和K交叉验证折数。N决定你迭代多少轮一般取50或100。N太小变量淘汰过程过于仓促可能错过更优的子集N太大计算时间线性增长尤其在变量数过万的光谱数据上会非常痛苦。交叉验证折数K默认取5或10用于计算每轮PLS子模型的RMSECV。这里有个隐藏问题如果样本数很少比如只有40个样本用10折交叉验证会导致每个验证集只有4个样本RMSECV稳定性会变差。反之如果样本数超过几百5折和10折差距不大但计算量会明显增加。除了N和K之外还有三个不常出现在说明文档里的参数EDF最终保留比例通常为0.5%即最终保留变量数约等于原始变量数的0.5%、蒙卡采样的抽样比例常见为0.80到0.85、以及权重下限。下表是我自己在处理近红外数据时常用的一组参数参考。参数常见取值范围我常用的值说明N50200100迭代轮次根据变量数和计算时间折中K5105内部PLS模型的交叉验证折数抽样比例0.750.900.80每次蒙特卡洛采样的样本占比最终保留比例0.1%1%0.5%控制最终变量数上限重复运行次数1205用于观察结果稳定性不改变核心算法表格中的“重复运行次数”不是标准CARS参数但实践中很管用。建议在正式跑全谱数据之前先用一组相同参数重复运行三次观察所选变量序号的重合率。如果三次结果差异极大说明样本数太少或者数据噪声过大此时应该优先考虑增大抽样比例或N而不是直接信任单次结果。3. 在MATLAB中运行CARS代码解压、路径设置与最小示例3.1 解压副本.rar并处理文件目录结构MATLAB对zip格式可以直接用unzip但rar格式需要外部工具这是打开这个压缩包时最常见的第一个坎。Windows上我一般用7-Zip的命令行或WinRAR先把压缩包解压到一个没有中文名的路径比如D:\cars_lib。命令行方式如下C:\Program Files\7-Zip\7z.exe x D:\downloads\CARS matlab 代码 - 副本.rar -oD:\cars_lib -y如果不习惯命令行直接在资源管理器里右键解压也可以。但要注意解压出来的文件夹如果叫“CARS matlab 代码 - 副本”务必把它重命名为类似CARS_lib这样的纯英文目录。原因很实际MATLAB的addpath对中文字符和空格不是完全不能处理但后续如果要在脚本里写循环或生成报表很容易因为路径编码问题报Unsupported path错误。解压完成后检查目录下是否包含.m文件和可能的示例数据.mat或.xlsx。常见实现会附带carspls.m、pls.m或使用自带PLS工具箱的函数缺少依赖时会直接提示Undefined function or variable这个时候要先定位是主函数缺失还是依赖函数缺失。3.2 准备光谱数据从Excel读入X和yCARS的主函数典型调用语法是把光谱矩阵X和响应向量y传进去。X是n行p列n为样本数p为变量数y是n行1列代表浓度、硬度、粘度等指标的测量值。用MATLAB读Excel最稳妥的方法是readmatrix因为老旧的xlsread在R2023b之后的版本里仍可用但新代码没有必要继续依赖它。下面这段代码把两个sheet的数据读进来% 读取光谱数据和响应值假设sheet1为光谱Xsheet2为浓度y X readmatrix(data.xlsx, Sheet, 1); y readmatrix(data.xlsx, Sheet, 2); y y(:); % 强制转换为列向量 % 检查维度一致性 assert(size(X, 1) size(y, 1), 样本数不一致); assert(rank(X) 0, 矩阵秩为0检查数据是否全零);这里readmatrix会自动处理数值类型但如果Excel单元格里有空值读出的是NaNCARS内部构建PLS时会报错。建议在读取后先检查any(isnan(X))有缺失值就插值或者删除对应样本。另外CARS建模前不强制标准化但绝大多数实现内部会先用zscore或autoscaling处理X所以不要自己先做标准化然后再传入否则等于做了两次标准化会扭曲变量权重的相对大小。3.3 调用CARS主函数并输出结果在典型的carspls实现中返回参数通常包括所选变量序号、RMSECV曲线和PLS系数。假设压缩包里提供的函数签名是[Rmscv, SelectedVariables, Coeff] carspls(X, y, K, N)那么在MATLAB中先加路径再调用addpath(D:\cars_lib); K 5; % 交叉验证折数 N 100; % 蒙特卡洛采样次数 % 运行CARS记录耗时 tic; [Rmscv, SelVars, Coeff] carspls(X, y, K, N); toc; % 查看最优变量数 fprintf(最优变量数量: %d\n, length(SelVars));逻辑说明Rmscv是每个迭代轮次的交叉验证均方根误差长度等于N。SelVars是RMSECV最小那一轮选出的变量索引你可以直接用X(:, SelVars)得到筛选后的光谱矩阵。Coeff是最终PLS回归系数常用于后续解释哪些波数点对预测贡献最大。如果主函数返回的是结构体则用result.RMSE之类的字段访问建议先用which carspls确认路径加载成功再执行调用。这条命令的另一种用途是排查有没有别的同名文件被误加载MATLAB里如果存在多个carspls.mwhich会显示当前生效的那个路径这个坑在“副本”类压缩包里特别常见。3.4 常见运行时错误未定义函数、维度不匹配、内存不足先看最常见的Undefined function or variable carspls。原因基本只有三类一是没有addpath二是文件在解压后被移动过当前工作目录不对三是函数名拼写不一致比如压缩包里的文件叫carspls_origin.m你需要调用对应名字。第二个常见错误是维度不匹配集中在y的维度上。如果y是行向量且没有执行yy(:)CARS内部在PLS计算时会报Dimension mismatch。第三种错误是Out of memory通常发生在p很大比如3500个波长且N200的场景。这时可以先用N50跑一遍确认代码本身没问题再逐步增大采样次数。还有一个容易被忽略的情况如果压缩包里的CARS代码需要额外的PLS工具箱而你的MATLAB没有安装Statistics or Optimization Toolbox就会在调用plsregress时失败。解决办法不是去搜安装包而是检查代码里是否自带pls或pls1函数。很多论文作者发布的CARS代码会自带一个简化版pls.m这样只需要基础MATLAB环境就能跑。如果确实依赖plsregress可以在命令窗口执行ver(stats)查看是否有统计工具箱没有时只能换用自带PLS的实现。4. 参数调优与结果可视化从CARS所选变量到模型评估4.1 如何根据RMSECV曲线选择最优变量子集CARS输出里最值得画图的是RMSECV随迭代轮次变化的曲线。很多人误以为直接取全局最小的RMSECV就行但在变量数快速减少的早期阶段RMSECV可能受噪声影响产生局部低点。常见处理办法是找出全局最小值点然后找到该点之前最后一个RMSECV显著低于平均水平的轮次再综合变量个数决定。下面的代码画出RMSECV曲线并标出最小值位置figure; plot(Rmscv, LineWidth, 1.2); xlabel(迭代轮次); ylabel(RMSECV); grid on; [bestRMSECV, bestIdx] min(Rmscv); hold on; plot(bestIdx, bestRMSECV, ro, MarkerSize, 8); legend(RMSECV, 最小值); title(sprintf(CARS变量筛选曲线最优变量数: %d, length(SelVars)));bestIdx对应的轮次不一定就是最优变量集因为同一轮次的RMSECV只代表该轮抽样样本下的表现。更稳的做法是取最优轮次附近若干轮比如前后各3轮的变量集合做交集或并集再通过外部交叉验证确认。SelVars就是bestIdx那一轮保留下来的变量索引画曲线时顺带打印变量数能直观看到变量数从几千急速衰减到几十的过程。4.2 用所选变量建立PLS模型并与全谱对比筛选完变量后下一步是用同一个PLS模型框架对比全谱和筛选谱的预测能力。下面的代码利用MATLAB自带的plsregress做10折交叉验证计算RMSECV和决定系数rng(2024); % 固定随机种子保证对比公平 cv cvpartition(size(X, 1), KFold, 10); % 全谱模型 mse_full zeros(cv.NumTestSets, 1); for i 1:cv.NumTestSets trainIdx cv.training(i); testIdx cv.test(i); [~, ~, ~, ~, beta] plsregress(X(trainIdx, :), y(trainIdx), 5); yhat [ones(sum(testIdx), 1), X(testIdx, :)] * beta; mse_full(i) mean((y(testIdx) - yhat).^2); end RMSECV_full sqrt(mean(mse_full)); % CARS筛选后的模型 X_sel X(:, SelVars); mse_sel zeros(cv.NumTestSets, 1); for i 1:cv.NumTestSets trainIdx cv.training(i); testIdx cv.test(i); [~, ~, ~, ~, beta] plsregress(X_sel(trainIdx, :), y(trainIdx), 5); yhat [ones(sum(testIdx), 1), X_sel(testIdx, :)] * beta; mse_sel(i) mean((y(testIdx) - yhat).^2); end RMSECV_sel sqrt(mean(mse_sel)); fprintf(全谱 RMSECV: %.4f\n, RMSECV_full); fprintf(CARS筛选后 RMSECV: %.4f\n, RMSECV_sel);逻辑说明这里统一固定随机种子为2024确保全谱和筛选后的模型使用完全相同的样本划分否则比较没有意义。使用plsregress时注意它默认把1向量拼进X所以预测时需要手动添加常数列。如果CARS自带PLS函数建议优先用它保持内部算法和外部验证的一致性避免出现“CARS选了变量外部模型从没用过CARS同一套PLS”这种脱节。4.3 三个实战坑随机数种子、重复运行差异、变量数过少第一个坑是随机数种子。CARS内部用了rand和randperm如果你不固定种子每次运行的结果都不一样写进论文的数据无法复现。建议在调用CARS之前执行rng(固定值)并在记录结果时把随机种子一起记录下来。第二个坑是重复运行差异。如果样本量只有几十个哪怕固定了随机种子CARS的抽样比例过大或过小都会造成稳定性问题。常见的补救方式是把N从50提高到200同时把抽样比例从0.8降到0.7牺牲一点计算速度换取更平滑的RMSECV曲线。第三个坑是最终选出的变量数过少比如只有个位数。变量数太少会丧失模型鲁棒性换一批样本预测时误差容易波动。可以在代码里找到最终保留比例参数将其从0.5%改为1%这样变量数会松弛到原先的2倍左右。5. 让CARS代码更可靠验证与嵌入自己的数据处理流程5.1 用稀疏模拟数据快速验证代码是否可用解压后的“副本”代码可能存在别人改动的痕迹比如变量名被改成中文、删除了部分注释、或者中间埋了一个不兼容的参数。直接拿真实数据跑出了问题很难定位。我的习惯是先造一组带少量真实变量的模拟数据用CARS能否找回真实变量来验证代码逻辑。下面这段代码生成一个10个真实变量、总共100个变量、50个样本的回归数据集rng(2024); n 50; p 100; true_idx [3, 17, 42, 56, 88]; % 真实变量与y线性相关其余变量为纯噪声 X_true randn(n, length(true_idx)); y X_true * [1.5; -0.8; 2.0; 1.2; -1.0] 0.1 * randn(n, 1); X randn(n, p); X(:, true_idx) X_true;调用CARS之后检查SelVars与true_idx的重合率。如果代码正常通常能找回4个以上真实变量如果找回的变量大多集中在噪声列那说明数据预处理或参数设置有问题。这个验证方法只有几行代码却能在真实数据跑之前排除80%的实现错误。5.2 将CARS封装为自动函数并清理“副本”留下的路径干扰实际使用中最好把CARS调用包装成一个自己的函数这样不用每次手写addpath。同时为了防止“副本”名目录里的其他脚本干扰主函数封装时用绝对路径定位function [SelVars, RMSECV, results] runCARS(X, y, K, N, seed) % 基于CARS库的封装自动设置路径并固定随机种子 carsPath D:\cars_lib; addpath(carsPath); rng(seed); [RMSECV, SelVars, results] carspls(X, y, K, N); end这样在主工作目录里就干净了所有与CARS相关的函数和依赖都集中在D:\cars_lib下不会因为当前目录切换到别的项目而失效。如果压缩包里有多个版本的carspls我建议在cars_lib下建立old子目录把不用的副本放进去并保持carsPath下只放当前要用的那一个版本。这一步看似简单却能避免以后某天改了个名字结果调到的还是旧函数。5.3 进阶把CARS与网格搜索结合自动确定抽样比例除了N和KCARS里最难调的就是抽样比例和最终保留比例。一种常用的做法是从0.6到0.9按0.05步长网格搜索每个参数组合运行三次取RMSECV最小的组合。由于CARS运行本身存在随机性网格搜索的时长会成倍增加。我的实际建议是先用默认参数抽样比例0.8保留比例0.5%跑通一遍然后只微调抽样比例这一个参数观察所选变量序号是否稳定若稳定就不再调。如果确实需要自动化可以在脚本中用一个外层循环遍历两个比例但每次循环内固定不同的随机种子最终输出“参数组合 → 变量数 → RMSECV”的表格这样既能看到趋势也不会因为单次异常值做错决定。对于真正想深入使用的人还有一个验证技巧把CARS跑N轮得到的RMSECV向量和变量数向量并排保存为.mat文件下次运行时先快速绘图再对比本次与上次的RMSECV最小值差异。如果差异超过全谱RMSECV的5%就要检查数据文件是否被改动或者随机种子是否被重置。这套验证习惯能把CARS变成流水线上的稳定工具而不只是论文里的一张结果图。本文还有配套的精品资源点击获取