人工智能时代(AI)Machine Learning & Recommendation & NLP & DL

无监督语义相似度匹配之Bert抽取文本特征实战

2020-01-12  本文已影响0人  王同学死磕技术

今天记一次采用bert抽取句子向量的实战过程,主要是想感受一下bert抽取出来的句子特征向量是否真的具有不错的语义表达。

在此之前,我们来回顾一下,如果我们想拿到一个句子的特征向量可以采用什么样的方式(ps 这些下方总结只是笔者在工作中学到的一些文本向量抽取方式,可能并未收集齐全):

基于词袋模型(Bag of Words)
基于无监督神经网络模型
基于文本主题的模型(这个词不是很严谨)

行文至此突然发现gensim真是文本处理这块不可不学习的python包,但更重要的是需要掌握每种方法的原理以及使用场景。由于笔者也在学习过程当中,实战经验也不是很足,还不能够很好的描述各方法的原理和以及了解各方法的适用场景,就不在这误人子弟了。

当然你也可以针对有监督的任务去训练一个词向量,但是由于在工业界,有监督的数据通常很难获取。所以词袋模型,无监督的神经网络模型还有LDA等是经常被采用的文本向量化技术。接下来进入实战部分。

实战部分

准备工作:

import numpy as np
from bert4keras.bert import build_bert_model
from bert4keras.tokenizer import Tokenizer, load_vocab

接下来定义四个函数:

###加载tokenizer
def build_tokenizer(dict_path):
    token_dict = load_vocab(dict_path)
    tokenizer = Tokenizer(token_dict, do_lower_case=True)  
    return tokenizer

###加载 bert 模型
def build_model(config_path,checkpoint_path):
    bert_model = build_bert_model(config_path,checkpoint_path)
    return bert_model

###生成mask矩阵
def generate_mask(sen_list,max_len):
    len_list = [len(i) if len(i)<=max_len else max_len for i in sen_list]
    array_mask = np.array([np.hstack((np.ones(j),np.zeros(max_len-j))) for j in len_list])
    return np.expand_dims(array_mask,axis=2)

###生成句子向量特征
def extract_emb_feature(model,tokenizer,sentences,max_len,mask_if=False):
    mask = generate_mask(sentences,max_len)
    token_ids_list = []
    segment_ids_list = []
    for sen in sentences:
        token_ids, segment_ids = tokenizer.encode(sen,first_length=max_len)
        token_ids_list.append(token_ids)
        segment_ids_list.append(segment_ids)
    result = model.predict([np.array(token_ids_list), np.array(segment_ids_list)])
    if mask_if:
        result = result * mask
    return np.mean(result,axis=1)

你需要定义好预训练模型的词库文件,配置文件,模型参数地址,然后加载bert的tokenizer和模型。

"""
chinese_wwm_L-12_H-768_A-12.zip
    |- bert_model.ckpt      # 模型权重
    |- bert_model.meta      # 模型meta信息
    |- bert_model.index     # 模型index信息
    |- bert_config.json     # 模型参数
    |- vocab.txt            # 词表
"""
dict_path = "./chinese_wwm_L-12_H-768_A-12/ bert_model"
config_path = "./chinese_wwm_L-12_H-768_A-12/bert_config.json"
checkpoint_path = "./chinese_wwm_L-12_H-768_A-12/vocab.txt"

tokenizer = build_tokenizer(dict_path)
model = build_model(config_path,checkpoint_path)

然后笔者用bert提取了"这台苹果真是好用","这颗苹果真是好吃","苹果电脑很好","这台小米真是好用",四句话的文本向量,计算了一下两两句子间的cosine相似度。

sentences = ["这台苹果真是好用","这颗苹果真是好吃","苹果电脑很好","这台小米真是好用"]
sentence_emb = extract_emb_feature(model,tokenizer,sentences,200)
from sklearn.metrics.pairwise import cosine_similarity
cosine_similarity(sentence_emb)

结果如下:我们发现"这台苹果真是好用""这颗苹果真是好吃"的相似度(0.92)就比和"苹果电脑很好"的相似度(0.96)要低。这充分显示出bert对 水果“苹果” 和电脑“苹果”还是做了比较好的区别。

无mask
笔者用mask了padding为0的地方后,句子相识度结果排序变化不大,但是相似度数值之间的差距变大了(方差变大),这说明mask还是对文本向量的提取有比较正向的作用。
sentences = ["这台苹果真是好用","这颗苹果真好吃","苹果电脑很好","这台小米真是好用"]
sentence_emb = extract_emb_feature(model,tokenizer,sentences,200,mask_if=True)
from sklearn.metrics.pairwise import cosine_similarity
cosine_similarity(sentence_emb)
含mask

结语

如果熟悉bert的同学会知道,如下图所示:其实句子向量的第一个token[CLS] 的向量经常代表句子向量取做下游分类任务的finetune,这里为啥笔者未直接使用[CLS]的向量呢,这就是深度学习玄学的部分:具笔者了解[CLS]向量在下游任务做finetuning的时候会比较好的文本向量表示。而直接采用bert做特征抽取时[CLS]向量的威力就没那么强了。至于到底采用何种方式用bert抽取文本句子向量是最好的方式,我也不知道,还是留给大家探索去吧。

bert

参考文献

https://www.lizenghai.com/archives/28761.html
https://github.com/bojone/bert4keras

上一篇 下一篇

猜你喜欢

热点阅读