bert第三篇:tokenizer

本文深入探讨BERT中的tokenizer,包括BasicTokenizer、WordpieceTokenizer和FullTokenizer的作用,强调WordPiece分词概念及其优点。文章还介绍了如何训练自定义的tokenizer,并讨论了BPE算法在解决词汇表外(OOV)问题上的作用。作者建议结合实践理解tokenizer的本质,以实现更高效的信息表示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

tokenizer基本含义

tokenizer就是分词器; 只不过在bert里和我们理解的中文分词不太一样,主要不是分词方法的问题,bert里基本都是最大匹配方法。

最大的不同在于“词”的理解和定义。 比如:中文基本是字为单位。
英文则是subword的概念,例如将"unwanted"分解成[“un”, “##want”, “##ed”] 请仔细理解这个做法的优点。
这是tokenizer的一个要义。

bert里涉及的tokenizer

BasicTokenzer

主要的类是BasicTokenizer,做一些基础的大小写、unicode转换、标点符号分割、小写转换、中文字符分割、去除重音符号等操作,最后返回的是关于词的数组(中文是字的数组)

 def tokenize(self, text):
    """Tokenizes a piece of text."""
    text = convert_to_unicode(text)
    text = self._clean_text(text)

    # This was added on November 1st, 2018 for the multilingual and Chinese
    # models. This is also applied to the English models now, but it doesn't
    # matter since the English models were not trained on any Chinese data
    # and generally don't have any Chinese data in them (there are Chinese
    # characters in the vocabulary because Wikipedia does have some Chinese
    # words in the English Wikipedia.).
    text = self._tokenize_chinese_chars(text)

    orig_tokens = whitespace_tokenize(text)
    split_tokens = []
    for token in orig_tokens:
      if self.do_lower_case:
        token = token.lower()
        token = self._run_strip_accents(token)
      split_tokens.extend(self._run_split_on_punc(token))

    output_tokens = whitespace_tokenize(" ".join(split_tokens))
    return output_tokens

BasicTokenzer是预处理。

wordpiecetokenizer

另外一个则是关键wordpiecetokenizer,就是基于vocab切词。

  def tokenize(self, text):
    """Tokenizes a piece of text into its word pieces.

    This uses a greedy longest-match-first algorithm to perform tokenization
    using the given vocabulary.

    For example:
      input = "unaffable"
      output = ["un", "##aff", "##able"]

    Args:
      text: A single token or whitespace separated tokens. This should have
        already been passed through `BasicTokenizer.

    Returns:
      A list of wordpiece tokens.
    """

    text = convert_to_unicode(text)

    output_tokens = []
    for token in whitespace_tokenize(text):
      chars = list(token)
      if len(chars) > self.max_input_chars_per_word:
        output_tokens.append(self.unk_token)
        continue

      is_bad = False
      start = 0
      sub_tokens = []
      while start < len(chars):
        end = len(chars)
        cur_substr = None

        #找个单词,找不到end向前滑动;还是看代码实在!!!
        while start < end:
          substr = "".join(chars[start:end])
          if start > 0:
            substr = "##" + substr
          if substr in self.vocab:
            cur_substr = substr
            break
          end -= 1
        if cur_substr is None:
          is_bad = True
          break
        sub_tokens.append(cur_substr)
        start = end

      if is_bad:
        output_tokens.append(self.unk_token)
      else:
        output_tokens.extend(sub_tokens)
    return output_tokens

FullTokenzier

这个基本上就是利用basic和wordpiece来切分。用于bert训练的预处理。基本就一个tokenize方法。不会有encode_plus等方法。

PretrainTokenizer

这个则是bert的base类,定义了很多方法(convert_ids_to_tokens)等。 后续的BertTokenzier,GPT2Tokenizer都继承自pretrainTOkenizer,下面的关系图可以看到这个全貌。

关系图

在这里插入图片描述

实操

from transformers.tokenization_bert import BertTokenizer


tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
print("词典大小:",tokenizer.vocab_size)
text = "the game has gone!unaffable  I have a new GPU!"
tokens = tokenizer.tokenize(text)
print("英文分词来一个:",tokens)


text = "我爱北京天安门,吢吣"
tokens = tokenizer.tokenize(text)
print("中文分词来一个:",tokens)

input_ids = tokenizer.convert_tokens_to_ids(tokens)
print("id-token转换:",input_ids)


sen_code = tokenizer.encode_plus("i like  you  much", "but not him")
print("多句子encode:",sen_code)

print("decode:",tokenizer.decode(sen_code['input_ids']))

输出结果:

词典大小: 30522
英文分词来一个: ['the', 'game', 'has', 'gone', '!', 'una', '##ffa', '##ble', 'i', 'have', 'a', 'new', 'gp', '##u', '!']
中文分词来一个: ['我', '[UNK]', '北', '京', '天', '安', '[UNK]', ',', '[UNK]', '[UNK]']
id-token转换: [1855, 100, 1781, 1755, 1811, 1820, 100, 1989, 100, 100]
多句子encode: {'input_ids': [101, 1045, 2066, 2017, 2172, 102, 2021, 2025, 2032, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 1, 1, 1, 1], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}
decode: [CLS] i like you much [SEP] but not him [SEP]

看代码或者实际操练一遍,再来看理论知识更好。实操是关键,是思想的体现。

当然也可以单独实验bertwordpiecetokenzer

from transformers.tokenization_bert import BertWordPieceTokenizer
# initialize tokenizer
tokenizer = BertWordPieceTokenizer(
    vocab_file= "vocab.txt",
    unk_token = "[UNK]",
    sep_token = "[SEP]",
    cls_token = "[CLS]",
    pad_token  = "[PAD]",
    mask_token = "[MASK]",
    clean_text = True,
    handle_chinese_chars = True,
    strip_accents= True,
    lowercase = True,
    wordpieces_prefix = "##"
)


# sample sentence
sentence = "Language is a thing of beauty. But mastering a new language from scratch is quite a daunting prospect."

# tokenize the sample sentence
encoded_output = tokenizer.encode(sentence)
print(encoded_output)
print(encoded_output.tokens)

如何训练

其实就是提取vacab的过程。
BPE算法也比较容易理解:不断的选择most common的加入到词典,为什么? 因为覆盖的语料量比较大。

举个bpe的例子。

原始统计词:
('hug', 10), ('pug', 5), ('pun', 12), ('bun', 4), ('hugs', 5)

开始统计char:
('h' 'u' 'g', 10), ('p' 'u' 'g', 5), ('p' 'u' 'n', 12), ('b' 'u' 'n', 4), ('h' 'u' 'g' 's', 5)

合并最大的ug:

('h' 'ug', 10), ('p' 'ug', 5), ('p' 'u' 'n', 12), ('b' 'u' 'n', 4), ('h' 'ug' 's', 5)

合并最大频度的hug:
 ['b', 'g', 'h', 'n', 'p', 's', 'u', 'ug', 'un', 'hug'] 

最后原始统计词的表示转换为:

('hug', 10), ('p' 'ug', 5), ('p' 'un', 12), ('b' 'un', 4), ('hug' 's', 5)

训练自己中文的tokenizer

def train_cn_tokenizer():
    # ! pip install tokenizers

    from pathlib import Path

    from tokenizers import ByteLevelBPETokenizer

    paths = [str(x) for x in Path("zho-cn_web_2015_10K").glob("**/*.txt")]

    # Initialize a tokenizer
    tokenizer = ByteLevelBPETokenizer()

    # Customize training
    tokenizer.train(files=paths, vocab_size=52_000, min_frequency=3, special_tokens=[
        "<s>",
        "<pad>",
        "</s>",
        "<unk>",
        "<mask>",
    ])

    # Save files to disk
    tokenizer.save( ".","zh-tokenizer-train")

我强烈建议,根据自己的业务定制自己的vocab,当然要配套模型。
最后的结果

{"<s>":0,"<pad>":1,"</s>":2,"<unk>":3,"<mask>":4,"!":5,"\"":6,"#":7,"$":8,"%":9,"&":10,"'":11,"(":12,")":13,"*":14,"+":15,",":16,"-":17,".":18,"/":19,"0":20,"1":21,"2":22,"3":23,"4":24,"5":25,"6":26,"7":27,"8":28,"9":29,":":30,";":31,"<":32,"=":33,">":34,"?":35,"@":36,"A":37,"B":38,"C":39,"D":40,"E":41,"F":42,"G":43,"H":44,"I":45,"J":46,"K":47,"L":48,"M":49,"N":50,"O":51,"P":52,"Q":53,"R":54,"S":55,"T":56,"U":57,"V":58,"W":59,"X":60,"Y":61,"Z":62,"[":63,"\\":64,"]":65,"^":66,"_":67,"`":68,"a":69,"b":70,"c":71,"d":72,"e":73,"f":74,"g":75,"h":76,"i":77,"j":78,"k":79,"l":80,"m":81,"n":82,"o":83,"p":84,"q":85,"r":86,"s":87,"t":88,"u":89,"v":90,"w":91,"x":92,"y":93,"z":94,"{":95,"|":96,"}":97,"~":98,"¡":99,"¢":100,"£":101,"¤":102,"¥":103,"¦":104,"§":105,"¨":106,"©":107,"ª":108,"«":109,"¬":110,"®":111,"¯":112,"°":113,"±":114,"²":115,"³":116,"´":117,"µ":118,"¶":119,"·":120,"¸":121,"¹":122,"º":123,"»":124,"¼":125,"½":126,"¾":127,"¿":128,"À":129,"Á":130,"Â":131,"Ã":132,"Ä":133,"Å":134,"Æ":135,
...

总结

  1. 理论结合实践,敲代码仔细深度理解。
  2. tokenzier的本质是分词,提取有意义的wordpiece,又尽可能的少,用尽量少的信息单元来描述无限的组合。
  3. 几个类的集成理清楚。
  4. 里面的细节可以继续阅读原始类来继续跟进。
  5. wordpiece是比word更小的概念,有何好处? 能解决oov吗。 需要再次思考。

引用

  1. https://albertauyeung.github.io/2020/06/19/bert-tokenization.html
  2. https://spacy.io/usage/spacy-101
  3. https://huggingface.co/transformers/main_classes/tokenizer.html#transformers.PreTrainedTokenizer
  4. https://zhuanlan.zhihu.com/p/160813500
  5. https://github.com/google/sentencepiece
  6. https://huggingface.co/transformers/tokenizer_summary.html
  7. https://huggingface.co/blog/how-to-train
<think>好的,用户问的是“bert_tokenizer是什么意思”。首先,我需要确定用户对这个概念了解的程度。BERT是自然语言处理中的一个重要模型,而tokenizer是处理文本的重要步骤。可能用户是刚开始学习NLP,或者在使用BERT模型时遇到了相关的问题,需要了解tokenizer的作用。 首先,我应该解释清楚BERT是什么,这样用户能有一个背景理解。BERT是由Google开发的预训练模型,基于Transformer架构,广泛应用于各种NLP任务。接着,Tokenizer的作用是将原始文本转换成模型能处理的输入形式,比如将句子分成词或子词,并转换为ID。 用户可能不太清楚Tokenizer的具体工作流程,需要详细说明。比如,BERT Tokenizer使用的是WordPiece算法,如何处理未登录词,添加特殊标记如[CLS]、[SEP]等。同时,需要提到输入格式,比如attention mask和segment IDs,这些是BERT模型需要的输入部分。 还要考虑用户可能的实际应用场景。比如在使用Hugging Face的Transformers库时,如何调用BertTokenizer,给出一个简单的代码示例会让用户更容易理解。另外,常见的注意事项,比如大小写敏感、最大序列长度限制,这些在实际使用中可能会遇到的问题,也应该提到。 需要判断用户是否了解子词切分(subword tokenization)的概念。如果用户是初学者,可能需要更基础的例子,比如将“unhappy”分成“un”和“happy”。但可能用户已经有一定的基础,所以简要说明即可。 最后,要确保回答结构清晰,分点说明,让用户能够逐步理解。同时避免使用过于专业的术语,或者在使用时给出解释,确保内容易于理解。可能用户的需求是实际应用中的问题,所以强调TokenizerBERT中的重要性,以及正确使用它的必要性。</think>好的!**BERT Tokenizer** 是与 BERT 模型配套使用的文本分词工具,主要作用是将原始文本拆分成模型能理解的「词片段」(subword units),并将其转换为对应的数字 ID。以下是详细介绍: --- ### **1. 核心功能** - **分词(Tokenization)**: 采用 **WordPiece 算法**,将文本拆分为子词(subword)。例如: - 单词 `"unhappy"` → 拆分为 `["un", "##happy"]` - 中文句子 `"我喜欢自然语言处理"` → 拆分为 `["我", "喜", "欢", "自", "然", "语", "言", "处", "理"]` (注:中文默认按字切分,但也可用分词工具预处理) - **特殊标记(Special Tokens)**: 添加模型所需的控制符号,例如: - `[CLS]`:句子开头,用于分类任务 - `[SEP]`:分隔句子(单句/句对) - `[PAD]`:填充短句至统一长度 - `[UNK]`:未知词占位符 - **映射为数字 ID**: 根据预训练的词表(vocab.txt),将每个词片段转换为对应的数字,供模型计算。 --- ### **2. 输入格式** BERT Tokenizer 输出的结果通常包含三部分(例如在 Hugging Face 库中): - **`input_ids`**:分词后的数字 ID 序列 - **`attention_mask`**:区分有效词与填充符(1=有效,0=填充) - **`token_type_ids`**(可选):区分句对的段落(如第一句全0,第二句全1) --- ### **3. 代码示例(使用 Hugging Face 库)** ```python from transformers import BertTokenizer # 加载预训练的 BERT 分词器 tokenizer = BertTokenizer.from_pretrained("bert-base-uncased") # 对文本进行分词和编码 text = "I love NLP!" encoded = tokenizer(text, padding=True, truncation=True, return_tensors="pt") print(encoded) # 输出类似: # {'input_ids': tensor([[101, 1045, 2293, 17953, 106, 102]]), # 'attention_mask': tensor([[1, 1, 1, 1, 1, 1]]), # 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0]])} ``` --- ### **4. 注意事项** - **大小写敏感**:BERT 分 `bert-base-uncased`(全小写)和 `bert-base-cased`(保留大小写)等版本。 - **最大长度限制**:BERT 通常处理 512 个 token,超长文本需截断。 - **中文处理**:原生 BERT 按字切分,若需词语级分词,需结合其他工具(如 Jieba)。 --- ### **5. 为什么重要?** - BERT 的预训练过程基于特定分词方式,错误的分词会导致模型性能下降。 - 子词分词平衡了词典大小与未登录词(OOV)问题,提升泛化能力。 希望这个解释对你有帮助!如果有具体使用场景的问题,可以进一步讨论~
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值