Loading

Bert 学习

熟悉Bert之路

Bert 源码

Bert的核心内容

核心内容 Q&A

BERT 是如何分词的

参考链接

  1. tokenization.py 就是预处理进行分词的程序,主要有两个分词器:BasicTokenizer 和 WordpieceTokenizer,另外一个 FullTokenizer 是这两个的结合:先进行 BasicTokenizer 得到一个分得比较粗的 token 列表,然后再对每个 token 进行一次 WordpieceTokenizer,得到最终的分词结果。

例子:example = "Keras是ONEIROS(Open-ended Neuro-Electronic Intelligent Robot Operating System,开放式神经电子智能机器人操作系统)项目研究工作的部分产物[3],主要作者和维护者是Google工程师François Chollet。\r\n"

对于中文来说,一句话概括:BERT 采取的是「分字」,即每一个汉字都切开。

2. BasicTokenizer

  • BasicTokenizer(以下简称 BT)是一个初步的分词器。对于一个待分词字符串,流程大致就是转成 unicode -> 去除各种奇怪字符 -> 处理中文 -> 空格分词 -> 去除多余字符和标点分词 -> 再次空格分词,结束。
  • 转成 unicode convert_to_unicode(text):经过这步后,example 和原来相同
  • 去除各种奇怪字符 _clean_text(text):经过这步后,example 中的 \r\n 被替换成两个空格
>>> example = _clean_text(example)
>>> example
'Keras是ONEIROS(Open-ended Neuro-Electronic Intelligent Robot Operating System,开放式神经电子智能机器人操作系统)项目研究工作的部分产物[3],主要作者和维护者是Google工程师François Chollet。  '
  • 处理中文,BT类中的_tokenize_chinese_chars(text):对于 text 中的字符,首先判断其是不是「中文字符」_is_chinese_char(cp) ,是的话在其前后加上一个空格,否则原样输出。经过这步后,中文被按字分开,用空格分隔,但英文数字等仍然保持原状:
>>> example = _tokenize_chinese_chars(example)
>>> example
'Keras 是 ONEIROS(Open-ended Neuro-Electronic Intelligent Robot Operating System, 开  放  式  神  经  电  子  智  能  机  器  人  操  作  系  统 ) 项  目  研  究  工  作  的  部  分  产  物 [3], 主  要  作  者  和  维  护  者  是 Google 工  程  师 François Chollet。  '
  • 空格分词, whitespace_tokenize(text),首先对 text 进行 strip() 操作,去掉两边多余空白字符,然后如果剩下的是一个空字符串,则直接返回空列表,否则进行 split() 操作,得到最初的分词结果 orig_tokens。经过这步后,example 变成一个列表:
>>> example = whitespace_tokenize(example)
>>> example
['Keras', '是', 'ONEIROS(Open-ended', 'Neuro-Electronic', 'Intelligent', 'Robot', 'Operating', 'System,', '开', '放', '式', '神', '经', '电', '子', '智', '能', '机', '器', '人', '操', '作', '系', '统', ')', '项', '目', '研', '究', '工', '作', '的', '部', '分', '产', '物', '[3],', '主', '要', '作', '者', '和', '维', '护', '者', '是', 'Google', '工', '程', '师', 'François', 'Chollet。']
  • 去除多余字符和标点分词,_run_strip_accents(text) 方法用于去除 accents,即变音符号,eg,résumé 变成 resume,á 变成 a;_run_split_on_punc(text) 是标点分词,按照标点符号分词,针对上一步空格分词后的每个 token 。
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))
>>> example
['keras', '是', 'oneiros', '(', 'open', '-', 'ended', 'neuro', '-', 'electronic', 'intelligent', 'robot', 'operating', 'system', ',', '开', '放', '式', '神', '经', '电', '子', '智', '能', '机', '器', '人', '操', '作', '系', '统', ')', '项', '目', '研', '究', '工', '作', '的', '部', '分', '产', '物', '[', '3', ']', ',', '主', '要', '作', '者', '和', '维', '护', '者', '是', 'google', '工', '程', '师', 'francois', 'chollet', '。']

  • 再次空格分词: whitespace_tokenize, 先用标准空格拼接上一步的处理结果,再执行空格分词。(去掉连续空格)经过这步后,和上步结果一样:
>>> example
['keras', '是', 'oneiros', '(', 'open', '-', 'ended', 'neuro', '-', 'electronic', 'intelligent', 'robot', 'operating', 'system', ',', '开', '放', '式', '神', '经', '电', '子', '智', '能', '机', '器', '人', '操', '作', '系', '统', ')', '项', '目', '研', '究', '工', '作', '的', '部', '分', '产', '物', '[', '3', ']', ',', '主', '要', '作', '者', '和', '维', '护', '者', '是', 'google', '工', '程', '师', 'francois', 'chollet', '。']

这就是 BT 最终的输出了

3. WordpieceTokenizer(WPT)

  • WordpieceTokenizer(以下简称 WPT)是在 BT 结果的基础上进行再一次切分,得到子词(subword,以 ## 开头),词汇表就是在此时引入的。该类只有两个方法:一个初始化方法 init(self, vocab, unk_token="[UNK]", max_input_chars_per_word=200),一个分词方法 tokenize(self, text)。

对于中文来说,使不使用 WPT 都一样,因为中文经过 BasicTokenizer 后已经变成一个字一个字了,没法再「子」了 ?

  • __init__(self, vocab, unk_token="[UNK]", max_input_chars_per_word=200):vocab 就是词汇表,collections.OrderedDict() 类型,由 load_vocab(vocab_file) 读入,key 为词汇,value 为对应索引,顺序依照 vocab_file 中的顺序。有一点需要注意的是,词汇表中已包含所有可能的子词。unk_token 为未登录词的标记,默认为 [UNK]。max_input_chars_per_word 为单个词的最大长度,如果一个词的长度超过这个最大长度,那么直接将其设为 unk_token。
  • tokenize(self, text):该方法就是主要的分词方法了,大致分词思路是按照从左到右的顺序,将一个词拆分成多个子词,每个子词尽可能长。

将 BT 的结果输入给 WPT,那么 example 的最终分词结果就是:

['keras', '是', 'one', '##iros', '(', 'open', '-', 'ended', 'neu', '##ro', '-', 'electronic', 'intelligent', 'robot', 'operating', 'system', ',', '开', '放', '式', '神', '经', '电', '子', '智', '能', '机', '器', '人', '操', '作', '系', '统', ')', '项', '目', '研', '究', '工', '作', '的', '部', '分', '产', '物', '[', '3', ']', ',', '主', '要', '作', '者', '和', '维', '护', '者', '是', 'google', '工', '程', '师', 'franco', '##is', 'cho', '##llet', '。']

至此,BERT 分词部分结束。

BERT 是如何构建模型的

BERT 是如何构建模型的

  • BERT 的大致流程就是:引入配置 BertConfig -> 定义初始化输入大小等常量 -> 对输入进行初步 embedding -> 加入 token type embedding 和 position embedding -> 创建 encoder 获取输出 -> 获取 pooled 输出,就是最终输出了,在 run_classifier.py 中会将此输出接上一个 Dropout,然后接上一个 softmax 分类层。
posted @ 2020-11-12 11:24  戴墨镜的长颈鹿  阅读(302)  评论(0编辑  收藏  举报