文本相似度部分方法介紹及實現

文本相似度任務:

最*接到文本結構化的任務,經過一番實驗發現,可將該任務轉化爲計算標題檢索排序任務,可用文本相似度的方法來做。文本相似度計算可直接根據文本本身計算距離來得到或使用模型將語義向量化後再計算距離得到。

一、根據文本本身計算相似度:

1)餘弦相似度

import numpy as np

from collections import Counter

def cos_sim(str1, str2): # str1,str2是分詞後的標籤列表
    co_str1 = (Counter(str1))
    co_str2 = (Counter(str2))
    p_str1 = []
    p_str2 = []
    for temp in set(str1 + str2):
    p_str1.append(co_str1[temp])
    p_str2.append(co_str2[temp])
    p_str1 = np.array(p_str1)
    p_str2 = np.array(p_str2)
    return p_str1.dot(p_str2) / (np.sqrt(p_str1.dot(p_str1)) * np.sqrt(p_str2.dot(p_str2)))

print(cos_sim('快樂在城市上空飄揚','快樂在鄉鎮上空飄揚'))

0.7777777777777778

 

2)difflib與fuzz均是python自帶標準庫的計算方法

import difflib
from fuzzywuzzy import fuzz

def diff_result(str1, str2):
    return difflib.SequenceMatcher(None, str1, str2).ratio()

print(diff_result('快樂在城市上空飄揚','快樂在鄉鎮上空飄揚'))

0.7777777777777778

def fuzz_result(str1,str2):
    return fuzz.ratio(str1,str2)

print(fuzz.ratio('快樂在城市上空飄揚','快樂在鄉鎮上空飄揚'))

78

 

二、使用深度學習模型計算文本相似度

使用模型進行語義抽取,然後根據各文本的特徵向量算出餘弦距離。

用於語義抽取的模型有很多,Bert及其變種在NLP領域一枝獨秀。SBert是基於Bert改進計算相似度的模型,其結構如下:

SBERT模型使用孿生網絡結構,他的子網絡都是用Bert模型,且兩個BERT模型共享參數。計算句子A與句子B的相似度時,將A、B輸入bert模型,分別得到句子表徵,再計算兩者之間的餘弦相似度。

SBERT相較於BERT而言速度要快很多,經過查找相關資料原因如下:

SBERT一類的方法都可以統稱爲基於表示的BERT方法,然後直接兩兩拼接取CLS向量(或者其他什麼pooling方法)的可以稱爲基於交互的BERT方法。相對於基於交互的BERT方法,基於表示的BERT方法有這麼幾個特點:

1、假設兩個文本的長度分別爲m和n,那麼因爲Self-Attention*似爲*方複雜度,交互方法計算拼接之後的計算代價可以*似看做O((m+n)^2),然後基於表示的方法因爲兩個文檔彼此獨立計算,忽略最後的cosine similarity的話計算代價*似爲O(m^2+n^2),比前者少一些

2、基於交互的BERT方法需要所有文檔都進行兩兩拼接計算,1W個句子就要計算1W X 1W次,就算去重也最多隻能砍掉一半的數量,而且這樣對於IR這樣的任務,計算過程必須全程在線上完成,上線成本極高。而基於表示的方法因爲文檔之間彼此並不耦合,所以只要把1W個句子都計算好,然後兩兩之間計算cosine similarity就可以了,而cosine similarity的計算代價極低,幾乎可以忽略。而且還可以事先在線下算好保存起來,這樣線上只要計算query就可以了(通常比doc短很多),可以大幅降低線上負載

3、這還沒算完——對於召回任務,你可以不用遍歷所有文檔一個一個手動計算,直接使用基於ANN的向量召回引擎(比如faiss)還可以進一步地加快召回計算速度。

實現代碼:

數據訓練
from sentence_transformers import SentenceTransformer, SentencesDataset
from sentence_transformers import InputExample, evaluation, losses
from torch.utils.data import DataLoader
import pandas as pd
from tqdm import tqdm

model = SentenceTransformer('distiluse-base-multilingual-cased')

train_data = pd.read_csv(r"./input/train.csv", sep=",")
train_data.sample(frac=1)
val_data = pd.read_csv(r"./input/dev.csv", sep=",")
val_data.sample(frac=1)
test_data = pd.read_csv(r"./input/test.csv", sep=",")

def get_input():
    train_datas = []
    _y = train_data["label"]
    _s1 = train_data["sentence1"]
    _s2 = train_data["sentence2"]
    for s1, s2, l in tqdm(zip(_s1, _s2, _y)):
        train_datas.append(InputExample(texts=[s1, s2], label=float(l)))
    return train_datas

train_datas = get_input()

def eval_examples():
    sentences1, sentences2, scores = [], [], []
    for s1, s2, l in tqdm(zip(val_data["sentence1"], val_data["sentence2"], val_data["label"])):
        sentences1.append(s1)
        sentences2.append(s2)
        scores.append(float(l))
        return sentences1, sentences2, scores

sentences1, sentences2, scores = eval_examples()


# Define your train dataset, the dataloader and the train loss
train_dataset = SentencesDataset(train_datas, model)
train_dataloader = DataLoader(train_dataset, shuffle=True, batch_size=64)
train_loss = losses.CosineSimilarityLoss(model)

evaluator = evaluation.BinaryClassificationEvaluator(sentences1, sentences2, scores)
# Tune the model
model.fit(train_objectives=[(train_dataloader, train_loss)], epochs=5, warmup_steps=100,
evaluator=evaluator, evaluation_steps=300, output_path='./two_albert_similarity_model')
model.evaluate(evaluator)

# Define your evaluation examples
def test_examples():
    sentences1, sentences2, scores = [], [], []
    for s1, s2, l in tqdm(zip(test_data["sentence1"], test_data["sentence2"], test_data["label"])):
    sentences1.append(s1)
    sentences2.append(s2)
    scores.append(float(l))
    return sentences1, sentences2, scores

sentences1, sentences2, scores = test_examples()

evaluator = evaluation.EmbeddingSimilarityEvaluator(sentences1, sentences2, scores)
print(model.evaluate(evaluator))

evaluator = evaluation.BinaryClassificationEvaluator(sentences1, sentences2, scores)
print(model.evaluate(evaluator))

模型預測

from sentence_transformers import SentenceTransformer, util
#微調後的模型
model = SentenceTransformer('./two_albert_similarity_model')
# model = SentenceTransformer('cyclone/simcse-chinese-roberta-wwm-ext')
emb1 = model.encode("預備黨員")
emb2 = model.encode("五、\t應用系統及數據資源設計方案,(一)\t架構設計,省級架構設計")
emb3 = model.encode("交納黨費")
cos_sim = util.pytorch_cos_sim(emb1, emb2)
cos_sim1 = util.pytorch_cos_sim(emb3, emb2)
print("Cosine-Similarity:", cos_sim,cos_sim1)

 

參考:

https://www.zhihu.com/question/468746817

https://zhuanlan.zhihu.com/p/351678987

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章