传知代码-上下位关系自动检测方法(论文复现)
代码以及视频讲解
本文所涉及所有资源均在传知代码平台可获取
概述
本文复现论文 Hearst patterns revisited: Automatic hypernym detection from large text corpora[1] 提出的文本中上位词检测方法。
在自然语言处理中,上下位关系(Is-a Relationship)表示的是概念(又称术语)之间的语义包含关系。其中,上位词(Hypernym)表示的是下位词(Hyponym)的抽象化和一般化,而下位词则是对上位词的具象化和特殊化。举例来说:“水果”是“苹果”、“香蕉”、“橙子”等的上位词,“汽车”、“电动车”、“自行车”等则是“交通工具”的下位词。在自然语言处理任务中,理解概念之间的上下位关系对于诸如词义消歧、信息检索、自动问答、语义推理等任务都具有重要意义。
文本中上位词检测方法,即从文本中提取出互为上下位关系的概念。现有的无监督上位词检测方法大致可以分为两类——基于模式的方法和基于分布模型的方法:
(1)基于模式的方法:其主要思想是利用特定的词汇-句法模式来检测文本中的上下位关系。例如,我们可以通过检测文本中是否存在句式“【词汇1】是一种【词汇2】”或“【词汇1】,例如【词汇2】”来判断【词汇1】和【词汇2】间是否存在上下位关系。这些模式可以是预定义的,也可以是通过机器学习得到的。然而,基于模式的方法存在一个众所周知的问题——极端稀疏性,即词汇必须在有限的模式中共同出现,其上下位关系才能被检测到。
(2)基于分布模型的方法:基于大型文本语料库,词汇可以被学习并表示成向量的形式。利用特定的相似度度量,我们可以区分词汇间的不同关系。
在该论文中,作者研究了基于模式的方法和基于分布模型的方法在几个上下位关系检测任务中的表现,并发现简单的基于模式的方法在常见的数据集上始终优于基于分布模型的方法。作者认为这种差异产生的原因是:基于模式的方法提供了尚不能被分布模型准确捕捉到的重要上下文约束。
算法原理
Hearst 模式
作者使用如下模式来捕捉文本中的上下位关系:
模板 | 例子 |
---|---|
X X X which is a (example|class|kind…) of Y Y Y | Coffee, which is a beverage, is enjoyed worldwide. |
X X X (and|or) (any|some) other Y Y Y | Coffee and some other hot beverages are popular in the morning. |
X X X which is called Y Y Y | Coffee, which is called “java”. |
X X X is JJS (most)? Y Y Y | Coffee is the most consumed beverage worldwide. |
X X X is a special case of Y Y Y | Espresso is a special case of coffee. |
X X X is an Y Y Y that | A latte is a coffee that includes steamed milk. |
X X X is a !(member|part|given) Y Y Y | A robot is a machine. |
!(features|properties) Y Y Y such as X 1 X_1 X1, X 2 X_2 X2, … | Beverages such as coffee, tea, and soda have various properties such as caffeine content and flavor. |
(Unlike|like) (most|all|any|other) Y Y Y, X X X | Unlike most beverages, coffee is often consumed hot. |
Y Y Y including X 1 X_1 X1, X 2 X_2 X2, … | Beverages including coffee, tea, and hot chocolate are served at the café. |
通过对大型语料库使用模式捕捉候选上下位词对并统计频次,可以计算任意两个词汇之间存在上下位关系的概率
上下位关系得分
设 p ( x , y ) p(x,y) p(x,y)是词汇 x x x 和 y y y 分别作为下位词和上位词出现在预定义模式集合 P P P 中的频率, p − ( x ) p^-(x) p−(x)是 x x x 作为任意词汇的下位词出现在预定义模式中的频率, p + ( y ) p^+(y) p+(y)是 y y y 作为任意词汇的上位词出现在预定义模式中的频率。作者定义正逐点互信息(Positive Point-wise Mutual Information)作为词汇间上下位关系得分的依据:
ppmi ( x , y ) = max ( 0 , log p ( x , y ) p − ( x ) , p + ( y ) ) \text{ppmi}(x,y)=\max(0,\log\frac{p(x,y)}{p^-(x),p^+(y)}) ppmi(x,y)=max(0,logp−(x),p+(y)p(x,y))
由于模式的稀疏性,部分存在上下位关系的词对并不会出现在特定的模式中。为了解决这一问题,作者利用PPMI得分矩阵的稀疏表示来预测任意未知词对的上下位关系得分。PPMI得分矩阵定义如下:
M ∈ R m × m , M i j = ppmi ( x , y ) ( 1 ≤ x , y ≤ m ) M\in R^{m\times m},M_{ij}=\text{ppmi}(x,y)(1\le x,y\le m) M∈Rm×m,Mij=ppmi(x,y)(1≤x,y≤m)
,其中 KaTeX parse error: Undefined control sequence: \or at position 18: …|\{x|(x,y)\in P\̲o̲r̲(y,x)\in P\}|。
对矩阵 M M M 做奇异值分解可得 M = U Σ V T M=U\Sigma V^T M=UΣVT,然后我们可以通过下式计算出上下位关系 spmi 得分:
spmi ( x , y ) = u x T Σ r v y \text{spmi}(x,y)=u_x^T\Sigma_r v_y spmi(x,y)=uxTΣrvy
其中 u x u_x ux 和 v y v_y vy 分别是矩阵 U U U 和 V V V 的第 x x x 行和第 y y y 行, Σ r \Sigma_r Σr是对 Σ \Sigma Σ 的 r r r 截断(即除了最大的 r r r 个元素其余全部置零)。
核心逻辑
具体的核心逻辑如下所示:
import spacy
import json
from tqdm import tqdm
import re
from collections import Counter
import numpy as np
import mathnlp = spacy.load("en_core_web_sm")def clear_text(text):"""对文本进行清理"""# 这里可以添加自己的清理步骤# 删去交叉引用标识,例如"[1]"pattern = r'\[\d+\]'result = re.sub(pattern, '', text)return resultdef split_sentences(text):"""将文本划分为句子"""doc = nlp(text)sentences = [sent.text.strip() for sent in doc.sents]return sentencesdef extract_noun_phrases(text):"""从文本中抽取出术语"""doc = nlp(text)terms = []# 遍历句子中的名词性短语(例如a type of robot)for chunk in doc.noun_chunks:term_parts = []for token in list(chunk)[-1::]:# 以非名词且非形容词,或是代词的词语为界,保留右半部分(例如robot)if token.pos_ in ['NOUN', 'ADJ'] and token.dep_ != 'PRON':term_parts.append(token.text)else:breakif term_parts != []:term = ' '.join(term_parts)terms.append(term)return termsdef term_lemma(term):"""将术语中的名词还原为单数"""lemma = []doc = nlp(term)for token in doc:if token.pos_ == 'NOUN':lemma.append(token.lemma_)else:lemma.append(token.text)return ' '.join(lemma)def find_co_occurrence(sentence, terms, patterns):"""找出共现于模板的术语对"""pairs = []# 两两之间匹配for hyponym in terms:for hypernym in terms:if hyponym == hypernym:continuefor pattern in patterns:# 将模板中的占位符替换成候选上下位词pattern = pattern.replace('__HYPONYM__', re.escape(hyponym))pattern = pattern.replace('__HYPERNYM__', re.escape(hypernym))# 在句子中匹配if re.search(pattern, sentence) != None:# 将名词复数还原为单数pairs.append((term_lemma(hyponym), term_lemma(hypernym)))return pairsdef count_unique_tuple(tuple_list):"""统计列表中独特元组出现次数"""counter = Counter(tuple_list)result = [{"tuple": unique, "count": count} for unique, count in counter.items()]return resultdef find_rth_largest(arr, r):"""找到第r大的元素"""rth_largest_index = np.argpartition(arr, -r)[-r]return arr[rth_largest_index]def find_pairs(corpus_file, patterns, disable_tqdm=False):"""读取文件并找出共现于模板的上下位关系术语对"""pairs = []# 按行读取语料库lines = corpus_file.readlines()for line in tqdm(lines, desc="Finding pairs", ascii=" 123456789#", disable=disable_tqdm):# 删去首尾部分的空白字符line = line.strip()# 忽略空白行if line == '':continue# 清理文本line = clear_text(line)# 按句处理sentences = split_sentences(line)for sentence in sentences:# 抽取出句子中的名词性短语并分割成术语candidates_terms = extract_noun_phrases(sentence)# 找出共现于模板的术语对pairs = pairs + find_co_occurrence(sentence, candidates_terms, patterns)return pairsdef spmi_calculate(configs, unique_pairs):"""基于对共现频率的统计,计算任意两个术语间的spmi得分"""# 计算每个术语分别作为上下位词的出现频次terms = list(set([pair["tuple"][0] for pair in unique_pairs] + [pair["tuple"][1] for pair in unique_pairs]))term_count = {term: {'hyponym_count': 0, 'hypernym_count': 0} for term in terms}all_count = 0for pair in unique_pairs:term_count[pair["tuple"][0]]['hyponym_count'] += pair["count"]term_count[pair["tuple"][1]]['hypernym_count'] += pair["count"]all_count += pair["count"]# 计算PPMI矩阵 ppmi_matrix = np.zeros((len(terms), len(terms)), dtype=np.float32)for pair in unique_pairs:hyponym = pair["tuple"][0]hyponym_id = terms.index(hyponym)hypernym = pair["tuple"][1]hypernym_id = terms.index(hypernym)ppmi = (pair["count"] * all_count) / (term_count[hyponym]['hyponym_count'] * term_count[hypernym]['hypernym_count'])ppmi = max(0, math.log(ppmi))ppmi_matrix[hyponym_id, hypernym_id] = ppmi# 对PPMI进行奇异值分解并截断r = configs['clip']U, S, Vt = np.linalg.svd(ppmi_matrix)S[S < find_rth_largest(S, r)] = 0S_r = np.diag(S)# 计算任意两个术语间的spmiparis2spmi = []for hyponym_id in range(len(terms)):for hypernym_id in range(len(terms)):# 同一个术语间不计算得分if hyponym_id == hypernym_id:continuespmi = np.dot(np.dot(U[hyponym_id , :], S_r), Vt[:, hypernym_id]).item()# 保留得分大于阈值的术语对if spmi > configs["threshold"]:hyponym = terms[hyponym_id]hypernym = terms[hypernym_id]paris2spmi.append({"hyponym": hyponym, "hypernym": hypernym, "spmi": spmi})# 按spmi从大到小排序paris2spmi = sorted(paris2spmi, key=lambda x: x["spmi"], reverse=True)return paris2spmiif __name__ == "__main__":# 读取配置文件with open('config.json', 'r') as config_file:configs = json.load(config_file)# 读取模板with open(configs['patterns_path'], 'r') as patterns_file:patterns = json.load(patterns_file)# 语料库中共现于模板的术语对with open(configs['corpus_path'], 'r', encoding='utf-8') as corpus_file:pairs = find_pairs(corpus_file, patterns)# 统计上下位关系的出现频次unique_pairs = count_unique_tuple(pairs)with open(configs["pairs_path"], 'w') as pairs_file:json.dump(unique_pairs, pairs_file, indent=6, ensure_ascii=True)# 计算任意两个术语间的spmi得分paris2spmi = spmi_calculate(configs, unique_pairs)with open(configs['spmi_path'], 'w') as spmi_file:json.dump(paris2spmi, spmi_file, indent=6, ensure_ascii=True)
以上代码仅作展示,更详细的代码文件请参见附件。
效果演示
运行脚本main.py,程序会自动检测语料库中存在的上下位关系。运行结果如下所示:
使用方式
- 解压附件压缩包并进入工作目录。如果是Linux系统,请使用如下命令:
unzip Revisit-Hearst-Pattern.zip
cd Revisit-Hearst-Pattern
- 代码的运行环境可通过如下命令进行配置:
pip install -r requirements.txt
python -m spacy download en_core_web_sm
- 如果希望在本地运行程序,请运行如下命令:
python main.py
- 如果希望在线部署,请运行如下命令:
python main-flask.py
- 如果希望添加新的模板,请修改文件
data/patterns.json
。- "_HYPONYM_"表示下位词占位符;
- "_HYPERNYM_"表示上位词占位符;
- 其余格式请遵照 python.re 模块的正则表达式要求。
- 如果希望使用自己的文件路径或改动其他实验设置,请在文件
config.json
中修改对应参数。以下是参数含义对照表:
参数名 | 含义 |
---|---|
corpus_path | 文本语料库文件路径,默认为“data/corpus.txt”。 |
patterns_path | 预定义模式库的路径。默认为“data/patterns.json”。 |
pairs_path | 利用模式筛选出的上下位关系词对路径,默认为“data/pairs.json”。 |
spmi_path | 上下位关系词对及其spmi得分路径,默认为“data/spmi.json”。 |
clip | 用于对 Σ \Sigma Σ 进行截断的参数 r r r ,默认为10。 |
threshold | spmi得分小于该值的词对将被舍去。默认为1。 |
max_bytes | 输入文件大小上限(用于在线演示),默认为200kB。 |
(以上内容皆为原创,请勿转载)
参考文献
[1] Roller S, Kiela D, Nickel M. Hearst patterns revisited: Automatic hypernym detection from large text corpora[J]. arXiv preprint arXiv:1806.03191, 2018.
源码下载
相关文章:

传知代码-上下位关系自动检测方法(论文复现)
代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 概述 本文复现论文 Hearst patterns revisited: Automatic hypernym detection from large text corpora[1] 提出的文本中上位词检测方法。 在自然语言处理中,上下位关系(Is-a Relations…...

从零开始的MicroPython(二) GPIO及代码应用
上一篇:http://t.csdnimg.cn/mg2Qt 文章目录 ESP32(NodeMCU-32S)简介引脚注意事项 类与对象的概念MicroPython的GPIO使用文档解释machine.PinPin.irq 点灯 ESP32(NodeMCU-32S) 简介 NodeMCU-32S 是安信可基于 ESP32-32S 模组所设计的核心开发板。该开发板延续了 N…...

嵌入式day15
数组指针 能够指向整个数组 一维数组: &a,考察a的数据类型 int(*p)[10]:表示一个指向长度为10的一维整型数组的指针 二维数组: 指向函数的指针 函数的函数名,即为函数的入口地址&#x…...

【电池管理系统(BMS)-01】 | 电池管理系统简介,动力电池和储能电池区别
🎩 欢迎来到技术探索的奇幻世界👨💻 📜 个人主页:一伦明悦-CSDN博客 ✍🏻 作者简介: C软件开发、Python机器学习爱好者 🗣️ 互动与支持:💬评论 &…...

C++ STL partial_sum 用法
一:功能 计算部分和,即遍历序列中每个元素,计算前 i 个元素的累加和,并将结果存在 i 的位置上。 二:用法 #include <iostream> #include <vector> #include <numeric>int main() {std::vector<…...

诚宜开张圣听不应妄自菲薄
拾人牙慧孜孜不倦 青山依旧在几度夕阳红朝闻道夕死可矣 青山依旧在几度夕阳红 安能以血补天我计不成乃天命也臣本布衣躬耕南阳大丈夫宁死不辱尔要试我宝剑是否锋利吗又待怎样休教天下人负我竖子不足与谋皇天不佑天下英雄唯使君与操尔青光殷殷其灿如炎备不量力欲申大义于天下我…...

Vue3 加载条(LoadingBar)
效果如下图:在线预览 APIs LoadingBar 参数说明类型默认值必传containerClass加载条容器的类名stringundefinedfalsecontainerStyle加载条容器的样式CSSProperties{}falseloadingBarSize加载条大小,单位 pxnumber2falsecolorLoading加载中颜色string‘…...

《CSS创意项目实战指南》:点亮网页,从实战中掌握CSS的无限创意
CSS创意项目实战指南 在数字时代,网页不仅是信息的载体,更是艺术与技术的融合体。通过CSS,你可以将平凡的网页转变为引人入胜的视觉盛宴,让用户体验跃升至全新高度。《CSS创意项目实战指南》正是这样一本引领你探索CSS无限可能的…...

[FBCTF2019]RCEService (PCRE回溯绕过和%a0换行绕过)
json格式输入ls出现index.php 这道题原本是给了源码的,BUUCTF没给 源码: <?phpputenv(PATH/home/rceservice/jail);if (isset($_REQUEST[cmd])) {$json $_REQUEST[cmd];if (!is_string($json)) {echo Hacking attempt detected<br/><br/…...

vue3后台管理系统 vue3+vite+pinia+element-plus+axios上
前言 项目安装与启动 使用vite作为项目脚手架 # pnpm pnpm create vite my-vue-app --template vue安装相应依赖 # sass pnpm i sass # vue-router pnpm i vue-router # element-plus pnpm i element-plus # element-plus/icon pnpm i element-plus/icons-vue安装element-…...

Mysql的事务隔离级别实现原理
一、事务隔离级别 mysql支持四种事务隔离级别: 读未提交:一个事务可以读取到另一个事务还未提交的数据;读已提交:一个事务可以读取到另一个事务已经提交的数据;可重复读:同一个事务中,无论读取…...

计算机体系结构:缓存一致性ESI
集中式缓存处理器结构(SMP) 不同核访问存储器时间相同。 分布式缓存处理器结构(NUMA) 共享存储器按模块分散在各处理器附近,处理器访问本地存储器和远程存储器的延迟不同,共享数据可进入处理器私有高速缓存…...

log4j2漏洞练习(未完成)
log4j2 是Apache的一个java日志框架,我们借助它进行日志相关操作管理,然而在2021年末log4j2爆出了远程代码执行漏洞,属于严重等级的漏洞。apache log4j通过定义每一条日志信息的级别能够更加细致地控制日志生成地过程,受影响的版本…...

常见网络攻击方法原理、应用场景和防御方法(一)
目录 1、SQL注入(SQL Injection)原理应用场景防御方法 2、跨站脚本攻击(XSS,Cross-Site Scripting)原理应用场景防御方法 3、跨站请求伪造(CSRF,Cross-Site Request Forgery)原理应用场景防御方法 4、文件上传漏洞原理应用场景防御方法 5、远程代码执行(…...

【leetcode十分钟】覆盖所有点的最少矩形数目(C++思路详解)
思路详解: 0. 题目情境并未限制矩形高度,故矩形数目的判断只和点的横坐标有关 1. 为了不重不漏地考虑到所有点,故笔者选择首先将二维数组中的点按横坐标的大小排序 //说明:本来笔者以为需要自定义sort排序,后来发现…...

【Vue3】默认插槽
【Vue3】默认插槽 背景简介开发环境开发步骤及源码 背景 随着年龄的增长,很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来,技术出身的人总是很难放下一些执念,遂将这些知识整理成文,以纪念曾经努力学习奋斗的日子。本文内…...

华清day4 24-7-31
1> 使用父子进程完成两个文件的拷贝 父进程拷贝前一半内容,子进程拷贝后一半内容 子进程结束后退出,父进程回收子进程的资源 /* 使用父子进程完成两个文件的拷贝父进程拷贝前一半内容,子进程拷贝后一半内容 子进程结束后退出ÿ…...

搜维尔科技:Manus VR数据手套-适用于机器人、人工智能和机器学习解决方案
在劳动力短缺和工作环境日益严峻的今天,机器人技术正成为解决这些复杂问题的关键。MANUS™ 手指捕捉技术,结合先进的量子追踪技术,为机器人的精确操作和远程控制提供了准确且先进的解决方案。 技术亮点 实时数据捕捉:通过Quantum…...

知识文库杂志知识文库杂志社知识文库编辑部2024年第12期目录
文艺理论 现代高校书院对中国传统书院学术精神的汲取与转化 李奥楠;时新洁; 1-4 个案工作介入高中美术艺考生及家长心理调适的应用研究 魏星; 5-8《知识文库》投稿:cn7kantougao163.com 中华优秀传统文化视角下高校美育课程实践教学 李丛丛; 9-12 基…...

【Linux网络编程】套接字Socket
网络编程基础概念: ip地址和端口号 ip地址是网络协议地址(4字节32位,形式:xxx.xxx.xxx.xxx xxx在范围[0, 255]内),是IP协议提供的一种统一的地址格式,每台主机的ip地址不同,一个…...

es之must、filter、must_not、should
文章目录 概述mustfiltermust_notshouldmust和filter的区别 概述 在Elasticsearch中,布尔查询(bool query)是构建复杂查询的基本工具。它允许你组合多个查询子句,每个子句可以使用不同的逻辑操作符。常见的逻辑操作符包括 must、…...

RocketMQ消息发送基本示例(推送消费者)
消息生产者通过三种方式发送消息 1.同步发送:等待消息返回后再继续进行下面的操作 同步发送保证了消息的可靠性,适用于关键业务场景。 2.异步发送:不等待消息返回直接进入后续流程.broker将结果返回后调用callback函数,并使用 CountDownLatch计数 3.单向发送:只…...

23 MySQL基本函数、分组查询、多列排序(3)
上一篇「22 B端产品经理与MySQL基本查询、排序(2)」了解了基本的常识和基本查询以及单列排序。下面介绍常见的基本函数、分组查询以及多列排序: 基本函数 user表 (注:以下SQL语句示例全部基于下面「user表」) uidunamedepiduag…...

PHP与SEO,应用curl库获取百度下拉关键词案例!
编程语言从来都是工具,编程逻辑思维才是最重要的,在限定的规则内,实现自己的想法,正如人生一样! 不管是python还是php只要掌握了基础语法规则,明确了实现过程,都能达到想要实现的结果࿰…...

MySQL:子查询
MySQL 子查询 MySQL中的子查询是一个强大的功能,子查询是指在一个查询语句中嵌套另一个查询语句的情况。嵌套查询中的内部查询语句可以使用外部查询语句的结果来进行过滤、联接或作为子查询的值,它允许我们在一个查询内部嵌套另一个查询。通过子查询可以…...

C++—— IO流
一、C语言的输入与输出 C语言中我们用到的最频繁的输入输出方式就是scanf()和printf()。 scanf():从标准输入设备(键盘)中读取数据,并将值存放在变量中。 printf():将指定的文字/字符串输出到标准输出设备(…...

vue+node后台处理大文件切片上传--前端部分
本文主要介绍,在vue3vite项目下,如何进行有效的大文件上传,本文章主要讲大文件切片上传方式,并提供简单的demo代码供参考 首先,请确保已经创建好项目,这一步跳过。 1、为了选择合适的文件,我们…...

【通俗理解】艺术与数学交融
【通俗理解】艺术与数学交融 艺术与数学的奇妙交融 你可以把艺术比作一个“梦幻花园”,它充满了无限的可能性和美感。而数学则是一把“精密钥匙”,它能够解开花园中的秘密,揭示美的内在结构。 艺术与数学交融的核心作用 组件/步骤描述艺术表…...

深入探讨 Docker 容器文件系统
引言 随着云计算和微服务架构的兴起,Docker 容器技术迅速成为开发和运维人员的首选工具。Docker 容器不仅提供了一种轻量级的虚拟化方式,还简化了应用程序的部署和管理。在众多的技术细节中,Docker 容器文件系统是一个至关重要的组成部分。本…...

《LeetCode热题100》---<4.子串篇三道>
本篇博客讲解LeetCode热题100道子串篇中的三道题 第一道:和为 K 的子数组 第二道:滑动窗口最大值 第三道:最小覆盖子串 第一道:和为 K 的子数组(中等) 法一:暴力枚举 class Solution {public in…...