{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "title: gensim生成长本文中文摘要\n", "date: 2018-06-20 8:17:55\n", "tags: [gensim, python, 摘要生成]\n", "toc: true\n", "xiongzhang: true\n", "\n", "---\n", "\n", "\n", "\n", "> 声明: 本文由[DataScience](http://mlln.cn)原创发表, 转载请注明[本文链接](http://mlln.cn)mlln.cn, 并在文后留言`转载`.\n", "\n", "本文代码运行环境:\n", "\n", "- windows10\n", "- python3.6\n", "- jupyter notebook" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 介绍\n", "\n", "随着今日头条/微信公众号等通过推送文章让用户阅读的信息获取方式的繁荣,为长文本生成智能和准确摘要的任务已经成为流行的研究以及行业问题。自动摘要可以提高用户获取信息的效率, 并且可以降低编辑的劳动成本。\n", "\n", "文本摘要有两种基本方法:抽取和抽象。前者从原始文本中提取单词和单词短语以创建摘要。后者学习了一种内部语言表达方式,以产生更多像人类一样的摘要,并转述了原文的意图。\n", "\n", "今天的教程主要介绍如何用gensim自动实现摘要生成, 以后的文章也会逐一介绍其他一些python工具用于实现同样的目的, 但是效果不同.\n", "\n", "### gensim文本摘要技术解释\n", "\n", "`gensim.summarization`模块实现了TextRank算法,一种基于Mihalcea等人的论文的加权图的无监督算法。它被另一个学生Olavur Mortensen添加在他的博客上。这种算法的灵感来自google用于网页排名的算法。 TextRank的工作原理如下:\n", "\n", "- 预处理文本:删除停用词或其他词。\n", "- 创建一个顶点是句子的图。\n", "- 通过边缘将每个句子连接到每个其他句子。边缘的权重是两个句子的相似程度。\n", "- 在图上运行PageRank算法。\n", "- 选择具有最高PageRank分数的顶点(句子)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 代码实现\n", "\n", "由于`gensim`通常是用在英文的主题提取, 所以`gensim.summarization`模块很多文本预处理的方法都假设我们的语料是英文的, 总的来说有以下假设:\n", "\n", "- 句号是英文符号`.`, 这会影响断句.\n", "- 词以空格分隔, 这会影响分词\n", "\n", "今天我们想要使用gensim来进行中文的摘要生成, 怎么办? 我首先想到的是修改gensim源码, 但是工程比较大, 不适合在教程中讲解, 所以我最终选了一种绕行方式, 就是将中文语料转换成英文格式. 比如:\n", "\n", "`gensim可以实现中文的文章主题生成。`\n", "\n", "转换成:\n", "\n", "`gensim 可以 实现 中文 的 文章 主题 生成。. `\n", "\n", "这样才能正确的被`gensim.summarization`模块所接受.\n", "\n", "#### 分句分词和文本转换\n", "\n", "我们使用`pyltp`模块进行句子分割和词分割, 只有这样才能实现文本格式转换. 如果你用的是linux系统, 安装pyltp很简单, 如果你用的是windows系统, 我建议你看这篇文章来安装pyltp模块: pyltp windows下的安装.\n", "\n", "(下面的教程假设你已经安装好了pyltp.)\n", "\n", "下面我们就来实现这个文本转换过程:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "from pyltp import Segmentor\n", "from pyltp import SentenceSplitter\n", "\n", "\n", "_segmentor = None\n", "_sent_splitter = None\n", "\n", "def split(content):\n", " '''分句和分词'''\n", " global _segmentor, _sent_splitter\n", " if _segmentor is None:\n", " model_path = r'D:\\mysites\\text-characters\\tcharacters\\ltp\\ltp_data\\cws.model'\n", " segmentor = Segmentor() # 初始化实例\n", " segmentor.load(model_path) # 加载分词模型\n", " _segmentor = segmentor # 设置全局变量, 避免每次都重新加载模型, 耗时\n", " _sent_splitter = SentenceSplitter() # 句子分割模型\n", " sents = _sent_splitter.split(content) # 先进行分句\n", " _sents = []\n", " for sent in sents:\n", " words = _segmentor.segment(sent) # 分词\n", " sent = ' '.join(words) # 用空格把词隔开\n", " _sents.append(sent)\n", " content = '. '.join(_sents) # 用.把句子隔开\n", " return content\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 测试分句分词" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "转换前: 本模块提供文本摘要的功能。基于TextRank算法来计算文本句子的等级。\n", "转换后: 本 模块 提供 文本 摘要 的 功能 。. 基于 TextRank 算法 来 计算 文本 句子 的 等级 。\n" ] } ], "source": [ "content = '本模块提供文本摘要的功能。基于TextRank算法来计算文本句子的等级。'\n", "result = split(content)\n", "print(f'转换前: {content}')\n", "print(f'转换后: {result}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### gensim实现文本摘要\n", "\n", "这个过程很简单, 因为我们并不介绍算法, 只是调用接口." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'. LexRank 是 这里 的 赢家 , 因为 它 产生 了 更 好 的 ROUGE 和 BLEU 分数 。.\\n. 不幸 的 是 , 我们 发现 由 Gensim 的 TextRank 和 Luhn 模型 产生 的 摘要 信息 比 摘要 要 少 。.'" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from gensim.summarization.summarizer import summarize\n", "content = '''\n", "使用51篇文章的Opinion数据集进行比较。\n", "每篇文章都是关于产品的功能,例如iPod的电池寿命等,并且是购买该产品的客户的评论集合。\n", "数据集中的每篇文章都有5个手动编写的“黄金”摘要。\n", "通常5金总结是不同的,但它们也可以是相同的文本重复5次。\n", "LexRank是这里的赢家,因为它产生了更好的ROUGE和BLEU分数。\n", "不幸的是,我们发现由Gensim的TextRank和Luhn模型产生的摘要信息比摘要要少。\n", "此外,LexRank并不总是在ROUGE评分中击败TextRank - 例如,TextRank在DUC 2002数据集上的表现稍好于LexRank。\n", "因此,LexRank和TextRank之间的选择取决于您的数据集,值得一试。\n", "数据的另一个结论是Gensim的Textrank优于普通的PyTextRank。\n", "因为它在明文TextRank中使用BM25函数而不是余弦IDF。\n", "表中的另一点是Luhn的算法具有较低的BLEU分数。\n", "这是因为它提取了更长的摘要,因此涵盖了更多的产品评论。\n", "不幸的是,我们不能缩短它,因为Sumy中Luhn算法的封装不提供参数来改变字数限制。\n", "'''\n", "tokens = split(content)\n", "summarize(tokens)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "至此, 我们生成的摘要还算可以, 但是多了一些空格和句子分隔符. 那么, 我们就来清理以下这些无意义的符号." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "def clean(content):\n", " content = content.replace('.', '') # 删除句子分隔符\n", " content = content.replace(' ', '') # 删除空格\n", " return content" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "测试以下效果:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "LexRank是这里的赢家,因为它产生了更好的ROUGE和BLEU分数。\n", "不幸的是,我们发现由Gensim的TextRank和Luhn模型产生的摘要信息比摘要要少。\n" ] } ], "source": [ "result = summarize(tokens)\n", "result = clean(result)\n", "print(result)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4" } }, "nbformat": 4, "nbformat_minor": 2 }