关于自然语言处理系列-文本摘要提取进阶

2022-03-11 14:42:01 浏览数 (1)

关于自然语言处理重要的一个部分是文本摘要,文本摘要的提取涉及到分词、断句、文本权重问题;分词前文已述,断句通过正则表达式完成;文本权重又包括句子的tfidf权重、文本相似度权重和句子的位置权重;关于权重又涉及到归一化处理和权重的权值等等。总的来说提取的摘要质量要比之前的snownlp、sumy、goose直接拿来用效果要好一些。

相关代码来自互联网,不过自己做了一些优化和完善。

代码示例

代码语言:javascript复制
# coding:utf-8
import jieba
import numpy as np
import collections
from sklearn import feature_extraction
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn import preprocessing
import math
import re

# 分割语句,生成语句列表和语句顺序字典
def split_sentence(text, punctuation_list=r'([s.!?。!?] )'):
    # 将文章按照标点符号列表里的符号切分成句子,将所有句子保存在列表里;同时生成一份带句子顺序的字典
    # 正则表达式分割中文文本
    sentence_set = re.split(punctuation_list, text)
    # 追加一个空标志
    sentence_set.append("")
    # 将分割后的字符串添加回原来的标点符号
    sentence_set = ["".join(i) for i in zip(sentence_set[0::2], sentence_set[1::2])]
    sentence_with_index = dict(zip(range(len(sentence_set)), sentence_set))
    # 返回语句列表和带语句顺序的字典
    return sentence_set, sentence_with_index

# 计算语句列表中每个词的tfidf值
def get_tfidf_matrix(sentence_set, stop_word):
    corpus = []
    # 对每条语句进行分词,并且去掉停用词,写入corpus列表
    for sent in sentence_set:
        sent_cut = jieba.cut(sent)
        sent_list = [word for word in sent_cut if word not in stop_word]
        sent_str = ' '.join(sent_list)
        corpus.append(sent_str)

    vectorizer = CountVectorizer()
    transformer = TfidfTransformer()
    # CountVectorizer.fit_transform将文本进行词袋处理
    # TfidfTransformer.fit_transform 用于统计vectorizer中每个词语的TF-IDF值。
    tfidf = transformer.fit_transform(vectorizer.fit_transform(corpus))
    word=vectorizer.get_feature_names()
    tfidf_matrix = tfidf.toarray()
    # 返回tfidf矩阵
    return np.array(tfidf_matrix)

# 基于tfidf对各行语句求权重
def get_sentence_with_words_weight(tfidf_matrix):
    # 对tfidf_matrix值求和
    tfidf_matrix_sum = tfidf_matrix.sum(1)
    # 转换矩阵维度,进行归一化处理
    tfidf_matrix_sum = np.reshape(tfidf_matrix_sum,(-1, 1))
    min_max_scaler = preprocessing.MinMaxScaler()
    tfidf_matrix_sum = min_max_scaler.fit_transform(tfidf_matrix_sum)
    # 归一化处理后,将二维转一维再转list
    tfidf_list_sum=tfidf_matrix_sum.flatten().tolist()
    # 将list转为当前行对应的tfidf值
    sentence_with_words_weight = dict(zip(range(len(tfidf_list_sum)),tfidf_list_sum ))

    return sentence_with_words_weight

# 计算各语句的位置权重
def get_sentence_with_position_weight(sentence_set):
    # 线性处理各语句位置权重,会导致后面的语句被忽视,这里做了对数规约化处理
    sentence_with_position_weight = {}
    total_sent = len(sentence_set)
    for i in range(total_sent):
        #sentence_with_position_weight[i] = (total_sent - i) / total_sent
        sentence_with_position_weight[i]=math.log(total_sent-i,10)
    return sentence_with_position_weight

# 计算余弦相似度返回值比较
def similarity(sent1, sent2):
    # 计算余弦相似度
    return np.sum(sent1 * sent2) / 1e-6   (np.sqrt(np.sum(sent1 * sent1)) * 
                                           np.sqrt(np.sum(sent2 * sent2)))

# 计算相似度权重
def get_similarity_weight(tfidf_matrix):
    sentence_score = collections.defaultdict(lambda: 0.)
    # 遍历构建各语句之间的相似度,累加后,生成语句间相似度字典
    for i in range(len(tfidf_matrix)):
        score_i = 0.
        for j in range(len(tfidf_matrix)):
            score_i  = similarity(tfidf_matrix[i], tfidf_matrix[j])
        sentence_score[i] = score_i

    # 进行归一化处理
    max_score = max(sentence_score.values())  # 归一化
    min_score = min(sentence_score.values())
    for key in sentence_score.keys():
        x = sentence_score[key]
        sentence_score[key] = (x - min_score) / (max_score - min_score)

    return sentence_score

# 基于权重和得分生成总权重值
def ranking_base_on_weigth(sentence_with_words_weight,
                           sentence_with_position_weight,
                           sentence_score, feature_weight=[1, 1, 1]):
    sentence_weight = collections.defaultdict(lambda: 0.)
    # 遍历各语句的tfidf值、语句顺序值、相似度值,并乘以相应的权重,加权后得到每条语句的总权重值
    for sent in sentence_score.keys():
        sentence_weight[sent] = feature_weight[0] * sentence_with_words_weight[sent]   
                                feature_weight[1] * sentence_with_position_weight[sent]   
                                feature_weight[2] * sentence_score[sent]
    sort_sent_weight = sorted(sentence_weight.items(), key=lambda d: d[1], reverse=True)

    return sort_sent_weight

# 基于各语句总权重值和摘要比例,从语句中挑选相关摘要
def get_summarization(sentence_with_index, sort_sent_weight, topK_ratio=0.3):
    topK = int(len(sort_sent_weight) * topK_ratio)
    # 按各语句的权重值进行排序,并获取topN条数据
    summarization_sent = sorted([sent[0] for sent in sort_sent_weight[:topK]])
    # 通过语句索引找相关语句拼装回去
    summarization = []
    for i in summarization_sent:
        summarization.append(sentence_with_index[i])

    summary = ''.join(summarization)
    return summary

if __name__ == '__main__':
    stopwordfile= 'C:PythonPycharmlangprocess\stopwords.txt'
    test_text = 'C:PythonPycharmlangprocess\trainC4-LiteratureC4-Literature02.txt'
    test_text = 'C:PythonPycharmlangprocess\背影.txt'
    #test_text = 'C:PythonPycharmlangprocess\第一章.txt'
    # 读取待做摘要的文章内容
    with open(test_text, 'r', encoding='utf-8', errors='ignore') as f:
        text = f.read()
    # 读取停用词词典
    stop_word = []
    with open(stopwordfile, 'r', encoding='utf-8') as f:
        for line in f.readlines():
            stop_word.append(line.strip())
    # 返回断句后的语句 和 索引语句字典
    sentence_set, sentence_with_index = split_sentence(text, punctuation_list=r'([s.!?。!?] )')
    # 返回各语句各分词的tfidf矩阵
    tfidf_matrix = get_tfidf_matrix(sentence_set, stop_word)
    # 根据tfidf矩阵,生成该语句的tfidf值
    sentence_with_words_weight = get_sentence_with_words_weight(tfidf_matrix)
    # 生成语句的位置权重值
    sentence_with_position_weight = get_sentence_with_position_weight(sentence_set)
    # 根据tfidf矩阵,进行文本相似度计算,生成各语句的文本相似度值
    sentence_score = get_similarity_weight(tfidf_matrix)
    # 将tfidf值、位置权重值、文本相似度值按照相关权重进行计算,返回总的权重值
    sort_sent_weight = ranking_base_on_weigth(sentence_with_words_weight,
                                              sentence_with_position_weight,
                                              sentence_score, feature_weight=[1, 0.01, 1])
    # 根据权重值、摘要比例生成摘要
    summarization = get_summarization(sentence_with_index, sort_sent_weight, topK_ratio=0.2)
    print('摘要:n', summarization)

摘要内容:

代码语言:javascript复制
我与父亲不相见已二年余了,我最不能忘记的是他的背影。
那年冬天,祖母死了,父亲的差使也交卸了,正是祸不单行的日子,我从北京到徐州,打算跟着父亲奔丧回家。
到徐州见着父亲,看见满院狼藉的东西,又想起祖母,不禁簌簌地流下眼泪。
这些日子,家中光景很是惨淡,一半为了丧事,一半为了父亲赋闲。
丧事完毕,父亲要到南京谋事,我也要回北京念书,我们便同行。
父亲因为事忙,本已说定不送我,叫旅馆里一个熟识的茶房陪我同去。
但他终于不放心,怕茶房不妥帖;颇踌躇了一会。父亲是一个胖子,走过去自然要费事些。
我看见他戴着黑布小帽,穿着黑布大马褂,深青布棉袍,蹒跚地走到铁道边,慢慢探身下去,尚不大难。
过铁道时,他先将橘子散放在地上,自己慢慢爬下,再抱起橘子走。
我北来后,他写了一信给我,信中说道,“我身体平安,惟膀子疼痛利害,举箸提笔,诸多不便,大约大去之期不远矣。”
我读到此处,在晶莹的泪光中,又看见那肥胖的,青布棉袍,黑布马褂的背影。

0 人点赞