justaipanda 2019-04-18
你曾有没有把冗长的文件归纳成一个简短的段落的工作?你花了多长时间?这样的手动生成摘要可能既费时又乏味。自动文本摘要有望克服这些困难,并且可以一键提取出文章中重要的关键信息。
文本摘要是一种生成简明而精确的大量文本摘要的技术,同时可以传达有用信息的那一部分,但不会改变原文的整体意义。
自动文本摘要旨在将冗长的文档转换为缩短的版本,如果手动完成,可能是困难且成本高昂。
机器学习算法可以经过训练,在生成所需的摘要文本之前,理解文档并识别传达重要事实和信息的部分。例如,下面的图片是一篇新闻文章,它被输入机器学习算法来生成摘要。
一篇在线新闻文章,使用文本摘要机器学习算法进行了总结
进行文本摘要的需求
随着目前数据空间的大量涌现(主要是非结构化的文本数据),我们很有必要开发自动文本摘要工具,使人们能够轻松地从中获得关键信息。目前,我们可以快速访问大量信息。但是,大多数此类信息都是多余的、无关紧要,并且可能无法传达预期的含义。例如,如果您正在寻找在线新闻文章中的特定信息,您可能需要深入了解其内容,并在获取所需信息之前花费大量时间清除不必要的内容。因此,使用能够提取有用信息的自动文本摘要器,这些信息会筛选出不必要且无关紧要的数据。实现摘要可以增强文档的可读性,减少研究信息所花费的时间,并允许在特定区域中包含更多信息。
文本摘要的主要类型
从广义上讲,有两种方法可以总结NLP中的文本:提取和抽象。
基于提取的摘要
在基于提取的摘要中,会从一段文本中提取一组表示最重要的单词子集,并将其组合形成摘要。可以把它看作是一个荧光笔,它从源文本中选择出了主要信息。
高亮=基于提取的摘要
在机器学习中,提取摘要通常涉及权衡句子的基本部分并使用结果生成摘要。
可以使用不同类型的算法和方法来测量句子的权重,然后根据它们的相关性和相似性对它们进行排序——然后将它们连接起来生成摘要。举个例子:
正在提取摘要的过程中
如您所见,提取的摘要由以粗体突出显示的单词组成,当然结果可能在语法上不准确。
基于抽象的摘要
在基于抽象的总结中,高级深度学习技术被应用于解释和缩短原始文档。把它想象成一支笔,它能写出不属于原始文档的新句子。
笔 =基于抽象的摘要
由于抽象机器学习算法可以生成表示源文本中最重要信息的新短语和句子,因此它们可以帮助克服提取技术的语法错误。举个例子:
正在提取基于抽象的摘要
尽管抽象摘要得形式表现更好,但开发其算法需要复杂的深度学习技术和复杂的语言建模。
为了生成合理的输出,基于抽象的摘要方法必须解决各种NLP问题,例如自然语言生成、语义表示和推理置换。
因此,提取文本摘要方法仍然广泛流行。在本文中,我们将专注于基于提取的方法。
如何执行文本摘要
让我们用一小段话来说明如何执行提取文本摘要。
段落内容:
“彼得和伊丽莎白乘出租车去城市的晚会。在聚会期间,伊丽莎白垮了,被送往医院。自从她被诊断出患有脑损伤后,医生告诉彼得要留在她身边直到她康复。因此,彼得在医院住了3天而没有离开。”以下是总结上段的步骤,同时尽可能地保持其本意。
步骤1:将段落转换为句子
首先,让我们将段落分成相应的句子。进行转换的最佳方法是在句点出现时提取一个句子。
步骤2:文本处理
接下来,让我们通过从句子中删除停用词(极其常见的词语,例如“和”和“the”)、数字、标点符号和其他特殊字符来进行文本处理。
执行过滤有助于删除冗余和无关紧要的信息,这些信息可能无法为文本的含义提供任何附加值。
以下是文本处理的结果:
步骤3:标记
对句子进行标记以获得句子中出现的所有单词。这是一个单词列表:
[ '彼得', '伊丽莎白', '花', '出租车', '出席', '夜', '聚会', '城市', '聚会', '伊丽莎白', '崩溃', '抢',”医院','诊断','大脑','受伤','医生','告诉','彼得','留','除','得','好','彼得','留' , '医院', '天', '无', '离开']
步骤4:评估单词的加权出现频率
之后,让我们计算所有单词的加权出现频率。为了达到这个目的,我们将每个单词的出现频率除以段落中最常用单词的频率,即出现三次的“彼得”。
这是一个表格,给出了每个单词的加权出现频率。
步骤5:用加权频率替换单词
让我们用他们的加权频率替换原始句子中的每个单词。然后,我们将计算它们的总和。
由于在处理阶段被删除的无效字的加权频率(例如停用词和特殊字符)为零,因此不必添加它们。
从单词的加权频率之和,我们可以推断出第一句在段落中的权重最大。因此,它可以提供该段落的最佳代表性摘要。
此外,如果第一个句子与第三个句子组合,这是段落中第二个重要的句子,可以产生更好的摘要。
以上示例仅给出了如何在机器学习中执行基于提取的文本摘要的基本说明。现在,让我们看看如何在创建真实世界的摘要生成器时应用上述概念。
维基百科文章的文本摘要
让我们亲自动手,创建一个文本摘要生成器,它可以缩短冗长的Web文章中的信息。为了简单起见,除了Python的NLTK工具包之外,我们不会使用任何其他机器学习库。
以下是摘要生成器的代码蓝图:
# Creating a dictionary for the word frequency table frequency_table = _create_dictionary_table(article) # Tokenizing the sentences sentences = sent_tokenize(article) # Algorithm for scoring a sentence by its words sentence_scores = _calculate_sentence_scores(sentences, frequency_table) # Getting the threshold threshold = _calculate_average_score(sentence_scores) # Producing the summary article_summary = _get_article_summary(sentences, sentence_scores, 1.5 * threshold) print(article_summary)
以下是在Python中创建简单文本摘要生成器的步骤。
第1步:准备数据
在这个例子中,我们想要总结这篇维基百科文章中的信息,这篇文章只概述了20世纪的重大事件。
为了使我们能够获取文章的文本,我们将使用Beautiful Soup库。
以下是抓取文章内容的代码:
import bs4 as BeautifulSoup import urllib.request # Fetching the content from the URL fetched_data = urllib.request.urlopen('https://en.wikipedia.org/wiki/20th_century') article_read = fetched_data.read() # Parsing the URL content and storing in a variable article_parsed = BeautifulSoup.BeautifulSoup(article_read,'html.parser') # Returning <p> tags paragraphs = article_parsed.find_all('p') article_content = '' # Looping through the paragraphs and adding them to the variable for p in paragraphs: article_content += p.text
在上面的代码中,我们首先导入用于从网页获取数据的基本库。 BeautifulSoup库用于解析页面,而urllib库用于连接到页面并检索HTML。
BeautifulSoup将传入的文本转换为Unicode字符,将传出的文本转换为UTF-8字符,从而节省了在从Web上抓取文本时管理不同字符集编码的麻烦。
我们将使用urllib.request实用程序中的urlopen函数打开网页。然后,我们将使用read函数来读取已删除的数据对象。为了解析数据,我们将调用BeautifulSoup对象并将两个参数传递给它;也就是article_read和html.parser。
find_all函数用于返回HTML中存在的所有<p>元素。此外,使用.text使我们只能选择<p>元素中的文本。
第2步:处理数据
为确保报废的文本数据尽可能无噪音,我们将执行一些基本的文本清理。为了帮助我们进行处理,我们将从nltk库导入一个停用词列表。
我们还将导入PorterStemmer,这是一种将单词缩减为词根形式的算法。
此外,我们将创建一个字典表,其中包含文本中每个单词的出现频率。我们将遍历文本和相应的单词以消除任何停用词。
然后,我们将检查单词是否出现在frequency_table中。如果该单词以前在字典中可用,则其值将更新为1。否则,如果第一次识别该单词,则其值将设置为1。
例如,频率表应如下所示:
代码如下:
from nltk.corpus import stopwords from nltk.stem import PorterStemmer def _create_dictionary_table(text_string) -> dict: # Removing stop words stop_words = set(stopwords.words("english")) words = word_tokenize(text_string) # Reducing words to their root form stem = PorterStemmer() # Creating dictionary for the word frequency table frequency_table = dict() for wd in words: wd = stem.stem(wd) if wd in stop_words: continue if wd in frequency_table: frequency_table[wd] += 1 else: frequency_table[wd] = 1 return frequency_table
第3步:将文章标记为句子
要将article_content拆分为一组句子,我们将使用nltk库中的内置方法。
from nltk.tokenize import word_tokenize, sent_tokenize sentences = sent_tokenize(article)
第4步:找到句子的加权频率
为了评估文本中每个句子的分数,我们将分析每个术语的出现频率。在这种情况下,我们将用它的词来评分每个句子;也就是说,添加句子中找到的每个重要单词的频率。
看看下面的代码:
def _calculate_sentence_scores(sentences, frequency_table) -> dict: # Algorithm for scoring a sentence by its words sentence_weight = dict() for sentence in sentences: sentence_wordcount = (len(word_tokenize(sentence))) sentence_wordcount_without_stop_words = 0 for word_weight in frequency_table: if word_weight in sentence.lower(): sentence_wordcount_without_stop_words += 1 if sentence[:7] in sentence_weight: sentence_weight[sentence[:7]] += frequency_table[word_weight] else: sentence_weight[sentence[:7]] = frequency_table[word_weight] sentence_weight[sentence[:7]] = sentence_weight[sentence[:7]] / sentence_wordcount_without_stop_words return sentence_weight
重要的是,为了确保长句不会比短句得分高,我们将句子的每个分数除以该句中的单词数。
另外,为了优化字典的内存,我们任意添加句子[:7],它指的是每个句子中的前7个字符。但是,对于较长的文档,您可能会遇到具有相同第一个n_chars的句子,最好使用哈希函数或智能索引函数来考虑这些边届情况并避免冲突。
第5步:计算句子的阈值
为了进一步调整符合摘要条件的句子类型,我们将为这些句子创建平均分。使用此阈值,我们可以避免选择分数低于平均分数的句子。
代码如下:
def _calculate_average_score(sentence_weight) -> int: # Calculating the average score for the sentences sum_values = 0 for entry in sentence_weight: sum_values += sentence_weight[entry] # Getting sentence average value from source text average_score = (sum_values / len(sentence_weight)) return average_score
第6步:总结
最后,由于我们拥有所有必需的参数,我们现在可以为文章生成摘要。
代码如下:
def _get_article_summary(sentences, sentence_weight, threshold): sentence_counter = 0 article_summary = '' for sentence in sentences: if sentence[:7] in sentence_weight and sentence_weight[sentence[:7]] >= (threshold): article_summary += " " + sentence sentence_counter += 1 return article_summary
总结
这是一个图像,展示了创建摘要生成器的工作流程。
创建摘要算法的基本工作流程
以下是机器学习中简单的提取文本摘要生成器的完整代码:
#importing libraries from nltk.corpus import stopwords from nltk.stem import PorterStemmer from nltk.tokenize import word_tokenize, sent_tokenize import bs4 as BeautifulSoup import urllib.request #fetching the content from the URL fetched_data = urllib.request.urlopen('https://en.wikipedia.org/wiki/20th_century') article_read = fetched_data.read() #parsing the URL content and storing in a variable article_parsed = BeautifulSoup.BeautifulSoup(article_read,'html.parser') #returning <p> tags paragraphs = article_parsed.find_all('p') article_content = '' #looping through the paragraphs and adding them to the variable for p in paragraphs: article_content += p.text def _create_dictionary_table(text_string) -> dict: #removing stop words stop_words = set(stopwords.words("english")) words = word_tokenize(text_string) #reducing words to their root form stem = PorterStemmer() #creating dictionary for the word frequency table frequency_table = dict() for wd in words: wd = stem.stem(wd) if wd in stop_words: continue if wd in frequency_table: frequency_table[wd] += 1 else: frequency_table[wd] = 1 return frequency_table def _calculate_sentence_scores(sentences, frequency_table) -> dict: #algorithm for scoring a sentence by its words sentence_weight = dict() for sentence in sentences: sentence_wordcount = (len(word_tokenize(sentence))) sentence_wordcount_without_stop_words = 0 for word_weight in frequency_table: if word_weight in sentence.lower(): sentence_wordcount_without_stop_words += 1 if sentence[:7] in sentence_weight: sentence_weight[sentence[:7]] += frequency_table[word_weight] else: sentence_weight[sentence[:7]] = frequency_table[word_weight] sentence_weight[sentence[:7]] = sentence_weight[sentence[:7]] / sentence_wordcount_without_stop_words return sentence_weight def _calculate_average_score(sentence_weight) -> int: #calculating the average score for the sentences sum_values = 0 for entry in sentence_weight: sum_values += sentence_weight[entry] #getting sentence average value from source text average_score = (sum_values / len(sentence_weight)) return average_score def _get_article_summary(sentences, sentence_weight, threshold): sentence_counter = 0 article_summary = '' for sentence in sentences: if sentence[:7] in sentence_weight and sentence_weight[sentence[:7]] >= (threshold): article_summary += " " + sentence sentence_counter += 1 return article_summary def _run_article_summary(article): #creating a dictionary for the word frequency table frequency_table = _create_dictionary_table(article) #tokenizing the sentences sentences = sent_tokenize(article) #algorithm for scoring a sentence by its words sentence_scores = _calculate_sentence_scores(sentences, frequency_table) #getting the threshold threshold = _calculate_average_score(sentence_scores) #producing the summary article_summary = _get_article_summary(sentences, sentence_scores, 1.5 * threshold) return article_summary if __name__ == '__main__': summary_results = _run_article_summary(article_content) print(summary_results)
你可以点击下面的按钮,在FloydHub笔记本上运行代码:
https://www.floydhub.com/signup?source=run
在本例中,我们应用的阈值是平均分的1.5倍。这是超参数值,经过几次试验,我们得到了很好的结果。当然,您可以根据自己的偏好对值进行微调,并改进摘要结果。
这是维基百科文章摘要版本的图片。
维基百科上一篇使用摘要算法进行摘要的文章
正如您所看到的,运行代码总结了冗长的维基百科文章,并简要概述了20世纪的主要事件。
尽管如此,可以改进摘要生成器,以便更好地生成大量文本的简明和精确的摘要。
把事情提高一个档次......
当然,本文只是简单介绍了机器学习中使用文本摘要算法可以实现的功能。
要了解有关该主题的更多信息,尤其是有关抽象文本摘要的更多信息,可以使用以下一些有用的资源:
原文来源:https://blog.floydhub.com/gentle-introduction-to-text-summarization-in-machine-learning/
编译出品