本文一步步为你演示,如何用Python从中文文本中提取关键词。如果你需要对长文“观其大略”,不妨尝试一下。
2017-12-07-20-38-22-7-426487.png
需求
好友最近对自然语言处理感兴趣,因为他打算利用自动化方法从长文本里提取关键词,来确定主题。
他向我询问方法,我推荐他阅读我的那篇《如何用Python从海量文本提取主题?》。
看过之后,他表示很有收获,但是应用场景和他自己的需求有些区别。
《如何用Python从海量文本提取主题?》一文面对的是大量的文档,利用主题发现功能对文章聚类。而他不需要处理很多的文档,也没有聚类的需求,但是需要处理的每篇文档都很长,希望通过自动化方法从长文提取关键词,以观其大略。
我突然发现,之前居然忘了写文,介绍单一文本关键词的提取方法。
虽然这个功能实现起来并不复杂,但是其中也有些坑,需要避免踩进去的。
通过本文,我一步步为你演示如何用Python实现中文关键词提取这一功能。
环境
Python
第一步是安装Python运行环境。我们使用集成环境Anaconda。
请到这个网址 下载最新版的Anaconda。下拉页面,找到下载位置。根据你目前使用的系统,网站会自动推荐给你适合的版本下载。我使用的是macOS,下载文件格式为pkg。
2017-12-03-22-19-31-2-856072.png
下载页面区左侧是Python 3.6版,右侧是2.7版。请选择2.7版本。
双击下载后的pkg文件,根据中文提示一步步安装即可。
2017-12-03-22-19-31-2-856170.jpeg
样例
我专门为你准备了一个github项目,存放本文的配套源代码和数据。请从这个地址下载压缩包文件,然后解压。
解压后的目录名称为demo-keyword-extraction-master
,样例目录包含以下内容:
2017-12-7_21-24-56_snapshots-01.jpg
除了README.md这个github项目默认说明文件外,目录下还有两个文件,分别是数据文件sample.txt和程序源代码文件demo-extract-keyword.ipynb。
结巴分词
我们使用的关键词提取工具为结巴分词。
之前在《如何用Python做中文分词?》一文中,我们曾经使用过该工具为中文语句做分词。这次我们使用的,是它的另一项功能,即关键词提取。
请进入终端,使用cd命令进入解压后的文件夹demo-keyword-extraction-master,输入以下命令:
pip install jieba
好了,软件包工具也已经准备就绪。下面我们执行
jupyter notebook
进入到Jupyter笔记本环境。
image.png
到这里,环境已经准备好了,我们下面来介绍本文使用的中文文本数据。
数据
一开始,我还曾为寻找现成的中文文本发愁。
网上可以找到的中文文本浩如烟海。
但是拿来做演示,是否会有版权问题,我就不确定了。万一把哪位大家之作拿来做了分析,人家可能就要过问一句“这电子版你是从哪里搞到的啊?”
万一再因此提出诉讼,我可无法招架。
后来发现,我这简直就是自寻烦恼——找别人的文本干什么?用我自己的不就好了?
这一年多以来,我写的文章已有90多篇,总字数已经超过了27万。
image.png
我特意从中找了一篇非技术性的,以避免提取出的关键词全都是Python命令。
我选取的,是去年的那篇《网约车司机二三事》。
image.png
这篇文章,讲的都是些比较有趣的小故事。
我从网页上摘取文字,存储到sample.txt中。
注意,这里是很容易踩坑的地方。在夏天的一次工作坊教学中,好几位同学因为从网上摘取中文文本出现问题,卡住很长时间。
这是因为不同于英语,汉字有编码问题。不同系统都有不同的默认编码,不同版本的Python接受的编码也不同。你从网上下载的文本文件,也可能与你系统的编码不统一。
image.png
不论如何,这些因素都有可能导致你打开后的文本里,到处都是看不懂的乱码。
因而,正确的使用中文文本数据方式,是你在Jupyter Notebook里面,新建一个文本文件。
image.png
然后,会出现以下的空白文件。
image.png
把你从别处下载的文本,用任意一种能正常显示的编辑器打开,然后拷贝全部内容,粘贴到这个空白文本文件中,就能避免编码错乱。
避开了这个坑,可以为你节省很多不必要的烦恼尝试。
好了,知道了这个窍门,下面你就能愉快地进行关键词提取了。
执行
回到Jupyter Notebook的主界面,点击demo-extract-keyword.ipynb,你就能看到源码了。
image.png
对,你没看错。只需要这短短的4个语句,就能完成两种不同方式(TF-idf与TextRank)的关键词提取。
本部分我们先讲解执行步骤。不同关键词提取方法的原理,我们放在后面介绍。
首先我们从结巴分词的分析工具箱里导入所有的关键词提取功能。
from jieba.analyse import *
在对应的语句上,按下Shift+Enter组合按键,就可以执行语句,获取结果了。
然后,让Python打开我们的样例文本文件,并且读入其中的全部内容到data变量。
with open('sample.txt') as f: data = f.read()
使用TF-idf方式提取关键词和权重,并且依次显示出来。如果你不做特殊指定的话,默认显示数量为20个关键词。
for keyword, weight in extract_tags(data, withWeight=True): print('%s %s' % (keyword, weight))
显示内容之前,会有一些提示,不要管它。
Building prefix dict from the default dictionary ... Loading model from cache /var/folders/8s/k8yr4zy52q1dh107gjx280mw0000gn/T/jieba.cache Loading model cost 0.547 seconds. Prefix dict has been built succesfully.
然后列表就出来了:
优步 0.280875594782司机 0.119951947597乘客 0.105486129485师傅 0.0958888107815张师傅 0.0838162334963目的地 0.0753618512886网约车 0.0702188986954姐姐 0.0683412127766自己 0.0672533110661上车 0.0623276916308活儿 0.0600134354214天津 0.056915805679210 0.0526641740216开优步 0.0526641740216事儿 0.048554456767李师傅 0.0485035501943天津人 0.0482653686026绕路 0.0478244723097出租车 0.0448480260748时候 0.0440840298591
我看了一下,觉得关键词提取还是比较靠谱的。当然,其中也混入了个数字10,好在无伤大雅。
如果你需要修改关键词数量,就需要指定topK
参数。例如你要输出10个关键词,可以这样执行:
for keyword, weight in extract_tags(data, topK=10, withWeight=True): print('%s %s' % (keyword, weight))
下面我们尝试另一种关键词提取方式——TextRank。
for keyword, weight in textrank(data, withWeight=True): print('%s %s' % (keyword, weight))
关键词提取结果如下:
优步 1.0司机 0.749405996648乘客 0.594284506457姐姐 0.485458741991天津 0.451113490366目的地 0.429410027466时候 0.418083863303作者 0.416903838153没有 0.357764515052活儿 0.291371566494上车 0.277010013884绕路 0.274608592084转载 0.271932903186出来 0.242580745393出租 0.238639889991事儿 0.228700322713单数 0.213450680366出租车 0.212049665481拉门 0.205816713637跟着 0.20513470986
注意这次提取的结果,与TF-idf的结果有区别。至少,那个很突兀的“10”不见了。
但是,这是不是意味着TextRank方法一定优于TF-idf呢?
这个问题,留作思考题,希望在你认真阅读了后面的原理部分之后,能够独立做出解答。
如果你只需要应用本方法解决实际问题,那么请跳过原理部分,直接看讨论吧。
原理
我们简要讲解一下,前文出现的2种不同关键词提取方式——TF-idf和TextRank的基本原理。
为了不让大家感到枯燥,这里咱们就不使用数学公式了。后文我会给出相关的资料链接。如果你对细节感兴趣,欢迎按图索骥,查阅学习。
先说TF-idf。
它的全称是 Term Frequency - inverse document frequency。中间有个连字符,左右两侧各是一部分,共同结合起来,决定某个词的重要程度。
第一部分,就是词频(Term Frequency),即某个词语出现的频率。
我们常说“重要的事说三遍”。
同样的道理,某个词语出现的次数多,也就说明这个词语重要性可能会很高。
但是,这只是可能性,并不绝对。
例如现代汉语中的许多虚词——“的,地,得”,古汉语中的许多句尾词“之、乎、者、也、兮”,这些词在文中可能出现许多次,但是它们显然不是关键词。
这就是为什么我们在判断关键词的时候,需要第二部分(idf)配合。
逆文档频率(inverse document frequency)首先计算某个词在各文档中出现的频率。假设一共有10篇文档,其中某个词A在其中10篇文章中都出先过,另一个词B只在其中3篇文中出现。请问哪一个词更关键?
给你一分钟思考一下,然后继续读。
公布答案时间到。
答案是B更关键。
A可能就是虚词,或者全部文档共享的主题词。而B只在3篇文档中出现,因此很有可能是个关键词。
逆文档频率就是把这种文档频率取倒数。这样第一部分和第二部分都是越高越好。二者都高,就很有可能是关键词了。
TF-idf讲完了,下面我们说说TextRank。
相对于TF-idf,TextRank要显得更加复杂一些。它不是简单做加减乘除运算,而是基于图的计算。
下图是原始文献中的示例图。
image.png
TextRank首先会提取词汇,形成节点;然后依据词汇的关联,建立链接。
依照连接节点的多少,给每个节点赋予一个初始的权重数值。
然后就开始迭代。
根据某个词所连接所有词汇的权重,重新计算该词汇的权重,然后把重新计算的权重传递下去。直到这种变化达到均衡态,权重数值不再发生改变。这与Google的网页排名算法PageRank,在思想上是一致的。
image.png
根据最后的权重值,取其中排列靠前的词汇,作为关键词提取结果。
如果你对原始文献感兴趣,请参考以下链接:
讨论
小结一下,本文探讨了如何用Python对中文文本做关键词提取。具体而言,我们分别使用了TF-idf和TextRank方法,二者提取关键词的结果可能会有区别。
你做过中文关键词提取吗?使用的是什么工具?它的效果如何?有没有比本文更高效的方法?欢迎留言,把你的经验和思考分享给大家,我们一起交流讨论。
作者:王树义
链接:https://www.jianshu.com/p/cf383fd471bb
共同学习,写下你的评论
评论加载中...
作者其他优质文章