前言
最近在做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']
这样就找到对应实体
|