TextRank演算法源自於PageRank演算法。PageRank演算法最初是作為網際網路網頁排序的方法,經過輕微地改動,可以被應用於文字摘要領域。
本文分為兩部分,第一部分介紹TextRank做文字自動摘要的原理,第二部分介紹用TextRank做中文新聞摘要的案例。
一、基於TextRank的自動摘要原理
1、PageRank演算法
首先看PageRank的相關概念。PageRank對於每個網頁頁面都給出一個正實數,表示網頁的重要程度,PageRank值越高,表示網頁越重要,在網際網路搜尋的排序中越可能被排在前面。假設整個網際網路是一個有向圖,節點是網頁,每條邊是轉移概率。網頁瀏覽者在每個頁面上依照連線出去的超連結,以等概率跳轉到下一個網頁,並且在網頁上持續不斷地進行這樣的隨機跳轉,這個過程形成了一階馬爾科夫鏈,比如下圖,每個笑臉是一個網頁,既有其他網頁跳轉到該網頁,該網頁也會跳轉到其他網頁。在不斷地跳轉之後,這個馬爾科夫鏈會形成一個平穩分佈,而PageRank就是這個平穩分佈,每個網頁的PageRank值就是平穩概率。
PageRank的核心公式是PageRank值的計算公式。這個公式來自於《統計學習方法》,和很多部落格上的公式有點輕微的差別,那就是等號右邊的平滑項不是(1-d),而是(1-d)/n。
加平滑項是因為有些網頁沒有跳出去的連結,那麼轉移到其他網頁的概率將會是0,這樣就無法保證存在馬爾科夫鏈的平穩分佈。於是,我們假設網頁以等概率(1/n)跳轉到任何網頁,再按照阻尼係數d,對這個等概率(1/n)與存在連結的網頁的轉移概率進行線性組合,那麼馬爾科夫鏈一定存在平穩分佈,一定可以得到網頁的PageRank值。
所以PageRank的定義意味著網頁瀏覽者按照以下方式在網上隨機遊走:以概率d按照存在的超連結隨機跳轉,以等概率從超連結跳轉到下一個頁面;或以概率(1-d)進行完全隨機跳轉,這時以等概率(1/n)跳轉到任意網頁。
PageRank的計算是一個迭代過程,先假設一個初始的PageRank分佈,通過迭代,不斷計算所有網頁的PageRank值,直到收斂為止,也就是:
2、TextRank演算法
在文字自動摘要的案例中,TextRank和PageRank的相似之處在於:
-
用句子代替網頁
-
任意兩個句子的相似性等價於網頁轉換概率
-
相似性得分儲存在一個方形矩陣中,類似於PageRank的矩陣M
不過公式有些小的差別,那就是用句子的相似度類比於網頁轉移概率,用歸一化的句子相似度代替了PageRank中相等的轉移概率,這意味著在TextRank中,所有節點的轉移概率不會完全相等。
然後迭代過程就和PageRank一致了。
3、TextRank做單領域多文字的自動摘要
用TextRank做單領域多文字的自動摘要的過程如下:
- 把所有文章整合成文字資料,並把文字分割成單個句子;
- 用WordAVG的方法,將每個句子中所有單詞的詞向量合併為句子的向量表示;
- 計算句子向量間的相似性並存放在矩陣中,作為轉移概率矩陣M;
- 然後將轉移概率矩陣轉換為以句子為節點、相似性得分為邊的圖結構,用於句子TextRank計算;
- 對句子按照TextRank值進行排序,排名最靠前的n個句子作為摘要。
好的,那下面我們就用TextRank演算法,以及按照上面的流程,做一個新聞自動摘要的小案例。
二、基於TextRank的中文新聞摘要
參考了一篇用TextRank做英文新聞摘要的文章。
1、文章:《手把手|基於TextRank演算法的文字摘要(附Python程式碼)》 https://mp.weixin.qq.com/s/fGaEYvo3WYKdzA3r8l6O3g 2、github地址 :https://github.com/prateekjoshi565/textrank_text_summarization
本文要處理的新聞一共3篇,都是關於證監會主席易會滿同志新官上任的報導,新聞的大致內容是易會滿同志怎麼對中國資本市場的改革指點江山。
文件的原網頁(看到百家號,不要鄙視我):
- https://baijiahao.baidu.com/s?id=1626615436040775944&wfr=spider&for=pc
- https://baijiahao.baidu.com/s?id=1626670136476331971&wfr=spider&for=pc
- http://finance.sina.com.cn/roll/2019-02-28/doc-ihrfqzka9798377.shtml
這個小實踐的github:https://github.com/DengYangyong/textrank_summarization
好,那就開始。
1、整合文件,劃分句子
首先把文件讀入,放在列表中,可以看到,有些句子已經被劃分出來了。
['資訊量巨大!易會滿首秀,直面科創板8大問題,對散戶加速入場笑而不語……', '每日經濟新聞', '02-2717:56', '每經編輯:郭鑫 王曉波', '圖片來源:新華社記者 李鑫 攝', '易會滿上任一個月,還沒有在公開場合說過一句話。', '2月27日下午三點半開始,中國證監會主席易會滿在北京國新辦出席其首場新聞釋出會,離釋出會開始前兩小時現場已經座無虛席,只等易主席來到現場。此外,副主席李超、副主席方星海,上海證券交易所理事長黃紅元等也共同出席。',
...]
不過通過觀察,我們可以發現存在兩個問題:
一是以[。?!;]作為句子的分隔符,那麼列表中的每個字串元素中可能有多個句子;
二是每個字串元素可能以[:,]結尾,也就是說可能是一個不完整的句子。
考慮到這只是一個小案例,所以就沒花太多時間,僅僅處理一下第一個問題,把句子按照[。?!;]進行劃分,如果字串元素是不完整的句子,那也作為一句。
import numpy as np import pandas as pd import re,os,jieba from itertools import chain """第一步:把文件劃分成句子""" # 文件所在的資料夾 c_root = os.getcwd()+os.sep+"cnews"+os.sep sentences_list = [] for file in os.listdir(c_root): fp = open(c_root+file,'r',encoding="utf8") for line in fp.readlines(): if line.strip(): # 把元素按照[。!;?]進行分隔,得到句子。 line_split = re.split(r'[。!;?]',line.strip()) # [。!;?]這些符號也會劃分出來,把它們去掉。 line_split = [line.strip() for line in line_split if line.strip() not in ['。','!','?',';'] and len(line.strip())>1] sentences_list.append(line_split) sentences_list = list(chain.from_iterable(sentences_list)) print("前10個句子為:\n") print(sentences_list[:10])
前10個句子為: ['資訊量巨大', '易會滿首秀,直面科創板8大問題,對散戶加速入場笑而不語……', '每日經濟新聞', '02-2717:56',
'每經編輯:郭鑫 王曉波', '圖片來源:新華社記者 李鑫 攝', '易會滿上任一個月,還沒有在公開場合說過一句話', '2月27日下午三點半開始,中國證監會主席易會滿在北京國新辦出席其首場新聞釋出會,離釋出會開始前兩小時現場已經座無虛席,只等易主席來到現場', '此外,副主席李超、副主席方星海,上海證券交易所理事長黃紅元等也共同出席', '這可能是這個月國內關注的人最多的一場新聞釋出會了']
2、文字預處理
文字預處理包括去除停用詞和非漢字字元,並進行分詞。處理的過程要保證處理之後的句子的數量和處理之前的一樣,因為後面我們計算了每個句子的textrank值之後,需要根據textrank值的大小,取出相應的句子作為摘要。
比如 '02-2717:56' 這個句子整個被過濾了,那就令這個句子為[],下面也會給它一個句子的向量表示,只是元素都為0。
"""第二步:文字預處理,去除停用詞和非漢字字元,並進行分詞""" #建立停用詞列表 stopwords = [line.strip() for line in open('./stopwords.txt',encoding='UTF-8').readlines()] # 對句子進行分詞 def seg_depart(sentence): # 去掉非漢字字元 sentence = re.sub(r'[^\u4e00-\u9fa5]+','',sentence) sentence_depart = jieba.cut(sentence.strip()) word_list = [] for word in sentence_depart: if word not in stopwords: word_list.append(word) # 如果句子整個被過濾掉了,如:'02-2717:56'被過濾,那就返回[],保持句子的數量不變 return word_list sentence_word_list = [] for sentence in sentences_list: line_seg = seg_depart(sentence) sentence_word_list.append(line_seg) print("一共有",len(sentences_list),'個句子。\n') print("前10個句子分詞後的結果為:\n",sentence_word_list[:10]) # 保證處理後句子的數量不變,我們後面才好根據textrank值取出未處理之前的句子作為摘要。 if len(sentences_list) == len(sentence_word_list): print("\n資料預處理後句子的數量不變!")
一共有 347 個句子。 前10個句子分詞後的結果為: [['資訊量'],
['易會', '滿首秀', '直面', '科創板', '散戶', '加速', '入場', '笑', '不語'],
['每日', '經濟', '新聞'],
[],
['每經', '編輯', '郭鑫', '王曉波'],
['圖片', '來源', '李鑫', '攝'],
['易會', '上任', '一個月', '公開場合', '說', '一句', '話'],
['三點', '中國證監會', '主席', '易會', '北京', '國新辦', '出席', '首場', '新聞', '釋出會', '釋出會', '前', '兩', '小時', '現場', '座無虛席', '易', '主席', '來到', '現場'],
['副', '主席', '李超', '副', '主席', '星海', '上海證券交易所', '理事長', '黃紅元', '出席'],
['國內', '關注', '一場', '新聞', '釋出會']] 資料預處理後句子的數量不變!
3、載入word2vec詞向量
從這裡下載了金融新聞word2vec詞向量:https://github.com/Embedding/Chinese-Word-Vectors。
詞向量是300維的,字和詞語都有。我們把詞向量載入進來,做成一個字典,共有467140個詞語或字。
"""第三步:準備詞向量""" word_embeddings = {} f = open('./sgns.financial.char', encoding='utf-8') for line in f: # 把第一行的內容去掉 if '467389 300\n' not in line: values = line.split() # 第一個元素是詞語 word = values[0] embedding = np.asarray(values[1:], dtype='float32') word_embeddings[word] = embedding f.close() print("一共有"+str(len(word_embeddings))+"個詞語/字。")
一共有467140個詞語/字。
4、得到詞語的embedding,用WordAVG作為句子的向量表示
WordAVG也就是先得到句子中的所有詞語的詞向量,然後求詞向量的平均,作為該句子的向量表示。WordAVG可以用來計算句子的相似度。
"""第四步:得到詞語的embedding,用WordAVG作為句子的向量表示""" sentence_vectors = [] for i in sentence_word_list: if len(i)!=0: # 如果句子中的詞語不在字典中,那就把embedding設為300維元素為0的向量。 # 得到句子中全部詞的詞向量後,求平均值,得到句子的向量表示 v = sum([word_embeddings.get(w, np.zeros((300,))) for w in i])/(len(i)) else: # 如果句子為[],那麼就向量表示為300維元素為0個向量。 v = np.zeros((300,)) sentence_vectors.append(v)
5、計算句子之間的餘弦相似度,構成相似度矩陣
"""第五步:計算句子之間的餘弦相似度,構成相似度矩陣""" sim_mat = np.zeros([len(sentences_list), len(sentences_list)]) from sklearn.metrics.pairwise import cosine_similarity for i in range(len(sentences_list)): for j in range(len(sentences_list)): if i != j: sim_mat[i][j] = cosine_similarity(sentence_vectors[i].reshape(1,300), sentence_vectors[j].reshape(1,300))[0,0] print("句子相似度矩陣的形狀為:",sim_mat.shape)
句子相似度矩陣的形狀為: (347, 347)
6、迭代得到句子的textrank值,排序並取出摘要
以句子為節點、相似性得分為轉移概率,構建圖結構,然後迭代得到句子的TextRank分數。
對句子按照TextRank值進行降序排序,取出排名最靠前的10個句子作為摘要。
"""第六步:迭代得到句子的textrank值,排序並取出摘要""" import networkx as nx # 利用句子相似度矩陣構建圖結構,句子為節點,句子相似度為轉移概率 nx_graph = nx.from_numpy_array(sim_mat) # 得到所有句子的textrank值 scores = nx.pagerank(nx_graph) # 根據textrank值對未處理的句子進行排序 ranked_sentences = sorted(((scores[i],s) for i,s in enumerate(sentences_list)), reverse=True) # 取出得分最高的前10個句子作為摘要 sn = 10 for i in range(sn): print("第"+str(i+1)+"條摘要:\n\n",ranked_sentences[i][1],'\n')
第1條摘要:
在新聞釋出會上,易會滿表示,我到證監會工作今天是31天,剛剛滿月,是資本市場的新兵,從市場參與者到監管者,角色轉換角色挑戰很大,如履薄冰,不敢懈怠,唯恐辜負中央信任和市場期待,這也是我做好工作的動力,
近期加強調查研究,和部門協作維護市場平穩發展,維護科創板前期基礎工作
第2條摘要:
易會滿在新聞釋出會上表示,防止發生系統性風險是底線和根本任務,當前受國內外多種因素影響,資本市場風險形式嚴峻複雜,證監會將堅持精準施策,做好股票質押私募基金、場外配資和地方各類場所的重點領域風險的防範化解
和處置工作,完善資本市場逆週期機制,健全及時反映風險波動系統,運用大資料、人工智慧等手段對上市公司專業監管,平衡事前、事中、事後關係,監管埠前移,強化監管效能
第3條摘要:
證監會將堅持精準施策,做好股票質押私募基金、場外配資和地方各類場所的重點領域風險的防範化解和處置工作,完善資本市場逆週期機制,健全及時反映風險波動系統,運用大資料、人工智慧等手段對上市公司專業監管,
平衡事前、事中、事後關係,監管埠前移,強化監管效能,切實做好打鐵必須自身硬,做好中介機構和高管的強監管
第4條摘要:
這兩者出發點和規則不同,我來證監會後不斷學習研究,這麼專業的問題證監會有專業化的團隊,資本市場是大的生態,什麼叫市場,應該是依靠市場各參與者,調動市場參與者,市場規律辦事, 培養健康生態比什麼都重要,
這一考驗和要求比專業更重要,生態建設好了,資本市場的健康發展才有保證
第5條摘要:
證監會副主席李超今天也給市場吃下定心丸:“對二級市場影響的問題,(科創板)設立時已經高度關注,在一系列的制度、規則層面作了相應安排
第6條摘要:
他表示,第一,設立科創板主要目的是增強資本市場對實體經濟的包容性,更好地服務具有核心技術、行業領先、有良好發展前景和口碑的企業,通過改革進一步完善支援創新的資本形成機制
第7條摘要:
一是提高巨集觀思維能力,貼近市場各參與方,堅持市場導向、法治導向、監管導向,加強對資本市場巨集觀戰略問題的研究思考,加強頂層設計,增強戰略定力,穩步推進重點關注問題的改革創新,在改革中、在發展中破解難題
第8條摘要:
集體學習的通稿中,中央給資本市場定的“法治化”要求有不少,比如“把好市場入口和市場出口兩道關,加強全程監管”、“解決資本市場違法違規成本過低問題”
第9條摘要:
易會滿表示,證監會將以xijinping新時代中國特色社會主義思想為指導,在國務院金融委的統一指揮協調下,主動加強與相關部委、地方黨委政府和市場各方的溝通協作,努力形成工作合力,共同促進資本市場高質量發展
第10條摘要:
目前,資本市場已經回暖,這為改革提供了良好市場條件,我們要齊心協力,堅持“嚴標準、穩起步”的原則,積極做好落實和應對工作,注重各市場之間的平衡,確保改革平穩啟動實施
Nice!
這樣就完成了一個文字自動摘要的小實踐了。
這是抽取型的摘要方法,以後再探索其他的文字自動摘要方法。
參考資料:
1、李航:《統計學習方法》(第二版)第21章
2、https://mp.weixin.qq.com/s/fGaEYvo3WYKdzA3r8l6O3g