Lead算法

Lead算法是摘要算法中比较简单快速,但是又高效的算法
基本原理就是只选择文章的前若干句话作为摘要,经常设置为3
于是对应的实现也比较简单

  1. class Lead:
  2. def __init__(self, name, n):
  3. self.name = name
  4. self.n = n
  5. def get_summary(self,document):
  6. sentences=possess_sentence(document)
  7. if len(sentences) < self.n:
  8. return sentences
  9. else:
  10. return sentences[0:self.n]
  11. def get_labels(self,label):
  12. sentences = possess_sentence(label)
  13. return sentences
  14. def train_eval(self,path1,path2,pairs_num):
  15. fo = open(path1, "r",encoding='gb18030', errors='ignore')
  16. fl = open(path2, "r",encoding='gb18030', errors='ignore')
  17. rouge1=0
  18. rouge2=0
  19. rougeL=0
  20. rouge = Rouge()
  21. for i in range(pairs_num):
  22. line1 = fo.readline()
  23. line2 = fl.readline()
  24. result=self.get_summary(line1)
  25. label=self.get_labels(line2)
  26. result=" ".join(result)
  27. label=" ".join(label)
  28. rouge_score = rouge.get_scores(result, label)
  29. #print(rouge_score[0]["rouge-1"])
  30. rouge1+=rouge_score[0]["rouge-1"]['r']
  31. rouge2+=rouge_score[0]["rouge-2"]['r']
  32. rougeL+=rouge_score[0]["rouge-l"]['r']
  33. print("Rouge1 Recall",rouge1/pairs_num)
  34. print("Rouge2 Recall",rouge2/pairs_num)
  35. print("RougeL Recall",rougeL/pairs_num)
  36. model=Lead('lead3',3)
  37. model.train_eval(file_path,label_path,1000)

数据集

这里使用的文本数据是CNN_Daily Mail
基本的观察发现

  1. 对应的Train.txt.src里放的是原文,并用##SENTEN##对句子进行分割,一行是多个句子
  2. 对应的Train.txt.tgt里放的是对应的概述,同样做分割处理,一行多个句子

文本语种为英语,数据集较大,本次仅仅使用部分
同时对文本进行简单的初步的数据清洗和分割处理

  1. def normalizeString(s):
  2. #s = re.sub(r"([.!?])", r" \1", s)
  3. #s = re.sub(r"[^a-zA-Z.!?]+", r" ", s)
  4. s=s.strip()
  5. s = s.replace("\n", "")
  6. return s
  7. def possess_sentence(s):
  8. lines=s.split("##SENT##")
  9. for i in lines:
  10. i=normalizeString(i)
  11. return lines

ROUGE评估

对应的python里有这个包,最简单的就是pip install rouge之后快乐的使用
使用方法如下
支持传入列表和字符串
输出结果是f1和recall 和precision

  1. rouge = Rouge()
  2. rouge_score = rouge.get_scores(a, b)
  3. print(rouge_score[0]["rouge-1"])
  4. print(rouge_score[0]["rouge-2"])
  5. print(rouge_score[0]["rouge-l"])

对应的自己实现,这里参考了网上的实现版本,进行了一定的适配修改
RougeL似乎要求LCS感觉很麻烦。。。算了不弄
对应的求值等于上面的Recall项的值

  1. class MyRouge:
  2. def rouge1_score(self,sentence,label):
  3. line1 = sentence.split(" ")
  4. line2 = label.split(" ")
  5. ngram_all = len(line2)
  6. temp=0
  7. for x in line2:
  8. if x in line1: temp = temp + 1
  9. rouge_1 = temp / ngram_all
  10. return rouge_1
  11. def rouge2_score(self,sentence,label):
  12. gram_2_model = []
  13. gram_2_reference = []
  14. temp = 0
  15. sentence = sentence.split(" ")
  16. label = label.split(" ")
  17. ngram_all = len(label) - 1
  18. for x in range(len(sentence) - 1):
  19. gram_2_model.append(sentence[x] + sentence[x + 1])
  20. for x in range(len(label) - 1):
  21. gram_2_reference.append(label[x] + label[x + 1])
  22. for x in gram_2_model:
  23. if x in gram_2_reference: temp = temp + 1
  24. rouge_2 = temp / ngram_all
  25. return rouge_2

结果

TIM图片20200706203927.png

其他

感觉结果并不理想

  1. 一个是我感觉实现上有问题
  2. 另一个是我对数据的清洗可能有点问题,这里只是做了基本的去点空格和换行,但是观察到原文里有大量的固定格式的一些句子成分,感觉和数据本身的特性有关,而我没有对应的进行处理
  3. 第三点就是在Lead算法里我只是用切割符切割,然后用空格链接多个句子作为摘要,可能这么粗糙的拼接,对结果有所影响