CARS变量筛选算法:从近红外光谱到PLS建模的实用指南 简介CARS竞争性自适应重加权算法可执行代码与文档整合包面向化学计量学、光谱分析及机器学习研究者解决高维数据下PLS回归的变量选择与过拟合问题。rar压缩包内共有37个文件以32个m格式MATLAB源程序为主另有2个mat实测数据集、1份PDF手册、1个doc说明和1张示意图整体大小1.27MB。已有11425人学习下载。资料内含PDF操作手册、多种示例脚本与玉米光谱数据覆盖CARS、SCARS、PLS、MCS等算法代码含交叉验证、预处理、可视化等模块便于理解自适应重加权迭代流程与RMSECV评估机制可直接用于论文复现和实际光谱特征筛选。1. 变量筛选为何要竞争CARS算法切入光谱建模的独特视角近红外光谱数据动辄上千个波长点变量比样本还要多按常理信息越丰富模型越准实测却往往是变量越多预测误差越大。因为波长点之间存在严重的共线性冗余信息和不相关噪声会把偏最小二乘模型的回归系数拉扯变形导致模型泛化能力急速下降。竞争性自适应重加权算法CARS要解决的正是这个矛盾它不追求一次性找出最优变量组合而是通过模拟自然选择的迭代过程让变量在竞争中逐轮淘汰最终收敛到一个低维、低相关性的变量子集。理解CARS就理解了变量选择里随机但有方向这一类方法背后的设计动机与底层机制。2. CARS的骨架蒙特卡洛采样与回归系数权重的底层逻辑2.1 蒙特卡洛采样为什么不看全量样本CARS的第一步是抽样。每一轮迭代算法会从校正集样本里随机抽取一定比例的样本通常约80%基于这些样本建立PLS模型。这里有个问题值得先想清楚为什么不直接用全部样本建模我个人的理解是变量选择的目标不只是找哪些变量有用还要找哪些变量总是有用——即鲁棒性。如果用全量样本只建一次模型某个变量的回归系数可能是偶然大、也可能是偶然小而多次随机抽样相当于给每个变量提供了多次考试机会。一个真正重要的变量无论抽到哪一批样本其回归系数的绝对值都会稳定地居于前列一个靠运气突出重围的变量则会在不同抽样下权重忽高忽低。这种做法的本质是交叉验证的思想下放到变量评估不用单独留出验证集而是通过反复重采样观察系数的稳定性。每轮抽样都得到一组回归系数算法比较的是这些系数在不同抽样下的相对大小而不是某一次的绝对数值。抽样比例这个参数的敏感性也在这里体现比例太高每轮模型几乎相同系数波动小等于白抽样比例太低样本子集不足以代表整体系数方差过大评估结果又不可靠。2.2 回归系数绝对值最朴素的变量贡献度量有了PLS模型每个变量都会得到一个回归系数。系数的绝对值越大意味着该变量对预测结果的贡献越强系数接近零的变量则基本可以被视为噪声携带者。CARS把每个变量的回归系数绝对值作为其权重并在这个权重的基础上设计后续的随机抽样。这里要特别强调一个细节CARS用的不是VIP分数或相关系数而是PLS回归系数。原因是回归系数天然地考虑了变量之间的联合作用——某个变量单独来看与目标值毫无线性关系但在PLS的潜变量结构中它可能与其他变量协同发挥作用回归系数能捕捉这种协同贡献。VIP分数也能做到类似的事但VIP是基于协方差的投影度量而CARS选择回归系数的绝对值核心动机是可加性多个变量组合的贡献大致可以按系数绝对值累加这为后面的按权重随机抽样提供了天然的数学基础。在实际计算中权重计算非常廉价核心逻辑只有几行import numpy as np from sklearn.cross_decomposition import PLSRegression def evaluate_variable_weight(X, y, sample_ratio0.8, n_components5): # 随机抽取样本子集完成蒙特卡洛采样 n_samples X.shape[0] sampled_idx np.random.choice(n_samples, int(n_samples * sample_ratio), replaceFalse) # 用抽样样本建立 PLS 模型 pls PLSRegression(n_componentsn_components) pls.fit(X[sampled_idx], y[sampled_idx]) # 回归系数的绝对值作为该轮变量权重 coefficients pls.coef_.ravel() weight np.abs(coefficients) # 按权重降序排序返回排序后的索引和权重 sorted_idx np.argsort(weight)[::-1] return sorted_idx, weight这段代码把蒙特卡洛采样 回归系数评估两个步骤压缩在一个函数里。关键参数有三个sample_ratio控制抽样比例过大则每轮模型几乎相同失去多样性过小则模型不稳定系数估计方差太大一般取0.7到0.9之间。n_components是PLS潜变量数它决定模型对数据结构的拟合程度太小会欠拟合太大会把噪声也拟合进去。返回值里的sorted_idx专门用于下一阶段的变量淘汰。注意coef_的形状PLSRegression的coef_在单因变量场景下是一个二维数组形状为n_features行、1列所以需要ravel()展平成一维。很多初写CARS实现的人会在这里取错维度导致后续的索引对应不上原始变量这是一个非常隐蔽的坑。2.3 权重排序之后次轮变量子集如何产生每轮评估完权重后并不是简单地砍掉后半截。CARS使用指数衰减函数确定本轮保留的变量比例然后用自适应重加权采样按权重从保留变量里做放回式抽样抽样结果作为下一轮的变量子集。这里要区分两个概念强制淘汰与随机抽样。强制淘汰是把当前轮次里确定不重要的变量直接删掉这是硬性操作随机抽样则是在剩余变量里按权重概率性地选取权重大的变量有更高概率被选中但权重小的变量也保留了一丝被选中的机会。这个保留一丝机会的设计恰恰是CARS与贪心算法的分水岭——贪心算法会无情地丢弃所有当前看起来不重要的变量而CARS允许眼下弱、但组合起来有用的变量有存活的机会。关于这个机制的数学细节是下一章要展开的重点。3. 指数衰减函数与自适应重加权采样竞争淘汰的节奏控制3.1 EDF的数学形式为什么是幂函数衰减CARS每轮要保留多少比例的变量由指数衰减函数EDF决定。设初始变量总数为p当前第i轮从1开始计数总运行轮数为N则第i轮的变量保留率近似为r_i (p_min / p)^(i / N)p_min是计划在最后一轮保留的最小变量数。这个形式变换一下就能看出指数衰减的本质等式两边取对数log(r_i) (i/N)·log(p_min/p)保留率的对数随轮次线性下降也就是说保留率本身是几何级数式的衰减。开头几轮淘汰速度快越到后面淘汰速度越慢。为什么不是线性衰减线性衰减意味着每轮淘汰固定比例这在变量数很多的光谱场景下会抢跑——前十几轮把大量变量快速砍光后面想精细筛选时已经没有候选变量了。而指数衰减前半段快速粗筛、后半段慢速细选正好匹配光谱变量成百上千个变量里真正关键的只有几十个的分布特征。常见实现的EDF代码如下def compute_retained_ratio(current_round, total_rounds, n_variables, min_variables): 计算第 current_round 轮需要保留的变量比例 current_round: 从 1 开始计数的当前轮次 total_rounds: 总轮次 N n_variables: 初始变量总数 min_variables: 计划最终保留的最小变量数 # 指数衰减保留率按轮次比例指数下降 ratio (min_variables / n_variables) ** (current_round / total_rounds) return ratio # 每轮保留变量数 当前变量数 * ratio向下取整 # 典型运行轨迹从 1000 个变量出发50 轮后计划保留约 30 个这里面的min_variables是个超参数一般取2到10之间的值。它并不代表最终一定保留那么多变量而是给EDF一个收敛锚点让衰减曲线有末端约束。如果设得太小比如1衰减曲线过于陡峭可能在第30轮就把变量数压到个位数后面的轮次完全退化成矮子里拔将军如果设得偏大比如50衰减又太缓前几轮几乎没什么淘汰效果计算开销增大。3.2 强制淘汰与自适应重加权一只脚刹车、一只脚油门EDF决定的是保留多少个——这是刹车ARS决定的是保留哪一些——这是油门。两者必须配合起来才能让迭代正常推进。强制淘汰是硬约束即使某个变量之前一直权重很高一旦EDF的保留变量数小于当前变量数权重最低的那一截会被直接移除。这个步骤保证了变量子集规模的单调递减是算法收敛的刹车。自适应重加权采样是软操作变量不是简单地按权重比例被抽中而是要经过一个非均匀采样过程。具体做法是把保留下来的变量按权重归一化每个变量的被抽中概率等于其权重占总权重的比例然后从这个概率分布里做放回抽样抽出的变量集合作为下一轮输入。这一步是油门——它让权重高的变量在下一轮里被重复选中从而增加其出镜率权重低的变量也有机会被抽到避免了重要变量被一步错杀。用一句话概括强制淘汰筛掉确定不行的随机重加权筛掉概率上不行的。前者是规则驱动后者是概率驱动。3.3 权重归一化与相关性保持还有一个经常被忽略的细节CARS的随机抽样是放回式的。放回意味着同一变量可以在一次抽样中被抽到多次这相当于变相提高了该变量在下一轮中的权重。叠加上权重归一化高权重变量的影响力会被进一步放大。很多人第一次看CARS会觉得奇怪为什么要放回放回不会导致变量重复吗实际上放回恰恰是CARS保护协同变量组的手段。在光谱数据里一个有效变量往往与其附近波长的变量高度相关它们是作为一个模块共同发挥作用的。如果抽样改成不放回那么当某个模块里的多个变量被抽走后模块的整体贡献会被削弱而放回抽样允许模块里的关键成员被多次选中保持模块在下一轮模型中的结构完整性。理解到这一层CARS和普通逐步回归的差别就更清晰了逐步回归靠统计检验决定变量的去留而CARS靠的是加权随机抽样带来的模块级选择压力。这种机制在变量高度共线性的光谱数据里特别有效因为它不需要预先判断变量之间的相关结构算法自己会在竞争中自发地保留那些互相支撑的变量组。4. 从采样到收敛CARS完整流程的机制拆解4.1 运行阶段N轮循环里发生了什么把前两章的机制拼起来CARS的完整运行流程可以拆成五个步骤每轮迭代都走一遍从校正集样本中随机抽取一份子集约80%。用该子集建立PLS模型得到每个变量的回归系数。取回归系数绝对值作为变量权重。用EDF计算本轮变量保留数强制淘汰权重最低的变量。在保留变量中按权重做自适应重加权采样生成下一轮的变量子集。迭代N轮之后算法会得到一个轮次→变量子集的轨迹。每一轮对应的变量子集都能重新建立PLS模型并用交叉验证评估预测能力。CARS最后做的事情是用交叉验证去评估每一轮产生的变量子集所建立模型的RMSECV选RMSECV最小的那一轮作为最优变量子集。这里的关键是最优变量子集往往不是最后那一轮。原因在于EDF在最后几轮会把变量数压到很小的规模此时已经出现欠拟合——变量子集漏掉了某些必要的协同信息。所以CARS的价值不是收敛到某个终点而是在收敛路径上找到一个最佳停留点。4.2 主循环实现与参数含义下面给出一个结构完整的CARS主循环包含EDF、强制淘汰和ARS三个核心步骤import numpy as np from sklearn.cross_decomposition import PLSRegression from sklearn.model_selection import cross_val_predict from sklearn.metrics import mean_squared_error def cars(X, y, total_rounds50, sample_ratio0.8, min_variables3, n_components5): n_samples, n_vars X.shape current_idx np.arange(n_vars) # 当前变量索引 results [] # 每一轮的变量子集与RMSEcv for i in range(1, total_rounds 1): # 1. 蒙特卡洛采样随机抽样本 sampled_rows np.random.choice(n_samples, int(n_samples * sample_ratio), replaceFalse) X_s X[sampled_rows][:, current_idx] y_s y[sampled_rows] # 2. 建立PLS模型取回归系数绝对值作为权重 pls PLSRegression(n_componentsn_components) pls.fit(X_s, y_s) coef np.abs(pls.coef_.ravel()) # 3. EDF计算保留比例得到本轮的保留变量数 ratio (min_variables / n_vars) ** (i / total_rounds) retain_count max(int(len(coef) * ratio), 1) # 4. 强制淘汰只保留权重最高的 retain_count 个变量 top_idx np.argsort(coef)[::-1][:retain_count] selected current_idx[top_idx] # 5. 自适应重加权采样按权重放回抽样 weights coef[top_idx] / (coef[top_idx].sum()) sampled np.random.choice(selected, sizelen(selected), pweights, replaceTrue) current_idx np.unique(sampled) # 去重后作为下一轮变量集 # 用当前变量子集做交叉验证评估 cv_pred cross_val_predict(PLSRegression(n_componentsn_components), X[:, current_idx], y, cv5) rmsecv mean_squared_error(y, cv_pred) ** 0.5 results.append((rmsecv, current_idx.copy())) # 选 RMSECV 最小的轮次作为最终变量子集 best min(results, keylambda r: r[0]) return best[1], best[0]几个需要注意的参数细节sample_ratio是单轮采样的样本比例对模型稳定性影响很大total_rounds是总迭代次数一般50到100之间min_variables是EDF的末端锚点不能单独看要和total_rounds一起调节。第5步里np.unique去重是必要的否则放回抽样可能让下一轮变量数膨胀导致EDF无法按预期控制规模。这段代码也暴露了CARS的一个典型短板每一轮都要重新训练PLS并进行交叉验证计算量不小。对于光谱数据动辄几千个变量、几百个样本来说跑50轮通常还好如果数据规模更大建议用并行方式将各轮的交叉验证分散到多个计算单元上。4.3 交叉验证与最终选择轮次轨迹有什么价值CARS最终的输出并不是一个普通的变量列表而是一个带评估记录的轨迹。这个轨迹信息非常有用第一它可以告诉你模型从过拟合到欠拟合的变化过程。最早的几轮变量最多、模型最容易过拟合中间的轮次变量数适中RMSECV往往最低最后几轮变量太少RMSECV重新上升。画出轮次→RMSECV的曲线你会非常直观地看到什么规模的变量子集对这组数据是甜蜜点。第二它可以帮你识别不稳定变量。如果一个变量在多轮次中都出现在变量子集里说明它是稳定重要的如果某变量只在个别轮次出现且其后迅速消失说明它权重不高、可有可无。实践中运行多次CARS统计每个变量被选中的频率挑选出现频率高的变量组成最终集合是一种比单次运行更稳健的做法。第三轮次曲线的形状还能用来判断潜变量数n_components设得是否合理。如果曲线非常平坦、所有轮次的RMSECV都差不多说明潜变量数选择不当模型没有充分利用变量信息如果曲线在中间轮次出现明显低谷则说明潜变量数设置是合适的。5. CARS实践避坑变量筛选中的常见问题与排查5.1 每次运行选出的变量都不一样现象同一份数据、同样的参数连续跑三次CARS三次选出的变量子集重合度不到一半。原因CARS每一轮都做蒙特卡洛采样随机性从第一轮就开始累积。如果total_rounds取得偏小比如20轮随机偏差还没有被充分平均掉最终结果会高度依赖初始抽样的运气。解决把total_rounds提到50以上并多次运行取变量出现频率。具体做法是先跑10次CARS统计每个变量被选中的次数再按出现频率排序选出高频变量。留意一个边界情况如果样本量本来就只有40、50个抽样比例0.8会让每轮仅抽30多个样本此时建议降低total_rounds、提高sample_ratio到0.85以上避免样本代表性不足。5.2 先标准化还是先跑CARS现象CARS选出的变量集中在中高频波长段而低频段几乎没有变量被选中模型效果却很差。原因不同波段的信号强度差异很大。CARS的权重基于PLS回归系数绝对值如果原始光谱没有做过标准化数值大的波段天然会获得更高权重算法倾向保留数值大的变量而不是贡献大的变量。解决先对光谱数据做均值中心化或方差标准化再运行CARS。均值中心化通常就够了因为PLS本身对数据的列中心化有天然需求如果数据量纲差异极端再叠加方差标准化。经验上标准化的操作必须在划分校正集和测试集之前统一完成避免数据泄漏。5.3 校正集和测试集划分时机不对现象CARS在训练数据上选出的变量表现很好但在独立测试集上误差翻倍。原因如果先把全部样本混在一起做CARS再划分校正集和测试集变量选择过程已经把测试集的信息泄漏进了模型的选变量阶段。这属于典型的后选择偏差变量选择时的交叉验证结果虚高真实泛化能力远低于预期。解决严格按照先划分、后筛选的流程先把数据按7:3或8:2切成校正集和测试集只在校正集上运行CARS选出的变量固定后再用相同的变量索引去提取测试集特征。CARS的变量选择结果不能因为加入了测试集样本而重新计算。5.4 RMSECV最小的轮次太多无法区分现象RMSECV曲线在很长一段范围内非常接近最小值前后的轮次波动差别很小难以判断哪一轮是真正最优。原因变量子集之间的预测能力差异可能很小交叉验证本身的随机波动就已经掩盖了差异。这种情况在共线性严重、变量数接近的情况下很常见。解决优先选变量数更少的那一轮因为模型更简洁、更容易部署和解释。另外可以配合变量出现频率选同时满足RMSECV接近最优和变量出现频率高的子集。提示CARS选出的变量子集必须经过再验证。用选出的变量重新建立PLS模型在测试集上计算RMSEP和全变量模型对比确认变量筛选确实带来了泛化性能提升才算走完完整流程。5.5 数据量太小导致PLS拟合失败现象某轮迭代时PLS模型无法收敛报出线性代数相关的错误。原因蒙特卡洛抽样后参与建模的样本数可能少于潜变量数或在某些变量组合下设计矩阵接近奇异导致矩阵求逆失败。解决检查n_components是否小于抽样样本数的三分之一提前对抽样后的矩阵做条件数检查如果条件数过大跳过该轮或重新抽样。更根本的做法是在数据预处理阶段先做简单的无信息变量剔除把明显是纯噪声的波长提前删掉减少CARS在高危区域挣扎的概率。6. 设计取舍为什么CARS选择竞争而不是贪心把CARS和同类算法摆在一起会看到一条清晰的设计取舍线。逐步回归、向前选择这类贪心算法试图在每一步做出当前最优的决定每步精准砍掉一个或几个变量参数少、可解释性强但它们对变量的共线性极度敏感两个相关性极高的变量贪心算法会先选一个然后再也无法公正评估另一个的独立贡献。CARS用随机性和竞争绕开了这个问题——它不在每一步做最优决策而是让多个变量在反复竞争中自发分层重要变量自然留在高频区冗余变量被随机淘汰。这个选择是有代价的CARS的随机性导致单次运行结果不可完全复现必须靠多次运行或调大N来换取稳定。相比贪心算法的一次性确定输出CARS像是一种统计式的变量选择它的结果本质上是一个概率分布而不是一个唯一答案。我个人的使用习惯是把CARS当作变量预筛器而非最终决策器。先用CARS把变量从上千压到几十再用简单的手工推理或逐步回归从几十里挑出最终的十几二十个。这样既利用了CARS对共线性的容忍又保留了贪心算法在最后阶段的确定性。另一个值得想的设计点是EDF的强制性。为什么不让权重直接决定去留而要额外加一个指数衰减的外力因为纯按权重随机抽样变量的淘汰速度会很慢而且方差大EDF给了整个迭代一个时间表让变量数在第N轮之前收敛到用户预设的尺度。这个外力看似粗暴其实是对随机过程的一种正则化——它保证了收敛速度也让何时停止这个问题摆脱了对权重阈值的主观设定。从那以后我每次跑CARS都会做三件事先画RMSECV曲线看收敛节奏多跑几次统计频率再用最终变量子集做一次独立的测试集验证。这套流程每次都能帮我把变量从混乱的物理信号里捞出来。希望帮到你。本文还有配套的精品资源点击获取