人工智能 – NLP 关键词提取:TF-IDF算法 和 TextRank算法
1.基于TF-IDF算法进行关键词抽取 ---- analyse.extract_tags() 解压标签
from jieba import analyse
# 引入TF-IDF关键词抽取接口
tfidf = analyse.extract_tags
# 原始文本
text = "\u3000\u3000,中新网,1月7日电\xa0 恰逢CES 2017拉开大幕,却惊闻“AlphaGo升级版”的Master迎来60连胜,人类顶尖围棋手在一周内纷纷败给这个谷歌旗下DeepMind团队打造的“围棋大脑”,显然也为聚焦于人工智能的本届CES增添了声势。而首次参展,并致力于打造“原创AI大脑”的中国深度学习领军企业的商汤科技,在人工智能的浪潮之巅,及众多业界前辈和巨匠面前,将会交出一份怎样的答卷呢?\u3000\u3000徐立,商汤科技CEO在谈起本次参展时谈到:“作为一个成立刚刚两年的创业公司,这次参展,一方面是展示我们最新的人工智能技术和产品,但另一方面,其实是向外表达,我们对于人工智能的理解。人工智能在特定领域中超越人类,是其广泛应用的标志。这与Master的胜利,为围棋世界开拓的新局面不谋而合。”\u3000\u3000正如最后挑战Master的古力在落败后发表的观点:“人类与人工智能共同探索围棋世界的大幕即将拉开,新一次的围棋革命正在进行着”。商汤科技的展台,尽管只有两块屏幕,但却带来对人工智能带来的变革的最好诠释:一面是可以通过深度学习,"
# 基于TF-IDF算法进行关键词抽取
keywords = tfidf(text, 10) # 提取频率最高的10个关键词。默认20
print(keywords) # ['人工智能', '围棋', 'Master', '商汤', '参展', 'CES', '大幕', '科技', '人类', '大脑']
# join()转string输出
print("/".join(keywords)) # 人工智能/围棋/Master/商汤/参展/CES/大幕/科技/人类/大脑
print(" ".join(keywords)) # 人工智能 围棋 Master 商汤 参展 CES 大幕 科技 人类 大脑
# 遍历输出
for keyword in keywords:
print(keyword + "/")
2.基于TextRank算法进行关键词抽取 ---- analyse.textrank() 文本级别
from jieba import analyse
# 引入TextRank关键词抽取接口
textrank = analyse.textrank
# 原始文本
text = "'\u3000\u3000,中新网,1月7日电\xa0 恰逢CES 2017拉开大幕,却惊闻“AlphaGo升级版”的Master迎来60连胜,人类顶尖围棋手在一周内纷纷败给这个谷歌旗下DeepMind团队打造的“围棋大脑”,显然也为聚焦于人工智能的本届CES增添了声势。而首次参展,并致力于打造“原创AI大脑”的中国深度学习领军企业的商汤科技,在人工智能的浪潮之巅,及众多业界前辈和巨匠面前,将会交出一份怎样的答卷呢?\u3000\u3000徐立,商汤科技CEO在谈起本次参展时谈到:“作为一个成立刚刚两年的创业公司,这次参展,一方面是展示我们最新的人工智能技术和产品,但另一方面,其实是向外表达,我们对于人工智能的理解。人工智能在特定领域中超越人类,是其广泛应用的标志。这与Master的胜利,为围棋世界开拓的新局面不谋而合。”\u3000\u3000正如最后挑战Master的古力在落败后发表的观点:“人类与人工智能共同探索围棋世界的大幕即将拉开,新一次的围棋革命正在进行着”。商汤科技的展台,尽管只有两块屏幕,但却带来对人工智能带来的变革的最好诠释:一面是可以通过深度学习,"
print "\nkeywords by textrank:"
# 基于TextRank算法进行关键词抽取
keywords = tfidf(text, 10) # 提取频率最高的10个关键词。默认20
# 与TF-IDF算法算法不同,TextRank算法的参数allowPOS有默认值,参数默认allowPOS=('ns', 'n', 'vn', 'v'),
# ["n", "nr", "ns", "vn", "v"],表示只能从词性为名词、人名(r=ren)、地名(s=site)、动名词、动词这些词性的词中抽取关键词。
print(keywords) # ['人工智能', '围棋', '参展', '世界', '人类', '商汤', '打造', '带来', '科技', '大幕']
print("/".join(keywords)) # 人工智能/围棋/参展/世界/人类/商汤/打造/带来/科技/大幕
可以看到,若两个函数都用自己的参数默认值的话,二者print结果并不同。
算法分析
1. TF-IDF算法分析
TF-IDF在实际中主要是将二者相乘,也即TF * IDF,TF为词频(Term Frequency),表示词t在文档d中出现的频率;IDF为反文档频率(Inverse Document Frequency),表示语料库中包含词t的文档的数目的倒数。
count(t)表示文档di中包含词t的个数;count(di)表示文档di的词的总数。
num(corpus)表示语料库corpus中文档的总数;num(t)表示语料库corpus中包含t的文档的数目。
TF-IDF实践步骤,也即是一般的文本处理和模型训练步骤:
- 获取原始文本内容信息。
- 转换成纯小写,按空格把文章分成独立的词组成的list。
- 去除噪音符号: [""","=","\","/",":","-","(",")",",",".","\n"]等
- 去除停用词
- 提取词干,把相近的词转换为标准形式,比如把文章中的go,going,went,goes统一成go
- wordcount,统计每个词出现的次数,去掉出现次数较少的词,比如在一百篇文档中,只出现了1~2次的词,显然是没有意义的。
- 训练idf模型
- 对输入的每篇测试文章计算其tfidf向量,然后可以利用tfidf向量求文章之间的相似度(比如用欧拉距离,余弦相似度,Jaccard系数等方法)。
2. TextRank算法分析
类似于PageRank的思想,将文本中的语法单元视作图中的节点,如果两个语法单元存在一定语法关系(例如共现),则这两个语法单元在图中就会有一条边相互连接,通过一定的迭代次数,最终不同的节点会有不同的权重,权重高的语法单元可以作为关键词。
节点的权重不仅依赖于它的入度结点,还依赖于这些入度结点的权重,入度结点越多,入度结点的权重越大,说明这个结点的权重越高;
TextRank迭代计算公式为,
WS(Vi)=(1−d)+d∗∑Vj∈In(Vi)wji∑Vk∈Out(Vj)wjk∗WS(Vj)
节点i的权重取决于节点i的邻居节点中i-j这条边的权重 / j的所有出度的边的权重 * 节点j的权重,将这些邻居节点计算的权重相加,再乘上一定的阻尼系数,就是节点i的权重;
阻尼系数 d 一般取0.85;
源码分析:
def extract_tags(self, sentence, topK=20, withWeight=False, allowPOS=(), withFlag=False):
# 传入了词性限制集合
if allowPOS:
allowPOS = frozenset(allowPOS)
# 调用词性标注接口
words = self.postokenizer.cut(sentence)
# 没有传入词性限制集合
else:
# 调用分词接口
words = self.tokenizer.cut(sentence)
freq = {}
for w in words:
if allowPOS:
if w.flag not in allowPOS:
continue
elif not withFlag:
w = w.word
wc = w.word if allowPOS and withFlag else w
# 判断词的长度是否小于2,或者词是否为停用词
if len(wc.strip()) < 2 or wc.lower() in self.stop_words:
continue
# 将其添加到词频词典中,次数加1
freq[w] = freq.get(w, 0.0) + 1.0
# 统计词频词典中的总次数
total = sum(freq.values())
for k in freq:
kw = k.word if allowPOS and withFlag else k
# 计算每个词的tf-idf值
freq[k] *= self.idf_freq.get(kw, self.median_idf) / total
# 根据tf-idf值进行排序
if withWeight:
tags = sorted(freq.items(), key=itemgetter(1), reverse=True)
else:
tags = sorted(freq, key=freq.__getitem__, reverse=True)
# 输出topK个词作为关键词
if topK:
return tags[:topK]
else:
return tags
def textrank(self, sentence, topK=20, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v'), withFlag=False):
self.pos_filt = frozenset(allowPOS)
# 定义无向有权图
g = UndirectWeightedGraph()
# 定义共现词典
cm = defaultdict(int)
# 分词
words = tuple(self.tokenizer.cut(sentence))
# 依次遍历每个词
for i, wp in enumerate(words):
# 词i 满足过滤条件
if self.pairfilter(wp):
# 依次遍历词i 之后窗口范围内的词
for j in xrange(i + 1, i + self.span):
# 词j 不能超出整个句子
if j >= len(words):
break
# 词j不满足过滤条件,则跳过
if not self.pairfilter(words[j]):
continue
# 将词i和词j作为key,出现的次数作为value,添加到共现词典中
if allowPOS and withFlag:
cm[(wp, words[j])] += 1
else:
cm[(wp.word, words[j].word)] += 1
# 依次遍历共现词典的每个元素,将词i,词j作为一条边起始点和终止点,共现的次数作为边的权重
for terms, w in cm.items():
g.addEdge(terms[0], terms[1], w)
# 运行textrank算法
nodes_rank = g.rank()
# 根据指标值进行排序
if withWeight:
tags = sorted(nodes_rank.items(), key=itemgetter(1), reverse=True)
else:
tags = sorted(nodes_rank, key=nodes_rank.__getitem__, reverse=True)
# 输出topK个词作为关键词
if topK:
return tags[:topK]
else:
return tags