BERT實現QA中的問句語義相似度計算


1. BERT 語義相似度

BERT的全稱是Bidirectional Encoder Representation from Transformers,是Google2018年提出的預訓練模型,即雙向Transformer的Encoder,因為decoder是不能獲要預測的信息的。模型的主要創新點都在pre-train方法上,即用了Masked LM和Next Sentence Prediction兩種方法分別捕捉詞語和句子級別的representation。

有一個這樣的場景,QA對話系統,希望能夠在問答庫中找到與用戶問題相似的句子對,然后把答案返回給用戶。這篇就是要解決這個問題的。

關於 BERT 的原理知識請訪問: http://mantchs.com/2019/09/28/NLP/BERT/

下面介紹一個封裝好的 BERT 工具,利用該工具能夠快速的得到詞向量表示。該工具的名稱叫做: bert-as-service,從名稱就可以看出作者是把 BERT 作為一種服務了,只要調用該服務就能夠得到我們想要的向量表示,得到向量以后,就可以通過余弦相似度的計算公式計算向量之間的相似度。

bert-as-service 源碼詳見: https://github.com/hanxiao/bert-as-service

bert-as-service 使用文檔: https://bert-as-service.readthedocs.io/en/latest/index.html

步驟如下:

  1. 安裝 bert-as-service 的服務端和客戶端。
  2. 預訓練 BERT 模型。
  3. 客戶端編寫代碼請求服務端得到句向量。
  4. 句子與句子向量之間計算相似度,並返回 top_k 個結果。

2. 安裝 bert-as-service

  1. 環境要求:

    Python版本 >= 3.5,Tensorflow版本 >= 1.10

    (本人環境,Python = 3.7 Tensorflow = 1.13.1)

  2. 安裝服務端和客戶端

    pip install -U bert-serving-server bert-serving-client
    

3. 啟動 BERT 服務

  1. 下載預訓練模型

    Google AI發布的經過預訓練的BERT模型。這里我們下載 BERT-Base, Chinese,12-layer, 768-hidden, 12-heads, 110M parameters。

    鏈接:

    https://pan.baidu.com/s/1jJudiTj__vbFb0WkEQUxWw

    密碼:mf4p

  2. 啟動服務

    解壓縮后,運行如下命令進行啟動,目錄換成解壓后的路徑。(-num_worker指定使用多少個CPU)

    bert-serving-start -model_dir /Users/mantch/Downloads/chinese_L-12_H-768_A-12 -num_worker=4
    

    運行后會看到如下結果:

        http_max_connect = 10
               http_port = None
            mask_cls_sep = False
          max_batch_size = 256
             max_seq_len = 25
               model_dir = /Users/mantch/Downloads/chinese_L-12_H-768_A-12
    no_position_embeddings = False
        no_special_token = False
              num_worker = 4
           pooling_layer = [-2]
        pooling_strategy = REDUCE_MEAN
                    port = 5555
                port_out = 5556
           prefetch_size = 10
     priority_batch_size = 16
    show_tokens_to_client = False
         tuned_model_dir = None
                 verbose = False
                     xla = False
    

    其中就已經顯示了port = 5555,port_out = 5556 等端口號信息。

    如果顯示以下信息就表示可以使用了

    I:WORKER-0:[__i:gen:559]:ready and listening!
    I:WORKER-3:[__i:gen:559]:ready and listening!
    I:WORKER-1:[__i:gen:559]:ready and listening!
    I:WORKER-2:[__i:gen:559]:ready and listening!
    I:VENTILATOR:[__i:_ru:164]:all set, ready to serve request!
    

4. 相似度計算

  1. 數據集

    我們使用螞蟻金服語義相似度比賽的一份數據集,該數據集分為 4 列,第一列是索引,第二列和第三列是句子,第四列中的 1 表示這兩個句子是同義句,否則表示為 0。

    數據集下載地址: https://www.lanzous.com/ia9dg8b

  2. 編寫代碼

    import pandas as pd
    import numpy as np
    from bert_serving.client import BertClient
    from termcolor import colored
    
    num = 100     # 采樣數
    topk = 5     # 返回 topk 個結果
    
    # 讀取數據集
    sentence_csv = pd.read_csv('atec_nlp_sim_train_all.csv', sep='\t', names=['idx', 's1', 's2', 'label'])
    sentences = sentence_csv['s1'].tolist()[:num]
    print('%d questions loaded, avg.len %d' % (len(sentences), np.mean([len(d) for d in sentences])))
    
    
    with BertClient(port=5555, port_out=5556) as bc:
    
        # 獲取句子向量編碼
        doc_vecs = bc.encode(sentences)
    
        while True:
            query = input(colored('your question:', 'green'))
            query_vec = bc.encode([query])[0]
    
            # 余弦相似度 分數計算。
            # np.linalg.norm 是求取向量的二范數,也就是向量長度。
            score = np.sum(query_vec * doc_vecs, axis=1) / np.linalg.norm(doc_vecs, axis=1)
            
            '''
            		argsort()函數是將x中的元素從小到大排列,提取其對應的index(索引)
            
                [::-1]取從后向前(相反)的元素, 例如[ 1 2 3 4 5 ]
                則輸出為[ 5 4 3 2 1 ]
            '''
            topk_idx = np.argsort(score)[::-1][:topk]
            print('top %d questions similar to "%s"' % (topk, colored(query, 'green')))
            for idx in topk_idx:
                print('> %s\t%s' % (colored('%.1f' % score[idx], 'cyan'), colored(sentences[idx], 'yellow')))
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM