textrank算法介紹

TextRank算法

TextRank算法基於PageRank,用於爲文本生成關鍵字和摘要。其論文是: 
Mihalcea R, Tarau P. TextRank: Bringing order into texts[C]. Association for Computational Linguistics, 2004.

先從PageRank講起 
在淺入淺出:PageRank算法這篇博客中我做過簡要的介紹,這裏再補充一下。 
PageRank最開始用來計算網頁的重要性。整個www可以看作一張有向圖圖,節點是網頁。如果網頁A存在到網頁B的鏈接,那麼有一條從網頁A指向網頁B的有向邊。 
構造完圖後,使用下面的公式: 
 
S(Vi)是網頁i的中重要性(PR值)。d是阻尼係數,一般設置爲0.85。In(Vi)是存在指向網頁i的鏈接的網頁集合。Out(Vj)是網頁j中的鏈接存在的鏈接指向的網頁的集合。|Out(Vj)|是集合中元素的個數。 
也就是說: 
對於一篇網頁來說,可以這麼理解:它的重要性,取決於到他的每個鏈接頁面的重要性之和來決定的。每個鏈接到該頁面的頁面的重要性S(Vj)S(Vj)還需要對所有它所出去的頁面所評分。所以除以了OUT(Vj)。同時,該頁面S(Vj)S(Vj)的重要性不能單單由其他的鏈接頁面決定,還包含一定的概率來決定要不要接受由其他頁面來決定,這也就是d的作用。

PageRank需要使用上面的公式多次迭代才能得到結果。初始時,可以設置每個網頁的重要性爲1。上面公式等號左邊計算的結果是迭代後網頁i的PR值,等號右邊用到的PR值全是迭代前的。 
舉個例子: 
 
上圖表示了三張網頁之間的鏈接關係,直覺上網頁A最重要。

依然能判斷出A、B、C的重要性。 
使用TextRank提取關鍵字 
將原文本拆分爲句子,在每個句子中過濾掉停用詞(可選),並只保留指定詞性的單詞(可選)。由此可以得到句子的集合和單詞的集合。 
每個單詞作爲pagerank中的一個節點。設定窗口大小爲k,假設一個句子依次由下面的單詞組成: 
w1,w2,w3,w4,w5,…,wn 
[w1,w2,…,wk]、[w2,w3,…,wk+1]、[w3,w4,…,wk+2]等都是一個窗口。在一個窗口中的任兩個單詞對應的節點之間存在一個無向無權的邊。 
基於上面構成圖,可以計算出每個單詞節點的重要性。最重要的若干單詞可以作爲關鍵詞。 
使用TextRank提取關鍵短語 
參照“使用TextRank提取關鍵詞”提取出若干關鍵詞。若原文本中存在若干個關鍵詞相鄰的情況,那麼這些關鍵詞可以構成一個關鍵短語。 
例如,在一篇介紹“支持向量機”的文章中,可以找到三個關鍵詞支持、向量、機,通過關鍵短語提取,可以得到支持向量機。 使用TextRank提取摘要 
將每個句子看成圖中的一個節點,若兩個句子之間有相似性,認爲對應的兩個節點之間有一個無向有權邊,權值是相似度。 
通過pagerank算法計算得到的重要性最高的若干句子可以當作摘要。 
論文中使用下面的公式計算兩個句子Si和Sj的相似度: 

分子是在兩個句子中都出現的單詞的數量。|Si|是句子i的單詞數。 
由於是有權圖,PageRank公式略做修改: 

但是很明顯我只想計算關鍵字,如果把一個單詞視爲一個句子的話,那麼所有句子(單詞)構成的邊的權重都是0(沒有交集,沒有相似性),所以分子分母的權值w約掉了,算法退化爲PageRank。所以說,這裏稱關鍵字提取算法爲PageRank也不爲過。

TextRank代碼

使用textrank源代碼可以抽取摘要,也可以抽取關鍵詞。 

下面是一個textrank算法的python源碼:

# -*- coding:utf-8 -*-
import numpy as np
import jieba
import jieba.posseg as pseg


class TextRank(object):
    
    def __init__(self, sentence, window, alpha, iternum):
        self.sentence = sentence
        self.window = window
        self.alpha = alpha
        self.edge_dict = {} #記錄節點的邊連接字典
        self.iternum = iternum#迭代次數


    #對句子進行分詞
    def cutSentence(self):
        jieba.load_userdict('user_dict.txt')
        tag_filter = ['a','d','n','v']
        seg_result = pseg.cut(self.sentence)
        self.word_list = [s.word for s in seg_result if s.flag in tag_filter]
        print(self.word_list)


    #根據窗口,構建每個節點的相鄰節點,返回邊的集合
    def createNodes(self):
        tmp_list = []
        word_list_len = len(self.word_list)
        for index, word in enumerate(self.word_list):
            if word not in self.edge_dict.keys():
                tmp_list.append(word)
                tmp_set = set()
                left = index - self.window + 1#窗口左邊界
                right = index + self.window#窗口右邊界
                if left < 0: left = 0
                if right >= word_list_len: right = word_list_len
                for i in range(left, right):
                    if i == index:
                        continue
                    tmp_set.add(self.word_list[i])
                self.edge_dict[word] = tmp_set


    #根據邊的相連關係,構建矩陣
    def createMatrix(self):
        self.matrix = np.zeros([len(set(self.word_list)), len(set(self.word_list))])
        self.word_index = {}#記錄詞的index
        self.index_dict = {}#記錄節點index對應的詞


        for i, v in enumerate(set(self.word_list)):
            self.word_index[v] = i
            self.index_dict[i] = v
        for key in self.edge_dict.keys():
            for w in self.edge_dict[key]:
                self.matrix[self.word_index[key]][self.word_index[w]] = 1
                self.matrix[self.word_index[w]][self.word_index[key]] = 1
        #歸一化
        for j in range(self.matrix.shape[1]):
            sum = 0
            for i in range(self.matrix.shape[0]):
                sum += self.matrix[i][j]
            for i in range(self.matrix.shape[0]):
                self.matrix[i][j] /= sum


    #根據textrank公式計算權重
    def calPR(self):
        self.PR = np.ones([len(set(self.word_list)), 1])
        for i in range(self.iternum):
            self.PR = (1 - self.alpha) + self.alpha * np.dot(self.matrix, self.PR)


    #輸出詞和相應的權重
    def printResult(self):
        word_pr = {}
        for i in range(len(self.PR)):
            word_pr[self.index_dict[i]] = self.PR[i][0]
        res = sorted(word_pr.items(), key = lambda x : x[1], reverse=True)
        print(res)


if __name__ == '__main__':
    s = '程序員(英文Programmer)是從事程序開發、維護的專業人員。一般將程序員分爲程序設計人員和程序編碼人員,但兩者的界限並不非常清楚,特別是在中國。軟件從業人員分爲初級程序員、高級程序員、系統分析員和項目經理四大類。'
    tr = TextRank(s, 3, 0.85, 700)
    tr.cutSentence()
    tr.createNodes()
    tr.createMatrix()
    tr.calPR()
    tr.printResult()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章