0


【NLP_事件抽取】基于条件随机场模型

数据预处理

  1. #!/usr/bin/env python
  2. # coding=utf-8
  3. from string import punctuation
  4. import re
  5. import codecs
  6. # 英文标点符号+中文标点符号
  7. # 未去除空格、换行符等(正则表达式以"/s"表示)
  8. punc = punctuation + u'.,;《》?!“”‘’@#¥%…&×()——+【】{};;●,。&~、|::'
  9. fr = codecs.open("……\YWP_EventExtraction_CRF_Elephant\\01 Preprocessing\\20220107 test data.txt", "r", encoding='utf-8')
  10. fw = codecs.open("……\YWP_EventExtraction_CRF_Elephant\\01 Preprocessing\\20220107 test data_result.txt", "w", encoding='utf-8')
  11. # 利用正则表达式替换为一个空格
  12. for line in fr:
  13. line = re.sub(r"[{}]+".format(punc)," ",line)
  14. fw.write(line+' ')
  15. fr.close()
  16. fw.close()


预处理后的数据示例

LTP:分词+词性标注+命名实体识别

  • 输入:即形如上述的预处理后的数据
  1. # -*- coding: utf-8 -*-
  2. import os
  3. from pyltp import NamedEntityRecognizer
  4. from pyltp import Postagger
  5. from pyltp import Segmentor
  6. LTP_DATA_DIR = 'D:/Software/Anaconda3/ltp_data_v3.4.0/' # ltp模型目录的路径,根据实际情况修改
  7. cws_model_path = os.path.join(LTP_DATA_DIR,
  8. 'cws.model') # 分词模型路径,模型名称为`cws.model`
  9. pos_model_path = os.path.join(LTP_DATA_DIR,
  10. 'pos.model') # 词性标注模型路径,模型名称为`pos.model`
  11. ner_model_path = os.path.join(LTP_DATA_DIR,
  12. 'ner.model') # 命名实体识别模型路径,模型名称为`ner.model`
  13. with open("D:\Desktop\北移象群\代码\YWP_EventExtraction_CRF_Elephant\\01 Preprocessing\\20220107 test data_result.txt", "r", encoding='utf-8') as f1:
  14. content = f1.read()
  15. # 分词
  16. segmentor = Segmentor() # 初始化分词实例
  17. segmentor.load_with_lexicon(cws_model_path, 'dict') # 加载分词模型,以及自定义词典
  18. seg_list = segmentor.segment(content) # 分词
  19. seg_list = list(seg_list) # 返回值并不是list类型,因此需要转换为list
  20. # LTP不能很好地处理回车,因此需要去除回车给分词带来的干扰。
  21. # LTP也不能很好地处理数字,可能把一串数字分成好几个单词,因此需要连接可能拆开的数字
  22. i = 0
  23. while i < len(seg_list):
  24. # 如果单词里包含回车,则需要分三种情况处理
  25. if '\n' in seg_list[i] and len(seg_list[i]) > 1:
  26. idx = seg_list[i].find('\n')
  27. # 回车在单词的开头,如\n被告人
  28. if idx == 0:
  29. remains = seg_list[i][1:]
  30. seg_list[i] = '\n'
  31. seg_list.insert(i + 1, remains)
  32. # 回车在单词末尾,如被告人\n
  33. elif idx == len(seg_list[i]) - 1:
  34. remains = seg_list[i][:-1]
  35. seg_list[i] = remains
  36. seg_list.insert(i + 1, '\n')
  37. # 回车在单词中间,如被告人\n张某某
  38. else:
  39. remains1 = seg_list[i].split('\n')[0]
  40. remains2 = seg_list[i].split('\n')[-1]
  41. seg_list[i] = remains1
  42. seg_list.insert(i + 1, '\n')
  43. seg_list.insert(i + 2, remains2)
  44. # 将拆开的数字连接起来
  45. if seg_list[i].isdigit() and seg_list[i + 1].isdigit():
  46. seg_list[i] = seg_list[i] + seg_list[i + 1]
  47. del seg_list[i + 1]
  48. i += 1
  49. # 词性标注
  50. postagger = Postagger() # 初始化词性标注实例
  51. postagger.load(pos_model_path) # 加载模型
  52. postags = postagger.postag(seg_list) # 词性标注
  53. # 命名实体识别
  54. recognizer = NamedEntityRecognizer() # 初始化命名实体识别实例
  55. recognizer.load(ner_model_path) # 加载模型
  56. netags = recognizer.recognize(seg_list, postags) # 命名实体识别
  57. # 写入结果
  58. f2 = open("……\YWP_EventExtraction_CRF_Elephant\\02 LTP (Seg, POS, NER)\\05LTP分词\\20220107 test data_LTPresult.txt", "w", encoding='utf-8')
  59. for word, postag, netag in zip(seg_list, postags, netags):
  60. if word == '\n':
  61. f2.write('\n')
  62. else:
  63. f2.write(word + " " + postag + " " + netag + "\n")
  64. f2.close()
  65. # 释放模型
  66. segmentor.release()
  67. postagger.release()
  68. recognizer.release()
  • 输出


LTP处理后的数据示例

CRF++:事件抽取

标注数据

  • train.datatest.data数据格式


train.data示例(BMES标注)

训练模型


命令行下训练CRF模型


命令行下测试CRF模型

  • 输出output.txt→用于后续事件抽取模型评估


output数据格式

展示事件元素

  • (输入:分词+词性+命名实体+事件标注)
  1. #!/usr/bin/env python
  2. # _*_coding:utf-8 _*_
  3. # @Author:Zhang Shiwei + YWP
  4. # @Date :2019-07-21 + 2021-01-07
  5. #通过CRF++模型,得到事件标注(类似命名实体识别)后,运行该程序
  6. # 去除列表中重复元素,同时保持相对顺序不变
  7. def remove_duplicate_elements(l):
  8. new_list = []
  9. for i in l:
  10. if i not in new_list:
  11. new_list.append(i)
  12. return new_list
  13. # 将属于同一事件要素的词语合并
  14. def func(file_name):
  15. words = []
  16. element_type = []
  17. with open(file_name, "r", encoding='utf-8') as f1:
  18. contents = f1.readlines()
  19. new_contents = []
  20. # 将文本转换成list,方便后续处理
  21. for content in contents:
  22. new_contents.append(content.strip("\n").split(" "))
  23. for index, content in enumerate(new_contents):
  24. if "S" in content[-1]:
  25. # 处理由一个单词组成的事件要素
  26. words.append(content[0])
  27. element_type.append(content[-1])
  28. elif "B" in content[-1]:
  29. # 处理由多个单词组成的事件要素
  30. words.append(content[0])
  31. element_type.append(content[-1])
  32. j = index + 1
  33. while "I" in new_contents[j][-1] or "E" in new_contents[j][-1]:
  34. words[-1] = words[-1] + new_contents[j][0]
  35. j += 1
  36. if j == len(new_contents):
  37. break
  38. T = []
  39. K = []
  40. D = []
  41. P = []
  42. N = []
  43. R = []
  44. for i in range(len(element_type)):
  45. if element_type[i][-1] == "T":
  46. T.append(words[i])
  47. elif element_type[i][-1] == "K":
  48. K.append(words[i])
  49. elif element_type[i][-1] == "D":
  50. D.append(words[i])
  51. elif element_type[i][-1] == "P":
  52. P.append(words[i])
  53. elif element_type[i][-1] == "N":
  54. N.append(words[i])
  55. elif element_type[i][-1] == "R":
  56. R.append(words[i])
  57. # 整理抽取结果
  58. result = dict()
  59. result["时间"] = remove_duplicate_elements(T)
  60. result["头数"] = remove_duplicate_elements(K)
  61. result["名称"] = remove_duplicate_elements(D)
  62. result["地点"] = remove_duplicate_elements(P)
  63. result["肇事"] = remove_duplicate_elements(N)
  64. result["原因"] = remove_duplicate_elements(R)
  65. # 打印出完整的事件要素
  66. for key, value in result.items():
  67. print(key, value)
  68. return result
  69. func("…….txt")
  70. #func("…….txt")

精度评价

  • 输入数据五列,分别为:分词 + 词性 + 命名实体识别 + 事件元素真实标注 + 事件元素实际标注,后两列用于评价事件抽取模型的精度
  1. # -*- coding:utf-8 -*-
  2. # @Author:Zhang Shiwei + YWP
  3. # @Date :2019-06-10 + 2021-01-07
  4. with open("…….txt", "r", encoding="utf-8") as f1:
  5. contents = f1.read().splitlines()
  6. count = 0
  7. real_count = 0
  8. tp = 0
  9. fp = 0
  10. fn = 0
  11. tn = 0
  12. for i in range(len(contents)):
  13. if len(contents[i]) > 1:
  14. real_count += 1
  15. if contents[i].split(" ")[-2] != "O":
  16. if contents[i].split(" ")[-1] == contents[i].split(" ")[-2]:
  17. tp += 1
  18. else:
  19. fn += 1
  20. else:
  21. if contents[i].split(" ")[-1] != "O":
  22. fp += 1
  23. else:
  24. tn += 1
  25. P = tp / (tp + fp)
  26. R = tp / (tp + fn)
  27. F1 = 2 * P * R / (P + R)
  28. print("P=" + str(P))
  29. print("R=" + str(R))
  30. print("F1=" + str(F1))

重要参考

https://github.com/zhang17173/Event-Extractionhttps://github.com/zhang17173/Event-Extraction


本文转载自: https://blog.csdn.net/YWP_2016/article/details/122364463
版权归原作者 YWP_2016 所有, 如有侵权,请联系我们删除。

“【NLP_事件抽取】基于条件随机场模型”的评论:

还没有评论