大模型系列:大模型tokenizer分词编码算法BPE理论简述和实践

news2024/11/15 23:05:50

关键词:大语言模型分词BPEBBPE

前言

token是大模型处理和生成语言文本的基本单位,在之前介绍的Bert和GPT-2中,都是简单地将中文文本切分为单个汉字字符作为token,而目前LLaMAChatGLM等大模型采用的是基于分词工具sentencepiece实现的BBPE(Byte-level BPE)分词编码算法,本节介绍BBPE分词编码作为大模型系列的开篇。


内容摘要
  • 常用分词算法简述
  • 以中文LLaMA(Atom)为例快速开始BBPE
  • Byte-Pair Encoding (BPE) 原理简述
  • Byte-level BPE(BBPE)原理简述
  • 使用sentencepiece训练BPE,BBPE

常用分词算法简述

分词编码指的是将自然语言切割为最小的语义单元token,并且将token转化为数值id供给计算机进行模型学习的过程。常用的分词算法根据切分文本的颗粒度大小分为word,char,subword三类,以英文文本I am disappointed in you为例,三种方法切分结果如下

颗粒度切割方式分词结果
word单词级别分词,英文天然可以根据空格分割出单词[I, am, disappointed, in, you]
character字符级别分词,以单个字符作为最小颗粒度[I, , a, m, , d, i, s, ..., y, o, u]
subword介于word和character之间,将word拆分为子串[I, am, disappoint, ed, in, you]

word方式的优点是保留住了完整的单词作为有意义的整体,相比于character语义表达更加充分,但是缺点是导致词表变大,因为罗列出单词的所有组合明显比穷举出所有字符更加困难,并且对于极少出现单词组合容易训练不充分,另外的word虽然区分出了单词,但是对于单词之间语义关系无法进一步刻画,比如英文中的cat和cats这种单复数情况。
character方法的优势在于词表小,5000多个中文常用字基本能组合出所有文本序列,但是缺点也很明显,缺乏单词的语义信息,并且分词的结果较长,增加了文本表征的成本。
subword方法平衡以上两种方法, 它可以较好的平衡词表大小和语义表达能力,本篇介绍的Byte-Pair Encoding (BPE) 和Byte-level BPE(BBPE)都属于subword方法。


以中文LLaMA(Atom)为例快速开始BBPE

Atom是基于LLaMA架构在中文文本上进行训练得到的语言模型,它对中文采用BBPE分词,整个词表包含65000个token,在HuggingFace搜索Atom-7B进行模型下载

HuggingFace Atom

Atom的分词器使用的是LlamaTokenizer,使用Python简单调用对文本进行分词如下

>>> from transformers import LlamaTokenizer
>>> tokenizer = LlamaTokenizer.from_pretrained("./Atom-7B")
>>> text = "我很开心我能和我们的团队一起工作"
>>> tokenizer.tokenize(text)
['▁我', '很开心', '我能', '和我们', '的团队', '一起', '工作']
>>> tokenizer.encode(text)
[32337, 43804, 42764, 53769, 49300, 32212, 32001]

从分词结果来看,BBPE类似jieba分词一样将中文字符进行了聚合成为一个一个的子串,而最终也是以子串整体映射到一个数值id,其中句子开头,或者文本中存在空格符,分词算法会将其替换为符号。
在LlamaTokenizer类中调用了sentencepiece来获取模型分词器,后续的分词操作也是基于sentencepiece提供的API方法

import sentencepiece as spm
...
self.sp_model = spm.SentencePieceProcessor(**self.sp_model_kwargs)
self.sp_model.Load(vocab_file)

Atom-7B模型目录下的tokenizer.model为BBPE分词模型,使用sentencepiece载入该分词模型可以实现LlamaTokenizer同样的效果

# pip install sentencepiece
>>> import sentencepiece
>>> tokenizer = sentencepiece.SentencePieceProcessor()
>>> tokenizer.Load("./Atom-7B/tokenizer.model")
>>> tokenizer.encode_as_pieces(text)
['▁我', '很开心', '我能', '和我们', '的团队', '一起', '工作']
>>> tokenizer.encode(text)
[32337, 43804, 42764, 53769, 49300, 32212, 32001]

tokenizer.model分词模型可以通过手动安装谷歌的项目源码,使用命令行导出为tokenizer.vocab词表,从而得到每个token和token id的对应关系,sentencepiece命令工具安装方式如下

# download sentencepiece项目源码
$ unzip sentencepiece.zip
$ cd sentencepiece
$ mkdir build
$ cd build
$ cmake ..
$ make -j $(nproc)
$ make install
$ ldconfig -v

安装完成在环境变量下出现命令spm_export_vocab,指定分词模型地址和输出词表文本地址即可完成词表导出

$ which spm_export_vocab
/usr/local/bin/spm_export_vocab

$ spm_export_vocab \
--model=./Atom-7B/tokenizer.model \
--output=./Atom-7B/tokenizer.vocab

完成后生成tokenizer.vocab词表文件,打开词表搜索下'很开心'这个子串处在43805行,和编码结果43804一致(索引从0开始)

$ less -N tokenizer.vocab
...
43804 骑行    0
43805 很开心  0
43806 在里面  0
...

对于不在tokenizer.vocab中的生僻中文字符,BBPE会将他进行UTF-8编码用字节表示,使用字节去映射词表的token id,而不是使用UNK位置填充,这也是BBPE中Byte-level的体现

# 以生僻字’龘‘为例,对’龘‘进行UTF-8编码为字节表示
>>> "龘".encode("utf-8")
b'\xe9\xbe\x98'

>>> tokenizer.encode_as_pieces("龘")
['▁', '<0xE9>', '<0xBE>', '<0x98>']
>>> tokenizer.tokenize("龘")
[29871, 236, 193, 155]

Byte-Pair Encoding (BPE) 原理简述

BBPE是基于BPE在字节颗粒度上的拓展,两者在分词算法上没有本质区别,本节先介绍BPE分词算法。
BPE的核心思想是事先给定一个最大分词数量,针对语料文本中的每个字符token,逐步合并出现频率最高的连续的两个字符组合,形成一个新词token,直到达到目标分词数量。BPE的计算流程图如下


BPE计算流程图
  • step 1:设定最大分词词典数量vocab size,初始化一个词典
  • step 2:将语料中所有文本切成单个字符形式加入词典,并且将<eos>,<bos>,<unk>,空格符等特殊字符也加入词典
  • step 3:对已经切为字符的语料,全局统计一轮连续两个字符出现组合的频率
  • step 4:取最大频率的组合,将这两个字符合并为一个整体,将这个整体添加到词典,并且在语料中将这两个字符也同步全部替换为这个新的整体,当作一个词
  • step 5:重复step 3和step 4直到达到vocab size或者无法再合并为止
  • step 6:将最终的词典生成分词编码模型文件,比如tokenizer.model,后续的任务都以这个分词词典来切词和编码

以一个只有2行的小文本“我很开心我能和我们的团队一起工作。我很欣赏我团队”为例,来说明BPE的计算流程,事先设定vocab size为23。两句话一共包含16个字符,但是还需要加上<eos>,<bos>,<unk>以及句子开头▁四种特殊符号,将它们全部添加到词表已经有20个token,下一步对单个token进行聚合,统计出有三个组合的频率最高,分别为'▁我','我很','团队'各出现了2次,继续添加到词表,最终刚好形成23个词,BPE算法停止

语料我很开心我能和我们的团队一起工作。我很欣赏我团队
单字符作, 们, 一, 工, 能, 队, 欣, 的, 和, 我, 心, 起, 开, 赏, 团, 很
特殊字符<eos>, <bos>, <unk>, ▁
组合字符▁我, 我很, 团队

Byte-level BPE(BBPE)原理简述

BBPE将BPE的聚合下推到字节级别的,先通过UTF-8的编码方式将任意字符转化为长度1到4个字节,1个字节有256种表示,以字节为颗粒度进行聚合,其他流程和BPE是一样的。
在BBPE训练之前,256个字节表示作为token会全部加入词典,观察Atom的tokenizer.vocab,前三个位置分别为未登录词UNK,句子开头符,句子结束符,从第四个位置开始插入了256个字节集

1 <unk>   0
2 <s>     0
3 </s>    0
4 <0x00>  0
5 <0x01>  0
...
258 <0xFE>  0
259 <0xFF>  0
...

随着字节的聚合形成原始的字符,进一步可以形成词组,最终输出到tokenizer.model的时候会转化为聚合后的字符。
在模型使用的时候对于输入的字符,如果直接存在则映射为token id,如果不存在则转化为UTF-8编码之后的字节作为单位做映射,例如前文中的'龘‘会被映射为3个token id。
BBPE的优点:可以跨语言共用词表,任意语种都可以被编码到字节进行表示,另外UTF-8编码可以在不同语言之间具有一定互通性,底层字节层面的共享来实可能能够带来知识迁移。针对稀有字符,BBPE不会为其分配专门的token id,而是使用字节级别来编码来解决OOV的问题,一定程度上控制了词表大小和解决了稀疏字符难以训练的问题。
BBPE的缺点:会使得单个中文字符被切割为多个字节表示,导致表征的成本上升,可以通过扩大vocab size来促进字节的聚合,使得更多的字符和词组被挖掘出来作为单独的token id。


使用sentencepiece训练BPE,BBPE

Python安装的包sentencepiece和源码安装的spm_train命令工具都可以完成BPE和BBPE的训练,例如以小部分《狂飙》的剧本作为语料训练分词模型,代码如下

>>> import sentencepiece as spm

>>> spm.SentencePieceTrainer.train(
    input='./data/corpus.txt',
    model_type="bpe",
    model_prefix='tokenizer',   
    vocab_size=3000, 
    character_coverage=1,  
    max_sentencepiece_length=6, 
    byte_fallback=False
)

SentencePieceTrainer的训练模式支持BPE,unigram等多种模式,当model_type为'bpe'且不开启byte_fallback,该模式为BPE,如果开启byte_fallback代表BBPE模式,byte_fallback代表是否将未知词转化为UTF-8字节表示进行编码,如果不开启则对于OOV的词会直接输出<unk>。
训练完成后在目录下会生成tokenizer.model和tokenizer.vocab两个文件,查看BPE的分词词表tokenizer.vocab如下

1 <unk>   0
2 <s>     0
3 </s>    0
4 :“      -0
5 ▁”      -1
6 安欣    -2
7 ..      -3
8 高启    -4
9 ?”      -5
10 高启强  -6

词表从上到下的顺序也蕴含了词频从高到低的关系。针对未在语料中出现过的字符分别测试下BPE和BBPE的编码结果

>>> # BPE
>>> token_model_1 = sentencepiece.SentencePieceProcessor()
>>> token_model_1.Load("./tokenizer.model")

>>> # BBPE
>>> token_model_2 = sentencepiece.SentencePieceProcessor()
>>> token_model_2.Load("./tokenizer2.model")

针对文本中未出现的'凰'字符分词编码结果如下

>>> token_model_1.encode("凰")
[882, 0]

>>> token_model_2.encode("凰")
[882, 232, 138, 179]

结论和前文一致,BPE方式对于未登陆词输出<unk>的token id为0,而BBPE如果映射不到该词会转化为3个字节表示,输出三个token id,全文完毕。



喜欢的朋友记得点赞、收藏、关注哦!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1982144.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

云原生高级必备基础

一.文件管理 相对路径和绝对路径 touch 创建文件 mkdir 创建目录 -p多级创建 rm 删除 -i 删除前逐一询问确认。 -f 即使原档案属性设为唯读&#xff0c;亦直接删除&#xff0c;无需逐一确认。 -r 将目录及以下之档案亦逐一删除。 cp 复制 -p -r mv 移动 cp和mv的区别 …

8月4号分析:CSGO市场行情如何,给几个操作建议

很多粉丝让我聊聊对近期CSGO饰品市场的看法&#xff0c;那今天就简单聊聊&#xff01; 最近的CSGO市场&#xff0c;从在线人数就可以看出来&#xff0c;这段时间是实打实的流失了很多玩家&#xff0c;就目前这个情况&#xff0c;120万的在线人数里面&#xff0c;至少还有10多万…

sql注入之无列名注入

目录 一、基础知识 二、平替information_schema库 三、无列名注入 3.1 正常无列名查询&#xff1a; 3.2 子查询&#xff1a; 3.3 实战 一、基础知识 我们在注入的过程中很多时候利用的就是information_schema这个库获取 table_schema table_name, column_name这些数据库内…

一键转换语言,五款强大文件翻译软件推荐!

在当今的职场环境中&#xff0c;跨语言沟通已成为常态。无论是与国际客户洽谈业务&#xff0c;还是处理海外项目报告&#xff0c;精准高效的文件翻译能力都是每位职场人士的必备技能。今天&#xff0c;我们就来盘点几款职场人士必备的文件翻译工具。 福昕在线翻译&#xff1a;…

PXE批量安装——————rhel7

实验前准备 什么是PXE&#xff1f; PXE是一种基于网络的启动技术&#xff0c;它集成了在计算机的BIOS或UEFI中&#xff0c;允许计算机从网络服务器下载并启动操作系统或其他软件。 应用场景 无盘工作站&#xff1a;在教育和科研机构中&#xff0c;无盘工作站通过PXE启动操作…

字符串切割split

let obj {} let str "aa占比:17.48%,aa计费占比:0.00%" let arr str.split(,) // [aa占比:17.48%,aa计费占比:0.00%] arr.forEach(item > { let [key,value] item.split(:) obj[key] value }) console.log(obj) //{aa占比: 17.48%, aa计费占比: 0.00%} con…

Markdown文本编辑器:Typora for Mac/win 中文版

Markdown 是一种轻量级的标记语言&#xff0c;它允许用户使用易读易写的纯文本格式编写文档。Typora 支持且仅支持 Markdown 语法的文本编辑&#xff0c;生成的文档后缀名为 .md。 这款软件的特点包括&#xff1a; 实时预览&#xff1a;Typora 的一个显著特点是实时预览&#x…

lombok安装成功但是找不到方法

2024.1.1版本的IDE的插件安装了默认的lombok&#xff08;如图1&#xff09;&#xff0c;pom文件中也引入了lombok的依赖&#xff0c;在实体类写了Data的注解&#xff0c;当调用实体类的get和set方法运行时&#xff0c;报错找不到相应的方法&#xff0c;但是在调用get、set方法的…

Java实现全局异常统一处理

Java实现全局异常统一处理 【一】介绍【二】为什么要使用全局异常处理【三】案例实现【1】GlobalException【2】GlobalExceptionHandler【3】使用 【一】介绍 全局异常处理器是一种在应用程序中几种处理异常的机制&#xff0c;它允许在应用程序的任何地方抛出异常时&#xff0…

2024年6月scratch图形化编程等级考试一级真题

202406 scratch编程等级考试一级真题 选择题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 1、音乐Video Game1的时长将近8秒&#xff0c;点击一次角色&#xff0c;下列哪个程序不能完整地播放音乐两次 A、 B、 C、 D、 答案&#xff1a;D 考点分…

CasaOS系统小主机Docker部署memos结合内网穿透打造私有云笔记

文章目录 前言1. 使用Docker部署memos2. 注册账号与简单操作演示3. 安装cpolar内网穿透4. 创建公网地址5. 创建固定公网地址 前言 本文主要介绍如何在CasaOS轻NAS系统设备中使用Docker本地部署开源云笔记服务memos&#xff0c;并结合cpolar内网穿透工具配置公网地址&#xff0…

STL-queue容器适配器

目录 一、queue 1.1 使用 1.2 模拟实现 二、priority_queue 2.1 使用 2.2 仿函数 2.2.1 概念 2.2.2 使用 2.3 模拟实现 一、queue 1.1 使用 具体解释详见官方文档&#xff1a;queue - C Reference (cplusplus.com) queue就是数据结构中的队列&#xff1a;数据结构之…

体系结构论文导读(三十四):Design of Reliable DNN Accelerator with Un-reliable ReRAM

文章核心 这篇文章主要讨论了一种在不可靠的ReRAM&#xff08;阻变存储器&#xff09;设备上设计可靠的深度神经网络&#xff08;DNN&#xff09;加速器的方法。文章提出了两种关键技术来解决ReRAM固有的不可靠性问题&#xff1a;动态定点&#xff08;DFP&#xff09;数据表示…

日撸Java三百行(day14:栈)

目录 一、栈的基本知识 1.栈的概念 2.栈的功能 3.栈的实现 二、栈的代码实现 1.栈的基本属性与方法 2.栈的遍历 3.入栈实现 4.出栈实现 5.数据测试 6.完整的程序代码 总结 一、栈的基本知识 1.栈的概念 根据百度百科&#xff0c;我们知道“栈”是存储货物或供旅客…

小怡分享之Java图书管理系统

前言&#xff1a; &#x1f308;✨前面小怡给大家分享了抽象类和接口&#xff0c;今天小怡给大家分享用Java实现图书管理系统。 1.功能 不同的用户看到的菜单是不一样的&#xff0c;我们分为两个用户身份&#xff0c;管理员和普通用户。 2.知识点 数据类型、变量、数组、方法…

跳妹儿学编程之ScratchJr(12):综合篇-五只小猴子床上跳

博主资深软件架构师&#xff0c;拥有13年大型软件与互联网系统开发、设计和架构经验&#xff0c;曾就职于华为&#xff0c;现任职于国内知名互联网公司。平时在家教咱家“跳妹儿”编程&#xff0c;并将心得和过程记录下来。希望可以帮助更多对编程感兴趣的家庭。 引言 在前面的…

略读ArrayList源码

ArrayList是Java集合框架中的一部分&#xff0c;底层是通过数组实现的&#xff0c;可以动态增长和缩减。 一、首先看成员变量 序列化ID定义。在Java中&#xff0c;如果一个类实现了Serializable接口&#xff0c;那么它的serialVersionUID就非常重要了。serialVersionUID用于确…

Cesium初探-CallbackProperty

在Cesium中&#xff0c;CallbackProperty 是一种非常有用的特性&#xff0c;可以用来动态更新实体的属性&#xff0c;如位置、方向、高度等。CallbackProperty 允许你在指定的时间点计算属性值&#xff0c;这样就可以实时地改变实体的状态而不需要频繁地重新设置整个属性。 下…

PCIe学习笔记(16)

层次结构&#xff08;Hierarchy&#xff09;ID Message &#xff08;PCIe I/O 互连的树形拓扑结构称为 PCIe 的 Hierarchy&#xff0c;或称层级、层次&#xff08;不是事务层、数据链路层的“层”&#xff09;。层次区域是指与 RC 某一 RP 相关联的所有设备和链路组成的线路结…

【Linux课程学习】:对于权限的理解(粘滞位)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;Linux课程学习 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 这篇文章主要理解权限的概念&#xff0c;以及如何更改…