1. 程式人生 > >同義詞挖掘的一些常用方法 及同義詞替換程式

同義詞挖掘的一些常用方法 及同義詞替換程式

先談談同義詞挖掘的一些常用方法 

在使用者使用搜索引擎的過程中,由於地區差異、文化水平等差異,使用者所輸入的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資料預處理——同義詞替換程式

自然語言資料預處理中經常會涉及到同義詞替換,比如計算兩個句子的相似度中,把一個詞的兩個同義詞利用同義詞替換技術轉換為同一個詞,那麼就提高了相似度計算的可靠性。學習自然語言處理的同學肯定都會做到資料預處理的工作,下面分享我最近寫的一個程式,希望能為從事同樣工作的同學提供那麼一點幫助,也希望自己的程式能夠得到指點。

我採用的同義詞詞表是下面這種結構:

  1. 人 士

  2. 人類 生人

  3. 人手 人員

  4. 勞力 勞動力

  5. 匹夫 個人

  6. 傢伙 東西

  7. 者 手

  8. 每人 各人

  9. 該人 此人

  10. 人民 民

下面是同義詞替換程式(註釋比較詳細,這裡不再贅述程式具體實現方式):

  1. from pyltp import Segmentor

  2. class SynonymsReplacer:

  3. def __init__(self, synonyms_file_path, cws_model_path):

  4. self.synonyms = self.load_synonyms(synonyms_file_path)

  5. self.segmentor = self.load_segmentor(cws_model_path)

  6. def __del__(self):

  7. """物件銷燬時要釋放pyltp分詞模型"""

  8. self.segmentor.release()

  9. def load_segmentor(self, cws_model_path):

  10. """

  11. 載入ltp分詞模型

  12. :param cws_model_path: 分詞模型路徑

  13. :return: 分詞器物件

  14. """

  15. segmentor = Segmentor()

  16. segmentor.load(cws_model_path)

  17. return segmentor

  18. def segment(self, sentence):

  19. """呼叫pyltp的分詞方法將str型別的句子分詞並以list形式返回"""

  20. return list(self.segmentor.segment(sentence))

  21. def load_synonyms(self, file_path):

  22. """

  23. 載入同義詞表

  24. :param file_path: 同義詞表路徑

  25. :return: 同義詞列表[[xx,xx],[xx,xx]...]

  26. """

  27. synonyms = []

  28. with open(file_path, 'r', encoding='utf-8') as file:

  29. for line in file:

  30. synonyms.append(line.strip().split(' '))

  31. return synonyms

  32. def permutation(self, data):

  33. """

  34. 排列函式

  35. :param data: 需要進行排列的資料,列表形式

  36. :return:

  37. """

  38. assert len(data) >= 1, "Length of data must greater than 0."

  39. if len(data) == 1: # 當data中只剩(有)一個詞及其同義詞的列表時,程式返回

  40. return data[0]

  41. else:

  42. head = data[0]

  43. tail = data[1:] # 不斷切分到只剩一個詞的同義詞列表

  44. tail = self.permutation(tail)

  45. permt = []

  46. for h in head: # 構建兩個詞列表的同義詞組合

  47. for t in tail:

  48. if isinstance(t, str): # 傳入的整個data的最後一個元素是一個一維列表,其中每個元素為str

  49. permt.extend([[h] + [t]])

  50. elif isinstance(t, list):

  51. permt.extend([[h] + t])

  52. return permt

  53. def get_syno_sents_list(self, input_sentence):

  54. """

  55. 產生同義句,並返回同義句列表,返回的同義句列表沒有包含該句本身

  56. :param input_sentence: 需要製造同義句的原始句子

  57. :return:

  58. """

  59. assert len(input_sentence) > 0, "Length of sentence must greater than 0."

  60. seged_sentence = self.segment(input_sentence)

  61. candidate_synonym_list = [] # 每個元素為句子中每個詞及其同義詞構成的列表

  62. for word in seged_sentence:

  63. word_synonyms = [word] # 初始化一個詞的同義詞列表

  64. for syn in self.synonyms: # 遍歷同義詞表,syn為其中的一條

  65. if word in syn: # 如果句子中的詞在同義詞表某一條目中,將該條目中它的同義詞新增到該詞的同義詞列表中

  66. syn.remove(word)

  67. word_synonyms.extend(syn)

  68. candidate_synonym_list.append(word_synonyms) # 新增一個詞語的同義詞列表

  69. perm_sent = self.permutation(candidate_synonym_list) # 將候選同義詞列表們排列組合產生同義句

  70. syno_sent_list = [seged_sentence]

  71. for p in perm_sent:

  72. if p != seged_sentence:

  73. syno_sent_list.append(p)

  74. return syno_sent_list

程式功能測試程式碼如下:

  1. if __name__ == '__main__':

  2. replacer = SynonymsReplacer(synonyms_file_path='/your/path', cws_model_path='/your/path')

  3. test_sentence = '承蒙關照今天早'

  4. _syn = replacer.get_syno_sents_list(test_sentence)

  5. for s in _syn:

  6. print(s)

程式輸出如下所示:

  1. ['承蒙', '關照', '今天', '早']

  2. ['承蒙', '關照', '今天', '早日']

  3. ['承蒙', '關照', '今天', '早安']

  4. ['承蒙', '關照', '今日', '早']

  5. ['承蒙', '關照', '今日', '早日']

  6. ['承蒙', '關照', '今日', '早安']

  7. ['承情', '關照', '今天', '早']

  8. ['承情', '關照', '今天', '早日']

  9. ['承情', '關照', '今天', '早安']

  10. ['承情', '關照', '今日', '早']

  11. ['承情', '關照', '今日', '早日']

  12. ['承情', '關照', '今日', '早安']

輸入是一個str型別的句子,輸出是包含它本身的list形式的句子。歡迎各位朋友指教!