BytePiece:更纯粹、更高压缩率的Tokenizer
By 苏剑林 | 2023-09-07 | 50430位读者 |目前在LLM中最流行的Tokenizer(分词器)应该是Google的SentencePiece了,因为它符合Tokenizer的一些理想特性,比如语言无关、数据驱动等,并且由于它是C++写的,所以Tokenize(分词)的速度很快,非常适合追求效率的场景。然而,它也有一些明显的缺点,比如训练速度慢(BPE算法)、占用内存大等,同时也正因为它是C++写的,对于多数用户来说它就是黑箱,也不方便研究和二次开发。
事实上,Tokenizer的训练就相当于以往的“新词发现”,而笔者之前也写过中文分词和最小熵系列文章,对新词发现也有一定的积累,所以很早之前就有自己写一版Tokenizer的想法。这几天总算腾出了时间初步完成了这件事情,东施效颦SentencePiece,命名为“BytePiece”。
理想特性 #
既然要重写Tokenizer,那么我们就要思考一个理想的Tokenizer应该是怎样的,这样才能判断最终是否达到了预期。照笔者看来,Tokenizer至少应该具备如下基本特性:
1、无损重构:分词结果应该可以无损还原为输入;
2、高压缩率:词表大小相同时,同一批数据的tokens数应该尽可能少;
3、语言无关:基于统计,训练和分词过程都不应引入语言特性;
4、数据驱动:可以直接基于原始语料进行无监督训练;
5、训练友好:能够在合理的时间和配置上完成训练过程。
最后,还有一些加分项,比如分词速度快、代码易读、方便二次拓展等,这些满足自然最好,但笔者认为可以不列入基本特性里边。
对于笔者来说,SentencePiece最大的槽点就是“无损重构”和“训练友好”。首先,SentencePiece默认会进行NFKC normalization,这会导致“全角逗号转半角逗号”等不可逆变化,所以默认情况下它连“无损重构”都不满足,所以很长时间里它都不在笔者的候选名单中,直到后来发现,在训练时添加参数--normalization_rule_name=identity
就可以让它不做任何转换。所以SentencePiece算是支持无损重构,只不过要特别设置。
至于训练方面,就更让人抓狂了。SentencePiece支持BPE和Unigram两种主流算法,Unigram训练速度尚可,但压缩率会稍低一些,BPE的压缩率更高,但是训练速度要比Unigram慢上一个数量级!而且不管是BPE还是Unigram,训练过程都极费内存。总而言之,用较大的语料去训练一个SentencePiece模型真不是一种好的体验。
模型构思 #
一个新Tokenizer的构建,可以分解为三个部分:1、基本单元;2、分词算法;3、训练算法。确定这三个部分后,剩下的就只是编程技巧问题了。下面逐一介绍BytePiece对这些问题的思考。
基本单元 #
我们知道,Python3的默认字符串类型是Unicode,如果以Unicode为基本单位,我们称之为Char-based。Char-based很直观方便,汉字表现为长度为1的单个字符,但不同语言的Char实在太多,即便只是覆盖单字都需要消耗非常大的vocab_size,更不用说引入Word。所以BytePiece跟主流的Tokenizer一样,以Byte为基本单位。
回到Byte之后,很多问题都“豁然开朗”了。因为不同的单Byte只有256个,所以只要词表里包含了这256个单Byte,那么就可以杜绝OOV(Out of Vocabulary),这是它显而易见的好处。此外,我们知道汉字的平均信息熵要比英文字母的平均信息熵要大,如果我们选择Char-based,那么虽然每个Char表面看起来长度都是1,但“内在”的颗粒度不一样,这会导致统计结果有所偏置。相比之下,每个Byte的信息熵则更加均匀【比如,大部分汉字的UTF-8编码对应3个Byte,而汉字的平均信息熵正好是英文字母(对应一个Byte)的2~3倍左右】,因此用Byte的统计结果会更加无偏,这将会使得模型更加“语言无关”。
在Byte-based方面,BytePiece比SentencePiece更彻底,SentencePiece是先以Char-based进行处理,然后遇到OOV再以Byte-based处理,BytePiece则是在一开始就将文本通过text.encode()
转为Bytes,然后才进行后续操作,相比之下更加纯粹。
分词算法 #
基于词典进行分词的算法无非就那几种,比如最大匹配、最短路径、最大概率路径等,有兴趣追溯的读者,可以参考Matrix67之前写的《漫话中文自动分词和语义识别(上):中文分词算法》,
跟jieba等中文分词工具一样,BytePiece选择的是最大概率路径分词,也称“一元文法模型”,即Unigram。选择Unigram有三方面的考虑:第一,Unigram的最大概率换言之就是最大似然,而LLM的训练目标也是最大似然,两者更加一致;第二,从压缩的角度看,最大概率实际上就是最短编码长度(也叫最小描述长度),是压缩率最大化的体现,这也跟“压缩就是智能”的信仰一致;第三,Unigram求最优分词方案可以通过Viterbi算法在线性复杂度内完成,这是理论最优的复杂度了。
当然,既然有“一元文法模型”,自然也有更复杂的“二元文法模型”、“三元文法模型”等,但它们的复杂度增加远大于它能带来的收益,所以我们通常不考虑这些高阶模型。
训练算法 #
之所以先讨论分词算法在讨论训练算法,是因为只有分词算法确定下来后,才能确定训练的优化目标,从而研究对应的训练算法。
开头就提到,Tokenizer的训练本质上就是以往的“新词发现”,而笔者之前也提了好几种新词发现算法,如《基于切分的新词发现》、《基于语言模型的无监督分词》、《更好的新词发现算法》。现在看来,跟Unigram分词算法最契合、最有潜力的,应该是《基于语言模型的无监督分词》,BytePiece的训练就是基于它实现的,这里称之为Byte-based N-gram Language Model(BNLM)。
具体来说,对于Unigram分词,如果一个长度为$l$的字节串$c_1, c_2, \dots, c_l$,最优分词结果为$w_1, w_2, \dots, w_m$,那么概率乘积$p(w_1)p(w_2)\dots p(w_m)$应该是所有切分中最大的。设$w_1,w_2,\cdots,w_m$的长度分别为$l_1,l_2,\cdots,l_m$,那么根据条件分解公式
\begin{equation}\prod_{i=1}^m p(w_i) = \prod_{i=1}^m \prod_{j=L_{i-1} + 1}^{j=L_{i-1} + l_i} p(c_j|c_{L_{i-1} + 1},\cdots,c_{j-1})\end{equation}
这里$L_i=l_1+l_2+\cdots+l_i$。只考虑$n$-gram模型,将$j\gt L_{i-1} + n$的$p(c_j|c_{L_{i-1} + 1},\cdots,c_{j-1})$统一用$p(c_j|c_{j - n + 1},\cdots,c_{j-1})$近似,那么Unigram分词就转化为一个字(节)标注问题,而Tokenizer的训练则转化为$n$-gram语言模型的训练(推荐$n=6$),可以直接无监督完成。更详细的介绍请读者移步原文《基于语言模型的无监督分词》。
(注意:$n=6$只是说BytePiece的统计信息最多到6-gram,但并非最大只能生成长度为6的piece,因为大于$6$的$n$-gram条件概率我们会用6-gram的近似,所以它是可以做到任意阶的,即理论上可以生成任意长度piece。)
代码实现 #
原理确定之后,剩下的就是枯燥的开发工作了。幸不辱命,勉强写出了一套可用的代码:
代码很简单,单文件,里边就Trainer
和Tokenizer
两个类,分别对应分词两部分。分词借助pyahocorasick来构建AC自动机来稍微提了一下速,能凑合用,但还是会比SentencePiece慢不少,毕竟速度方面纯Python跟C++确实没法比。训练则分为四个主要步骤:1、$n$-gram计数;2、$n$-gram剪枝;3、预分词;4、预分词结果剪枝。其中1、3、4都是计算密集型,并且都是可并行的,所以编写了相应的多进程实现。在开足够多的进程(笔者开了64进程,每个进程的使用率基本上都是满的)下,训练速度能媲美SentencePiece的Unigram训练速度。
这里特别要提一下结果剪枝方面。剪枝最基本的依据自然是频数和vocab_size,但这还不够,因为有时候会出现$p(w_1)p(w_2) > p(w_1\circ w_2)$($w_1\circ w_2$指两个词拼接)且$w_1,w_2,w_1\circ w_2$三个词都在词表中,这种情况下$w_1\circ w_2$这个词永远不会切分出来,所以将它放在词表中是纯粹浪费空间的,因此剪枝过程也包含了这类结果的排除。
效果测试 #
到了大家喜闻乐见的测试环节,是骡子是马总要拉出来遛遛。首先做个小规模的测试,从悟道之前开源的数据集里边随机采样10万条作为训练集(导出来的文件大概330MB),然后另外采样1千作为测试集,训练一个vocab_size=50k的词表,结果对比如下:
\begin{array}{c|ccc}
\hline
& \text{训练时间}\downarrow & \text{最大内存占用}\downarrow & \text{压缩率}\uparrow \\
\hline
\text{SP-BPE} & \text{55.3分钟} & \text{5.2GB} & 4.80 \\
\text{SP-Unigram} & \text{1.6分钟} & \text{2.5GB} & 4.73 \\
\text{BytePiece} & \text{6.5分钟} & \text{4.3GB} & 5.05 \\
\hline
\end{array}
解释一下,这里SP-BPE、SP-Unigram分别指SentencePiece的model_type设为BPE和Unigram,训练代码分别是
spm.SentencePieceTrainer.train('--input=wudao.txt --model_prefix=wudao_m --vocab_size=50000 --model_type=bpe --train_extremely_large_corpus=true --normalization_rule_name=identity')
spm.SentencePieceTrainer.train('--input=wudao.txt --model_prefix=wudao_m2 --vocab_size=50000 --model_type=unigram --train_extremely_large_corpus=true --normalization_rule_name=identity')
压缩率的单位是“bytes/token”,即平均每个token对应的字节数。可见,BytePiece能够在训练时间和内存都比较折中的情况下,获得最大的压缩率。
接下来进行一个更大规模的测试。从中英比例大致为3:5的混合语料库中,抽取出10万条样本训练vocab_size=100k的Tokenizer。这个语料库的文本都比较长,所以这时候10万条导出来的文件已经13GB了,测试集包含两部分,一部分是同样的语料库中采样出1000条(即同源),另一部分是刚才采样出来的1000条悟道数据集(代表不同源)。结果如下:
\begin{array}{c|cccc}
\hline
& \text{训练时间}\downarrow & \text{最大内存占用}\downarrow & \text{压缩率(同源)}\uparrow & \text{压缩率(异源)}\uparrow \\
\hline
\text{SP-BPE} & \text{19.21小时} & \text{97GB} & 4.52 & 4.46 \\
\text{SP-Unigram} & \text{2.02小时} & \text{384GB} & 4.51 & 4.48 \\
\text{BytePiece} & \text{2.24小时} & \text{51GB} & 5.39 & 4.51\\
\hline
\end{array}
不管是训练时间、内存还是压缩率,看起来训练数据量越大,BytePiece越有优势!
未完待续 #
就目前的结果看来,BytePiece在训练方面是有一定优势的,分词效果也尚可,不过吃了纯Python的亏,分词速度只有SentencePiece的1/10左右,这是未来的一个优化方向之一,期待有C/C++大牛能参与进来,帮助提升BytePiece的分词速度。(注:从0.2.0版开始,使用Cython加速了分词函数,目前BytePiece的分词速度已经接近BPE,并且在文本足够长时能优于BPE。)
实际上,如果采用随机采样、动态剪枝等技术,BytePiece的训练速度和内存都还可以进一步优化。目前BytePiece为了保证结果的确定性,直到所有结果都统计完毕才进行剪枝,这样不管是单进程还是多进程,都能保证结果的一致性。如果随机打乱输入,并且定时进行剪枝,那么可以进一步控制内存的占用量,同时还能加快统计速度,并且可以预期对最终效果的影响也不大。这部分工作,也在后面根据用户体验进一步引入。
除了以上这些,BytePiece细节之处还有不少需要完善的地方,以及可能还有未发现的错漏之处,敬请大家海涵且反馈
文章小结 #
本文介绍了笔者自行开发的Tokenizer——BytePiece,它是Byte-based的Unigram分词器,纯Python实现,更加易读和易拓展。由于采用了新的训练算法,所以压缩率通常比现有tokenizer更高,同时支持多进程加速训练。此外,它直接操作文本的utf-8 bytes,几乎不进行任何的预处理,所以更加纯粹和语言无关。
转载到请包括本文地址:https://spaces.ac.cn/archives/9752
更详细的转载事宜请参考:《科学空间FAQ》
如果您还有什么疑惑或建议,欢迎在下方评论区继续讨论。
如果您觉得本文还不错,欢迎分享/打赏本文。打赏并非要从中获得收益,而是希望知道科学空间获得了多少读者的真心关注。当然,如果你无视它,也不会影响你的阅读。再次表示欢迎和感谢!
如果您需要引用本文,请参考:
苏剑林. (Sep. 07, 2023). 《BytePiece:更纯粹、更高压缩率的Tokenizer 》[Blog post]. Retrieved from https://spaces.ac.cn/archives/9752
@online{kexuefm-9752,
title={BytePiece:更纯粹、更高压缩率的Tokenizer},
author={苏剑林},
year={2023},
month={Sep},
url={\url{https://spaces.ac.cn/archives/9752}},
}
September 7th, 2023
[...]Read More [...]
September 7th, 2023
您好,請問一下,您這個方法,會不會遇到斷在byte組字邊界內的問題呢?
例如說中文字是3 bytes,會不會就剛好斷在第二個byte,導致連正常中文字都無法decode出來?
因為 byte level 基於統計的分詞模型,不保證一定會斷在正確的組字邊界上,總是有一定機率斷錯,導致無法正確decode,您是怎麼處理這問題的呢?
会遇到。这个问题不用处理,不影响结果,或者说这就不算问题。
从tokenizer的作用来看,它只需要保证高压缩率,以及可以无损还原就行,至于分解出的每个token是否可读、是否可以还原为unicode,这都是不重要的。
从LLM的角度来看,确实有可能预测出无法正确还原为unicode的bytes序列,但这个也不重要,直接在decode的时候加errors='ignore'参数就行,因为这个问题就好比LLM无法输出逻辑通顺的结果一样,是LLM的能力问题,不是tokenizer的问题。
所以,假設用你的 Byte level 發生斷錯的問題,就好比原本用 SentencePiece 斷出沒意義的 subword;而 LLM 的能力在於要選對有意義的 subword 接下去,如果他選錯了,就會產生無意義的單字(word),跟 byte level 預測出無法decode 的序列是類似的。
不過有沒有可能 byte level 預測一步錯,步步錯,原本只錯一個地方無法decode,結果影響到後面整句話都無法decode,若加上ignore參數,就整句都無法輸出了?而subword 預測錯了,至少輸出還是字元,後面的輸出還是可以看得懂,不至於整句無法輸出。
在Python3中,以下代码
>>> '今天天气不错'.encode()[1:].decode(errors='ignore')
还是可以输出“天天气不错”的,所以不存在你所说的问题。
事实上,即便是setencepiece,也会对OOV的低频单字拆分为byte的,所以这种情况即便在sentencepiece也有,虽然它针对的是低频单字。但也正因为sentencepiece开始先char-based,所以词表里边保留了过多不常用单词,浪费了空间,也降低了压缩率。
utf-8可以保证解码的唯一性
September 7th, 2023
不知道苏老师有尝试更极端的 bit level 的分词么
没有试过,感觉太极端了,没有必要。
September 7th, 2023
另外,請問您這方法跟 Byte level BPE 有何不同呢?
https://zhuanlan.zhihu.com/p/146114164
看上去,除了大家都是直接处理bytes之外,训练算法和分词算法都不同,训练算法明显不同了,至于分词算法,我看BPE的介绍,应该用的是最大匹配?
因為ChatGPT就是使用Byte level BPE,如果你也是使用byte level,那麼你的算法跟BBPE比起來,除了都有Byte level 的好處外,還有沒有更多其他好處呢?
據我了解,由於ChatGPT是以token數計價,不同語言斷出來的token數差異非常大,導致價差可以達到數倍:
https://technews.tw/2023/08/03/linguistic-inequality-in-big-ai-models/
就說同一個句子,翻成英文跟翻成中文輸出,token數就差一倍,因此中文價格是英文的兩倍。
因此,一個有潛力的研究方向是: tokenizer 能不能做到不同語言儘量「平等」?也就是同一句話,翻譯成不同語言,但斷出來的token數都差不多?若能做到的話,那你的算法就比BBPE 有明顯的好處,至少對中文使用者來說可以更節省成本。對於一些少資源的語言來說,也可以降低訓練成本,對環境更加友好。
事实上,任意一个语言无关的tokenizer都能做到一点,语言无关意味着它对语言的偏置只取决于训练语料中语言的分布。ChatGPT之所以会使得相同意思的中文比英文需要更多的token,大概率是因为它训练tokenizer的语料多数都是英文,少数甚至没有中文。
当训练充分时,结果可以参考本文的第二个表格,同源的压缩率是在中英混合语料上测的,异源的压缩率是在纯中文语料上测的,它们相差不算大,意味着它们为中英文分配了大致相同数量的tokens,其源头就是训练tokenizer的语料中英文比例是3:5,相差不大,所以tokens的分配也相差不大。
我上面貼的文章中提到:「OpenAI GPT3 tokenizer詞元化「你的愛意」,英文只兩個詞元,簡中需八個詞元,即使簡中只有4字元,英文文本有14個字元。」
換成byte的話,中文4*3=12 bytes,英文14*1 = 14 bytes,以bytes數來說是差不多的。但是英文只需要2個詞元,也就是一個詞元14/2 = 7 bytes,而中文卻需要8個詞元,也就是一個詞元 12/8 = 1.5 bytes,大概就是半個中文字。這有點奇怪,似乎他們完全沒有訓練中文的 tokenizer,就只是單純的把中文拆成1 或 2 bytes一個 token 而已。可想而知,對於其他語言應該也只是單純拆byte,並沒有針對語言訓練tokenizer。
但是從詞表大小的角度來看就合理了。更長的bytes排列組合會讓可能的token數更多。例如以你的壓縮率 5 bytes/token,一個 byte 有 256 種可能,5個byte就有 256^5 種可能的token。而 LLM 基本上還是預測詞表中的token,而非直接預測byte。所以如果照你的切法,你的詞表要非常大,才有辦法容納這些token。但是如果不希望詞表那麼大的話,就只能讓某些語言只用 1 或 2 bytes/token 來切,這樣就只需要256^2 就可以容納了。
你文章前面提到「杜绝OOV(Out of Vocabulary)」,這個講法其實是不太準確的。因為在LLM上,除非你訓練的時候就直接餵byte進去,預測輸出的index也是指byte,那才會讓詞表大小只有256。若你預測的index是指詞表中的token而非byte,那詞表的大小會是正比於可能的token數量。而可能的token數量是其byte數的指數增長。因此就算用你的算法,窮盡所有語料切出來的token,可能也放不進一個有限大小的詞表,還是會有OOV問題。除非你把一個token再拆成更小的bytes,透過LLM能力來組詞。
而光是2 bytes就有 256^2 = 65536 種可能,所以 1 or 2 bytes 在詞表中就需要 65536 + 256 = 65792個token,透過LLM的能力,這已經足夠組成各種語言的詞彙了。但是byte level的序列太長了,為了優化速度,所以才再加上一些byte數較長的常用token。由於詞表大小有限,以ChatGPT主要服務英語市場,為英語添加更多較長的token也是合理的。
結論:
雖然 tokenizer 可以切出來各語言差不多byte長度的token,但是數量太大無法完全放進詞表。為了避免OOV的問題,還是需要放進所有1~2byte的組合進詞表,這就佔了詞表大部分。因此,若為了速度考量,也只能再增加1~2種常用語言的較長token,無法每個語言都放進去。其他語言就通通拆成byte讓LLM組詞。
1、杜绝OOV只需要保证词表里边包含256个单byte,因为最极端的情况,也不外乎将输入切分为单个byte的列表,所以这是100%准确的杜绝,并非大概率;
2、如果这个世界上有足够多的语言,每种语言又有足够多的不同单字,那么覆盖所有语言的常见字词确实需要一个非常大的vocab size,这个没办法;
3、5 bytes/token,并非所有5 bytes的排列组合都出现,这个还是得看语言的数据分布,跟第2点同理。
4、假设训练集每种语言的比例都比较均衡,那么固定vocab_size时,模型只能尽可能做到每种语言的tokens或者说压缩率都尽可能均衡,肯定没法又均匀又压缩率高。
1. 我的意思是,您在訓練LLM時,應該不是以byte為單位的吧?例如LLM輸出一串id: 1149 23586 3427 5581.... 這id 代表的意思是softmax 預測出來機率最高的dimension index。因此若詞表有10萬tokens,softmax output就是10萬dimensions,取 argmax 得到最大的那個index id,再用這些id反查詞表中所代表的token。
假如你是用byte訓練LLM的,那麼你的詞表中確實就只要有256個token就夠了,每個token代表一個byte,就足以組成所有可能。但是一個byte一個byte輸出,很容易長度過長,超出context size。
所以今天 tokenizer 的目的應該是降低輸出序列的長度,藉由把常出現的byte序列聚合在一起成為一個新token,例如原本需要5byte的序列,就用一個token來代表,顯著壓縮了輸出的長度。
因此我認為,tokenizer的好壞,應該不是只考慮壓縮率,而是要考慮在限制詞表大小下的壓縮率。
您上面的比較表格中,除了壓縮率之外,並沒有統計同一份訓練資料,不同算法切出來的詞表大小。有可能你的算法壓縮率高,是因為長序列多,但切出來的詞表特別大,這兩者互為trade off。
-----------------
抱歉,後來我發現你有給 vocab_size=50k ,是我漏看了。
以上算是我給自己寫的一篇解釋吧,因為先前一直沒搞懂為何Byte level tokenizer 還要那麼大詞表?不是說沒有OOV問題了嗎?現在瞭解了,確實只要詞表包含256 個代表一個byte的token 就不會有OOV,而詞表中增加其他token 主要是為了壓縮輸出序列的長度,但代價就是softmax output的維度增加了。
而之所以會有需要壓縮序列長度的問題,根本原因還是在於目前的LLM都是autoregressive的,根據前一個輸出預測下一個輸出,因此當序列很長的時候就會很沒效率。
我認為使用 tokenizer 不斷加大詞表,也只是暫時的辦法。因為每個時代常用詞會改變,10年後會流行甚麼詞也不知道。基於資料的分詞算法,也無法應對不斷創造的新詞,甚至是一些舊詞新用,把舊詞拆解變新的意義,那舊的tokenizer 也要隨時間而重新訓練了。所以目前這種不斷加大詞表的作法,我想是很難持續的。
@Allen Li|comment-22691
OK,那么对tokenizer的基本认知上,我们应该对齐了。
至于你最后说的时代更替问题,这是目前的主流难题之一,而且瓶颈并非在tokenizer上,因为相对来说训练一个tokenizer是轻松的,问题在于如果词频分布有太大变化的话,可能整个LLM都要重新训练,这才是关键。
September 8th, 2023
想请教苏神一些简单的问题:
1. 就是传统的子词算法有BPE、WordPiece和Unigram这些,那SentencePiece和它们之间的关系是什么呢?我可不可以认为SP是一个框架,实现了这些子词算法?但是我看有些教程里说SP本身也是一个子词算法,这是不是有些不妥呢?
2. SP提到了不需要做预分词,似乎只需要把原始文本中的空格替换成特殊的下划线然后再应用相应的子词算法即可。但是BPE还有Unigram这些都是依赖于预分词的,那SP是怎么去应用BPE的呢?
1、sentencepiece应该是包含了bpe、unigram等算法的一个框架吧?
2、这个细节我真不大清楚了,没读它的源码,按照我的猜测,直接按照空格和标点符号分就行?(即便中文问题也不大),因为都会切细。
September 9th, 2023
提供一個 C/C++ 以外的新選擇: Mojo
https://www.infoq.cn/news/eXdKQS3BhVWJ6JlXuiXy
據說Mojo可以完全兼容Python,又比Python快上68000倍!
或許您的代碼只需少量更動,改用Mojo後就能大幅提速,可以試試看!
很多库都还没适配,还是先等等吧,现在体验可能欠佳。
我现在用cython加速,目前速度勉强够用了。
我对mojo的评价是,跟cython一样,兼容python更像是“碰瓷”,事实上代码越像python,提速幅度就越小,只要将代码改得几乎不像python,才会有明显的提速。
September 12th, 2023
苏神,我看词表里"\n"重复出现几次,这个是啥导致的
我知道了。。。
因为value中的\n仅仅是观察用,实际读的是key,它可能是\n多加了1~2个bytes,然后这些额外的bytes无法正确解码为unicode,从而显示为空。
嗯,我后面注意到了~谢谢苏神
另外,这几天抽了2个晚上写了rust版本,简单优化了下,能比现在提升差不多一倍。不过比另一个版本还是慢一倍,汗啊!有空再看看怎么优化。
https://github.com/hscspring/bytepiece-rs/tree/main/bindings/python
你对比的是我写的吗,你的内存分配太多了。。。
https://github.com/SunDoge/bytepiece-rs
欢迎两位大佬碰撞。
点赞。如果能快3~4倍,那么基本上就追平sentencepiece了哈哈。
两位大佬,训练部分有r实现吗?
训练部分比较复杂,搬到rust估计比较困难,而且训练部分可能瓶颈不大了。
September 16th, 2023
[...]上一篇文章《大词表语言模型在续写任务上的一个问题及对策》发布后,很快就有读者指出可以在训练阶段引入带有随机性的分词结果来解决同样的问题,并且已经有论文和实现。经过进一步查阅学习,笔者发现这是一个名为Subword Regularization的技巧,最早应用在NMT(机器翻译)中,目前SentencePiece也有相应的实现。看起来这个技巧确实能缓解前述问题,甚至有助于增强语言模型的容错能力,所以[...]
October 8th, 2023
苏老师,我理解 s_j 的条件概率中“条件”的最后一项应该是 s_j 的前一项,所以公式 (1) 这里最后一项 s_{L_{i−1}+j−1} 是不是应该改为 s_{j-1}?
你是对的,是我懵了哈哈。
January 5th, 2024
我想问下,实验表格中的压缩率是怎么计算的呢,现在的语言模型对应的论文中也有这个指标的对比,想知道这边指标具体是怎么计算的,
bytes/token:给定一批语料,计算总bytes数以及分词后的总token数,然后相除。