c

codeye

V1

2022/10/28阅读:31主题:默认主题

如何用Python和NLTK有效地总结文本

用Python对文本进行总结 如何用Python和NLTK有效地总结文本

有时你需要对一个给定的文本进行总结。我在建立一个新闻帖子的集合时遇到了这个挑战。使用完整的文本来解释一篇文章的含义需要大量的时间(我收集了大约250.000篇),所以我开始寻找一种将文本总结为三句话的方法。这篇文章描述了一个相对但令人惊讶的有效方法来创建一个摘要。

该算法的目标是将几段文字的内容总结为几个句子。这些句子将取自原文。将不使用文本生成。

我们的想法是,文本的目标可以通过识别文本中最常用的词来找到。常见的停顿词将被排除。在找到最常用的词之后,使用加权计数算法,找到包含这些词最多的句子(一个词在文本中使用得越多,其权重就越高)。具有最高权重的句子被选中并构成摘要。

  1. 计算每个词在文本中的出现次数(不包括停顿词)。
  2. 计算每个用过的词的权重
  3. 通过总结每个词的权重来计算句子的权重
  4. 找到权重最高的句子
  5. 将这些句子按其原来的顺序排列

该算法使用自然语言工具包(NLTK)将文本分割成句子,将句子分割成单词。NLTK是用pip安装的。

pip install nltk numpy

但是,我们首先要导入所需的模块,并建立要从算法中排除的停止词列表。

from nltk import tokenize, word_tokenize

with open("stopwords.txt"), "r", encoding="utf-8") as f:
     text = " ".join(f.readlines())
STOP_WORDS = set(text.split())

在互联网上可以很容易地找到每种语言的停止词列表。

该算法的第一步是用文本中的单词频率建立一个列表。


text = '...............'

word_weights={}
for word in word_tokenize(text) :  
    word = word.lower()
    if len(word) > 1 and word not in STOP_WORDS:
        if word in word_weights.keys():            
            word_weights[word] += 1
        else:
            word_weights[word] = 1

代码建立了一个以单词为键,以单词出现的次数为值的字典。使用NLTK的单词标记器将文本分割成小写的单词(第4-5行)。所有的词都被转换为小写字母,因此我们计算句子中的词和句子开始时的词。否则,'cat'将被识别为'Cat'这样一个不同的词。

如果该词的长度至少为两个字符,除去像',','.'等这样的标记,并且它不在止损词列表中(第6行),如果该词已经在字典中,则出现次数增加1,或者添加到字典中,计数为1(第7-10行)。

当完整的文本被解析后,'word_weights'字典包含了所有的词和它们各自的计数。实际计数被用作单词权重。可以通过将出现次数与最高出现次数相除,将这些值在0和1之间进行缩放,但尽管这样做可能很直观,但它并没有改变算法的工作。不进行这种划分,可以节省宝贵的时间。

现在我们可以确定文本中每个句子的权重,并找到最高权重。

sentence_weights={}
for sent in tokenize.sent_tokenize(text):
    for word in word_tokenize(sent) :  
        word = word.lower()
        if word in word_weights.keys():            
            if sent in sentence_weights.keys():
                sentence_weights[sent] += word_weights[word]
            else:
                sentence_weights[sent] = word_weights[word]

no_sentences = 3
highest_weights = sorted(sentence_weights.values())[-no_sentences:]

首先,用sent_tokenize()将文本分割成句子。然后,每个句子被分割成单词,并对每个句子的单个单词权重进行总结,以确定句子的权重。在这些循环之后,字典sentence_weights包含了每个句子的权重以及它们的重要性。

最重要的句子权重可以通过以下方式找到(第12行):从字典中取值,对这些值进行排序,并取最后的n个值,其中n是我们希望用于总结的句子数量。变量 highest_weights 包含那些需要在摘要中出现的句子的权重。

最后一步是将这些句子合并成一个摘要。有两个选择。首先,我们可以把它们按重要性排序,其次,我们可以使用它们在提供的文本中出现的原始顺序。经过一些实验,后一种选择是更好的。

summary=""
for sentence,strength in sentence_weights.items():  
    if strength in highest_weights:
        summary += sentence + " "
summary = summary.replace('_'' ').strip()

摘要是通过走过所有的句子和它们的权重,并使用权重最高的_权重的句子来创建的。字典保持了加法的顺序,所以句子是根据它们在文本中的出现情况来解析的。最后,进行一些清理工作,我们最终得到一个令人惊讶的准确的摘要。

最后一步是将所有的步骤合并成一个函数。

from nltk import tokenize, word_tokenize

with open("stopwords.txt""r", encoding="utf-8") as f:
     text = " ".join(f.readlines())
STOP_WORDS = set(text.split())

def summarize(text, no_sentences=3):
    word_weights={}
    for word in word_tokenize(text):
        word = word.lower()
        if len(word) > 1 and word not in STOP_WORDS:
            if word in word_weights.keys():            
                word_weights[word] += 1
            else:
                word_weights[word] = 1

    sentence_weights={}
    for sent in tokenize.sent_tokenize(text):
        for word in word_tokenize(sent) :  
            word = word.lower()
            if word in word_weights.keys():            
                if sent in sentence_weights.keys():
                    sentence_weights[sent] += word_weights[word]
                else:
                    sentence_weights[sent] = word_weights[word]
    highest_weights = sorted(sentence_weights.values())[-no_sentences:]

    summary=""
    for sentence,strength in sentence_weights.items():  
        if strength in highest_weights:
            summary += sentence + " "
    summary = summary.replace('_'' ').strip()
    return summary

这个函数在我的新闻档案项目中具有重要地位。将一个文本从几十句减少到三句确实有帮助。

代码并非完美无缺。句子标记器不是最好的,但相当快。有时我最终得到的是一个4句话的摘要,因为它错过了两个句子的分离。但是,这种速度是值得的。

同样的情况也适用于创建摘要的最后部分。如果在heighest_weights中有多个权重最低的句子,所有这些句子都会被添加到摘要中。

并不总是需要写出无瑕疵的代码。一个句子过多的摘要并不会使银行破产或系统崩溃。在这种情况下,速度要高于准确性。

如果需要对一篇较大的文章进行总结,可将其分成20至50个句子、段落或章节的部分,并为每个部分生成一个摘要。将这些摘要结合起来,就可以得到整个文本的摘要。一个天真的方法是这样做的。

with open("longtext.txt""r", encoding="utf-8") as f:
     text = " ".join(f.readlines())

sentences = []
for sent in tokenize.sent_tokenize():
    sentences.append(sent)
chunks = [sentences[x:x+50] for x in range(0, len(sentences), 50)]

summary = []
for c in chunks:
    summary.append(unidecode.unidecode(summarize(' '.join(c))))
summary = " ".join(summary)

请注意,这段代码从文本中提取句子,对它们进行分组,并在调用summaryize方法之前将它们串联成一个字符串。这个方法将再次提取句子。如果需要对大的文本进行总结,可以调整summarize函数以接受一个句子列表。

该函数在多种语言下工作。我已经用英语、荷兰语和德语对它进行了测试,当你使用正确的停止词列表时,它对每一种语言都有效。

祝您愉快!

分类:

后端

标签:

后端

作者介绍

c
codeye
V1