【论文复现】上下位关系自动检测方法

news/2024/11/27 17:06:58/

在这里插入图片描述

📝个人主页🌹:Eternity._
🌹🌹期待您的关注 🌹🌹

在这里插入图片描述
在这里插入图片描述

❀ 上下位关系自动检测方法

  • 算法原理
    • Hearst 模式
    • 上下位关系得分
  • 核心逻辑
  • 效果演示
  • 使用方式
  • 参考文献

本文复现论文 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 模式


作者使用如下模式来捕捉文本中的上下位关系:
在这里插入图片描述
通过对大型语料库使用模式捕捉候选上下位词对并统计频次,可以计算任意两个词汇之间存在上下位关系的概率

上下位关系得分

设 p(x,y)是词汇 x和 y 分别作为下位词和上位词出现在预定义模式集合
P 中的频率, ( p − ) (p^-) (p)(x)是x 作为任意词汇的下位词出现在预定义模式中的频率, ( p + ) (p^+) (p+)(y)是 yy 作为任意词汇的上位词出现在预定义模式中的频率。作者定义正逐点互信息(Positive Point-wise Mutual Information)作为词汇间上下位关系得分的依据:
在这里插入图片描述
由于模式的稀疏性,部分存在上下位关系的词对并不会出现在特定的模式中。为了解决这一问题,作者利用PPMI得分矩阵的稀疏表示来预测任意未知词对的上下位关系得分。PPMI得分矩阵定义如下:
在这里插入图片描述
,其中 m=|{x|(x,y)\in P\or(y,x)\in P}|。

对矩阵 M 做奇异值分解可得 M = U Σ V T M = U\Sigma V^T M=UΣVT ,然后我们可以通过下式计算出上下位关系 spmi 得分:
在这里插入图片描述
其中 ( u x ) (u_x) (ux) ( v y ) (v_y) (vy)​分别是矩阵 U 和 V 的第 x行和第 y 行,Σ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用于对 Σ 进行截断的参数 r ,默认为10。
thresholdspmi得分小于该值的词对将被舍去。默认为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.


编程未来,从这里启航!解锁无限创意,让每一行代码都成为你通往成功的阶梯,帮助更多人欣赏与学习!

更多内容详见:这里


http://www.ppmy.cn/news/1550397.html

相关文章

Android 分区相关介绍

目录 一、MTK平台 1、MTK平台分区表配置 2、MTK平台刷机配置表 3、MTK平台分区表配置不生效 4、Super分区的研究 1&#xff09;Super partition layout 2&#xff09;Block device table 二、高通平台 三、展锐平台 四、相关案例 1、Super分区不够导致编译报错 经验…

linux上制作启动盘命令

linux上制作启动盘命令 准备一个U盘&#xff0c;插上电脑。 卸载 卸载设备&#xff1a;sudo umount /dev/sda # 格式化U盘前建议都卸载设备&#xff1b;格式化命令&#xff1a; mkfs.vfat /dev/sda 格式化整个设备&#xff0c;会删除分区&#xff08;推荐vfat文件系统…

Linux 证书安装:mkcert 使用指南,如何快速创建自签名 SSL 证书

我们平时在本地开发时,有时会要求通过 HTTPS 请求来访问。一个通用的做法是用 OpenSSL 生成自签证书,然后对 Web服务进行配置。但 OpenSSL 的命令比较繁琐,参数也比较复杂,用起来不够方便。于是这里分享一个替代方案:mkcert。 一、项目介绍 mkcert 是一个用于生成本地自签…

前后端分离,后端拦截器无法获得前端请求的token

首先需要了解两个知识点&#xff1a; 跨域是指在浏览器中&#xff0c;一个网页试图请求另一个域&#xff08;协议、域名或端口不同&#xff09;的资源时&#xff0c;所涉及的安全机制。在Web开发中&#xff0c;一般来说&#xff0c;由于安全性原因&#xff0c;浏览器会阻止一个…

Flink学习连载文章8--时间语义

Time的分类 (时间语义) EventTime:事件(数据)时间,是事件/数据真真正正发生时/产生时的时间 IngestionTime:摄入时间,是事件/数据到达流处理系统的时间 ProcessingTime:处理时间,是事件/数据被处理/计算时的系统的时间 EventTime的重要性 假设&#xff0c;你正在去往地下停…

Diffusion异常检测相关论文及代码整理

扩散模型&#xff08;Diffusion Models&#xff09;是一种生成模型&#xff0c;广泛用于图像生成、文本生成等领域。在异常检测任务中&#xff0c;扩散模型也可以被用来识别和检测异常数据点。该文章对近几年利用扩散模型进行异常检测的文章进行了整理&#xff1a; 2024 1. A…

利用c语言详细介绍下希尔排序

希尔排序是针对插入排序的优化算法。它是缩少增量的算法&#xff0c;一开始增量从元素个数len/2的增量开始&#xff0c;然后缩小增量gapgap/2&#xff0c;直到gap为1&#xff0c;最终完成序列排序。 一、图文介绍 我们还是使用数组【10&#xff0c;5&#xff0c;3&#xff0c;2…

CentOS 7 安装部署 KVM

1.关闭虚拟机 打开相关选项 打开虚拟机centos7 连接xshell 测试网络&#xff0c;现在就是没问题的&#xff0c;因为我们要使用网络源 安装 GNOME 桌面环境 安装KVM 模块 安装KVM 调试工具 构建虚拟机的命令行工具 qemu 组件,创建磁盘、启动虚拟机等 输入这条命令&#xff0c;…