数据集划分不是随便切:手把手切分大众点评情感数据集

昨天跑中文情感分类 baseline,一上来就栽在数据集上了。本来想着从魔搭拖个现成的大众点评数据集,加载完直接喂模型,结果 MsDataset.load 下来一看,整份数据全塞在 train 里,连个验证集测试集都没给分好。盯着控制台输出的单条样本愣了半分钟,得,自己动手切吧。

先看看拿到手的数据集长啥样

最开始的加载代码很简单,魔搭提供了专属的加载工具,一行就能拉下来。

python 复制代码
from modelscope.msdatasets import MsDataset

# 加载大众点评情感分类数据集
full_ms_ds = MsDataset.load(
    "DAMO_NLP/yf_dianping", 
    subset_name="default",
    split="train" 
)

# 瞅一眼第一条数据
print(full_ms_ds[0])

跑出来的第一条长这样:

每条数据就三个字段:sentence 是用户的真实评价原文,label 是情感二分类标签,dataset 标记数据来源。干净利落,典型的中文情感分析数据集。

我当时扫了一眼就往下写了,心想这么大个数据集,肯定自带 train/val/test 拆分吧,改个 split 参数就行。

坑来了我顺手把 split="train" 改成了 split="test",运行直接红了一片,报错说找不到 test 拆分。翻了数据集主页才反应过来 ------ 人家上传的时候就只打包了完整数据,所有样本都在 train 里,拆分得自己做。

就是这个 DAMO_NLP 出品的数据集,总共 21.72MB,四万多条评论,拿来做情感分类入门刚好。但架不住它不给切分啊,只能自己动手。

三份数据集到底分别干嘛的

切之前我先逼着自己把逻辑理了理,不然稀里糊涂切完也不知道为啥这么切。说实话我之前对这三个集的概念一直模模糊糊,背定义能背出来,但真到自己动手才反应过来背后的逻辑其实特别朴素 ------ 你就把模型当成一个要考试的学生。

训练集就是课本和讲义。模型反复看、反复学,从里面找字词规律、情感倾向,所有知识都从这儿来。对应到学生身上,就是上课学知识点、刷例题,占比最大,一般 80% 左右。

验证集就是课后作业。训练过程中每隔几轮就跑一次验证集,看看分数涨了还是跌了,错在哪类样本上,然后回去调学习率、改网络结构、加正则。它的作用是帮我们「调参」,让模型针对性补短板,大概占 10%。

测试集就是期末密封卷。从切分完那一刻起,训练全程绝对不能碰,也绝对不能拿它调参。等模型彻底训练完成,再拆封跑一次,这时候的分数才是模型的真实水平,测的是「泛化能力」------ 也就是遇上从没见过的新评论,还能不能准确判断情感。也差不多占 10%。

比例不是死的,数据量大的时候测试集可以再少点,数据量小就得换别的玩法,后面说。

8:1:1 划分的正确姿势

理清楚逻辑就好动手了。魔搭的数据集原生不带切分工具,但它支持转成 Hugging Face 的 Dataset 格式,人家自带 train_test_split,用起来特别顺手。

我第一反应是找有没有一次性切三份的参数,翻了五分钟文档才发现,人家一次就只能切两份。想切出 train/val/test 三份,得切两刀。

python 复制代码
from modelscope.msdatasets import MsDataset

# 1. 加载完整数据集
full_ms_ds = MsDataset.load(
    "DAMO_NLP/yf_dianping", 
    subset_name="default",
    split="train" 
)

# 2. 转成 Hugging Face 格式,别问我为啥知道要转,原生方法找了半小时
full_hf_ds = full_ms_ds.to_hf_dataset()

# 3. 第一刀:先切 10% 当测试集,封死,后面绝对不能碰
split1 = full_hf_ds.train_test_split(test_size=0.1, seed=42)
train_temp_hf = split1["train"] # 90%
test_hf = split1["test"] # 10%

# 4. 第二刀:剩下的 90% 再切 10% 当验证集
split2 = train_temp_hf.train_test_split(test_size=0.1, seed=42)
train_hf = split2["train"] # 80%
val_hf = split2["test"] # 10%

seed=42 一定要加,保证每次运行切出来的结果都一样。不然今天跑一个划分,明天跑一个划分,结果都没法对比,纯给自己找麻烦。

跑出来的结果是这样:

plaintext 复制代码
训练集 train:36436
验证集 val:4049
测试集 test:4499

总数据大概四万五千条,算下来比例差不多就是 8:1:1,刚好符合预期。随便抽一条训练集看看,标签 0 是负面评价,说 KTV 设备旧、环境差,也对得上。

为啥非得切这么麻烦?

可能有人觉得,不就是分个数据吗,差不多切切得了,至于这么较真?真至于。

验证集在训练过程中是频繁使用的 ------ 我们会盯着验证集的准确率调参数,哪个学习率效果好、哪层网络要加 dropout,全靠验证集反馈。说白了模型已经「间接见过」验证集的数据了,所以验证集分数通常会偏乐观,不能代表真实水平。

这时候必须有一份完全独立、模型从没见过的测试集,才能测出真正的泛化能力。要是你图省事,拿测试集当验证集调参,相当于考试提前把卷子给学生看了,分数看着特别高,一遇上真实数据直接拉胯。这就是常说的「数据泄露」,做实验的大忌。

我之前写课程作业就干过这事,把测试集混进去一起训了,准确率飙到 98%,还沾沾自喜。结果答辩的时候老师拿新数据一测,直接掉了二十个点,尴尬得我抠脚。

数据少的时候怎么办:交叉验证

当然 8:1:1 也不是万能的。如果数据集特别小,比如只有几千条,硬切出 10% 当测试集,一来数据浪费,二来结果不稳定,抽不同的样本测试分数差很多。

这时候一般会用 k 折交叉验证。思路也很简单:把整个数据集平分成 k 份,每次挑 1 份当测试集,剩下 k-1 份拿去训练,循环 k 次,每一份数据都当过一次测试集,最后取 k 次结果的平均值。

这样所有数据都参与了训练,也都参与了测试,数据利用率高,评估结果也更稳。行业里一般 k 取 10,也就是十折交叉验证。

这次大众点评数据集有四万多条,量足够大,固定划分就够用,我就没折腾交叉验证。但要是以后做小样本任务,肯定绕不开这个。

再往深聊:数据才是大模型的地基

折腾切分的时候我顺便翻了点资料,越看越觉得之前的认知太浅了。以前我总觉得大模型厉害,全靠 Transformer 架构设计得牛,算法牛逼。这段时间接触数据多了才发现,算法反而是门槛最低的部分 ------ 架构论文都是公开的,真正拉开差距的,全在数据上。

数据不是攒够了往模型里一扔就行。从采集、清洗、去重、标注,到训练过程中动态调整数据配比、淘汰劣质样本、检测数据污染,这一整套数据工程的活儿,才是真正的核心竞争力。

而且现在大模型的数据管理根本不是静态的,不是分完三份就放着不动了。训练中会不断评估,哪些数据模型总学不好、哪些数据有噪声会拖后腿,就拿回去重新清洗采样;验证集也会动态更换,防止模型过拟合到验证集上。还要控制知识覆盖率,中文英文、各个领域、不同模态都得兼顾,不能偏科。

说句实在的,在模型之上是算法的艺术,在模型之下,全是数据的地基。地基打不牢,再花哨的架构都没用。

最后说几句

一下午折腾下来,从最开始加载报错,到一步步把数据集切明白,再顺着想清楚背后的逻辑,感觉比背十遍定义都管用。

实打实的收获有三个:第一,数据集划分从来不是走个形式,它本质是在模拟「学习 - 作业 - 考试」的认知规律,核心目的是测出模型的真实能力,而不是刷出好看的分数。第二,开源数据集别想当然,拿到手先看结构、看字段、看有没有内置拆分,上来就瞎写参数,大概率踩坑。第三,别总盯着模型结构死磕,很多时候数据侧的优化收益更大。清洗、去重、配比,每一项做好了都能涨点。

当然也不是划分越复杂越好,简单场景下 8:1:1 足够用,别为了炫技硬上交叉验证,反而增加复杂度。适合自己的数据量和业务场景最重要。

你们平时处理文本数据集的时候,有没有踩过什么印象深刻的坑?比如标签标错了、数据泄露了,或者切分的时候把顺序搞乱了?评论区唠唠,我也攒点避坑经验。

相关推荐
冬奇Lab2 小时前
每日一个开源项目(第142篇):android/skills - Google 官方 Android 开发 AI Skill 库
人工智能·开源·资讯
冬奇Lab2 小时前
Skill 系列(06):Skill 工程化与治理——路由准确率 38%、压缩节省 76%
人工智能·开源·agent
IT_陈寒4 小时前
Vue这个坑我跳了两次,原来问题出在这
前端·人工智能·后端
新新技术迷5 小时前
Node给AI接口做SSE代理与鉴权
人工智能
redreamSo5 小时前
大模型是不是到顶了?瓶颈到底在哪
人工智能·openai
Oo9206 小时前
Tool Use 背后的技术逻辑
人工智能
姗姗来迟了6 小时前
Vue3封装AI流式对话组件踩坑实录
人工智能
码上天下6 小时前
用Pinia管理AI多会话状态
人工智能