Aman4Real - 博客园

BERT实战[1]:利用BERT对语句中[MASK]部分进行预测

找了一下,网上关于利用 BERT 对[MASK]掉的词语的预测比较少,大概总结一下一个小的工作,也算是 BERT 的一个应用。

本文对给定中文文本的名词进行提取,对名词对之间的[MASK]部分(期望是动词),基于 keras_bert 进行预测。keras_bert 是 CyberZHG 大佬封装好了 Keras 版的 BERT,可以直接调用官方发布的预训练权重。

1. 下载 BERT 预训练权重

下载中文模型预训练权重(字级别)

  • BERT-Base, Chinese: Chinese Simplified and Traditional, 12-layer, 768-hidden, 12-heads, 110M parameters
  • 其他版本预训练权重的下载请参考原RepoBERT

2. BERT 运用

A. 文本内容

网上随便找了个名人名言,内容放到.txt文件中,一行是一句话,如下:

生活的全部意义在于无穷地探索尚未知道的东西,在于不断地增加更多的知识。
看书和学习是思想的经常营养,是思想的无穷发展。
我们不需要死读硬记,我们需要用基本的知识来发展和增进每个学习者的思考力。
情况是在不断地变化,要使自己的思想适应新的情况,就得学习。
只要心还在跳,就要努力学习。
立身以立学为先,立学以读书为本。
保持和培养每个学生的自尊心,取决于教师如何看待学生的个人学习成绩。
没有一个大学,是比拥有我们从未使用过的能力的大自我和人类意志与理智所创造的现实,更能包罗万象的了。
学习有如母亲一般慈爱,它用纯洁和温柔的欢乐来哺育孩子,如果向它要求额外的报酬,也许就是罪过。
伟大的成绩和辛勤劳动是成正比例的,有一分劳动就有一分收获,日积月累,从少到多,奇迹就可以创造出来。
学习专看文学书,也是不好的。先前的文学青年,往往厌恶数学、理化、史地、生物学,以为这些都无足轻重,后来变成连常识也没有。
学问是异常珍贵的东西,从任何源泉吸收都不可耻。
必须记住我们学习的时间有限的。时间有限,不只由于人生短促,更由于人事纷繁。
三人行,必有我师焉。择其善者而从之,其不善者而改之。
理想的书籍是智慧的钥匙。
游手好闲的学习并不比学习游手好闲好。
学习永远不晚。
春庭早色和烟暖;午夜书声带月寒。
熟读唐诗三百首,不会作诗也会吟。
在寻求真理的长河中,唯有学习,不断地学习,勤奋地学习,有创造性地学习,才能越重山跨峻岭。
非淡泊无以明志,非宁静无以致远。
倘能生存,我当然仍要学习。
聪明的人有长的耳朵和短的舌头。
惜时专心苦读是做学问的一个好方法。
人做了书的奴隶,便把活人带死了。把书作为人的工具,则书本上的知识便活了。有了生命力了。
对所学知识内容的兴趣可能成为学习动机。
研卷知古今;藏书教子孙。
只要愿意学习,就一定能够学会。

B. 词对提取

运用了 哈工大LTP工具 对每句话进行词性标注,然后把每句话中的名词对提取出来。

from ltp import LTP
import numpy

rfile = "content.txt"
wfile = "nouns.txt"
ltp = LTP()

def rnwfile(file):
    file_data = ""
    with open(file, "r", encoding="utf-8") as f:
        for line in f:
        	line_data = ""
        	seg, hidden = ltp.seg([line])
        	pos = ltp.pos(hidden)
        	seg = sum(seg, []) # 二维列表转一维
        	pos = sum(pos, [])
        	# print(seg, pos)
        	for i in range(len(pos)):
        		if pos[i] == 'n': # 提取出名词
        			line_data += seg[i] + "/"
        	line_data = line_data[:-1] # 去除最后的/
        	# print(line_data)
        	if "/" not in line_data:
        		continue
        	else:
        		# print(line_data)
        		tmp = line_data.split("/")
        		tmp = list(set(tmp)) # 去重
        		if len(tmp) <= 1:
        			continue
        		file_data += '/'.join(tmp) + '\n'
    with open(wfile,"w",encoding="utf-8") as f:
        f.write(file_data)
 
rnwfile(rfile)

去掉每句话中重复的名词,排除掉一些只有一个名词的句子。名词放到nouns.txt文件中。大致格式如下:

障碍/光明/境界/知识/灯烛/人生
责任/学生/道/先生/人/人生
花木/天赋/自然
学校/结果/一生/学生
阳光/意境
上下/修道/路
钻劲/挤劲/精神/好处/钉子
一生/精力
东西/诀窍
生命/友谊/人生
朋友/外语
德/世家/人品/天地
思子/旧书
人/知识
时间/时候/精神
伴侣/老师/朋友/书/人/安慰者
人民/大志/雄心/国家/人才/目标/青年人
挚情/朋友/方法/人/心
思想/社会主义/青年/社会/部分/时代/生气/力量

C. [MASK] 部分预测

下面用keras_bert[MASK]部分进行预测。要加载的中文模型预训练权重放在./models文件夹下。

import sys
import codecs
import numpy as np
from keras_bert import load_trained_model_from_checkpoint, Tokenizer

config_path, checkpoint_path, dict_path ='./models/chinese_L-12_H-768_A-12/bert_config.json',\
'./models/chinese_L-12_H-768_A-12/bert_model.ckpt','./models/chinese_L-12_H-768_A-12/vocab.txt'

model = load_trained_model_from_checkpoint(config_path, checkpoint_path, training=True)

# 读取词典
token_dict = {}
with codecs.open(dict_path, 'r', 'utf8') as reader:
    for line in reader:
        token = line.strip()
        token_dict[token] = len(token_dict)
token_dict_inv = {v: k for k, v in token_dict.items()}

tokenizer = Tokenizer(token_dict)


wordfile = "nouns.txt"
wfile = "OUT.txt"
rowtag = 0
count = len(open(wordfile,'r').readlines())
with open(wordfile, "r", encoding="utf-8") as f:
		write_data = ""
		for line in f:
			rowtag += 1
			line_nouns = line.split('/')
			nounsnum = len(line_nouns)
			write_data += str(rowtag) + ":\n"
			for i in range(nounsnum-1):
				text = line_nouns[i] + "##了" + line_nouns[i+1].strip('\n')
				test_str = "##"
				pos = text.index(test_str)
				index = [_+pos+1 for _ in range(len(test_str))]
				# print("index:",index)

				tokens = tokenizer.tokenize(text)
				for s in index:
				    tokens[s] = '[MASK]'

				indices = np.array([[token_dict[token] for token in tokens] + [0] * (512 - len(tokens))])
				segments = np.array([[0] * len(tokens) + [0] * (512 - len(tokens))])
				masks = np.array([[0]*index[0] + [1, 1] + [0] * (512 - index[0]-2)])

				top_k = 5 # 取前k个
				# predicts = model.predict([indices, segments, masks])[0].argmax(axis=-1).tolist() # 取最大的值
				predicts = model.predict([indices, segments, masks])[0] # (1,512,21128)
				idxlist = np.argsort(-predicts, axis=-1) # (1,512,21128)
				# 加负号即从大到小排列
				top_k_pred = idxlist[:,:,:top_k]
				
				write_data += text + ": "
				for k in range(top_k):
					pred_k = top_k_pred[:,:,k]
					# print(list(map(lambda x: token_dict_inv[x], predicts[0][:])))
					# predtext = list(map(lambda x: token_dict_inv[x], predicts[0][index[0]:index[0]+len(test_str)]))
					predtext = list(map(lambda x: token_dict_inv[x], pred_k[0][index[0]:index[0]+len(test_str)]))
					# print('Fill with: ', predtext)
					write_data += '<' + "".join(predtext) + '> '
				write_data += '\n'
			print("%d/%d"%(rowtag, count))

with open(wfile,"w",encoding="utf-8") as f:
        f.write(write_data)

修改text的值就可以预测不同句式不同词语,而test_str[MASK]掉的词语。例如text = '巴黎是#国的首都' test_str = '#'的话,BERT很容易预测出来#部分该填的为

BERT预测的前5个值进行了输出,也可以根据注释只输出预测的最可能的结果,或者自定义输出前k个。

3. 结果

结果大概就像这个样子,但由于是character-based,所以有的输出也不太像词语,不过就当是BERT的一个应用实战,也无所谓了。

25:
障碍##了光明: <变来> <挡成> <发去> <阻入> <开灭> 
光明##了境界: <就到> <达成> <成入> <是有> <,高> 
境界##了知识: <决成> <就定> <成就> <超为> <变生> 
知识##了灯烛: <变成> <点亮> <燃燃> <照上> <就为> 
灯烛##了人生: <照亮> <点响> <影醒> <代燃> <,动> 
26:
责任##了学生: <推给> <超到> <压定> <落不> <加在> 
学生##了道: <们笑> <都开> <说怒> <吃哭> <回说> 
道##了先生: <士殺> <子杀> <長救> <长見> <人见> 
先生##了人: <,死> <是殺> <去不> <回去> <嫁見> 
人##了人生: <生有> <,為> <间为> <不成> <就不> 
27:
花木##了天赋: <失有> <充来> <展成> <提出> <开现> 
天赋##了自然: <超越> <改变> <决代> <变成> <代就> 
28:
学校##了结果: <公布> <宣出> <通认> <报报> <给到> 
结果##了一生: <他死> <她错> <我误> <,变> <失活> 
一生##了学生: <中为> <变不> <都成> <,上> <成有> 
29:
阳光##了意境: <赋满> <充添> <带成> <增来> <融出> 
30:
上下##了修道: <都为> <皆行> <进成> <共同> <同有> 
修道##了路: <士走> <院修> <者断> <人开> <会毁> 
31:
钻劲##了挤劲: <变成> <,多> <挤少> <缩大> <又去> 
挤劲##了精神: <压没> <,不> <刺有> <挤出> <打来> 
精神##了好处: <上到> <带有> <得来> <也得> <病多> 
好处##了钉子: <:少> <是多> <没除> <多有> <就成> 
32:
一生##了精力: <耗去> <失尽> <消费> <充满> <丧失> 
33:
东西##了诀窍: <变有> <找成> <都失> <失到> <成现> 
34:
生命##了友谊: <充满> <变成> <赋上> <结有> <缺为> 
友谊##了人生: <改就> <决变> <变定> <影造> <包响> 
35:
朋友##了外语: <学会> <教上> <开成> <选习> <变学> 

参考:[1] BERT实战——基于Keras

posted @ 2022-05-27 19:37  aman4real  阅读(124)  评论(0编辑  收藏  举报
This blog has running: