SPXY样本集划分:从KS到联合距离,解决随机切分导致的模型虚高
简介这份资源围绕SPXY样本划分法展开面向从事近红外光谱分析、化学计量学建模及机器学习数据预处理的研究生与科研人员帮助解决复杂或不均衡数据集中训练集与验证集如何更具代表性的问题。包内共4个文件以3个m脚本和1份pdf文献为主脚本可直接在MATLAB中运行pdf则给出SPXY样本划分法与蒙特卡罗交叉验证结合用于橘叶中橙皮苷含量测定的完整案例压缩包约291KB轻量便于快速查阅。资源将SPXY划分、KS检验与交叉验证思路串联起来读者可据此理解样本选择对模型精度的影响并借鉴脚本实现自己的划分流程。目前已有1825人学习下载适合需要复现光谱建模流程或优化样本集划分策略的读者参考。1. 样本集划分的隐形陷阱为什么随机切分让模型“虚高”很多人做回归或分类建模时拿到样本集的第一反应是train_test_split随机切一刀然后看着验证集上 0.95 的 R² 沾沾自喜。但如果你把同一批数据换个随机种子再切一次指标可能直接掉到 0.7。这不是玄学而是样本集划分本身出了问题——当样本在特征空间里分布不均匀时随机切分会让训练集和验证集的分布产生系统性偏移模型在验证集上的表现就成了“碰运气”。SPXYSample set Partitioning based on joint X-Y distances方法解决的正是这个痛点。它不像 KSKennard-Stone只考虑特征空间 X 的距离而是把特征变量 X 和目标变量 Y 同时纳入距离计算保证划分出的校正集能覆盖整个样本空间的“光谱-浓度”联合分布。关键词里的 SPXY、KS、交叉验证本质上是在回答同一个问题如何让有限的样本在训练和验证之间被“公平”地分配使模型评估结果可信、可复现。搞懂这套机制比调参更能决定你模型的真实上限。2. 从 KS 到 SPXY距离度量里为什么要塞进 Y2.1 KS 方法的本质在 X 空间里“最远优先”选点KS 算法的逻辑非常朴素先找欧氏距离最远的一对样本放进校正集然后每次从剩余样本里挑一个“离已选集合最近距离最大”的样本加入直到校正集达到目标数量。这个策略在特征空间里是合理的——它试图让校正集均匀覆盖整个 X 空间。但问题在于KS 完全无视 Y 的存在。假设你在做近红外光谱定量分析样本的 Y 是某种成分浓度。如果两个样本在光谱上差异很大但浓度几乎一样KS 会认为它们“很不同”而优先选入校正集反过来两个光谱相似但浓度差异显著的样本KS 可能把它们都丢进验证集。结果就是校正集在 Y 维度上分布稀疏模型学不到高浓度或低浓度区域的映射关系。常见的实现思路是KS 的距离矩阵只计算 X 的欧氏距离选点过程是一个贪心策略时间复杂度 O(n²)对于几百个样本的规模完全可接受。2.2 SPXY 的改进联合距离 d_xy 的构造SPXY 在 KS 的基础上做了一个关键改动把 Y 也当作一个维度参与距离计算。具体做法是先分别计算 X 空间和 Y 空间的欧氏距离然后做归一化处理最后相加得到联合距离。import numpy as np from scipy.spatial.distance import cdist def spxy_split(X, Y, n_train, random_state42): SPXY 样本集划分 X: 特征矩阵 (n_samples, n_features) Y: 目标值 (n_samples, 1) 或 (n_samples,) n_train: 校正集样本数 返回: 校正集索引, 验证集索引 X np.asarray(X, dtypefloat) Y np.asarray(Y, dtypefloat).reshape(-1, 1) n X.shape[0] # 分别计算 X 和 Y 空间的欧氏距离矩阵 dx cdist(X, X, metriceuclidean) dy cdist(Y, Y, metriceuclidean) # 归一化除以各自的最大距离消除量纲影响 dx_norm dx / dx.max() if dx.max() 0 else dx dy_norm dy / dy.max() if dy.max() 0 else dy # 联合距离 dxy dx_norm dy_norm # KS 式贪心选点 selected [] # 第一步选联合距离最远的一对 idx np.unravel_index(np.argmax(dxy), dxy.shape) selected.extend([idx[0], idx[1]]) # 剩余样本中迭代选择 remaining set(range(n)) - set(selected) while len(selected) n_train: # 对每个剩余样本计算它到已选集合的最小距离 min_dists [] for r in remaining: d_min min(dxy[r, s] for s in selected) min_dists.append((r, d_min)) # 选最小距离最大的那个 next_idx max(min_dists, keylambda x: x[1])[0] selected.append(next_idx) remaining.remove(next_idx) selected np.array(selected) test_idx np.array(list(set(range(n)) - set(selected))) return selected, test_idx这段代码的核心在dxy dx_norm dy_norm这一行。归一化是必须的因为 X 可能是几十维的光谱数据Y 只是一维浓度值如果不归一化X 的距离数值会完全淹没 Y 的贡献。归一化之后X 和 Y 对联合距离的权重各占一半这是 SPXY 的默认策略。参数说明n_train决定校正集大小通常取总样本的 70%80%random_state在这个确定性算法里其实用不到但保留是为了接口兼容。选点过程是确定性的同样的输入永远得到同样的划分这一点比随机切分强太多。2.3 联合距离的几何直觉把 X 和 Y 想象成两个独立的空间SPXY 做的事情是在这两个空间的“笛卡尔积”里做最远优先采样。一个样本如果 X 离群且 Y 也离群它的联合距离会很大优先被选入校正集如果只是 X 离群但 Y 居中联合距离被 Y 拉低可能不会优先选。这样选出来的校正集在 X-Y 联合分布上是覆盖最广的模型训练时见过的“模式”最全。我一般会这么理解KS 是在 X 空间撒网SPXY 是在 X-Y 空间撒网。对于光谱定量、传感器标定这类 Y 与 X 有强关联的场景SPXY 的划分结果通常能让交叉验证的 RMSE 更稳定。3. 交叉验证与划分的耦合为什么划分完还要再切3.1 校正集内部还要做交叉验证SPXY 划分出校正集和验证集之后很多人直接在校正集上训练、在验证集上评估然后就结束了。但这里有个隐藏问题模型的超参数怎么定如果用验证集来调参验证集就“泄漏”了最终评估指标会偏乐观。正确的做法是在校正集内部再做交叉验证。比如 5 折交叉验证把校正集分成 5 份每次用 4 份训练、1 份验证循环 5 次取平均指标用来选超参数。定好超参数后再用整个校正集重新训练最后在独立的验证集上评估一次。这个流程里SPXY 负责“粗分”交叉验证负责“细分”两者配合才能得到可信的泛化误差估计。from sklearn.model_selection import KFold from sklearn.cross_decomposition import PLSRegression from sklearn.metrics import mean_squared_error def cv_pls(X_train, Y_train, max_components15, n_splits5): 在校正集内部做 K 折交叉验证选择 PLS 最优主成分数 kf KFold(n_splitsn_splits, shuffleTrue, random_state0) rmse_list [] for n_comp in range(1, max_components 1): fold_rmse [] for train_idx, val_idx in kf.split(X_train): pls PLSRegression(n_componentsn_comp) pls.fit(X_train[train_idx], Y_train[train_idx]) pred pls.predict(X_train[val_idx]) fold_rmse.append( np.sqrt(mean_squared_error(Y_train[val_idx], pred)) ) rmse_list.append(np.mean(fold_rmse)) best_n np.argmin(rmse_list) 1 return best_n, rmse_list这里n_splits5是经验值样本量少于 50 时可以用留一法LOO但 LOO 的计算量是 O(n)样本多的时候会慢。max_components一般不超过 15PLS 主成分数太多会过拟合。rmse_list记录了每个主成分数对应的交叉验证 RMSE选最小值对应的主成分数作为最终模型参数。3.2 划分比例与交叉验证折数的联动SPXY 划分时校正集占多少、验证集占多少和交叉验证的折数是有耦合的。如果校正集只有 30 个样本做 5 折交叉验证每折只有 6 个样本方差会很大这时候要么减少折数到 3 折要么增大校正集比例到 85%。常见的经验规则是校正集样本数至少是特征数的 510 倍交叉验证每折的验证样本数不少于 5 个。如果样本总量实在少可以考虑重复交叉验证repeated K-fold把随机种子换几次取平均降低划分随机性带来的波动。提示SPXY 划分本身是确定性的但交叉验证的折划分如果用了shuffleTrue每次运行结果会不同。做最终报告时要么固定随机种子要么用重复交叉验证取平均。4. 避坑与排查SPXY 落地时的五个翻车现场4.1 现象SPXY 划分后验证集指标比随机划分还差原因Y 的归一化没做或者 X 和 Y 的量纲差异过大导致联合距离实际上只反映了 X 的距离SPXY 退化成 KS。另一种可能是 Y 本身噪声很大把噪声也当成“距离”参与选点选出的校正集在真实 Y 上并不均匀。解决检查dx.max()和dy.max()的数量级如果相差超过两个数量级必须归一化。如果 Y 噪声大可以先对 Y 做平滑或剔除异常值再跑 SPXY。4.2 现象校正集和验证集的 Y 均值差异很大原因SPXY 的贪心选点策略倾向于选极端样本导致校正集里高浓度和低浓度样本偏多中间浓度样本被挤到验证集。这在样本量少的时候尤其明显。解决划分完成后画一下校正集和验证集的 Y 分布直方图如果均值差异超过一个标准差考虑改用分层 SPXY先按 Y 分箱每个箱内做 SPXY或者手动调整划分比例。4.3 现象交叉验证的 RMSE 曲线剧烈震荡原因折数太多而每折样本太少或者样本本身有批次效应不同折之间的数据分布差异大。SPXY 只保证了校正集整体的覆盖度没有保证每折内部的分布一致性。解决减少折数到 35 折或者在交叉验证之前先对校正集做一次 SPXY 排序按排序结果均匀分折而不是随机分折。4.4 现象SPXY 运行极慢几百个样本跑了几分钟原因cdist计算的是全距离矩阵复杂度 O(n²)贪心选点又是 O(n²) 的循环。样本量到几千时Python 循环会成为瓶颈。解决用 NumPy 向量化选点过程或者改用scipy.spatial.distance.cdist的metricsqeuclidean减少开方运算。样本量超过 5000 时可以考虑近似算法比如先聚类再在簇间做 SPXY。4.5 现象换了随机种子SPXY 结果不变但交叉验证结果变了原因SPXY 本身是确定性的但交叉验证的KFold如果设了shuffleTrue折划分会随种子变化。这不是 SPXY 的问题是交叉验证的随机性。解决如果希望整个流程完全可复现把交叉验证的shuffle设为False或者固定random_state。但要注意shuffleFalse时如果样本有顺序趋势折之间的分布会有偏。5. 设计取舍确定性划分与随机性评估的平衡SPXY 的核心价值在于“确定性”——同样的输入永远得到同样的划分这让不同模型之间的对比变得公平。但确定性也意味着它无法通过多次随机划分来估计划分本身带来的方差。随机切分虽然每次结果不同但你可以跑 100 次取平均得到一个更稳健的评估。我的做法是用 SPXY 做一次确定性划分固定校正集和验证集用于模型选型和超参数调优然后在最终报告时用随机切分跑 2050 次观察指标分布把 SPXY 的结果放在分布里对比。如果 SPXY 的指标落在随机分布的 50% 分位附近说明划分没有引入系统性偏差如果落在极端位置就要检查是不是 SPXY 的联合距离权重需要调整。划分策略确定性Y 空间覆盖计算复杂度适用场景随机切分否不保证O(n)样本量大、分布均匀KS是不保证O(n²)仅关注 X 空间覆盖SPXY是保证O(n²)X-Y 强关联、样本量中等分层 SPXY是更强O(n² kn)Y 分布极不均匀还有一个取舍是 X 和 Y 的权重。默认各占 0.5但如果你的 X 特征维度很高且信噪比低可以调低 X 的权重让 Y 主导选点反之如果 Y 噪声大就调高 X 的权重。这个权重没有理论最优值需要根据具体数据做敏感性分析。从那以后我每次做样本集划分都强制走一遍“SPXY 划分 → 校正集内交叉验证 → 独立验证集评估 → 随机划分对比”的流程再也不敢直接train_test_split了。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

K8s部署Redis实战:从StatefulSet设计到故障排查

K8s部署Redis实战:从StatefulSet设计到故障排查

部署Redis到K8s这件事,说简单也简单,说复杂也复杂。我见过太多人把Redis部署到K8s之后就当甩手掌柜,结果过两天数据全没了;也见过有人以为部署进去就万事大吉,客户端连接的时候才发现根本连不上。这篇文章是我自己在实…

2026/10/16 16:32:42 阅读更多 →
InternVL3_5-14B-HF训练流程深度拆解:从CPT到CascadeRL四阶段训练全景图

InternVL3_5-14B-HF训练流程深度拆解:从CPT到CascadeRL四阶段训练全景图

【免费下载链接】InternVL3_5-14B-HF 项目地址: https://ai.gitcode.com/OpenGVLab/InternVL3_5-14B-HF 点击查看 免费下载 InternVL3_5-14B-HF 是 InternVL3.5-14B 开源多模态大模型的 HuggingFace 标准格式检查点(15.1B 参数),…

2026/10/16 16:32:42 阅读更多 →
Mini-InternVL2-2B-DA-DriveLM架构深度剖析:InternViT+PixelShuffle+InternLM2的2B参数高效公式

Mini-InternVL2-2B-DA-DriveLM架构深度剖析:InternViT+PixelShuffle+InternLM2的2B参数高效公式

【免费下载链接】Mini-InternVL2-2B-DA-DriveLM 项目地址: https://ai.gitcode.com/OpenGVLab/Mini-InternVL2-2B-DA-DriveLM 点击查看 免费下载 Mini-InternVL2-2B-DA-DriveLM 是 OpenGVLab 开源的自动驾驶领域多模态大模型(VLM)&#xff0…

2026/10/16 16:32:42 阅读更多 →

最新新闻

等几何分析Matlab库:NURBS与PDE耦合的可调试实现

等几何分析Matlab库:NURBS与PDE耦合的可调试实现

简介:本资源是一个面向计算力学研究者与工程仿真工程师的等几何分析(IGA)专用MATLAB开源库,聚焦CAD模型与数值分析的无缝衔接,有效解决传统有限元中几何近似误差大、模型重构繁琐等痛点,特别适用于断裂力学…

2026/10/16 17:08:06 阅读更多 →
人脸识别SDK集成实战:从离线部署到阈值标定的完整指南

人脸识别SDK集成实战:从离线部署到阈值标定的完整指南

简介:这是一套免费提供的人脸识别开发包SDK,面向需要在应用中集成人脸检测、关键点定位、特征提取与比对功能的开发者。包体小巧,仅2.82MB,共214个文件,包含93个dat数据文件、34个h头文件、26个cpp源码文件&#xff0c…

2026/10/16 17:08:06 阅读更多 →
Qwen-Image-2.1-FP8快速开始:5步部署量化版AI绘图模型,完整安装指南

Qwen-Image-2.1-FP8快速开始:5步部署量化版AI绘图模型,完整安装指南

人工智能大模型基础模型媒体生成模型量化Qwen 【免费下载链接】Qwen-Image-2.1-FP8 项目地址: https://ai.gitcode.com/hf_mirrors/unsloth/Qwen-Image-2.1-FP8 点击查看 免费下载 Qwen-Image-2.1-FP8 是 Qwen-Image-2.1 文生图/图像编辑模型的 FP8 / INT8 量化版本…

2026/10/16 17:08:06 阅读更多 →
Content-Type 详解:HTTP响应头如何决定浏览器行为

Content-Type 详解:HTTP响应头如何决定浏览器行为

简介:Content-Type 是 HTTP 响应中的核心头字段,决定浏览器和客户端如何解析服务器返回的实体内容。这份 Word 学习文档从 HTTP 请求/响应模型切入,先交代头域组成与实体头位置,再系统讲解 Content-Type 的写法、用途及 MIME 注册…

2026/10/16 17:08:06 阅读更多 →
35岁程序员转型大模型:从RAG到Agent的实战路线图

35岁程序员转型大模型:从RAG到Agent的实战路线图

35岁,程序员,这两个词叠在一起,在现在的职场上很容易让人焦虑。但如果把目光从“焦虑”挪开,放到“大模型”这三个字上,你会发现一条完全不同的路。我身边好几个三十五六岁的老开发,去年还在愁晋升通道变窄…

2026/10/16 17:08:06 阅读更多 →
贝叶斯决策实战:不用公式也能做高质量判断

贝叶斯决策实战:不用公式也能做高质量判断

1. 这不是数学考试,而是你每天都在做的判断“贝叶斯决策”这四个字一出来,很多人第一反应是:哦,又是概率论里那个带公式的章节——先背公式,再套题,最后考完就忘。但事实是,你今天早上决定要不要…

2026/10/16 17:07:06 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →