NLP—TextRank算法獲取文本關鍵詞和摘要

    TextRank算法主要用來生成文本的關鍵詞和摘要,其來源於PageRank算法,下面先介紹PageRank。PageRank在搜索領域有廣泛的應用,最開始用來計算網頁的重要性。可以把整個網絡看成有向圖,網頁是結點,如果網頁A中存在一條鏈接指向網頁B,則認爲A到B存在一條有向邊。


    S(Vi)表示網頁i的重要性即PR值,d是阻尼係數(確保當一個網頁沒有鏈接指向它時,也有一定PR值),經驗一般設置爲0.85,In(Vi)是包含指向網頁i的鏈接的網頁集合。Out(Vj)是網頁j中指向其他網頁的鏈接的集合,S(Vj)代表網頁j的PR值。所以對於一個網頁,它的重要性取決於到它的每個鏈接頁面的重要性之和,每個鏈接到該網頁的頁面的PR值S(Vj)同時還需要對其他的頁面貢獻評分,所以除以了|Out(Vj)|。另外,網頁的重要性不單單由鏈接網頁決定,還包含一定的概率要不要接受其他網頁的重要性評價,這也就是d的作用。
    初始時,可以將各個網頁的重要性值設置爲1,PageRank經過多次迭代最終得到結果。公式左邊代表迭代後的網頁PR值,等號右邊的是迭代前的PR值。

TextRank提取關鍵詞:


    該公式跟PageRank基本相似,其中多了一個重要參數Wji,用來代表兩個節點間的重要程度。算法流程如下:將給定的文本分割成句子,T=[S1,S2,......,Sm],然後對每個句子進行分詞,得到Si=[pi1,pi2,...,pin],構建詞圖。設定窗口大小爲k,[p1,p2,...,pk][p2,p3,...,pk+1]等都是一個個的窗口,在一個窗口中如果兩個單詞同時出現,則認爲對應單詞節點間存在一個邊。根據這個思想和公式迭代傳播詞圖中的各節點權重,直至收斂。最後對節點權重進行排序,從而得到最重要的幾個單詞作爲候選關鍵詞。若提取出來的若干關鍵詞在文本中相鄰,則構成一個關鍵短語。

TextRank生成摘要:



    將文本中的每個句子看做一個節點,如果兩個句子有相似性,則認爲兩個句子對應的節點之間存在一條無向有權邊。句子相似度的計算式子如上所示,Si、Sj兩個句子,Wk代表句子中的單詞,那麼分子代表同時出現在兩個句子中的單詞的個數,分母是對句子中單詞個數求對數之和。分母使用對數可以抵消長句子在相似度計算上的優勢(長句子包含相同單詞的可能性更高)。根據以上相似度公式循環迭代計算得到任意兩個節點之間的相似度,構建節點連接圖,最後計算PR值,經排序選出PR值最高的的節點對應的句子作爲摘要。

def solve(self): #針對抽關鍵句
        for cnt, doc in enumerate(self.docs):
            scores = self.bm25.simall(doc) #在本實現中,使用的不是前面提到的公式,而是使用的BM25算法,之前會有一個預處理(self.bm25 = BM25(docs)),然後求doc跟其他所有預料的相似程度。
            self.weight.append(scores)
            self.weight_sum.append(sum(scores)-scores[cnt])#需要減掉本身的權重。
            self.vertex.append(1.0)
        for _ in range(self.max_iter):
            m = []
            max_diff = 0
            for i in range(self.D):#每個文本都要計算與其他所有文檔的鏈接,然後計算出重要程度。
                m.append(1-self.d)
                for j in range(self.D):
                    if j == i or self.weight_sum[j] == 0:
                        continue
                    m[-1] += (self.d*self.weight[j][i]
                              / self.weight_sum[j]*self.vertex[j])
                              #利用前面的公式求解
                if abs(m[-1] - self.vertex[i]) > max_diff:
                #找到該次迭代中,變化最大的一次情況。
                    max_diff = abs(m[-1] - self.vertex[i])
            self.vertex = m
            if max_diff <= self.min_diff:#當變化最大的一次,仍然小於某個閾值時認爲可以滿足跳出條件,不用再循環指定的次數。
                break
        self.top = list(enumerate(self.vertex))
        self.top = sorted(self.top, key=lambda x: x[1], reverse=True)


def solve(self):#針對抽關鍵詞
        for doc in self.docs:
            que = []
            for word in doc:
                if word not in self.words:
                    self.words[word] = set()
                    self.vertex[word] = 1.0
                que.append(word)
                if len(que) > 5:
                    que.pop(0)
                for w1 in que:
                    for w2 in que:
                        if w1 == w2:
                            continue
                        self.words[w1].add(w2)
                        self.words[w2].add(w1)
        for _ in range(self.max_iter):
            m = {}
            max_diff = 0
            tmp = filter(lambda x: len(self.words[x[0]]) > 0,
                         self.vertex.items())
            tmp = sorted(tmp, key=lambda x: x[1] / len(self.words[x[0]]))
            for k, v in tmp:
                for j in self.words[k]:
                    if k == j:
                        continue
                    if j not in m:
                        m[j] = 1 - self.d
                    m[j] += (self.d / len(self.words[k]) * self.vertex[k]) #利用之前提到的公式,簡化的結果。
            for k in self.vertex:
                if k in m and k in self.vertex:
                    if abs(m[k] - self.vertex[k]) > max_diff:
                        max_diff = abs(m[k] - self.vertex[k])
            self.vertex = m
            if max_diff <= self.min_diff:
                break
        self.top = list(self.vertex.items())
        self.top = sorted(self.top, key=lambda x: x[1], reverse=True)

參考資料:https://blog.csdn.net/kamendula/article/details/51756552

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章