本文分享自华为云社区《浅谈如何处理大语言模型训练数据之一常见的数据处理方法》,作者: 码上开花_Lancer。
大语言模型训练需要数万亿的各类型数据。如何构造海量“高质量”数据对于大语言模型的训练具有至关重要的作用。虽然,截止到2023 年9 月为止,还没有非常好的大模型的理论分析和解释,也缺乏对语言模型训练数据的严格说明和定义。但是,大多数研究人员都普遍认为训练数据是影响大语言模型效果以及样本泛化能力的关键因素之一。从此前的研究来看,预训练数据需要涵盖各种类型,包括网络数据、图书、论文、百科和社交媒体等,还需要覆盖尽可能多的领域、语言、文化和视角,从而提高大语言模型的泛化能力和适应性。本文将介绍当前常见的大语言模型训练数据的来源、处理方法、预训练数据对大语言模型影响的分析以及常见开源数据集合等。
一、数据来源
有论文专门 介绍了OpenAI 训练GPT-3 所使用的主要数据来源,包含经过过滤的CommonCrawl数据集、WebText2、Books1、Books2 以及英文Wikipedia 等数据集合。其中CommonCrawl 的原始数据有45TB,进行过滤后仅保留了570GB 的数据。通过词元方式对上述语料进行切分,大约一共包含5000 亿词元。
为了保证模型使用更多高质量数据进行训练,在GPT-3 训练时,根据语料来源的不同,设置不同的采样权重。在完成3000 亿词元训练时,英文Wikipedia 的语料平均训练轮数为3.4 次,而CommonCrawl 和Books 2 仅有0.44 次和0.43 次。
由于CommonCrawl 数据集合的过滤过程繁琐复杂,Meta 公司的研究人员在训练OP 模型时则采用了混合RoBERTa、Pile[68] 和PushShift.io Reddit 数据的方法。由于这些数据集合中包含的绝大部分都是英文数据,因此OPT 也从CommonCrawl 数据集中抽取了部分非英文数据加入训练语料。
大语言模型训练所需的数据来源大体上可以分为通用数据和专业数据两大类。通用数据(GeneralData)包括网页、图书、新闻、对话文本等内容。通用数据具有规模大、多样性和易获取等特点,因此可以支持大语言模型的构建语言建模和泛化能力。
专业数据(Specialized Data)包括多语言数据、科学数据、代码以及领域特有资料等数据。通过在预训练阶段引入专业数据可以有效提供大语言模型的任务解决能力。图3.1给出了一些典型大语言模型所使用数量类型的分布情况。可以看到不同的大语言模型在训练类型分布上的差距很大,截止2023 年9 月为止,还没有得到广泛认可数据类型分布比例。
典型大语言模型所使用数量类型的分布
1.1 通用数据
通用数据在大模型训练数据中占比通常非常高,主要包括网页、书籍、对话文本等类型,为大模型提供了大规模且多样的训练数据。网页(Webpages)是通用数据中数量最大的一类。随着互联网的大规模普及,人们通过网站、论坛、博客、APP 等各种类型网站和应用,创造了海量的数据。
根据2016 年Google 公开的数据,其搜索引擎索处理了超过130 万亿网页。网页数据所包含的海量内容,使得语言模型能够获得多样化的语言知识并增强其泛化能力。爬取和处理这些海量网页内容并不是一件容易的事情,因此一些研究人员构建了包括ClueWeb09[70]、ClueWeb12、SogouT-16、CommonCrawl 等在内的开源网页数据集。但是,这些爬取的网络数据虽然包含大量高质量的文本,如维基百科,但也包含非常多的低质量的文本,如垃圾邮件等。因此,如何过滤和处理网页以提高质量数据对与大语言模型训练来说非常重要。
对话数据(Conversation Text)是指包含两个或更多参与者之间交流的文本内容。对话数据包含书面形式的对话、聊天记录、论坛帖子、社交媒体评论等。当前的一些研究也表明,对话数据可以有效增强语言模型的对话能力,并潜在地提高其在多种问答任务上的表现。对话数据可以通过收集、清洗、归并等过程从社会媒体、论坛、邮件组等构建。
相较于网页数据,对话数据收集和处理更加困难,数据数量也相对少非常多。常见的对话数据集包括PushShift.io Reddit、Ubuntu Dialogue Corpus、Douban Conversation Corpus、Chromium Conversations Corpus 等。此外,文献[75] 也提出了如何使用大语言模型自动生成对话数据的UltraChat 方法。
书籍(Book)是人类知识的主要积累方式之一,从古代经典著作到现代学术著述,书籍承载了丰富多样的人类思想。书籍通常包含广泛的词汇,包括专业术语、文学表达以及各种主题词汇。
利用书籍数据进行训练,语言模型可以接触到多样化的词汇,从而提高其对不同领域和主题的理解能力。相较于其他语料库,书籍也是最重要的,甚至是唯一的长文本书面语的数据来源。书籍提供了完整的句子和段落,使得语言模型可以学习到上下文之间的联系。这对于模型理解句子中的复杂结构、逻辑关系和语义连贯性非常重要。书籍涵盖了各种文体和风格,包括小说、科学著作、历史记录等等。通过使用书籍数据训练语言模型,可以使模型学习到不同的写作风格和表达方式,提高大语言模型在各种文本类型上的能力。由于版权因素,开源书籍数据集合很少,现有的开源大语言模型研究通常采用Pile 数据集[68] 中提供的Books3 和Bookcorpus2 数据集。
2.1 专业数据
专业数据在通用大语言模型中所占比例通常较低,但是专业数据对于改进大语言模型在下游任务上的特定能力有着非常重要的作用。专业数据有非常多的种类,我总结了当前大语言模型使用的三类专业数据,包括多语言数据、科学文本以及代码。
**多语言数据(Multilingual Text)**对于增强大语言模型语言理解和生成多语言能力具有至关重要的作用。当前的大语言模型训练除了需要目标语言中的文本之外,通常还要整合多语言语料库。
例如,BLOO的预训练语料中包含46 种语言,而PaLM[的训练语料中甚至高达122 种语言的数据。此前的研究发现,通过多语言混合训练,预训练模型中可以在一定程度上自动构建多语言之间的语义关联。因此,多语言数据混合训练,可以有效提升翻译、多语言摘要和多语言问答等任务能力。此外,由于不同语言中不同类型的知识获取难度不同,多语言数据还可以有效增加数据的多样性和知识的丰富性。
**科学文本(Scientific Text)**包括教材、论文、百科以及其他相关资源。这些数据对于提升大型语言模型在理解科学知识方面具有重要作用。科学文本数据的来源主要包括arXiv 论文、PubMed 论文、教材、课件和教学网页等。由于科学领域涉及众多专业领域且数据形式复杂,通常还需要对公式、化学式、蛋白质序列等采用特定的符号标记进行预处理。
例如,公式可以使用LaTeX 语法进行表示,化学结构可以使用SMILES(Simplified Molecular Input Line Entry System)表示,蛋白质序列可以使用单字母代码或三字母代码。这样可以将不同格式的数据转换为统一的形式,使得语言模型更好地处理和分析科学文本数据。
**代码(Code)**数据是进行程序生成任务所必须的训练数据。最近的研究和ChatGPT 的结果表明,通过在大量代码上进行预训练,大语言模型可以有效提升代码生成的效果。代码数据不仅包含程序代码本身,还包含大量的注释信息。与自然语言文本相比,代码数据具有一些显著的区别。代码是一种格式化语言,它对应着长程依赖和准确的执行逻辑。代码的语法结构、关键字和特定的编程范式都对其含义和功能起着重要的作用。代码数据的主要来源是编程问答社区(如Stack Exchange)和公共软件仓库(如GitHub)。编程问答社区中的数据包含了开发者提出的问题、其他开发者的回答以及相关代码示例。这些数据提供了丰富的语境和真实世界中的代码使用场景。公共软件仓库中的数据则包含了大量的开源代码,涵盖了各种编程语言和领域。这些代码库中的很多代码经过了严格的代码评审和实际的使用测试,因此具有一定的质量和可靠性。
二、数据处理
大语言模型的相关研究表明,数据质量对于模型的影响非常大。因此在收集到各类型数据之后,需要对数据进行处理,去除低质量数据、重复数据、有害信息、个人隐私等内容[14, 85]。典型的数据处理过程如图3.1所示,主要包含质量过滤、冗余去除、隐私消除、词元切分等几个步骤。本节将依次介绍上述内容。
图2.1 典型大语言模型数据处理流程图
1. 低质过滤
互联网上的数据质量参差不齐,无论是OpenAI 联合创始人Andrej Karpathy 在微软Build 2023的报告,还是当前的一些研究都表明,训练数据的质量对于大语言模型效果具有非常重要的影响。因此,如何从收集到的数据中删除低质量数据成为大语言模型训练中的重要步骤。大语言模型训练中所使用的低质量数据过滤方法可以大致分为两类:基于分类器的方法和基于启发式的方法。基于分类器的方法目标是训练文本质量判断模型,并利用该模型识别并过滤低质量数据。
GPT-3、PALM 以及GLam模型在训练数据构造时都使用了基于分类器的方法。 采用基于特征哈希的线性分类器(Feature Hash Based Linear Classifier),可以非常高效地完成文本质量判断。该分类器使用一组精选文本(维基百科、书籍和一些选定的网站)进行训练,目标是将与训练数据类似的网页给定较高分数。利用这个分类器可以评估网页的内容质量。在实际应用中,还可以通过使用Pareto 分布对网页进行采样,根据其得分选择合适的阈值,从而选定合适的数据集合。
但是,一些研究也发现,基于分类器的方法可能会删除包含方言或者口语的高质量文本,从而损失一定的多样性。基于启发式的方法则通过一组精心设计的规则来消除低质量文本,BLOOM 和Gopher采用了基于启发式的方法。这些启发式规则主要包括:
• 语言过滤:如果一个大语言模型仅关注一种或者几种语言,那么就可以大幅度的过滤掉数据中其他语言的文本。
• 指标过滤:利用评测指标也可以过滤低质量文本。例如,可以使用语言模型对于给定文本的困惑度(Perplexity)进行计算,利用该值可以过滤掉非自然的句子。
• 统计特征过滤:针对文本内容可以计算包括标点符号分布、符号字比(Symbol-to-Word Ratio)、句子长度等等在内的统计特征,利用这些特征过滤低质量数据。
**
• 关键词过滤:根据特定的关键词集,可以识别和删除文本中的噪声或无用元素,例如,HTML标签、超链接以及冒犯性词语等。**
在大语言模型出现之前,在自然语言处理领域已经开展了很多文章质量判断(Text Quality Evaluation)相关研究,主要应用于搜索引擎、社会媒体、推荐系统、广告排序以及作文评分等任务中。
在搜索和推荐系统中,结果的内容质量是影响用户体验的的重要因素之一,因此,此前很多工作都是针对用户生成内容(User-Generated Content,UGC)质量进行判断。自动作文评分也是文章质量判断领域的一个重要子任务,自1998 年文献[87] 提出了使用贝叶斯分类器进行作文评分预测以来,基于SVM[88]、CNN-RNN[89]、BERT[90, 91] 等方法的作文评分算法也相继提出,并取得了较大的进展。
这些方法也都可以应用于大语言模型预训练数据过滤中。但是由于预训练数据量非常大,并且对于质量判断的准确率并不要求非常高,因此一些基于深度学习以及基于预训练的方法还没有应用于低质过滤过滤中。
2.冗余去除
文献指出大语言模型训练语料库中的重复数据,会降低语言模型的多样性,并可能导致训练过程不稳定,从而影响模型性能。因此,需要对预训练语料库中的重复进行处理,去除其中的冗余部分。文本冗余发现(Text Duplicate Detection)也称为文本重复检测,是自然语言处理和信息检索中的基础任务之一,其目标是发现不同粒度上的文本重复,包括句子、段落以及文档等不同级别。
冗余去除就是在不同的粒度上进行去除重复内容,包括句子、文档和数据集等粒度的重复。在句子级别上,文献 指出,包含重复单词或短语的句子很可能造成语言建模中引入重复的模式。这对语言模型来说会产生非常严重的影响,使得模型在预测时容易陷入重复循环(RepetitionLoops)。
例如,使用GPT-2 模型,对于给定的上下文:“In a shocking finding, scientist discovereda herd of unicorns living in a remote, previously unexplored valley, in the Andes Mountains. Evenmore surprising to the researchers was the fact that the unicorns spoke perfect English.”。如果使用束搜索(Beam Search),在设置b = 32 时,模型就会产生如下输出,进入了重复循环模式。“Thestudy, published in the Proceedings of the National Academy of Sciences of the United States of America(PNAS), was conducted by researchers from the Universidad Nacional Autónoma de México (UNAM)and the Universidad Nacional Autónoma de México (UNAM/Universidad Nacional Autónoma de México/ Universidad Nacional Autónoma de México/Universidad Nacional Autónoma de México/Universidad Nacional Autónoma de …”。由于重复循环对于语言模型生成的文本质量有非常大的影响,因此在预训练语料中需要删除这些包含大量重复单词或者短语的句子。
RefinedWeb 构造过程中也进行了句子级别的过滤。使用了文献所提出的过滤方法,提取并过滤文档间超过一定长度的相同字符串。给定两个文档xi 和xj,其中存在长度为k 的公共子串xa…a+ki = xb…b+kj 。当k ⩾ 50 时,就将其中一个子串过滤。公共子串匹配的关键是如何高效完成字符串匹配,文献[64] 将整个文档D 转换为一个超长的字符串序列S,之后构造序列S 的后缀数组(Suffix Array)A。
该数组包含在该序列中的所有后缀的按字典顺序排列的列表。具体而言,后缀数组A是一个整数数组,其中每个元素表示S 中的一个后缀的起始位置。按照字典顺序,A中的元素按照后缀的字典顺序排列。例如,序列“banana”的后缀包括“banana”,“anana”,“nana”,“ana”,“na”和“a”,对应的后缀数组A 为[6, 4, 2, 1, 5, 3]。根据数组A,可以很容易的找出相同的子串。如果S i..i+|s| = S j..j+|s|,那么i 和j 在数组A 中一定在紧邻的位置上。
文献 中设计了并行的后缀数组构造方法,针对Wiki-40B 训练语料(约包含4GB 文本内容),使用拥有96 核CPU 以及768GB 内存的服务器,可以在2.3 分钟内完成计算。对于包含350GB 文本的C4 数据集合,仅需要12 小时可以完成后缀数组构造。在文档级别上,大部分大语言模型都是依靠文档之间的表面特征相似度(例如n-gram 重叠比例)进行检测并删除重复文档[33, 37, 64, 94]。
LLaMA 采用CCNet的处理模式,首先将文档拆分为段落,并把所有字符转换为小写字符、将数字替换为占位符,以及删除所有Unicode 标点符号和重音符号来对每个段落进行规范化处理。然后,使用为SHA-1 方法为每个段落计算一个哈希码(Hash Code),并使用前64 位数字作为键。最后,利用每个段落的键进行重复判断。RefinedWeb[64]首先去除掉页面中菜单、标题、页脚、广告等内容,仅抽取页面中的主要内容。在此基础上,在文档级别进行过滤,采用与文献提到类似的方法,使用n-gram 重叠程度来衡量句子、段落以及文档的相似度。如果重复程度超过预先设定的阈值,则会过滤掉重复段落或文档。
此外,数据集层面也可能存在一定数量的重复情况,比如很多大语言模型预训练集合都会包含GitHub、Wikipedia、C4 等数据集。还需要特别注意的是,预训练语料中混入测试语料,从而造成数据集污染的情况。在实际产生预训练数据时,需要从数据集、文档以及句子三个级别去除重复,这对于改善语言模型的训练具有重要的作用[14, 96]
3.隐私消除
由于绝大多数预训练数据源于互联网,因此不可避免地会包含涉及敏感或个人信息(PersonallyIdentifiable Information,PII)的用户生成内容,这可能会增加隐私泄露的风险。如图2.2所示,输入前缀词“East Stroudsburg Stroudsburg”,语言模型在此基础上补全了姓名、电子邮件地址、电话号码、传真号码以及实际地址。这些信息都是模型从预训练语料中学习得到的。因此,有非常必要从预训练语料库中删除包含个人身份信息的内容。
删除隐私数据最直接的方法是采用基于规则的算法,BigScience ROOTS Corpus 构建过程从大语言模型中获得隐私数据的例子中就是采用了基于命名实体识别的方法,利用命名实体识别算法检测姓名、地址和电话号码等个人信息内容并进行删除或者替换。该方法使用了基于Transformer 的模型,并结合机器翻译技术,可以处理超过100 种语言的文本,消除其中的隐私信息。该算法被集成在muliwai 类库中。
图2.2 从大语言模型中获得隐私数据的例子
4 词元切分
传统的自然语言处理通常以单词为基本处理单元,模型都依赖预先确定的词表V,在编码输入词序列时,这些词表示模型只能处理词表中存在的词。因此,在使用中,如果遇到不在词表中的未登录词,模型无法为其生成对应的表示,只能给予这些未登录词(Out-of-vocabulary,OOV)一个默认的通用表示。
在深度学习模型中,词表示模型会预先在词表中加入一个默认的“[UNK]”(unknown)标识,表示未知词,并在训练的过程中将[UNK] 的向量作为词表示矩阵的一部分一起训练,通过引入某些相应机制来更新[UNK] 向量的参数。在使用时,对于全部的未登录词,都使用[UNK] 的向量作为这些词的表示向量。
此外,基于固定词表的词表示模型对词表大小的选择比较敏感。当词表大小过小时,未登录词的比例较高,影响模型性能。而当词表大小过大时,大量低频词出现在词表中,而这些词的词向量很难得到充分学习。理想模式下,词表示模型应能覆盖绝大部分的输入词,并避免词表过大所造成的数据稀疏问题。
为了缓解未登录词问题,一些工作通过利用亚词级别的信息构造词表示向量。一种直接的解决思路是为输入建立字符级别表示,并通过字符向量的组合来获得每个单词的表示,以解决数据稀疏问题。然而,单词中的词根、词缀等构词模式往往跨越多个字符,基于字符表示的方法很难学习跨度较大的模式。
为了充分学习这些构词模式,研究人员们提出了子词词元化(Subword Tokenization)方法,试图缓解上文介绍的未登录词问题。词元表示模型会维护一个词元词表,其中既存在完整的单词,也存在形如“c”, “re”, “ing”等单词部分信息,称为子词。词元表示模型对词表中的每个词元计算一个定长向量表示,供下游模型使用。对于输入的词序列,词元表示模型将每个词拆分为词表内的词元。
例如,将单词“reborn”拆分为“re”和“born”。模型随后查询每个词元的表示,将输入重新组成为词元表示序列。当下游模型需要计算一个单词或词组的表示时,可以将对应范围内的词元表示合成为需要的表示。因此,词元表示模型能够较好地解决自然语言处理系统中未登录词的问题。词元分析(Tokenization)目标是将原始文本分割成由词元(Token)序列的过程。词元切分也是数据预处理中至关重要的一步。
字节对编码(Byte Pair Encoding,BPE)模型[99] 是一种常见的子词词元模型。该模型所采用的词表包含最常见的单词以及高频出现的子词。在使用中,常见词通常本身位于BPE 词表中,而罕见词通常能被分解为若干个包含在BPE 词表中的词元,从而大幅度降低未登录词的比例。BPE算法包括两个部分:(1)词元词表的确定;(2)全词切分为词元以及词元合并为全词的方法。计算过程如图4所示。
首先,确定语料库中全词的词表和词频,然后将每个单词切分为单个字符的序列,并在序列最后添加符号“”作为单词结尾的标识。比如单词“low”被切分为序列“l␣o␣w␣”。所切分出的序列元素称为字节,即每个单词都切分为字节的序列。之后,按照每个字节序列的相邻字节对和单词的词频,统计每个相邻字节对的出现频率,合并出现频率最高的字节对,将其作为新的词元加入词表,并将全部单词中的该字节对合并为新的单一字节。如图4所示,在第一次迭代时,出现频率最高的字节对是(e,s),故将“es”作为词元加入词表,并将全部序列中相邻的(e,s)字节对合并为es 字节。重复这一步骤,直至BPE 词元词表的大小达到指定的预设值,或没有可合并的字节对为止。
**在词元词表确定之后,对于输入词序列中未在词表中的全词进行切分,BPE 算法对词表中的词元按从长到短的顺序进行遍历,用每一个词元和当前序列中的全词或未完全切分为词元的部分进行匹配,将其切分为该词元和剩余部分的序列。例如,对于单词“lowest”,首先通过匹配词元“est”将其切分为“low”, “est”的序列,再通过匹配词元“low”,确定其最终切分结果为“low”, “est”的序列。通过这样的过程,BPE 尽量将序列中的词切分成已知的词元。在遍历词元词表后,对于切分得到的词元序列,为每个词元查询词元表示,构成词元表示序列。若出现未登录词元,即未出现在BPE 词表中的词元,则采取和未登录词类似的方式,为其赋予相同的表示,最终获得输入的词元表示序列。
**
此外,字节级(Byte-level)BPE 通过将字节视为合并的基本符号,用来改善多语言语料库(例如包含非ASCII 字符的文本)的分词质量。GPT-2、BART 和LLaMA 等大语言模型都采用了这种分词方法。原始LLaMA 的词表大小是32K,并且主要根据英文进行训练,因此,很多汉字都没有直接出现在词表中,需要字节来支持所有的中文字符,由2 个或者3 个Byte Token 才能拼成一个完整的汉字。
对于使用了字节对编码的大语言模型,其输出序列也是词元序列。对于原始输出,根据终结语符 的位置确定每个单词的范围,合并范围内的词元,将输出重新组合为词序列,作为最终的结果。WordPiece也是一种常见的词元分析算法,最初应用于语音搜索系统。此后,该算法做为BERT 的分词器。
WordPiece 与BPE 有非常相似的思想,都是通过迭代地合并连续的词元,但在合并的选择标准上略有不同。为了进行合并,WordPiece 需要首先训练一个语言模型,并用该语言模型对所有可能的词元对进行评分。在每次合并时,选择使得训练数据似然概率增加最多的词元对。
图4 BPE 模型中词元词表的计算过程
由于Google 并没有发布其WordPiece 算法的官方实现,HuggingFace 在其在线NLP 课程中提供了一种更直观的选择度量方法:一个词元对的评分是根据训练语料库中两个词元的共现计数除以它们各自的出现计数的乘积。计算公式如下所示:
Unigram 词元分析 是另外一种应用于大语言模型的词元分析方法,T5 和mBART 采用该方法构建词元分析器。不同于BPE 和WordPiece,Unigram 词元分析从一个足够大的可能词元集合开始,然后迭代地从当前列表中删除词元,直到达到预期的词汇表大小为止。基于训练好的Unigram语言模型,使用从当前词汇表中删除某个字词后,训练语料库似然性的增加量作为选择标准。
为了估计一元语言(Unigram)模型,采用了期望最大化(Expectation–Maximization,EM)算法:每次迭代中,首先根据旧的语言模型找到当前最佳的单词切分方式,然后重新估计一元语言单元概率以更新语言模型。在这个过程中,使用动态规划算法(如维特比算法)来高效地找到给定语言模型时单词的最佳分解方式。以HuggingFace NLP 课程中介绍的Byte Pair Encoding 代码为例,介绍BPE 方法的构建和使****用,代码实现如下所示:
from transformers import AutoTokenizer
from collections import defaultdict
corpus = [
"This is the Hugging Face Course.",
"This chapter is about tokenization.",
"This section shows several tokenizer algorithms.",
"Hopefully, you will be able to understand how they are trained and generate tokens.",
]
# 使用GPT-2 tokenizer 将输入分解为单词:
tokenizer = AutoTokenizer.from_pretrained("gpt2")
word_freqs = defaultdict(int)
for text in corpus:
words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
new_words = [word for word, offset in words_with_offsets]
for word in new_words:
word_freqs[word] += 1
# 计算基础词典, 这里使用语料库中的所有字符:
alphabet = []
for word in word_freqs.keys():
for letter in word:
if letter not in alphabet:
alphabet.append(letter)
alphabet.sort()
# 增加特殊Token 在字典的开头,GPT-2 中仅有一个特殊Token``<|endoftext|>''表示文本结束
vocab = ["<|endoftext|>"] + alphabet.copy()
# 将单词切分为字符
splits = {word: [c for c in word] for word in word_freqs.keys()}
#compute_pair_freqs 函数用于计算字典中所有词元对的频率
def compute_pair_freqs(splits):
pair_freqs = defaultdict(int)
for word, freq in word_freqs.items():
split = splits[word]
if len(split) == 1:
continue
for i in range(len(split) - 1):
pair = (split[i], split[i + 1])
pair_freqs[pair] += freq
return pair_freqs
#merge_pair 函数用于合并词元对
def merge_pair(a, b, splits):
for word in word_freqs:
split = splits[word]
if len(split) == 1:
continue
i = 0
while i < len(split) - 1:
if split[i] == a and split[i + 1] == b:
split = split[:i] + [a + b] + split[i + 2 :]
else:
i += 1
splits[word] = split
return splits
# 迭代训练,每次选取得分最高词元对进行合并,直到字典大小达到设置目标为止:
vocab_size = 50
while len(vocab) < vocab_size:
pair_freqs = compute_pair_freqs(splits)
best_pair = ""
max_freq = None
for pair, freq in pair_freqs.items():
if max_freq is None or max_freq < freq:
best_pair = pair
max_freq = freq
splits = merge_pair(*best_pair, splits)
merges[best_pair] = best_pair[0] + best_pair[1]
vocab.append(best_pair[0] + best_pair[1])
# 训练完成后,tokenize 函数用于给定文本进行词元切分
def tokenize(text):
pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)
pre_tokenized_text = [word for word, offset in pre_tokenize_result]
splits = [[l for l in word] for word in pre_tokenized_text]
for pair, merge in merges.items():
for idx, split in enumerate(splits):
i = 0
while i < len(split) - 1:
if split[i] == pair[0] and split[i + 1] == pair[1]:
split = split[:i] + [merge] + split[i + 2 :]
else:
i += 1
splits[idx] = split
return sum(splits, [])
tokenize("This is not a token.")
Huggingface 的transformer 类中已经集成了很多分词器,可以直接使用。例如,利用BERT 分词器获得输入“I have a new GPU!”的词元代码如下所示:
>>> from transformers import BertTokenizer
>>> tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
>>> tokenizer.tokenize("I have a new GPU!")
["i", "have", "a", "new", "gp", "##u", "!"]
好了,上面就是关于大语言模型训练数据常见的处理方法,大家如果还想了解更多关于大语言模型的干货,可以持续关注我!