一、关键字
1.项目概述
主要完成一个相对简单的 “关键字提取” 算法,可以提取英文新闻中关键的消息
2.项目构思
拥有关键词最多的句子就是最重要的句子。我们把句子按照关键词数量的多少排序,取前 n 句,即可汇总成我们的摘要。
所以我们的工作可以分为如下步骤:
- 给在文章中出现的单词按照算法计算出重要性
- 按照句子中单词的重要性算出句子的总分
- 按照句子的总分给文章中的每个句子排序
- 取出前 n 个句子作为摘要
3.项目技术栈
from nltk.tokenize import sent_tokenize, word_tokenize
from nltk.corpus import stopwords
from collections import defaultdict
from string import punctuation
from heapq import nlargest
nltk.tokenize
是 NLTK 提供的分词工具包。所谓的分词 tokenize
实际就是把段落分成句子,把句子分成一个个单词的过程。我们导入的 sent_tokenize()
函数对应的是分段为句。 word_tokenize()
函数对应的是分句为词。
stopwords
是一个列表,包含了英文中那些频繁出现的词,如 am
, is
, are
。
defaultdict
是一个带有默认值的字典容器。
puctuation
是一个列表,包含了英文中的标点和符号。
nlargest()
函数可以很快地求出一个容器中最大的 n
个数字。
4.实现思路
词频统计
首先我们先要统计出每个词在文章中出现的次数,在统计出次数之后,我们可以知道出现次数最多的词的出现次数 m
。
我们把每个词 wi
出现的次数 mi
除以 m
,算出每个词的 “重要系数”。
重要性高的词就是我们的关键词。
def compute_frequencies(word_sent):
freq = defaultdict(int)
for s in word_sent:
for word in s:
if word not in stopwords:
freq[word] += 1
m = float(max(freq.values()))
for w in list(freq.keys()):
freq[w] = freq[w]/m
if freq[w] >= max_cut or freq[w] <= min_cut:
del freq[w]
return freq
获得摘要
现在每个单词(stopwords 和出现频率异常的单词除外)都有了 “重要性” 这样一个量化描述的值。我们现在需要统计的是一个句子中单词的重要性。只需要把句子中每个单词的重要性叠加就行了。
def summarize(text, n):
sents = sent_tokenize(text)
assert n <= len(sents)
word_sent = [word_tokenize(s.lower()) for s in sents]
freq = compute_frequencies(word_sent)
ranking = defaultdict(int)
for i, word in enumerate(word_sent):
for w in word:
if w in freq:
ranking[i] += freq[w]
sents_idx = rank(ranking, n)
return [sents[j] for j in sents_idx]
二、TextRank 算法
1.项目概述及项目构思
PageRank 算法
PageRank 是根据网页之间相互的超链接计算网页重要性 的技术,是 Google 的创始人拉里 · 佩奇和谢尔盖 · 布林于 1998 年在斯坦福大学发明了这项技术。
于是我们就可以列出一张邻接矩阵来描述推荐出去 (Out) 的箭头,别的界面推荐进来 (In) 的箭头。形成一个邻接矩阵。每一列代表的是你推荐的页面 (Out) 。如 A 列,代表的就是 A 推荐的页面。如表格所描述的,A 谁也没有推荐。每一行代表的是谁推荐了你 (In)。如 A 行,代表的就是推荐 A 的页面。如表格所描述的,D 推荐了 A。有了这张邻接矩阵作为基础,我们就能够计算 PageRank 的核心:每一个页面的分数 S。
𝑆(𝑉𝑖)=(1−𝑑)+𝑑∗∑𝑗∈In(𝑉𝑖)1lout(𝑉𝑗)|𝑆(𝑉𝑗)
我们会发现,每一个页面的分数 𝑆(𝑉𝑖)都是依赖于别的页面的分数 𝑆(𝑉𝑗)的。我们需要对每个页面的分数进行初始化。
就这样一步一步算出每一个顶点的值,然后接着从 𝐴页面开始继续算,一遍一遍迭代,直到每个页面的分数都不再变化为止。这样,我们就得到了每一个页面的评分 𝑆 。
当我们把 PageRank 应用到我们的文本当中去的时候,我们首先会发现的问题是,句子和句子之间 如何相互 “推荐”?
𝑆𝑖𝑚𝑖𝑙𝑎𝑟𝑖𝑡𝑦(𝑆𝑖,𝑆𝑗)=||{𝑤𝑘|𝑤𝑘∈𝑆𝑖&𝑤𝑘∈𝑆𝑗}||log(|𝑆𝑖|)+log(||𝑆𝑗||)
相当于是在求两个句子之间的相似度,也就是推荐程度。根据推荐程度便可以列出一张邻接矩阵。
在此基础上,我们可以应用新的 PageRank 算法来完成分数的计算
𝑊𝑆(𝑉𝑖)=(1−𝑑)+𝑑∗∑𝑉𝑗∈In(𝑉𝑖)𝑤𝑗𝑖∑𝑉𝑘∈𝑂𝑢𝑡(𝑉𝑗)𝑤𝑗𝑘𝑊𝑆(𝑉𝑗)
至此我们进一步迭代,计算出第二句和第三句的 分数 ,然后继续从第一句开始计算。直到最终每一句的分数不再变化为止。
3.项目技术栈
from nltk.tokenize import sent_tokenize, word_tokenize
from nltk.corpus import stopwords
import math
from itertools import product, count
from string import punctuation
from heapq import nlargest
4.思路
计算相似性
def calculate_similarity(sen1, sen2):
counter = 0
for word in sen1:
if word in sen2:
counter += 1
return counter / (math.log(len(sen1)) + math.log(len(sen2)))
创建相似度邻接矩阵
def create_graph(word_sent):
num = len(word_sent)
board = [[0.0 for _ in range(num)] for _ in range(num)]
for i,j in product(range(num), repeat = 2):
if i != j:
board[i][j] = calculate_similarity(word_sent[i], word_sent[j])
return board
根据 PageRank 算法,算出句子分数
def weighted_pagerank(weight_graph):
scores = [0.5 for _ in range(len(weight_graph))]
old_scores = [0.0 for _ in range(len(weight_graph))]
while different(scores, old_scores):
for i in range(len(weight_graph)):
old_scores[i] = scores[i]
for i in range(len(weight_graph)):
scores[i] = calculate_score(weight_graph, scores, i)
return scores
def different(scores, old_scores):
flag = False
for i in range(len(scores)):
if math.fabs(scores[i] - old_scores[i]) >= 0.0001:
flag = True
break
return flag
def calculate_score(weight_graph, scores, i):
length = len(weight_graph)
d = 0.85
added_score = 0.0
for j in range(length):
fraction = 0.0
denominator = 0.0
fraction = weight_graph[j][i] * scores[j]
for k in range(length):
denominator += weight_graph[j][k]
added_score += fraction / denominator
weighted_score = (1 - d) + d * added_score
return weighted_score
找出分数最高的两个句子
def Summarize(text,n):
sents = sent_tokenize(text)
word_sent = [word_tokenize(s.lower()) for s in sents]
for i in range(len(word_sent)):
for word in word_sent[i]:
if word in stopwords:
word_sent[i].remove(word)
similarity_graph = create_graph(word_sent)
scores = weighted_pagerank(similarity_graph)
sent_selected = nlargest(n, zip(scores, count()))
sent_index = []
for i in range(n):
sent_index.append(sent_selected[i][1])
return [sents[i] for i in sent_index]