我正在根据此处提供的实现使用pytorch_pretrained_bert for NER:https://github.com/kamalkraj/BERT-NER
在对较长的文本进行预测时,其中predict
中的bert.py
函数将返回索引不足错误。我目前的理解是,这是由于伯特令牌的nr个(不一定相同,而且实际上在许多情况下与ex。nltk.word_tokenize()
返回的令牌数目不同)导致输入文本。如果超过512(如果我正确理解的话,这是预训练模型的默认长度),它将返回此错误(并且对于较短的输入也适用)。
因此,我决定实施一个包装器,以防万一我拥有的bert令牌(即bert自己的令牌生成器返回的东西)多于512个,则会生成部分重叠的批处理。需要重叠,因为只是将其切成小块,填充最后一个(因为不太可能将我的令牌的nr数除以512,因此最后一个较短的令牌将需要填充)会丢失上下文。但是,这意味着我必须分批进行预测,然后再将它们放回去。
将其切成批次(长度为512)非常简单。我目前正在将预测部分放在一起。 为此,我编写了以下代码:
def map_batches(batches, blen):
master = []
for i in range(len(batches)-1):
appendbatch = []
# if first round, append first bit of current batch with only one prediction
if i == 0:
appendbatch = batches[0][:int(blen/2)-1]
thisbatch = batches[i]
nextbatch = batches[i+1]
offset = int(blen/2)-1 # think -1 is needed for zero offsetting, but not sure, test/debug!
for j in range(offset, blen-offset+2): # and debug +2 here too!
thistuple = thisbatch[j]
nexttuple = nextbatch[j-offset]
if thistuple[1] > nexttuple[1]:
thisbatch[j] = thistuple
else:
thisbatch[j] = nexttuple
appendbatch.extend(thisbatch[int(blen/2)-1:])
# and at end add last bit of last batch with only one prediction
if i == len(batches)-2:
appendbatch.extend(nextbatch[int(blen/2)+1:])
master.append(appendbatch)
return master
blen = 6
a = [('I-LOC', 0.1),
('O', 0.2),
('I-LOC', 0.5),
('O', 0.2),
('I-LOC', 0.9),
('O', 0.1)]
b = [('B-LOC', 0.6),
('O', 0.7),
('I-LOC', 0.8),
('O', 0.9),
('O', 0.99),
('O', 0.99)]
correct = [[('I-LOC', 0.1), ('O', 0.2), ('B-LOC', 0.6), ('O', 0.7), ('I-LOC', 0.9), ('O', 0.9), ('O', 0.99), ('O', 0.99)]]
batches = [a, b]
master = map_batches(batches, blen)
print('mapped: ', master)
print('correct:', correct)
函数map_batches
应该可以解决问题。输入列表仅部分重叠;对于列表a
中的前2个项目,我只有一个预测(本身的预测)。然后对于接下来的6个令牌,我有两个预测,分别对应于a[2:]
和b[:6]
。最后2个,我只有b个。该函数应该将它们放在一起,基于它的可能性最高(在这里,组成数字,在真实代码中,这些当然是标签的置信度得分)。
检查一下我的玩具示例a和b,它是否有效,即上面的输出是:
映射:[[('I-LOC',0.1),('O',0.2),('B-LOC',0.6),('O',0.7),('I-LOC', 0.9),('O',0.9),('O',0.99),('O',0.99)]]
正确:[[('I-LOC',0.1),('O',0.2),('B-LOC',0.6),('O',0.7),('I-LOC', 0.9),('O',0.9),('O',0.99),('O',0.99)]]
很抱歉,很长的介绍。
现在是问题:
首先,将它们放在一起很快。虽然它适用于我的玩具示例,但我有点担心它可能在所有情况下(批次大小不同等)都是不正确的(因此提供了调试/测试的注释),并且我计划进行更多调试。尽管如此,对此问题的任何想法或建议都将受到欢迎!
第二,我猜是一个真正的问题:我怀疑这是一个很早就已经有人解决的问题(可能以一种更好,更有效的方式)。谁会知道在keras,tensorflow,numpy,sklearn或任何其他常见嫌疑犯中对此实现的任何实现吗?