GaoPeng97 / naive-event-extraction-from-email

extract key info from chinese email by using CRF and HMM

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

naive-event-extraction-from-email

程序使用说明

运行环境

Windows
python3.6

打开程序先进入到CRF的文件夹下:

cd CRF

然后可以运行如下命令:

python execute.py train  //就能训练模型,默认CRF若想使用HMM,手动加入即可

python execute.py process  //预处理语料库

python execute.py recognize email_data label_data  //处理所有的邮件 得到的结果会存在                                                      //result目录下

python execute.py test_calendar   //测试日历模块,运行前请预先删除token.json

如果想测试HMM模型

cd HMM_Model  //进入到HMM模型文件下
python  HmmNERTagger.py [要测试的句子] 

若想更改训练的语料库和标签,找到该目录, 更改对应的代码即可

/CRF/corpus.py

一、分析与实验

思路概要:通过调研发现我们想做的效果很接近NLP中一个经典的问题NER,NER又称命名实体识别,通常指的是从文本中提取哪些具有特别意义或者指代性非常强的实体,包括人名,地名,机构名,时间,专有名词等。而人名,地名,和时间恰恰也是我们想提取出来的。所以我们的首要目标应当是搭建一个NER系统。但是我们的目标却不只是提取这些地名,人名,我们还需要文本中提炼出事件,者是一个很棘手的事情,因为事件是一个很宽泛的概念,它可以是名词,动词,动名词等等。我们最后采用的是用TFIDF方法,从文本中得到重要性最高的名词,动词集合,从中得到事件的表达。

我们的实验步骤主要分为以下内容:

  • 邮件的预处理
  • NER系统
    • 语料库及语料库的预处理
    • HMM模型
    • CRF模型
    • NER系统结果
  • TFIDF方法提取事件
  • 提取内容导入日历

接下来也会根据以上思路展开叙述。

1、邮件的预处理

邮件的基本格式一般如下,我们再做实验时,也默认所有邮件都是如下格式:

尊敬的张三老师:
    请您明天下午5点,到301教室开会。
                               李四

很容易想到的是直接将第一句,最后一句作为人名直接提取,虽然这样比较直接,但是我们并不是这种思路,我们是先用jieba分词将敬语和名字前的形容词去除,得到sender和receiver。然后将句子内容中的您、你、我、我们这种代词直接用receiver和sender替换。

代词 替换词
我 sender
你 receiver
您 receiver
我们 sender and receiver

对于时间,我们先读取当地时间MM/DD,然后将文本中的今天、明天对应替换。

时间词 替换词
今天 MM/DD
明天 MM/DD + 1

所以例子中得到的预处理结果就会是:(假如今天是1月1日)

请张三1月2日下午5点,到301教室开会。

然后将该结果继续进行下一步操作。

2、NER系统

方法选择:

对于NER任务通常使用的有基于规则和基于模型的方法。

基于规则:

基于规则适合实体本身有很多特征的情况,方法简单且有效。比如抽取物品价格时,基本都是“数字+元”的形式,直接找到 ’ 元 ‘ 将前面的数字提取即可。但随着场景和表达越来越复杂,当出现 “一千万”, “两块五”这种形式时,就不太实用了。所以基于规则的方式主要是适用于比较规范和结构化的文本抽取任务。

基于模型:

如果从模型的角度,NER问题实际上就是序列标注的问题。序列标注问题是指模型的输入是一个序列,对于输入序列的每一个单元,输出特定的标签,如BIO模型,B表示这个字是词的开始,I是词的其他部分,o表示非该词的部分。数列标注问题可以涵盖NLP中很多任务,包括语音识别,中文分词,命名实体识别等。常用的模型有HMM,CRF。

综合考虑,基于规则的方法约束太大,用基于模型的方法更适合我们的题目。

语料库的处理:

基于调研我们最终选择了人民日报1998年语料库,该语料库是对纯文本预料进行了词语切分和词性标注而成的,文章中每个词语都带有词性标记,包括名词n、时间词t、处所词s、量词q、等40多种词性,其中还包括方便语料库应用的专有名词人名nr、地名ns、机构名nt。考虑到我们的目标是将文本中的地名,人名,机构名和时间处理,所以有很多标注对我们而言是无用的,所以我们需要先对语料库进行预处理:

1、姓名的合并:

原有语料库中,姓和名是分开标注的,我们需要将姓名和并如

周/nr 恩来/nr   ----->    周恩来/nr

2、时间类合并

原有语料库中的时间也是分开标注的,也需要合并:

1998年/t 12月/t 1/日   ------------> 1998年12月1日

3、中括号括起来的词表示大粒度分词,表达能力更强,需要合并

 [**/ns  政府/n]nt   ------------>  **政府/nt

4、全角字符统一转为半角字符

全角占两个字节,半角站一个字节,主要是要对数字和标点符号进行处理,转成半角。

部分语料库处理前后对比:

模型的训练

我们综合考虑了两种模型,并分别进行测试。

CRF模型

用CRF模型进行训练时,我们主要用了有以下几个步骤:

  • 确定标签体系
  • 确定特征模板
  • 模型训练

1、确定标签体系:

从下图中可以很容易看出不同标签体系的区别。(注:图片来源于网络)

显而易见的是,标签体系越复杂准确度也越高,相应的训练时间也会增加。我们最后选择的是BIO标签体系。

2、特征模板的设计

特征模板的我们也选用了简单的方式就是选用了该词的前一个,后一个,前一个和当前的和,当前和后一个的和:

3、模型训练

我们使用的是sklearn-crfsuite工具包,参考官方文档得到训练方法如下

训练结果有:

HMM模型:

主要步骤如下

  • 输入要测试的单句,调用hmm_nerf_tagger函数
  • 根据语料库文件,实例化HMM类。对语料库进行处理,调用HMM类中get_word_tag:读取语料库中的字和相应标签; get_tag_tag:读取标签的转移状态;get_words(self):读取语料库中的所有字,写成文件
  • 若所需要的参数文件(emiss_prob.txt,trans_prob.txt,start_prob.txt)未存在于当前路径中,则调用get_start_prob,统计初始概率get_transition_probability,统计转移概率,get_emission_probability,统计发射概率,得到相应的参数文件
  • 若所需要的参数文件存在,则调用get_observation方法将句子处理成列表,并用load_start_pro,load_emission_pro,load_transition_pro读取初始概率,发射概率和转移概率。

流程图如下:

得到训练结果如下:

综合分析,HMM是一种生成模型,对部分实体识别率不高,如机构名,识别甚至不到50%,整体要比判别模型CRF低。所以我们最后采用了CRF模型来进行NER识别。

3、TFIDF提取事件

正如上文中所提到的,事件无法给出一个精确的定义,但是就我们日常生活中来看,大部分具有真实含义的动名词或者是名词都能准确的表达邮件的主旨,比如在学校日常的“考试”、“开会”、“上课”、“打篮球”等都是简单的名词或者动名词,所以我们认为再学校这个大的背景前提下,动词或者名词就能够描述事件了,但是一般一封邮件中名词,动词、动名词都不止一个,如何判断那个才是我们需要的?我们就采用了TFIDF方法来判断词的重要性,然后去除重要性排列中所有已经NER出来的关键词,然后再选取重要性最高的候选词描述事件。

TFIDF原理

TFIDF(Term Frequency-Inverse Document Frequency)即词频-逆文件频率,是一种统计方法,用于统计一个字对于一个文件集或一个语料库中的其中一份文件的重要程度,字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的文档数反比下降。总结来说就是,当一个词对于一个文档越重要,他在该文档中出现的次数就应该越多,在其他文档中出现的次数就应该越少。

  • TF: (Frequency Term,TF) 指的是某一个给定的词语在该文件中出现的次数。需要归一化,因为越长的文档越容易出现更多次数的关键词。公式为 TF = \frac {某词条在文档中的出现次数} {该文档中所有词条数目}
  • IDF: (Inverse document frequency, IDF)指的是,如果包含该词条的文档越少,IDF越大,说明词条具有很好的类别区分能力。所以某个特定词的IDF可以有总文件数目初一包含该词语的文件数目,再将商取对数得到: IDF = log(\frac {文档总数} {包含词条的文档数 + 1})
  • TF-IDF: TFIDF = TF*IDF

根据上述原理,我们直接使用了别人已有的idf.txt,统计了每个词的逆文件频率,然后根据我们的情形适当加大“打球”,“开会”,“考试”这种和学校相关性比较大的词的数值,提升效果。然后处理我们的文本,把一些 ‘的’ ‘了’ 之类的无意义的停用词去除,再把已经用NER打上标签的词去除,将余下的词用TFIDF方法排序即可以得到表达事件的词。

4、导入日历

主要是参考了google日历的api(https://developers.google.com/calendar),编写我们插入事件的模块,注意在使用的时候,一定要将credential.json拷贝进文件夹,然后删除token.json重新运行会得到适配自己账户的token.json。

二、执行

程序架构

D:.
│  list.txt        //文件结构图
│  result.PNG      //CRF模型训练结果图
│  test.py  
│  train.data
│  
├─.idea            //运行时产生的文件
│  │  encodings.xml
│  │  misc.xml
│  │  modules.xml
│  │  project.iml
│  │  vcs.xml
│  │  workspace.xml
│  │  
│  ├─inspectionProfiles
│  └─libraries
│          R_User_Library.xml
│          
├─CRF                      //CRF模型
│  │  calendar_api.py      //插入日历模块
│  │  corpus.py            //语料处理模块
│  │  credentials.json     //google日历运行必需
│  │  execute.py           //总的执行文件
│  │  model.py             //CRF模型
│  │  preprocess_email.py  //邮件预处理
│  │  token.json           //google日历账户信息
│  │  
│  ├─data
│  │      model.pkl        //保存的训练好的模型
│  │      rmrb.txt         //预处理后的语料库
│  │      rmrb199801.txt   //1998人民日报语料库
│  │      test.txt         //测试所用
│  │      
│  ├─email_data
│  │      xx.txt           //100封邮件
│  │      
│  ├─label_data
│  │     xx_label.txt      //100封邮件的label
│  │      
│  ├─result
│  │      email_result.txt //存储100封邮件的提取结果
│  │      
│  └─__pycache__
│          calendar_api.cpython-36.pyc
│          corpus.cpython-36.pyc
│          model.cpython-36.pyc
│          preprocess_email.cpython-36.pyc
│          
│          
├─HMM_model
│  │  emiss_prob.txt     //发射概率矩阵
│  │  HmmNERTagger.py    //读入测试语句,进行实体识别
│  │  HMM_Model.py       //定义HMM类与方法
│  │  start_prob.txt     //初始概率矩阵
│  │  trans_prob.txt     //转移概率矩阵
│  │  word.txt           //词库中所有字的词典
│  │  
│  └─__pycache__
│          HMM_Model.cpython-36.pyc
│          
├─tfidf
│  │  idf.txt            //idf数据
│  │  tfidf.py           //TFIDF模块
│  │  __init__.py
│  │  
│  └─__pycache__
│          tfidf.cpython-36.pyc
│          __init__.cpython-36.pyc
│          
└─__pycache__

三、实验结果与分析

我们建了100条邮件的数据,然后为这100条数据标记label, 经过运行识别后的结果放在了email_result.txt中, 经统计得到准确率 如下,

注1: organization 一般在邮件中出现较少,而且我们样本中也大部分为空,所以该项不予统计

注2:对于人名, 有时识别不全或者人名后面多了一些额外的字,这种不影响对人名的理解 给0.5分

类别 准确率
地点(LOC) 78/100 时间(T) 94/100 人(PER) 72/100 事件(EVENT) 68/100

部分结果截图

分析:

  • 地点识别 对地点的识别, 对于南京,北京,广州这种城市名识别没有问题. 对于'学校门口', '黄山路旁'这种只能识别出'学校' '黄山路' 这些关键词,附加的细节无法识别出来.另外对于书店,食堂, 图书馆,教室的识别不是特别稳定, 跟idf.txt里面对应条目的权重有关, 需要手动更改一下,提高识别准确率.
  • 时间识别: 效果很好,基本没什么问题, 但注意的是,我们label建立的日期,与测试的日期不同,所以对于邮件里有"今天" "明天" 这种给出的时间可能有所差别,但结果是正确的.
  • 人名识别: 人名识别上, 对于正常的名字,识别准确率还是可以的,但是有些名字比如"小红 " "小明"这种无法识别, 或者"王**" "李解放" 这种 姓+词语 的往往效果也不太好.
  • 事件识别 因为人有总结能力, 而我们的程序只能从句子里面抽取表示事件的关键词, 所以有时候会出现下面的情况, 只能给出部分关键的词语,而不能概括出事件.

About

extract key info from chinese email by using CRF and HMM


Languages

Language:Python 100.0%