链载Ai

标题: 【LLM基础知识】LLMs-Tokenizer知识总结笔记v2.0 [打印本页]

作者: 链载Ai    时间: 昨天 10:57
标题: 【LLM基础知识】LLMs-Tokenizer知识总结笔记v2.0


Tokenize粒度和Tokenizer

ingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: normal;text-align: left;background-color: rgb(255, 255, 255);border-bottom: 2px solid rgb(239, 112, 96);visibility: visible;margin-top: 8px;margin-bottom: 8px;">【1】Tokenize有三种粒度:word/char/subword

【1】Tokenize有三种粒度:word/char/subword

a.Tokenize的目标:Tokenize的目标是把文本text切分成子串token,每个子串token相对有完整的语义,便于学习embedding表达和后续模型的使用。
b.Tokenize的粒度:基于词[word]的切分,基于字符[char]的切分,基于子词级[subword]的切分。
以英文文本 Today is sunday为例,切分结果如下:
word:按照词进行分词,可根据空格或标点进行切分。如: Today is sunday. 切分成[today, is, sunday, .]
char:按照单字符进行分词,就是以char为最小粒度。如:Today is sunday. 切分成[t,o,d,a,y, ... ,s,u,n,d,a,y, .]
subword:按照词的subword进行分词,将word拆分为子串。如:Today is sunday. 切分成[to, day,is , s,un,day, .]

3.Tokenize的各粒度的优缺对比:

a.基于word的分词方式优点是保留单词的边界和意义,相比于char语义表达更加充分,缺点是会导致词表变大,稀有词学不好,易出现OOV问题,无法处理单词词缀关系。
b.基于char的分词方式优点是词表小,缺点是缺乏单词的语义信息,分词的结果较长,增加文本表征的成本。
c.基于subword的分词方式平衡以上两种方法优缺点,可以较好的平衡词表大小和语义表达能力。

4.基于subword的切分是目前主流切分方式。subword的切分包括: BPE(/BBPE), WordPiece 和 ULM三种分词模型。WordPiece是种特殊的BPE。

a.使用 BPE模型【主流】:GPT, GPT-2, RoBERTa, BART, DeBERTa, LLaMA, ChatGLM, Baichuan等。

b.使用WordPiece模型:BERT, DistilBERT, MobileBERT, Funnel Transformers, MPNET等。

c.使用ULM模型:AlBERT,T5,mBART,Big Bird,XLNet等。

Tonkenizer各种分词方法对比

5. Tokenizer对输入文本的分词处理流程包括:Normalization文本归一化,Pre-tokenization预分词,Model基于分词模型的切分,Postprocessor后处理。

a.Normalization:文本归一化阶段,进行常规清理例如删除多余换行、空格、转小写、以及删除重音符号等。

b.Pre-tokenization:预分词阶段,会把句子切分成更小的“词”单元。可以基于空格或者标点进行切分。

c.Model:基于分词模型的切分阶段,使用如BPE分词模型执行分词从而生成token序列。

d.Postprocessor:后处理阶段,针对具体的任务插入special token,以及生成attention mask等。

HuggingFace-Tokenizer对输入文本的分词处理流程

抱抱脸文档:

https://huggingface.co/docs/tokenizers/api/normalizers

https://huggingface.co/docs/tokenizers/api/pre-tokenizers
https://huggingface.co/docs/tokenizers/api/models
https://huggingface.co/docs/tokenizers/api/post-processors
推荐阅读:
【★】BPE、WordPiece和SentencePiece#1. 背景与基础-简书@ Jarkata【202204】:https://www.jianshu.com/p/d4de091d1367
【★】大模型基础组件- Tokenizer #2.切分流程-知乎@nghuyong【202308】:https://zhuanlan.zhihu.com/p/651430181
【★】大模型词表构建#2.技术基础-博客园@努力生活的叶子吖【202312】:https://www.cnblogs.com/Leahy/p/17808159.html
【★】优雅谈大模型:Token与分词方法-@庞德公【202406】:https://mp.weixin.qq.com/s/miiC6DEjroPLq33D6sC4BQ

ingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: normal;text-align: left;background-color: rgb(255, 255, 255);border-bottom: 2px solid rgb(239, 112, 96);visibility: visible;margin-top: 8px;margin-bottom: 8px;">【2】subword切分与word,char切分对比

【2】subword切分与word,char切分对比

a.基于词word的切分会造成:1.无法很好的处理未知或罕见的词汇[OOV问题]。2.一定会存在UNK,造成信息丢失。3. 不利于模型学习词缀之间的关系,例如:dog与dogs,happy与unhappy。4.词表中的低频词/稀疏词在模型训练过程中无法得到充分训练,进而模型不能充分理解这些词的语义。


b.基于字符char的切分会造成:1.每个token的粒度太细,丢失了词的语义信息。2.导致模型输入序列过长,解码效率很低,使得模型的训练更加复杂难以收敛。


c.基于subword的切分可以实现:1.词表规模适中,解码效率较高。不存在UNK,信息不丢失。2.能学习到词缀之间的关系。能够较好的平衡OOV问题。


subword的基本切分原则是:1.高频词依旧切分成完整的整词。2.低频词被切分成有意义的子词,例如 dogs =>[dog, ##s]。因而subword方法能够大大降低词典的大小,同时对相近词能更好地处理。


ingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: normal;text-align: left;background-color: rgb(255, 255, 255);border-bottom: 2px solid rgb(239, 112, 96);visibility: visible;margin-top: 8px;margin-bottom: 8px;">【3】Tokenizer在训练,微调模型中使用代码

【3】Tokenizer在训练,微调模型中使用代码

importtorch
fromdatasetsimportload_dataset
fromtransformersimport(
AutoModelForCausalLM,AutoTokenizer,
BitsAndBytesConfig,HfArgumentParser,
TrainingArguments,pipeline,logging,
)
frompeftimportLoraConfig,PeftModel
fromtrlimportSFTTrainer


base_model="Llama2-7b-chat-hf"#基础模型路径
guanaco_dataset="guanaco-llama2-1k"#数据集路径
new_model="llama-2-7b-chat-guanaco"#微调模型名称
dataset=load_dataset(guanaco_dataset,split="train")#加载微调数据集

tokenizer=AutoTokenizer.from_pretrained(base_model,trust_remote_code=True)#加载tokenizer
tokenizer.pad_token=tokenizer.eos_token#序列结束的标记eos_token默认是[SEP]
tokenizer.padding_side="right"#padding_side设置为right以修复fp16的问题
...

#完整微调代码:https://github.com/zzzichen277/LLM_SFT

ingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: normal;text-align: left;background-color: rgb(255, 255, 255);border-bottom: 2px solid rgb(239, 112, 96);visibility: visible;margin-top: 8px;margin-bottom: 8px;">【4】模拟Tokenizer执行过程代码

【4】模拟Tokenizer执行过程

#kaggle link:https://www.kaggle.com/code/zichen998/chatglm3-6b-tokenizer

fromtransformersimportAutoTokenizer,AutoModel

model_path="/kaggle/input/chatglm3-6b/pytorch/6b/6"
tokenizer=AutoTokenizer.from_pretrained(model_path,trust_remote_code=True)
model=AutoModel.from_pretrained(model_path,trust_remote_code=True,device='cuda')
model=model.eval()

text="你好,我是人工智能助手。"
print(f"1.用户的提问:{text}")

seg_words=tokenizer.tokenize(text)
print(f"2.将用户的提问分词成token结果:{seg_words}")

seg_word_ids=tokenizer.convert_tokens_to_ids(seg_words)
print(f"3.将用户的提问分词成token编码ids结果:{seg_word_ids}")


model_inputs=tokenizer([text],return_tensors="pt").to("cuda")
print(f"4.将用户的提问tokenizer后结果:{model_inputs}")

print("###############################################################")

generated_ids=model.generate(model_inputs.input_ids,max_new_tokens=512)
print(f"5.模型返回提问结果回答token编码ids结果:{generated_ids}")


generated_seg_word=tokenizer.convert_ids_to_tokens(generated_ids[0])
print(f"6.将模型回复编码ids 反编码为token结果:{generated_seg_word}")


response=tokenizer.batch_decode(generated_ids,skip_special_tokens=True)
print(f"7.将模型回复反编码ids 反编码为token并合并结果:{response}")

"""
1.用户的提问:你好,我是人工智能助手。
2.将用户的提问分词成token结果:['▁你', '好', ',', '我是', '人工智能', '助手', '。']
3.将用户的提问分词成token编码ids结果:[36474, 54591, 31123, 33030, 34797, 42481, 31155]
4.将用户的提问tokenizer后结果:{'input_ids': tensor([[64790, 64792, 36474, 54591, 31123, 33030, 34797, 42481, 31155]],
device='cuda:0'),'attention_mask':tensor([[1,1,1,1,1,1,1,1,1]],device='cuda:0'),'position_ids':tensor([[0,1,2,3,4,5,6,7,8]],device='cuda:0')}
###############################################################
5.模型返回提问结果回答token编码ids结果:tensor([[64790, 64792, 36474, 54591, 31123, 33030, 34797, 42481, 31155, 48895,
38549,31645,31404,42693,33277,31639,40648,55268,55353,36295,
31514,2]],device='cuda:0')
6.将模型回复编码ids 反编码为token结果:['[gMASK]', 'sop', '▁你', '好', ',', '我是', '人工智能', '助手', '。', '很高兴', '为您', '服务', '!', '请问', '有什么', '问题', '我可以', '帮', '您', '解答', '?', '']
7.将模型回复反编码ids 反编码为token并合并结果:['[gMASK] sop 你好,我是人工智能助手。很高兴为您服务!请问有什么问题我可以帮您解答?']
"""

ingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: normal;text-align: left;background-color: rgb(255, 255, 255);border-bottom: 2px solid rgb(239, 112, 96);visibility: visible;margin-top: 8px;margin-bottom: 8px;">【5】训练Tokenizer代码

【5】训练Tokenizer代码

importtorch

fromdatasetsimportload_dataset
fromtokenizersimport(decoders,models,normalizers,pre_tokenizers,processors,trainers,Tokenizer,)
fromtransformersimportPreTrainedTokenizerFast

dataset_path="TurboPascal/tokenizers_example_zh_en"
tokenize_path="BPE_tokenizer.json"
#加载训练数据集
dataset=load_dataset(data_files=dataset_path,cache_dir='./cache/')
defbatch_iterator(batch_size=10000):
foriinrange(0,len(dataset),batch_size):
yielddataset['train'][i:i+batch_size]["text"]

special_tokens=["[CLS]","[SEP]","[PAD]","[MASK]","<s>","</s>","<t>","</t>"]
trainer=trainers.BpeTrainer(special_tokens=special_tokens,vocab_size=54000)

#创建BPEtokenizer对象
tokenizer=Tokenizer(models.BPE())
tokenizer.train_from_iterator(batch_iterator(),trainer=trainer,length=len(dataset['train']))

#保存trainedtokenizer
tokenizer.save(tokenize_path)

#加载trained tokenizer
tokenizer=Tokenizer.from_file(tokenize_path)
output=tokenizer.encode(samplexxx)
print(output.tokens)


分词算法:BPE【Byte Pair Encoding】

ingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: normal;text-align: left;background-color: rgb(255, 255, 255);border-bottom: 2px solid rgb(239, 112, 96);visibility: visible;margin-top: 8px;margin-bottom: 8px;">【1】简要介绍BPE

【1】简要介绍BPE
BPE是字符级别的无监督词汇编码算法。BPE构建词典是从字符级词表开始,不断在语料库中找词频最高且连续的token合并再加入词表,直到达到目标词数。


BPE的优点:1)能够解决OOV问题;2)减少词汇表大小;3)具有一定的泛化能力;4)可以很有效地平衡词典大小和编码步骤数[将语料编码所需要的token数量]。BPE缺点:1)是基于统计的分词算法,对语料依赖性很强,如果语料规模很小,则效果一般不佳;

BPE是主流采用的subword分词器。经典模型(如GPT、GPT-2、RoBERTa、LLaMA、ChatGLM-6B, Baichuan等)使用BPE作为分词器。


ingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: normal;text-align: left;background-color: rgb(255, 255, 255);border-bottom: 2px solid rgb(239, 112, 96);visibility: visible;margin-top: 8px;margin-bottom: 8px;">【2】BPE如何构建词典

【2】BPE如何构建词典

BPE[字符级-字节对编码]构建词典是从字符级词表开始,不断在语料库中找词频最高且连续的token合并再加入词表,直到达到目标词数。
BPE的词典构建过程如下:
1.初始化词典:将每个字符视为一个初始的词。
2.统计词频:对于每个词,统计其在文本中的频率。
3.合并频率最高的词对:在每次迭代中,选择频率最高的词对进行合并。合并的方式是将两个词连接起来。
4.更新词频:更新合并后的词频。对于合并的词,统计其在文本中的频率。
5.重复步骤3和4:重复步骤3和4,直到达到预设的词典大小或者满足其他停止条件。每次迭代都会合并频率最高的词对,并更新词频。
通过BPE算法,可以将文本分解为多个子词,其中一些子词可能是常见的词汇,而其他子词则是根据输入文本的特点生成的。这种方式可以更好地处理未登录词和稀有词,并提高模型对复杂词汇和短语的处理能力。
【注:合并频率最高的词对是指所有相邻的tokens组成的pair里频次最高的!】

ingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: normal;text-align: left;background-color: rgb(255, 255, 255);border-bottom: 2px solid rgb(239, 112, 96);visibility: visible;margin-top: 8px;margin-bottom: 8px;">【2】BPE构建词典模拟代码

【2】BPE构建词典模拟代码

importre,collections

defget_vocab(filename):
vocab=collections.defaultdict(int)
withopen(filename,'r',encoding='utf-8')asfhand:
forlineinfhand:
words=line.strip().split()
forwordinwords:
vocab[''.join(list(word))+'</w>']+=1
returnvocab

defget_stats(vocab):
pairs=collections.defaultdict(int)
forword,freqinvocab.items():
symbols=word.split()
foriinrange(len(symbols)-1):
pairs[symbols[i],symbols[i+1]]+=freq
returnpairs

defmerge_vocab(pair,v_in):
v_out={}
bigram=re.escape(''.join(pair))
p=re.compile(r'(?<!\S)'+bigram+r'(?!\S)')
forwordinv_in:
w_out=p.sub(''.join(pair),word)
v_out[w_out]=v_in[word]
returnv_out

defget_tokens(vocab):
tokens=collections.defaultdict(int)
forword,freqinvocab.items():
word_tokens=word.split()
fortokeninword_tokens:
tokens[token]+=freq
returntokens

vocab={'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>':3}

#GetfreebookfromGutenberg
#wgethttp://www.gutenberg.org/cache/epub/16457/pg16457.txt
#vocab=get_vocab('pg16457.txt')

print('==========')
print('TokensBeforeBPE')
tokens=get_tokens(vocab)
print('Tokens:{}'.format(tokens))
print('Numberoftokens:{}'.format(len(tokens)))
print('==========')

num_merges=5
foriinrange(num_merges):
pairs=get_stats(vocab)
ifnotpairs:
break
best=max(pairs,key=pairs.get)
vocab=merge_vocab(best,vocab)
print('Iter:{}'.format(i))
print('Bestpair:{}'.format(best))
tokens=get_tokens(vocab)
print('Tokens:{}'.format(tokens))
print('Numberoftokens:{}'.format(len(tokens)))
print('==========')


#BPE算法详解:https://www.jianshu.com/p/6415a2e9ec09

ingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: normal;text-align: left;background-color: rgb(255, 255, 255);border-bottom: 2px solid rgb(239, 112, 96);visibility: visible;margin-top: 8px;margin-bottom: 8px;">【3】BPE实现编码和解码

【3】BPE实现编码和解码
编码:1).在BPE构建词典中得到subword的词表,对该subword词表按照字符个数由多到少排序。2).编码时,对于每个单词,遍历排好序的subword子词词表寻找是否有token是当前单词的子字符串,如果有,则该 token 是表示单词的 tokens 之一。从最长的token迭代到最短的token,尝试将每个单词中的子字符串替换为token。3).最终,在迭代所有的tokens后,将所有子字符串替换为tokens。如果仍然有子字符串没被替换但所有token都已迭代完毕,则将剩余的子词替换为特殊token,如<unk>。
#0)给定单词序列
["the</w>","highest</w>","mountain</w>"]
# 1)在BPE构建词典中得到subword 的词表,对该subword词表按照字符个数由多到少排序。
#长度6544442
["errrr</w>","tain</w>","moun","est</w>","high","the</w>","a</w>"]

# 2)编码时,尝试将每个单词中的子字符串替换为token。

# 3)在迭代所有的tokens后,将所有子字符串替换为tokens。返回迭代结果
"the</w>"->["the</w>"]
"highest</w>"->["high","est</w>"]
"mountain</w>"->["moun","tain</w>"]
解码:如果相邻token间没有</w>中止符,则将两token直接拼接,否则两token之间添加分隔符。如果仍然有子字符串没被替换但所有 token 都已迭代完毕,则将剩余的子词替换为特殊 token,如<unk>。
#编码序列
["the</w>","high","est</w>","moun","tain</w>"]
#解码序列
"the</w>highest</w>mountain</w>"

【★】BPE算法详解-简书@ Jarkata【202202】:https://www.jianshu.com/p/6415a2e9ec09


ingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: normal;text-align: left;background-color: rgb(255, 255, 255);border-bottom: 2px solid rgb(239, 112, 96);visibility: visible;margin-top: 8px;margin-bottom: 8px;">【4】BPE代码实现-创建BPE Tokenizer

【4】BPE代码实现-创建BPE Tokenizer

fromtokenizersimport(decoders,models,normalizers,pre_tokenizers,processors,trainers,Tokenizer,)
fromtransformersimportPreTrainedTokenizerFast
fromdatasetsimportDataset


#CreatingByte-PairEncodingtokenizer
raw_tokenizer=Tokenizer(models.BPE(unk_token="[UNK]"))#设置Tokenizer
raw_tokenizer.normalizer=normalizers.Sequence([normalizers.NFC()]+[normalizers.Lowercase()]ifLOWERCASEelse[])#normalizers
raw_tokenizer.pre_tokenizer=pre_tokenizers.ByteLevel()#pre_tokenizers

special_tokens=["[UNK]","[PAD]","[CLS]","[SEP]","[MASK]"]
trainer=trainers.BpeTrainer(vocab_size=VOCAB_SIZE,special_tokens=special_tokens)#trainers

dataset=Dataset.from_pandas(test[['text']])#Dataset
deftrain_corp_iter():
foriinrange(0,len(dataset),25):
yielddataset[i:i+25]["text"]

raw_tokenizer.train_from_iterator(train_corp_iter(),trainer=trainer)

tokenizer=PreTrainedTokenizerFast(#PreTrainedTokenizerFast
tokenizer_object=raw_tokenizer,
unk_token="[UNK]",pad_token="[PAD]",cls_token="[CLS]",sep_token="[SEP]",mask_token="[MASK]",
)

tokenized_texts_test=[]
fortextintqdm(test['text'].tolist()):
tokenized_texts_test.append(tokenizer.tokenize(text))
tokenized_texts_train=[]
fortextintqdm(train['text'].tolist()):
tokenized_texts_train.append(tokenizer.tokenize(text))


【5】BPE代码实现-BPE 构建词典过程

【5】BPE代码实现-BPE构建词典过程

importre,collections

defget_stats(vocab):
pairs=collections.defaultdict(int)
forword,freqinvocab.items():
symbols=word.split()
foriinrange(len(symbols)-1):
pairs[symbols[i],symbols[i+1]]+=freq
returnpairs

defmerge_vocab(pair,v_in):
v_out={}
bigram=re.escape(''.join(pair))
p=re.compile(r'(?<!\S)'+bigram+r'(?!\S)')
forwordinv_in:
w_out=p.sub(''.join(pair),word)
v_out[w_out]=v_in[word]
returnv_out

vocab={'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>':3}
num_merges=1000
foriinrange(num_merges):
pairs=get_stats(vocab)
ifnotpairs:
print(f"第{i}轮合并结束")
break

best=max(pairs,key=pairs.get)
vocab=merge_vocab(best,vocab)
print(f"第{i}轮合并,best-pair值为{best},|||合并后vocab为{vocab}")

"""
第0轮合并,best-pair值为('e','s'),|||合并后vocab为{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>':3}
第1轮合并,best-pair值为('es','t'),|||合并后vocab为{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>':3}
第2轮合并,best-pair值为('est','</w>'),|||合并后vocab为{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>':3}
第3轮合并,best-pair值为('l','o'),|||合并后vocab为{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>':3}
第4轮合并,best-pair值为('lo','w'),|||合并后vocab为{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>':3}
第5轮合并,best-pair值为('n','e'),|||合并后vocab为{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>':3}
第6轮合并,best-pair值为('ne','w'),|||合并后vocab为{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>':3}
第7轮合并,best-pair值为('new','est</w>'),|||合并后vocab为{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>':3}
第8轮合并,best-pair值为('low','</w>'),|||合并后vocab为{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>':3}
第9轮合并,best-pair值为('w','i'),|||合并后vocab为{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>':3}
第10轮合并,best-pair值为('wi','d'),|||合并后vocab为{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>':3}
第11轮合并,best-pair值为('wid','est</w>'),|||合并后vocab为{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>':3}
第12轮合并,best-pair值为('low','e'),|||合并后vocab为{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>':3}
第13轮合并,best-pair值为('lowe','r'),|||合并后vocab为{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>':3}
第14轮合并,best-pair值为('lower','</w>'),|||合并后vocab为{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>':3}
第15轮合并结束
"""


【5】通过例子说明BPE构建词典

【5】通过例子说明BPE构建词典

假设有语料集经过统计后表示为{'low':5,'lower':2,'newest':6,'widest':3},其中数字代表的是对应单词在语料中的频数。

输入【语料】:{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>': 3}。此时词表:{'l','o','w','e','r','n','s','t','i','d','</w>',}

Iter1,最高频连续字节对"e"和"s"出现了6+3=9次,合并成"es"。输出【新语料】:{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>': 3}。【新词表】:{'l','o','w','e','r','n','s','t','i','d','</w>','es',}。

Iter2,最高频连续字节对"es"和"t"出现了6+3=9次,合并成"est"。输出【新语料】:{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>': 3}。【新词表】:{'l','o','w','e','r','n','t','i','d','</w>','es','est',}。

Iter3,以此类推,最高频连续字节对为"est"和"</w>"。输出【新语料】:{'low</w>':5,'lower</w>':2,'newest</w>':6,'widest</w>': 3}。【新词表】:{'l','o','w','e','r','n','i','d','</w>','est','est</w>',}。

……

Iter n, 继续迭代直到达到预设的subword词表大小或下一个最高频的字节对出现频率为1。

注:停止符"</w>"的意义在于表示subword是词后缀。举例来说:"st"字词不加"</w>"可以出现在词首如"star",加了"</w>"表明该字词位于词尾,如"widest</w>",二者意义截然不同。

推荐阅读:
【★】理解NLP最重要的编码方式(BPE)-知乎@Jinjie Ni【202304】:https://zhuanlan.zhihu.com/p/424631681
【★】BPE算法详解-简书@ Jarkata【202202】:https://www.jianshu.com/p/6415a2e9ec09
【★】NLP三大Subword模型详解:BPE、WordPiece、ULM#BPE构建词典过程模拟-知乎@阿北【202008】:https://zhuanlan.zhihu.com/p/191648421


【6】BBPE和BPE的区别

【6】BBPE和BPE的区别

BPE的问题是,如果遇到了unicode,基本字符集可能会很大。一种处理方法BBPE是以一个字节为一种“字符”,不管实际字符集用了几个字节来表示一个字符。这样的话,基础字符集的大小就锁定在了256。例如,像GPT-2的词汇表大小为50257 = 256 +<EOS>+ 50000 mergers,<EOS>是句子结尾的特殊标记。

BBPE是BPE的扩展版本,BBPE核心思想将BPE的从字符级别扩展到字节级别。即字节级 BPE 将所有 Unicode 代码点转换为多个字节级字符)。LLaMA,ChatGLM...都基于BBPE实现。


使用BBPE有什么好处?使用Byte-level BPE主要优点是:较小的词汇表。没有未知token,不会出现OOV问题。



特点CharacterSet|字符集
传统BPE-字符级别BPE。传统的BPE基于char粒度去执行合并的过程生成词表。

(ASCII编码--)

传统BPE使用1个字节对字符进行编码

以字符粒度的编码

BBPE-字节级别的BPE。

BBPE是基于字节粒度 去执行合并过程生成词表。

BBPE能比较好支持语料是多种语言的分词。

(Unicode编码-改进utf-8编码)

BBPE使用1~4个字节对字符进行编码

以字节粒度的编码

对于英文、拉美体系的语言来说使用BPE分词足以在可接受的词表大小下解决OOV的问题。但面对中文、日文等语言时,其稀有的字符可能会不必要地占用词汇表,因此考虑使用字节级别byte-level解决不同语言进行分词时OOV的问题。
BBPE在不同词表大小下的对日文、英语的编码结果
推荐阅读:
【★】1.深入理解NLP Subword算法:BPE、WordPiece、ULM-知乎@Luke【201910】:https://zhuanlan.zhihu.com/p/86965595
【★】2.BPE 算法原理及使用指南【深入浅出】-知乎@Suprit【202112】:https://zhuanlan.zhihu.com/p/448147465
【★】3.BPE、WordPiece和SentencePiece-简书@ Jarkata【202204】:https://www.jianshu.com/p/d4de091d1367
【★】3x【HugBert11】聚沙成塔:关于tokenization(词元化)的解疑释惑【202105】:https://zhuanlan.zhihu.com/p/371300063
【★】4.大模型系列:BPE理论简述和实践【202405】:https://blog.csdn.net/2401_85325397/article/details/139472993
【★】5.理解NLP最重要的编码方式(BPE)-知乎@Jinjie Ni【202304】:https://zhuanlan.zhihu.com/p/424631681
【★】5xBPE算法详解-简书@ Jarkata【202202】:
https://www.jianshu.com/p/6415a2e9ec09
【★】6.NLP三大Subword模型详解:BPE、WordPiece、ULM-知乎@阿北【202008】:https://zhuanlan.zhihu.com/p/191648421


分词算法:WordPiece


【1】简要介绍WordPiece

【1】简要介绍WordPiece

WordPiece算法可以看作是BPE的变种。每次从词表中选出两个子词合并成新的子词。不同点在于,WordPiece基于 下个最高互信息值的pair生成新的subword而不是下个最高频pair。

v2:WordPiece分词与BPE非常类似,只是在训练阶段合并pair的策略不是pair的频率而是互信息。
Score=log(P(AB))−[log(P(A))+log(P(B))]=log( P(AB)/P(A)*P(B) )。

WordPiece每次合并的两个字符串A和B,应该具有最大的P(AB)/P(A)*P(B)值。合并AB之后,所有原来切成A+B两个tokens的就只保留AB一个token,整个训练集上最大似然变化量与P(AB)/P(A)*P(B)成正比。

【2】WordPiece是如何选取子词的

【2】WordPiece是如何选取子词的

WordPiece每次选择合并的两个子词具有最大的互信息值,即两个子词在语言模型上具有较强的关联性,它们经常在语料中以相邻方式同时出现。

【3】WordPiece 与 BPE 的异同点是什么

【3】WordPiece 与 BPE 的异同点是什么


BPE(Byte-PairEncoding)

WordPiece

相似点:

分词目标:WordPiece和BPE都旨在将文本分解为子词或字符级别的单位,以便更好地处理未登录词和稀有词,提高模型对复杂词汇和短语的处理能力。

无监督学习:WordPiece和BPE都是无监督学习方法,不需要依赖外部的标注数据,而是通过分析输入文本自动构建词典。

不同点:

拆分策略

BPE则采用自底向上的拆分策略,通过合并频率最高的词对来构建词典。它使用词频来选择合并的词对,并通过更新词频来更新词典。

WordPiece采用贪婪的自顶向下的拆分策略,将词汇表中的词分解为更小的子词。它使用最大似然估计来确定最佳的分割点,并通过词频来更新词典。

不同点:

分割粒度

BPE则将词分解为更小的子词或字符级别的单位。它不使用特殊的前缀或后缀来表示子词。

WordPiece通常将词分解为更小的子词,例如将"running"分解为"run"和"##ning"。这些子词通常以"##"前缀表示它们是一个词的一部分。

不同点:

处理未登录词

BPE则将未登录词作为单独的词处理,不进行进一步的拆分。

WordPiece通常将未登录词分解为更小的子词,以便模型可以更好地处理它们。

典型模型

典型使用BPE模型有:GPT-2 与RoBERTa

典型使用WordPiece模型有:Bert/Electra/DistilBERT。


【4】WordPiece代码实现-CreatingWordPiece Tokenizer

【4】WordPiece代码实现-CreatingWordPiece Tokenizer

fromtokenizersimport(decoders,models,normalizers,pre_tokenizers,processors,trainers,Tokenizer,)
fromtransformersimportPreTrainedTokenizerFast
fromdatasetsimportDataset


#CreatingWordPiecetokenizer
raw_tokenizer=Tokenizer(models.WordPiece())#models
raw_tokenizer.normalizer=normalizers.Sequence([normalizers.NFC()]+[normalizers.Lowercase()]ifLOWERCASEelse[])#normalizers
raw_tokenizer.pre_tokenizer=pre_tokenizers.ByteLevel()#pre_tokenizers
special_tokens=["[UNK]","[PAD]","[CLS]","[SEP]","[MASK]"]

trainer=trainers.BpeTrainer(vocab_size=VOCAB_SIZE,special_tokens=special_tokens)#trainers
...

推荐阅读:

【★】1.LLM 分词算法(BPE, WordPiece, Unigram)简介【202311】:https://zhuanlan.zhihu.com/p/664717335

【★】2.BPE、WordPiece和SentencePiece-简书@ Jarkata【202204】:https://www.jianshu.com/p/d4de091d1367 -

【★】3.NLP三大Subword模型详解:BPE、WordPiece、ULM【202008】:https://zhuanlan.zhihu.com/p/191648421



分词算法:Unigram【Unigram Language Model】


【1】简要介绍ULM

【1】简要介绍ULM

与BPE和WordPiece不同,Unigram算法是从预分词器分的词+所有高频的子词构成的大词汇表出发,再逐步删除其中重要性较低的subword,直到满足预定义size。

Unigram语言模型通过计算删除不同subword造成的损失loss 来衡量subword的重要性,删除loss较小或者说重要性较低的subword。每次从词汇表中删除词汇的原则是使预定义的损失loss最小。


训练时,计算loss的公式为:


Unigram算法每次会从词汇表中挑出使得loss增长最小的10%~20%的词汇来删除。


理论基础:假设训练文档中的所有词分别为(x1,x2,x3...,xn),由n个子词组成,而每个词tokenize的方法是对应集合S(xi)。

当词汇表确定时,每个词tokenize的方法集合S(xi)就是确定的,而每种方法对应着一个概率p(x)。
如果从词汇表中删除部分词,则某些词的tokenize的种类集合就会变少,log(*)中的求和项就会减少,从而增加整体loss【删除重要的subword对loss增长影响大,不删除】。Unigram算法每次会从词汇表中挑出使得loss增长最小的10%~20%的词汇来删除。

ULM使用大量符号初始化基础词汇,并逐渐修剪每个符号以获得较小的词汇。在每个训练步骤中,计算当前词汇和unigram语言模型给定训练数据的损失。移除影响整体损失最小的符号,重复此过程直到词汇达到所需大小。Unigram保留基本字符,以便能够对任何单词进行分词。

ULM用EM算法求解每个子词subword在语料上的概率?(??)。

假设当前词表V, 语料库中语料数量是|D|,则M步最大化的对象是如下似然函数:

ULM会保留那些以较高频率出现在很多句子的分词结果中的子词,因为这些子词如果被丢弃,其损失会很大。

【★】Subword Tokenization算法-@DonngZH【202303】:https://blog.csdn.net/weixin_44750512/article/details/129435981

【★】NLP三大Subword模型详解:BPE、WordPiece、ULM-知乎@阿北【202008】:https://zhuanlan.zhihu.com/p/191648421


【2】ULM如何来构造词表以及求解分词概率

【2】ULM算法如何来构造词表以及求解分词概率

输入:训练语料;词表大小V;保留阈值X;输出:ULM算法得到的subword词表。

ULM算法采用不断迭代的方法来构造词表以及求解分词概率:

1.准备基础词表:初始时建立一个足够大的词表。可用语料中的所有字符+常见的子字符串初始化词表,也可以通过BPE算法初始化。

2.针对当前词表,用EM算法求解每个子词subword在语料上的概率。

3.对于每个子词,计算当该子词被从词表中移除时,总的loss降低了多少,记为该子词的loss。

4.将子词按照loss大小进行排序,丢弃一定比例loss最小的子词(比如20%),保留前X%的子词生成新的词表。注意单字符不能被丢弃,为避免OOV情况。

5.重复步骤2到4,直到词表大小减少到设定范围V或第4步的结果不再变化。


可以看出,ULM会保留那些以较高频率出现在很多句子的分词结果中的子词,因为这些子词如果被丢弃,其损失会很大。


【★】NLP三大Subword模型详解:BPE、WordPiece、ULM#Unigram Language Model (ULM)-知乎@阿北【202008】:https://zhuanlan.zhihu.com/p/191648421


推荐阅读:
【★】1.Unigram-Subword Regularization: Improving Neural Network Translation Models with Multiple Subword Candidates:https://arxiv.org/abs/1804.10959
【★】2.NLP三大Subword模型详解:BPE、WordPiece、ULM#Unigram Language Model (ULM)-知乎@阿北【202008】:https://zhuanlan.zhihu.com/p/191648421
【★】3.Subword Tokenization算法-@DonngZH【202303】:https://blog.csdn.net/weixin_44750512/article/details/129435981



分词工具:SentencePiece

【1】简要介绍SentencePiece

【1】简要介绍SentencePiece

SentencePiece是分词工具,内置BPE,Unigram等多种分词方法,基于Unicode编码并且将空格视为特殊的token。当前主流的大模型都是基于SentencePiece实现,例如ChatGLM的tokenizer。

SentencePiece 在大模型领域具有以下优势:分词效果好速度快,能够准确地识别词语和符号的边界。编码效率高,能够节省空间。能够将词语或符号之间的关系编码到编码中,有利于模型学习。
SentencePiece用于处理不使用空格分隔单词的语言,如中文、日文和泰文。
...
classTextTokenizer:
def__init__(self,model_path):
self.sp=spm.SentencePieceProcessor()
self.sp.Load(model_path)
self.num_tokens=self.sp.vocab_size()

defencode(self,text):
returnself.sp.EncodeAsIds(text)

defdecode(self,idsist[int]):
returnself.sp.DecodeIds(ids)
...

#https://huggingface.co/THUDM/chatglm-6b/blob/main/tokenization_chatglm.py#L21

【1】SentencePiece特性

【1】SentencePiece特性

1.唯一Token数量是预先确定的;与大多数假设无限词汇量的无监督分词算法不同,SentencePiece 在训练分词模型时,使最终的词汇表大小固定,例如:8k、16k 或 32k。
2.可以从原始句子进行训练;以前的子词(sub-word)实现假设输入句子是预标记(pre-tokenized)的。这种约束是有效训练所必需的,但由于我们必须提前运行依赖于语言的分词器,因此使预处理变得复杂。SentencePiece 的实现速度足够快,可以从原始句子训练模型。这对于训练中文和日文的tokenizer和detokenizer很有用,因为在这些词之间不存在明确的空格。
3.空格被视为基本符号;SentencePiece 将输入文本视为一系列 Unicode 字符。空格也作为普通符号处理。为了明确地将空格作为基本标记处理,SentencePiece 首先使用元符号"▁"(U+2581)转义空格。


【1】SentencePiece技术优势

【1】SentencePiece技术优势

1.纯数据驱动:SentencePiece 从句子中训练 tokenization 和 detokenization 模型。并不总是需要Pre-tokenization(Moses tokenizer/MeCab/KyTea)。
2.独立于语言:SentencePiece 将句子视为 Unicode 字符序列。没有依赖于语言的逻辑。
3.多子词算法:支持 BPE 和 unigram 语言模型。
4.子词正则化:SentencePiece 实现子词正则化和 BPE-dropout 的子词采样,有助于提高 NMT 模型的鲁棒性和准确性。
5.快速且轻量级:分割速度约为 50k 句子/秒,内存占用约为 6MB。
6.Self-contained:只要使用相同的模型文件,就可以获得相同的tokenization/detokenization。
7.直接从词汇 ID 生成:SentencePiece 管理词汇到 ID 的映射,可以直接从原始句子生成词汇 ID 序列。
8.基于 NFKC 的 normalization:SentencePiece 执行基于 NFKC 的文本 normalization。

【★】转载于:大模型词表扩充必备工具SentencePiece#训练模型-知乎@吃果冻不吐果冻皮【202305】:https://zhuanlan.zhihu.com/p/630696264


【2】使用SentencePiece训练分词器

【2】使用Sentencepiece训练分词器

##v1TrainaBPEModel
importsentencepieceasspm

#trainsentencepiecemodelfromourblogcorpus
spm.SentencePieceTrainer.train(input='blog_test.txt',model_prefix=bpe--vocab_size=500,user_defined_symbols=['foo','bar'])

#makessegmenterinstanceandloadstheBPEmodelfile(bpe.model)
sp_bpe=spm.SentencePieceProcessor()
sp_bpe.load('bpe.model')
####################################################################################
##v2TrainaUnigramModel
importsentencepieceasspm

#trainsentencepiecemodelfromourblogcorpus
spm.SentencePieceTrainer.train(input='blog_test.txt',model_prefix=bpe--vocab_size=500,user_defined_symbols=['foo','bar'])

#makessegmenterinstanceandloadstheBPEmodelfile(bpe.model)
sp_uni=spm.SentencePieceProcessor()
sp_uni.load('uni.model')
####################################################################################
##比较一下两个分词器结果
print("BPE:{}".format(sp_bpe.encode_as_pieces('Thisisatest')))
print("UNI:{}".format(sp_uni.encode_as_pieces('Thisisatest')))
BPE:['▁This','▁is','▁a','▁t','est']
UNI:['▁Thi','s','▁is','▁a','▁t','est']

#https://zhuanlan.zhihu.com/p/620508648

【3】使用SentencePiece加载和使用词表模型文件tokenizer.model

【3】使用SentencePiece加载和使用词表模型文件tokenizer.model

ChatGLM3-6B中tokenizer.model模型是使用SentencePiece训练得到的词表模型文件。

tokenization_chatglm.py:分词器的 .py 文件,用于模型分词器,加载和使用模型的必要部分(只是处理文本,不涉及任何向量操作)。
tokenizer.model:包含了训练好的分词模型,用于将输入文本转换为标记序列;二进制使用 pickle 或者其他序列化工具进行存储和读取。
tokenizer_config.json:分词模型的配置信息,用于指定分词模型的超参和其他的相关信息,例如分词器的类型、词汇表大小、最大序列长度、特殊标记等。
fromsentencepieceimportSentencePieceProcessor
model_path="/kaggle/input/chatglm3-6b/pytorch/6b/6/tokenizer.model"
sp_model=SentencePieceProcessor(model_file=model_path)
text="你好,你是谁?"

tokens=sp_model.EncodeAsPieces(text)
print(f"1.句子转为tokens结果:{tokens}")

ids=sp_model.EncodeAsIds(text)
print(f"2.句子转为ids结果:{ids}")
decode_text=sp_model.Decode(ids)
print(f"3.ids转为句子结果:{decode_text}")

#1.句子转为tokens结果:['▁你', '好', ',', '你是', '谁', '?']
#2.句子转为ids结果:[36474, 54591, 31123, 34607, 55622, 31514]
#3.ids转为句子结果:你好,你是谁?

【4】使用SentencePiece对LLM词表扩充

【4】使用SentencePiece对LLM词表扩充

使用sentencepiece训练模型:

#0.从C++源构建和安装SentencePiece命令行工具
>sudoapt-getinstallcmakebuild-essentialpkg-configlibgoogle-perftools-dev
>gitclonehttps://github.com/google/sentencepiece.git
>cdsentencepiece|mkdirbuild|cdbuild|cmake..|make-j$(nproc)|makeinstall|ldconfig-v

#1.spm_train进行模型训练spm_train--input=训练语料文件--model_prefix=输出模型名称前缀--vocab_size=训练后的词表大小--character_coverage=模型覆盖的字符数量--model_type=模型类型如bpe
>spm_train--input=/workspace/data/book/hongluomeng_clean.txt--model_prefix=/workspace/model/book/hongluomeng-tokenizer--vocab_size=4000--character_coverage=0.9995--model_type=bpe

#2.模型输出文件(词表及模型权重)
>ls-al/workspace/model/book

#3.查看词表:
>head-n20/workspace/model/book/hongluomeng-tokenizer.vocab

#4.基于命令行使用模型,将原始文本编码成句子片段(token)。
>echo"白日依山尽,黄河入海流。"|spm_encode--model=/workspace/model/book/hongluomeng-tokenizer.model#▁白日依山尽 , 黄河入海流。
>echo"白日依山尽,黄河入海流。"|spm_encode--model=/workspace/model/book/hongluomeng-tokenizer.model--output_format=id#602547033346840014733147631760351015

#将句子片段(token) id 解码为原始文本。
>echo"602547033346840014733147631760351015"|spm_decode--model=/workspace/model/book/hongluomeng-tokenizer.model--input_format=id#白日依山尽,黄河入海流。

#5.spm_export_vocab基于模型文件导出词汇表。# spm_export_vocab --model=<模型文件>--output=<输出文件>
>spm_export_vocab--model=/workspace/model/book/hongluomeng-tokenizer.model--output=/workspace/output/hongluomeng.vocab
使用sentencepiece使用模型:
importsentencepieceasspm

sp=spm.SentencePieceProcessor()

text="这贾雨村原系胡州人氏,也是诗书仕宦之族,因他生于末世,父母祖宗根基已尽,人口衰丧,只剩得他一身一口,在家乡无益,因进京求取功名,再整基业。"

sp.Load("hongluomeng-tokenizer.model")

print(sp.EncodeAsPieces(text))
['▁','这','贾','雨','村','原','系','胡','州','人','氏',',','也','是','诗','书','仕','宦','之','族',',','因','他','生','于','末','世',',','父','母','祖','宗','根','基','已','尽',',','人','口','衰','丧',',','只','剩','得','他','一','身','一','口',',','在','家','乡','无','益',',','因','进','京','求','取','功','名',',','再','整','基','业','。']
【★】转载于:大模型词表扩充必备工具SentencePiece#训练模型-掘金@吃果冻不吐果冻皮【202305】:https://zhuanlan.zhihu.com/p/630696264

【5】SentencePiece-byte回退

【5】SentencePiece-byte回退

当SentencePiece在训练BPE的时开启--byte_fallback, 在效果上类似BBPE,遇到UNK会继续按照byte进行进一步的切分。
参见:https://github.com/google/sentencepiece/issues/621
具体实现上是将<0x00> ... <0xFF>这256个token添加到词表中。
分析ChatGLM的模型,可以发现ChatGLM就是开启了--byte_fallback。
fromsentencepieceimportsentencepiece_model_pb2

m=sentencepiece_model_pb2.ModelProto()
withopen('chatglm-6b/ice_text.model','rb')asf:
m.ParseFromString(f.read())
print('ChatGLMtokenizer\n\n'+str(m.trainer_spec))

"""
ChatGLMtokenizer
input:"/root/train_cn_en.json"
model_prefix:"new_ice_unigram"
vocab_size:130000
character_coverage:0.9998999834060669
split_digits:true
user_defined_symbols:"<n>"
byte_fallback:true
pad_id:3
train_extremely_large_corpus:true
"""

可以看到 byte_fallback: true。同样的方法,可以验证LLaMA, ChatGLM-6B, Baichuan这些大模型都是基于sentencepiece实现的BPE的分词算法,并且采用byte回退。

【★】大模型基础组件Tokenizer-SentencePiece @nghuyong【202308】:https://zhuanlan.zhihu.com/p/651430181


【6】和SentencePiece类似的工具

【6】和SentencePiece类似的工具

SentencePiece:英文分词工具,使用无监督学习的分词方法。基于BPE编码的编码方法。分词效果好,编码效率高,但需要训练模型。

Jieba:中文分词工具,使用最大似然法的方法进行分词。基于哈希编码的编码方法。Jieba的分词效果较好,并且速度较快,但需要人工制定分词规则。

Hmmseg:中文分词工具,使用隐马尔可夫模型的方法进行分词。基于哈希编码的编码方法。Hmmseg的分词效果较好,并且可以支持多种语言,但需要训练模型。

StanfordCoreNLP:自然语言处理工具包,包含分词、词性标注、句法分析等功能。使用基于规则的方法分词。基于哈希编码的编码方法。Stanford CoreNLP 的分词效果较好,并且可以支持多种语言。

Tiktoken:基于BPE算法的快速分词器,专门针对GPT-4和ChatGPT等大模型进行了优化。Tiktoken 的主要特点是分词速度比 SentencePiece 快很多,分词效果与SentencePiece相当,提供简单的 API接口,方便使用。


【7】分词器使用建议

分词器使用建议:

1.如果需要对中文文本进行分词,并且对分词效果要求较高,可以选择 SentencePiece、Jieba 或 Hmmseg。如果需要对多种语言文本进行分词,可以选择 Stanford CoreNLP。
2.如果需要对文本进行分词和编码,并且对速度要求较高,可以选择 Jieba。对分词效果要求较高,可以选择 SentencePiece 或 Hmmseg。

【★】大模型分词:sentencepiece vs titoken-知乎@王几行【202404】:https://zhuanlan.zhihu.com/p/691609961

推荐阅读:

【★】1.SentencePiece:https://arxiv.org/pdf/1808.06226.pdf

【★】2.Github:https://github.com/google/sentencepiece

【★】3.大模型分词:sentencepiece vs titoken-知乎@王几行【202404】:https://zhuanlan.zhihu.com/p/691609961

【★】4.大模型词表扩充必备工具SentencePiece-掘金@吃果冻【202305】:https://zhuanlan.zhihu.com/p/630696264



Toknenizer相关面试题总结

【X】简要介绍常用Tokenizer的原理和区别

【X】简要介绍常用Tokenizer的原理和区别

1.BPE构建词典从字符级词表开始,不断在语料库中找词频最高且连续的token合并加入词表,直到达到目标词数。生成大小可控的开放词汇表。

2.WordPiece分词与BPE非常类似,只是在训练阶段合并pair的策略不是pair的频率而是互信息。?????=???(?(??))−(???(?(?))+???(?(?)))=???(?(??)/?(?)?(?))。

3.与BPE和WordPiece不同,Unigram算法是从预分词器分的词+所有高频的子词构成的大词汇表出发,再逐步删除其中重要性较低的subword,直到满足预定义size。


【X】BBPE和BPE的区别

【X】BBPE和BPE的区别
BPE的问题是,如果遇到了unicode,基本字符集可能会很大。一种处理方法BBPE是以一个字节为一种“字符”,不管实际字符集用了几个字节来表示一个字符。这样的话,基础字符集的大小就锁定在了256。例如,像GPT-2的词汇表大小为50257 = 256 +<EOS>+ 50000 mergers,<EOS>是句子结尾的特殊标记。
BBPE是BPE的扩展版本,BBPE核心思想将BPE的从字符级别扩展到字节级别。即字节级 BPE 将所有 Unicode 代码点转换为多个字节级字符)。LLaMA,ChatGLM...都基于BBPE实现。
使用BBPE有什么好处?使用BBPE主要优点是:较小的词汇表。没有未知token,不会出现OOV问题。


·EN
■■■■






欢迎光临 链载Ai (https://www.lianzai.com/) Powered by Discuz! X3.5