做机器学习项目这些年,我见过太多人把八成精力砸在调模型、调超参上,结果模型在本地跑出漂亮指标,一上线就拉胯。回头排查半天,问题往往出在最不起眼的一步——训练集和测试集怎么划分。这个环节看起来就是调个库、切一刀的事,但它直接决定了你评估出来的指标到底可不可信。划分得不对,你后面所有的模型选择、阈值调整、特征取舍,全都建立在错误的反馈上,越努力偏得越远。
这篇内容我想把“训练集和测试集划分”这件事彻底讲透。它适合刚入门、还在用默认参数切数据的同学,也适合做了几个项目、但没系统想过划分策略背后逻辑的从业者。我会从划分的整体设计思路讲起,拆解几种主流划分方式的适用场景和取舍理由,再落到可直接复现的实操步骤和参数选择,最后把我自己踩过的坑和排查经验整理出来。核心关键词训练集、测试集、数据划分、数据泄漏、分层抽样、交叉验证会自然贯穿全文,你跟着读下来,基本能把这一刀切得明明白白。
1. 数据划分的整体设计与思路拆解
1.1 为什么“切一刀”值得单独拿出来讲
很多人对数据划分的理解停留在“把数据按 8:2 分一下”这个层面。这个理解不能算错,但它只覆盖了最简单的一种情形。真实项目里,数据划分要回答的问题远比这复杂:你的数据有没有时间顺序?类别是不是严重不平衡?同一个用户/同一台设备是否产生了多条样本?测试集要模拟的是“未来数据”还是“同分布随机数据”?这些问题的答案不同,划分方式就完全不同。
我打个生活化的比方。假设你要考核一个学生的真实水平,你手里有一本习题册。如果你把习题册里的题随机抽 20% 留作考试,剩下的 80% 给他练习,那这个考核是合理的——练习题和考题来自同一个题库,难度分布一致。但如果你把习题册前 80 页给他练,后 20 页拿来考,而习题册恰好是按难度递增编排的,那这个考核就失真了,因为考题系统性偏难。数据划分也是同样的道理:划分方式必须让测试集的分布尽可能贴近模型上线后真正会遇到的数据分布,否则评估结果就是自欺欺人。
这就是划分环节的核心设计原则,我把它总结成一句话:测试集不是用来“陪跑”的,它是模型上线前的最后一道模拟考,模拟得越真,你上线时越有底。围绕这个原则,后面所有的方案选型、参数设置才有依据。
1.2 三种划分思路的取舍逻辑
从大方向上看,数据划分可以归为三类思路,它们对应不同的数据特性和业务目标。
第一类是随机划分,也就是最常被提到的按比例随机切分。它的前提假设是样本之间相互独立、分布一致,测试集和训练集来自同一个总体。绝大多数入门教程默认用这种方式,因为实现简单、适用面广。但它的隐患在于,一旦样本之间存在隐藏的关联(比如同一用户的多条记录),随机切分会让关联样本同时出现在训练集和测试集里,造成数据泄漏,指标虚高。
第二类是分层划分,在随机的基础上保证各个子集的类别比例与整体一致。分类任务里类别不平衡是常态,比如正负样本 1:99 的风控场景,纯随机切分很可能让测试集里正样本少得可怜,评估结果波动极大。分层抽样能稳住这个比例,让评估更可靠。
第三类是按时间或按组划分,适用于数据本身带有时序性或分组结构的情形。时序数据必须用“过去训练、未来测试”的方式切,绝不能随机打乱,否则等于用未来信息预测过去,属于典型的数据泄漏。分组数据(同一实体多条样本)则要保证同一组的所有样本只出现在一个子集里,避免组内信息串味。
这三种思路没有绝对优劣,关键看你的数据长什么样、业务要模拟什么场景。我在实际项目里的判断顺序通常是:先看有没有时间字段和分组字段,有就优先考虑时序或分组划分;没有的话再看类别是否平衡,不平衡就上分层;都还好就用随机划分。这个判断链条后面我会展开讲具体怎么落地。
1.3 训练集、验证集、测试集三者的分工
严格来说,一个规范的划分不止训练集和测试集两份,中间还应该有一个验证集。三者的分工是这样的:训练集用来拟合模型参数,验证集用来做模型选择和超参调整,测试集只在最后用一次,给出对泛化能力的无偏估计。
为什么要把验证集单独拎出来?因为如果你反复用测试集来调超参,测试集的信息就会一点点泄漏进模型选择过程,你最终报告的那个指标就不再是无偏的了,它会偏乐观。这就像考试前你偷偷看了考卷并针对性复习,考出来的分数不能代表你的真实水平。验证集的存在就是为了给调参过程提供一个“可以反复看”的集合,把测试集保护起来,只在最终验收时用一次。
实践中常见的比例是训练:验证:测试 = 6:2:2 或 7:1.5:1.5,数据量小的时候验证集比例可以适当放大,因为小数据下验证指标的方差很大,需要更多样本才能稳住。数据量特别大的时候(比如百万级以上),测试集占比可以压到 5% 甚至更低,因为绝对数量已经足够支撑稳定的评估。这个比例不是死规定,核心是保证每个子集都有足够的样本量来支撑其承担的职责。
2. 核心细节解析与实操要点
2.1 随机划分的实现与随机种子的重要性
随机划分是最基础的起点,用现成的库一行代码就能完成。以常见的 Python 生态为例,train_test_split是最常用的入口,核心参数就几个:测试集比例、随机种子、是否分层。看起来简单,但每个参数背后都有讲究。
随机种子(random_state)这个参数,新手最容易忽略,老手最不敢忽略。它的作用是固定随机数生成器的初始状态,保证每次运行切出来的子集完全一致。为什么重要?因为如果你不固定种子,每次跑实验切出来的训练集和测试集都不一样,模型指标一波动,你根本分不清是模型改动带来的提升,还是数据切分带来的噪声。我见过有人调了一下午超参,指标忽高忽低,最后发现是没固定种子,白忙活。固定种子是可复现实验的最低要求,这一点怎么强调都不过分。
测试集比例的选择也有讲究。常见的 0.2 到 0.3 是经验值,但真正该看的是测试集的绝对样本量。如果你的数据集只有几百条,20% 的测试集只有几十条,评估结果的置信区间会非常宽,这时候要么扩大测试集比例,要么改用交叉验证来充分利用数据。反过来,数据量上到十万级,10% 的测试集就有一万条,足够稳定了,没必要留太多,把数据尽量留给训练。
from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, # 测试集占比 20% random_state=42, # 固定随机种子,保证可复现 shuffle=True # 切分前是否打乱,时序数据必须设为 False )这段代码几乎是所有项目的起点,但请注意shuffle参数。默认是 True,也就是切分前先打乱顺序。对于独立同分布的数据这没问题,但如果你的数据是按时间采集的,打乱就等于把未来样本混进了训练集,这是数据泄漏的重灾区。后面讲时序划分时会专门说这个坑。
2.2 分层抽样:类别不平衡场景的救命稻草
分类任务里,类别不平衡几乎是常态。假设你做一个欺诈检测,正样本占比 2%,总共一万条数据。如果纯随机切 20% 做测试集,测试集里正样本的期望数量是 40 条,但实际可能多几条少几条,波动很大。更糟的是,如果某次切分恰好正样本特别少,你算出来的召回率、F1 会剧烈跳动,根本没法用来做决策。
分层抽样(stratified sampling)解决的就是这个问题。它的做法是在切分时保证训练集和测试集里各类别的比例与原始数据一致。上面那个例子里,分层之后测试集的正样本比例会稳定在 2% 左右,评估指标的方差大幅下降。实现上,train_test_split提供了stratify参数,传入标签列即可。
X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y # 按标签 y 的分布分层 )这里有个细节值得说:分层不仅对二分类有用,多分类同样适用,甚至回归任务在把目标值分箱之后也可以做分层,保证目标值分布在各子集里一致。我做过一个房价预测的项目,目标值长尾分布很严重,直接随机切分导致测试集里高价房比例偏高,模型评估偏差明显,后来把价格分箱后做分层,评估才稳定下来。
注意:分层的前提是每个类别都有足够的样本。如果某个类别只有一两个样本,分层会直接报错,因为无法在训练集和测试集里同时保证比例。这种情况下要么合并稀有类别,要么改用其他策略。
2.3 时序数据划分:绝不能打乱顺序
时序数据是数据划分里最容易翻车的地方,没有之一。所谓时序数据,就是样本之间有时间先后关系,比如每天的销量、每分钟的传感器读数、按时间排列的交易记录。这类数据的核心特性是:未来的数据依赖于过去,但过去不依赖未来。
如果你对时序数据用随机划分,会发生什么?训练集里混进了“未来”的样本,模型在训练时已经见过未来的信息,测试时自然表现很好,但这个好是假的。上线之后模型只能看到历史数据去预测未来,性能立刻打回原形。这就是时序场景下最典型的数据泄漏。
正确的做法是按时间切分:用较早的一段做训练,较晚的一段做测试,中间可以再切一段做验证。比如你有三年的日度数据,可以前两年训练、第三年上半年验证、下半年测试。这样测试集模拟的正是“用历史预测未来”的真实场景。
# 假设 df 已按时间升序排列 n = len(df) train_end = int(n * 0.7) val_end = int(n * 0.85) train = df.iloc[:train_end] val = df.iloc[train_end:val_end] test = df.iloc[val_end:]这段代码的关键在于数据必须先按时间排序,然后按位置切片,绝不能 shuffle。我踩过的一个坑是:数据从数据库导出来的时候顺序是乱的,我没检查就直接切,结果训练集里混了后期的数据,模型指标好得离谱,上线后一塌糊涂。后来养成了习惯,切分前第一件事就是按时间字段排序并打印首尾时间戳核对。
还有一个进阶技巧叫滚动划分(walk-forward),适合数据量较大、需要多次评估的场景。做法是设定一个固定的训练窗口,每次向后滑动一步,用窗口内的数据训练、紧接着的一段测试,多次滚动后把各次的评估结果平均。这种方式能更充分地利用数据,也能观察模型在不同时间段的稳定性,比单次切分更可靠。
2.4 分组划分:同一实体不能跨子集
有些数据天然带有分组结构,比如同一个用户的多条行为记录、同一台设备的多条日志、同一个病人的多次检查。这类数据如果随机切分,同一个实体的样本会同时出现在训练集和测试集里,模型可能记住了这个实体的特征,测试时自然表现好,但这不代表它能泛化到新实体上。
举个具体例子。假设你做一个用户流失预测,每个用户有多条行为记录。随机切分后,用户 A 的三条记录可能两条在训练集、一条在测试集。模型在训练时已经见过用户 A 的行为模式,测试时对用户 A 的那条记录预测得很准,但上线后面对的是全新用户,性能会明显下降。这就是分组泄漏。
正确的做法是按实体分组划分,保证同一个实体的所有样本只出现在一个子集里。实现上可以用GroupShuffleSplit,传入分组标识列。
from sklearn.model_selection import GroupShuffleSplit gss = GroupShuffleSplit(n_splits=1, test_size=0.2, random_state=42) train_idx, test_idx = next(gss.split(X, y, groups=user_ids)) X_train, X_test = X.iloc[train_idx], X.iloc[test_idx] y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]判断要不要用分组划分,有个简单的自检方法:问自己“测试集里的样本,其所属实体在训练集里出现过吗?如果出现过,这合理吗?”如果答案是不合理,那就必须分组。这个判断在推荐系统、风控、医疗等场景里尤其关键。
2.5 交叉验证:小数据时代的评估利器
当数据量不大时,单次划分的评估结果方差很大,切得好指标高,切得差指标低,很难判断模型的真实水平。交叉验证(cross-validation)就是为这种场景设计的。最常用的是 K 折交叉验证:把数据分成 K 份,每次拿其中一份做验证、其余 K-1 份做训练,重复 K 次,最后把 K 次的评估结果平均。
K 的取值通常是 5 或 10。5 折计算量适中,偏差和方差的平衡较好;10 折更充分但计算量翻倍。数据量特别小的时候可以用留一法(LOOCV),每次只留一个样本做验证,但计算量随样本数线性增长,样本上千就不实用了。
from sklearn.model_selection import cross_val_score, StratifiedKFold cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) scores = cross_val_score(model, X, y, cv=cv, scoring='f1') print(f"F1 均值: {scores.mean():.4f}, 标准差: {scores.std():.4f}")注意这里用的是StratifiedKFold而不是普通的KFold,原因和前面讲分层抽样一样——分类任务要保证每折的类别比例一致。另外,交叉验证的结果要同时看均值和标准差,均值反映平均水平,标准差反映稳定性。如果标准差很大,说明模型对数据切分敏感,这时候光看均值会误导决策。
交叉验证还有一个容易忽略的点:它评估的是“模型配置”的好坏,不是最终模型的好坏。你通过交叉验证选定了超参之后,还应该用全部训练数据重新训练一个最终模型,再用独立的测试集做最终验收。测试集在整个调参过程中都不能碰,这一点前面已经强调过,这里再提醒一次。
3. 实操过程与核心环节实现
3.1 从原始数据到可用子集的完整流程
光讲理论不够,我把一个完整的划分流程从头到尾走一遍,你可以直接对照自己的项目复现。假设我们有一个二分类数据集,带有时间字段、用户分组字段和类别标签,这个设定覆盖了前面讲的多种情形,便于演示判断逻辑。
第一步是数据体检。切分之前先摸清数据的基本情况:总样本量、类别分布、时间跨度、分组数量、缺失值情况。这一步很多人跳过,但它决定了后面所有策略的选择。我通常会写一小段探查代码,把关键统计量打印出来。
import pandas as pd print(f"总样本数: {len(df)}") print(f"类别分布:\n{df['label'].value_counts(normalize=True)}") print(f"时间范围: {df['ts'].min()} ~ {df['ts'].max()}") print(f"分组数量: {df['user_id'].nunique()}") print(f"每组平均样本数: {len(df) / df['user_id'].nunique():.1f}")第二步是确定划分策略。根据体检结果做判断:有时间字段且业务需要模拟未来预测,走时序划分;有分组字段且同一实体多条样本,走分组划分;类别不平衡,叠加分层;以上都不涉及,用随机划分。注意这些策略可以组合,比如时序划分的同时也可以在每个时间段内做分层。
第三步是执行切分并核对。切完之后一定要核对子集的统计特征,确认分布符合预期。核对内容包括各子集的样本量、类别比例、时间范围是否重叠、分组是否交叉。这一步是防止低级错误的关键,我后面会专门讲怎么核对。
第四步是固化切分结果。把切分好的索引或子集保存下来,后续所有实验都用同一份切分,保证可比性。可以用 pickle 保存索引,也可以直接把子集落成文件。这一步在团队协作里尤其重要,避免每个人各切各的导致结果无法对比。
3.2 参数选择背后的计算过程
划分过程中涉及几个需要拍板的参数,我把选择逻辑和计算过程摊开讲。
测试集比例。前面说过要看绝对样本量。一个实用的经验是:测试集至少要有几百条样本,评估指标才相对稳定。假设你的数据有一万条,测试集比例取 0.2 就是两千条,足够;如果只有一千条,0.2 只有两百条,偏少,可以考虑 0.3 或者改用交叉验证。更严谨的做法是用置信区间来估算:分类任务里,评估指标的标准误大致与 1/√n 成正比,n 是测试集样本量。测试集一千条时标准误约 3%,三百条时约 6%,你可以根据对精度的要求反推需要的样本量。
K 折的 K 值。K 越大,每次训练用的数据越多,评估偏差越小,但计算量越大,且各折之间的相关性越强。5 折是通用默认值,数据量小(几百条)可以用 10 折榨取更多信息,数据量大(十万级以上)用 3 折甚至单次划分就够了,因为大样本下单次评估已经足够稳定。
随机种子的选取。种子本身取什么值无所谓,42、0、2024 都行,关键是整个项目统一。我习惯在项目配置里定义一个全局种子常量,所有涉及随机的地方都引用它,避免不同模块用了不同种子导致结果对不上。
分层字段的选择。分类任务用标签分层是标准做法。但如果类别特别多(比如上百类),每类样本又少,分层可能报错,这时候可以退而求其次,只对主要类别分层,或者改用分组划分。回归任务可以把目标值分箱后分层,箱数取 5 到 10 比较合适,箱太多会导致每箱样本不足。
3.3 切分结果的核对清单
切分执行完,别急着训练模型,先花两分钟核对。我整理了一份核对清单,每次切分后逐项过一遍,能挡掉大部分低级错误。
| 核对项 | 检查方法 | 异常表现 |
|---|---|---|
| 样本量 | 打印各子集长度 | 某子集为空或远小于预期 |
| 类别比例 | 对比各子集与整体的标签分布 | 比例偏差超过几个百分点 |
| 时间范围 | 打印各子集时间字段的最小最大值 | 训练集时间晚于测试集 |
| 分组交叉 | 检查分组 ID 是否跨子集 | 同一 ID 出现在多个子集 |
| 特征分布 | 对比各子集关键特征的均值方差 | 分布明显偏移 |
| 缺失值 | 统计各子集缺失比例 | 某子集缺失异常集中 |
这张表里,时间范围和分组交叉两项是时序和分组场景的必查项,也是最容易出问题的地方。特征分布对比则是一个通用的健康检查,如果训练集和测试集在某个关键特征上分布差异很大,说明切分可能有问题,或者数据本身存在漂移。
3.4 把划分固化进项目流程
划分不是一次性动作,它应该固化进项目流程。我的做法是写一个独立的划分脚本,输入原始数据,输出切分好的子集文件和一份划分元信息(记录种子、比例、策略、各子集样本量等)。后续所有训练、评估脚本都读取这份固化结果,不再各自切分。
这样做的好处有三个:一是可复现,任何人拿到这份划分结果都能复现实验;二是可对比,不同模型、不同特征工程都在同一份切分上评估,结果可比;三是可追溯,划分元信息记录了当时的决策依据,过几个月回头看也知道为什么这么切。
import json import pickle # 保存切分索引 with open('split_indices.pkl', 'wb') as f: pickle.dump({'train': train_idx, 'test': test_idx}, f) # 保存划分元信息 meta = { 'strategy': 'stratified_random', 'test_size': 0.2, 'random_state': 42, 'n_train': len(train_idx), 'n_test': len(test_idx), 'label_dist_train': y.iloc[train_idx].value_counts(normalize=True).to_dict(), 'label_dist_test': y.iloc[test_idx].value_counts(normalize=True).to_dict(), } with open('split_meta.json', 'w') as f: json.dump(meta, f, indent=2, ensure_ascii=False)这份元信息在团队协作和项目交接时价值极高,尤其是当项目运行了几个月、原始数据又更新过之后,你能清楚知道当初的划分是怎么做的,避免重复踩坑。
4. 常见问题与排查技巧实录
4.1 指标虚高:数据泄漏的典型症状与定位
数据泄漏是划分环节最隐蔽也最致命的问题。它的典型症状是:本地评估指标好得反常,比如准确率 99%、AUC 0.99,但上线后性能断崖式下跌。遇到这种情况,第一反应就该怀疑数据泄漏。
定位泄漏有一套系统的排查思路。先查时间泄漏:训练集里有没有晚于测试集时间戳的样本?如果有,说明切分时没按时间排序或者 shuffle 了。再查分组泄漏:同一个用户/设备/实体的样本是否跨了子集?用分组字段做一次交集检查就能发现。然后查特征泄漏:有没有特征是在目标发生之后才产生的?比如预测用户是否流失,却用了“流失后 30 天的登录次数”这种特征,这种特征在训练时存在、上线时拿不到,属于典型的未来信息泄漏。
我踩过最隐蔽的一次泄漏是特征工程阶段引入的。当时做了一个滑动窗口统计特征,窗口计算时不小心把当前样本之后的数据也算了进去,导致每个样本的特征里都混入了未来信息。这种泄漏在划分环节查不出来,得回到特征工程代码里逐行核对时间边界。所以排查泄漏不能只盯着划分,要顺着数据流从原始数据一路查到模型输入。
提示:一个快速验证泄漏的方法是把模型换成一个极简单的基线(比如逻辑回归甚至随机猜测),如果复杂模型和简单模型的指标差距大得离谱,往往说明有泄漏在起作用,因为真实任务里复杂模型的提升通常是渐进的。
4.2 类别不平衡导致的评估失真
类别不平衡场景下,即使划分正确,评估指标本身也可能误导人。假设正样本占比 1%,一个把所有样本都预测为负的“傻瓜模型”准确率高达 99%,但这个模型毫无价值。这时候看准确率就是自欺欺人。
解决办法有两个层面。划分层面,用分层抽样稳住各子集的类别比例,减少评估方差。评估层面,换用对不平衡不敏感的指标,比如 F1、AUC-ROC、AUC-PR、召回率等。特别是 AUC-PR,在极度不平衡的场景下比 AUC-ROC 更能反映模型对少数类的识别能力,因为 ROC 曲线在负样本极多时会被“稀释”,看起来很美但实际没用。
还有一个实操细节:分层抽样时如果某个类别样本极少(比如只有个位数),分层会报错。这时候的处理方式是先把稀有类别合并成“其他类”,或者干脆从数据里剔除(如果业务上允许),保证每个类别在训练集和测试集里都有足够样本。硬要分层只会让代码崩掉。
4.3 小数据集的划分困境与应对
数据量小是很多实际项目的常态,几百条甚至几十条样本的情况并不少见。这种场景下,单次划分的评估结果极不稳定,切分方式稍微一变指标就大幅波动。应对策略有这么几条。
首选是交叉验证,把每一份数据都轮流当一次验证集,充分利用有限样本。K 取 5 到 10,配合分层保证每折类别比例一致。如果数据量实在太小(比如几十条),可以用留一法,虽然计算量大但能榨干每一条数据的信息。
其次是重复划分。单次随机划分做多次(比如重复 10 次不同种子),把各次结果平均,也能降低方差。这种做法实现简单,适合快速评估。
最后是谨慎解读结果。小数据下的评估指标置信区间很宽,报告结果时应该给出区间而不是单点值。比如“F1 为 0.75,95% 置信区间 [0.68, 0.82]”,比单纯说“F1 为 0.75”诚实得多,也能让决策者理解不确定性。
4.4 划分策略速查表
把前面讲的判断逻辑整理成一张速查表,遇到新项目时对照着走一遍,基本不会选错策略。
| 数据特征 | 推荐策略 | 关键参数 | 主要风险 |
|---|---|---|---|
| 独立同分布、类别平衡 | 随机划分 | test_size, random_state | 样本量不足导致评估不稳 |
| 类别不平衡 | 分层随机划分 | stratify=标签 | 稀有类别样本过少报错 |
| 带时间顺序 | 按时间切分 | 切分点位置 | 未排序导致时间泄漏 |
| 同一实体多条样本 | 分组划分 | groups=实体ID | 分组字段选错导致泄漏 |
| 数据量小 | 交叉验证 | n_splits, shuffle | 计算量大、方差仍偏高 |
| 时序+不平衡 | 时序切分+段内分层 | 时间切分点+分层字段 | 实现复杂度上升 |
这张表不是穷举,实际项目里往往是多种特征的组合,需要灵活叠加策略。比如时序数据里如果类别也不平衡,可以在每个时间段内再做分层,保证各时间段的类别比例一致。组合策略的实现会复杂一些,但核心逻辑不变:让测试集尽可能贴近上线后的真实分布。
4.5 几个容易被忽略的实操心得
最后分享几个我在实际项目里攒下来的心得,都是文档里不会写、但踩过坑才明白的细节。
第一个心得:切分前先做数据去重。重复样本如果跨了训练集和测试集,等于变相泄漏。我遇到过一次,数据源合并时产生了大量重复记录,随机切分后训练集和测试集里都有相同的样本,模型指标虚高。后来在划分脚本里加了一步去重,问题才解决。去重不一定要删,但至少要保证重复样本只落在一个子集里。
第二个心得:测试集要“锁起来”。物理上把测试集单独存一个文件,权限上限制访问,流程上规定只有最终验收才能碰。人都是有惰性的,测试集放在手边,调参调不动的时候难免想“就看一眼”,看一眼就收不住了。锁起来是最有效的自律手段。
第三个心得:划分策略要写进项目文档。别觉得这是小事,项目交接时如果没写清楚当初怎么切的,接手的人很可能用错误的方式重新切一遍,之前所有实验结论都得推翻。我现在的习惯是在项目 README 里专门留一节讲数据划分,把策略、参数、种子、各子集样本量都写清楚。
第四个心得:定期复查划分是否仍然合理。数据是会变的,几个月前合理的划分策略,数据分布漂移之后可能就不合理了。比如业务扩张后新增了大量新用户,原来的分组划分可能让测试集里全是新用户,评估结果不能代表整体。定期(比如每个迭代周期)重新体检数据、复查划分策略,是保证评估持续可信的必要动作。
这些心得听起来琐碎,但每一条背后都是一次真实的翻车。数据划分这件事,做对了不显眼,做错了处处是坑。把上面这些逻辑和细节吃透,你切出来的数据才真正撑得起后续的模型评估,指标也才敢拿去汇报和上线。