☰
MindSpore大模型预训练数据清洗与质量过滤实战
2026/10/2 10:19:48 网站建设 项目流程

做预训练的人常说一句话:数据不行,模型就废了。模型结构可以抄,训练框架可以换,但数据质量这件事,没有任何一个现成的模型能替你兜底。我在用MindSpore跑大模型预训练时,前期最耗时间的不是调并行策略,也不是对齐loss曲线,而是和数据死磕——准确说是跟一锅乱炖的原始语料死磕。这篇就把“数据质量过滤”真正落到MindSpore训练流程里的整套方案拆开讲清楚,包括思路、方法、可复现的步骤和我踩过的坑。

文章面向两类人:一类是刚开始做预训练、正被脏数据折磨的工程师,另一类是已经有清洗经验、但想在MindSpore上把流程规范化的朋友。内容不涉及某个特定大模型的完整训练配置,重点放在“如何让送进模型的文本更干净”这件事上。我会把过滤拆成规则过滤、去重过滤、质量评估过滤三个层次,讲清楚每个层次怎么在MindSpore的数据管线里落地,也会顺便聊几个数据清洗的常见误判场景——这些在开源文档里基本找不到。

1. 预训练第一道关口:数据质量决定了模型的上限

1.1 一个真实的“数据翻车”现场

有一次我训一个中等规模的通用模型,语料来自爬虫、开源数据集和内部积累的混合来源。刚开始一切正常,loss稳步下降,训练到第三个epoch时发现下游阅读理解分数明显偏低,而去重测试里模型又能“背”出大量训练样本原文。排查后发现问题很典型:数据里混了20%左右的重复新闻和广告文本,其中新闻站点的正文抽取失败,把导航栏、版权声明、“相关阅读”全都当成正文存了进去。模型在这堆垃圾上“认真”学了很久,最后把“点击了解更多”也当成了一种语言规律。

这个案例告诉我们一个朴素的道理:预训练模型的语言能力上限,大概率不取决于模型结构选得多花哨,而取决于喂进去的文本本身干不干净。“垃圾进垃圾出”在大模型场景里会被无限放大,因为参数量越大,模型对数据中的噪声模式就越敏感,记忆得也越牢固。

1.2 数据质量问题的常见类型

做过滤之前,得先知道自己面对的是什么脏。我在实践中把预训练数据的质量问题分成七类,每一类的处理方式差别很大:

  • 重复数据:包括完全重复、近似重复、跨来源转载。典型的是同一篇新闻被几十个站点转载,或者同一个开源数据集被反复拼接。
  • 噪声文本:HTML标签残留、导航栏、版权声明、乱码字符、全角半角混杂、不可见Unicode字符。
  • 低质量内容:过短文本、纯列表文本、广告语、无意义重复句(比如“哈哈哈”刷屏)、机器生成的低质量灌水内容。
  • 有害与隐私内容:需要按安全规范拦截,这部分我建议单独建规则库,不要和质量过滤混在一起。
  • 错误信息与误导文本:比如把“感冒治愈率100%”这类伪科学混进医疗语料里。
  • 语言混杂:目标语言以外的其他语言文本,以及中英混杂到无法学习的噪声文本。
  • 格式异常:编码错误导致的浣熊字符串、表格转换丢失、BOM头残余等。

每一类问题对应不同过滤手段。一上来就上大模型打分往往成本高效果差,正确做法是先用低成本手段把能量化的噪声干掉,再用稍微昂贵的模型做语义层面的筛选。

1.3 过滤方案的整体设计思路

我的整体设计是一个“漏斗式”三级过滤链:

第一级是规则过滤,目标是“快、狠、准”,用正则、字数、符号比例这些零成本规则,干掉明显不合格的样本。第二级是去重过滤,先用精确哈希去重,再用MinHash做近似去重,这一级主要解决训练数据重复导致的过拟合和评估失真问题。第三级是质量评估过滤,用小模型计算困惑度、做文本分类打分,把“能读但质量不高”的文本筛掉。

这个顺序不能乱,原因是成本递增:规则过滤几乎不消耗算力且可解释性强,去重需要一定计算量,困惑度评估要跑模型,最贵的一定放在最后。更重要的是,如果前两级做得干净,第三级模型打分的方差会小很多,阈值也更好定。

2. MindSpore上的数据链路与过滤时机

2.1 数据在MindSpore中的流转路径

MindSpore的数据管线核心是Dataset模块。原始数据一般以JSONL、CSV或元组列表形式存在,通过GeneratorDataset或MindDataset加载进训练流程。这里有个关键选择:要不要提前转成MindRecord格式。

我强烈建议在正式训练前把清洗后的数据转成MindRecord。所谓MindRecord可以理解为一种二进制存储格式,它把样本按schema组织在一起,读取时省去了反复解析文本的开销,还天然支持按num_shards和shard_id做分布式分片。实测下,用MindDataset读MindRecord,比用GeneratorDataset逐行读JSONL快很多,尤其在数据量几百GB以上的时候,这个差距会直接影响训练吞吐。

从原始文件到训练样本的完整路径是:原始语料(各种来源) → 离线清洗过滤 → 统一格式(JSONL) → 二次过滤(去重+打分) → MindRecord → MindDataset加载 → map做tokenize → batch → 模型输入。

2.2 过滤放在离线还是在线

这是很多人纠结的点。我把过滤拆成两层来看:能离线做完的,绝不放在线;必须在线做的,尽量轻量。

离线阶段适合做所有重计算:全量去重、困惑度打分、语言识别、规则过滤、人工抽检。这个阶段可以随便用Spark、多进程、GPU推理,反正不走训练流程,怎么舒服怎么来。在线阶段只适合做两类事:一是基于全局统计信息的动态过滤,比如按当前batch的难度分布做采样调整;二是轻量的格式校验,防止离线阶段漏掉的少数字符异常在训练时爆雷。

从工程经验看,我见过太多团队把重过滤逻辑写进map算子,结果每个step都要重复计算一遍正则匹配和哈希,白白浪费大量算力。正确的做法是:把过滤结果落成字段,比如每行文本后面挂着keep_flag、ppl_score、dup_hash,训练时只做一个简单的字段判断。

2.3 MindSpore Dataset API选型建议

MindSpore的Dataset接口一直在迭代,我以2.x版本为主给出建议:

  • 纯文本行读取:GeneratorDataset最灵活,可以包一个Python生成器,天然支持过滤逻辑写在数据源内部。
  • 大文件、分布式训练:优先MindDataset,配合num_shards与shard_id完成分片。
  • 数据变换:用map算子,内部可以传自定义函数,但记住map做不了“有条件丢弃样本”,它只能做一对一的变换。
  • 有条件丢弃:要么在GeneratorDataset的生成器里continue掉不合格样本,要么用支持过滤的数据源类,要么干脆离线阶段过滤完再进训练。
  • 打乱:用shuffle(buffer_size)。特别注意buffer_size不要设得太小,否则随机性不足,对预训练影响明显。

很多人一上来就把过滤逻辑塞进map,遇到报错才回头改。我的建议是:先想清楚哪部分数据是“进了训练管线才发现不对”的,哪部分是“离线就能判断的”,尽可能把后者前置。

3. 核心过滤方法的实操拆解

3.1 规则过滤:用最便宜的方式干掉80%的脏数据

规则过滤是所有方案的地基。它便宜、直观、可解释,适合在第一轮快速缩减数据规模。我实际用过的规则清单包括:

规则参数建议说明
文本长度中文按字符数≥200,英文按token数≥100过短文本信息量太低,容易让模型学到碎片化表达
平均句长句平均字符数≤10或≥500异常句长往往代表正文抽取失败
重复标点连续重复的“!!!!”、“………”等高频出现在营销号和标题党文本中
URL占比链接字符数/全文字符数>5%纯链接聚合页没有学习价值
乱码字符特定Unicode区间的字符数占比>1%比如U+FFFD替换符、控制字符、未定义字形
全角半角混杂同一文本中全角数字与半角数字混用比例异常常出现在编码转换损坏的数据里
脏词/安全词库按业务自定义宁可误杀不可放过,安全合规这块单独处理
语言识别fasttext的lid.176.bin目标语言置信度低于0.5的直接丢弃

长度阈值不要照抄,要先统计自己语料的长度分布再定。比如你的语料本来就有大量短视频字幕文本,强行为200个字符会误杀掉一批真实对话数据。我的习惯是清洗前先抽1万条文本,跑一个长度直方图,把阈值定在分布中位数以下某个位置,而不是拍脑袋。

规则过滤还有一个容易忽略的细节:文本标准化。先做全角转半角、统一换行符、去掉BOM头,再做后续规则判断,否则同一个规则可能因为编码差异漏掉一批样本。中文场景里,\u3000全角空格和普通空格混在一起非常常见,建议第一步就统一替换成半角空格。

3.2 去重过滤:精确去重与MinHash模糊去重

去重是预训练数据质量里最容易被低估的一环。很多人以为数据去重只是省算力,其实它直接关系到模型评估的公平性。如果测试集里的文本与训练集重复,模型在下游任务上的分数会虚高,这种虚高一旦被当成模型能力提升,后面所有决策都会被带偏。

精确去重最简单:对每行文本做sha256或md5,维护一个全局哈希集合,遇到重复的直接丢弃。但真实场景里更棘手的是近似重复:同一篇新闻被不同站点改写,加了不同的头尾或者中间换了几句话。这时候需要MinHash。

MinHash的原理不复杂,核心是把文本转成一组“签名”,然后通过签名的相似度近似估计两段文本的Jaccard相似度。实际操作分为三步:先把文本切成shingle(我习惯按字符n-gram切,中文场景n取5比较稳),再对每个shingle计算多个独立哈希值并保留每组最小值,得到长度为H的签名向量,最后把签名向量分成b个band、每个band r行,任意一个band完全相同就判定为候选重复。

参数上,我常用H=256个哈希函数,分成b=32个band、每个band r=8行。这个配置的经验含义是:对于相似度70%的文本对,有大约85%的概率能被抓到;相似度50%的文本对只有约12%的概率被误判。想调整阈值,按公式1-(1-s^r)^b画一条曲线看效果,别盲改。Spark上可以直接用datasketch库跑MinHash,几千万元素规模下几个小时能跑完。

去重还有一个层级问题:究竟是整篇去重还是段落级去重。我的建议是两层都做:整篇用MinHash,段落级用SimHash或者基于n-gram overlap的轻量去重。段落级去重能解决“一篇文章里混了一段和别处完全重复的引用”这种问题,这在技术博客和论文语料里特别常见。

3.3 质量评估过滤:困惑度、分类器与人工复核

规则和去重把“明显不行的”清掉了,接下来还有一批“看起来能读,但读起来很怪”的文本。判断这类文本质量,最有效的手段是困惑度。

困惑度本质上衡量一段文本被语言模型“认可”的程度。文本越符合自然语言统计规律,困惑度越低;越乱、越重复、越不连贯,困惑度越高。实际操作上,我会用一个小规模的预训练模型(几百MB级别就够)对每条样本打分,然后看困惑度分布。这里有一个经验:困惑度异常低和异常高都要警惕,异常低往往意味着文本重复度过高(模型已经背下来了),异常高意味着文本可能是乱码、多语言混杂或者机器生成的无意义内容。我在实践中还会把困惑度分成“前5%低分位”“中间区间”“后5%高分位”三批分别抽检,而不是只盯平均分。

比困惑度更进一步的方案是用一个分类器给文本打分。常见做法是人工标注一批“高质量/低质量”样本,训练一个小分类器,或者直接用大模型API批量打分。这个方案成本高但上限也高,适合在正式训练前对核心数据做一次精修。成本控制上我建议分级处理:先用规则和困惑度把数据分成“确定保留”“确定删除”“存疑”三档,只对存疑部分做模型打分,这样能把昂贵计算用在真正需要的地方。

质量评估这一层也很容易出现误杀。比如把“代码库里的README”误判为低质量文本,或者把“论文摘要”因为术语密度过高打得分很低。所以不管用什么打分方式,一定保留人工抽检环节。我的习惯是每轮过滤后抽500条被删样本、500条被留样本,逐个过一眼,确认过滤方向没跑偏。

4. 一条可落地的完整清洗Pipeline

4.1 Pipeline整体设计

我自己跑过的预训练数据清洗管线长这样:

阶段一:原始数据收拢后统一转成JSONL,每行一个样本,字段至少包含text、source、timestamp。这个阶段不做过滤,只做格式统一和编码修复。

阶段二:跑第一轮规则过滤。用Spark或者Python多进程并行处理,按第3.1的规则清单逐条过滤,结果把keep_flag写入字段。这里不要直接物理删除,先标记,方便后续回溯分析。

阶段三:跑去重。先精确去重,再MinHash模糊去重。去重结果同样写入字段,比如dup_group_id,重复组的保留策略由人工指定:可以保留最早爬取时间的那一条,也可以保留文本长度最长的那一条。

阶段四:跑困惑度评估。用一个已经训好的小型语言模型批量打分,写入ppl_score字段。

阶段五:生成质量报告,人工抽检,根据报告调整规则和阈值。重复2-4轮直到指标稳定。

阶段六:把keep_flag=True的样本转成MindRecord,正式进入训练流程。

这个设计最大的好处是每一轮过滤都可回溯。不会被一句“数据是从某天起突然变差的”困住,因为每条样本都带着完整的过程标签。

4.2 从清洗到MindRecord的实现细节

清洗完的数据最终要落在MindSpore能高效读取的格式上。我的建议是先整理成干净的JSONL,再写一个转换脚本生成MindRecord。这个转换脚本的核心逻辑很简单:逐行读JSONL,按schema组织字段,写入MindRecord文件。

import json from mindspore.mindrecord import FileWriter def build_mindrecord(jsonl_path, output_path, keep_flag=True): writer = FileWriter(output_path, shard_num=8) # 定义schema,字段名和类型要与训练时的MindDataset一致 schema = { "text": {"type": "string"}, "source": {"type": "string"}, "ppl_score": {"type": "float32"} } writer.add_schema(schema, "pretrain_data") count = 0 with open(jsonl_path, "r", encoding="utf-8") as f: for line in f: item = json.loads(line) if keep_flag and not item.get("keep_flag", True): continue if len(item["text"]) < 200: continue writer.write_raw_data([{ "text": item["text"], "source": item.get("source", ""), "ppl_score": float(item.get("ppl_score", 0.0)) }]) count += 1 if count % 10000 == 0: print(f"processed {count} samples") writer.commit() print(f"total samples: {count}")

这里有几个坑提醒一下:shard_num决定了MindRecord分几个分片文件,分布式训练时不同rank会各自负责一个分片,所以分片数最好和训练卡数成倍数关系;schema一旦定义好,训练阶段就不能轻易改字段名和类型,否则MindDataset读取时直接报错;写入前最好先手工检查数据里有没有无法编码的字符,不然后续训练时decode异常会很难排查。

4.3 训练脚本中在线过滤算子实战

离线阶段过滤完毕后,在线阶段只需要做轻量判定。这里分享一个用GeneratorDataset包装过滤数据源的模板,适合数据量不大、或者想在训练启动前再快速过滤一轮的场景:

import json import mindspore as ms from mindspore.dataset import GeneratorDataset class PretrainDataGenerator: """从清洗后的JSONL读取数据,按条件过滤后发送给训练""" def __init__(self, jsonl_path, max_length=1024, ppl_max=200.0): self.path = jsonl_path self.max_length = max_length self.ppl_max = ppl_max self._preload() def _preload(self): # 启动时先扫描一遍所有样本,标记可保留的样本索引 self.valid_indices = [] with open(self.path, "r", encoding="utf-8") as f: for idx, line in enumerate(f): item = json.loads(line) if not item.get("keep_flag", True): continue if item.get("ppl_score", 0) > self.ppl_max: continue self.valid_indices.append(idx) def __getitem__(self, item_idx): real_idx = self.valid_indices[item_idx] # 这里只是示例,真实场景建议从索引文件直接读取或放到内存中 with open(self.path, "r", encoding="utf-8") as f: for cur, line in enumerate(f): if cur == real_idx: item = json.loads(line) break text = item["text"][:self.max_length] return text def __len__(self): return len(self.valid_indices) ds = GeneratorDataset( source=PretrainDataGenerator("data/pretrain.jsonl"), column_names=["text"] ) ds = ds.shuffle(buffer_size=10000) ds = ds.batch(batch_size=8, drop_remainder=True)

这个方案有个明显短板:__getitem__里逐行找索引在大数据量下很慢。所以这个模板只适合中小规模数据作为演示。真实场景我强烈建议用MindRecord替代,训练阶段只需要:

import mindspore.dataset as ds from mindspore.dataset import MindDataset dataset = MindDataset( dataset_file="data/pretrain.mindrecord", num_parallel_workers=8, num_shards=rank_size, shard_id=rank_id ) # tokenize等操作通过map实现 dataset = dataset.map(operations=tokenize_op, input_columns=["text"]) dataset = dataset.batch(batch_size=8, drop_remainder=True)

在线阶段不要做复杂逻辑,这是我在多个项目里磨出来的教训。过滤、打分、去重这些应该全部提前完成,训练阶段的任务只有一个:把文本变成token,然后送进模型。

5. 高频问题与避坑记录

5.1 常见问题速查表

数据清洗踩坑是必然的,下面是过去几个月我高频遇到并且仔细排查过的问题:

现象根因解决办法
训练中突然报错BadRecord文本里混入了无法按UTF-8解析的字节离线清洗时统一做编码修复,删除特殊Unicode控制字符
中文文本被误删全角空格和普通空格混用,导致语言识别置信度偏低先做全角转半角,再做长度和语言判断
模型评估分数虚高测试集与训练集存在近似重复用MinHash对测试集也过一遍去重,清洗后重新划分
困惑度打分后“好文本”反而被删专业术语密集造成困惑度异常高不分位数一刀切,结合文本领域信息调整阈值
过滤后数据量锐减URL占比、标点占比等规则阈值过严每次调参后都抽检被删样本,不要把“疑似”当成“确定”
分布式训练时不同rank数据分布不均分片前没有做全局shuffle清洗完成后先全局shuffle再转MindRecord
内存被撑爆GeneratorDataset一次性把数据源全量加载用生成器惰性读取,或者改用MindDataset流式读取

这些问题的共同点是:多数不是过滤逻辑本身写错,而是“过滤逻辑的边界条件”没考虑清楚。比如长度阈值是>=200还是>200,在全角半角混用的情况下,字符统计方式不同会导致结果差很多。我会建议把这类边界规则单独写单元测试,数据清洗脚本一样要有测试意识。

5.2 关于MindSpore数据API的几个坑

先说filter接口。MindSpore的Dataset提供了filter(predicate)方法,但它的语义是从数据集中保留满足条件的样本,性能表现受限于Python回调,在大数据量下并不理想。我更建议把过滤放在数据源内部做,不要让训练线程去做Python级别的逐条判定。如果你真的要在在线流程里过滤,也优先考虑用C++算子或者预处理好的标志字段。

另一个坑是map算子里的自定义函数返回值。map是一对一的变换,它要求每个输入样本都产生一个输出。如果你在函数里把不想要的样本返回一个空tensor,后续batch会报shape不一致,这是一个非常经典的新手错误。正确思路是:不想要的样本在数据源生成阶段就continue掉,根本不要让它进入map。

还有一个容易被忽略的坑是shuffle(buffer_size)的随机性。预训练数据量动辄上亿条,如果buffer太小,模型的训练顺序会和原始数据顺序高度相关,导致局部数据分布偏差。我一般会把buffer_size设得尽量大,比如训练卡总显存允许的情况下设为10万以上,实在不行也要保证一个epoch内样本被多次打乱的机会足够多。

关于分布式分片,还有一个容易踩的坑:MindDataset的num_shards必须等于总训练卡数,否则会出现数据重叠或者某些卡分不到数据。如果有动态扩容缩容的需求,或者评估和训练共用同一份数据,建议建好MindRecord之后再按需二次分片,别怼着原始数据反复转换。

5.3 过滤误杀的经典案例

误杀比漏杀更隐蔽,因为模型训练完你未必能第一时间发现。我举几个真实案例。

第一个案例:规则里写了“URL占比超过5%直接删除”,结果把大量论文PDF转成的文本误删了,因为论文末尾的参考文献列表里URL比例非常高。解决方法是把URL过滤改成“按行判断”,只删除“纯URL行”,而不是按整篇文本的URL占比一刀切。

第二个案例:困惑度过滤时,把“包含大量代码片段的技术文档”误判为低质量文本。代码片段的困惑度天然高于自然语言,如果数据集本身是混合型的,建议把代码和自然语言分开打分,分开设阈值。

第三个案例:MinHash的字符串切分用了不带空格的字符n-gram,导致中文文本里常见的四字成语、专有名词被拆散,相似度估计失真。中文场景下,我这里验证过的做法是先分词或者按标点分句,再做shingle,相似度计算也会更稳定。

这三个案例反映的是同一个原则:任何过滤规则都有它的适用边界。规则不是越严越好,而是要在“数据多样性”和“数据纯度”之间找平衡。预训练需要的是多样化的高质量语料,不是经过反复清洗后只剩下同一种写作风格的文本。

6. 质量指标、数据报告与持续迭代

6.1 离线质量指标怎么定

没有指标就没有迭代。我在跑完一轮过滤后,会生成一份标准质量报告,核心字段如下:

指标参考范围说明
原始样本数/过滤后样本数剩余率40%-70%左右具体取决于原始语料来源,太低说明规则过严
平均文本长度按语料类型分布与业务场景强相关,不能一刀切
精确重复率/近似重复率分别低于1%和5%控制训练数据的记忆效应
困惑度均值与分位数p5-p95区间内无明显长尾尾部过重说明仍有大量异常文本漏过
语言覆盖占比目标语言占比高于95%防止多语言混杂下降目标语言能力
URL占比、脏词占比尽量归零是规则有效性的直观体现
被删样本的人工抽检通过率低于5%可接受抽检被删样本,确认没有好东西被误杀

这些指标不要看完就扔。我在实践中会保留每一轮过滤的报告,形成“规则版本”概念。比如我调整了长度阈值,就把这轮报告和上一轮报告对比,看被删样本的结构变化。有了这个过程,后面再做数据迭代时就非常清楚到底改了哪些规则、带来什么影响。

6.2 训练中的持续监控

数据过滤不能只在训练前做一次,训练过程中也要持续监控。MindSpore训练脚本里,我习惯定期把当前step采样的batch数据统计信息打印出来,包括平均token长度、batch内重复n-gram比例、罕见字符出现次数。如果某一步数据分布出现明显漂移,往往说明离线过滤后的数据仍存在隐性分布不均匀。

另一个值得注意的点是loss曲线的解读。如果loss下降很快但下游任务分数不涨,要怀疑数据重复率偏高;如果loss出现奇怪的周期性波动,要怀疑数据分片没做全局shuffle,导致不同rank之间的数据难度差异过大。这时候不要先调模型超参,先检查数据。

我还会做一个相对简单的线上文本巡检:在训练日志里随机打印一批当前batch的真实文本样本。这些文本不一定每次都能看懂,但偶尔看到一堆广告语或者无意义文本混进来,就能第一时间发现离线过滤的漏网之鱼。这个土办法在关键时刻可以救命,因为它提供了模型视角看到的数据,而不是我们假设的数据。

6.3 过滤方案如何持续迭代

数据清洗没有终点,一次性的干净不等于永远干净。我的做法是在训练代码仓库里维护一个数据过滤配置目录,把每条规则、每个阈值、每次阈值调整的原因都记录下来。这样做的好处很直接:当语料来源新增了一个渠道,只需要对比旧渠道的配置,就能快速判断哪些规则要沿用,哪些规则要重新标定。

迭代时还有一个重要经验:不要一上来就用最大的语料集做全量过滤。我会先抽一个5万条的样本子集,快速跑完整条过滤Pipeline,生成报告,人工确认过滤效果,再全量跑。这个做法在工程上节省了我大量时间——你永远不想在几百亿token的语料上跑完三个小时,才发现长度阈值设错了导致删掉了一半有效数据。

另外一个容易忽略的点是“保留原始数据版本”。清洗会改变数据,但旧版本数据在某些情况下仍然有参考价值。比如之后你发现某个下游任务对某种文本风格特别敏感,而当时正好把这种风格的文本当低质量数据删了,这时候如果原始数据已经被覆盖,就只能欲哭无泪了。所以我是强烈建议每次清洗都输出一个新版本数据,而不是在原文件上做原地修改。

说到我自己实际操作中的体会:数据过滤最难的其实不是写规则,也不是调阈值,而是建立“对数据的直觉”。这种直觉需要反复看数据、反复抽检被删样本才能建立。单凭一个困惑度分数或者一个MinHash结果就判定数据好坏,很容易翻车。每次跑完一轮过滤,我都会花一点时间随机翻一批被删和被留的文本,亲手感受一下这批语料的真实形态。这个习惯坚持下来,后面设规则、定阈值都会快很多。

最后分享一个小技巧:如果你刚开始做预训练,别一上来就追求“最完美的过滤方案”。先用规则过滤和精确去重跑通整个流程,把训练管线搭起来,再逐步引入MinHash、困惑度打分这些更复杂的模块。数据清洗和模型训练一样,都是迭代的过程,先跑起来永远比先想清楚更重要。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询