大模型中常用的分词器Tokenizer学习总结记录与代码实现

news2024/9/29 5:30:45

最近大模型领域内如火如荼,很多企业、个人组织都陆续进入这个领域,笔者最近也是在接触大模型相关的技术领域,本文的主要目的就是想记录总结汇总大模型常用到的分词器算法,总结记录,学习备忘!由于博主本身知识缺乏总结内容难免会有错误,欢迎指正!

 

一、Byte Pair Encoding (BPE)

Byte Pair Encoding (BPE):BPE是一种基于字符级别的分词算法,常用于处理未分词的文本。它从字符级别开始,逐步合并出现频率最高的字符或字符组合,形成更长的词汇。

Byte Pair Encoding (BPE)是一种基于字符级别的无监督分词算法,常用于将未分词的文本划分为更小的单元。BPE算法的原理如下:
初始化:将所有字符作为初始词汇表中的单元。
统计频次:对文本进行统计,记录每个字符或字符组合的频次。
合并:在每次迭代中,选择频次最高的字符或字符组合,将其合并为一个新的单元,并将其作为新的词汇表中的单元。
更新词汇表:更新词汇表,将新合并的单元添加到词汇表中。
重复步骤2和3,直到达到预设的词汇表大小或满足停止条件。

BPE算法的优缺点如下:
优点:
无监督学习:BPE是一种无监督学习算法,不需要人工标注的分词数据即可进行词汇划分。
适应性强:BPE可以根据语料库进行自适应,能够学习到不同语种、领域的词汇特点,适用范围广。
有效处理未登录词:BPE可以将未登录词(Out-of-Vocabulary)分割成较小的子词,从而提高模型对未登录词的处理能力。
缺点:
Out-of-Vocabulary问题:由于BPE算法基于统计,其词汇表是固定大小的,如果遇到未在词汇表中出现的单元,将其分割为子词可能会增加语义上的困惑。
等分割问题:由于BPE算法在合并时选择频次最高的字符或字符组合,可能会导致某些词被过于粗糙地分割,产生一些半词或半短语。
分词效率较低:BPE算法是一个迭代的过程,可能需要大量的计算资源和时间来处理大规模的文本数据。
总之,BPE算法是一种常用的分词算法,能够有效地处理未分词文本,并且无需标注数据。然而,它也存在一些缺点,如Out-of-Vocabulary问题和等分割问题,需要在应用中加以考虑和处理。

demo代码实现如下所示:

from collections import defaultdict

def get_stats(vocab):
    pairs = defaultdict(int)
    for word, freq in vocab.items():
        symbols = word.split()
        for i in range(len(symbols)-1):
            pairs[symbols[i], symbols[i+1]] += freq
    return pairs

def merge_vocab(pair, v_in):
    v_out = {}
    bigram = ' '.join(pair)
    for word in v_in:
        v_out[word] = v_in[word]
        new_word = word.replace(' '.join(pair), bigram)
        if new_word != word:
            v_out[new_word] = v_in[word]
    return v_out

def bpe(text, num_iters):
    vocab = defaultdict(int)
    for word in text:
        vocab[' '.join(word)] += 1
    
    for i in range(num_iters):
        pairs = get_stats(vocab)
        if not pairs:
            break
        best_pair = max(pairs, key=pairs.get)
        vocab = merge_vocab(best_pair, vocab)
        
    return vocab

# 示例用法
text = ['low', 'lower', 'newest', 'widest', 'room', 'rooms']
num_iters = 10

vocab = bpe(text, num_iters)
print(vocab)

结果输出如下所示:

{'l o w': 1, 'l o w e r': 1, 'n e w e s t': 1, 'w i d e s t': 1, 'r o o m': 1, 'r o o m s': 1}

二、WordPiece

WordPiece:WordPiece是一种基于子词级别的分词算法,常用于将文本划分为更小的单元。它通过合并出现频率最高的子词或子词组合,构建词汇表,并将文本划分为这些子词。

WordPiece是一种基于子词级别的分词算法,常用于将文本划分为更小的单元。WordPiece算法的原理如下:
初始化:将每个字符作为初始词汇表中的单元。
统计频次:对语料进行统计,记录每个单词及子词的频次。
合并:在每次迭代中,选择频次最高的相邻两个单元(字符或子词),将其合并为一个新的单元,并将其作为新的词汇表中的单元。
更新词汇表:更新词汇表,将新合并的单元添加到词汇表中。
重复步骤2和3,直到达到预设的词汇表大小或满足停止条件。

WordPiece算法的优缺点如下:
优点:
语言无关性:WordPiece算法是一种通用的分词算法,不依赖于特定的语言或语料库,能够适应不同的语种和领域。
词汇控制:通过合并相邻的单元,WordPiece算法可以根据词汇表的需求,动态控制词汇大小,使得词汇表能够适应不同的任务和数据规模。
有效处理未登录词:WordPiece算法将文本划分为子词,可以有效处理未登录词,并提供更好的语义表示能力。
缺点:
Out-of-Vocabulary问题:由于词汇表是固定大小的,WordPiece算法在遇到未在词汇表中出现的单元时,将其分割为子词可能会导致一些语义上的困惑。
词的不连续性:WordPiece算法将单词分割为子词,可能导致词的不连续性,使得模型需要更长的上下文来理解词的语义。
分割歧义:由于WordPiece算法仅根据频次合并单元,并不能解决所有的分割歧义问题,可能产生一些歧义的分词结果。
总之,WordPiece算法是一种常用的基于子词级别的分词算法,具有语言无关性和词汇控制的优点。然而,它也存在一些缺点,如Out-of-Vocabulary问题和词的不连续性,需要在实际应用中进行一定的处理和权衡。

demo代码实现如下所示:

from collections import defaultdict

def get_stats(vocab):
    pairs = defaultdict(int)
    for word, freq in vocab.items():
        symbols = word.split()
        for i in range(len(symbols)-1):
            pairs[symbols[i], symbols[i+1]] += freq
    return pairs

def merge_vocab(pair, v_in):
    v_out = {}
    bigram = ''.join(pair)
    for word in v_in:
        v_out[word] = v_in[word]
        new_word = word.replace(' '.join(pair), bigram)
        if new_word != word:
            v_out[new_word] = v_in[word]
    return v_out

def wordpiece(text, num_iters):
    vocab = defaultdict(int)
    for word in text:
        vocab[' '.join(list(word))+' </w>'] += 1
    
    for i in range(num_iters):
        pairs = get_stats(vocab)
        if not pairs:
            break
        best_pair = max(pairs, key=pairs.get)
        vocab = merge_vocab(best_pair, vocab)
        
    return vocab

# 示例用法
text = ['low', 'lower', 'newest', 'widest', 'room', 'rooms']
num_iters = 10

vocab = wordpiece(text, num_iters)
print(vocab)

结果输出如下所示:

{'l o w </w>': 1, 'lo w </w>': 1, 'l o w e r </w>': 1, 'lo w e r </w>': 1, 'l o w e r</w>': 1, 'lo w e r</w>': 1, 'l o we r </w>': 1, 'lo we r </w>': 1, 'l o we r</w>': 1, 'lo we r</w>': 1, 'n e w e s t </w>': 1, 'n e we s t </w>': 1, 'w i d e s t </w>': 1, 'r o o m </w>': 1, 'r o o m s </w>': 1}

三、SentencePiece

SentencePiece:SentencePiece是一种基于未分词语料的无监督分词算法,可以用于构建多语言的分词器。它可以通过训练,自动识别出多种语言的词汇,并对文本进行分词。

SentencePiece是一种基于子词级别的分词算法,常用于将文本划分为更小的单元。SentencePiece算法的原理如下:
初始化:将每个字符作为初始词汇表中的单元。
统计频次:对语料进行统计,记录每个单词及子词的频次。
合并:在每次迭代中,选择频次最高的相邻两个单元(字符或子词),将其合并为一个新的单元,并将其作为新的词汇表中的单元。
更新词汇表:更新词汇表,将新合并的单元添加到词汇表中。
重复步骤2和3,直到达到预设的词汇表大小或满足停止条件。

SentencePiece算法的优缺点如下:
优点:
语言无关性:SentencePiece算法不依赖于特定的语言或语料库,可以适应不同的语种和领域。
动态词汇表:通过合并单元,SentencePiece算法可以动态控制词汇大小,使得词汇表能够适应不同的任务和数据规模,能够在大模型中高效地使用。
分词效果好:SentencePiece算法精细地将单词划分为子词,提供了更好的语义表示能力和更好的分词效果。
显著降低Out-of-Vocabulary问题:SentencePiece算法将未登录词分割成子词,显著降低了Out-of-Vocabulary问题的出现,提高了模型的泛化能力。
缺点:
分词复杂性:SentencePiece算法在处理大规模的文本数据时可能需要较长的时间,因为它是一个迭代的过程,并且使用了统计方法进行合并。
模型大小:由于SentencePiece算法通过生成一个大词汇表来表示子词,可能会导致模型的大小增加,占用更多的存储空间。
分词结果不唯一:由于合并过程是基于统计的,SentencePiece算法可能产生多个合理的分词结果,导致分词结果不唯一。
总之,SentencePiece算法是一种常用且效果优秀的基于子词级别的分词算法,具有语言无关性和动态词汇表的优点,能够显著降低Out-of-Vocabulary问题。然而,它也存在一些缺点,如分词复杂性和分词结果的不唯一性,需要在实际应用中进行权衡和处理。

demo代码实现如下所示:

from collections import defaultdict

def train_sentencepiece(corpus, vocab_size, max_iters):
    """
    基于词频统计来迭代地进行符号合并,直到达到指定的词汇表大小或达到最大迭代次数
    """
    vocab = get_vocabulary(corpus)
    for _ in range(max_iters):
        pairs = get_stats(vocab)
        if not pairs:
            break
        best_pair = max(pairs, key=pairs.get)
        vocab = merge_vocab(best_pair, vocab)
        if len(vocab) >= vocab_size:
            break
    return vocab

def get_vocabulary(corpus):
    """
    从语料库中获取初始词汇表
    """
    vocab = defaultdict(int)
    with open(corpus, 'r', encoding='utf-8') as file:
        for line in file:
            line = line.strip()
            tokens = line.split()
            for token in tokens:
                vocab[token] += 1
    return vocab

def get_stats(vocab):
    """
    获取每个字符或字符组合的频
    """
    pairs = defaultdict(int)
    for word, freq in vocab.items():
        symbols = word.split()
        for i in range(len(symbols)-1):
            pairs[symbols[i], symbols[i+1]] += freq
    return pairs

def merge_vocab(pair, vocab):
    """
    根据频次最高的字符组合进行合并
    """
    vocab_out = {}
    bigram = ''.join(pair)
    for word in vocab:
        vocab_out[word] = vocab[word]
        new_word = word.replace(' '.join(pair), bigram)
        if new_word != word:
            vocab_out[new_word] = vocab[word]
    return vocab_out

def encode_sentencepiece(text, vocab):
    """
    将文本编码为SentencePiece标记序列
    """
    encoded_text = []
    for token in text.split():
        if token in vocab:
            encoded_text.extend(vocab[token].split())
        else:
            encoded_text.append(token)
    return encoded_text

def decode_sentencepiece(encoded_text, reverse_vocab):
    """
    将标记序列解码为原始文本
    """
    decoded_text = ' '.join(encoded_text)
    for token, replacement in reverse_vocab.items():
        decoded_text = decoded_text.replace(token, replacement)
    return decoded_text

# 示例用法
corpus = "data.txt"  # 输入语料文件路径
vocab_size = 1000  # 词汇表大小
max_iters = 10  # 最大迭代次数

vocab = train_sentencepiece(corpus, vocab_size, max_iters)

text = "This is a sample sentence to encode with SentencePiece."
encoded_text = encode_sentencepiece(text, vocab)
print(f"Encoded text: {encoded_text}")

reverse_vocab = {v: k for k, v in vocab.items()}
decoded_text = decode_sentencepiece(encoded_text, reverse_vocab)
print(f"Decoded text: {decoded_text}")

上述代码是一个简化的实现,并不完全符合SentencePiece算法的细节和复杂性。如果需要更强大和高效的SentencePiece实现,建议使用现有的开源库,如sentencepiece。下面同样给出实例代码:

import sentencepiece as spm

def train_sentencepiece(corpus, model_prefix, vocab_size):
    spm.SentencePieceTrainer.train(
        input=corpus,
        model_prefix=model_prefix,
        vocab_size=vocab_size
    )

def encode_sentencepiece(model_prefix, text):
    sp = spm.SentencePieceProcessor(model_file=f"{model_prefix}.model")
    encoded_text = sp.encode_as_pieces(text)
    return encoded_text

def decode_sentencepiece(model_prefix, encoded_text):
    sp = spm.SentencePieceProcessor(model_file=f"{model_prefix}.model")
    decoded_text = sp.decode_pieces(encoded_text)
    return decoded_text

# 示例用法
corpus = "data.txt"  # 输入语料文件路径
model_prefix = "spm_model"  # SentencePiece模型前缀
vocab_size = 1000  # 词汇表大小

train_sentencepiece(corpus, model_prefix, vocab_size)

text = "This is a sample sentence to encode with SentencePiece."
encoded_text = encode_sentencepiece(model_prefix, text)
print(f"Encoded text: {encoded_text}")

decoded_text = decode_sentencepiece(model_prefix, encoded_text)
print(f"Decoded text: {decoded_text}")

四、Transformer

Transformer分词(如BERT Tokenizer):Transformer模型中常使用一种称为BERT Tokenizer的分词器。它可以将文本划分为基于词、子词或字符级别的标记,并生成对应的词嵌入。

Transformer是一种基于注意力机制的分词算法,常用于处理序列数据,其中包括自然语言处理任务。Transformer算法的原理如下:
自注意力机制(Self-Attention):Transformer使用自注意力机制来建立词与词之间的关系。对于输入的序列,通过计算每个词与其他词之间的相似度得到注意力权重,然后将注意力权重作用于词向量上,从而关注到序列中重要的关键词。
编码器-解码器结构(Encoder-Decoder Architecture):在机器翻译等任务中,Transformer使用了编码器-解码器结构。编码器将输入序列转换为中间表示,解码器通过自注意力机制和编码器的输出生成目标序列。
多头注意力机制(Multi-Head Attention):为了对不同的词之间的依赖关系进行建模,Transformer使用了多头注意力机制。它将注意力机制应用到多个线性映射的投影中,然后将它们拼接起来并通过一个线性映射得到最终的表示。
位置编码(Positional Encoding):为了处理序列中的位置信息,Transformer引入了位置编码。位置编码是一种对词的位置进行编码的方式,它可以与词向量相加获得每个词的综合表示。

Transformer算法的优缺点如下:
优点:
并行计算:由于Transformer使用自注意力机制,每个词都可以并行计算其与其他词之间的关系,从而加快计算速度,使得Transformer在大规模数据上具有较高的效率。
长距离依赖性:通过自注意力机制,Transformer更好地处理长距离的依赖关系,能够捕捉到更远距离的上下文信息,提供更准确的语义表示能力。
上下文感知:Transformer利用自注意力机制可以对每个词进行不同程度的关注,从而更好地理解上下文的语意。
可解释性:由于Transformer使用自注意力机制,可以直观地观察到哪些词对于当前词最重要,提供了一定的可解释性。
缺点:
训练复杂性:Transformer中包含了大量的参数和复杂的计算过程,导致训练过程相对较慢,尤其在处理大规模数据时需要更多的计算资源。
学习长距离依赖性的挑战:尽管Transformer可以较好地处理长距离的依赖关系,但在某些复杂任务中,仍可能存在对长距离依赖的挑战。
总之,Transformer是一种在大模型中常用的分词算法,通过自注意力机制处理序列数据,具有并行计算、长距离依赖性、上下文感知和可解释性的优点。然而,它也存在训练复杂性和学习长距离依赖性的挑战。

demo代码实现如下所示:

import torch
import torch.nn as nn
import torch.nn.functional as F

class PositionalEncoding(nn.Module):
    def __init__(self, d_model, max_len=5000):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(p=0.1)
        
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0).transpose(0, 1)
        self.register_buffer('pe', pe)
        
    def forward(self, x):
        x = x + self.pe[:x.size(0), :]
        return self.dropout(x)

class TransformerEncoderLayer(nn.Module):
    def __init__(self, d_model, nhead, dim_feedforward=2048, dropout=0.1):
        super(TransformerEncoderLayer, self).__init__()
        self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
        self.linear1 = nn.Linear(d_model, dim_feedforward)
        self.dropout = nn.Dropout(dropout)
        self.linear2 = nn.Linear(dim_feedforward, d_model)
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.dropout1 = nn.Dropout(dropout)
        self.dropout2 = nn.Dropout(dropout)
        
    def forward(self, src, src_mask=None, src_key_padding_mask=None):
        src2 = self.self_attn(src, src, src, attn_mask=src_mask, key_padding_mask=src_key_padding_mask)[0]
        src = src + self.dropout1(src2)
        src = self.norm1(src)
        src2 = self.linear2(self.dropout(F.relu(self.linear1(src))))
        src = src + self.dropout2(src2)
        src = self.norm2(src)
        return src

class TransformerEncoder(nn.Module):
    def __init__(self, encoder_layer, num_layers, norm=None):
        super(TransformerEncoder, self).__init__()
        self.layers = nn.ModuleList([copy.deepcopy(encoder_layer) for _ in range(num_layers)])
        self.num_layers = num_layers
        self.norm = norm
        
    def forward(self, src, mask=None, src_key_padding_mask=None):
        output = src

        for layer in self.layers:
            output = layer(output, src_mask=mask, src_key_padding_mask=src_key_padding_mask)
            
        if self.norm is not None:
            output = self.norm(output)
            
        return output

# 示例用法
d_model = 512  # 词嵌入维度
nhead = 8  # 多头自注意力头数
dim_feedforward = 2048  # 前馈网络隐藏层维度
dropout = 0.1  # 丢弃率
num_layers = 6  # 编码器层数
src = torch.randn(10, 32, d_model)  # 输入张量

pos_encoder = PositionalEncoding(d_model)
encoder_layer = TransformerEncoderLayer(d_model, nhead, dim_feedforward, dropout)
transformer_encoder = TransformerEncoder(encoder_layer, num_layers)

src = pos_encoder(src)
output = transformer_encoder(src)
print(output.shape)

五、Unigram LM

在大模型中,常用的分词算法之一是unigram language model (unigram LM)。unigram LM算法是一种基于统计的单元划分方法,它基于语言模型构建分词规则,其原理如下:
数据预处理:将训练语料库进行预处理,包括分割句子、标记词性等,得到一组训练样本。
统计词频:统计训练样本中的每个词,得到每个词的出现频次。
构建词表:根据词频,将训练样本的词按照频次从大到小进行排序,构建一个词表。
构建分词模型:根据词表中每个词的出现频次,可以计算出每个词在整个语料中出现的概率。这样,将语料库中的文本数据拆分为最有可能的词序列。
分词:对于新的文本输入,利用unigram LM模型根据词的概率选择最可能的划分方式,进行分词操作。

unigram LM算法的优缺点如下所示:
优点:
简单高效:unigram LM算法的实现相对简单,计算效率较高,适合在大规模数据上使用。
数据驱动:unigram LM算法依靠训练语料库中的统计信息进行分词,具备较好的语言模型学习能力。
高度可定制化:通过对训练样本的预处理和统计词频,可以根据需要自定义不同规则,满足特定领域和任务的分词需求。
缺点:
上下文信息缺失:unigram LM算法只考虑了每个词自身的出现概率,缺乏上下文信息,可能导致一些模糊的划分结果。
未登录词问题:unigram LM算法对未登录词(未在训练集中出现的词)处理能力较差,可能无法正确划分未登录词。
歧义问题:某些词在不同的上下文中可能具有不同的含义,unigram LM算法可能无法准确划分。
总之,unigram LM算法是一种简单、高效的基于统计的分词算法,具有数据驱动和高度可定制化的优点。然而,它也存在上下文信息缺失、未登录词问题和歧义问题等缺点,需要在实际应用中进行改进和处理。

demo代码实现如下所示:

import random
from collections import defaultdict

class UnigramLM:
    def __init__(self):
        self.word_counts = defaultdict(int)
        self.total_words = 0

    def train(self, corpus):
        with open(corpus, 'r', encoding='utf-8') as file:
            for line in file:
                line = line.strip()
                tokens = line.split()
                for token in tokens:
                    self.word_counts[token] += 1
                    self.total_words += 1

    def generate_sentence(self, length):
        sentence = []
        for _ in range(length):
            rand_idx = random.randint(0, self.total_words-1)
            for word, count in self.word_counts.items():
                rand_idx -= count
                if rand_idx < 0:
                    sentence.append(word)
                    break
        return ' '.join(sentence)

# 示例用法
corpus = "data.txt"  # 训练语料文件路径
length = 10  # 生成句子的长度

lm = UnigramLM()
lm.train(corpus)
generated_sentence = lm.generate_sentence(length)
print(generated_sentence)

【总结分析】

Byte Pair Encoding (BPE)
优点:
能够处理未分词的文本,且容易实现和使用。
根据语料频率合并字符或字符组合,生成对应的词汇表。
对于语言中的常见词汇有较好的表示能力。
缺点:
没有考虑语义信息,可能造成词汇划分的歧义。
生成的词汇表可能较大,增加了存储和计算资源的需求。

WordPiece:
优点:
可以处理未分词的文本,并根据语料频率合并子词或子词组合。
考虑了语义信息,提供更好的词汇表示能力。
适用于多语言场景。
缺点:
生成的词汇表可能较大,增加了存储和计算资源的需求。

SentencePiece:
优点:
可以通过无监督学习方式构建多语言分词器。
能够根据语料自动学习语言的词汇,并生成对应的分词模型。
缺点:
对于特定语料和应用场景,学习过程可能需要较长的时间和计算资源。
生成的词汇表可能较大,增加了存储和计算资源的需求。

Transformer分词(如BERT Tokenizer):
优点:
基于Transformer模型,能够处理复杂的语言结构和上下文信息。
生成的词嵌入能同时表示词汇和上下文,提高模型对语义和上下文关系的理解能力。
缺点:
比较复杂,处理速度可能较慢。

Unigram LM:
优点:
根据语料中词的频率构建词汇表,能够有效捕捉常见词汇。
计算简单,速度较快。
缺点:
没有考虑语义信息,可能造成词汇划分的歧义。
对于非常见词汇的处理较为困难。
理想的分词器应具备以下特性:
能够处理未分词的文本,可适应不同语料和应用场景。
考虑语义信息,以准确划分词汇边界。
生成的词汇表大小适中,平衡存储和计算资源需求。
处理速度高,满足实时性需求。
跨语言分词能力较好,适用于多语言场景。
可定制和调整,以满足特定任务需求。
良好的稳定性和鲁棒性,能够应对输入中的错误或噪声。

【未来展望】

这里总结分析了已有各种大模型中用到的分词器模型,各种模型都有各自对应的优缺点,那么理想完美的分词器应该具备什么样的特性呢?这里简单展望一二:

一个理想完美的分词器应该具备以下特性:

  1. 对于不同语料和应用场景具有高适应性:

    • 能够处理未分词的文本,适用于不同领域和语言。
    • 能够根据语料自动学习或调整,以适应不同的数据特点。
  2. 准确划分词汇边界:

    • 能够考虑上下文和语法信息,准确区分词汇边界,避免歧义。
    • 能够捕捉词汇中的常见搭配和复合词结构。
  3. 考虑语义和结构信息:

    • 能够对词汇进行更好的语义表示,提供更丰富的语义信息。
    • 能够处理复杂的语言结构,包括长距离依赖关系和上下文依赖。
  4. 词汇表大小适中:

    • 生成的词汇表既能包含常见词汇,又不会过于庞大,以平衡存储和计算资源的需求。
    • 能够处理未登录词,识别和推断未见过的词汇。
  5. 高处理效率:

    • 具备快速的分词速度,以满足实时性要求。
    • 能够在大规模数据上高效训练和推断,充分利用计算资源。
  6. 跨语言分词能力:

    • 能够处理不同语言的分词需求,包括具有不同语法和结构的语言。
  7. 可定制和调整:

    • 允许用户根据特定任务需求进行定制和调整。
    • 提供丰富的参数配置选项,以便满足不同的分词需求。
  8. 鲁棒性和稳定性:

    • 能够应对输入中的错误、噪声和异常情况,具备良好的鲁棒性。
    • 在不同环境和数据分布下保持稳定的性能表现。

这些特性综合考虑了分词器在语义理解、上下文处理、效率和适应性等方面的需求,一个理想完美的分词器应该在这些方面有较好的表现。

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

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

相关文章

巨人互动|Google海外户Google Analytics的优缺点是什么?

Google Analytics是一个由谷歌开发的网站分析工具&#xff0c;旨在帮助网站和移动应用程序运营者收集和分析数据&#xff0c;以更好地了解用户行为和改进业务。虽然Google Analytics具有许多优势&#xff0c;但也存在一些缺点。在本文中&#xff0c;我们将探讨Google Analytics…

cuda安装

1.cuda 11.8网址 CUDA Toolkit 11.8 Downloads | NVIDIA Developer 2.安装 sh cuda_11.8.0_520.61.05_linux.run 直接accept 移动上下到driver上&#xff0c;再enter一下取消driver的更新 更新一下.bashrc&#xff0c;cp -r ~/.bashrc xxx export PATH/usr/local/cuda-11.…

慢慢来,这次一定能搞懂Promise

1.Promise到底是个什么东西&#xff1f; 让我们使用万能console打印一下它的内容。 根据控制控制台的输出可见Promise 是一个构造函数&#xff0c;本身身上有 all、reject、resolve 这几个方法&#xff0c;原型&#xff08;prototype&#xff09;上有 then、catch 等方法。 根…

JVM源码剖析之Java命令行参数全解

最近&#xff0c;有一位网友询问关于Java命令行参数方面的问题&#xff0c;因为在Java中参数有很多种&#xff0c;有不少的读者一直没弄明白&#xff0c;所以特意写下此篇文章。 此篇文章分2大块&#xff0c;第一块是不同参数的解释&#xff0c;第2块就是JVM源码论证&#xff…

理解 Python 的 for 循环

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 在本篇博客中&#xff0c;我们将讨论 Python 中 for 循环的原理。 我们将从一组基本例子和它的语法开始&#xff0c;还将讨论与 for 循环关联的 else 代码块的用处。 然后我们将介绍迭代对象、迭代器和迭代器协议&…

低代码平台——减少开发bug,解放程序员

目录 一、前言 二、低代码提供了哪些功能 三、低代码对程序员的好处 四、小结 一、前言 在计算机语言的世界里&#xff0c;一直存在着一个千奇百怪的生物——bug。bug产生的本质是因为写代码&#xff0c;于是一种真理就诞生了。真理如下&#xff1a;只要写代码&#xff0c;就会…

【多维定向滤波器组和表面波】表面变换:用于高效表示多维 s 的多分辨率变换(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

信创麒麟操作系统卸载docker,并分别用在线、yum、rpm三种方式安装信创的docker

备注&#xff1a;操作前建议对机器打快照备份&#xff0c;或者备份好数据&#xff0c;如未使用&#xff0c;第一次部署的情况可直接操作 一、卸载DataEase自带的docker # 停止服务 service dataease stop# 删除 docker 可执行文件 rm -f /usr/bin/containerd-shim-runc-v2 r…

快速解决IDEA中类的图标变成J,不是C的情况

有时候导入新的项目后&#xff0c;会出现如下情况&#xff0c;类的图标变成J&#xff0c;如图&#xff1a; 直接上解决方法: 找到项目的pom.xml&#xff0c;右键&#xff0c;在靠近最下方的位置找到Add as Maven Project&#xff0c;点击即可。 此时&#xff0c;一般类的图标就…

【springboot项目】在idea中启动报错合集

一、IDEA中报错 “Error running ‘Application‘: Command line is too long.“ 的解决办法 报错详情&#xff1a; Error running Application: Command line is too long.Shorten command line for Application or also for Spring Boot default configuration.报错原因&am…

校对软件助力公安公检:提高调查报告质量

校对软件可以为公安公检机关提供有力支持&#xff0c;帮助提高调查报告的质量。以下是校对软件在这方面的助力&#xff1a; 1.拼写和语法检查&#xff1a;校对软件可以自动检查调查报告中的拼写错误和语法问题。这可以避免由于疏忽或拼写错误而导致的报告不准确或难以理解的情况…

TENNECO EDI 项目——X12与XML之间的转换

近期为了帮助广大用户更好地使用 EDI 系统&#xff0c;我们根据以往的项目实施经验&#xff0c;将成熟的 EDI 项目进行开源。用户安装好知行之桥EDI系统之后&#xff0c;只需要下载我们整理好的示例代码&#xff0c;并放置在知行之桥指定的工作区中&#xff0c;即可开始使用。 …

java Spring Boot yml多环境配置

我们项目 线上和线下 环境配置不是特别一样 例如 运行的URL 数据库地址 数据库的账号密码 这些经常是不一样的 如果每次上线钱改 也不是特别方便 甚至可能忘记 那么 进入我们代码中 所谓的多环境 就是在不同的环境下配置不同的值 终端还是在application配置文件中 多环境的话…

【网络编程】万字详解||一个简单TCP服务器(TCP、线程池、守护进程)源码+介绍

TCP服务器 锁&#xff1a;Lock.hpp代码介绍 守护进程&#xff1a;daemonize.hpp代码说明 日志文件&#xff1a;log.hpp代码说明 任务处理 Task.hpp代码说明 线程池 ThreadPool.hpp代码说明 客户端 TCPClient.cc代码说明 服务器 TCPServer.cc代码说明 头文件包 util.hpp代码 Mak…

【工程实践】使用Roformer-sim(SimBERTv2 )做数据增强

前言 此文仅记录以Roformer-sim为基础模型做数据增强的过程&#xff0c;Roformer-sim模型细节请移步&#xff1a;SimBERTv2来了&#xff01;融合检索和生成的RoFormer-Sim模型 - 科学空间|Scientific Spaces https://github.com/ZhuiyiTechnology/roformer-sim 1.功能介绍 可以…

[LeetCode - Python]344.反转字符串(Easy);345. 反转字符串中的元音字母(Easy);977. 有序数组的平方(Easy)

1.题目 344.反转字符串(Easy) 1.代码 class Solution:def reverseString(self, s: List[str]) -> None:"""Do not return anything, modify s in-place instead."""# 双指针left,right 0, len(s)-1while left < right:temp s[left]s[…

利用PCL实现点云配准

一、介绍 This document demonstrates using the Iterative Closest Point algorithm in your code which can determine if one PointCloud is just a rigid transformation of another by minimizing the distances between the points of two pointclouds and rigidly tran…

Ishikawa

Ishikawa 石川、鱼骨头、因果 其实我压根不知道 Ishikawa 这个日文就是石川&#xff0c;^_^&#xff0c;视乎也没啥影响

fastadmin动态获取单选框选中值修改页面

需求场景&#xff1a; 在编辑页面中&#xff0c;要求要根据某一单选框&#xff08;字段名称popup&#xff09;的选中值&#xff0c;来动态显示或者隐藏某个div&#xff08;idupload_img&#xff09;。 edit: function () {var popVal $("input[typeradio][namerow[popup]…

CNN(四):ResNet与DenseNet结合--DPN

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊|接辅导、项目定制 前面实现了ResNet和DenseNet的算法&#xff0c;了解了它们有各自的特点&#xff1a; ResNet&#xff1a;通过建立前面层与后面层之间的“短路…