cs336-01-bpe编码

unicode编码

在python可以使用ord函数查询某个字符的unicode编码,可以chr函数反查

特殊的,chr(0) 就是空字符(NUL)

  • print打印这个字符是看不到的
  • repr可以更精细,看到的是’’
1
2
3
4
5
6
ord('牛') #29275
chr(29275) #'牛'
chr(0) #'\x00'
print(chr(0)) #
"this is a test" + chr(0) + "string" #'this is a test\x00string'
print("this is a test" + chr(0) + "string")#this is a teststring

NOTE

Q:为什么用UTF-8编码进行tokenizer而不是UTF-16/UTF-32

UTF-32:每个Unicode字符固定4 字节,不管是 a、中、🙂,全部 4 字节,例如 'a' -> 00 00 00 61 '中' -> 00 00 4E 2D

UTF-16:常见字符2 字节,少数(emoji / 古文字)4 字节,相比UTF-32好一些,但是还是有点大

UTF-8:字符ASCII 1字节;中文 3 字节;Emoji 4 字节;并且完全向后兼容 ASCII,例如 'a' -> 61 '中' -> E4 B8 AD '🙂' -> F0 9F 99 82


NOTE

Q:下面的代码有什么问题,怎么修正

1
2
3
4
5
6
7
def decode_utf8_bytes_to_str_wrong(bytestring: bytes):
    return "".join(
        [bytes([b]).decode("utf-8") for b in bytestring]
    )

>>> print(decode_utf8_bytes_to_str_wrong("hello".encode("utf-8")))
'hello'

问题在于UTF-8 是变长编码,而上面的代码在按字节强行拆开解码

比如print(decode_utf8_bytes_to_str_wrong("中文".encode("utf-8")))就会有问题'utf-8' codec can't decode byte 0xe4 in position 0: unexpected end of data


NOTE

Q:有没有两字节的序列,不是合法的unicode编码

以UTF-8来说,byte 序列空间 ≫ Unicode 字符空间,有效的两字节序列只有一类:110xxxxx 10xxxxxx,对应到

  • 第 1 字节:0xC2 – 0xDF
  • 第 2 字节:0x80 – 0xBF

留下大量非法序列可以快速进行错误检测


bpe(byte-pair encoding, Gage, 1994)

简单来讲就是每一次迭代合并两个词,选择频率最高的那组进行组合,下一次依旧是选择频率最高的那组组合

如果合并过程中碰到两个合并频率相同,比如”ab”和”fe”,我们按照lexicographically greater pair优先(选择字符串排序大的那个),选择”fe”

举一个简单的例子:

1
2
3
low low low low low
lower lower widest widest widest
newest newest newest newest newest newest

首先按照空格分词:

  • low 5
  • lower 2
  • widest 3
  • newest 6

第一轮:依次组合两个字符组合,按照频率,最高的是(e,s)和(s,t),都是9次,按照排序选择(s,t)

第二轮:依次组合两个字符组合,按照频率,最高的是(e,st)是9次

以此类推,假设再跑四轮,组合依次是:(o,w) (l,ow) (w,est) (n,e)

经过六轮过后,我们有了(st) (est) (ow) (low) (west) (ne)

把他们应用到原来的词上,得到:

  • low: low
  • lower: low e r
  • widest: w i d est
  • newest: ne west