nlp中文文本摘要提取,快速提取文本主要意思

文本摘要提取

之前写过一版 文本摘要提取,但那版并不完美。有所缺陷(但也获得几十次收藏)。
中文文本摘要提取 (文本摘要提取 有代码)基于python

今天写改进版的文本摘要提取。

文本摘要旨在将文本或文本集合转换为包含关键信息的简短摘要。文本摘要按照输入类型可分为单文档摘要和多文档摘要。单文档摘要从给定的一个文档中生成摘要,多文档摘要从给定的一组主题相关的文档中生成摘要。按照输出类型可分为抽取式摘要和生成式摘要。
摘要:意思就是从一段文本 用几句话来概括这段话的意思

范例文本

花果园中央商务区F4栋楼B33城,时尚星廊,市民来电反映在此店消费被欺骗,该店承诺消费不用钱但后来需要收钱,并且此处工作人员表示自己是著名发型师,市民表示向花果园工商局反映后工作人员不作为,并且自己有相关证据,但花果园工商局叫其自己进行协商,市民希望职能部门能够尽快处理此问题,对此店的行为进行处罚并且将自己的钱退还,请职能部门及时处理。商家联系方式:11111111111。

最后的结果图
nlp中文文本摘要提取,快速提取文本主要意思_第1张图片
结果

{‘mean_scoredsenteces’: [‘花果园中央商务区F4栋楼B33城,’, ‘市民来电反映在此店消费被欺骗,’, ‘该店承诺消费不用钱但后来需要收钱,’, ‘市民表示向花果园工商局反映后工作人员不作为,’]}
{‘topnsenteces’: [‘花果园中央商务区F4栋楼B33城,’, ‘该店承诺消费不用钱但后来需要收钱,’, ‘市民表示向花果园工商局反映后工作人员不作为,’]}

实现过程

第一步:分句

#coding:utf-8
texts="""花果园中央商务区F4栋楼B33城,时尚星廊,市民来电反映在此店消费被欺骗,该店承诺消费不用钱但后来需要收钱,并且此处工作人员表示自己是著名发型师,市民表示向花果园工商局反映后工作人员不作为,并且自己有相关证据,但花果园工商局叫其自己进行协商,市民希望职能部门能够尽快处理此问题,对此店的行为进行处罚并且将自己的钱退还,请职能部门及时处理。商家联系方式:11111111111。"""

def sent_tokenizer(texts):
    start=0
    i=0#每个字符的位置
    sentences=[]
    punt_list=',.!?:;~,。!?:;~'#标点符号

    for text in texts:#遍历每一个字符
        if text in punt_list and token not in punt_list: #检查标点符号下一个字符是否还是标点
            sentences.append(texts[start:i+1])#当前标点符号位置
            start=i+1#start标记到下一句的开头
            i+=1
        else:
            i+=1#若不是标点符号,则字符位置继续前移
            token=list(texts[start:i+2]).pop()#取下一个字符.pop是删除最后一个
    if start<len(texts):
        sentences.append(texts[start:])#这是为了处理文本末尾没有标点符号的情况
    return sentences

sentence=sent_tokenizer(str(texts))
print(sentence)

第二步:加载停用词
停用词:见nlp 中文停用词数据集

#停用词
def stopwordslist(filepath):
    stopwords = [line.strip() for line in open(filepath, 'r', encoding='gbk').readlines()]
    return stopwords
# 加载停用词
stopwords = stopwordslist("停用词.txt")

第三步:取出高频词
我这里取出的前20个高频词
针对不同长度的文本可以取更多的高频词,文本越长,需要的高频词越多

import nltk
import jieba
import numpy

stopwords = stopwordslist("停用词.txt")
sentence=sent_tokenizer(texts)#分句
words=[w for sentence in sentence for w in jieba.cut(sentence) if w not in stopwords if len(w)>1 and w!='\t']#词语,非单词词,同时非符号
wordfre=nltk.FreqDist(words)#统计词频
topn_words=[w[0] for w in sorted(wordfre.items(),key=lambda d:d[1],reverse=True)][:20]#取出词频最高的20个单词
nlp中文文本摘要提取,快速提取文本主要意思_第2张图片

第四步:核心代码:给句子打分
思路

  1. 传入参数:sentences,topn_words。sentences为分句文本,topn_words为高频词数组
  2. 准备好记录每个分句的得分 数组 scores=[] ,初始化句子标号-1 :sentence_idx=-1
  3. 对每一个分句分词。得到分句分词数组
  4. 遍历每一个分句分词数组: for s in [list(jieba.cut(s)) for s in sentences]
  5. 句子索引+1。0表示第一个句子:sentence_idx+=1
  6. 准备好word_idx=[] ,用来存放高频词在当前分句中的索引位置
  7. 遍历每一个高频词
  8. 记录高频词在当前分句出现索引位置
  9. 对word_idx进行排序,得到的word_idx 类似1, 2, 3, 4, 5]或者[0, 1] 其中的0,1表示高频词在当前分句出现的索引位置
  10. 对得到 的 word_idx 进行聚类,初始化clusters=[], cluster=[word_idx[0]] ,clusters存放的是当前分句的所有簇, cluster存放的是一个簇 。
  11. 对当前分句的word_idx 中的高频词索引进行遍历,如果相邻两个高频词索引位置差小于阈值(我设为2,如果文本过长,阈值需增大),则这两个词是一类,添加进 cluster。
  12. clusters添加cluster 。得到clusters类似[[0, 1, 2], [4, 5], [7]]。(当前分句高频词索引为012457)
  13. 遍历clusters中的每一个簇,对每个簇进行打分,打分公式为:当前分句高频词总个数*当前分句高频词总个数/当前分句最后一个高频词与第一个高频词之间的距离
  14. 存放当前分句的最高分簇。
  15. 记录当前分句的的得分,记录格式(分句标号,簇的最高分)
  16. 重复步骤4-15 ,开始对下一个分句打分
  17. 返回 scores
def _score_sentences(sentences,topn_words):#参数 sentences:文本组(分好句的文本,topn_words:高频词组
    scores=[]
    sentence_idx=-1#初始句子索引标号-1
    for s in [list(jieba.cut(s)) for s in sentences]:# 遍历每一个分句,这里的每个分句是 分词数组 分句1类似 ['花', '果园', '中央商务区', 'F4', '栋楼', 'B33', '城', ',']
        sentence_idx+=1 #句子索引+1。。0表示第一个句子
        word_idx=[]#存放高频词在分句中的索引位置.得到结果类似:[1, 2, 3, 4, 5],[0, 1],[0, 1, 2, 4, 5, 7]..
        for w in topn_words:#遍历每一个高频词
            try:
                word_idx.append(s.index(w))#高频词出现在该分句子中的索引位置
            except ValueError:#w不在句子中
                pass
        word_idx.sort()
        if len(word_idx)==0:
            continue

        #对于两个连续的单词,利用单词位置索引,通过距离阀值计算族
        clusters=[] #存放的是几个cluster。类似[[0, 1, 2], [4, 5], [7]]
        cluster=[word_idx[0]] #存放的是一个类别(簇) 类似[0, 1, 2]
        i=1
        while i<len(word_idx):#遍历 当前分句中的高频词
            CLUSTER_THRESHOLD=2#举例阈值我设为2
            if word_idx[i]-word_idx[i-1]<CLUSTER_THRESHOLD:#如果当前高频词索引 与前一个高频词索引相差小于3,
                cluster.append(word_idx[i])#则认为是一类
            else:
                clusters.append(cluster[:])#将当前类别添加进clusters=[] 
                cluster=[word_idx[i]] #新的类别
            i+=1
        clusters.append(cluster)
        
        #对每个族打分,每个族类的最大分数是对句子的打分
        max_cluster_score=0
        for c in clusters:#遍历每一个簇
            significant_words_in_cluster=len(c)#当前簇 的高频词个数
            total_words_in_cluster=c[-1]-c[0]+1#当前簇里 最后一个高频词 与第一个的距离 
            score=1.0*significant_words_in_cluster*significant_words_in_cluster/total_words_in_cluster
            if score>max_cluster_score:
                max_cluster_score=score
        scores.append((sentence_idx,max_cluster_score))#存放当前分句的最大簇(说明下,一个分解可能有几个簇) 存放格式(分句索引,分解最大簇得分)
    return scores;
scored_sentences=_score_sentences(sentence,topn_words)
print(scored_sentences)

第五步:摘要提取
方法1:利用均值和标准差过滤非重要句子

avg=numpy.mean([s[1] for s in scored_sentences])#均值
std=numpy.std([s[1] for s in scored_sentences])#标准差
mean_scored=[(sent_idx,score) for (sent_idx,score) in scored_sentences if score>(avg+0.5*std)]#sent_idx 分句标号,score得分
c= dict(mean_scored_summary=[sentence[idx] for (idx,score) in mean_scored])
print(c)
方法2:返回得分最高的几个句子
top_n_scored=sorted(scored_sentences,key=lambda s:s[1])[-3:]#对得分进行排序,取出3个句子
top_n_scored=sorted(top_n_scored,key=lambda s:s[0])#对得分最高的几个分句,进行分句位置排序
c= dict(top_n_summary=[sentence[idx] for (idx,score) in top_n_scored])
print(c)

第五步:整理后的全文代码


#coding:utf-8
import nltk
import jieba
import numpy



#分句
def sent_tokenizer(texts):
    start=0
    i=0#每个字符的位置
    sentences=[]
    punt_list=',.!?:;~,。!?:;~'#标点符号

    for text in texts:#遍历每一个字符
        if text in punt_list and token not in punt_list: #检查标点符号下一个字符是否还是标点
            sentences.append(texts[start:i+1])#当前标点符号位置
            start=i+1#start标记到下一句的开头
            i+=1
        else:
            i+=1#若不是标点符号,则字符位置继续前移
            token=list(texts[start:i+2]).pop()#取下一个字符.pop是删除最后一个
    if start<len(texts):
        sentences.append(texts[start:])#这是为了处理文本末尾没有标点符号的情况
    return sentences

#对停用词加载
def stopwordslist(filepath):
    stopwords = [line.strip() for line in open(filepath, 'r', encoding='gbk').readlines()]
    return stopwords

#对句子打分
def score_sentences(sentences,topn_words):#参数 sentences:文本组(分好句的文本,topn_words:高频词组
    scores=[]
    sentence_idx=-1#初始句子索引标号-1
    for s in [list(jieba.cut(s)) for s in sentences]:# 遍历每一个分句,这里的每个分句是 分词数组 分句1类似 ['花', '果园', '中央商务区', 'F4', '栋楼', 'B33', '城', ',']
        sentence_idx+=1 #句子索引+1。。0表示第一个句子
        word_idx=[]#存放关键词在分句中的索引位置.得到结果类似:[1, 2, 3, 4, 5],[0, 1],[0, 1, 2, 4, 5, 7]..
        for w in topn_words:#遍历每一个高频词
            try:
                word_idx.append(s.index(w))#关键词出现在该分句子中的索引位置
            except ValueError:#w不在句子中
                pass
        word_idx.sort()
        if len(word_idx)==0:
            continue

        #对于两个连续的单词,利用单词位置索引,通过距离阀值计算族
        clusters=[] #存放的是几个cluster。类似[[0, 1, 2], [4, 5], [7]]
        cluster=[word_idx[0]] #存放的是一个类别(簇) 类似[0, 1, 2]
        i=1
        while i<len(word_idx):#遍历 当前分句中的高频词
            CLUSTER_THRESHOLD=2#举例阈值我设为2
            if word_idx[i]-word_idx[i-1]<CLUSTER_THRESHOLD:#如果当前高频词索引 与前一个高频词索引相差小于3,
                cluster.append(word_idx[i])#则认为是一类
            else:
                clusters.append(cluster[:])#将当前类别添加进clusters=[]
                cluster=[word_idx[i]] #新的类别
            i+=1
        clusters.append(cluster)

        #对每个族打分,每个族类的最大分数是对句子的打分
        max_cluster_score=0
        for c in clusters:#遍历每一个簇
            significant_words_in_cluster=len(c)#当前簇 的高频词个数
            total_words_in_cluster=c[-1]-c[0]+1#当前簇里 最后一个高频词 与第一个的距离
            score=1.0*significant_words_in_cluster*significant_words_in_cluster/total_words_in_cluster
            if score>max_cluster_score:
                max_cluster_score=score
        scores.append((sentence_idx,max_cluster_score))#存放当前分句的最大簇(说明下,一个分解可能有几个簇) 存放格式(分句索引,分解最大簇得分)
    return scores;

#结果输出
def results(texts,topn_wordnum,n):#texts 文本,topn_wordnum高频词个数,为返回几个句子
    stopwords = stopwordslist("停用词.txt")#加载停用词
    sentence = sent_tokenizer(texts)  # 分句
    words = [w for sentence in sentence for w in jieba.cut(sentence) if w not in stopwords if
             len(w) > 1 and w != '\t']  # 词语,非单词词,同时非符号
    wordfre = nltk.FreqDist(words)  # 统计词频
    topn_words = [w[0] for w in sorted(wordfre.items(), key=lambda d: d[1], reverse=True)][:topn_wordnum]  # 取出词频最高的topn_wordnum个单词

    scored_sentences = score_sentences(sentence, topn_words)#给分句打分

    # 1,利用均值和标准差过滤非重要句子
    avg = numpy.mean([s[1] for s in scored_sentences])  # 均值
    std = numpy.std([s[1] for s in scored_sentences])  # 标准差
    mean_scored = [(sent_idx, score) for (sent_idx, score) in scored_sentences if
                   score > (avg + 0.5 * std)]  # sent_idx 分句标号,score得分

    # 2,返回top n句子
    top_n_scored = sorted(scored_sentences, key=lambda s: s[1])[-n:]  # 对得分进行排序,取出n个句子
    top_n_scored = sorted(top_n_scored, key=lambda s: s[0])  # 对得分最高的几个分句,进行分句位置排序
    c = dict(mean_scoredsenteces=[sentence[idx] for (idx, score) in mean_scored])
    c1=dict(topnsenteces=[sentence[idx] for (idx, score) in top_n_scored])
    return c,c1

if __name__=='__main__':
    texts = str(input('请输入文本:'))
    topn_wordnum=int(input('请输入高频词数:'))
    n=int(input('请输入要返回的句子个数:'))
    c,c1=results(texts,topn_wordnum,n)
    print(c)
    print(c1)


nlp中文文本摘要提取,快速提取文本主要意思_第3张图片

简单界面程序

封装成简单 界面

超级简单额界面程序,懒得写太复杂的界面


#coding:utf-8
import nltk
import jieba
import numpy



#分句
def sent_tokenizer(texts):
    start=0
    i=0#每个字符的位置
    sentences=[]
    punt_list=',.!?:;~,。!?:;~'#标点符号

    for text in texts:#遍历每一个字符
        if text in punt_list and token not in punt_list: #检查标点符号下一个字符是否还是标点
            sentences.append(texts[start:i+1])#当前标点符号位置
            start=i+1#start标记到下一句的开头
            i+=1
        else:
            i+=1#若不是标点符号,则字符位置继续前移
            token=list(texts[start:i+2]).pop()#取下一个字符.pop是删除最后一个
    if start<len(texts):
        sentences.append(texts[start:])#这是为了处理文本末尾没有标点符号的情况
    return sentences

#对停用词加载
def stopwordslist(filepath):
    stopwords = [line.strip() for line in open(filepath, 'r', encoding='gbk').readlines()]
    return stopwords

#对句子打分
def score_sentences(sentences,topn_words):#参数 sentences:文本组(分好句的文本,topn_words:高频词组
    scores=[]
    sentence_idx=-1#初始句子索引标号-1
    for s in [list(jieba.cut(s)) for s in sentences]:# 遍历每一个分句,这里的每个分句是 分词数组 分句1类似 ['花', '果园', '中央商务区', 'F4', '栋楼', 'B33', '城', ',']
        sentence_idx+=1 #句子索引+1。。0表示第一个句子
        word_idx=[]#存放关键词在分句中的索引位置.得到结果类似:[1, 2, 3, 4, 5],[0, 1],[0, 1, 2, 4, 5, 7]..
        for w in topn_words:#遍历每一个高频词
            try:
                word_idx.append(s.index(w))#关键词出现在该分句子中的索引位置
            except ValueError:#w不在句子中
                pass
        word_idx.sort()
        if len(word_idx)==0:
            continue

        #对于两个连续的单词,利用单词位置索引,通过距离阀值计算族
        clusters=[] #存放的是几个cluster。类似[[0, 1, 2], [4, 5], [7]]
        cluster=[word_idx[0]] #存放的是一个类别(簇) 类似[0, 1, 2]
        i=1
        while i<len(word_idx):#遍历 当前分句中的高频词
            CLUSTER_THRESHOLD=2#举例阈值我设为2
            if word_idx[i]-word_idx[i-1]<CLUSTER_THRESHOLD:#如果当前高频词索引 与前一个高频词索引相差小于3,
                cluster.append(word_idx[i])#则认为是一类
            else:
                clusters.append(cluster[:])#将当前类别添加进clusters=[]
                cluster=[word_idx[i]] #新的类别
            i+=1
        clusters.append(cluster)

        #对每个族打分,每个族类的最大分数是对句子的打分
        max_cluster_score=0
        for c in clusters:#遍历每一个簇
            significant_words_in_cluster=len(c)#当前簇 的高频词个数
            total_words_in_cluster=c[-1]-c[0]+1#当前簇里 最后一个高频词 与第一个的距离
            score=1.0*significant_words_in_cluster*significant_words_in_cluster/total_words_in_cluster
            if score>max_cluster_score:
                max_cluster_score=score
        scores.append((sentence_idx,max_cluster_score))#存放当前分句的最大簇(说明下,一个分解可能有几个簇) 存放格式(分句索引,分解最大簇得分)
    return scores;

#结果输出
def results(texts,topn_wordnum,n):#texts 文本,topn_wordnum高频词个数,为返回几个句子
    stopwords = stopwordslist("停用词.txt")#加载停用词
    sentence = sent_tokenizer(texts)  # 分句
    words = [w for sentence in sentence for w in jieba.cut(sentence) if w not in stopwords if
             len(w) > 1 and w != '\t']  # 词语,非单词词,同时非符号
    wordfre = nltk.FreqDist(words)  # 统计词频
    topn_words = [w[0] for w in sorted(wordfre.items(), key=lambda d: d[1], reverse=True)][:topn_wordnum]  # 取出词频最高的topn_wordnum个单词

    scored_sentences = score_sentences(sentence, topn_words)#给分句打分

    # 1,利用均值和标准差过滤非重要句子
    avg = numpy.mean([s[1] for s in scored_sentences])  # 均值
    std = numpy.std([s[1] for s in scored_sentences])  # 标准差
    mean_scored = [(sent_idx, score) for (sent_idx, score) in scored_sentences if
                   score > (avg + 0.5 * std)]  # sent_idx 分句标号,score得分

    # 2,返回top n句子
    top_n_scored = sorted(scored_sentences, key=lambda s: s[1])[-n:]  # 对得分进行排序,取出n个句子
    top_n_scored = sorted(top_n_scored, key=lambda s: s[0])  # 对得分最高的几个分句,进行分句位置排序
    c = dict(mean_scoredsenteces=[sentence[idx] for (idx, score) in mean_scored])
    c1=dict(topnsenteces=[sentence[idx] for (idx, score) in top_n_scored])
    return c,c1


from PyQt5.QtWidgets import QApplication, QWidget, QTextEdit, QVBoxLayout, QPushButton,QLabel,QLineEdit,QFormLayout
import sys

class TextEditDemo(QWidget):
    def __init__(self, parent=None):
        super(TextEditDemo, self).__init__(parent)
        self.setWindowTitle("中文摘要提取")
        self.resize(500, 570)
        self.label1 = QLabel('输入文本')
        self.textEdit1 = QTextEdit()

        self.lineedit1 = QLineEdit()#请输入高频词数

        self.lineedit2 = QLineEdit()#请输入返回句子数

        self.btnPress1 = QPushButton("点击运行")

        self.textEdit2 = QTextEdit()#方法1显示
        self.textEdit3 = QTextEdit()#方法2 显示

        flo = QFormLayout()#表单布局

        flo.addRow("请输入高频词数:", self.lineedit1)
        flo.addRow("请输入返回句子数:", self.lineedit2)

        layout = QVBoxLayout()
        layout.addWidget(self.label1)
        layout.addWidget(self.textEdit1)
        layout.addLayout(flo)
        layout.addWidget(self.btnPress1)
        layout.addWidget(self.textEdit2)
        layout.addWidget(self.textEdit3)
        self.setLayout(layout)
        self.btnPress1.clicked.connect(self.btnPress1_Clicked)

    def btnPress1_Clicked(self):
        try:
            text = self.textEdit1.toPlainText()  # 返回输入的文本
            topn_wordnum =  int(self.lineedit1.text())  # 高频词 20
            n =  int(self.lineedit2.text()) # 3个返回句子
            c, c1 = results(str(text), topn_wordnum, n)
            self.textEdit2.setPlainText(str(c))
            self.textEdit2.setStyleSheet("font:10pt '楷体';border-width:5px;border-style: inset;border-color:gray")

            self.textEdit3.setPlainText(str(c1))
            self.textEdit3.setStyleSheet("font:10pt '楷体';border-width:5px;border-style: inset;border-color:red")

        except:
            self.textEdit2.setPlainText('操作失误')
            self.lineedit1.setText('操作失误,请输入整数')
            self.lineedit2.setText('操作失误,请输入整数')


if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = TextEditDemo()
    win.show()
    sys.exit(app.exec_())

nlp中文文本摘要提取,快速提取文本主要意思_第4张图片 nlp中文文本摘要提取,快速提取文本主要意思_第5张图片 nlp中文文本摘要提取,快速提取文本主要意思_第6张图片

在这里插入图片描述
作者:电气 余登武
nlp中文文本摘要提取,快速提取文本主要意思_第7张图片

你可能感兴趣的:(NLP,自然语言处理,自然语言处理,nlp,python)