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

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

代码示例

  1. # coding:utf-8

  2. import jieba

  3. import numpy as np

  4. import collections

  5. from sklearn import feature_extraction

  6. from sklearn.feature_extraction.text import TfidfTransformer

  7. from sklearn.feature_extraction.text import CountVectorizer

  8. from sklearn import preprocessing

  9. import math

  10. import re

  11. # 分割语句,生成语句列表和语句顺序字典

  12. def split_sentence(text, punctuation_list=r'([\s\.\!\?\。\!\?]+)'):

  13. # 文章按照标点符号列表里的符号切分成句子,将所有句子保存在列表里;同时生成一份带句子顺序的字典

  14. # 正则表达式分割中文文本

  15. sentence_set = re.split(punctuation_list, text)

  16. # 追加一个空标志

  17. sentence_set.append("")

  18. # 将分割后的字符串添加回原来的标点符号

  19. sentence_set = ["".join(i) for i in zip(sentence_set[0::2], sentence_set[1::2])]

  20. sentence_with_index = dict(zip(range(len(sentence_set)), sentence_set))

  21. # 返回语句列表和带语句顺序的字典

  22. return sentence_set, sentence_with_index

  23. # 计算语句列表中每个词的tfidf

  24. def get_tfidf_matrix(sentence_set, stop_word):

  25. corpus = []

  26. # 对每条语句进行分词,并且去掉停用词,写入corpus列表

  27. for sent in sentence_set:

  28. sent_cut = jieba.cut(sent)

  29. sent_list = [word for word in sent_cut if word not in stop_word]

  30. sent_str = ' '.join(sent_list)

  31. corpus.append(sent_str)

  32. vectorizer = CountVectorizer()

  33. transformer = TfidfTransformer()

  34. # CountVectorizer.fit_transform将文本进行词袋处理

  35. # TfidfTransformer.fit_transform 用于统计vectorizer中每个词语的TF-IDF值。

  36. tfidf = transformer.fit_transform(vectorizer.fit_transform(corpus))

  37. word=vectorizer.get_feature_names()

  38. tfidf_matrix = tfidf.toarray()

  39. # 返回tfidf矩阵

  40. return np.array(tfidf_matrix)

  41. # 基于tfidf对各行语句求权重

  42. def get_sentence_with_words_weight(tfidf_matrix):

  43. # tfidf_matrix值求和

  44. tfidf_matrix_sum = tfidf_matrix.sum(1)

  45. # 转换矩阵维度,进行归一化处理

  46. tfidf_matrix_sum = np.reshape(tfidf_matrix_sum,(-1, 1))

  47. min_max_scaler = preprocessing.MinMaxScaler()

  48. tfidf_matrix_sum = min_max_scaler.fit_transform(tfidf_matrix_sum)

  49. # 归一化处理后,将二维转一维再转list

  50. tfidf_list_sum=tfidf_matrix_sum.flatten().tolist()

  51. # list转为当前行对应的tfidf

  52. sentence_with_words_weight = dict(zip(range(len(tfidf_list_sum)),tfidf_list_sum ))

  53. return sentence_with_words_weight

  54. # 计算各语句的位置权重

  55. def get_sentence_with_position_weight(sentence_set):

  56. # 线性处理各语句位置权重,会导致后面的语句被忽视,这里做了对数规约化处理

  57. sentence_with_position_weight = {}

  58. total_sent = len(sentence_set)

  59. for i in range(total_sent):

  60. #sentence_with_position_weight[i] = (total_sent - i) / total_sent

  61. sentence_with_position_weight[i]=math.log(total_sent-i,10)

  62. return sentence_with_position_weight

  63. # 计算余弦相似度返回值比较

  64. def similarity(sent1, sent2):

  65. # 计算余弦相似度

  66. return np.sum(sent1 * sent2) / 1e-6 + (np.sqrt(np.sum(sent1 * sent1)) * \

  67. np.sqrt(np.sum(sent2 * sent2)))

  68. # 计算相似度权重

  69. def get_similarity_weight(tfidf_matrix):

  70. sentence_score = collections.defaultdict(lambda: 0.)

  71. # 遍历构建各语句之间的相似度,累加后,生成语句间相似度字典

  72. for i in range(len(tfidf_matrix)):

  73. score_i = 0.

  74. for j in range(len(tfidf_matrix)):

  75. score_i += similarity(tfidf_matrix[i], tfidf_matrix[j])

  76. sentence_score[i] = score_i

  77. # 进行归一化处理

  78. max_score = max(sentence_score.values()) # 归一化

  79. min_score = min(sentence_score.values())

  80. for key in sentence_score.keys():

  81. x = sentence_score[key]

  82. sentence_score[key] = (x - min_score) / (max_score - min_score)

  83. return sentence_score

  84. # 基于权重和得分生成总权重值

  85. def ranking_base_on_weigth(sentence_with_words_weight,

  86. sentence_with_position_weight,

  87. sentence_score, feature_weight=[1, 1, 1]):

  88. sentence_weight = collections.defaultdict(lambda: 0.)

  89. # 遍历各语句的tfidf值、语句顺序值、相似度值,并乘以相应的权重,加权后得到每条语句的总权重值

  90. for sent in sentence_score.keys():

  91. sentence_weight[sent] = feature_weight[0] * sentence_with_words_weight[sent] + \

  92. feature_weight[1] * sentence_with_position_weight[sent] + \

  93. feature_weight[2] * sentence_score[sent]

  94. sort_sent_weight = sorted(sentence_weight.items(), key=lambda d: d[1], reverse=True)

  95. return sort_sent_weight

  96. # 基于各语句总权重值和摘要比例,从语句中挑选相关摘要

  97. def get_summarization(sentence_with_index, sort_sent_weight, topK_ratio=0.3):

  98. topK = int(len(sort_sent_weight) * topK_ratio)

  99. # 按各语句的权重值进行排序,并获取topN条数据

  100. summarization_sent = sorted([sent[0] for sent in sort_sent_weight[:topK]])

  101. # 通过语句索引找相关语句拼装回去

  102. summarization = []

  103. for i in summarization_sent:

  104. summarization.append(sentence_with_index[i])

  105. summary = ''.join(summarization)

  106. return summary

  107. if __name__ == '__main__':

  108. stopwordfile= 'C:\Python\Pycharm\langprocess\\stopwords.txt'

  109. test_text = 'C:\Python\Pycharm\langprocess\\train\C4-Literature\C4-Literature02.txt'

  110. test_text = 'C:\Python\Pycharm\langprocess\\背影.txt'

  111. #test_text = 'C:\Python\Pycharm\langprocess\\第一章.txt'

  112. # 读取待做摘要的文章内容

  113. with open(test_text, 'r', encoding='utf-8', errors='ignore') as f:

  114. text = f.read()

  115. # 读取停用词词典

  116. stop_word = []

  117. with open(stopwordfile, 'r', encoding='utf-8') as f:

  118. for line in f.readlines():

  119. stop_word.append(line.strip())

  120. # 返回断句后的语句 索引语句字典

  121. sentence_set, sentence_with_index = split_sentence(text, punctuation_list=r'([\s\.\!\?\。\!\?]+)')

  122. # 返回各语句各分词的tfidf矩阵

  123. tfidf_matrix = get_tfidf_matrix(sentence_set, stop_word)

  124. # 根据tfidf矩阵,生成该语句的tfidf

  125. sentence_with_words_weight = get_sentence_with_words_weight(tfidf_matrix)

  126. # 生成语句的位置权重值

  127. sentence_with_position_weight = get_sentence_with_position_weight(sentence_set)

  128. # 根据tfidf矩阵,进行文本相似度计算,生成各语句的文本相似度值

  129. sentence_score = get_similarity_weight(tfidf_matrix)

  130. # tfidf值、位置权重值、文本相似度值按照相关权重进行计算,返回总的权重值

  131. sort_sent_weight = ranking_base_on_weigth(sentence_with_words_weight,

  132. sentence_with_position_weight,

  133. sentence_score, feature_weight=[1, 0.01, 1])

  134. # 根据权重值、摘要比例生成摘要

  135. summarization = get_summarization(sentence_with_index, sort_sent_weight, topK_ratio=0.2)

  136. print('摘要:\n', summarization)


摘要内容:

  1. 我与父亲不相见已二年余了,我最不能忘记的是他的背影。

  2. 那年冬天,祖母死了,父亲的差使也交卸了,正是祸不单行的日子,我从北京到徐州,打算跟着父亲奔丧回家。

  3. 到徐州见着父亲,看见满院狼藉的东西,又想起祖母,不禁簌簌地流下眼泪。

  4. 这些日子,家中光景很是惨淡,一半为了丧事,一半为了父亲赋闲。

  5. 丧事完毕,父亲要到南京谋事,我也要回北京念书,我们便同行。

  6. 父亲因为事忙,本已说定不送我,叫旅馆里一个熟识的茶房陪我同去。

  7. 但他终于不放心,怕茶房不妥帖;颇踌躇了一会。父亲是一个胖子,走过去自然要费事些。

  8. 我看见他戴着黑布小帽,穿着黑布大马褂,深青布棉袍,蹒跚地走到铁道边,慢慢探身下去,尚不大难。

  9. 过铁道时,他先将橘子散放在地上,自己慢慢爬下,再抱起橘子走。

  10. 我北来后,他写了一信给我,信中说道,“我身体平安,惟膀子疼痛利害,举箸提笔,诸多不便,大约大去之期不远矣。”

  11. 我读到此处,在晶莹的泪光中,又看见那肥胖的,青布棉袍,黑布马褂的背影。



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

长按二维码关注“追梦IT人”

关于自然语言处理系列-文本摘要提取进阶_归一化_02