当前位置:   article > 正文

BertTokenizer详解

BertTokenizer详解

Tokenizing:将任意长的文本转化成数字序列,定义为TokenIds.每一个TokenId是word,subword或者character在词汇表里的索引。

Tokenizing的前提必须要有一张词汇表。

词汇表的学习过程:

1.Standardize:滤出标点符号,将所有大写统一为小写。这个步骤属于可选步骤,可以执行,也可以不执行。

2.split:分词

3.最后便是统计所有出现过的字符,并组成一个词汇表。

英语文本的预处理通常是Tokenizing,而Tokenizing的最小维度分为三种——word(单词),wordpiece(单词块),character(字母)。word的Tokenizing很好处理,因为英文文本单词之间有着天然的空格符,完全可以根据空格符进行Tokenizing。character的Tokenizing也同样好处理,只需将文本里所有字母切分开就行了。但是wordpiece的Tokenizing却显得更复杂,却又更实用。这种方式可以将" unintended "分解成 " un "," intend ", "ed",将" actor "分解成" act "和" or "。显然这种Tonkenizing更符合英语文本的语法分析。以wordpiece建立的词汇表,称之为Bert wordpiece vocabulary。


利用tensorflow_text包可以在英文文本集上学习到一张Bert wordpiece vocabulary。

 导包,
from tensorflow_text.tools.wordpiece_vocab import bert_vocab_from_dataset as bert_vocab

  利用如下方法即可实现,

  1. bert_vocab.bert_vocab_from_dataset(
  2. dataset,
  3. **bert_vocab_args
  4. )

 该方法的关键源码部分为

  1. tokenizer = bert_tokenizer.BasicTokenizer(**bert_tokenizer_params)
  2. words_dataset = dataset.map(tokenizer.tokenize)
  3. word_counts = learner.count_words(words_dataset)
  4. vocab = learner.learn(word_counts, vocab_size, reserved_tokens,
  5. **learn_params)
  6. return vocab

 

注意

从数据集里产生wordpiece vocabulary之前,该方法会先通过类对象BasicTokenizer进行分词。我们可以通过使用它,分析一下该对象的分词原理是什么。

 导入包,

from tensorflow_text.python.ops import bert_tokenizer

  使用该对象对英文文本分词,

  1. text_inputs1 = [b'taste the rustisc indiefrost']
  2. text_inputs2 = [b'tastethe rustiscindiefrost']
  3. a = bert_tokenizer.BasicTokenizer(lower_case=False, normalization_form='NFC')
  4. print(a.tokenize(text_inputs1))
  5. print(a.tokenize(text_inputs2))
  6. <tf.RaggedTensor [[b'taste', b'the', b'rustisc', b'indiefrost']]>
  7. <tf.RaggedTensor [[b'tastethe', b'rustiscindiefrost']]>

  观察输出结果可知,对英文文本是以空格符作为分词依据的


   使用该对象对中文文本分词,

  1. text_inputs1 = '明天你有时间吗'
  2. text_inputs2 = '明 天 你 有 时 间 吗'
  3. a = bert_tokenizer.BasicTokenizer(lower_case=False, normalization_form='NFC')
  4. print(a.tokenize(text_inputs1))
  5. for i in a.tokenize(text_inputs1).numpy():
  6. for j in i:
  7. print(j.decode('utf-8'))
  8. print(a.tokenize(text_inputs2).numpy())
  9. for i in a.tokenize(text_inputs2).numpy():
  10. for j in i:
  11. print(j.decode('utf-8'))
  1. <tf.RaggedTensor [[b'\xe6\x98\x8e', b'\xe5\xa4\xa9', b'\xe4\xbd\xa0', b'\xe6\x9c\x89',
  2. b'\xe6\x97\xb6', b'\xe9\x97\xb4', b'\xe5\x90\x97']]>
  3. [[b'\xe6\x98\x8e' b'\xe5\xa4\xa9' b'\xe4\xbd\xa0' b'\xe6\x9c\x89'
  4. b'\xe6\x97\xb6' b'\xe9\x97\xb4' b'\xe5\x90\x97']]

发现对于中文来说,不管字与字之间是否有空格,都会将一句话分解成一个个字。

 

A basic tokenizer that tokenizes using some deterministic rules:
- For most languages, this tokenizer will split on whitespace.
- For Chinese, Japanese, and Korean characters, this tokenizer will split on
    Unicode characters.

学习词汇表,

  1. dataset = tf.data.Dataset.from_tensor_slices(tf.constant(['明天你有时间吗']))
  2. en_vocab = bert_vocab.bert_vocab_from_dataset(
  3. dataset,
  4. vocab_size=2,
  5. # Reserved tokens that must be included in the vocabulary
  6. reserved_tokens=[],
  7. # Arguments for `text.BertTokenizer`
  8. bert_tokenizer_params=dict(lower_case=True),
  9. # Arguments for `wordpiece_vocab.wordpiece_tokenizer_learner_lib.learn`
  10. learn_params={}
  11. )
  12. print(en_vocab)
['你', '吗', '天', '时', '明', '有', '间', '##你', '##吗', '##天', '##时', '##明', '##有', '##间']

 将词汇表写入文件,每一行含一个字。然后以形参的方式传给BertTokenizer对象,

  1. tokenizers = text.BertTokenizer(vocab_lookup_table='vocab.txt')
  2. print(tokenizers.tokenize(['明天你']))
  1. <tf.RaggedTensor [[[5],
  2. [2],
  3. [0]]]>

4e25792ae72548858bb984e44d721084.png 

 输出的维度是三维的,(batch_size, text_length, subword_num),因为中文没有subword的概念,所以subword_num都是为1。

注意:BertTokenizer里的tokenize方法在Tokenizing之前同样调用了BasicTokenizer进行分词处理。上文已经强调了,BasicTokenizer对于中文的分词原则是根据Unicode字符(一个汉字对应一个Unicode字符单位)来进行处理的,也就是说,无需将中文文本的每个字用空格隔开。但在tf.keras.layers.TextVectorization层里需要用空格隔开,因为该层一律是根据空格来进行分词的。

总结:

  1. tensorflow_text对于中文,日文,韩文的分词是根据Unicode,而英文,葡萄牙文之类的是依据空格的。
  2. 在tokenizing之前或者学习wordpiece词汇表之前,首先进行的是分词处理。

加深影响,以英文为例,

  1. tokenizers = text.BertTokenizer(vocab_lookup_table='vocab.txt', lower_case=True)
  2. print(tokenizers.tokenize(['I am unintended']))
  3. print(tokenizers.detokenize(tokenizers.tokenize(['I am unintended'])))
  1. <tf.RaggedTensor [[[3], [4, 5], [0, 1, 2]]]>
  2. <tf.RaggedTensor [[[b'i'],
  3. [b'am'],
  4. [b'unintended']]]>

01d9e2399e0b4c87b69ee45b5261a0bf.png

##代表充当后缀的subword,无##的subword是位于word首部的。

由上面可知,am被分解为a和##m,unintended分解为un,##intend和##ed。

detokenize就是将索引对应的subword在三维数组中沿着-1轴进行拼接。以无##的subword为首,然后向后拼接有##的subword。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/348688
推荐阅读
相关标签
  

闽ICP备14008679号