同義詞挖掘的一些常用方法 及同義詞替換程式
先談談同義詞挖掘的一些常用方法
在使用者使用搜索引擎的過程中,由於地區差異、文化水平等差異,使用者所輸入的query很多時候和資料中的描述不一致。這種情況下,為了能夠召回更多的文件向用戶展示,搜尋引擎需要對使用者的輸入做同義詞、糾錯、歸一化等操作。在進行這些操作的過程中,同義詞的挖掘是一個基礎工作。下面簡單介紹一下幾個簡單實用的演算法。
詞典
從百度詞典、金山詞霸的詞條中抓取資料,根據原詞的描述和解釋提取同義詞。這種演算法簡單有效,但是挖掘出來的同義詞通常比較書面語。
百科詞條
從百度百科、搜搜百科等百科網站抓取詞條,在詞條中,有“又叫”“別名”等特徵詞,從這些特徵詞之後可以提取該詞條的其他說法。百科挖掘出的詞條通常質量比較高。
元搜尋資料
元搜尋資料中,原始Query和其召回的文件存在一定的關係,有些是元搜尋引擎做了同義替換或者非必留召回的結果。在這些結果和原始query做term對齊,從中可以挖掘出來其中隱含的同義詞,為了提高召回的同義詞的準確率,可以做一些處理:
1. 帶有相同字的term
2. 飄紅的term
3. 簡寫到全稱
4. 上下位位置資訊
上下文相關性挖掘
在所有的文件中,如果兩個term經常在相同的上下文下出現,那麼這兩個詞是同義詞的可能性較大。基於這個假設,
1. 對文件進行分詞,統計每個term的上下文,根據一定的策略對上下文進行剪枝。
2. 把剪枝之後的上下文作為每個term的特徵向量,求term之間兩兩的夾角餘弦。
3. 提取餘弦值比較高的作為候選同義詞。
這種演算法挖掘出的同義詞會出現一些相關詞但不是同義詞的詞對,因此這部分同義詞通常需要一個人工稽核的步驟。
語料對齊
在一個完備的檢索系統中,會記錄點選日誌和使用者的session日誌。此外,還有不同的anchor指向同一個url。通過這些資料進行語料對齊,可以挖掘出來很多同義詞:
1. Query<>Query對齊:點選了相同URL的Query
2. Query<>Title對齊:某個Query點選了某個URL
3. Query<>Query對齊:同一個session內的Query,可能是使用者的自糾錯或者自修改
4. Title<>Title對齊:被同一個Query點選的URL
5. Anchor<>Title對齊:指向了某個URL的Anchor
6. Anchor<>Anchor:指向了同一個URL的Anchor
下邊講一下NLP資料預處理——同義詞替換程式
自然語言資料預處理中經常會涉及到同義詞替換,比如計算兩個句子的相似度中,把一個詞的兩個同義詞利用同義詞替換技術轉換為同一個詞,那麼就提高了相似度計算的可靠性。學習自然語言處理的同學肯定都會做到資料預處理的工作,下面分享我最近寫的一個程式,希望能為從事同樣工作的同學提供那麼一點幫助,也希望自己的程式能夠得到指點。
我採用的同義詞詞表是下面這種結構:
-
人 士
-
人類 生人
-
人手 人員
-
勞力 勞動力
-
匹夫 個人
-
傢伙 東西
-
者 手
-
每人 各人
-
該人 此人
-
人民 民
下面是同義詞替換程式(註釋比較詳細,這裡不再贅述程式具體實現方式):
-
from pyltp import Segmentor
-
class SynonymsReplacer:
-
def __init__(self, synonyms_file_path, cws_model_path):
-
self.synonyms = self.load_synonyms(synonyms_file_path)
-
self.segmentor = self.load_segmentor(cws_model_path)
-
def __del__(self):
-
"""物件銷燬時要釋放pyltp分詞模型"""
-
self.segmentor.release()
-
def load_segmentor(self, cws_model_path):
-
"""
-
載入ltp分詞模型
-
:param cws_model_path: 分詞模型路徑
-
:return: 分詞器物件
-
"""
-
segmentor = Segmentor()
-
segmentor.load(cws_model_path)
-
return segmentor
-
def segment(self, sentence):
-
"""呼叫pyltp的分詞方法將str型別的句子分詞並以list形式返回"""
-
return list(self.segmentor.segment(sentence))
-
def load_synonyms(self, file_path):
-
"""
-
載入同義詞表
-
:param file_path: 同義詞表路徑
-
:return: 同義詞列表[[xx,xx],[xx,xx]...]
-
"""
-
synonyms = []
-
with open(file_path, 'r', encoding='utf-8') as file:
-
for line in file:
-
synonyms.append(line.strip().split(' '))
-
return synonyms
-
def permutation(self, data):
-
"""
-
排列函式
-
:param data: 需要進行排列的資料,列表形式
-
:return:
-
"""
-
assert len(data) >= 1, "Length of data must greater than 0."
-
if len(data) == 1: # 當data中只剩(有)一個詞及其同義詞的列表時,程式返回
-
return data[0]
-
else:
-
head = data[0]
-
tail = data[1:] # 不斷切分到只剩一個詞的同義詞列表
-
tail = self.permutation(tail)
-
permt = []
-
for h in head: # 構建兩個詞列表的同義詞組合
-
for t in tail:
-
if isinstance(t, str): # 傳入的整個data的最後一個元素是一個一維列表,其中每個元素為str
-
permt.extend([[h] + [t]])
-
elif isinstance(t, list):
-
permt.extend([[h] + t])
-
return permt
-
def get_syno_sents_list(self, input_sentence):
-
"""
-
產生同義句,並返回同義句列表,返回的同義句列表沒有包含該句本身
-
:param input_sentence: 需要製造同義句的原始句子
-
:return:
-
"""
-
assert len(input_sentence) > 0, "Length of sentence must greater than 0."
-
seged_sentence = self.segment(input_sentence)
-
candidate_synonym_list = [] # 每個元素為句子中每個詞及其同義詞構成的列表
-
for word in seged_sentence:
-
word_synonyms = [word] # 初始化一個詞的同義詞列表
-
for syn in self.synonyms: # 遍歷同義詞表,syn為其中的一條
-
if word in syn: # 如果句子中的詞在同義詞表某一條目中,將該條目中它的同義詞新增到該詞的同義詞列表中
-
syn.remove(word)
-
word_synonyms.extend(syn)
-
candidate_synonym_list.append(word_synonyms) # 新增一個詞語的同義詞列表
-
perm_sent = self.permutation(candidate_synonym_list) # 將候選同義詞列表們排列組合產生同義句
-
syno_sent_list = [seged_sentence]
-
for p in perm_sent:
-
if p != seged_sentence:
-
syno_sent_list.append(p)
-
return syno_sent_list
程式功能測試程式碼如下:
-
if __name__ == '__main__':
-
replacer = SynonymsReplacer(synonyms_file_path='/your/path', cws_model_path='/your/path')
-
test_sentence = '承蒙關照今天早'
-
_syn = replacer.get_syno_sents_list(test_sentence)
-
for s in _syn:
-
print(s)
程式輸出如下所示:
-
['承蒙', '關照', '今天', '早']
-
['承蒙', '關照', '今天', '早日']
-
['承蒙', '關照', '今天', '早安']
-
['承蒙', '關照', '今日', '早']
-
['承蒙', '關照', '今日', '早日']
-
['承蒙', '關照', '今日', '早安']
-
['承情', '關照', '今天', '早']
-
['承情', '關照', '今天', '早日']
-
['承情', '關照', '今天', '早安']
-
['承情', '關照', '今日', '早']
-
['承情', '關照', '今日', '早日']
-
['承情', '關照', '今日', '早安']
輸入是一個str型別的句子,輸出是包含它本身的list形式的句子。歡迎各位朋友指教!