尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
光谱建模校正集与验证集划分:SPXY、KS、RS算法对比与Python实现
简介这份资源聚焦机器学习建模中校正集与验证集的样本划分问题面向本科、硕士及科研人员进行教研学习使用。包内提供SPXY算法、KS算法与RS算法三种经典划分策略的Matlab实现可用于光谱分析、近红外建模、回归预测等需要合理拆分训练与测试样本的场景帮助使用者对比不同划分方法对模型精度与泛化能力的影响。资源共5个文件以4个m脚本文件和1个txt说明文件为主m文件承载各划分算法的核心函数与调用示例txt则提示需自行带入数据压缩包整体约6KB轻量易读。目前已有332人学习下载。读者可借此快速理解三种算法的原理差异与代码结构将其嵌入自己的建模流程完成样本集划分与后续验证实验。1. 光谱建模第一步为什么你的校正集和验证集划分总让模型翻车做近红外、拉曼或者高光谱定量建模的人大概率都遇到过这种玄学同一批样本、同一个预处理、同一个 PLS 或 SVR换一次随机种子R² 能从 0.95 掉到 0.72。很多人第一反应是调模型参数其实根子常常在数据划分上——校正集训练用和验证集评估用分得不合理验证集里混进了校正集里没有的浓度区间或异常样本模型自然一测就崩。这个标题讲的正是这件事机器学习里校正集和验证集的划分方法重点落在三种经典算法——SPXY、KS、RS。它们解决的是同一个问题手里有一堆带标签的样本比如 200 个小麦样品的近红外光谱 对应蛋白含量怎么切出一部分当校正集去建模剩下的当验证集去检验。RS 是随机划分KS 是 Kennard-Stone 基于距离的均匀划分SPXY 是 KS 的升级版同时考虑光谱距离和浓度距离。适合做光谱、色谱、传感器阵列等小样本定量建模的人尤其是样本量在几十到几百、又不想靠随机种子碰运气的那批人。2. 三种划分算法到底在切什么RS、KS、SPXY 的选型逻辑2.1 RS 随机划分最省事也最容易埋雷RSRandom Sampling就是随机抽样。用sklearn的train_test_split或者numpy.random.permutation都能做。它的假设是样本独立同分布随机切出来的校正集和验证集在统计上应该差不多。但小样本场景下这个假设经常不成立200 个样本里如果浓度范围是 8% 到 18%随机切可能让验证集集中在 12% 到 14%校正集却覆盖了 8% 到 18%模型在验证集上表现虚高换一批真实样本就露馅。RS 不是不能用而是要用对场景。样本量大比如上千条、分布本身均匀、或者你只是做快速基线对比RS 够用。但如果你要做的是最终交付的定量模型RS 只能当对照组不能当主力。2.2 KS 算法让校正集在特征空间里铺得最开KSKennard-Stone的核心思想很朴素先选距离最远的两个样本进校正集然后每次选那个“离已选校正集最近距离最大”的样本直到校正集数量达标。这样做的结果是校正集在光谱空间里分布最均匀验证集自然落在校正集包围的范围内外推风险小。KS 的距离通常是欧氏距离直接算光谱矩阵的样本间距离。它的优点是确定性——同样的数据跑多少次结果都一样不用跟随机种子较劲。缺点是只看了 X光谱没看 Y浓度。如果两个样本光谱几乎一样但浓度差很多KS 可能只选其中一个另一个进验证集模型就学不到这个浓度点。2.3 SPXY 算法同时管住光谱和浓度的双距离划分SPXYSample set Partitioning based on joint X-Y distances是 KS 的改进版。它把样本间距离定义成两部分光谱距离和浓度距离各自归一化后相加。公式常见写法是[ d_{xy}(i,j) \frac{d_x(i,j)}{\max_{i,j} d_x(i,j)} \frac{d_y(i,j)}{\max_{i,j} d_y(i,j)} ]其中 (d_x) 是光谱欧氏距离(d_y) 是浓度差的绝对值多组分就取欧氏距离。归一化是为了让两个距离量纲一致不然浓度差 0.1 和光谱差 1000 根本没法加。SPXY 的好处是校正集在光谱空间和浓度空间都铺得开验证集不会出现“浓度盲区”。代价是计算量比 KS 大一点样本量上千时距离矩阵会占内存但几百个样本完全无压力。做近红外定量的人如果只记一个算法记 SPXY 就够了。2.4 三种算法的选择对照算法依据确定性适合场景主要风险RS随机否大样本、快速对比验证集分布偏斜KS光谱距离是光谱差异主导的定性/定量忽略浓度分布SPXY光谱浓度距离是小样本定量建模计算量略大选型建议很直接样本量小于 500、做定量、浓度范围宽优先 SPXY只做定性或者浓度分布很均匀KS 够用样本量上千、只想快速看模型上限RS 可以当基线。常见做法是三个都跑一遍比较验证集 RMSEP但最终交付模型用 SPXY 或 KS。3. 用 Python 把 SPXY、KS、RS 跑通从距离矩阵到划分结果3.1 数据准备X 和 y 的维度要对齐假设你有一个光谱矩阵X形状是(n_samples, n_features)浓度向量y形状是(n_samples,)或(n_samples, n_targets)。下面用 numpy 造一组模拟数据实际替换成你自己的.csv或.mat读进来的数组即可。import numpy as np from scipy.spatial.distance import cdist # 模拟 120 个样本200 个波长点浓度范围 8~18 np.random.seed(42) n_samples 120 n_features 200 X np.random.randn(n_samples, n_features) * 0.5 np.sin(np.linspace(0, 3, n_features)) y np.random.uniform(8, 18, n_samples).reshape(-1, 1) print(X.shape, y.shape) # (120, 200) (120, 1)这里X是光谱y是浓度。注意y要 reshape 成二维后面算距离时统一用二维数组避免广播出错。如果你的y是多组分比如同时测蛋白和水分y就是(n_samples, 2)距离用欧氏距离即可。3.2 RS 随机划分一行代码但别只跑一次from sklearn.model_selection import train_test_split # 随机划分校正集 80 个验证集 40 个 X_cal, X_val, y_cal, y_val train_test_split( X, y, test_size40, random_state0, shuffleTrue ) print(RS 校正集浓度范围:, y_cal.min(), y_cal.max()) print(RS 验证集浓度范围:, y_val.min(), y_val.max())test_size40表示验证集 40 个剩下 80 个进校正集。random_state固定后结果可复现但换个种子分布就变。实际用的时候建议跑 20 次不同种子看验证集 RMSEP 的均值和标准差如果标准差很大说明 RS 不适合你的数据。3.3 KS 算法手写距离矩阵选最远点def ks_partition(X, n_cal): Kennard-Stone 划分返回校正集索引和验证集索引 n X.shape[0] # 计算样本间欧氏距离矩阵 D cdist(X, X, metriceuclidean) cal_idx [] # 第一步选距离最远的两个样本 i, j np.unravel_index(np.argmax(D), D.shape) cal_idx.extend([i, j]) # 剩余样本 remaining list(set(range(n)) - set(cal_idx)) while len(cal_idx) n_cal: # 对每个剩余样本算它到已选校正集的最小距离 min_dists [np.min(D[idx, cal_idx]) for idx in remaining] # 选最小距离最大的那个 next_idx remaining[np.argmax(min_dists)] cal_idx.append(next_idx) remaining.remove(next_idx) val_idx list(set(range(n)) - set(cal_idx)) return np.array(cal_idx), np.array(val_idx) cal_idx, val_idx ks_partition(X, n_cal80) X_cal_ks, y_cal_ks X[cal_idx], y[cal_idx] X_val_ks, y_val_ks X[val_idx], y[val_idx] print(KS 校正集浓度范围:, y_cal_ks.min(), y_cal_ks.max()) print(KS 验证集浓度范围:, y_val_ks.min(), y_val_ks.max())cdist算的是(n, n)距离矩阵120 个样本就是 14400 个距离内存很小。np.argmax(D)找到全局最大距离的位置np.unravel_index把它转成行列索引。循环里每次算剩余样本到已选集合的最小距离取最大者加入。这个实现的时间复杂度是 O(n²)几百个样本秒出上千个样本也能接受。3.4 SPXY 算法把浓度距离加进去def spxy_partition(X, y, n_cal): SPXY 划分同时考虑光谱距离和浓度距离 n X.shape[0] # 光谱距离矩阵 Dx cdist(X, X, metriceuclidean) # 浓度距离矩阵多组分用欧氏距离 Dy cdist(y, y, metriceuclidean) # 归一化后相加 Dxy Dx / Dx.max() Dy / Dy.max() cal_idx [] i, j np.unravel_index(np.argmax(Dxy), Dxy.shape) cal_idx.extend([i, j]) remaining list(set(range(n)) - set(cal_idx)) while len(cal_idx) n_cal: min_dists [np.min(Dxy[idx, cal_idx]) for idx in remaining] next_idx remaining[np.argmax(min_dists)] cal_idx.append(next_idx) remaining.remove(next_idx) val_idx list(set(range(n)) - set(cal_idx)) return np.array(cal_idx), np.array(val_idx) cal_idx_spxy, val_idx_spxy spxy_partition(X, y, n_cal80) X_cal_spxy, y_cal_spxy X[cal_idx_spxy], y[cal_idx_spxy] X_val_spxy, y_val_spxy X[val_idx_spxy], y[val_idx_spxy] print(SPXY 校正集浓度范围:, y_cal_spxy.min(), y_cal_spxy.max()) print(SPXY 验证集浓度范围:, y_val_spxy.min(), y_val_spxy.max())Dx / Dx.max()和Dy / Dy.max()把两个距离压到 0~1 之间避免量纲差异。如果你的浓度范围很小比如 0.01~0.05不归一化的话浓度距离几乎不起作用SPXY 就退化成 KS 了。归一化之后光谱和浓度各占一半权重这是最常见的做法。如果业务上更看重浓度覆盖可以把浓度距离乘个系数比如1.5 * Dy / Dy.max()但一般不建议除非你有明确理由。3.5 划分结果怎么验证看浓度分布和模型 RMSEPfrom sklearn.cross_decomposition import PLSRegression from sklearn.metrics import mean_squared_error def evaluate_partition(X_cal, y_cal, X_val, y_val, n_components10): pls PLSRegression(n_componentsn_components) pls.fit(X_cal, y_cal) y_pred pls.predict(X_val) rmsep np.sqrt(mean_squared_error(y_val, y_pred)) return rmsep print(RS RMSEP:, evaluate_partition(X_cal, y_cal, X_val, y_val)) print(KS RMSEP:, evaluate_partition(X_cal_ks, y_cal_ks, X_val_ks, y_val_ks)) print(SPXY RMSEP:, evaluate_partition(X_cal_spxy, y_cal_spxy, X_val_spxy, y_val_spxy))同一组数据、同一个 PLS 模型只换划分方式RMSEP 就能差出不少。如果 SPXY 的 RMSEP 明显低于 RS说明你的数据确实存在分布不均的问题SPXY 值得用。如果三者差不多说明样本本身很均匀RS 也能凑合但 SPXY 至少不会更差。4. 避坑与排查划分算法落地时最容易翻车的 5 个点4.1 现象SPXY 跑出来和 KS 一模一样原因浓度向量y没有归一化或者y的数值范围远小于光谱距离导致Dy / Dy.max()在Dxy里几乎可以忽略。解决检查Dy.max()是否远小于Dx.max()如果是确认归一化代码写对了。另外如果y是整数类别标签而不是连续浓度SPXY 的浓度距离意义不大应该用分层抽样而不是 SPXY。4.2 现象KS 选出来的校正集浓度范围比验证集还窄原因KS 只看光谱距离如果光谱和浓度不是强相关光谱上均匀的样本在浓度上可能不均匀。解决换 SPXY或者先对光谱做 PCA用主成分得分算距离再结合浓度分层。常见做法是先用 SPXY 跑一遍如果验证集浓度覆盖不够手动把边界浓度样本强制放进校正集。4.3 现象样本量 50 个SPXY 跑完校正集 40 个验证集 10 个验证集 RMSEP 波动巨大原因验证集太小10 个样本的 RMSEP 本身就不稳定。解决小样本场景下不要只切一次用 KS 或 SPXY 做多次划分比如每次校正集 35 个、验证集 15 个重复 20 次看 RMSEP 的分布。或者直接用交叉验证但交叉验证的折数也要用 KS/SPXY 来分不能随机分。4.4 现象距离矩阵内存爆了原因样本量上万时cdist生成的(n, n)矩阵占内存。解决样本量超过 5000 时KS 和 SPXY 的全距离矩阵会吃几个 GB。可以改用分批计算或者先用 K-means 聚类每个簇里再跑 KS/SPXY。实际光谱建模样本量通常几百这个问题不常见但如果你处理的是高光谱图像像素级数据就要注意。4.5 现象换了随机种子RS 的 RMSEP 比 SPXY 还好原因RS 碰巧切到了一个“简单”的验证集或者 SPXY 的校正集里混入了异常样本。解决不要只看一次结果。RS 跑 20 次取平均SPXY 跑一次但检查校正集里有没有光谱异常点用马氏距离或 PCA 的 Q 残差判断。如果 SPXY 校正集里有异常样本先剔除再划分否则异常样本会被优先选进校正集因为它的距离往往很大。5. 进阶技巧用 SPXY 做分层交叉验证把模型评估做扎实单次划分的验证集 RMSEP 终究只是一个点估计样本量小的时候置信区间很宽。更稳的做法是把 SPXY 和交叉验证结合起来每次留出一部分样本做验证但留出的方式不是随机而是用 SPXY 的距离排序来分层。具体操作先用 SPXY 把全部样本按“代表性”排序排在前面的样本距离远、代表性强排在后面的样本距离近、代表性弱。然后做 5 折交叉验证时每折的验证集从排序后的不同区间抽取保证每折验证集的浓度范围和光谱多样性都差不多。这样得到的 5 个 RMSEP 的均值和标准差比单次划分可靠得多。def spxy_order(X, y): 返回 SPXY 选择顺序越靠前代表性越强 n X.shape[0] Dx cdist(X, X, metriceuclidean) Dy cdist(y, y, metriceuclidean) Dxy Dx / Dx.max() Dy / Dy.max() order [] i, j np.unravel_index(np.argmax(Dxy), Dxy.shape) order.extend([i, j]) remaining list(set(range(n)) - set(order)) while remaining: min_dists [np.min(Dxy[idx, order]) for idx in remaining] next_idx remaining[np.argmax(min_dists)] order.append(next_idx) remaining.remove(next_idx) return order order spxy_order(X, y) # 按顺序分成 5 折每折取排序中均匀间隔的样本 n_folds 5 folds [[] for _ in range(n_folds)] for rank, idx in enumerate(order): folds[rank % n_folds].append(idx) for k in range(n_folds): val_idx folds[k] cal_idx [i for i in range(n_samples) if i not in val_idx] rmsep evaluate_partition(X[cal_idx], y[cal_idx], X[val_idx], y[val_idx]) print(fFold {k1} RMSEP: {rmsep:.4f})这段代码先拿到 SPXY 的完整选择顺序然后按rank % n_folds把样本轮流分到 5 折里。这样每折验证集都覆盖了从强代表性到弱代表性的样本浓度分布也均匀。跑完 5 折你会得到 5 个 RMSEP报告的时候写“RMSEP 均值 ± 标准差”比单次划分有说服力得多。我自己的习惯是只要样本量低于 300单次划分的结果只用来快速看模型有没有大问题最终报告一定用 SPXY 分层交叉验证的均值。这个习惯帮我避开了好几次“单次划分看起来很好、实际预测一塌糊涂”的翻车。另外SPXY 的距离矩阵算一次就能复用不用每折重算代码里把Dxy缓存下来交叉验证跑起来很快。如果你正在做光谱定量或者任何小样本回归先把 SPXY 跑通再对比 KS 和 RS最后用分层交叉验证收口。这套流程不复杂但能让你少走很多弯路。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

SSA优化CNN-LSTM实现小样本时序回归预测

SSA优化CNN-LSTM实现小样本时序回归预测

简介:本资源是一套面向机器学习与智能优化算法初学者及进阶研究者的Matlab实战代码包,聚焦于时间序列回归预测任务,特别适用于电力负荷、环境参数、金融时序等多输入单输出场景。资源采用麻雀搜索算法(SSA)对CNN-LSTM混…

📅 2026/10/25 10:30:05
TensorFlow双塔推荐系统:MovieLens全流程实现

TensorFlow双塔推荐系统:MovieLens全流程实现

简介:本资源是一套基于Python与TensorFlow实现的电影推荐系统完整项目实践包,面向人工智能、深度学习方向的初学者与进阶学习者,聚焦解决个性化推荐中的协同过滤、矩阵分解与深度学习建模等核心问题。压缩包共10个文件,含2个CSV&a…

📅 2026/10/25 10:30:05
Bash 用 xprintidle 控制 Firefox 时每秒开新标签的原因与修复

Bash 用 xprintidle 控制 Firefox 时每秒开新标签的原因与修复

问题现象 在信息亭(kiosk)场景下需要两个行为:一是无人操作时把浏览器杀掉,二是浏览器被关掉后自动重新拉起,让用户回到站点首页。最初的脚本是这样写的: while true; dofirefox --kiosk --start-fullscreen --noerrdialogs --no-first-run \--disable-infobars thesit…

📅 2026/10/25 10:30:05
MORE NEWS

更多资讯

📰

spooling假脱机输入输出模拟技术:Python实现打印队列与优先级调度

简介:这份资源是面向操作系统课程学习者与计算机专业学生的Spooling假脱机输入输出模拟技术实践包,围绕联机外围设备同时操作原理,用两个请求输出进程与一个spooling输出进程的进程模型,演示如何借助磁盘缓冲区把慢速I/O设备虚拟化…

📰

BIRT运行时4.8.0部署实战:从解压到验证的完整指南

简介:BIRT(Business Intelligence and Reporting Tools)是Eclipse基金会维护的开源报表系统,这份运行时环境包面向需要在Java Web项目中集成动态数据报表的开发者,尤其适合已具备一定Java Web基础、希望快速搭建报表服…

📰

CozyClay motion3编译器原理:浏览器内的FK/IK数学,150毫秒烤出730帧

【免费下载链接】CozyClay Open source previs software in the browser: block a scene, pose characters, author camera moves and cuts, then take the same shots to an AI video model. 项目地址: https://gitcode.com/gh_mirrors/co/CozyClay 点击查看 免费下…

📰

10天两个补丁!Home Assistant 2026.10 升级翻车实录:新卡片好看,自动化先罢工了

10天两个补丁!Home Assistant 2026.10 升级翻车实录:新卡片好看,自动化先罢工了 【免费下载链接】core :house_with_garden: Open source home automation that puts local control and privacy first. 项目地址: https://gitcode.com/GitH…

📰

只读MCP如何实时抓取任意网站?Inspo study()的SSRF防护完整指南

【免费下载链接】inspo Real websites for your coding agent to learn design from, searchable over MCP. 项目地址: https://gitcode.com/gh_mirrors/ins/inspo 点击查看 免费下载 Inspo 是一个为编码 Agent 提供"真实网站设计档案"的 MCP 服务器&…

📰

MATLAB生成与量化分析斑马图:图像伪影诊断实战

简介:本资源是一份面向雷达系统工程师与信号处理方向研究生的MATLAB实战工具包,聚焦PRF(脉冲重复频率)设计中的距离模糊与方位模糊分析问题。斑马图作为雷达参数权衡的关键可视化手段,能直观揭示不同PRF设置下模糊区域…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬