BERT中tokenizer的char与token的映射

前言

最近在做NER相关任务,数据集是采用start、end的方式。
为了能够找到原文text每个char与分词后token的映射,需要进行一番操作

问题

采用BertTokenizerFast的库函数进行分词
举例:

from transformers import BertTokenizerFast
tokenizer = BertTokenizerFast.from_pretrained('bert-base-chinese',add_special_tokens=True, do_lower_case=True)
text='2018新款ipad保护套2018ipad专用-小女孩和恐龙('
tokens = tokenizer.tokenize(text)
offset_mapping = tokenizer(text, add_special_tokens=False, return_offsets_mapping = True)['offset_mapping']

输出结果

tokens = ['2018', '新', '款', 'ipad', '保', '护', '套', '2018', '##ip', '##ad', '专', '用', '-', '小', '女', '孩', '和', '恐', '龙', '(']
offset_mapping = [(0, 4), (4, 5), (5, 6), (6, 10), (10, 11), (11, 12), (12, 13), (13, 17), (17, 19), (19, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31)]

其中实体有

ipad:[17:21]

可以发现实体ipad被分为”##ip“,"##ad"两个子词。
无法直接对照上去,但是如果对ipad进行分词,发现结果

tokens = tokenizer.tokenize('ipad')
tokens  = ['ipad']

就是单独的ipad会被分词为ipad,但是如果ipad前面如果数字或者字母进行变成两个子词。
所以需要构建char2token的映射

实现

实现映射,需要offset_mapping的帮助,因为offset_mapping中每个元组对应着分词在原文的length index(不过是相对位置),**offset_mapping[-1]【1】**与text的原文长度一致。
基于此,我们可以生成char2token的映射。

第一步:获取文本的长度

text_num = 0
for tok_ind in range(len(offset_mapping) - 1, -1, -1):
    if offset_mapping[tok_ind][1] != 0:
        text_num= offset_mapping[tok_ind][1]
        break
print(text_num)

第二步:建立文本与tokens之间的对应关系

char2tok_span = [[-1, -1] for _ in range(char_num)] # [-1, -1] is whitespace
for tok_ind, char_sp in enumerate(offset_mapping):
    for char_ind in range(char_sp[0], char_sp[1]):
        tok_sp = char2tok_span[char_ind]
        # 因为char to tok 也可能出现1对多的情况,比如韩文。所以char_span的pos1以第一个tok_ind为准,pos2以最后一个tok_ind为准
        if tok_sp[0] == -1:
            tok_sp[0] = tok_ind
        tok_sp[1] = tok_ind + 1

输出

char2tok_span =[[0, 1], [0, 1], [0, 1], [0, 1], [1, 2], [2, 3], [3, 4], [3, 4], [3, 4], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [7, 8], [7, 8], [7, 8], [8, 9], [8, 9], [9, 10], [9, 10], [10, 11], [11, 12], [12, 13], [13, 14], [14, 15], [15, 16], [16, 17], [17, 18], [18, 19], [19, 20]]

根据ipad的index,得知

ipad: start=17: end=21
char2tok_span[17:21] = [[8, 9], [8, 9], [9, 10], [9, 10]]

获取token的index

tokens[8:10] = ['##ip', '##ad']

这样就找到对应实体

你可能感兴趣的:(深度学习,bert,深度学习,token,中文分词,torch)