文字自動摘要:基於TextRank的中文新聞摘要

Luv_GEM發表於2019-05-18

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篇,都是關於證監會主席易會滿同志新官上任的報導,新聞的大致內容是易會滿同志怎麼對中國資本市場的改革指點江山。

文件的原網頁(看到百家號,不要鄙視我):

這個小實踐的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

相關文章