NLP入門(九)詞義消岐(WSD)的簡(jiǎn)介與實(shí)現(xiàn)

詞義消岐簡(jiǎn)介

??詞義消岐,英文名稱為Word Sense Disambiguation,英語縮寫為WSD,是自然語言處理(NLP)中一個(gè)非常有趣的基本任務(wù)。
??那么,什么是詞義消岐呢?通常,在我們的自然語言中,不管是英語,還是中文,都有多義詞存在。這些多義詞的存在,會(huì)讓人對(duì)句子的意思產(chǎn)生混淆,但人通過學(xué)習(xí)又是可以正確地區(qū)分出來的。
??以“小米”這個(gè)詞為例,如果僅僅只是說“小米”這個(gè)詞語,你并不知道它實(shí)際指的到底是小米科技公司還是谷物。但當(dāng)我們把詞語置于某個(gè)特定的語境中,我們能很好地區(qū)分出這個(gè)詞語的意思。比如,

雷軍是小米的創(chuàng)始人。

在這個(gè)句子中,我們知道這個(gè)“小米”指的是小米科技公司。比如

我今天早上喝了一碗小米粥。

在這個(gè)句子中,“小米”指的是谷物、農(nóng)作物。
??所謂詞義消岐,指的是在特定的語境中,識(shí)別出某個(gè)歧義詞的正確含義。
??那么,詞義消岐有什么作用呢?詞義消岐可以很好地服務(wù)于語言翻譯和智能問答領(lǐng)域,當(dāng)然,還有許多應(yīng)用有待開發(fā)~

詞義消岐實(shí)現(xiàn)

??在目前的詞義消岐算法中,有不少原創(chuàng)算法,有些實(shí)現(xiàn)起來比較簡(jiǎn)單,有些想法較為復(fù)雜,但實(shí)現(xiàn)的效果普遍都不是很好。比較經(jīng)典的詞義消岐的算法為L(zhǎng)esk算法,該算法的想法很簡(jiǎn)單,通過對(duì)某個(gè)歧義詞構(gòu)建不同含義的語料及待判別句子中該詞語與語料的重合程度來實(shí)現(xiàn),具體的算法原理可參考網(wǎng)址:https://en.wikipedia.org/wiki/Lesk_algorithm .
??在下面的部分中,筆者將會(huì)介紹自己想的一種實(shí)現(xiàn)詞義消岐的算法,僅僅是一個(gè)想法,僅供參考。
??我們以詞語“火箭”為例,選取其中的兩個(gè)義項(xiàng)(同一個(gè)詞語的不同含義):NBA球隊(duì)名燃?xì)馔七M(jìn)裝置 ,如下:

火箭的兩個(gè)義項(xiàng)

獲取語料

??首先,我們利用爬蟲爬取這兩個(gè)義項(xiàng)的百度百科網(wǎng)頁(yè),以句子為單位,只要句子中出現(xiàn)該詞語,則把這句話加入到這個(gè)義項(xiàng)的預(yù)料中。爬蟲的完整Python代碼如下:

import requests
from bs4 import BeautifulSoup
from pyltp import SentenceSplitter

class WebScrape(object):
    def __init__(self, word, url):
        self.url = url
        self.word = word

    # 爬取百度百科頁(yè)面
    def web_parse(self):
        headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 \
                                             (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36'}
        req = requests.get(url=self.url, headers=headers)

        # 解析網(wǎng)頁(yè),定位到main-content部分
        if req.status_code == 200:
            soup = BeautifulSoup(req.text.encode(req.encoding), 'lxml')
            return soup
        return None

    # 獲取該詞語的義項(xiàng)
    def get_gloss(self):
        soup = self.web_parse()
        if soup:
            lis = soup.find('ul', class_="polysemantList-wrapper cmn-clearfix")
            if lis:
                for li in lis('li'):
                    if '<a' not in str(li):
                        gloss = li.text.replace('?', '')
                        return gloss

        return None

    # 獲取該義項(xiàng)的語料,以句子為單位
    def get_content(self):
        # 發(fā)送HTTP請(qǐng)求
        result = []
        soup = self.web_parse()
        if soup:
            paras = soup.find('div', class_='main-content').text.split('\n')
            for para in paras:
                if self.word in para:
                    sents = list(SentenceSplitter.split(para))
                    for sent in sents:
                        if self.word in sent:
                            sent = sent.replace('\xa0', '').replace('\u3000', '')
                            result.append(sent)

        result = list(set(result))

        return result

    # 將該義項(xiàng)的語料寫入到txt
    def write_2_file(self):
        gloss = self.get_gloss()
        result = self.get_content()
        print(gloss)
        print(result)
        if result and gloss:
            with open('./%s_%s.txt'% (self.word, gloss), 'w', encoding='utf-8') as f:
                f.writelines([_+'\n' for _ in result])

    def run(self):
        self.write_2_file()

# NBA球隊(duì)名
#url = 'https://baike.baidu.com/item/%E4%BC%91%E6%96%AF%E6%95%A6%E7%81%AB%E7%AE%AD%E9%98%9F/370758?fromtitle=%E7%81%AB%E7%AE%AD&fromid=8794081#viewPageContent'
# 燃?xì)馔七M(jìn)裝置
url = 'https://baike.baidu.com/item/%E7%81%AB%E7%AE%AD/6308#viewPageContent'
WebScrape('火箭', url).run()

利用這個(gè)爬蟲,我們爬取了“火箭”這個(gè)詞語的兩個(gè)義項(xiàng)的語料,生成了火箭_燃?xì)馔七M(jìn)裝置.txt文件和火箭NBA球隊(duì)名.txt文件,這兩個(gè)文件分別含有361和171個(gè)句子。以火箭燃?xì)馔七M(jìn)裝置.txt文件為例,前10個(gè)句子如下:

火箭技術(shù)的飛速發(fā)展,不僅可提供更加完善的各類導(dǎo)彈和推動(dòng)相關(guān)科學(xué)的發(fā)展,還將使開發(fā)空間資源、建立空間產(chǎn)業(yè)、空間基地及星際航行等成為可能。
火箭技術(shù)是一項(xiàng)十分復(fù)雜的綜合性技術(shù),主要包括火箭推進(jìn)技術(shù)、總體設(shè)計(jì)技術(shù)、火箭結(jié)構(gòu)技術(shù)、控制和制導(dǎo)技術(shù)、計(jì)劃管理技術(shù)、可靠性和質(zhì)量控制技術(shù)、試驗(yàn)技術(shù),對(duì)導(dǎo)彈來說還有彈頭制導(dǎo)和控制、
1903年,俄國(guó)的К.E.齊奧爾科夫斯基提出了制造大型液體火箭的設(shè)想和設(shè)計(jì)原理。
火箭有很多種,原始的火箭是用引火物附在弓箭頭上,然后射到敵人身上引起焚燒的一種箭矢。
“長(zhǎng)征三號(hào)丙”火箭是在 “長(zhǎng)征三號(hào)乙”火箭的基礎(chǔ)上, 減少了兩個(gè)助推器并取消了助推器上的尾翼。
火箭與導(dǎo)彈有什么區(qū)別
為了能夠在未來大規(guī)模的將人類送入太空,不可能依賴傳統(tǒng)的火箭和飛船。
火箭V2火箭
探測(cè)高層大氣的物理特征(如氣壓、溫度、濕度等)和現(xiàn)象的探空火箭。
可一次發(fā)射一發(fā)至數(shù)十發(fā)火箭彈。

實(shí)現(xiàn)算法

??我們以句子為單位進(jìn)行詞義消岐,即輸入一句話,識(shí)別出該句子中某個(gè)歧義詞的含義。筆者使用的算法比較簡(jiǎn)單,是以TF-IDF為權(quán)重的頻數(shù)判別。以句子

賽季初的時(shí)候,火箭是眾望所歸的西部決賽球隊(duì)。

為例,對(duì)該句子分詞后,去掉停用詞(stopwords),然后分別統(tǒng)計(jì)除了“火箭”這個(gè)詞以外的TF-IDF值,累加起來,比較在兩個(gè)義項(xiàng)下這個(gè)值的大小即可。
??實(shí)現(xiàn)這個(gè)算法的完整Python代碼如下:

import os
import jieba
from math import log2

# 讀取每個(gè)義項(xiàng)的語料
def read_file(path):
    with open(path, 'r', encoding='utf-8') as f:
        lines = [_.strip() for _ in f.readlines()]
        return lines

# 對(duì)示例句子分詞
sent = '賽季初的時(shí)候,火箭是眾望所歸的西部決賽球隊(duì)。'
wsd_word = '火箭'

jieba.add_word(wsd_word)
sent_words = list(jieba.cut(sent, cut_all=False))

# 去掉停用詞
stopwords = [wsd_word, '我', '你', '它', '他', '她', '了', '是', '的', '啊', '誰', '什么','都',\
             '很', '個(gè)', '之', '人', '在', '上', '下', '左', '右', '。', ',', '!', '?']

sent_cut = []
for word in sent_words:
    if word not in stopwords:
        sent_cut.append(word)

print(sent_cut)


# 計(jì)算其他詞的TF-IDF以及頻數(shù)
wsd_dict = {}
for file in os.listdir('.'):
    if wsd_word in file:
        wsd_dict[file.replace('.txt', '')] = read_file(file)

# 統(tǒng)計(jì)每個(gè)詞語在語料中出現(xiàn)的次數(shù)
tf_dict = {}
for meaning, sents in wsd_dict.items():
    tf_dict[meaning] = []
    for word in sent_cut:
        word_count = 0
        for sent in sents:
            example = list(jieba.cut(sent, cut_all=False))
            word_count += example.count(word)

        if word_count:
            tf_dict[meaning].append((word, word_count))

idf_dict = {}
for word in sent_cut:
    document_count = 0
    for meaning, sents in wsd_dict.items():
        for sent in sents:
            if word in sent:
                document_count += 1

    idf_dict[word] = document_count

# 輸出值
total_document = 0
for meaning, sents in wsd_dict.items():
    total_document += len(sents)

# 計(jì)算tf_idf值
mean_tf_idf = []
for k, v in tf_dict.items():
    print(k+':')
    tf_idf_sum = 0
    for item in v:
        word = item[0]
        tf = item[1]
        tf_idf = item[1]*log2(total_document/(1+idf_dict[word]))
        tf_idf_sum += tf_idf
        print('%s, 頻數(shù)為: %s, TF-IDF值為: %s'% (word, tf, tf_idf))

    mean_tf_idf.append((k, tf_idf_sum))

sort_array = sorted(mean_tf_idf, key=lambda x:x[1], reverse=True)
true_meaning = sort_array[0][0].split('_')[1]
print('\n經(jīng)過詞義消岐,%s在該句子中的意思為 %s .' % (wsd_word, true_meaning))

輸出結(jié)果如下:

['賽季', '初', '時(shí)候', '眾望所歸', '西部', '決賽', '球隊(duì)']
火箭_燃?xì)馔七M(jìn)裝置:
初, 頻數(shù)為: 2, TF-IDF值為: 12.49585502688717
火箭_NBA球隊(duì)名:
賽季, 頻數(shù)為: 63, TF-IDF值為: 204.6194333469459
初, 頻數(shù)為: 1, TF-IDF值為: 6.247927513443585
時(shí)候, 頻數(shù)為: 1, TF-IDF值為: 8.055282435501189
西部, 頻數(shù)為: 16, TF-IDF值為: 80.88451896801904
決賽, 頻數(shù)為: 7, TF-IDF值為: 33.13348038429679
球隊(duì), 頻數(shù)為: 40, TF-IDF值為: 158.712783770034

經(jīng)過詞義消岐,火箭在該句子中的意思為 NBA球隊(duì)名 .

測(cè)試

??接著,我們對(duì)上面的算法和程序進(jìn)行更多的測(cè)試。

輸入句子為:

三十多年前,戰(zhàn)士們?cè)诟瓯跒┌资制鸺?,建起了我?guó)的火箭發(fā)射基地。

輸出結(jié)果為:

['三十多年', '前', '戰(zhàn)士', '們', '戈壁灘', '白手起家', '建起', '我國(guó)', '發(fā)射', '基地']
火箭_燃?xì)馔七M(jìn)裝置:
前, 頻數(shù)為: 2, TF-IDF值為: 9.063440958888354
們, 頻數(shù)為: 1, TF-IDF值為: 6.05528243550119
我國(guó), 頻數(shù)為: 3, TF-IDF值為: 22.410959804340102
發(fā)射, 頻數(shù)為: 89, TF-IDF值為: 253.27878721862933
基地, 頻數(shù)為: 7, TF-IDF值為: 42.38697704850833
火箭_NBA球隊(duì)名:
前, 頻數(shù)為: 3, TF-IDF值為: 13.59516143833253
們, 頻數(shù)為: 1, TF-IDF值為: 6.05528243550119

經(jīng)過詞義消岐,火箭在該句子中的意思為 燃?xì)馔七M(jìn)裝置 .

輸入句子為:

對(duì)于馬刺這樣級(jí)別的球隊(duì),常規(guī)賽只有屈指可數(shù)的幾次交鋒具有真正的意義,今天對(duì)火箭一役是其中之一。

輸出結(jié)果為:

['對(duì)于', '馬刺', '這樣', '級(jí)別', '球隊(duì)', '常規(guī)賽', '只有', '屈指可數(shù)', '幾次', '交鋒', '具有', '真正', '意義', '今天', '對(duì)', '一役', '其中', '之一']
火箭_燃?xì)馔七M(jìn)裝置:
只有, 頻數(shù)為: 1, TF-IDF值為: 7.470319934780034
具有, 頻數(shù)為: 5, TF-IDF值為: 32.35159967390017
真正, 頻數(shù)為: 2, TF-IDF值為: 14.940639869560068
意義, 頻數(shù)為: 1, TF-IDF值為: 8.055282435501189
對(duì), 頻數(shù)為: 5, TF-IDF值為: 24.03677461028802
其中, 頻數(shù)為: 3, TF-IDF值為: 21.16584730650357
之一, 頻數(shù)為: 2, TF-IDF值為: 14.11056487100238
火箭_NBA球隊(duì)名:
馬刺, 頻數(shù)為: 1, TF-IDF值為: 7.470319934780034
球隊(duì), 頻數(shù)為: 40, TF-IDF值為: 158.712783770034
常規(guī)賽, 頻數(shù)為: 14, TF-IDF值為: 73.4709851882102
只有, 頻數(shù)為: 1, TF-IDF值為: 7.470319934780034
對(duì), 頻數(shù)為: 10, TF-IDF值為: 48.07354922057604
之一, 頻數(shù)為: 1, TF-IDF值為: 7.05528243550119

經(jīng)過詞義消岐,火箭在該句子中的意思為 NBA球隊(duì)名 .

輸入句子為:

姚明是火箭隊(duì)的主要得分手之一。

輸出結(jié)果為:

['姚明', '火箭隊(duì)', '主要', '得分手', '之一']
火箭_燃?xì)馔七M(jìn)裝置:
主要, 頻數(shù)為: 9, TF-IDF值為: 51.60018906552445
之一, 頻數(shù)為: 2, TF-IDF值為: 14.11056487100238
火箭_NBA球隊(duì)名:
姚明, 頻數(shù)為: 18, TF-IDF值為: 90.99508383902142
火箭隊(duì), 頻數(shù)為: 133, TF-IDF值為: 284.1437533641371
之一, 頻數(shù)為: 1, TF-IDF值為: 7.05528243550119

經(jīng)過詞義消岐,火箭在該句子中的意思為 NBA球隊(duì)名 .

輸入的句子為:

從1992年開始研制的長(zhǎng)征二號(hào)F型火箭,是中國(guó)航天史上技術(shù)最復(fù)雜、可靠性和安全性指標(biāo)最高的運(yùn)載火箭。

輸出結(jié)果為:

['從', '1992', '年', '開始', '研制', '長(zhǎng)征二號(hào)', 'F', '型', '中國(guó)', '航天史', '技術(shù)', '最', '復(fù)雜', '、', '可靠性', '和', '安全性', '指標(biāo)', '最高', '運(yùn)載火箭']
火箭_燃?xì)馔七M(jìn)裝置:
從, 頻數(shù)為: 6, TF-IDF值為: 29.312144604353264
1992, 頻數(shù)為: 1, TF-IDF值為: 6.733354340613827
年, 頻數(shù)為: 43, TF-IDF值為: 107.52982410441274
開始, 頻數(shù)為: 5, TF-IDF值為: 30.27641217750595
研制, 頻數(shù)為: 25, TF-IDF值為: 110.28565614316162
長(zhǎng)征二號(hào), 頻數(shù)為: 37, TF-IDF值為: 159.11461253349566
F, 頻數(shù)為: 7, TF-IDF值為: 40.13348038429679
中國(guó), 頻數(shù)為: 45, TF-IDF值為: 153.51418105769093
技術(shù), 頻數(shù)為: 27, TF-IDF值為: 119.10850863461454
最, 頻數(shù)為: 2, TF-IDF值為: 7.614709844115208
、, 頻數(shù)為: 117, TF-IDF值為: 335.25857156467714
可靠性, 頻數(shù)為: 5, TF-IDF值為: 30.27641217750595
和, 頻數(shù)為: 76, TF-IDF值為: 191.22539545388003
安全性, 頻數(shù)為: 2, TF-IDF值為: 14.940639869560068
運(yùn)載火箭, 頻數(shù)為: 95, TF-IDF值為: 256.28439093389505
火箭_NBA球隊(duì)名:
從, 頻數(shù)為: 5, TF-IDF值為: 24.42678717029439
1992, 頻數(shù)為: 2, TF-IDF值為: 13.466708681227654
年, 頻數(shù)為: 52, TF-IDF值為: 130.0360663588247
開始, 頻數(shù)為: 2, TF-IDF值為: 12.11056487100238
中國(guó), 頻數(shù)為: 4, TF-IDF值為: 13.64570498290586
最, 頻數(shù)為: 3, TF-IDF值為: 11.422064766172813
、, 頻數(shù)為: 16, TF-IDF值為: 45.847326025938756
和, 頻數(shù)為: 31, TF-IDF值為: 77.99983235618791
最高, 頻數(shù)為: 8, TF-IDF值為: 59.76255947824027

經(jīng)過詞義消岐,火箭在該句子中的意思為 燃?xì)馔七M(jìn)裝置 .

輸入句子為:

到目前為止火箭已經(jīng)在休斯頓進(jìn)行了電視宣傳,并在大街小巷豎起廣告欄。

輸出結(jié)果為:

['到', '目前為止', '已經(jīng)', '休斯頓', '進(jìn)行', '電視', '宣傳', '并', '大街小巷', '豎起', '廣告欄']
火箭_燃?xì)馔七M(jìn)裝置:
到, 頻數(shù)為: 11, TF-IDF值為: 39.19772273088667
已經(jīng), 頻數(shù)為: 2, TF-IDF值為: 13.466708681227654
進(jìn)行, 頻數(shù)為: 14, TF-IDF值為: 68.39500407682429
并, 頻數(shù)為: 11, TF-IDF值為: 49.17351928258037
火箭_NBA球隊(duì)名:
到, 頻數(shù)為: 6, TF-IDF值為: 21.38057603502909
已經(jīng), 頻數(shù)為: 2, TF-IDF值為: 13.466708681227654
休斯頓, 頻數(shù)為: 2, TF-IDF值為: 14.940639869560068
進(jìn)行, 頻數(shù)為: 2, TF-IDF值為: 9.770714868117755
并, 頻數(shù)為: 5, TF-IDF值為: 22.351599673900168

經(jīng)過詞義消岐,火箭在該句子中的意思為 燃?xì)馔七M(jìn)裝置 .

總結(jié)

??對(duì)于筆者的這個(gè)算法,雖然有一定的效果,但是也不總是識(shí)別正確。比如,對(duì)于最后一個(gè)測(cè)試的句子,識(shí)別的結(jié)果就是錯(cuò)誤的,其實(shí)“休斯頓”才是識(shí)別該詞語義項(xiàng)的關(guān)鍵詞,但很遺憾,在筆者的算法中,“休斯頓”的權(quán)重并不高。
??對(duì)于詞義消岐算法,如果還是筆者的這個(gè)思路,那么有以下幾方面需要改進(jìn):

  • 語料大小及豐富程度;
  • 停用詞的擴(kuò)充;
  • 更好的算法。

??筆者的這篇文章僅作為詞義消岐的簡(jiǎn)介以及簡(jiǎn)單實(shí)現(xiàn),希望能對(duì)讀者有所啟發(fā)~

注意:本人現(xiàn)已開通微信公眾號(hào): Python爬蟲與算法(微信號(hào)為:easy_web_scrape), 歡迎大家關(guān)注哦~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容