2024年大模型面试准备(二):LLM容易被忽略的Tokenizer与Embedding

news2024/11/17 23:49:32

分词和嵌入一直是LM被忽略的一部分。随着各大框架如HF的不断完善,大家对tokenization和embedding的重视程度越来越低,到现在初学者大概只能停留在调用tokenizer.encode这样的程度了。

知其然不知其所以然是很危险的。比如你要调用ChatGPT的接口,但是经常发现输出被截断了,原因可能就是你输入的句子太长了。你计算句子长度是直接用空格分词,而ChatGPT是用不同的分词法(BPE分词法)。又如你不清楚T5的tokenizer的运作机理,输入数据中存在一些特殊token如’{}',但不知道T5使用的SentencePiece tokenizer不原生支持这些token,因此发现自己生成的结果有问题,都是。

以上这些问题听上去比较低级,但是debug起来往往是最头秃的,如果只会调用接口但不明白其中的机理,相信如果面试时问起来也是答不上来的。为了解决这些疑惑,并助准备面试的人一臂之力,本文会提供一份详细的关于分词和嵌入的解答。

喜欢本文记得收藏、关注、点赞。更多技术、面试交流,加入文末我们的社群。

Tokenizer

如果大家用过HuggingFace,对tokenizer肯定不会陌生。在使用模型前,都需要将sequence过一遍tokenizer,进去的是word序列(句子),出来的是number序列。但是,HF的tokenizer到底在做什么?事实上,tokenizer总体上做三件事情:

  1. 分词
    tokenizer将字符串分为一些sub-word token string,再将token string映射到id,并保留来回映射的mapping。从string映射到id为tokenizer encode过程,从id映射回token为tokenizer decode过程。映射方法有多种,例如BERT用的是WordPiece,GPT-2和RoBERTa用的是BPE等等,后面会详细介绍。

  2. 扩展词汇表
    部分tokenizer会用一种统一的方法将训练语料出现的且词汇表中本来没有的token加入词汇表。对于不支持的tokenizer,用户也可以手动添加。

  3. 识别并处理特殊token
    特殊token包括[MASK], <|im_start|>, , 等等。tokenizer会将它们加入词汇表中,并且保证它们在模型中不被切成sub-word,而是完整保留。

分词粒度

我们首先来看一下几种不同的分词粒度。

最直观的分词是单词分词法(word base)。单词分词法将一个word作为最小元,也就是根据空格或者标点分词。举例来说,Today is Sunday用word-base来进行分词会变成[‘Today’, ‘is’, ‘Sunday’]。

最详尽的分词是单字分词法(character-base)。单字分词法会穷举所有出现的字符,所以是最完整的。在上面的例子中,单字分词法会生成[‘T’, ‘o’, ‘d’, …, ‘a’, ‘y’]。

另外还有一种最常用的、介于两种方法之间的分词法叫子词分词法,会把上面的句子分成最小可分的子词[‘To’, ‘day’, ‘is’, ‘S’, ‘un’, ‘day’]。子词分词法有很多不同取得最小可分子词的方法,例如BPE(Byte-Pair Encoding,字节对编码法),WordPiece,SentencePiece,Unigram等等。

接下来我们具体看看各大主流模型用的是什么分词法。

GPT族:Byte-Pair Encoding (BPE)

从GPT-2开始一直到GPT-4,OpenAI一直采用BPE分词法。这种方法的子词构造算法是这样的:

1. 统计输入中所有出现的单词并在每个单词后加一个单词结束符</w> -> ['hello</w>': 6, 'world</w>': 8, 'peace</w>': 2]
2. 将所有单词拆成单字 -> {'h': 6, 'e': 10, 'l': 20, 'o': 14, 'w': 8, 'r': 8, 'd': 8, 'p': 2, 'a': 2, 'c': 2, '</w>': 3}
3. 合并最频繁出现的单字(l, o) -> {'h': 6, 'e': 10, 'lo': 14, 'l': 6, 'w': 8, 'r': 8, 'd': 8, 'p': 2, 'a': 2, 'c': 2, '</w>': 3}
4. 合并最频繁出现的单字(lo, e) -> {'h': 6, 'lo': 4, 'loe': 10, 'l': 6, 'w': 8, 'r': 8, 'd': 8, 'p': 2, 'a': 2, 'c': 2, '</w>': 3}
5. 反复迭代直到满足停止条件

显然,这是一种贪婪的算法。在上面的例子中,'loe’这样的子词貌似不会经常出现,但是当语料库很大的时候,诸如est,ist,sion,tion这样的特征会很清晰地显示出来。

在获得子词词表后,就可以将句子分割成子词了,算法见下面的例子:

# 给定单词序列
["the</w>", "highest</w>", "mountain</w>"]

# 从一个很大的corpus中排好序的subword表如下
# 长度 6         5           4        4         4       4          2
["errrr</w>", "tain</w>", "moun", "est</w>", "high", "the</w>", "a</w>"]

# 迭代结果
"the</w>" -> ["the</w>"]
"highest</w>" -> ["high", "est</w>"]
"mountain</w>" -> ["moun", "tain</w>"]

注意,在上述算法执行后,如果句子中仍然有子字符串没被替换, 但所有subword都已迭代完毕,则将剩余的子词替换为特殊token,如 。从这里大家也可以发现了,原则上这个token出现的越少越好,所以我们也往往用的数量来评价一个tokenizer的好坏程度,这个token出现的越少,tokenizer的效果往往越好。

管中窥豹,根据BPE算法,我们可以发现,tokenizer基本上是无法并行的,因为存在大量if-else的branch。学过GPU Programming的同学应该知道,conditional branch越多,GPU提供的加速越有限,有时候还会造成负加速,因为数据传输有很大开销。这就是为什么在tokenizing的时候,我们看到GPU util都是0。

BERT族:Word-Piece

Word-Piece和BPE非常相似,BPE使用出现最频繁的组合构造子词词表,而WordPiece使用出现概率最大的组合构造子词词表。换句话说,WordPiece每次选择合并的两个子词,通常在语料中以相邻方式同时出现。比如说 P(ed) 的概率比P(e) + P(d)单独出现的概率更大(可能比他们具有最大的互信息值),也就是两个子词在语言模型上具有较强的关联性。这个时候,Word-Piece会将它们组合成一个子词。

有关BERT的tokenizer还有一个重点:BERT在使用Word-Piece时加入了一些特殊的token,例如[CLS]和[SEP]。我们可以自己试一下:

inputs = 'Coolest pretrained Asmita!'
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
input_ids = tokenizer.encode(inputs,return_tensors='pt')
print(input_ids)
for input_id in input_ids [0]:
    print(tokenizer.decode(input_id))

# outputs
tensor([[101, 4658435536532365420982004229302050999102]])
[CLS] 
cool
##est
pre
##train
##ed 
as
##mit
##a
!
[SEP]

发现BERT在句首加上了[CLS],句尾加上了[SEP],而且对coolest做了子词分解,对词根est加上了##来表示这是一个后缀。对于没有出现在词汇表里的单词例如asmita(是个印度人名),BERT所用的Word-Piece tokenizer会将它分解为Word-Piece算法形成的子词词汇表中存在的as,mit和a,组成一个子词。

多语言支持:Sentence-Piece

大家在使用HF的时候有时候会提示安装Sentence-Piece,这个包其实是HF里面大量模型会调用的包,例如ALBERT,XLM-RoBERTa和T5:

图片

这个包主要是为了多语言模型设计的,它做了两个重要的转化:

  1. 以unicode方式编码字符,将所有的输入(英文、中文等不同语言)都转化为unicode字符,解决了多语言编码方式不同的问题。

  2. 将空格编码为‘_’, 如’New York’ 会转化为[‘_’, ‘New’, ‘_York’],这也是为了能够处理多语言问题,比如英文解码时有空格,而中文没有, 类似这种语言区别。

词汇表不全问题

但是,也是因为这两个转化,SentencePiece的tokenizer往往会出现词汇表不全的问题。下面是部分SentencePiece中可能出现的问题:

图片

如果某个token被识别成,那它就无法与其他也被识别成的token区分开来。例如在训练的时候有大量{hello world}的样本,在输出的时候就会变成 hello world 的样本。

这些问题不存在于WordPiece中。这是因为SentencePiece需要对多语言情况进行优化,有些token迫不得已要被删掉。想要加上某些本来tokenizer中不存在的token,可以使用add_tokens()方法。

inputs = '{ }'
# tokenizer = BartTokenizer.from_pretrained('facebook/bart-base')
tokenizer=T5Tokenizer.from pretrained('t5-small')
tokenizer.add_tokens(['{''}'])
input_ids = tokenizer.encode(inputs,return_tensors='pt') 
print(input_ids) 
for input_id in input_ids[0]:
    print(tokenizer.decode(input_id)) 

# save the tokenizer 
tokenizer.save_pretrained('tokenizer')

# outputs
tensor([[32100321011]]) 
{
}
</s>

使用后,保存的文件夹里面会出现一个added_tokens.json文件,里面就包含这两个新的token, 如下所示。这个时候再load这个tokenizer,这两个token就可以被模型识别了。

{
  "{": 32100,
  "}": 32101
}

这之后,还需要告诉模型我已经更新了词汇表,使用model.resize_token_embeddings(len(tokenizer))。完整的添加例子如下:

from transformers import AutoTokenizer, AutoModel

# pick the model type
model_type = "roberta-base"
tokenizer = AutoTokenizer.from_pretrained(model_type)
model = AutoModel.from_pretrained(model_type)

# new tokens
new_tokens = ["new_token"]

# check if the tokens are already in the vocabulary
new_tokens = set(new_tokens) - set(tokenizer.vocab.keys())

# add the tokens to the tokenizer vocabulary
tokenizer.add_tokens(list(new_tokens))

# add new, random embeddings for the new tokens
model.resize_token_embeddings(len(tokenizer))

词汇表不全的问题在Word-Piece和BPE中也存在,在使用tokenizer的时候要格外小心。

各路语言模型中的tokenizer

各大LM用的tokenizer和对应的词汇表大小如下:

图片

另外,有大佬做了各大LLM的词汇表大小和性能:

图片

更多关于tokenizer的知识可以参见huggingface的官方博客:https://huggingface.co/docs/transformers/tokenizer_summary

Embedding

tokenize完的下一步就是将token的one-hot编码转换成更dense的embedding编码。在ELMo之前的模型中,embedding模型很多是单独训练的,而ELMo之后则爆发了直接将embedding层和上面的语言模型层共同训练的浪潮(ELMo的全名就是Embeddings from Language Model)。不管是哪种方法,Embedding层的形状都是一样的。我们举个例子来看看embedding层是怎么工作的。在HuggingFace中,seq2seq模型往往是这样调用的:

input_ids = tokenizer.encode('Hello World!', return_tensors='pt')
output = model.generate(input_ids, max_length=50)
tokenizer.decode(output[0])

上面的代码主要涉及三个操作:tokenizer将输入encode成数字输入给模型,模型generate出输出数字输入给tokenizer,tokenizer将输出数字decode成token并返回。

例如,如果我们使用T5TokenizerFast来tokenize ‘Hello World!’,则:

  1. tokenizer会将token序列 [‘Hello’, ‘World’, ‘!’] 编码成数字序列[8774, 1150, 55, 1],也就是[‘Hello’, ‘World’, ‘!’, ‘’],在句尾加一个表示句子结束。

  2. 这四个数字会变成四个one-hot向量,例如8774会变成[0, 0, …, 1, 0, 0…, 0, 0],其中向量的index为8774的位置为1,其他位置全部为0。假设词表里面一共有30k个可能出现的token,则向量长度也是30k,这样才能保证出现的每个单词都能被one-hot向量表示。

  3. 也就是说,一个形状为 (4) 的输入序列向量,会变成形状为 (4, 30k) 的输入one-hot向量。为了将每个单词转换为一个word embedding,每个向量都需要被被送到embedding层进行dense降维。

  4. 现在思考一下,多大的矩阵可以满足这个要求?没错,假设embedding size为768,则矩阵的形状应该为(30k, 768) ,与BERT的实现一致:

BertForSequenceClassification(
  (bert):BertModel(
    (embeddings):BertEmbeddings(
      (word_embeddings):Embedding(30522768,padding_idx=0)
      (position_embeddings):Embedding(512768) 
      (token_type_embeddings):Embedding(2768)
      (LayerNorm): LayerNorm((768,),eps=1e-12,elementwise_affine=True)
      (dropout): Dropout(p=0.1, inplace=False) 
    )
  )
)

理解Embedding矩阵

Embedding矩阵的本质就是一个查找表。由于**输入向量是one-hot的,embedding矩阵中有且仅有一行被激活。**行间互不干扰。这是什么意思呢?如下图所示,假设词汇表一共有6个词,则one-hot表示的长度为6。现在我们有三个单词组成一个句子,则输入矩阵的形状为 (3, 6)。然后我们学出来一个embedding矩阵,根据上面的推导,如果我们的embedding size为4,则embedding矩阵的形状应该为(6, 4)。这样乘出来的输出矩阵的形状应为(3, 4)。

在这里插入图片描述

图中用不同颜色标明了三个subword embedding分别的计算过程。对于第一个单词’I’,假设其one-hot编码为 [0, 0, 1, 0, 0, 0],将其与embedding矩阵相乘,相当于取出embedding矩阵的第3行(index为2)。同理,对于单词’love’,相当于取出embedding矩阵的第二行(index为1)。这样一来大家就理解了,embedding矩阵的本质是一个查找表,每个单词会定位这个表中的某一行,而这一行就是这个单词学习到的在嵌入空间的语义。

技术交流群

前沿技术资讯、算法交流、求职内推、算法竞赛、面试交流(校招、社招、实习)等、与 10000+来自港科大、北大、清华、中科院、CMU、腾讯、百度等名校名企开发者互动交流~

我们建了大模型算法岗技术与面试交流群, 想要进交流群、需要源码&资料、提升技术的同学,可以直接加微信号:mlc2040。加的时候备注一下:研究方向 +学校/公司+CSDN,即可。然后就可以拉你进群了。

方式①、微信搜索公众号:机器学习社区,后台回复:加群
方式②、添加微信号:mlc2040,备注:技术交流

用通俗易懂方式讲解系列

  • 《大模型面试宝典》(2024版) 正式发布!

  • 《大模型实战宝典》(2024版)正式发布!

  • 用通俗易懂的方式讲解:自然语言处理初学者指南(附1000页的PPT讲解)

  • 用通俗易懂的方式讲解:1.6万字全面掌握 BERT

  • 用通俗易懂的方式讲解:NLP 这样学习才是正确路线

  • 用通俗易懂的方式讲解:28张图全解深度学习知识!

  • 用通俗易懂的方式讲解:不用再找了,这就是 NLP 方向最全面试题库

  • 用通俗易懂的方式讲解:实体关系抽取入门教程

  • 用通俗易懂的方式讲解:灵魂 20 问帮你彻底搞定Transformer

  • 用通俗易懂的方式讲解:图解 Transformer 架构

  • 用通俗易懂的方式讲解:大模型算法面经指南(附答案)

  • 用通俗易懂的方式讲解:十分钟部署清华 ChatGLM-6B,实测效果超预期

  • 用通俗易懂的方式讲解:内容讲解+代码案例,轻松掌握大模型应用框架 LangChain

  • 用通俗易懂的方式讲解:如何用大语言模型构建一个知识问答系统

  • 用通俗易懂的方式讲解:最全的大模型 RAG 技术概览

  • 用通俗易懂的方式讲解:利用 LangChain 和 Neo4j 向量索引,构建一个RAG应用程序

  • 用通俗易懂的方式讲解:使用 Neo4j 和 LangChain 集成非结构化知识图增强 QA

  • 用通俗易懂的方式讲解:面了 5 家知名企业的NLP算法岗(大模型方向),被考倒了。。。。。

  • 用通俗易懂的方式讲解:NLP 算法实习岗,对我后续找工作太重要了!。

  • 用通俗易懂的方式讲解:理想汽车大模型算法工程师面试,被问的瑟瑟发抖。。。。

  • 用通俗易懂的方式讲解:基于 Langchain-Chatchat,我搭建了一个本地知识库问答系统

  • 用通俗易懂的方式讲解:面试字节大模型算法岗(实习)

  • 用通俗易懂的方式讲解:大模型算法岗(含实习)最走心的总结

  • 用通俗易懂的方式讲解:大模型微调方法汇总

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

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

相关文章

mysql迁移达梦数据库 Java踩坑合集

达梦数据库踩坑合集 文章目录 安装达梦设置大小写不敏感Spring boot引入达梦驱动&#xff08;两种方式&#xff09;将jar包打入本地maven仓库使用国内maven仓库&#xff08;阿里云镜像&#xff09; 达梦驱动yml配置springboot mybatis-plus整合达梦,如何避免指定数据库名&…

进程和线程,线程实现的几种基本方法

什么是进程&#xff1f; 我们这里学习进程是为了后面的线程做铺垫的。 一个程序运行起来&#xff0c;在操作系统中&#xff0c;就会出现对应的进程。简单的来说&#xff0c;一个进程就是跑起来的应用程序。 在电脑上我们可以通过任务管理器可以看到&#xff0c;跑起来的应用程…

C语言 04 基本数据类型

整数 整数就是不包含小数点的数字&#xff0c;整数包含以下几种类型&#xff1a; short &#xff1a;占用 2 个字节&#xff0c;16 个 bit 位。int&#xff1a;占用 4 个字节&#xff0c;32 个 bit 位&#xff0c;能够表示 -2^32 到 2^32 之间的数字&#xff0c;默认使用这种…

Open CASCADE学习|最小二乘法拟合直线

最小二乘法&#xff0c;又称最小平方法&#xff0c;起源于十八世纪的大航海探索时期&#xff0c;发展于天文领域和航海领域。其历史可以追溯到法国科学家马里勒让德于1805年首次提出这一概念&#xff0c;而1809年&#xff0c;高斯在他的著作《天体运动论》中也提出了最小二乘法…

LangChain核心模块 Model I/O

Model I/O 任何语言模型应用程序的核心元素都是模型。LangChain 为您提供了与任何语言模型交互的构建块。 Models llm 和 chat_model 都是表示特定模型配置的对象。 LLM 对象将字符串作为输入和输出字符串。ChatModel 对象将消息列表作为输入并输出消息。 LLM 返回一个字符…

idea创建angular项目

1.idea创建项目 idea&#xff1a;2023.2.3版本 不做赘述&#xff0c;我这里是创建模块&#xff0c;创建项目的话大同小异 2.创建完成后注意一下红色部分&#xff0c;后期需要 3.进入项目根目录 注意&#xff1a;一定要进入项目根目录&#xff0c;就是我们上面红色方框部分&a…

研华工控机610L学习笔记1:基本了解与认识

今日开始学习一些工控机的基本知识&#xff1a; 目录 目录 1、工控机介绍&#xff1a; 2、研华610L说明书参数了解&#xff1a; 3、基本结构了解&#xff1a; 前面板&#xff1a; 后窗&#xff1a; 4.RS232串口&#xff1a; ​编辑 5、工控机分类&#xff1a; 6、工控上…

第四十五周:文献阅读

目录 摘要 Abstract 文献阅读&#xff1a;基于注意力的双向LSTM和编码器-解码器的水质预测 现有问题 提出方法 创新点 方法论 1、EMD&#xff08;经验模态分解&#xff09; 2、VMD&#xff08;变分模态分解&#xff09; 3、VBAED模型 研究实验 数据集 数据预处理 …

AC和AP wlan使用案例

关于AC和AP的一篇 拓补图&#xff1a; AC1 sysn AC vlan 10int g0/0/1 p l t p t a v 10 port trunk pvid vlan 10 q int g0/0/2 p l t p t a v 10 port trunk pvid vlan 10 q int vlan 10 ip add 192.168.10.1 24 q dhcp enable int vlan 10 dhcp select interface q capwa…

算法打卡day24|回溯法篇04|Leetcode 93.复原IP地址、78.子集、90.子集II

算法题 Leetcode 93.复原IP地址 题目链接:93.复原IP地址 大佬视频讲解&#xff1a;复原IP地址视频讲解 个人思路 这道题和昨天的分割回文串有点类似&#xff0c;但这里是限制了只能分割3次以及分割块的数字大小&#xff0c;根据这些不同的条件用回溯法解决就好啦 解法 回溯…

三.寄存器(内存访问)

1.内存中字的存储 2.并不是所有cpu都支持将数据段送入段寄存器&#xff0c;所以有时候用个别的寄存器先把数据段存储起来&#xff0c;再把该寄存器mov到段寄存器。 3.字的传送 4.栈 5.栈机制 举例说明 6.栈顶超界问题 push超界 pop超界 7.栈段

管理自由,体验简单,使用安全 | 详解威联通全套多用户多权限管理方案【附TS-466C产品介绍】

管理自由&#xff0c;体验简单&#xff0c;使用安全 | 详解威联通全套多用户多权限管理方案【附TS-466C产品介绍】 哈喽小伙伴们好&#xff0c;我是Stark-C~。今天我们来解决一个之前评论区多次被提及的问题--多用户权限管理。 对于我们NAS用户来说&#xff0c;基本都会面临这…

Windows server 2008 R2 在VMware虚拟机上的安装

Windows server 2008 R2 在VMware虚拟机上的安装 准备工作VMware 新建并配置虚拟机安装和启动Windows server 2008 R2 准备工作 Windows server 2008 R2 ISO镜像的下载&#xff1a;Windows server 2008 R2 ISO VMware 新建并配置虚拟机 第一步&#xff0c;点击新建虚拟机 第…

leetcode每日一题2671

目录 一.题目原型 二.思路解析&#xff1a; 三.具体代码实现&#xff1a; 一.题目原型 二.思路解析&#xff1a; 其实我们可以用两个哈希表来解决这道题&#xff0c;一个哈希表hash1用来存这个数字出现了几次。另一个哈希表hash2用来存出现了几次的次数有几次&#xff0c;由于…

MySQL的日志:undo log、redo log、binlog有什么作用

目录 从一个update语句说起 undo log 为什么需要undo log undo log 版本链 undo log 是如何持久化到磁盘? redo log 为什么需要redo log redo的组成 redo Log的刷盘策略 redo Log循环写 crash-safe能力 binlog 为什么需要 binlog &#xff1f; binlog与redo lo…

unity学习(69)——多人位置同步

简单的很&#xff0c;每个客户端向服务器发送位置信息&#xff0c;服务器再把这些位置信息发送给其他客户端。 1.客户端发送。 1.1在SocketModel脚本中添加一个新的类MoveDTO public class MoveDTO {public string Id{get; set;}public int Dir{get;set;}public Assets.Mode…

数据分析和机器学习库Pandas的使用

Pandas 库是一个免费、开源的第三方 Python 库&#xff0c;是 Python 数据分析和机器学习的工具之一。Pandas 提供了两种数据结构&#xff0c;分别是 Series&#xff08;一维数组结构&#xff09;与 DataFrame&#xff08;二维数组结构&#xff09;&#xff0c;极大地增强的了 …

2024-Spring IOC 和 AOP源码分析(上篇)

前言&#xff1a;笔者最近面了几次大厂。。。开局Spring源码暴击 之前看过忘了写篇总结。。。 1、介绍一下Spring 核心组件&#xff1a; 常用模块&#xff1a; 常用注解&#xff1a; 2、说一下SpringIOC原理 概念&#xff1a;Spring 通过一个配置文件描述 Bean 及 Bean …

登录校验:JWT令牌、Filter、Interceptor

JWT&#xff1a; 全称&#xff1a;JSON Web Token 定义了一种简洁的、自包含的格式&#xff0c;用于在通信双方以json数据格式安全的传输信息&#xff0c;由于数字签名的存在&#xff0c;这些信息是可靠的。 组成&#xff1a; Header(头部)&#xff1a;&#xff08;“alg&q…

基于物理的实时渲染 -- PBR

简介 PBR&#xff0c;或者用更通俗一些的称呼是指基于物理的渲染(Physically Based Rendering)&#xff0c;它指的是一些在不同程度上都基于与现实世界的物理原理更相符的基本理论所构成的渲染技术的集合。正因为基于物理的渲染目的便是为了使用一种更符合物理学规律的方式来模…