1 数据来源

整个预训练阶段最重要的就是数据的获取和处理。预训练的数据量级在10T tokens,继续预训练则至少在100B tokens 的量级

预训练阶段涉及学习大量数据,以全面了解世界及其各种复杂性。这不仅包括基本的语言能力,还包括诸如math、code和逻辑推理等高级技能,所以需要收集大量的多领域的语料,目前一些常见的数据来源有以下几种:​

  • Common Crawl: 这是一个开放的数据共享平台,里面是大量的网页数据​
  • GitHub: 从 GitHub 上收集代码相关的数据​
  • 网页文本: 收集与代码相关和与数学相关的网络文本,如选择代码论坛、库网站、数学网站等​
  • 电子书、教育材料和论文资料: 这些资源涵盖丰富的知识内容,有助于提升模型在不同领域的理解和生成能力
  • 内部数据: 做大模型的公司组织的内部相关数据,可能是业务上的垂域数据​
  • 开源通用数据

2 数据爬取

2.1 定向网站爬取

# -*- coding: utf-8 -*-​
import io​
import requests​
from bs4 import BeautifulSoup​
import urllib​
import ssl​
import pandas as pd​
import tqdm​
ssl._create_default_https_context = ssl._create_unverified_context​
​
def download_pdf(save_path, pdf_name, pdf_url):​
    headers = {​
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '​
                      'Chrome/88.0.4324.146 Safari/537.36'​
    }​
    response = requests.get(pdf_url, headers=headers)​
    bytes_io = io.BytesIO(response.content)​
    with open("./事故报告_pdf/" + "%s.pdf" % pdf_name, mode='wb') as f:​
        f.write(bytes_io.getvalue())​
        print('%s.pdf,下载成功!' % (pdf_name))​
​
def request_douban(url):​
    headers = {​
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '​
                      'Chrome/88.0.4324.146 Safari/537.36'​
    }​
​
    try:​
        response = requests.get(url=url, headers=headers, allow_redirects=False)​
        response.encoding = response.apparent_encoding​
        if response.status_code == 200:​
            return response.text​
    except requests.RequestException:​
        return None​
​
def askURL(url):​
    head = { ​
   "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"}​
    request=urllib.request.Request(url,headers=head)​
    html=""​
    try:​
        response=urllib.request.urlopen(request)​
        html=response.read().decode("utf-8")​
        # print(html)​
    ​
    df.to_csv('result.csv', index=False)

2.2 关键词爬取

参考项目(可以自行去github下载): https://github.com/NanmiCoder/MediaCrawler

2.3 基于搜索引擎爬取

3 数据清洗

数据清洗是预训练处理数据最重要的环节,得到的数据的质量、多样性决定了模型的通用能力效果

3.1 URL 过滤

通过制定 URL 黑名单(成人网站等)和计算 URL 分数来决定内容是否保留,为了区分于人工精心构建的数据集,像 Arxiv、WikiPedia 这些前缀网站的内容也将被过滤

3.2 内容抽取

在获得过滤后的 URL 集合后,我们需要获取到这些 URL 中的「文本信息」,需要过滤并丢弃「目录」、「标题」、「广告」等无关的内容,使用 trafilatura 库来进行内容抓取,trafilatura 的使用方法也比较简单:

# load necessary components​
from trafilatura import fetch_url, extract​

# download a web page​
url = 'https://github.blog/2019-03-29-leader-spotlight-erin-spiceland/'​ n
downloaded = fetch_url(url)​ 
downloaded is None  ​

# assuming the download was successfulFalse# extract information from HTML​
result = extract(downloaded)​
print(result)

3.3 语言识别

  • 使用FastText训练一个语言识别模型,去掉那些语言阈值得分低于 0.65 的文章​
  • 使用langid识别语言种类​
import langid​
text = "Tudo bem?"​
language = langid.classify(text)[0]​
print(language)​

3.4 低质过滤

篇章级别过滤: 首先,去除一些文章内一直重复同一段内容的文章,以及一些包含错误信息的文章(例如:抓取超时等);其次,对每一篇文章,通过判断文章整体长度、标点符号占文章长度的比例等,来过滤掉那些不正规的文章​
句子级别过滤: 过滤掉文章中的一些无用的句子,这种句子通常没有具体含义,但话术繁多,又难以穷举。因此,通过设定以下策略来过滤掉这些语句(某些规则只适用于英语):​

  1. 句子中主要由大写字符组成(丢弃)​
  2. 句子由纯数字组成(丢弃)​
  3. 命中特定关键词,如['关注', '转发', '点赞'](丢弃)​
  4. 如果句子长度小于 10,且命中以下模版:* 句子以(登录、注册)开头。* 句子以(展开、更多)结尾​​

利用启发式规则进行 pretrain 数据质量筛选,比如数据长度是否少于某个值,数据中某个 token 的比例超过某个阈值,数据的中文占比、英文占比、数字占比,数据是否有“http”字段,数据是否包含了“新冠”、“疫情”等低质量关键词,数据是否包含某些反动词汇,数据是否包含某些黄色字眼等。用规则清洗或者过滤数据的时候,一定不要把数据搞成分布有偏的数据

3.5 模型打分

利用模型对 pretrain 数据的质量进行打分,已经成了数据清洗工作的标配,llama3、qwen2 的技术报告都有提及。同 size 下,BERT 结构的模型的表征能力是强于 transformer-decoder 模型的,因此打分模型可以使用BERT类型的模型进行训练,或者利用强闭源模型来对训练数据进行打分,比如GPT4o

以下为通过Common Crawl构建DeepSeekMath语料库的迭代流程图

image-rzel.png

3.6 数据去重

在预训练过程中的数据重复,大体可以分为三类:​

  • 训练数据集中的重复: 用于训练的多个类型、来源的数据集内部和之间的重复。其一,数据集内部的重复,包括单个文档内部重复的lines、paragraphs、n-grams等(说明文档本身质量较低)、多个文档之间的重复(基于完全匹配或者模糊匹配);其二,数据集之间的重复,主要指多个数据集基于同一个来源但是进行了不同的预处理之后产出的数据,一个典型的例子是LLaMa等模型会同时使用基于CommonCrawl预处理过的数据以及之前T5模型基于当时CommonCrawl处理的C4数据,两者定然是有一定重复的​
  • 训练迭代设置的重复: 在给定数据集上人为设定的重复轮次(Epochs),一般来说针对不同类型、来源、质量的数据采样训练的Epochs并不一致​
  • 训练与测试集的重复: 例如,预训练过程中预留的观测Language Modeling Loss的测试集、用于评测效果的Bnchmarks等,都应该从训练集合中去除相似的数据

3.7 数据多样性探索

数据多样性有多个方面:有任务的多样性,有语义的多样性,有语种的多样性,数据来源的多样性等等。在大模型建设的各个环节,数据的质量和多样性一直是大家比较关注的问题,预训练阶段,收集不同来源的数据行业中共识是sft阶段用到的数据应该是高质量、多样性的,maybe only 0.5% data is needed论文中提出利用聚类的思想筛选sft样本的做法,方案包括向量化 -> 聚类 -> 核心 样本采样三步

image-yvmh.png

3.8 实际垂域经验

image-eflj.png

  1. 爬取尽可能多的数据文档,作为初始种子数据语料库​

  2. 基于聚类方法,从中文语料数据库中,筛选出垂域数据,即召回互联网开源书籍/百科数据/网页资料等的相关垂域文档,扩充种子数据​,pipeline:关键词初筛通用数据 -> 召回与种子数据相似度top10的数据 -> 人工筛选​

    1. 关键词初筛通用数据​
      • 首先,对种子文本细化处理,保留文档段落信息,以不同标题段落内容,划分知识文档数据​
      • 基于jieba分词挖掘内部种子文本的关键词:参考链接:138自然语言处理通关手册--新词挖掘​
      • 挖掘到的关键词再经过一次人工筛选,最后整理到关键词表​
      • 从通用数据中,筛选出命中N个关键词表的数据​
    2. 召回与种子数据相似度top10的数据​
      • 这里取通用数据的前 512 tokens 计算相似度​
    3. 人工筛选
      • 标注前面得到的通用数据,选择垂域相关的数据,作为扩充种子数据​
  3. 训练一个中文领域数据分类器 ​

    1. 训练数据 正例由垂域种子文档(这里使用划分后的数据),及第2步筛选出来的扩充种子数据(每个文档取前1k tokens)。而负例为第2步被标注为“不是垂域”的数据​
    2. 模型选择 1024长度的BERT中文模型
    3. 模型效果 最后,得到一个高精度(95%+)的中文领域分类模型,并从中文语料数据库(预测时,取每个文档的前 1k tokens进行识别)中,筛选出 0.5 B的垂直语料

4 数据配比和顺序

指导思想: 按照任务类型进行划分,分得越细越好,最好精细到3级类别。比如一级 中文、英文,code。训练一个数据分类器,对每一个 document 进行类别判断,不用特别精准,把数据划分成百科、code、markdown、等类目即可,分类器模型依然可以选择使用 BERT 家族。数据量级在2w条左右​
大部分的开源大模型技术报告里,都提及训练的数据是如何配比的,基本上都是“知识 + 代码 + 逻辑”三个大类目,其中知识数据分文中文知识和英文知识,逻辑数据则可以认为是 math 数据和 cot 数据的混合体。整体上,大部分中文模型的配比都差不多为 中:英:code = 4:4:2​
中文大模型中文尽量占一半以上,英文质量会更高,也不能太少,如果想要模型推理能力能强,math和code数据要尽可能多

大厂做法: 进一步到领域专项模型等训练的话会加入领域数据做进一步配比,对 continue pretraining,如果要让模型不丢失通用能力,比如 summarization,qa 等​

  • 领域数据 continue pretraining 时,一定更要混合大量通用数据。「领域数据比例要在15%以下」,一旦超过这个阈值,模型通用能力会下降很明显。这个阈值和不同的预训练模型相关,有些模型比如llama需要控制的阈值更低。阈值其实是经验主义结论,范围都在 10%-15% 左右。而且阈值和预训练模型的大小,预训练时原始数据的比例等条件都息息相关,需要在实践中反复修正​
  • sft 比例可提高不少,领域数据:通用数据=1:1,如果sft数据量少,混不混数据差别就不太大了​

上述的数据配比一般是先在小模型上实验,然后利用scaling law去推算应用到大模型上的效果,因此一般会在小模型上做多组数据配比实验,观测loss情况​

大模型在大量预训练数据上学习的时候一般会采用课程学习的方式,就是先学什么,再学什么,考虑到大模型在训练过程中会出现遗忘的情况,数据的训练顺序也很重要,这部分也需要先在小模型上进行实验