一、关键字
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] += 1m = float(max(freq.values()))for w in list(freq.keys()):freq[w] = freq[w]/mif 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 = 0for word in sen1: if word in sen2: counter += 1return 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 scoresdef different(scores, old_scores): flag = Falsefor i in range(len(scores)): if math.fabs(scores[i] - old_scores[i]) >= 0.0001: flag = Truebreakreturn flagdef calculate_score(weight_graph, scores, i):length = len(weight_graph)d = 0.85added_score = 0.0for j in range(length):fraction = 0.0denominator = 0.0fraction = weight_graph[j][i] * scores[j]for k in range(length):denominator += weight_graph[j][k]added_score += fraction / denominatorweighted_score = (1 - d) + d * added_scorereturn 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]