浅谈机器学习用于自动生成文本摘要

你有没有把冗长的文件归纳成一个简短的段落?你花了多长时间?手动生成摘要可能既费时又乏味。自动文本摘要有望克服这些困难,并允许您轻松地在一篇文章中生成关键思想。

文本摘要是一种生成简明而精确的大量文本摘要的技术,同时侧重于传达有用信息的部分,而不会失去整体意义。
自动文本摘要旨在将冗长的文档转换为缩短的版本,如果手动完成,这可能是困难且成本高昂的。
可以训练机器学习算法以理解文档并在生成所需的概括文本之前识别传达重要事实和信息的部分。例如,下面的图片是这篇新闻文章该文章已被输入机器学习算法以生成摘要。

img一篇在线新闻文章,使用文本摘要机器学习算法进行了总结

需要进行文本摘要

随着目前数据空间的大量涌现(主要是非结构化的文本数据),需要开发自动文本摘要工具,使人们能够轻松地从中获取洞察力。目前,我们可以快速访问大量信息。但是,大多数此类信息都是多余的,无关紧要,并且可能无法传达预期的含义。例如,如果您正在寻找在线新闻文章中的特定信息,您可能需要深入了解其内容,并在获取所需信息之前花费大量时间清除不必要的内容。因此,使用能够提取有用信息的自动文本摘要器,这些信息会遗漏不必要且无关紧要的数据,这一点变得至关重要。实现摘要可以增强文档的可读性,

文本摘要的主要类型

从广义上讲,有两种方法可以总结NLP中的文本:提取和抽象。

基于提取的摘要

在基于提取的摘要中,表示最重要点的单词的子集从一段文本中提取并组合以进行摘要。可以将其视为荧光笔 - 从源文本中选择主要信息。

img突出显示器=基于提取的摘要

在机器学习中,提取摘要通常涉及权衡句子的基本部分并使用结果生成摘要。

可以使用不同类型的算法和方法来测量句子的权重,然后根据它们的相关性和相似性对它们进行排序 - 并进一步加入它们以生成摘要。这是一个例子:

img基于提取的摘要在行动中。

如您所见,提取的摘要由以粗体突出显示的单词组成,但结果可能在语法上不准确。

基于抽象的摘要

在基于抽象的总结中,先进的深度学习技术应用于解释和缩短原始文档,就像人类一样。可以把它想象成一支笔 - 它产生的新颖句子可能不是源文件的一部分。

imgPen =基于抽象的摘要

由于抽象机器学习算法可以生成表示源文本中最重要信息的新短语和句子,因此它们可以帮助克服提取技术的语法不准确性。这是一个例子:

img基于抽象的摘要在行动中。

尽管抽象在文本摘要中表现更好,但开发其算法需要复杂的深度学习技术和复杂的语言建模。

为了生成合理的输出,基于抽象的摘要方法必须解决各种NLP问题,例如自然语言生成,语义表示和推理置换。

因此,提取文本摘要方法仍然广泛流行。在本文中,我们将专注于基于提取的方法。

如何执行文本摘要

让我们用一个简短的段落来说明如何执行提取文本摘要。

这是段落:

“彼得和伊丽莎白乘出租车参加了这个城市的夜间聚会。在聚会期间,伊丽莎白垮了,被送往医院。由于她被诊断出患有脑损伤,医生告诉彼得要留在她身边直到她康复。因此,彼得在医院住了3天而没有离开。“

以下是总结上段的步骤,同时尽可能地保持其预期含义。

第1步:将段落转换为句子

首先,让我们将段落分成相应的句子。进行转换的最佳方法是在句点出现时提取句子。

彼得和伊丽莎白乘出租车参加了这个城市的夜间聚会

在聚会期间,伊丽莎白垮了,然后被送往医院

由于她被诊断出患有脑损伤,医生告诉彼得要留在她身边直到她康复

因此,彼得在医院住了3天而没有离开

第2步:文本处理

接下来,让我们通过从句子中删除停用词(极其常见的词语,例如“和”和“the”),数字,标点符号和其他特殊字符来进行文本处理。

执行过滤有助于删除冗余和无关紧要的信息,这些信息可能无法为文本的含义提供任何附加值。

以下是文本处理的结果:

彼得伊丽莎白乘出租车参加夜晚派对

派对伊丽莎白崩溃急于医院

诊断脑损伤的医生告诉彼得住得好

彼得没有离开就住了几天

第3步:标记化

对句子进行标记以获得句子中存在的所有单词。这是一个单词列表:

1
['peter','elizabeth','took','taxi','attend','night','party','city','party','elizabeth','collapse','rush','hospital', 'diagnose','brain', 'injury', 'doctor','told','peter','stay','besides','get','well','peter', 'stayed','hospital','days','without','leaving']

步骤4:评估单词的加权出现频率

之后,让我们计算所有单词的加权出现频率。为了达到这个目的,我们将每个单词的出现频率除以段落中最常用单词的频率,即出现三次的“Peter”。

这是一个表格,给出了每个单词的加权出现频率。

WORD FREQUENCY WEIGHTED FREQUENCY
peter 3 1
elizabeth 2 0.67
took 1 0.33
taxi 1 0.33
attend 1 0.33
night 1 0.33
party 2 0.67
city 1 0.33
collapse 1 0.33
rush 1 0.33
hospital 2 0.67
diagnose 1 0.33
brain 1 0.33
injury 1 0.33
doctor 1 0.33
told 1 0.33
stay 2 0.67
besides 1 0.33
get 1 0.33
well 1 0.33
days 1 0.33
without 1 0.33
leaving 1 0.33

步骤5:用加权频率替换单词

让我们用他们的加权频率替换原始句子中的每个单词。然后,我们将计算它们的总和。

由于在处理阶段被删除的无效字的加权频率(例如停用词和特殊字符)为零,因此不必添加它们。

SENTENCE ADD WEIGHTED FREQUENCIES SUM
1 Peter and Elizabeth took a taxi to attend the night party in the city 1 + 0.67 + 0.33 + 0.33 + 0.33 + 0.33 + 0.67 + 0.33 3.99
2 While in the party, Elizabeth collapsed and was rushed to the hospital 0.67 + 0.67 + 0.33 + 0.33 + 0.67 2.67
3 Since she was diagnosed with a brain injury, the doctor told Peter to stay besides her until she gets well. 0.33 + 0.33 + 0.33 + 0.33 + 1 + 0.33 + 0.33 + 0.33 + 0.33 +0.33 3.97
4 Therefore, Peter stayed with her at the hospital for 3 days without leaving 1 + 0.67 + 0.67 + 0.33 + 0.33 + 0.33 3.33

从单词的加权频率之和,我们可以推断出第一句在段落中的权重最大。因此,它可以提供该段落的最佳代表性摘要。

此外,如果第一个句子与第三个句子组合,这是句子中第二个最重的句子,则可以生成更好的摘要。

以上示例仅给出了如何在机器学习中执行基于提取的文本摘要的基本说明。现在,让我们看看如何在创建真实世界的摘要生成器时应用上述概念。

维基百科文章的文本摘要

让我们亲自动手,创建一个文本摘要生成器,它可以缩短冗长的Web文章中的信息。为了简单起见,除了Python的NLTK工具包之外,我们不会使用任何其他机器学习库。

以下是摘要生成器的代码蓝图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 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库。

以下是抓取文章内容的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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上抓取文本时管理不同字符集编码的麻烦。

我们将使用该实用程序中的urlopen功能urllib.request打开网页。然后,我们将使用该read函数来读取已删除的数据对象。为了解析数据,我们将调用该BeautifulSoup对象并将两个参数传递给它; 也就是说,article_readhtml.parser

find_all函数用于返回<p>HTML中存在的所有元素。此外,使用.text使我们能够仅选择<p>元素中找到的文本。

第2步:处理数据

为确保报废的文本数据尽可能无噪音,我们将执行一些基本的文本清理。为了协助我们做加工,我们将导入列表禁用词NLTK库。

我们还将导入PorterStemmer,这是一种将单词缩减为根形式的算法。例如,清洁清洁清洁可以减少到根清洁

此外,我们将创建一个字典表,其中包含文本中每个单词的出现频率。我们将遍历文本和相应的单词以消除任何停用词。

然后,我们将检查单词中是否存在单词frequency_table如果单词以前在字典中可用,则其值将更新为1.否则,如果第一次识别该单词,则其值将设置为1。

例如,频率表应如下所示:

WORD FREQUENCY
century 7
world 4
United States 3
computer 1

这是代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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库中的内置方法。

1
2
3
from nltk.tokenize import word_tokenize, sent_tokenize

sentences = sent_tokenize(article)

第4步:找到句子的加权频率

为了评估文本中每个句子的分数,我们将分析每个术语的出现频率。在这种情况下,我们将用它的词来评分每个句子; 也就是说,添加句子中找到的每个重要单词的频率。

看看下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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最好使用哈希函数或智能索引函数来考虑这些边缘情况并避免冲突。

第五步:计算句子的阈值

为了进一步调整符合摘要条件的句子类型,我们将创建句子的平均分数。使用此阈值,我们可以避免选择分数低于平均分数的句子。

这是代码:

1
2
3
4
5
6
7
8
9
10
11
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步:获取摘要

最后,由于我们拥有所有必需的参数,我们现在可以生成文章的摘要。

这是代码:

1
2
3
4
5
6
7
8
9
10
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

包起来

这是一个图像,展示了创建摘要生成器的工作流程。

img

创建摘要算法的基本工作流程

以下是机器学习中简单的提取文本摘要生成器的完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#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 Notebook上运行代码:

在FloydHub上运行

在这种情况下,我们应用了平均得分的1.5倍的阈值。这是经过几次试验后为我们产生良好结果的超参数值。当然,您可以根据自己的喜好微调价值并改进汇总结果。

这是维基百科文章的摘要版本的图像。

img

使用摘要算法汇总的维基百科文章

正如您所看到的,运行代码总结了冗长的维基百科文章,并简要概述了20世纪的主要事件。

尽管如此,可以改进摘要生成器,以便更好地生成大量文本的简明和精确的摘要。

把事情提高一个档次……

当然,本文只是简单介绍了机器学习中使用文本摘要算法可以实现的功能。

要了解有关该主题的更多信息,尤其是有关抽象文本摘要的更多信息,可以使用以下一些有用的资源:

  • 是否有可能将两种方法(抽象和提取)结合起来?指针生成器网络背后的主要思想是通过结合提取(指向)和抽象(生成)来获得两全其美。

img图片来自“ 驯服回归神经网络以获得更好的总结


特别感谢FloydHub的整个团队,特别是Alessio,他们在提高本文的质量和流程方面提供了宝贵的反馈和支持。你们好棒!


关于Alfrick Opidi

Alfrick是一位对开发机器学习世界有浓厚兴趣的Web开发人员。目前,他参与了在生成敏捷和未来主义Web应用程序时实现机器学习概念的项目。在业余时间,他从事技术写作,为人类揭开复杂的机器学习概念的神秘面纱。将他视为一个全面的技术极客,敏锐地关注这个行业的最新发展是否易于学习和有趣。Alfrick也是FloydHub AI Writer。你可以在这里找到更多关于他的信息。您可以在LinkedInGitHub上与Alfrick联系。

以上为机翻结果,点击查看原文

来了,老弟
-------------    本文结束  感谢您的阅读    -------------
0%