数据工程:数据采集、清洗、去重、格式转换
做微调这么久,我越来越觉得一个道理:模型的上限由算法决定,但下限由数据决定。你想想看,再牛的模型,喂进去一堆垃圾,吐出来的只能是更精致的垃圾。
这一章,咱们就聊聊怎么把原始数据变成高质量微调数据集。说白了,就是数据工程的四个核心环节:采集、清洗、去重、格式转换。
数据采集:从哪里找数据?
数据采集是第一步,也是最容易被低估的一步。我个人习惯把数据来源分成三类:
- 公开数据集:比如 Hugging Face Datasets、阿里天池、百度飞桨社区。这些数据已经做过初步清洗,适合快速验证想法。
- 业务日志:公司内部积累的用户对话、客服记录、操作日志。这类数据最贴近真实场景,但噪声也最大。
- 爬虫数据:从网页、论坛、文档中抓取。嗯,这里要注意版权问题,别踩红线。
我在项目中遇到过最头疼的事:客户说「我有一亿条客服对话数据」,结果一打开,80% 是「嗯」「好的」「谢谢」这种无效对话。所以采集阶段就要想清楚——你要的是数量,还是质量?
数据清洗:把脏数据洗干净
数据清洗,说白了就是「去杂质」。我见过太多人拿到数据直接开训,结果模型学了一堆乱码和表情符号。
清洗工作通常包括:
- 去除 HTML 标签:爬虫数据里经常夹带
<div>、<span>这些标签,用正则或 BeautifulSoup 清理掉。 - 统一编码:UTF-8 是标配。我遇到过 GBK 编码的数据,训练时直接报错,排查了半天。
- 过滤过短/过长文本:少于 5 个字符的句子基本没意义,超过 2048 token 的文本容易撑爆显存。
- 去除特殊符号:比如连续的空格、制表符、不可见字符。
import re
def clean_text(text):
# 去除 HTML 标签
text = re.sub(r'<[^>]+>', '', text)
# 统一空格
text = re.sub(r'\s+', ' ', text)
# 去除特殊字符(保留中文、英文、数字、标点)
text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9,。!?、:;""''()]', '', text)
return text.strip()
数据去重:别让模型学重复了
为什么要去重?你想想看,如果训练数据里同一句话出现了一万次,模型会认为这句话特别重要,甚至把它当成「真理」来记忆。这不是学习,这是死记硬背。
去重的方法我常用两种:
- 精确去重:直接用哈希值比对,完全相同的文本只保留一份。适合短文本。
- 模糊去重:用 MinHash 或 SimHash 算法,计算文本相似度。适合长文本或语义相近但表述不同的数据。
我记得有一次做客服对话微调,数据里大量重复的「你好,请问有什么可以帮您?」。去重之后,数据量从 50 万降到 12 万,但模型效果反而提升了 15%。少即是多,这话在数据工程里特别对。
from datasketch import MinHash, MinHashLSH
def deduplicate(texts, threshold=0.8):
lsh = MinHashLSH(threshold=threshold, num_perm=128)
unique_texts = []
for i, text in enumerate(texts):
m = MinHash(num_perm=128)
for d in set(text.split()):
m.update(d.encode('utf8'))
# 检查是否与已有数据相似
if not lsh.query(m):
lsh.insert(i, m)
unique_texts.append(text)
return unique_texts
格式转换:JSON 还是 Parquet?
数据清洗干净了,接下来要转换成模型能吃的格式。目前主流的有两种:
| 格式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| JSON / JSONL | 可读性强,调试方便,生态好 | 文件大,解析慢 | 小规模数据、快速实验 |
| Parquet | 压缩率高,读取快,支持列式存储 | 可读性差,需要额外库 | 大规模数据、生产环境 |
我个人习惯:实验阶段用 JSONL,上线阶段转 Parquet。为什么?JSONL 每行一个样本,用 json.loads() 就能读,调试起来特别方便。但数据量上了百万级,JSONL 的 I/O 开销会让你怀疑人生。这时候 Parquet 的优势就出来了——读取速度能快 5-10 倍。
# JSONL 格式示例
{"instruction": "解释什么是微调", "output": "微调是指在预训练模型基础上,用特定领域数据继续训练..."}
{"instruction": "什么是过拟合", "output": "过拟合是指模型在训练数据上表现好,但在新数据上表现差..."}
# 转 Parquet
import pandas as pd
df = pd.read_json('data.jsonl', lines=True)
df.to_parquet('data.parquet', compression='snappy')
datasets 库,它原生支持 JSONL 和 Parquet,而且会自动做缓存。我建议直接用 datasets.load_dataset(),省去自己写数据加载的麻烦。
构建高质量微调数据集
最后,咱们聊聊怎么把上面这些环节串起来,构建一个真正能用的数据集。
我的经验是:先小后大,先手工后自动。别一上来就想搞百万级数据集。先拿几百条数据,手工清洗、标注、验证,跑一轮微调看看效果。效果好了,再扩大规模。
具体步骤:
- 定义数据格式:确定输入字段(如 instruction、input、output),保持统一。
- 采集原始数据:从业务日志或公开数据集中获取。
- 清洗与去重:用上面提到的方法,去掉噪声和重复。
- 格式转换:输出为 JSONL 或 Parquet。
- 质量抽检:随机抽取 5%-10% 的数据,人工检查。我曾经抽检时发现 30% 的数据标签是错的,还好及时发现,不然模型就学歪了。
好了,数据工程这部分就聊到这儿。下一章咱们会讲怎么用这些数据做微调训练,包括学习率设置、batch size 选择这些实战细节。到时候见。