目錄
一、文本匹配任務的定義
1.狹義解釋
2.廣義解釋
二、文本匹配的應用
1.問答對話
2.信息檢索
3.文本匹配任務應用
三、智能問答
1.智能問答的基本思路
依照基礎資源劃分:
依照答案產出方式劃分
依照NLP相關技術劃分
四、智能問答的價值
1.智能客服
2.Faq知識庫問答
總結
3.相關名詞
① 問答對
② faq庫 / 知識庫
③ 標準問
④ 相似問/擴展問
⑤ 用戶問
⑥ 知識加工
4.Faq知識庫示例
5.運行邏輯
6.算法核心
五、文本匹配算法Ⅰ?—— 編輯距離
🚀 代碼實現
思路與算法
1.初始化矩陣:
2.填充矩陣:
3.計算相似度:
優缺分析
優點:
缺點:
六、文本匹配算法 Ⅱ —— Jaccard相似度
代碼實現
優缺分析
優點:
缺點:
適用場景:
七、文本匹配算法 Ⅲ —— BM25算法
TF·IDF算法
BM25算法
詞的重要性(IDF)
詞與文檔的相關性(TF)
詞與查詢的相關性(可選)
參數設置
優缺分析
優點:
缺點:
代碼實現
Ⅰ、超參數定義
Ⅱ、初始化方法
Ⅲ、根據語料庫構建倒排索引并計算每個詞的IDF值
Ⅳ、計算文檔集合中平均每篇文檔的詞數
Ⅴ、計算查詢query 與 某篇文檔的相關性得分?
Ⅵ、計算查詢與所有文檔的相關性得分
Ⅶ、完整的BM25算法實現?
八、文本匹配算法 Ⅳ —— word2vec
1.什么是向量
2.什么是詞向量? ?
3.詞向量的特點
4.詞向量是如何尋得到的
5.如何訓練/訓練目標
6.訓練提速技巧
7.如何用于文本匹配
8.優缺分析
優點:
缺點:
后來時間平靜又強大,替我揭穿和篩選
????????????????????????????????????????????????????????—— 25.1.29
一、文本匹配任務的定義
1.狹義解釋
給定一組文本,判斷其語義是否相似
相似的:今天天氣不錯 match 今兒個天不錯呀
非相似:今天天氣不錯 match 你的代碼有BUG
以分值形式給出相似度
今天天氣不錯 ? match ?今兒個天不錯呀 ? 0.9
今天天氣不錯 ? match ?這幾天天氣不錯 ? 0.7
今天天氣不錯 ? match ?你的代碼有bug ? ?0.1?
2.廣義解釋
給定一組文本,計算某種自定義的關聯度
① Natural Language Inference 自然語言推理
兩句話判斷是否有關聯、矛盾、中立關系
eg:明天要下雨 vs 明天大晴天
② Text Entailment 文本內容
給出一段文本,和一個假設,判斷文本是否能支持或反駁這個假設
主題判斷、文章標題匹配內容等
二、文本匹配的應用
1.問答對話
智能客服、車載導航、手機助手、聊天機器人、智能音箱
2.信息檢索
各種?APP / 網頁 的搜索功能
3.文本匹配任務應用
短文本 vs 短文本:知識庫問答 ,聊天機器人等
短文本 vs?長文本:文章檢索,廣告推薦等
長文本?vs?長文本:新聞、文章的關聯推薦等
三、智能問答
1.智能問答的基本思路
① 基礎資源:包括faq庫,書籍文檔,網頁,知識圖譜等等
② 問答系統:對基礎資源進行了加工處理,形成問答所需要的索引和模型等
③ 用戶輸入問題
④ 回答系統給出答案
依照基礎資源劃分:
1)基于faq(熱點問題)知識庫的問答【以文本匹配方式為主】
2)基于文檔 / 網頁 / 書籍的問答【大模型RAG的方式進行問答】??
3)基于圖像/視頻的問答【基于多模態模型的問答】
4)基于知識圖譜的問答
5)基于表格的問答 ?
6)基于特定領域知識的問答 ?
7)基于人工規則的問答 ? …
依照答案產出方式劃分
1)檢索式的回答
答案原文或答案的多個片段存在于基礎資源中(答案是事先準備好的)
2)生成式的問答
答案文本不存在于基礎資源,由問答系統來生成答案(由系統模型生成)
3)二者結合
依照NLP相關技術劃分
1)單輪問答(一問一答)
2)多輪問答(關聯多輪問答信息)??
3)多語種問答(多種語言)??
4)事實性問答(明確存在唯一正確的答案,用檢索式方式回答較多)??
5)開放性問答(有不同的看法,用生成式方式回答較多)
6)多模態問答(與圖像、視頻相結合)??
7)選擇型問答(在幾個答案中選取一個)??
8)抽取式問答 ?
9)生成式問答
……
四、智能問答的價值
1.智能客服
人工客服的局限:①?響應慢 ② 服務時間有限 ③ 業務知識有限 ④ 流動性大,培訓新人成本高 ⑤ 離職了就帶走了業務回答經驗 ⑥ 回復內容不一樣,容易造成矛盾
智能客服的優勢:① 毫秒級響應② 全年24小時在線 ③ 精通所有業務知識 ④ 只需培養管理員 ⑤ 保存所有業務回答數據 ⑥ 回復內容標準
2.Faq知識庫問答
Faq = Frequently asked Questions:常見問題 / 熱點問題
智能客服通常做一些 Faq 的問答
總結
Faq知識庫問答:列表展示所有常見問題,用戶需要自己找到對應的問題,對用戶不友好
希望的改進:讓用戶以自然語言描述自己的問題,算法進行 Faq 庫的檢索,給出對應的答案
3.相關名詞
① 問答對
一個(或多個相似的)問題與它對應的答案
② faq庫 / 知識庫
很多問答對組成的集合
③ 標準問
每組問答對中的問題有多個時,選一對為其中代表
④ 相似問/擴展問
一組問答對中,標準問之外的其他問題,對標準問的擴充
⑤ 用戶問
用戶真正輸入的問題,而不是事先準備的
⑥ 知識加工
人工編輯 faq 庫的過程
4.Faq知識庫示例
5.運行邏輯
① 對用戶問題進行預處理:分詞、去停用詞、去標點、大小寫轉換、全半角轉換,按需處理
② 使用處理后的問題,與faq庫中的問題計算相似度
③ 按照相似度分值排序
④ 返回最相似問題對應的答案
6.算法核心
語義相似度計算?是 faq 問答的核心
一般簡稱文本匹配 ?? ? ? ?f(x,y) —>?Score
相似度分值合理,才可以找到正確的對應問題
計算分值的同時,也要考慮速度
思考:可不可以不通過相似度計算,匹將用戶問題配知識庫中最相似的問題,再通過知識庫中的問題匹配對應的答案,而是直接訓練一個模型,匹配用戶問題與知識庫中答案之間的相似度
事實上,在實際場景中,答案的格式不是固定的,如果直接匹配,如果和知識庫中的問題匹配,則問題對應的答案可以是多樣、多種渠道的,如果直接用問題與答案匹配,則只需要一個計算文本相似度之間的模型即可
五、文本匹配算法Ⅰ?—— 編輯距離
編輯距離:兩個字符串之間,由一個轉成另一個所需的最少編輯操作次數
許可的編輯操作(替換、插入、刪除)包括將一個字符替換成另一個字符,插入一個字符,刪除一個字符
例:
相似度計算公式:
????????ED:編輯距離????????L:字符串長度
? ? ? ? 1?- 兩個字符串之間替換、插入、刪除的編輯操作次數 /?兩字符串的最大長度,得到兩個字符串的相似度
兩個字符串完全一致:編輯距離 = 0,相似度 = 1
兩個字符串完全不一致:編輯距離 = 較長者長度,相似度 = 0
🚀 代碼實現
思路與算法
1.初始化矩陣:
1.matrix?是一個二維數組,大小為 (len(string1) + 1) x (len(string2) + 1)
2.第一行和第一列分別初始化為 0 到 len(string1) 和 0 到 len(string2),表示從一個空字符串轉換到目標字符串所需的操作次數
2.填充矩陣:
對于每個字符 string1[i - 1] 和 string2[j - 1],如果它們相等,則 d=0,否則 d=1。
matrix[i][j] 的值通過以下公式計算:
matrix[i][j] = min(matrix[i?1][j] + 1, matrix[i][j?1] + 1,matrix[i?1][j?1] + d)
其中,
matrix[i - 1][j - 1] + d?表示 替換操作(如果字符不同,則 d=1)
matrix[i][j - 1] + 1表示插入操作
matrix[i - 1][j] + 1表示刪除操作?
3.計算相似度:
編輯距離為:?matrix[len(string1)][len(string2)]
。
相似度為:,表示兩個字符串的相似程度,值越接近?1?表示越相似
np.zeros():用于創建一個指定形狀和數據類型的全零數組。
參數名 | 類型 | 說明 |
---|---|---|
shape | int 或 tuple | 數組的形狀,可以是一個整數或表示形狀的元組。 |
dtype | dtype, 可選 | 數組的數據類型,默認為?float64 。 |
order | {'C', 'F'}, 可選 | 數組元素在內存中的排列順序,'C' 表示按行排列,'F' 表示按列排列。 |
range():生成一個不可變的整數序列,常用于循環控制。
參數名 | 類型 | 說明 |
---|---|---|
start | int, 可選 | 序列的起始值,默認為 0。 |
stop | int | 序列的結束值(不包含)。 |
step | int, 可選 | 步長,默認為 1。 |
len():返回對象的長度(如字符串、列表、元組等)。
參數名 | 類型 | 說明 |
---|---|---|
obj | object | 需要計算長度的對象。 |
min():返回一組數據中的最小值。
參數名 | 類型 | 說明 |
---|---|---|
iterable | iterable | 可迭代對象(如列表、元組等)。 |
*args | 可選 | 多個單獨的參數,用于比較。 |
key | function, 可選 | 用于指定比較規則的函數。 |
default | object, 可選 | 當可迭代對象為空時返回的默認值。 |
max():返回一組數據中的最大值。
參數名 | 類型 | 說明 |
---|---|---|
iterable | iterable | 可迭代對象(如列表、元組等)。 |
*args | 可選 | 多個單獨的參數,用于比較。 |
key | function, 可選 | 用于指定比較規則的函數。 |
default | object, 可選 | 當可迭代對象為空時返回的默認值。 |
#編輯距離
def editing_distance(string1, string2):matrix = np.zeros((len(string1) + 1, len(string2) + 1))for i in range(len(string1) + 1):matrix[i][0] = ifor j in range(len(string2) + 1):matrix[0][j] = jfor i in range(1, len(string1) + 1):for j in range(1, len(string2) + 1):if string1[i - 1] == string2[j - 1]:d = 0else:d = 1matrix[i][j] = min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + d)edit_distance = matrix[len(string1)][len(string2)]return 1 - edit_distance / max(len(string1), len(string2))
優缺分析
優點:
① 可解釋性強
② 跨語種(甚至對于非語言序列)有效
③ 不需要訓練模型
缺點:
① 字符之間沒有語義相似度 eg:str1 = “我沒錢”,str2 = "俺沒錢",相似度得分:0.66(1 - 1 / 3)
② 受無關詞/停用詞影響大 eg:str1 = “我要辦卡”,str2 = “你好我需要辦一張卡”,相似度得分:0.44(1 - 5 / 9)
③ 受語序影響大 eg:str1 = “今天天氣不錯”,str2 = “天氣不錯今天”,相似度得分:0.33(1 - 4 / 6)
④ 文本長度對速度影響很大(算法實現中兩層for循環速度基于文本長度)
六、文本匹配算法 Ⅱ —— Jaccard相似度
通用表述:根據兩個集合中,不同元素所占的比例,來衡量兩個樣本之間的相似度
用于文本匹配:根據兩個文本中,不同的字或詞所占的比例,來衡量兩個文本之間的相似度
相似度計算公式:
例:str1 = “今天天氣真不錯”,str2 = “估計明天天氣更好”,公共字:天、氣?
A ∩ B:2(天、氣)、A ∪ B:11(今、天、氣、真、不、錯、估、計、明、更、好)
Jaccard相似度:2 / 11 = 0.18
如果輸入字符串,則得到基于字的 jaccard相似度;
如果輸入詞的列表,則得到基于詞的 jaccard相似度;
具體用基于詞 還是 用基于字的jaccrad相似度,看場景決定:① 分詞是否準確;② 是否有很多類似名詞、縮略詞;③ 文本長度等因素,都會影響選擇
代碼實現
set():?Python 中的一個內置函數,用于創建一個無序且不重復的元素集合。它可以將可迭代對象(如字符串、列表、元組等)轉換為集合,并自動去除重復元素
參數 | 描述 |
---|---|
iterable | 可選參數,表示一個可迭代對象(如列表、元組、字符串等)。如果不提供此參數,則返回一個空集合。 |
len():返回對象的長度(如字符串、列表、元組等)。
參數名 | 類型 | 說明 |
---|---|---|
obj | object | 需要計算長度的對象。 |
#jaccard距離
def jaccard_distance(string1, string2):words1 = set(string1)words2 = set(string2)distance = len(words1 & words2) / len(words1 | words2)return distance
優缺分析
優點:
① 語序不影響分數(詞袋模型)eg:今天天氣不錯 / 天氣不錯今天
② 實現簡單,速度很快
③ 可跨語種,無需訓練等
缺點:
① 語序不影響分數 eg:他打了我 / 我打了他
② 字符之間沒有相似度衡量:同編輯距離
③ 受無關詞影響
④ 非一致文本可能出現滿分?eg:他是不知道? ? ? ? 他不是不知道
適用場景:
文本越長,語序對于準確率的影響越低,語序越不重要,更加適合Jaccard距離算法
文本短的話,Jaccard算法缺點會被放大,優點被縮小
七、文本匹配算法 Ⅲ —— BM25算法
常用在搜索引擎框架中,用來做文檔和搜索問題的匹配。同樣也可以用在問答中,做文本匹配。
核心思想:假如一個詞在某類文本(假設為A類)中出現次數很多,而在其他類別文本(非A類)出現很少,那么這個詞是A類文本的重要詞(高權重詞);反之,如果一個詞在出現在很多領域,則其對于任意類別的重要性都很差。 ? ?
BM25算法的基礎是:TF · IDF算法
TF·IDF算法
TF:詞頻,代表這個詞在某個類別文本中出現的頻率,某個詞在某個類別中出現的次數 / 該類別詞的總數
? ? ? ? 公式:某個詞在某個類別中出現的次數 / 該類別詞的總數
IDF:逆文檔頻率,代表這個詞在其他文本中出現的頻率,N代表文本總數,dfi代表包含詞qi的文本的總數
????????公式:IDF(qi) = log[(N - df_i + 0.5) / (df_i + 0.5)]
????????N:文檔集合中的總文檔數? ? ? ? df_i:包含詞項q_i的文檔數?
逆文檔頻率IDF高 ——> 該詞很少出現在其他文檔
BM25算法
BM25是對TF·IDF的一種改進,優化其表示效果
公式:
詞的重要性(IDF)
公式:
其中,N 是文檔總數,df_i 是包含詞 q_i 的文檔數,IDF 值越高,表示該詞在文檔集合中越稀有,重要性越大
詞與文檔的相關性(TF)
BM25對詞頻(TF)進行了優化,引入了飽和函數S(q_i, d),避免詞頻過高時權重過大。
公式:?
????????TF(qi, D):詞 qi 在文檔 D 中的詞頻
????????K:一個與文檔長度相關的參數,
????????????????公式:
????????????????????????L_d:文檔 D 的長度,L_ave:所有文檔的平均長度,k_1,b:可學習參數
對詞頻進行調整,考慮了文檔長度對詞頻的影響。較長的文檔會受到懲罰,以避免偏向長文檔
詞與查詢的相關性(可選)
當查詢較長時,BM25還會考慮詞在查詢中的頻率
公式:
TF(qi, Q):詞qi?在查詢Q中的詞頻,k_3?:可學習參數。
參數設置
k_1?:控制詞頻的重要性,通常取值為 1.2。
b:控制文檔長度的影響,通常取值為 0.75。
k_3?:控制查詢中詞頻的影響,通常取值為 1.2。
這些參數和改動的意義在于控制文本長度對分值的影響
優缺分析
優點:
① 通過使用TF·IDF弱化了無關詞的影響,強化了重要詞的影響,使得效果大幅提升
② 統計模型計算快(時間消耗主要是分詞),不需要多輪迭代
③ 詞袋模型(雙刃劍)、跨語種等
缺點:
① 依然沒有考慮詞與詞之間的相似性(字符之間沒有相似度衡量)
② 需要一定量的訓練(統計)樣本(faq庫本身)
③ 對于新增類別,需要重新計算統計模型
④ 分值不是一個總在0,1之間的數
代碼實現
Ⅰ、超參數定義
ESPION:處理逆文檔頻率(IDF)計算中可能出現的負值的參數,確保IDF值始終為正,從而避免算法在計算相關性得分時出現異常。
PARAM_K1:超參數K_1,默認值為1.5,控制詞頻飽和度的上升速度。值越大,詞頻對得分的影響越大?
PARAM_B:超參數B,默認值為0.6,控制文檔長度歸一化的影響。值越大,文檔長度對得分的影響越大
typing:用于類型注解的庫
import json
import math
import os
import pickle
import sys
from typing import Dict, Listclass BM25:EPSILON = 0.25PARAM_K1 = 1.5 # BM25算法中超參數PARAM_B = 0.6 # BM25算法中超參數
Ⅱ、初始化方法
def __init__(self, corpus: Dict):"""初始化BM25模型:param corpus: 文檔集, 文檔集合應該是字典形式,key為文檔的唯一標識,val對應其文本內容,文本內容需要分詞成列表"""self.corpus_size = 0 # 文檔數量self.wordNumsOfAllDoc = 0 # 用于計算文檔集合中平均每篇文檔的詞數 -> wordNumsOfAllDoc / corpus_sizeself.doc_freqs = {} # 記錄每篇文檔中查詢詞的詞頻self.idf = {} # 記錄查詢詞的 IDFself.doc_len = {} # 記錄每篇文檔的單詞數self.docContainedWord = {} # 包含單詞 word 的文檔集合self._initialize(corpus)
Ⅲ、根據語料庫構建倒排索引并計算每個詞的IDF值
len():返回對象的長度或元素個數。
參數名 | 類型 | 描述 |
---|---|---|
obj | 對象 | 要計算長度的對象,如字符串、列表、元組、字典等。 |
set():創建一個無序且不重復元素的集合。
參數名 | 類型 | 描述 |
---|---|---|
iterable | 可迭代對象(如列表、元組等) | 可選參數,用于創建集合。如果未提供,則創建一個空集合。 |
集合.add():向集合中添加一個元素。如果元素已存在,則不會重復添加。
參數名 | 類型 | 描述 |
---|---|---|
element | 任意類型 | 要添加到集合中的元素。如果元素已存在,則不會重復添加。 |
float():將字符串或數字轉換為浮點數。
參數名 | 類型 | 描述 |
---|---|---|
x | 字符串或數字 | 要轉換為浮點數的字符串或數字。如果未提供參數,則返回0.0。 |
字典.keys():返回字典中所有鍵的視圖對象。
math.log():計算自然對數(以e為底的對數)。可以指定第二個參數作為對數的底數。
參數名 | 類型 | 描述 |
---|---|---|
x | 數字 | 要計算對數的數值。 |
base | 數字 | 可選參數,指定對數的底數。默認為自然對數(以e為底)。 |
列表.append():在列表末尾添加一個元素。
參數名 | 類型 | 描述 |
---|---|---|
object | 任意類型 | 要添加到列表末尾的元素。 |
float():將字符串或數字轉換為浮點數。
參數名 | 類型 | 描述 |
---|---|---|
x | 字符串或數字 | 要轉換為浮點數的字符串或數字。如果未提供參數,則返回0.0。 |
?公式:?,N是文檔總數,n(q_i?)是包含詞q_i?的文檔數
def _initialize(self, corpus: Dict):"""根據語料庫構建倒排索引"""# nd = {} # word -> number of documents containing the wordfor index, document in corpus.items():self.corpus_size += 1self.doc_len[index] = len(document) # 文檔的單詞數self.wordNumsOfAllDoc += len(document)frequencies = {} # 一篇文檔中單詞出現的頻率for word in document:if word not in frequencies:frequencies[word] = 0frequencies[word] += 1self.doc_freqs[index] = frequencies# 構建詞到文檔的倒排索引,將包含單詞的和文檔和包含關系進行反向映射for word in frequencies.keys():if word not in self.docContainedWord:self.docContainedWord[word] = set()self.docContainedWord[word].add(index)# 計算 idfidf_sum = 0 # collect idf sum to calculate an average idf for epsilon valuenegative_idfs = []for word in self.docContainedWord.keys():doc_nums_contained_word = len(self.docContainedWord[word])idf = math.log(self.corpus_size - doc_nums_contained_word +0.5) - math.log(doc_nums_contained_word + 0.5)self.idf[word] = idfidf_sum += idfif idf < 0:negative_idfs.append(word)average_idf = float(idf_sum) / len(self.idf)eps = BM25.EPSILON * average_idffor word in negative_idfs:self.idf[word] = eps
Ⅳ、計算文檔集合中平均每篇文檔的詞數
@property:?Python 中的一個內置裝飾器,用于將類的方法轉換為屬性,使得可以像訪問普通屬性一樣訪問這些方法。它主要用于封裝類的屬性,提供更簡潔和直觀的接口,同時允許在訪問或修改屬性時執行額外的邏輯,如數據驗證或計算。
float():將字符串或數字轉換為浮點數。
參數名 | 類型 | 描述 |
---|---|---|
x | 字符串或數字 | 要轉換為浮點數的字符串或數字。如果未提供參數,則返回0.0。 |
@propertydef avgdl(self):return float(self.wordNumsOfAllDoc) / self.corpus_size
Ⅴ、計算查詢query 與 某篇文檔的相關性得分?
公式:
其中,f(qi?, D) 是詞 qi? 在文檔D中的詞頻,∣D∣是文檔D的長度,avgdl 是文檔集合的平均長度。
self.avgdl:文檔集合中平均每篇文檔的詞數
def get_score(self, query: List, doc_index):k1 = BM25.PARAM_K1b = BM25.PARAM_Bscore = 0doc_freqs = self.doc_freqs[doc_index]for word in query:if word not in doc_freqs:continuescore += self.idf[word] * doc_freqs[word] * (k1 + 1) / (doc_freqs[word] + k1 * (1 - b + b * self.doc_len[doc_index] / self.avgdl))return [doc_index, score]
Ⅵ、計算查詢與所有文檔的相關性得分
遍歷返回一個包含文檔索引和得分的列表
列表推導式(List Comprehension)是 Python 中一種簡潔且高效的方式來創建列表。它允許你在一行代碼中從現有的可迭代對象(如列表、元組、字符串等)生成新的列表。列表推導式的基本語法如下:
new_list = [expression for item in iterable if condition]
expression
?是對?item
?的操作或表達式,用于定義新列表中的每個元素。item
?是可迭代對象中的每個元素。iterable
?是包含要迭代的元素的可迭代對象。condition
?是一個可選的條件,用于篩選要包含在新列表中的元素。?
字典.keys():返回字典中所有鍵的視圖對象。
def get_scores(self, query):scores = [self.get_score(query, index) for index in self.doc_len.keys()]return scores
Ⅶ、完整的BM25算法實現?
import json
import math
import os
import pickle
import sys
from typing import Dict, Listclass BM25:EPSILON = 0.25PARAM_K1 = 1.5 # BM25算法中超參數PARAM_B = 0.6 # BM25算法中超參數def __init__(self, corpus: Dict):"""初始化BM25模型:param corpus: 文檔集, 文檔集合應該是字典形式,key為文檔的唯一標識,val對應其文本內容,文本內容需要分詞成列表"""self.corpus_size = 0 # 文檔數量self.wordNumsOfAllDoc = 0 # 用于計算文檔集合中平均每篇文檔的詞數 -> wordNumsOfAllDoc / corpus_sizeself.doc_freqs = {} # 記錄每篇文檔中查詢詞的詞頻self.idf = {} # 記錄查詢詞的 IDFself.doc_len = {} # 記錄每篇文檔的單詞數self.docContainedWord = {} # 包含單詞 word 的文檔集合self._initialize(corpus)def _initialize(self, corpus: Dict):"""根據語料庫構建倒排索引"""# nd = {} # word -> number of documents containing the wordfor index, document in corpus.items():self.corpus_size += 1self.doc_len[index] = len(document) # 文檔的單詞數self.wordNumsOfAllDoc += len(document)frequencies = {} # 一篇文檔中單詞出現的頻率for word in document:if word not in frequencies:frequencies[word] = 0frequencies[word] += 1self.doc_freqs[index] = frequencies# 構建詞到文檔的倒排索引,將包含單詞的和文檔和包含關系進行反向映射for word in frequencies.keys():if word not in self.docContainedWord:self.docContainedWord[word] = set()self.docContainedWord[word].add(index)# 計算 idfidf_sum = 0 # collect idf sum to calculate an average idf for epsilon valuenegative_idfs = []for word in self.docContainedWord.keys():doc_nums_contained_word = len(self.docContainedWord[word])idf = math.log(self.corpus_size - doc_nums_contained_word +0.5) - math.log(doc_nums_contained_word + 0.5)self.idf[word] = idfidf_sum += idfif idf < 0:negative_idfs.append(word)average_idf = float(idf_sum) / len(self.idf)eps = BM25.EPSILON * average_idffor word in negative_idfs:self.idf[word] = eps@propertydef avgdl(self):return float(self.wordNumsOfAllDoc) / self.corpus_sizedef get_score(self, query: List, doc_index):"""計算查詢 q 和文檔 d 的相關性分數:param query: 查詢詞列表:param doc_index: 為語料庫中某篇文檔對應的索引"""k1 = BM25.PARAM_K1b = BM25.PARAM_Bscore = 0doc_freqs = self.doc_freqs[doc_index]for word in query:if word not in doc_freqs:continuescore += self.idf[word] * doc_freqs[word] * (k1 + 1) / (doc_freqs[word] + k1 * (1 - b + b * self.doc_len[doc_index] / self.avgdl))return [doc_index, score]def get_scores(self, query):scores = [self.get_score(query, index) for index in self.doc_len.keys()]return scores
八、文本匹配算法 Ⅳ —— word2vec
1.什么是向量
指在坐標系(空間)內具有大小和方向的量
2維向量 [0.1, 2.9]????????5維向量 [3, 1, 4, 2, 5]
2.什么是詞向量? ?
將每個詞或字轉換成同一向量空間內的一個向量
3.詞向量的特點
兩個詞如果語義相近,則在空間中的向量接近
4.詞向量是如何尋得到的
隨機初始化,之后通過文本語料進行訓練調整
5.如何訓練/訓練目標
① 基于窗口
② 基于語言模型
③ 基于共現矩陣
6.訓練提速技巧
① 層次softmax/Huffman樹 ? ? ? ?
②?負采樣
7.如何用于文本匹配
將文本中的所有詞的詞向量相加取平均
文本 ——> 句向量
公式:
句向量維度 = 詞向量維度,不論文本長度
文本相似度 = 向量相似度 = 向量夾角余弦值
向量夾角為0,余弦值為1
8.優缺分析
優點:
① 兩個文本包含語義相似的詞,會提高相似度
② 訓練需要的數據簡單(純文本語料即可)
③ 計算速度快,可以對知識庫內問題預先計算向量
④ 將文本轉化為數字,使后續復雜模型成為可能
缺點:
① 詞向量的效果決定句向量效果(語料數量、領域適配、分詞結果、未登錄詞)
② 一詞多意的情況難以處理 eg:梨 —— 蘋果 —— 華為
③ 受停用詞(無效詞)和文本長度影響很大(也是詞袋模型)
④ 更換語種,甚至更換領域,都需要重新訓練