├── .idea ├── ComplexEventExtraction.iml ├── markdown-navigator.xml ├── markdown-navigator │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── vcs.xml └── workspace.xml ├── README.md ├── complex_sentence.py └── img ├── but.png └── condition.png /.idea/ComplexEventExtraction.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 36 | 37 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /.idea/markdown-navigator/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 60 | 61 | 67 | 68 | 69 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 105 | 106 | 107 | 108 | 111 | 112 | 115 | 116 | 117 | 118 | 121 | 122 | 125 | 126 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 158 | 159 | 175 | 176 | 187 | 188 | 206 | 207 | 225 | 226 | 246 | 247 | 268 | 269 | 292 | 293 | 294 | 296 | 297 | 298 | 299 | 1538399164923 300 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 333 | 336 | 337 | 338 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ComplexEventExtraction 2 | chinese compound event extraction,中文复合事件抽取,包括条件事件、因果事件、顺承事件、反转事件等事件抽取,并形成事理图谱。 3 | 4 | # 关于项目 5 | 目前,知识图谱在学术界如火如荼地进行,但受限于知识图谱各个环节中的性能问题,还尚未能够在工业界大规模运用。而与知识图谱中以实体为知识节点,实体关系为实体关系边对知识进行组织不同,事件图谱,又称事理图谱,在目前也是很火的一个研究方向。就事理图谱而言,其从技术实现难度上不亚于知识图谱。 6 | 本人目前在事件图谱上的实验工作有: 7 | 顺承事件图谱(https://github.com/liuhuanyong/SequentialEventExtration) 8 | 因果事件图谱(https://github.com/liuhuanyong/CausalityEventExtraction) 9 | 目前,想到其实中文的事件在显式上的表达上遵循的是中文的复句表现形式。因此,打算对阶段性的工作进行整理,进一步形成中文复合事件抽取项目(https://github.com/liuhuanyong/ComplexEventExtraction) 10 | 接下来,将自己对事理图谱工作的一些理解整理出来,对事件图谱的类型、事件表示的相关方面进行归纳 。 11 | 12 | # 1、事件图谱(事理图谱)的类型 13 | | 事件 | 含义 | 形式化 | 事件应用 | 图谱场景 | 举例 | 14 | | --- | --- | --- | --- | --- | --- | 15 | | 因果事件 | 某一事件导致某一事件发生 | A导致B | 事件预警 |因果溯源 由因求果 | <地震,房屋倒塌> | 16 | | 条件事件 | 某事件条件下另一事件发生 | 如果A那么B |事件预警 |时机判定 | <限制放宽,立即增产> | 17 | | 反转事件 | 某事件与另一事件形成对立 | 虽然A但是B |预防不测 |反面教材 | <起步晚,发展快>| 18 | | 顺承事件 | 某事件紧接着另一事件发生 | A接着B |事件演化 |未来意图识别 | <去旅游,买火车票> | 19 | 20 | # 2、事件的表示 21 | 以因果事件为例: 22 | 已知句子:这几天非洲闹猪瘟,导致国内猪肉涨价 23 | 24 | |表示形式 | 含义 | 举例 | 优点 | 缺点 | 25 | | --- | --- | --- | --- | --- | 26 | | 短句 | 以中文标点符号为分割边界形成的短句 | 这几天非洲闹猪瘟&国内猪肉涨价 | 方便、最原始信息 |噪声多,不易融合| 27 | | 词序列 | 对短句进行分词、词性标注、停用词形成的词序列 | 非洲闹猪瘟&国内猪肉涨价 |语义丰富、较短句形式短 |停用规则不易控制 | 28 | | 短语 | 依存句法分析/语义角色标注,形成主谓短语、动宾短语、主谓宾短语 | 非洲闹猪瘟&猪肉涨价 |语义凝固简洁 |受限于依存、语义角色性能 | 29 | 30 | # 关于项目结构 31 | 本项目列举了汉语句子表顺承、条件、并列、转折的关联词,详见complex_sentence.py,例如: 32 | 33 | '''转折事件''' 34 | def pattern_but(self): 35 | wds = [[['与其'], ['不如'],'but'], 36 | [['虽然','尽管','虽'],['但也','但还','但却','但'],'but'], 37 | [['虽然','尽管','虽'],[ '但','但是也','但是还','但是却',],'but'], 38 | [['不是'],['而是'],'but'], 39 | [['即使','就算是'],['也','还'],'but'], 40 | [['即便'],['也','还'],'but'], 41 | [['虽然','即使'],['但是','可是','然而','仍然','还是','也', '但'],'but'], 42 | [['虽然','尽管','固然'],['也','还','却'],'but'], 43 | [['与其','宁可'],['决不','也不','也要'],'but'], 44 | [['与其','宁肯'],['决不','也要','也不'],'but'], 45 | [['与其','宁愿'],['也不','决不','也要'],'but'], 46 | [['虽然','尽管','固然'],['也','还','却'],'but'], 47 | [['不管','不论','无论','即使'],['都', '也', '总', '始终', '一直'],'but'], 48 | [['虽'],['可是','倒','但','可','却','还是','但是'],'but'], 49 | [['虽然','纵然','即使'],['倒','还是','但是','但','可是','可','却'],'but'], 50 | [['虽说'],['还是','但','但是','可是','可','却'],'but'], 51 | [['无论'],['都','也','还','仍然','总','始终','一直'],'but'], 52 | [['与其'],['宁可','不如','宁肯','宁愿'],'but']] 53 | 54 | # 实验结果 55 | 本项目基于1000W资讯进行实验,共得到古复合中文事件模式237条,top10的模式结果为: 56 | 57 | 模式 频次 58 | but_虽然_但 1484690 59 | but_尽管_但 1006669 60 | condition_如果_就 763451 61 | more_或_或 716354 62 | more_也_还 675549 63 | condition_如果_那么 494417 64 | more_不仅_也 483610 65 | condition_只有_才 432495 66 | more_不仅_还 429681 67 | condition_无论_都 399225 68 | 69 | # 应用结果 70 | 71 | # 1、事件举例 72 | 73 | | 事件类型 | 事件1 | 事件2 | 74 | | --- | --- | --- | 75 | | 反转事件 | 不是 太多 而是 太少 |虽然 小幅提涨 但是 成交不多| 76 | | 反转事件 | 不是 在消费 而是 在社交 | 虽然 幅度不算大 但是 形态收好 | 77 | | 反转事件 | 不是 多了 而是 少了| 虽然 缓慢 但是 步伐坚定 | 78 | | 反转事件 | 不是 目的 而是 手段 | 虽然 觉得有点坑 但是 毫无办法| 79 | | 反转事件 | 不是 太多 而是 太少 | 虽然 速缓 但是 质更优 | 80 | | 反转事件 | 不是 封闭的 而是 开放包容的 | 虽然 起步稍晚 但是 热度不减 | 81 | | 反转事件 | 不是 一个结果 而是 一种逻辑 | 虽然 压力比较大 但是 努力过 | 82 | | 反转事件 | 不是 周期性的 而是 结构性的 | 虽然 没有功劳 但是 我也有苦劳 | 83 | | 条件事件 | 一旦 时机成熟 就 坚决推行 |如果 数据疲软 那么 将打压瑞郎 | 84 | | 条件事件 | 一旦 触发 就 不可逆了 | 如果 美元涨 那么 黄金应该跌 | 85 | | 条件事件 | 一旦 形成 就 很难改变 | 如果 惯性下跌 那么 请及时平仓 | 86 | | 条件事件 | 一旦 产生恐慌 就 会手忙脚乱 |如果 英国退欧 那么 金价将上涨 | 87 | | 条件事件 | 一旦 制定了目标 就 必须完成 |如果 比值上升 那么 进口将盈利 | 88 | | 条件事件 | 一旦 停产 就 失去了份额 | 如果 是趋势 那么 就顺势操作 | 89 | | 条件事件 | 一旦 超调贬值 就 会失控 | 如果 看跌 那么 赶紧跑 | 90 | 91 | # 2、图谱展示 92 | 93 | 1、反转事件图谱 94 | ![image](https://github.com/liuhuanyong/ComplexEventExtraction/blob/master/img/but.png) 95 | 96 | 2、条件事件图谱 97 | ![image](https://github.com/liuhuanyong/ComplexEventExtraction/blob/master/img/condition.png) 98 | 99 | 100 | # 总结 101 | 1、本项目对事件图谱的类型、表现形式进行了归纳,并结合复合事件模式与语料进行了实验。 102 | 2、实验表明,反转事件,其实在某种程度上可以用来构造反义词词典,例如"不是A而是B"这种模式,可以得到很多反义的词或短语,这让我想到了我的一个反义词项目接口:(https://github.com/liuhuanyong/ChineseAntiword) ,我们可以用wordvector找相近词,可以靠这种方式收集反义词,对了,还可以加上情绪。 103 | 3、实验表明,汉语显示标记其实在中文文本当中还是用的很普遍的,我统计了以下,跑了1000W文本,有超过半数的文本中包含以上模式。因此,如果能够把显示事件图谱做好,感觉用处还是很多的。 104 | 4、本项目还有很多不足,比如模式上,比如对事件类型和事件表示的看法上,欢迎补充。 105 | 5、If any question about the project or me ,see https://liuhuanyong.github.io/ 106 | 107 | 如有自然语言处理、知识图谱、事理图谱、社会计算、语言资源建设等问题或合作,可联系我: 108 | 1、我的github项目介绍:https://liuhuanyong.github.io 109 | 2、我的csdn博客:https://blog.csdn.net/lhy2014 110 | 3、about me:刘焕勇,中国科学院软件研究所,lhy_in_blcu@126.com 111 | -------------------------------------------------------------------------------- /complex_sentence.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | # File: Complex_sentence.py 4 | # Author: lhy 5 | # Date: 18-9-4 6 | 7 | import re 8 | import pymongo 9 | import urllib.parse 10 | 11 | 12 | '''中文复句整理及模板''' 13 | class EventsExtraction: 14 | def __init__(self): 15 | self.but_wds = self.pattern_but() 16 | self.seq_wds = self.pattern_seq() 17 | self.condition_wds = self.pattern_condition() 18 | self.more_wds = self.pattern_more() 19 | 20 | self.but_patterns = self.create_pattern(self.but_wds) 21 | self.seq_patterns = self.create_pattern(self.seq_wds) 22 | self.condition_patterns = self.create_pattern(self.condition_wds) 23 | self.more_patterns = self.create_pattern(self.more_wds) 24 | '''转折事件''' 25 | def pattern_but(self): 26 | wds = [[['与其'], ['不如'],'but'], 27 | [['虽然','尽管','虽'],['但也','但还','但却','但'],'but'], 28 | [['虽然','尽管','虽'],[ '但','但是也','但是还','但是却',],'but'], 29 | [['不是'],['而是'],'but'], 30 | [['即使','就算是'],['也','还'],'but'], 31 | [['即便'],['也','还'],'but'], 32 | [['虽然','即使'],['但是','可是','然而','仍然','还是','也', '但'],'but'], 33 | [['虽然','尽管','固然'],['也','还','却'],'but'], 34 | [['与其','宁可'],['决不','也不','也要'],'but'], 35 | [['与其','宁肯'],['决不','也要','也不'],'but'], 36 | [['与其','宁愿'],['也不','决不','也要'],'but'], 37 | [['虽然','尽管','固然'],['也','还','却'],'but'], 38 | [['不管','不论','无论','即使'],['都', '也', '总', '始终', '一直'],'but'], 39 | [['虽'],['可是','倒','但','可','却','还是','但是'],'but'], 40 | [['虽然','纵然','即使'],['倒','还是','但是','但','可是','可','却'],'but'], 41 | [['虽说'],['还是','但','但是','可是','可','却'],'but'], 42 | [['无论'],['都','也','还','仍然','总','始终','一直'],'but'], 43 | [['与其'],['宁可','不如','宁肯','宁愿'],'but']] 44 | 45 | return wds 46 | '''顺承事件''' 47 | def pattern_seq(self): 48 | wds =[ 49 | [['又', '再', '才', '并'], ['进而'], 'sequence'], 50 | [['首先', '第一'], ['其次', '然后'], 'sequence'], 51 | [['首先', '先是'], ['再', '又', '还', '才'], 'sequence'], 52 | [['一方面'], ['另一方面', '又', '也', '还'], 'sequence']] 53 | 54 | return wds 55 | 56 | '''并列事件''' 57 | def pattern_more(self): 58 | wds = [ 59 | [['不但', '不仅'], ['并且'], 'more'], 60 | [['不单'], ['而且', '并且', '也', '还'], 'more'], 61 | [['不但'], ['而且', '并且', '也', '还'], 'more'], 62 | [['不管'], ['都', '也', '总', '始终', '一直'], 'more'], 63 | [['不光'], ['而且', '并且', '也', '还'], 'more'], 64 | [['虽然', '尽管'], ['不过'], 'more'], 65 | [['不仅'], ['还', '而且', '并且', '也'], 'more'], 66 | [['不论'], ['还是', '也', '总', '都', '始终', '一直'], 'more'], 67 | [['不只'], ['而且', '也', '并且', '还'], 'more'], 68 | [['不但', '不仅', '不光', '不只'], ['而且'], 'more'], 69 | [['尚且', '都', '也', '又', '更'], ['还', '又'], 'more'], 70 | [['既然', '既',], ['就', '便', '那', '那么', '也', '还'], 'more'], 71 | [['无论', '不管', '不论', '或'], ['或'], 'choice'], 72 | [['或是'], ['或是'], 'choice'], 73 | [['或者', '无论', '不管', '不论'], ['或者'], 'choice'], 74 | [['不是'], ['也'], 'choice'], 75 | [['要么', '或者'], ['要么', '或者'], 'choice'], 76 | ] 77 | return wds 78 | 79 | '''条件事件''' 80 | def pattern_condition(self): 81 | wds = [ 82 | [['除非'], ['否则', '才', '不然', '要不'], 'condition'], 83 | [['除非'], ['否则的话'], 'condition'], 84 | [['还是', '无论', '不管'], ['还是', '都', '总'], 'condition'], 85 | [['既然'], ['又', '且', '也', '亦'], 'condition'], 86 | [['假如'], ['那么', '就', '也', '还'], 'condition'], 87 | [['假若', '如果'], ['那么', '就', '那', '则', '便'], 'condition'], 88 | [['假使', '如果'], ['那么', '就', '那', '则', '便'], 'condition'], 89 | [['尽管', '如果'], ['那么', '就', '那', '则', '便'], 'condition'], 90 | [['即使', '就是'], ['也', '还是'], 'condition'], 91 | [['如果', '既然'], ['那么'], 'condition'], 92 | [['如', '假设'], ['则', '那么', '就', '那'], 'condition'], 93 | [['如果', '假设'], ['那么', '则', '就', '那'], 'condition'], 94 | [['万一'], ['那么', '就'], 'condition'], 95 | [['要是', '如果'], ['就', '那'], 'condition'], 96 | [['要是', '如果', '假如'], ['那么', '就', '那', '的话'], 'condition'], 97 | [['一旦'], ['就'], 'condition'], 98 | [['既然', '假如', '既', '如果'], ['则','就'], 'condition'], 99 | [['只要'], ['就', '便', '都', '总'], 'condition'], 100 | [['只有'], ['才', '还'], 'condition'], 101 | 102 | ] 103 | return wds 104 | 105 | '''编译模式''' 106 | def create_pattern(self, wds): 107 | patterns = [] 108 | for wd in wds: 109 | pre = wd[0] 110 | pos = wd[1] 111 | pattern = re.compile(r'({0})(.*)({1})([^??!!。;;::\n\r,,]*)'.format('|'.join(pre), '|'.join(pos))) 112 | patterns.append(pattern) 113 | return patterns 114 | 115 | '''文章分句处理, 切分长句,冒号,分号,感叹号等做维护标识''' 116 | def split_sents(self, content): 117 | return [sentence.replace(' ','') for sentence in re.split(r'[??!!。;;::\n\r]', content) if sentence] 118 | 119 | '''模式匹配''' 120 | def pattern_match(self, patterns, sent): 121 | datas = {} 122 | max = 0 123 | for p in patterns: 124 | ress = p.findall(sent) 125 | if ress: 126 | for res in ress: 127 | data = {'pre_wd': res[0], 'pre_part': res[1], 'post_wd': res[2], 'post_part ': res[3]} 128 | len_res = len(res[0] + res[2]) 129 | if len_res > max: 130 | datas = data 131 | max = len_res 132 | return datas 133 | '''基于模式,抽取出相应的四元组''' 134 | def extract_tuples(self, sent): 135 | but_tuples = self.pattern_match(self.but_patterns, sent) 136 | condition_tuples = self.pattern_match(self.condition_patterns, sent) 137 | seq_tuples = self.pattern_match(self.seq_patterns, sent) 138 | more_tuples = self.pattern_match(self.more_patterns, sent) 139 | 140 | return but_tuples, condition_tuples, seq_tuples, more_tuples 141 | 142 | '''处理主函数''' 143 | def extract_main(self, content): 144 | sents = self.split_sents(content) 145 | datas = [] 146 | for sent in sents: 147 | data = {} 148 | data['sent'] = sent 149 | but_tuples, condition_tuples, seq_tuples, more_tuples = self.extract_tuples(sent) 150 | if but_tuples: 151 | data['type'] = 'but' 152 | data['tuples'] = but_tuples 153 | if condition_tuples: 154 | data['type'] = 'condition' 155 | data['tuples'] = condition_tuples 156 | if seq_tuples: 157 | data['type'] = 'seq' 158 | data['tuples'] = seq_tuples 159 | if more_tuples: 160 | data['type'] = 'more' 161 | data['tuples'] = more_tuples 162 | if 'type' in data: 163 | datas.append(data) 164 | return datas 165 | 166 | '''基于给定语料与模板的事件抽取,假定你选择的是Mongodb数据库''' 167 | class TextMining: 168 | def __init__(self): 169 | mongo_host = '127.0.0.1' # 你数据库的ip 170 | mongo_port = 28017 #你数据库端口 171 | mongo_db = 'news' #数据库名称 172 | mongo_col = 'news_data' #数据表名称 173 | username = urllib.parse.quote_plus("root")#用户名 174 | password = urllib.parse.quote_plus("12345678") #密码 175 | client = pymongo.MongoClient( 176 | 'mongodb://{}:{}@{}:{}'.format(username, password, mongo_host, mongo_port)) 177 | self.db = client[mongo_db] 178 | self.col = self.db[mongo_col] 179 | self.extractor = EventsExtraction() 180 | 181 | '''批量跑数据库中的数据,并插入相应数据库当中''' 182 | def process_mongonews(self): 183 | count = 0 184 | for item in self.col.find(): 185 | content = item['content'] 186 | url = item['url'] 187 | count+=1 188 | try: 189 | datas = self.extractor.extract_main(content) 190 | if datas: 191 | data = {} 192 | data['url'] = url 193 | data['data'] = datas 194 | self.db['event_extract'].insert(data) 195 | except Exception as e: 196 | print(e) 197 | if count % 10000 == 0: 198 | print(count) 199 | 200 | if __name__ == '__main__': 201 | handler = TextMining() 202 | handler.process_mongonews() -------------------------------------------------------------------------------- /img/but.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuanyong/ComplexEventExtraction/a355874b94c8cb2148a714e22510debdcfbddbc0/img/but.png -------------------------------------------------------------------------------- /img/condition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuanyong/ComplexEventExtraction/a355874b94c8cb2148a714e22510debdcfbddbc0/img/condition.png --------------------------------------------------------------------------------