首页 > 其他分享 >【tokenization分词】WordPiece, Byte-Pair Encoding(BPE), Byte-level BPE(BBPE)的原理和代码

【tokenization分词】WordPiece, Byte-Pair Encoding(BPE), Byte-level BPE(BBPE)的原理和代码

时间:2024-11-18 10:19:26浏览次数:3  
标签:BPE Encoding ## 词表 split pair word Byte 分词

目录

前言

1、word (词粒度)

2、char (字符粒度)

3、subword (子词粒度)

WordPiece

Byte-Pair Encoding (BPE)

Byte-level BPE(BBPE)

总结


前言

Tokenization(分词) 在自然语言处理(NLP)的任务中是最基本的一步,将文本处理成一串tokens用于后续的处理,把文本处理成token有一系列的方法,基本思想是构建一个词表,通过词表一一映射进行分词,但如何构建合适的词表呢?以下介绍三种Tokenization方法。


以下分别介绍三种分词粒度,并且重点介绍 subword (子词粒度) 的三种分词方法。

1、word (词粒度)

在英文语系中,word(词)粒度的分词很容易实现,因为有天然的空格作为分隔符。而在中文里面 word (词)粒度,需要一些分词工具比如jieba,以下是中文和英文的例子:

中文句子:我喜欢看电影和读书。
分词结果:我 | 喜欢 | 看 | 电影 | 和 | 读书。


英文句子:I enjoy watching movies and reading books.
分词结果:I | enjoy | watching | movies | and | reading | books.

word (词粒度) 的优点:

  • 词自身的语义明确:以词为单位进行分词可以更好地保留每个词的语义。
  • 词之间的语义关联:以词为粒度进行分词有助于保留词语之间的关联性和上下文信息,从而在语义分析和理解时能够更好地捕捉句子的意图。

缺点:

  • 长尾效应:词表可能变得巨大,包含很多不常见的词汇,增加存储成本;稀有词的训练数据有限,难以获得准确的表示。
  • OOV(Out-of-Vocabulary): 词粒度分词模型只能使用词表中的词来进行处理,无法处理词表之外的词汇,这就是所谓的OOV问题。
  • 形态关系和词缀关系: 无法捕捉同一词的不同形态,比如love和loves在word(词)粒度的词表中会是两个词;也无法学习词缀在不同词汇之间的共通性,限制了模型的语言理解能力。

2、char (字符粒度)

以字符为单位进行分词,即将文本拆分成一个个单独的字符作为最小基本单元,这种字符粒度的分词方法适用于多语言,无论是英文、中文还是其他不同语言,都能够一致地使用字符粒度进行处理,因为英文就26个字母以及其他的一些符号,中文常见字就6000个左右。

中文句子:我喜欢看电影和读书。
分词结果:我 | 喜 | 欢 | 看 | 电 | 影 | 和 | 读 | 书 | 。

英文句子:I enjoy watching movies and reading books.
分词结果:I |   | e | n | j | o | y |   | w | a | t | c | h | i | n | g |   | m | o | v | i | e | s |   | a | n | d |   | r | e | a | d | i | n | g |   | b | o | o | k | s | .

char (字符粒度) 的优点有:

  • 统一处理方式:字符粒度分词方法适用于不同语言,无需针对每种语言设计不同的分词规则或工具,具有通用性。
  • 解决OOV问题:由于字符粒度分词可以处理任何字符,无需维护词表,因此可以很好地处理一些新创词汇、专有名词等问题。

缺点:

  • 语义信息不明确:字符粒度分词无法直接表达词的语义,导致在一些语义分析任务中效果较差。
  • 处理效率低:由于文本被拆分为字符,处理的粒度较小,增加后续处理的计算成本和时间。

3、subword (子词粒度)

在很多情况下,既不希望将文本切分成单独的词(太大),也不想将其切分成单个字符(太小),而是希望得到介于词和字符之间的子词单元。这就引入了 subword(子词)粒度的分词方法。

WordPiece

在BERT时代,WordPiece 分词方法被广泛应用,比如 BERT、DistilBERT等。WordPiece 分词方法属于 subword(子词)粒度的一种方法。 

WordPiece核心思想是将单词拆分成多个前缀符号(比如BERT中的##)最小单元,再通过子词合并规则将最小单元进行合并为子词级别。例如对于单词"word",拆分如下:

w ##o ##r ##d

然后通过合并规则进行合并,从而循环迭代构建出一个词表,以下是核心步骤:

  1. 计算初始词表:通过训练语料获得或者最初的英文中26个字母加上各种符号以及常见中文字符,这些作为初始词表。
  2. 计算合并分数:对训练语料拆分的多个子词单元通过合拼规则计算合并分数。
  3. 合并子词对:选择分数最高的子词对,将它们合并成一个新的子词单元,并更新词表。
  4. 重复合并步骤:不断重复步骤 2 和步骤 3,直到达到预定的词表大小、合并次数,或者直到不再有有意义的合并(即,进一步合并不会显著提高词表的效益)。
  5. 分词:使用最终得到的词汇表对文本进行分词。

简单举例:

有以下的训练语料中的样例,括号中第2位为在训练语料中出现的频率:

("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)

将其拆分为带前缀的形式:

("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##g" "##s", 5)

初始化的词表为:

["b", "h", "p", "##g", "##n", "##s", "##u"]

接下来重要的一步进行计算合并分数,也称作互信息(信息论中衡量两个变量之间的关联程度),简单来说就是以下公式来计算:

分数 = 候选pair的频率 / (第一个元素的频率 × 第二个元素的频率)

对于上述样例中这个pair("##u", "##g")出现的频率是最高的20次,但是"##u"出现的频率是36次, "##g"出现的频率是20次,所以这个pair("##u", "##g")的分数是(20)/(36*20) = 1/36,同理计算这个pair("##g", "##s")的分数为(5)/(20*5) = 1/20,所以最先合并的pair是("##g", "##s")→("##gs")。此时词表和拆分后的的频率将变成以下:

Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs"]
Corpus: ("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##gs", 5)

重复上述的操作,直到达到你想要的词表的大小:

Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu", "hug"]
Corpus: ("hug", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("hu" "##gs", 5)

代码实现:

# 用一些包含中英文的文本作为训练语料,因为英文有天然的分隔符,所以在这个例子中,中文已经进行了分词:
sentences = [
    "我",
    "喜欢",
    "吃",
    "苹果",
    "他",
    "不",
    "喜欢",
    "吃",
    "苹果派",
    "I like to eat apples",
    "She has a cute cat",
    "you are very cute",
    "give you a hug",
]

# 初始化词表
from collections import defaultdict
# 构建频率统计
def build_stats(sentences):
    stats = defaultdict(int)
    for sentence in sentences:
        symbols = sentence.split()
        for symbol in symbols:
            stats[symbol] += 1
    return stats

stats = build_stats(sentences)
print("stats:", stats)

alphabet = []
for word in stats.keys():
    if word[0] not in alphabet:
        alphabet.append(word[0])
    for letter in word[1:]:
        if f"##{letter}" not in alphabet:
            alphabet.append(f"##{letter}")

alphabet.sort()
# 初始词表
vocab = alphabet.copy()
print("alphabet:", alphabet)

# 根据初始词表拆分每个词
splits = {
    word: [c if i == 0 else f"##{c}" for i, c in enumerate(word)]
    for word in stats.keys()
}
print("splits:", splits)

# 计算合并分数
def compute_pair_scores(splits):
    letter_freqs = defaultdict(int)
    pair_freqs = defaultdict(int)
    for word, freq in stats.items():
        split = splits[word]
        if len(split) == 1:
            letter_freqs[split[0]] += freq
            continue
        for i in range(len(split) - 1):
            pair = (split[i], split[i + 1])
            letter_freqs[split[i]] += freq
            pair_freqs[pair] += freq
        letter_freqs[split[-1]] += freq

    scores = {
        pair: freq / (letter_freqs[pair[0]] * letter_freqs[pair[1]])
        for pair, freq in pair_freqs.items()
    }
    return scores

pair_scores = compute_pair_scores(splits)
for i, key in enumerate(pair_scores.keys()):
    print(f"{key}: {pair_scores[key]}")
    if i >= 5:
        break

# 看一看分数最高的pair(子词对):
best_pair = ""
max_score = None
for pair, score in pair_scores.items():
    if max_score is None or max_score < score:
        best_pair = pair
        max_score = score

print(best_pair, max_score)

# 合并函数
def merge_pair(a, b, splits):
    for word in stats:
        split = splits[word]
        if len(split) == 1:
            continue
        i = 0
        while i < len(split) - 1:
            if split[i] == a and split[i + 1] == b:
                merge = a + b[2:] if b.startswith("##") else a + b
                split = split[:i] + [merge] + split[i + 2 :]
            else:
                i += 1
        splits[word] = split
    return splits

# 循环迭代,直到vocab达到想要的数量
vocab_size = 50
while len(vocab) < vocab_size:
    scores = compute_pair_scores(splits)
    best_pair, max_score = "", None
    for pair, score in scores.items():
        if max_score is None or max_score < score:
            best_pair = pair
            max_score = score
    splits = merge_pair(*best_pair, splits)
    new_token = (
        best_pair[0] + best_pair[1][2:]
        if best_pair[1].startswith("##")
        else best_pair[0] + best_pair[1]
    )
    vocab.append(new_token)

# 结果
vocab: ['##a', '##e', '##g', '##h', '##i', '##k', '##l', '##o', '##p', '##r', '##s', '##t', '##u', '##v', '##y', '##果', '##欢', '##派', 'I', 'S', 'a', 'c', 'e', 'g', 'h', 'l', 't', 'v', 'y', '不', '他', '吃', '喜', '我', '苹', 'Sh', '喜欢', '苹果', '苹果派', 'li', 'lik', 'gi', 'giv', '##pl', '##ppl', '##ry', 'to', 'yo', 'ea', 'eat']

一般来说最后会在词表中加上一些特殊词汇、英文中26个字母、各种符号以及常见中文字符,如果训练语料比较大那么这些应该也已经包括了,只需要添加特殊词汇:

all_vocab = vocab + ["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"] + other_alphabet

Byte-Pair Encoding (BPE)

在大语言模型时代,最常用的分词方法是Byte-Pair Encoding (BPE) 和 Byte-level BPE(BBPE),BPE 最初是一种文本压缩算法,,在15年被引入到NLP用于分词,在训练 GPT 时被 OpenAI 用于tokenization,后续好多模型GPT,RoBERTa等都采用了这种分词方法。BBPE是于19年在BPE的基础上提出以Byte-level(字节)为粒度的分词方法,目前GPT2,BLOOM,Llama,Falcon等采用的是该分词方法。 

Byte-Pair Encoding (BPE)核心思想是逐步合并出现 频率最高 的子词对,而不是像Wordpiece计算合并分数来构建词汇表,以下是核心步骤:

  1. 计算初始词表:通过训练语料获得或者最初的英文中26个字母加上各种符号以及常见中文字符,这些作为初始词表。
  2. 构建频率统计:统计所有子词单元对(两个连续的子词)在文本中的出现频率。
  3. 合并子词对:选择出现频率最高的子词对,将它们合并成一个新的子词单元,并更新词汇表。
  4. 重复合并步骤:不断重复步骤 2 和步骤 3,直到达到预定的词汇表大小、合并次数,或者直到不再有有意义的合并(即,进一步合并不会显著提高词汇表的效益)。
  5. 分词:使用最终得到的词汇表对文本进行分词。

代码:

sentences = [
    "我",
    "喜欢",
    "吃",
    "苹果",
    "他",
    "不",
    "喜欢",
    "吃",
    "苹果派",
    "I like to eat apples",
    "She has a cute cat",
    "you are very cute",
    "give you a hug",
]

# 初始化词表:
# 构建频率统计
def build_stats(sentences):
    stats = defaultdict(int)
    for sentence in sentences:
        symbols = sentence.split()
        for symbol in symbols:
            stats[symbol] += 1
    return stats

stats = build_stats(sentences)
print("stats:", stats)

alphabet = []
for word in stats.keys():
    for letter in word:
        if letter not in alphabet:
            alphabet.append(letter)
alphabet.sort()

# 初始词表
vocab = alphabet.copy()
print("alphabet:", alphabet)

# 根据初始词表拆分每个词
splits = {word: [c for c in word] for word in stats.keys()}
print("splits:", splits)

def compute_pair_freqs(splits):
    pair_freqs = defaultdict(int)
    for word, freq in stats.items():
        split = splits[word]
        if len(split) == 1:
            continue
        for i in range(len(split) - 1):
            pair = (split[i], split[i + 1])
            pair_freqs[pair] += freq
    return pair_freqs
pair_freqs = compute_pair_freqs(splits)

for i, key in enumerate(pair_freqs.keys()):
    print(f"{key}: {pair_freqs[key]}")
    if i >= 5:
        break

# 找到出现频率最高的pair(子词对):
best_pair = ""
max_freq = None
for pair, freq in pair_freqs.items():
    if max_freq is None or max_freq < freq:
        best_pair = pair
        max_freq = freq

print(best_pair, max_freq)

# 合并函数
def merge_pair(a, b, splits):
    for word in stats:
        split = splits[word]
        if len(split) == 1:
            continue

        i = 0
        while i < len(split) - 1:
            if split[i] == a and split[i + 1] == b:
                split = split[:i] + [a + b] + split[i + 2 :]
            else:
                i += 1
        splits[word] = split
    return splits

# 循环直到vocab达到想要的数量:
# 假设我们想要的词典为50
merges = {}
vocab_size = 50

while len(vocab) < vocab_size:
    pair_freqs = compute_pair_freqs(splits)
    best_pair = ""
    max_freq = None
    for pair, freq in pair_freqs.items():
        if max_freq is None or max_freq < freq:
            best_pair = pair
            max_freq = freq
    splits = merge_pair(*best_pair, splits)
    merges[best_pair] = best_pair[0] + best_pair[1]
    vocab.append(best_pair[0] + best_pair[1])

print("merges:", merges)
print("vocab:", vocab)

# 结果
merges: {('喜', '欢'): '喜欢', ('苹', '果'): '苹果', ('a', 't'): 'at', ('c', 'u'): 'cu', ('cu', 't'): 'cut', ('cut', 'e'): 'cute', ('y', 'o'): 'yo', ('yo', 'u'): 'you', ('v', 'e'): 've', ('苹果', '派'): '苹果派', ('l', 'i'): 'li', ('li', 'k'): 'lik', ('lik', 'e'): 'like', ('t', 'o'): 'to', ('e', 'at'): 'eat', ('a', 'p'): 'ap', ('ap', 'p'): 'app', ('app', 'l'): 'appl', ('appl', 'e'): 'apple', ('apple', 's'): 'apples', ('S', 'h'): 'Sh', ('Sh', 'e'): 'She', ('h', 'a'): 'ha'}
vocab: ['I', 'S', 'a', 'c', 'e', 'g', 'h', 'i', 'k', 'l', 'o', 'p', 'r', 's', 't', 'u', 'v', 'y', '不', '他', '吃', '喜', '我', '果', '欢', '派', '苹', '喜欢', '苹果', 'at', 'cu', 'cut', 'cute', 'yo', 'you', 've', '苹果派', 'li', 'lik', 'like', 'to', 'eat', 'ap', 'app', 'appl', 'apple', 'apples', 'Sh', 'She', 'ha']

再加上一些特殊词汇和其他词汇:

all_vocab = vocab + ["[PAD]", "[UNK]", "[BOS]", "[EOS]"] + other_alphabet

BPE理论上还是会出现OOV的,当词汇表的大小受限时,一些较少出现的子词和没有在训练过程中见过的子词,就会无法进入词汇表出现OOV,而Byte-level BPE(BBPE)理论上是不会出现这个情况的。

Byte-level BPE(BBPE)

基础知识:

Unicode: Unicode 是一种字符集,旨在涵盖地球上几乎所有的书写系统和字符。Unicode 码点的取值范围是 U+0000U+10FFFF,总共覆盖了 1,114,112 个可能的码点。这些码点包括了全球几乎所有的书写系统和符号,可以容纳各种文字、符号、表情符号等。

UTF-8:UTF-8(Unicode Transformation Format-8)是一种变长的字符编码方案,它将 Unicode 中的代码点转换为字节序列。在 UTF-8 编码中,字符的表示长度可以是1到4个字节,不同范围的 Unicode 代码点使用不同长度的字节序列表示,这样可以高效地表示整个 Unicode 字符集。

例如,英文字母 "A" 的 Unicode 代码点是U+0041,在 UTF-8 中表示为 0x41(与 ASCII 编码相同);而中文汉字 "你" 的 Unicode 代码点是U+4F60,在 UTF-8 中表示为0xE4 0xBD 0xA0三个字节的序列。

Byte(字节):计算机存储和数据处理的最小单位。一个字节包含8个(Bit)二进制位,每个位可以是0或1,每位的不同排列和组合可以表示不同的数据。

Byte-level BPE (BBPE) 和Byte-Pair Encoding (BPE) 区别是:BPE的最小词汇是字符级别,而BBPE是字节级别的,通过UTF-8的编码方式,理论上可以表示这个世界上的所有字符。

所以实现步骤和BPE是一样的,只是实现的粒度不一样:

  1. 初始词表:构建初始词表,包含一个字节的所有表示(256)。
  2. 构建频率统计:统计所有子词单元对(两个连续的子词)在文本中的出现频率。
  3. 合并子词对:选择出现频率最高的子词对,将它们合并成一个新的子词单元,并更新词汇表。
  4. 重复合并步骤:不断重复步骤 2 和步骤 3,直到达到预定的词汇表大小、合并次数,或者直到不再有有意义的合并(即,进一步合并不会显著提高词汇表的效益)。
  5. 分词:使用最终得到的词汇表对文本进行分词。
from collections import defaultdict
sentences = [
    "我",
    "喜欢",
    "吃",
    "苹果",
    "他",
    "不",
    "喜欢",
    "吃",
    "苹果派",
    "I like to eat apples",
    "She has a cute cat",
    "you are very cute",
    "give you a hug",
]
# 构建初始词汇表,包含一个字节的256个表示
initial_vocab = [bytes([byte]) for byte in range(256)]
vocab = initial_vocab.copy()
print("initial_vocab:", initial_vocab)

# 构建频率统计
def build_stats(sentences):
    stats = defaultdict(int)
    for sentence in sentences:
        symbols = sentence.split()
        for symbol in symbols:
            stats[symbol.encode("utf-8")] += 1
    return stats
stats = build_stats(sentences)

splits = {word: [byte for byte in word] for word in stats.keys()}
def compute_pair_freqs(splits):
    pair_freqs = defaultdict(int)
    for word, freq in stats.items():
        split = splits[word]
        if len(split) == 1:
            continue
        for i in range(len(split) - 1):
            pair = (split[i], split[i + 1])
            pair_freqs[pair] += freq
    return pair_freqs

pair_freqs = compute_pair_freqs(splits)

def merge_pair(pair, splits):
    merged_byte = bytes(pair)
    for word in stats:
        split = splits[word]
        if len(split) == 1:
            continue
        i = 0
        while i < len(split) - 1:
            if split[i:i+2] == pair:  # 检查分割中是否有这对字节
                split = split[:i] + [merged_byte] + split[i + 2 :]
            else:
                i += 1
        splits[word] = split
    return splits

vocab_size = 50
while len(vocab) < vocab_size:
    pair_freqs = compute_pair_freqs(splits)
    best_pair = ()
    max_freq = None
    for pair, freq in pair_freqs.items():
        if max_freq is None or max_freq < freq:
            best_pair = pair
            max_freq = freq
    splits = merge_pair(best_pair, splits)
    merged_byte = bytes(best_pair)

print("vocab:", vocab)

着重解释一下为什么Byte-level BPE(BBPE)不会出现OOV问题,初始的词表里有256个表示如下:

[b'\x00', b'\x01', b'\x02', b'\x03', b'\x04', b'\x05', b'\x06', b'\x07', b'\x08', b'\t', b'\n', b'\x0b', b'\x0c', b'\r', b'\x0e', b'\x0f', b'\x10', b'\x11', b'\x12', b'\x13', b'\x14', b'\x15', b'\x16', b'\x17', b'\x18', b'\x19', b'\x1a', b'\x1b', b'\x1c', b'\x1d', b'\x1e', b'\x1f', b' ', b'!', b'"', b'#', b'$', b'%', b'&', b"'", b'(', b')', b'*', b'+', b',', b'-', b'.', b'/', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b':', b';', b'<', b'=', b'>', b'?', b'@', b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'[', b'\\', b']', b'^', b'_', b'`', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', b'{', b'|', b'}', b'~', b'\x7f', b'\x80', b'\x81', b'\x82', b'\x83', b'\x84', b'\x85', b'\x86', b'\x87', b'\x88', b'\x89', b'\x8a', b'\x8b', b'\x8c', b'\x8d', b'\x8e', b'\x8f', b'\x90', b'\x91', b'\x92', b'\x93', b'\x94', b'\x95', b'\x96', b'\x97', b'\x98', b'\x99', b'\x9a', b'\x9b', b'\x9c', b'\x9d', b'\x9e', b'\x9f', b'\xa0', b'\xa1', b'\xa2', b'\xa3', b'\xa4', b'\xa5', b'\xa6', b'\xa7', b'\xa8', b'\xa9', b'\xaa', b'\xab', b'\xac', b'\xad', b'\xae', b'\xaf', b'\xb0', b'\xb1', b'\xb2', b'\xb3', b'\xb4', b'\xb5', b'\xb6', b'\xb7', b'\xb8', b'\xb9', b'\xba', b'\xbb', b'\xbc', b'\xbd', b'\xbe', b'\xbf', b'\xc0', b'\xc1', b'\xc2', b'\xc3', b'\xc4', b'\xc5', b'\xc6', b'\xc7', b'\xc8', b'\xc9', b'\xca', b'\xcb', b'\xcc', b'\xcd', b'\xce', b'\xcf', b'\xd0', b'\xd1', b'\xd2', b'\xd3', b'\xd4', b'\xd5', b'\xd6', b'\xd7', b'\xd8', b'\xd9', b'\xda', b'\xdb', b'\xdc', b'\xdd', b'\xde', b'\xdf', b'\xe0', b'\xe1', b'\xe2', b'\xe3', b'\xe4', b'\xe5', b'\xe6', b'\xe7', b'\xe8', b'\xe9', b'\xea', b'\xeb', b'\xec', b'\xed', b'\xee', b'\xef', b'\xf0', b'\xf1', b'\xf2', b'\xf3', b'\xf4', b'\xf5', b'\xf6', b'\xf7', b'\xf8', b'\xf9', b'\xfa', b'\xfb', b'\xfc', b'\xfd', b'\xfe', b'\xff']

根据训练语料循环迭代合成子词或者词,最后形成词表,比如“苹果”通过UTF-8进行编码后为“\xe8\x8b\xb9\xe6\x9e\x9c”,如果词表里面有,那“苹果”就通过词表映射成了1个表示,准确来说是1个token;如果词表里没有,那就用256中的“\xe8+\x8b+\xb9+\xe6+\x9e+\x9c”来表示“苹果”这个词,那就是6个token。

在先前的各种分词方法中,如果词典里没有”苹果“这个词,也没有”苹“,”果“这样的子词的话,那就变成了[UNK]。所以在现在的大模型中,以Byte-level BPE(BBPE)这种方式进行分词是不会出现OOV,但词表中如果没有word级别的词的话,一些中英文就会分词分的很细碎,比如Llama在中文上就会把一些词分成多个token,其实就是UTF-8后的中文编码,对编码效率以及语义会有影响,于是出现了一些扩充Llama中文词表的工作。

上述分词算法在工程上实现一般使用sentencpiece工具包,谷歌在这个包中实现了上述的一系列算法,扩充Llama中文词表的工作也都是在此上面实现的。


总结

三种分词方法的不同主要在于 初始化词表 和 合并子词对 的方式,理解每一种分词能够更深刻理解不同分词方法在不同任务和模型中的表现。

标签:BPE,Encoding,##,词表,split,pair,word,Byte,分词
From: https://blog.csdn.net/m0_68116052/article/details/143781275

相关文章

  • Go - JSON Encoding Nuances
     ......
  • mysql 导入SQL文件报错, Specified key was too long; max key length is 767 bytes
    【方案1】一、my.ini文件加入配置,然后重启mysql服务innodb_large_prefix=1二、mysql登陆运行命令登录自己的mysql的方法:1.在D:\ProgramFiles\MySQL\MySQLServer5.7\bin路径下运行命令行,2.登录:mysql-h127.0.0.1-uroot-p然后输入密码3.切换到指定数据库  use数......
  • Word2Vec,此向量维度,以及训练数据集单条数据的大小,举例说明;Skip-gram模型实现词嵌入;热
    目录Word2VecWord2Vec,此向量维度,以及训练数据集单条数据的大小,举例说明一、Word2Vec的词向量维度二、训练数据集单条数据的大小综上所述热编码(One-HotEncoding)和词向量一、表示方式二、维度与计算效率三、语义捕捉能力四、举例说明Skip-gram模型实现词嵌入Skip-g......
  • 深入解析 Transformers 框架(四):Qwen2.5/GPT 分词流程与 BPE 分词算法技术细节详解
    前面我们已经通过三篇文章,详细介绍了Qwen2.5大语言模型在Transformers框架中的技术细节,包括包和对象加载、模型初始化和分词器技术细节:深入解析Transformers框架(一):包和对象加载中的设计巧思与实用技巧深入解析Transformers框架(二):AutoModel初始化及Qwen2.5模型加载全......
  • byte数组转16进制,二进制字符串
    1)16进制字符串a)c#内置apibyte[]bytes=BitConverter.GetBytes(123);varhexStr=BitConverter.ToString(bytes); b)实现1///返回低字节顺序十六进制字符串(低字节在左侧)publicstaticstringToHexString(byte[]bytes){char[]hexChars="012345678......
  • IDEA远程运行使用rsync异常:rsync: connection unexpectedly closed (0 bytes received
    前提:已经使用了Cygwin64且IDEA版本2023、2024均有这个问题发生这个问题可能是使用的是windows自带的C:\Windows\System32\OpenSSH\ssh.exe安装Cygwin64时,需要同时选择安装rsync和ssh。第一次安装没选也没关系,可以再次运行安装程序安装,不用删除上一次的安装。或者使用Cygwin64T......
  • Python的数值与bytes类型
    Python中数值的表示进制表示n=97#十进制表示97n=0b01100001#二进制表示97n=0x61#十六进制表示97n=0o141#八进制表示97上面四种方式定义的值是等价的,均为十进制的97ASCII编码表示n="a"n="\b01100001"n="\x61"n="\o141"上面四种方式定义的值也是等价......
  • Dedecms后台 Fatal error:Allowed memory size of 8388608 bytes 提示的解决方法
    修改 .htaccess 文件在 .htaccess 文件的最上面添加:php_valuemax_execution_time1200php_valuememory_limit200Mphp_valuepost_max_size200Mphp_valueupload_max_filesize200M修改 php.ini 文件将 memory_limit 的值从 8M 改为 12M 或更......
  • 深入浅出之QByteArray
    QByteArray 是Qt框架中的一个类,它提供了字节数组的存储和处理功能。这个类非常有用,因为它允许你以字节为单位存储和操作数据,这在处理二进制数据、网络通信、文件读写等场景中非常常见。以下是一些关于 QByteArray 的关键点和常用操作:一、创建和初始化默认构造函数:创建......
  • 独热编码(One-Hot Encoding)
    一、独热编码出现之前:针对无序离散的分类特征,机器学习算法的分类器并不能直接进行数据处理。因为,分类器通常处理的数据是连续且有序的。但是我们可以对这些离散的特征数据建立映射表来让其有序并且连续起来。例如:针对一个人对象,我们可以假设其属性进行了如下映射。性别特征:["男"......