# NLP菜鸟笔记 > This is the Way > Admin Email: 1209461019@qq.com ## 文章 ### Open AI API连接问题 报错信息 raise error.APIConnectionError(openai.error.APIConnectionError: Error communicating with OpenAI: HTTPSConnectionPool(host='api.openai.com', port=443): Max retries exceeded with url: /v1/chat/completions (Caused by ProxyError('Cannot connect to proxy.', NewConnectionError(': Failed to establish a new connection: [WinError 10061] 由于目标计算机积极拒绝,无法连接。'))) 主要原因为代理的问题,需要魔法 解决方法 需要修改的文件为 ...\你的环境\site-packages\openai\api_requestor.py 设置代理 proxy = {'http': "http://127.0.0.1:7890", 'https': "http://127.0.0.1:7890"} ### OBS录屏并配音 录制屏幕调研 因需要录制一段屏幕视频因此做了一个小小的调研,一共使用了三种方式录制。 第一种是通过OBS进行录制。OBS的官方网站为:https://obsproject.com/,在官网上下载相对应的客户端之后便可以录制。 第二种是通过谷歌浏览器的插件。参考知乎Chrome 录屏神器!自称“最强大”的屏幕录制和注释工具!这篇文章就可以。录制的感受是清晰度相对较低。 第三种是通过PPT。新建一个空白文档,选择“插入”,再选择“屏幕录制”,然后选择需要录制的区域的步骤即可。录制好之后右击,选择“将媒体另存为”即可保存视频。PPT有个问题是出现在选择区域的相关画面都会被录制进去,比如邮件弹窗,输入法的窗口等。 使用OBS屏幕录制 设置 可以通过“控制按钮”中的设置,选择录制视频的存储地址。 设置输出地址 也可以通过“热键”设置快捷键,通过尝试设置了两个不会和Chrome浏览器冲突的快捷键,开始和结束分别为“Ctrl+Shift+S”和“Ctrl+Shift+Z”。 快捷键设置 录制屏幕 在“来源”添加“显示器采集”就会捕捉到显示器上面的内容,可以通过快捷键操作录制和停止。录制好的视频就会存储在预选定义的路径中。 录制浏览器标签 若果选择屏幕录制,显示器的所有操作都会被录制。如果只想录制录制浏览器的某页或者固定窗口,可以在来源的位置添加“窗口采集”,这样就可以只录制当前的窗口了。然后有些网站的ip地址会暴露,所以希望只标签页。目前使用的方法是,回到需要录制的那页然后全屏(Chrome浏览器在联想Windows的快捷键为F11)会隐藏掉ip等信息,再通过录制和停止的快捷键控制录制。 为视频加配音 如果视频需要后期加入配音,可以通过在“来源”的位置添加“媒体源”选择需要配音的视频。 选择视频 选择好需要配音的视频后,在“来源”的位置添加“音频输入采集”,再录制就可以配音啦。 ### Linux安装配置OpenJDK1.8 下载、解压JDK 首先下载压缩包 wget https://download.java.net/openjdk/jdk8u41/ri/openjdk-8u41-b04-linux-x64-14_jan_2020.tar.gz 然后解压 tar -zxvf openjdk-8u41-b04-linux-x64-14_jan_2020.tar.gz 配置JAVA_HOME环境变量 # 打开环境变量文件 vim .bashrc # 子文件末尾追加 export JAVA_HOME=/home/user_name/java-se-8u41-ri export JRE_HOME=${JAVA_HOME}/jre export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib export PATH=${JAVA_HOME}/bin:$PATH export PATH=${JAVA_HOME}/jre/bin:$PATH # source一下,重新加载环境变量 source .bashrc 检查环境安装是否成功 输入: java -version 输出: openjdk version "1.8.0_41" OpenJDK Runtime Environment (build 1.8.0_41-b04) OpenJDK 64-Bit Server VM (build 25.40-b25, mixed mode) 参考 https://blog.csdn.net/linysuccess/article/details/108881251 ### 论文笔记:Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks 前言 论文:https://arxiv.org/pdf/1908.10084.pdf 代码:https://github.com/UKPLab/sentence-transformers 摘要 BERT和RoBERTa在句子对回归任务,如语义文本相似度(STS)上取得了新的最先进的性能。然而,它需要将两个句子都输入到网络中,这会导致巨大的计算开销:使用BERT在10,000个句子的集合中找到最相似的一对,需要大约5000万次推理计算(~65小时)。BERT的构造使其不适合语义相似性搜索以及聚类等无监督任务。 论文提出sentence -BERT (SBERT),一种预训练BERT网络的修改,使用孪生和三元网络结构来得到有语义的句子嵌入,可以用余弦相似度进行比较。这将寻找最相似的一对的工作量从BERT / RoBERTa的65小时减少到SBERT的大约5秒,同时保持BERT的准确性。 在常见的STS任务和迁移学习任务上评估了SBERT和SRoBERTa,其表现优于其他最先进的句子嵌入方法。 动机 BERT使用交叉编码器:将两个句子传递给transformer网络,并预测目标值。然而,由于可能的组合太多,这种设置不适合各种对回归任务。有些任务到目前为止并不适用于BERT。这些任务包括大规模的语义相似性比较、聚类和基于语义搜索的信息检索。 解决聚类和语义搜索的一种常见方法是将每个句子映射到向量空间,使语义相似的句子接近。研究人员已经开始将单个句子输入到BERT中,并获得固定大小的句子嵌入。最常用的方法是平均BERT输出层(称为BERT嵌入)或使用第一个标记的输出([CLS]标记)。但是,这种常见做法产生了相当糟糕的句子嵌入,通常比平均GloVe嵌入更糟糕。 解决方法 为了缓解这个问题,作者开发了SBERT。孪生网络架构使输入句子的固定大小的向量可以推导出来。使用余弦相似度或曼哈顿/欧几里得距离等相似性度量,可以找到语义相似的句子。这些相似性度量在现代硬件上可以非常高效地执行,使SBERT可以用于语义相似性搜索和聚类。 在NLI数据上对SBERT进行微调,创建的句子嵌入显著优于其他最先进的句子嵌入方法,如InferSent和Universal sentence Encoder。在7个语义文本相似度(STS)任务上,SBERT比InferSent提高了11.7分,比Universal Sentence Encoder提高了5.5分。在句子嵌入评估工具包SentEval上,分别取得了2.1和2.6分的提升。 模型 SBERT向BERT / RoBERTa的输出添加了池化操作,以导出固定大小的句子嵌入。用三种池化策略进行了实验:使用CLS-token的输出;计算所有输出向量的平均值(MEAN-strategy),以及计算输出向量的最大值(MAX-strategy)。默认配置为MEAN。 为了微调BERT / RoBERTa,创建了孪生网络和三元网络来更新权重,使生成的句子嵌入具有语义意义,并可以与余弦相似度进行比较。网络结构取决于可用的训练数据。用以下结构和目标函数进行实验。 分类目标函数 将句子嵌入u和v与元素差异|u−v|连接起来,并将其与可训练权重Wt∈R3n×k相乘: 其中n是句子嵌入的维度,k是标签的数量。优化交叉熵损失。这个结构如图1所示: 图1 回归目标函数 计算两个句子嵌入u和v之间的余弦相似度(图2)。使用均方误差损失作为目标函数。 图2 三元组目标函数 给定一个锚句子a、一个正句子p和一个负句子n,三元组损失调整网络,使a和p之间的距离小于a和n之间的距离。在数学上,最小化以下损失函数: 结论 BERT开箱即用,将句子映射到向量空间,不适合与常见的相似性度量(如余弦相似度)一起使用。7个STS任务的性能低于平均GloVe嵌入的性能。 为了克服这个缺点,提出了Sentence-BERT (SBERT)。SBERT在孪生/三元组网络架构中对BERT进行微调。在各种常见基准上评估了质量,与最先进的句子嵌入方法相比,它可以实现显著的改进。用RoBERTa替换BERT并没有在论文的实验中产生显著的改进。 SBERT的计算效率很高。在GPU上,它比InferSent快约9%,比Universal Sentence Encoder快约55%。SBERT可用于计算上不可用BERT建模的任务。例如,使用层次聚类对10,000个句子进行聚类需要BERT大约65小时,因为必须计算大约5000万个句子组合。使用SBERT,可以将时间缩短到5秒左右。 ### 论文笔记:Deep Short Text Classification with Knowledge Powered Attention 前言 论文:https://arxiv.org/pdf/1902.08050.pdf 代码:https://github.com/AIRobotZhang/STCKA 摘要 短文本分类是自然语言处理(NLP)中的重要任务之一。与段落或文档不同,短文本由于没有足够的上下文信息而更加模糊,这对分类提出了很大的挑战。论文通过从外部知识源检索知识来增强短文本的语义表示。将概念信息作为一种知识,并将其纳入深度神经网络。为了度量知识的重要性,引入了注意机制,并提出了基于知识驱动注意的短文本深度分类(STCKA)。利用概念对短文本(CST)的注意和概念对概念集(C-CS)的注意,从两个方面获取概念的权重。并利用概念信息对短文本进行分类。与传统方法不同的是,STCKA模型就像一个人一样,具有基于观察(即机器的训练数据)做出决策的内在能力,并且更加关注重要的知识。还针对不同的任务在四个公共数据集上进行了广泛的实验。实验结果和案例研究表明,STCKA模型优于最先进的方法,证明了知识驱动注意力的有效性。 挑战 挑战1 相对于段落或文档,短文本由于缺乏足够的上下文信息而具有更强的歧义性,这给短文本分类带来了巨大挑战。短文本分类主要可以分为显式表示和隐式表示两类。 显式模型具有可解释性,便于人类理解。然而,这种显式表示通常忽略了短文本的上下文,无法捕捉深层语义信息。 基于深度神经网络的隐式模型擅长捕捉短文本中的语法和语义信息。然而,它忽略了知识库中存在的isA、isPropertyOf等重要的语义关系。这些信息有助于理解短文本,特别是在处理未见过的单词时。 该文将短文本的显式和隐式表示融合到一个统一的深度神经网络模型中。在YAGO和Freebase等显式KBs的帮助下丰富了短文本的语义表示。这允许模型从短文本中没有明确声明但与分类相关的外部知识源检索知识。概念信息作为一种知识对分类是有帮助的。因此,该文利用isA关系,将短文本与知识库中的相关概念进行概念化关联。然后,将概念信息作为先验知识融入深度神经网络。 挑战2 尽管简单地将概念信息集成到深度神经网络中似乎很直观,但仍然存在两个主要问题。 首先,在对短文本进行概念化时,由于实体的歧义性或KBs中的噪声,容易引入一些不恰当的概念。例如,在短文本S2:“Alice has been using Apple for 10 more years”中,从KB中获取了Apple的fruit和mobile phone两个概念。显然,fruit在这里不是一个合适的概念,这是由于Apple的模糊性造成的。 其次,需要考虑概念的粒度和概念的相对重要性。例如,在短文本S3:“Bill Gates is one of the co-founders of Microsoft”中,从KB中检索了person和entrepreneur of Bill Gates的概念。虽然它们都是正确的概念,但企业家比人更具体,在这种情况下应该被赋予更大的权重。之前的工作利用网络规模的KBs来丰富短文本表示,但没有仔细解决这两个问题。 为了解决这两个问题,该文引入注意力机制,提出了基于知识驱动注意力的深度短文本分类(STCKA)。注意力机制被广泛用于获取向量的权重,在许多NLP应用中,包括机器翻译、摘要生成和问答。针对第一个问题,使用面向短文本的概念注意力(Concept towards Short Text,CST)来衡量短文本与其对应概念之间的语义相似度。该模型赋予S2中mobile phone概念较大的权重,因为它与短文本的语义相似度高于fruit概念。针对第二个问题,使用面向概念集的注意力(Concept towards Concept Set,C-CS)来探索每个概念相对于整个概念集的重要性。模型为S3中的概念企业家分配了更大的权重,这对特定的分类任务更具区分性。 引入一种软开关(soft switch)来将两个注意力权重组合为一个,并产生每个概念的最终注意力权重,该模型在不同的数据集上自适应地学习。然后对概念向量进行加权求和,得到概念表示;此外,充分利用短文本的字和词两级特征,并利用自注意力机制生成短文本表示;最后,根据短文本的表示及其相关概念对短文本进行分类。 模型 STCKA是一个知识增强的深度神经网络,如图所示。网络的输入是一个短文本s,它是一个单词序列。网络的输出是类标签的概率分布。用p(y|s,φ)表示短文本属于类别y的概率,其中φ是网络中的参数。模型包含四个模块。 知识检索模块从知识库中检索与短文本相关的概念信息。 输入嵌入模块利用短文本的字和词级别特征生成词和概念的表示。 短文本编码模块通过自注意力机制对短文本进行编码并产生短文本表示q。 知识编码模块将两种注意力机制应用于概念向量以获得概念表示p。 然后,将p和q连接起来以融合短文本和概念信息,并将其送入全连接层。最后,使用输出层来获取每个类别标签的概率。 知识检索(Knowledge Retrieval) 该模块的目标是从KBs中检索相关知识。文中以isA关系为例,其他语义关系如isPropertyOf也可以采用类似的方法进行应用。具体来说,给定一个短文本s,希望找到一个与它相关的概念集C。通过两个主要步骤来实现这一目标:实体链接和概念化。通过利用现有的实体链接解决方案获得短文本的实体集E。然后,对于e∈E的每个实体e,从现有的知识库中获取其概念信息,如YAGO、Probase和CNProbase。例如,给定一篇短文本“Jay and Jolin are born in Taiwan”,通过实体链接得到实体集合E = {Jay Chou, Jolin Tsai}。然后,对Jay Chou进行概念化,从CN-Probase中获取Jay Chou的概念集C = {person, singer, actor, musician, director}。 输入嵌入(Input Embedding) 输入由两部分组成:长度为n的短文本s和大小为m的概念集C。在该模块中使用了字符向量、词向量和概念向量三种嵌入向量。字符嵌入层负责将每个单词映射到高维向量空间。使用卷积神经网络(CNN)获得每个单词的字符级嵌入。将字符嵌入到向量中,可以认为向量是CNN的1维输入,其大小是CNN的输入通道大小。CNN的输出在整个宽度上进行最大池化,以为每个单词获得固定大小的向量。词和概念嵌入层还将每个词和概念映射到高维向量空间。我们使用预训练的词向量(word2vec)来获得每个单词的词嵌入。词向量、字向量和概念向量的维数均为d/2。将字符嵌入向量和词/概念嵌入向量连接起来得到d维的词/概念表示。 短文本编码(Short Text Encoding) 该模块的目标是为长度为n的给定短文本生成短文本表示q,该短文本表示为d维词向量序列(x1, x2,…xn)。自注意力是注意力机制的一种特殊情况,只需要一个序列来计算其表示。在使用self-attention之前,添加一个循环神经网络(RNN)来转换来自底层的输入。原因解释如下,注意力机制使用加权和来生成输出向量,因此其表征能力有限。同时,RNN擅长捕捉序列的上下文信息,可以进一步增强注意力网络的表达能力。 在论文中,采用双向LSTM (BiLSTM)来处理短文本,由前向网络和后向网络组成: 将每个前向和后向连接起来,得到一个隐藏状态ht。设每个单向LSTM的隐单元数为u。为简单起见,我们将所有hts表示为H∈Rn×2u: 然后,使用缩放点积(scaled dot-product)注意力,这是点积(乘性)注意力的一种变体。目的是学习句子中单词的依赖关系,捕捉句子的内部结构。给定一个由n个查询向量(query vectors)Q∈Rn×2u,keys K∈Rn×2u和values V∈Rn×2u组成的矩阵,缩放点积注意力根据以下等式计算注意力得分: 这里Q、K和V是同一个矩阵,等于H。注意力层的输出是一个矩阵,记为A∈Rn×2u。接下来,使用A上的max-pooling层来获取短文本表示q∈R2u。其思想是在向量的每个维度上选择最高的值来捕获最重要的特征。 知识编码(Knowledge Encoding) 从知识库等外部资源中获取的先验知识为短文本的类别标签判定提供了更丰富的信息。以概念信息为例来说明知识编码,其他先验知识也可以以类似的方式使用。给定一个大小为m的概念集C,记为(c1, c2,… , cm)其中ci是第i个概念向量,目标是产生其向量表示p。首先,引入了两种注意力机制,以更多地关注重要的概念。 为了减少由于实体的歧义性或KBs中的噪声而引入的一些不当概念的不良影响,提出了基于vanilla attention的面向短文本的概念注意力,以衡量第i个概念和短文本表示q之间的语义相似度。使用以下公式来计算C-ST注意力: 这里的αi表示第i个概念对短文本的注意力权重。较大的αi意味着第i个概念与短文本在语义上更相似。F(·)是一个非线性激活函数,如双曲正切变换,softmax用于归一化每个概念的注意力权重。W1∈Rda*(2u+d)是一个权重矩阵,w1∈Rda是一个权重向量,其中da是超参数,b1是偏移量。 此外,为了考虑概念的相对重要性,基于source2token self-attention提出了面向概念集的概念注意力,以衡量每个概念相对于整个概念集的重要性。将每个概念的C-CS注意力定义如下: 其中βi表示第i个概念对整个概念集的注意力权重。W2∈Rdb×d是一个权重矩阵,w2∈Rdb是一个权重向量,其中db是超参数,b2是偏移量。C-CS注意力的效果与特征选择类似。它是一种“软”特征选择,将较大的权重分配给重要概念,而将较小的权重(接近于零)分配给平凡概念。 通过下面的公式组合αi和βi来获得每个概念的最终注意力权重: 这里ai表示第i个概念对短文本的最终注意力权重,γ∈[0,1]是一个软开关,用于调整两个注意力权重αi和βi的重要性。有多种方法可以设置参数γ。最简单的一种是将γ视为一个超参数,并手动调整以获得最佳性能。或者,γ也可以由神经网络自动学习。论文选择后者,因为它在不同的数据集上自适应地为γ分配不同的值,并取得了更好的实验结果。用下面的公式计算γ: 其中向量w和标量b是可学习的参数,σ是sigmoid函数。最后,利用注意力权重计算概念向量的加权和,得到表示概念的语义向量: 一般来说,对于分类来说重要的概念被赋予较大的权重,反之亦然。还发现了模型的一些特性。 首先,它是可解释的。给定一篇短文本及其对应的概念,该模型通过注意力机制得到每个概念对分类的贡献。 其次,对噪声概念具有鲁棒性。例如,如图2a所示,在对短文本进行概念化时,获得了一些不合适的概念,如工业产品,这些概念对分类没有帮助。该模型给这些概念赋予了较小的注意力权重,因为它们与短文本无关,且与短文本几乎没有相似度。 第三,C-CS注意力的效果与特征选择相似。在某种程度上,C-CS注意力是一种“软”特征选择,为不相关的概念分配了较小的权重(几乎为零)。因此,C-CS注意力产生的注意力权重是稀疏的,类似于L1范数正则化。 图2 训练(Training) 为了训练模型,将所有要训练的参数表示为一个集合φ。网络的训练目标是最大化关于φ的对数似然: 其中S是训练短文本集,y是短文本s的正确类别。 总结 论文提出一种基于知识驱动注意力的深度短文本分类方法。融合KBs中的概念信息以增强短文本的表示。为了度量每个概念的重要性,采用两种注意力机制自动获取概念的权重,并将其用于生成概念表示。根据短文本及其相关概念对短文本进行分类。最后,在4个不同任务的数据集上验证了该模型的有效性,结果表明其性能优于当前最先进的方法。未来,可以将在深度神经网络中融入属性值信息,进一步提升短文本分类的性能。研究发现,由于知识库的不完备性,短文本中提到的一些实体缺乏概念。除了概念信息,实体属性及其值也可以作为显式特征注入深度神经网络。 ### Hugging Face-Transformers快速指南 这个快速指南将帮助入门,并展示如何使用pipeline()进行推理,使用AutoClass加载预训练模型和预处理器,并使用PyTorch或TensorFlow快速训练模型。 首先安装必要的库,例如transformers、datasets等,还需要安装深度学习框架,PyTorch或者TensorFlow。 Pipeline pipeline()是使用预训练模型进行推理的最简单方法。可以将pipeline()开箱即用地用于不同模式的许多任务。下面是一些支持的任务: 任务描述Pipeline标识符文本分类为给定的文本序列分配一个标签pipeline(task="sentiment-analysis")文本生成根据给定的提示生成文本pipeline(task="text-generation")命名实体识别为序列中的每个token分配一个标签(人员、组织、位置等)pipeline(task="ner")问答给定上下文和一个问题,从文本中提取答案pipeline(task=“question-answering”)填充掩码(Fill-mask)预测序列中正确的掩码标记pipeline(task=“fill-mask”)摘要生成一系列文本或文档的摘要pipeline(task=“summarization”)翻译将文本从一种语言翻译成另一种语言pipeline(task=“translation”)pipeline()支持的NLP任务 首先创建一个pipeline()实例,并指定要使用它执行的任务。可以在前面提到的任何任务中使用pipeline(),要了解它支持的任务的完整列表,请查看pipeline API参考(pipeline API reference)。在本指南中,将使用pipeline()进行情感分析作为示例: from transformers import pipeline classifier = pipeline("sentiment-analysis") pipeline()下载并缓存用于情感分析的默认预训练模型和分词器。现在可以在目标文本上使用分类器了: classifier("We are very happy to show you the Transformers library.") 输出:[{'label': 'POSITIVE', 'score': 0.9998}] 如果有多个输入,将输入作为列表传递给pipeline(),返回一个字典列表: results = classifier(["We are very happy to show you the Transformers library.", "We hope you don't hate it."]) for result in results: print(f"label: {result['label']}, with score: {round(result['score'], 4)}") 输出: label: POSITIVE, with score: 0.9998 label: NEGATIVE, with score: 0.5309 pipeline()也可以遍历整个数据集,执行任何喜欢的任务。对于这个例子,选择自动语音识别任务: import torch from transformers import pipeline speech_recognizer = pipeline("automatic-speech-recognition", model="facebook/wav2vec2-base-960h") 加载想要迭代的音频数据集(更多细节请参阅Datasets  Quick Start)。例如,加载MInDS-14数据集: from datasets import load_dataset, Audio dataset = load_dataset("PolyAI/minds14", name="en-US", split="train") 需要确保数据集的采样率与facebook/wav2vec2-base-960h训练的采样率相匹配: dataset = dataset.cast_column("audio", Audio(sampling_rate=speech_recognizer.feature_extractor.sampling_rate)) 调用“audio”列时,音频文件会自动加载并重新采样。从前4个样本中提取原始波形数组,并将其作为一个列表传递给pipeline: result = speech_recognizer(dataset[:4]["audio"]) print([d["text"] for d in result]) 输出:['I WOULD LIKE TO SET UP A JOINT ACCOUNT WITH MY PARTNER HOW DO I PROCEED WITH DOING THAT', "FODING HOW I'D SET UP A JOIN TO HET WITH MY WIFE AND WHERE THE AP MIGHT BE", "I I'D LIKE TOY SET UP A JOINT ACCOUNT WITH MY PARTNER I'M NOT SEEING THE OPTION TO DO IT ON THE AP SO I CALLED IN TO GET SOME HELP CAN I JUST DO IT OVER THE PHONE WITH YOU AND GIVE YOU THE INFORMATION OR SHOULD I DO IT IN THE AP AND I'M MISSING SOMETHING UQUETTE HAD PREFERRED TO JUST DO IT OVER THE PHONE OF POSSIBLE THINGS", 'HOW DO I THURN A JOIN A COUNT'] 对于输入较大的大型数据集(如语音或视觉),将需要传递一个生成器而不是列表,以将所有输入加载到内存中。查看pipeline API reference以获取更多信息。 在pipeline中使用其他模型和分词器 pipeline()可以容纳来自Hub的任何模型,因此很容易为其他用例调整pipeline()。例如,如果希望模型能够处理法语文本,则使用Hub上的tags来筛选适当的模型。上面的过滤结果返回一个针对情感分析进行微调的多语言BERT模型,可以将其用于法语文本: model_name = "nlptown/bert-base-multilingual-uncased-sentiment" 使用AutoModelForSequenceClassification和AutoTokenizer来加载预训练模型及其相关的分词器: from transformers import AutoTokenizer, AutoModelForSequenceClassification model = AutoModelForSequenceClassification.from_pretrained(model_name) tokenizer = AutoTokenizer.from_pretrained(model_name) classifier = pipeline("sentiment-analysis", model=model, tokenizer=tokenizer) classifier("Nous sommes très heureux de vous présenter la bibliothèque Transformers.") 输出:[{'label': '5 stars', 'score': 0.7273}] 如果找不到适合用例的模型,需要在相关的数据上微调一个预训练的模型。可参考微调(finetuning)教程来学习如何操作。最后,在对预训练模型进行微调后,可以考虑将模型分享(sharing )在Hub上的社区。 AutoClass 在底层,AutoModelForSequenceClassification和AutoTokenizer类一起工作,为上面使用的pipeline()提供动力。AutoClass是一种快捷方式,可以从名称或路径自动检索预训练模型的体系结构。只需要为任务选择适当的AutoClass及其相关的预处理类。回到上一节的例子,使用AutoClass来复制pipeline()的结果。 AutoTokenizer 分词器(tokenizer)负责将文本预处理为数字数组,作为模型的输入。有多种规则管理分词过程,包括如何切分单词以及应该在什么级别切分单词(在tokenizer summary中了解有关分词的更多信息)。要记住的最重要的事情是,需要实例化一个具有相同模型名称的分词器,以确保使用的是与预训练模型相同的分词规则。使用AutoTokenizer加载一个分词器: from transformers import AutoTokenizer model_name = "nlptown/bert-base-multilingual-uncased-sentiment" tokenizer = AutoTokenizer.from_pretrained(model_name) encoding = tokenizer("We are very happy to show you the Transformers library.") print(encoding) 输出: {'input_ids': [101, 11312, 10320, 12495, 19308, 10114, 11391, 10855, 10103, 100, 58263, 13299, 119, 102], 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]} 分词器返回一个字典,其中包含:input_ids是tokens的数字表示;Attention_mask表示应该处理哪些tokens。分词器还可以接受输入列表,并对文本进行填充和截断,以返回一个长度相同的批处理: pt_batch = tokenizer( ["We are very happy to show you the Transformers library.", "We hope you don't hate it."], padding=True, truncation=True, max_length=512, return_tensors="pt", ) 查看预处理(preprocess)教程了解有关tokenization的更多详细信息,以及如何使用AutoImageProcessor、 AutoFeatureExtractor和AutoProcessor预处理图像、音频和多模态输入。 AutoModel Transformers提供了一种简单统一的方法来加载预训练实例。可以像加载AutoTokenizer一样加载AutoModel。唯一的区别是为任务选择正确的AutoModel。有关AutoModel类支持的任务,请参阅task summary。对于文本(或序列)分类,应该加载AutoModelForSequenceClassification: from transformers import AutoModelForSequenceClassification model_name = "nlptown/bert-base-multilingual-uncased-sentiment" pt_model = AutoModelForSequenceClassification.from_pretrained(model_name) 现在将预处理的批量输入直接传递给模型。只需要通过添加**来解压字典: pt_outputs = pt_model(**pt_batch) 该模型在logits属性中输出最终激活值。对logits应用softmax函数来获取概率: from torch import nn pt_predictions = nn.functional.softmax(pt_outputs.logits, dim=-1) print(pt_predictions) 输出: tensor([[0.0021, 0.0018, 0.0115, 0.2121, 0.7725], [0.2084, 0.1826, 0.1969, 0.1755, 0.2365]], grad_fn=) 所有Transformers模型(PyTorch或TensorFlow)在最终激活函数(如softmax)之前输出张量,因为最终激活函数通常与损失融合。模型输出是特殊的数据类,因此它们的属性在IDE中自动补全。模型的输出类似于元组或字典(可以用整数、切片或字符串进行索引),在这种情况下,None属性将被忽略。 保存模型 一旦模型进行了微调,可以使用PreTrainedModel.save_pretrained()将其与分词器一起保存: pt_save_directory = "./pt_save_pretrained" tokenizer.save_pretrained(pt_save_directory) pt_model.save_pretrained(pt_save_directory) 当准备再次使用模型时,使用PreTrainedModel.from_pretrained()重新加载: pt_model = AutoModelForSequenceClassification.from_pretrained("./pt_save_pretrained") 一个特别酷的Transformers功能是能够保存模型并将其重新加载为PyTorch或TensorFlow模型。from_pt或from_tf参数可以将模型从一个框架转换为另一个框架: from transformers import AutoModel tokenizer = AutoTokenizer.from_pretrained(tf_save_directory) pt_model = AutoModelForSequenceClassification.from_pretrained(tf_save_directory, from_tf=True) 自定义模型构建 可以修改模型的配置类以更改模型的构建方式。配置指定模型的属性,如隐藏层或注意力头的数量。当从自定义配置类初始化模型时,将从头开始。模型属性是随机初始化的,在使用模型得到有意义的结果之前,需要对模型进行训练。首先导入AutoConfig,然后加载要修改的预训练模型。在AutoConfig.from_pretrained()中,可以指定要更改的属性,例如注意力头的数量: from transformers import AutoConfig, AutoModel my_config = AutoConfig.from_pretrained("distilbert-base-uncased", n_heads=12) my_model = AutoModel.from_config(my_config) Trainer——PyTorch优化的训练循环 所有模型都是标准的torch.nn.Module,因此可以在任何典型的训练循环中使用它们。虽然你可以编写自己的训练循环,但Transformers为PyTorch提供了Trainer类,其中包含基本的训练循环,并添加了额外的功能,如分布式训练、混合精度等。根据任务的不同,通常会将以下参数传递给Trainer: 1、A PreTrainedModel or a torch.nn.Module from transformers import AutoModelForSequenceClassification model = AutoModelForSequenceClassification.from_pretrained("distilbert-base-uncased") 2、TrainingArguments包含可以更改的模型超参数,如学习率、批大小和要训练的epoch数量。如果不指定任何训练参数,则使用默认值: from transformers import TrainingArguments training_args = TrainingArguments( output_dir="path/to/save/folder/", learning_rate=2e-5, per_device_train_batch_size=8, per_device_eval_batch_size=8, num_train_epochs=2, ) 3、预处理类,如分词器、图像处理器、特征提取器或处理器: from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased") 4、加载数据 from datasets import load_dataset dataset = load_dataset("rotten_tomatoes") # doctest: +IGNORE_RESULT 5、创建一个函数对数据集进行分词,并使用map将其应用到整个数据集上: def tokenize_dataset(dataset): return tokenizer(dataset["text"]) dataset = dataset.map(tokenize_dataset, batched=True) 6、使用DataCollatorWithPadding从数据集中创建一批示例: from transformers import DataCollatorWithPadding data_collator = DataCollatorWithPadding(tokenizer=tokenizer) 在Trainer中使用所有这些类: from transformers import Trainer trainer = Trainer( model=model, args=training_args, train_dataset=dataset["train"], eval_dataset=dataset["test"], tokenizer=tokenizer, data_collator=data_collator, ) # doctest: +SKIP 调用train()开始训练: trainer.train() 对于使用序列到序列模型的任务(如翻译或摘要),请使用Seq2SeqTrainer和Seq2SeqTrainingArguments类。 可以通过继承Trainer中的方法来自定义训练循环。允许自定义特性,如损失函数、优化器和调度器(scheduler)。请查看 Trainer参考文档,了解哪些方法可以被子类化。 另一种定制训练循环的方法是使用回调(Callbacks)。您可以使用回调与其他库集成,并检查训练循环以报告进度或提前停止训练。回调不会修改训练循环本身的任何内容。如要自定义类似损失函数,需要继承Trainer。 原文链接 https://huggingface.co/docs/transformers/quicktour ### 项目整合管理(一) 项目整合管理包括对隶属于项目管理过程组的各种过程和项目管理活动进行识别、定义、组合、统一和协调的各个过程。项目整合管理过程包括: 制定项目章程 编写一份正式批准项目并授权项目经理在项目活动中使用组织资源的文件的过程。本过程的主要作用是,明确项目与组织战略目标之间的直接联系,确立项目的正式地位,并展示组织对项目的承诺。本过程仅开展一次或仅在项目的预定义点开展。 制定项目章程:输入、工具与技术和输出 制定项目管理计划 定义、准备和协调项目计划的所有组成部分,并把它们整合为一份综合项目管理计划的过程。本过程的主要作用是,生成一份综合文件,用于确定所有项目工作的基础及其执行方式,它仅开展一次或仅在项目的预定义点开展。 制定项目管理计划:输入、工具与技术和输出 项目管理计划是说明项目执行、监控和收尾方式的一份文件,它整合并综合了所有子管理计划和基准,以及管理项目所需的其他信息。究竟需要哪些项目管理计划组件,取决于具体项目的需求。项目管理组件包括(但不限于): 项目管理计划是用于管理项目的主要文件之一。管理项目时还会使用其他项目文件。这些其他文件不属于项目管理计划,但它们也是实现高效管理所必需的文件。 项目管理计划项目文件项目文件1、范围管理计划1、活动属性19、质量控制测量结果2、需求管理计划2、活动清单20、质量测量指标3、进度管理计划3、假设日志21、质量报告4、成本管理计划4、估算依据22、需求文件5、质量管理计划5、变更日志23、需求跟踪矩阵6、资源管理计划6、成本估算24、资源分解结构7、沟通管理计划7、成本预测25、资源日历8、风险管理计划8、持续时间估算26、资源需求9、采购管理计划9、问题日志27、风险登记册10、相关方参与计划10、经验教训登记册28、风险报告11、变更管理计划11、里程碑清单29、进度数据12、配置管理计划12、实物资源分配单30、进度预测13、范围基准13、项目日历31、相关方登记册14、进度基准14、项目沟通记录32、团队章程15、成本基准15、项目进度计划33、测试与评估文件16、绩效测量基准16、项目进度网络图17、项目生命周期描述17、项目范围说明书18、开发方法18、项目团队派工单项目管理计划和项目文件 指导与管理项目工作 为实现项目目标而领导和执行项目管理计划中所确定的工作,并实施已批准变更的过程。本过程的主要作用是,对项目工作和可交付成果开展综合管理,以提高项目成功的可能性。本过程需要在整个项目期间开展。 指导与管理项目工作:输入、工具与技术和输出 管理项目知识 使用现有知识并生成新知识,以实现项目目标,并且帮助组织学习的过程。本过程的主要作用是,利用已有的组织知识来创造或改进项目成果,并且使当前项目创造的知识可用于支持组织运营和未来的项目或阶段。本过程需要在整个项目期间开展。 项目管理知识:输入、工具与技术和输出 ### BERT的动态量化 简介 使用HuggingFace Transformers示例(the HuggingFace Transformers examples)中的BERT模型,在BERT模型上应用动态量化。通过这一步一步的过程,演示如何将BERT转换为动态量化模型。 BERT (Bidirectional Embedding Representations from Transformers)是一种预训练语言表示的方法,它在许多流行的自然语言处理(NLP)任务上取得了最先进的精度结果,如问答、文本分类等。原始论文BERT。 PyTorch 中的动态量化支持将浮点模型转换为量化模型,其中权重为静态 int8 或 float16 数据类型,激活为动态量化。当权重量化为 int8 时,激活被动态量化(每batch)为 int8。在PyTorch中,torch.quantization.quantize_dynamic API,将指定的模块替换为动态仅权重量化的版本,并输出量化模型。 在通用语言理解评估基准(GLUE)中的Microsoft Research Paraphrase Corpus (MRPC) task上展示了准确性和推理性能结果。MRPC (Dolan and Brockett, 2005)是一个自动从在线新闻源中提取的句子对语料库,并配有人工标注,以判断句子对中的句子是否语义等价。由于类别是不平衡的(正样本为68%,负样本为32%),通常的做法是使用F1指标评估。MRPC是语言对分类的一个常见NLP任务,如下图所示。 安装 安装PyTorch和HuggingFace Transformers 首先按照 PyTorch 和 HuggingFace Github Repo 中的安装说明进行操作,也可以参考之前的博客《PyTorch安装》。此外,还安装了scikit-learn包,使用其内置的 F1 分数计算辅助函数。 pip install sklearn pip install transformers 导入必要的模块 from __future__ import absolute_import, division, print_function import logging import numpy as np import os import random import sys import time import torch from argparse import Namespace from torch.utils.data import (DataLoader, RandomSampler, SequentialSampler, TensorDataset) from tqdm import tqdm from transformers import (BertConfig, BertForSequenceClassification, BertTokenizer,) from transformers import glue_compute_metrics as compute_metrics from transformers import glue_output_modes as output_modes from transformers import glue_processors as processors from transformers import glue_convert_examples_to_features as convert_examples_to_features # Setup logging logger = logging.getLogger(__name__) logging.basicConfig(format = '%(asctime)s - %(levelname)s - %(name)s - %(message)s', datefmt = '%m/%d/%Y %H:%M:%S', level = logging.WARN) logging.getLogger("transformers.modeling_utils").setLevel( logging.WARN) # Reduce logging print(torch.__version__) 设置线程数来比较FP32和INT8性能之间的单线程性能。 torch.set_num_threads(1) print(torch.__config__.parallel_info()) helper函数 transformers库内置了辅助函数。主要使用以下辅助函数:一个用于将文本示例转换为特征向量;另一个用于测量预测结果的F1分数。 glue_convert_examples_to_features函数将文本转换为输入特征: 标记输入序列; 在开头插入[CLS]; 在第一句和第二句之间,以及最后插入[SEP]; 生成 token type ids 来指示一个 token 是属于第一个序列还是属于第二个序列。 glue_compute_metrics函数可以计算指标的F1分数,它可以解释为准确率和召回率的加权平均值,其中F1分数在1时达到最佳值,在0时达到最差值。精确率和召回率对F1分数的相对贡献是相等的。 下载数据集 在运行MRPC任务之前,先运行脚本download_glue_data.py下载GLUE data ,然后解压到glue_data目录下。 目前的download_glue_data.py文件会报错加入下面三行代码即可(参考原文评论区中的bug修复) import io URLLIB = urllib.request 'MRPC':'https://raw.githubusercontent.com/MegEngine/Models/master/official/nlp/bert/glue_data/MRPC/dev_ids.tsv' inside the TASK2PATH dict python download_glue_data.py --data_dir='glue_data' --tasks='MRPC' 微调BERT模型 将使用预训练的BERT模型进行微调,在MRPC任务上对语义等价的句子对进行分类。为MRPC任务微调的预训练BERT模型(HuggingFace transformers中的bert-base-uncase模型),https://github.com/huggingface/transformers/tree/main/examples#mrpc export GLUE_DIR=./glue_data export TASK_NAME=MRPC export OUT_DIR=./$TASK_NAME/ python ./run_glue.py \ --model_type bert \ --model_name_or_path bert-base-uncased \ --task_name $TASK_NAME \ --do_train \ --do_eval \ --do_lower_case \ --data_dir $GLUE_DIR/$TASK_NAME \ --max_seq_length 128 \ --per_gpu_eval_batch_size=8 \ --per_gpu_train_batch_size=8 \ --learning_rate 2e-5 \ --num_train_epochs 3.0 \ --save_steps 100000 \ --output_dir $OUT_DIR 针对MRPC任务,微调BERT模型。为了节省时间,可以将模型文件(约400 MB)直接下载到本地文件夹$OUT_DIR中。 设置全局配置 设置用于在动态量化前后评估微调BERT模型的全局配置。 configs = Namespace() # The output directory for the fine-tuned model, $OUT_DIR. configs.output_dir = "./MRPC/" # The data directory for the MRPC task in the GLUE benchmark, $GLUE_DIR/$TASK_NAME. configs.data_dir = "./glue_data/MRPC" # The model name or path for the pre-trained model. configs.model_name_or_path = "bert-base-uncased" # The maximum length of an input sequence configs.max_seq_length = 128 # Prepare GLUE task. configs.task_name = "MRPC".lower() configs.processor = processors[configs.task_name]() configs.output_mode = output_modes[configs.task_name] configs.label_list = configs.processor.get_labels() configs.model_type = "bert".lower() configs.do_lower_case = True # Set the device, batch size, topology, and caching flags. configs.device = "cpu" configs.per_gpu_eval_batch_size = 8 configs.n_gpu = 0 configs.local_rank = -1 configs.overwrite_cache = False # Set random seed for reproducibility. def set_seed(seed): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) set_seed(42) 加载微调的BERT模型 从config .output_dir加载tokenizer和经过微调的BERT序列分类器模型(FP32)。 tokenizer = BertTokenizer.from_pretrained( configs.output_dir, do_lower_case=configs.do_lower_case) model = BertForSequenceClassification.from_pretrained(configs.output_dir) model.to(configs.device) 定义tokenize和evaluation函数 使用了Huggingface的tokenize和evaluation函数 def evaluate(args, model, tokenizer, prefix=""): # Loop to handle MNLI double evaluation (matched, mis-matched) eval_task_names = ("mnli", "mnli-mm") if args.task_name == "mnli" else (args.task_name,) eval_outputs_dirs = (args.output_dir, args.output_dir + '-MM') if args.task_name == "mnli" else (args.output_dir,) results = {} for eval_task, eval_output_dir in zip(eval_task_names, eval_outputs_dirs): eval_dataset = load_and_cache_examples(args, eval_task, tokenizer, evaluate=True) if not os.path.exists(eval_output_dir) and args.local_rank in [-1, 0]: os.makedirs(eval_output_dir) args.eval_batch_size = args.per_gpu_eval_batch_size * max(1, args.n_gpu) # Note that DistributedSampler samples randomly eval_sampler = SequentialSampler(eval_dataset) if args.local_rank == -1 else DistributedSampler(eval_dataset) eval_dataloader = DataLoader(eval_dataset, sampler=eval_sampler, batch_size=args.eval_batch_size) # multi-gpu eval if args.n_gpu > 1: model = torch.nn.DataParallel(model) # Eval! logger.info("***** Running evaluation {} *****".format(prefix)) logger.info(" Num examples = %d", len(eval_dataset)) logger.info(" Batch size = %d", args.eval_batch_size) eval_loss = 0.0 nb_eval_steps = 0 preds = None out_label_ids = None for batch in tqdm(eval_dataloader, desc="Evaluating"): model.eval() batch = tuple(t.to(args.device) for t in batch) with torch.no_grad(): inputs = {'input_ids': batch[0], 'attention_mask': batch[1], 'labels': batch[3]} if args.model_type != 'distilbert': inputs['token_type_ids'] = batch[2] if args.model_type in ['bert', 'xlnet'] else None # XLM, DistilBERT and RoBERTa don't use segment_ids outputs = model(**inputs) tmp_eval_loss, logits = outputs[:2] eval_loss += tmp_eval_loss.mean().item() nb_eval_steps += 1 if preds is None: preds = logits.detach().cpu().numpy() out_label_ids = inputs['labels'].detach().cpu().numpy() else: preds = np.append(preds, logits.detach().cpu().numpy(), axis=0) out_label_ids = np.append(out_label_ids, inputs['labels'].detach().cpu().numpy(), axis=0) eval_loss = eval_loss / nb_eval_steps if args.output_mode == "classification": preds = np.argmax(preds, axis=1) elif args.output_mode == "regression": preds = np.squeeze(preds) result = compute_metrics(eval_task, preds, out_label_ids) results.update(result) output_eval_file = os.path.join(eval_output_dir, prefix, "eval_results.txt") with open(output_eval_file, "w") as writer: logger.info("***** Eval results {} *****".format(prefix)) for key in sorted(result.keys()): logger.info(" %s = %s", key, str(result[key])) writer.write("%s = %s\n" % (key, str(result[key]))) return results def load_and_cache_examples(args, task, tokenizer, evaluate=False): if args.local_rank not in [-1, 0] and not evaluate: torch.distributed.barrier() # Make sure only the first process in distributed training process the dataset, and the others will use the cache processor = processors[task]() output_mode = output_modes[task] # Load data features from cache or dataset file cached_features_file = os.path.join(args.data_dir, 'cached_{}_{}_{}_{}'.format( 'dev' if evaluate else 'train', list(filter(None, args.model_name_or_path.split('/'))).pop(), str(args.max_seq_length), str(task))) if os.path.exists(cached_features_file) and not args.overwrite_cache: logger.info("Loading features from cached file %s", cached_features_file) features = torch.load(cached_features_file) else: logger.info("Creating features from dataset file at %s", args.data_dir) label_list = processor.get_labels() if task in ['mnli', 'mnli-mm'] and args.model_type in ['roberta']: # HACK(label indices are swapped in RoBERTa pretrained model) label_list[1], label_list[2] = label_list[2], label_list[1] examples = processor.get_dev_examples(args.data_dir) if evaluate else processor.get_train_examples(args.data_dir) features = convert_examples_to_features(examples, tokenizer, label_list=label_list, max_length=args.max_seq_length, output_mode=output_mode, pad_on_left=bool(args.model_type in ['xlnet']), # pad on the left for xlnet pad_token=tokenizer.convert_tokens_to_ids([tokenizer.pad_token])[0], pad_token_segment_id=4 if args.model_type in ['xlnet'] else 0, ) if args.local_rank in [-1, 0]: logger.info("Saving features into cached file %s", cached_features_file) torch.save(features, cached_features_file) if args.local_rank == 0 and not evaluate: torch.distributed.barrier() # Make sure only the first process in distributed training process the dataset, and the others will use the cache # Convert to Tensors and build dataset all_input_ids = torch.tensor([f.input_ids for f in features], dtype=torch.long) all_attention_mask = torch.tensor([f.attention_mask for f in features], dtype=torch.long) all_token_type_ids = torch.tensor([f.token_type_ids for f in features], dtype=torch.long) if output_mode == "classification": all_labels = torch.tensor([f.label for f in features], dtype=torch.long) elif output_mode == "regression": all_labels = torch.tensor([f.label for f in features], dtype=torch.float) dataset = TensorDataset(all_input_ids, all_attention_mask, all_token_type_ids, all_labels) return dataset 应用动态量化 调用为torch.quantization.quantize_dynamic对模型进行量化,对HuggingFace BERT模型进行动态量化。具体地说:1)指定模型中的torch.nn.Linear模块被量化;2)指定要将权重转换为量化的int8值。 quantized_model = torch.quantization.quantize_dynamic( model, {torch.nn.Linear}, dtype=torch.qint8 ) print(quantized_model) 部分输出如下: 检查模型尺寸 先检查一下模型大小。可以观察到模型大小的显著减少(FP32总大小:438 MB;INT8总大小:181 MB) def print_size_of_model(model): torch.save(model.state_dict(), "temp.p") print('Size (MB):', os.path.getsize("temp.p")/1e6) os.remove('temp.p') print_size_of_model(model) print_size_of_model(quantized_model) 使用的BERT模型(bert-base-uncased)的词汇表大小V为30522。当嵌入(embedding)大小为768时,词嵌入表的总大小约为4 (Bytes/FP32)* 30522 * 768 = 90 MB,因此借助量化,将非嵌入表部分的模型大小从350 MB (FP32模型)减小到90 MB (INT8模型)。 在MRPC任务上对微调后的BERT模型应用训练后动态量化后,我们的F1分数准确率降低了0.6%。作为对比,在论文Q8BERT: Quantized 8Bit BERT(表1)中,通过应用训练后动态量化,达到了0.8788,通过应用量化感知训练,达到了0.8956。主要的区别是torch支持PyTorch中的非对称量化,而论文Q8BERT只支持对称量化。 表1 为了进行单线程比较,将线程数设置为1。还支持这些量化的INT8运算符的操作内并行化。可以通过 torch.set_num_threads(N) 设置多线程(N是intra-op并行化线程的数量)。启用intra-op并行化支持的一个初步要求是使用正确的后端,如OpenMP、Native或TBB构建PyTorch。可以使用 torch.__config__.parallel_info() 来检查并行化设置。 评估推理的准确性和时间 接下来,比较原始FP32模型和动态量化后的INT8模型的推理时间和评估精度。 def time_model_evaluation(model, configs, tokenizer): eval_start_time = time.time() result = evaluate(configs, model, tokenizer, prefix="") eval_end_time = time.time() eval_duration_time = eval_end_time - eval_start_time print(result) print("Evaluate total time (seconds): {0:.1f}".format(eval_duration_time)) # Evaluate the original FP32 BERT model time_model_evaluation(model, configs, tokenizer) # Evaluate the INT8 BERT model after the dynamic quantization time_model_evaluation(quantized_model, configs, tokenizer) PrecF1 scoreModel Size1 thread4 threadsFP320.9019438 MB94.922.2INT80.8977181 MB49.412.7量化前后对比结果 序列化量化模型 在跟踪模型之后,可以使用torch.jit.save序列化并保存量化模型以供将来使用。 input_ids = ids_tensor([8, 128], 2) token_type_ids = ids_tensor([8, 128], 2) attention_mask = ids_tensor([8, 128], vocab_size=2) dummy_input = (input_ids, attention_mask, token_type_ids) traced_model = torch.jit.trace(quantized_model, dummy_input) torch.jit.save(traced_model, "bert_traced_eager_quant.pt") 要加载量化模型,可以使用torch.jit.load loaded_quantized_model = torch.jit.load("bert_traced_eager_quant.pt") 原文链接 https://pytorch.org/tutorials/intermediate/dynamic_quantization_bert_tutorial.html#fine-tune-the-bert-model ### LSTM的动态量化 简介 量化涉及将模型的权重和激活值从float转换为int,这可以使模型大小跟小和推理的速度更快,而只损失很小的精度。 使用PyTorch示例中的单词语言模型(word language model),将最简单的量化形式,动态量化(dynamic quantization)应用于基于LSTM的下一个单词预测模型。 # imports import os from io import open import time import torch import torch.nn as nn import torch.nn.functional as F 定义模型 按照单词语言模型示例中的模型(model)定义LSTM模型架构。 class LSTMModel(nn.Module): """Container module with an encoder, a recurrent module, and a decoder.""" def __init__(self, ntoken, ninp, nhid, nlayers, dropout=0.5): super(LSTMModel, self).__init__() self.drop = nn.Dropout(dropout) self.encoder = nn.Embedding(ntoken, ninp) self.rnn = nn.LSTM(ninp, nhid, nlayers, dropout=dropout) self.decoder = nn.Linear(nhid, ntoken) self.init_weights() self.nhid = nhid self.nlayers = nlayers def init_weights(self): initrange = 0.1 self.encoder.weight.data.uniform_(-initrange, initrange) self.decoder.bias.data.zero_() self.decoder.weight.data.uniform_(-initrange, initrange) def forward(self, input, hidden): emb = self.drop(self.encoder(input)) output, hidden = self.rnn(emb, hidden) output = self.drop(output) decoded = self.decoder(output) return decoded, hidden def init_hidden(self, bsz): weight = next(self.parameters()) return (weight.new_zeros(self.nlayers, bsz, self.nhid), weight.new_zeros(self.nlayers, bsz, self.nhid)) 加载文本数据 接下来,再次按照单词语言模型示例的预处理(preprocessing)过程,将Wikitext-2数据集(Wikitext-2 dataset)加载到语料库中。 class Dictionary(object): def __init__(self): self.word2idx = {} self.idx2word = [] def add_word(self, word): if word not in self.word2idx: self.idx2word.append(word) self.word2idx[word] = len(self.idx2word) - 1 return self.word2idx[word] def __len__(self): return len(self.idx2word) class Corpus(object): def __init__(self, path): self.dictionary = Dictionary() self.train = self.tokenize(os.path.join(path, 'train.txt')) self.valid = self.tokenize(os.path.join(path, 'valid.txt')) self.test = self.tokenize(os.path.join(path, 'test.txt')) def tokenize(self, path): """Tokenizes a text file.""" assert os.path.exists(path) # Add words to the dictionary with open(path, 'r', encoding="utf8") as f: for line in f: words = line.split() + [''] for word in words: self.dictionary.add_word(word) # Tokenize file content with open(path, 'r', encoding="utf8") as f: idss = [] for line in f: words = line.split() + [''] ids = [] for word in words: ids.append(self.dictionary.word2idx[word]) idss.append(torch.tensor(ids).type(torch.int64)) ids = torch.cat(idss) return ids model_data_filepath = 'data/' corpus = Corpus(model_data_filepath + 'wikitext-2') 加载预训练模型 动态量化是一种在模型训练后应用的量化技术。因此,将一些预训练权重加载到该模型架构中;这些权重是在单词语言模型示例中使用默认设置进行5个epoch的训练得到的。 ntokens = len(corpus.dictionary) model = LSTMModel( ntoken = ntokens, ninp = 512, nhid = 256, nlayers = 5, ) model.load_state_dict( torch.load( model_data_filepath + 'word_language_model_quantize.pth', map_location=torch.device('cpu') ) ) model.eval() print(model) 生成一些文本以确保预训练模型正常工作(here) input_ = torch.randint(ntokens, (1, 1), dtype=torch.long) hidden = model.init_hidden(1) temperature = 1.0 num_words = 1000 with open(model_data_filepath + 'out.txt', 'w') as outf: with torch.no_grad(): # no tracking history for i in range(num_words): output, hidden = model(input_, hidden) word_weights = output.squeeze().div(temperature).exp().cpu() word_idx = torch.multinomial(word_weights, 1)[0] input_.fill_(word_idx) word = corpus.dictionary.idx2word[word_idx] outf.write(str(word.encode('utf-8')) + ('\n' if i % 20 == 19 else ' ')) if i % 100 == 0: print('| Generated {}/{} words'.format(i, 1000)) with open(model_data_filepath + 'out.txt', 'r') as outf: all_output = outf.read() print(all_output) 只需要再定义几个辅助函数: bptt = 25 criterion = nn.CrossEntropyLoss() eval_batch_size = 1 # create test data set def batchify(data, bsz): # Work out how cleanly we can divide the dataset into bsz parts. nbatch = data.size(0) // bsz # Trim off any extra elements that wouldn't cleanly fit (remainders). data = data.narrow(0, 0, nbatch * bsz) # Evenly divide the data across the bsz batches. return data.view(bsz, -1).t().contiguous() test_data = batchify(corpus.test, eval_batch_size) # Evaluation functions def get_batch(source, i): seq_len = min(bptt, len(source) - 1 - i) data = source[i:i+seq_len] target = source[i+1:i+1+seq_len].reshape(-1) return data, target def repackage_hidden(h): """Wraps hidden states in new Tensors, to detach them from their history.""" if isinstance(h, torch.Tensor): return h.detach() else: return tuple(repackage_hidden(v) for v in h) def evaluate(model_, data_source): # Turn on evaluation mode which disables dropout. model_.eval() total_loss = 0. hidden = model_.init_hidden(eval_batch_size) with torch.no_grad(): for i in range(0, data_source.size(0) - 1, bptt): data, targets = get_batch(data_source, i) output, hidden = model_(data, hidden) hidden = repackage_hidden(hidden) output_flat = output.view(-1, ntokens) total_loss += len(data) * criterion(output_flat, targets).item() return total_loss / (len(data_source) - 1) 测试动态量化 最后,可以在模型上调用torch.quantization.quantize_dynamic,具体地: 在模型中指定nn.LSTM和nn.Linear模块被量化 指定将权重转换为int8值 import torch.quantization quantized_model = torch.quantization.quantize_dynamic( model, {nn.LSTM, nn.Linear}, dtype=torch.qint8 ) print(quantized_model) 量化前后模型输出的区别 量化前: LSTMModel( (drop): Dropout(p=0.5, inplace=False) (encoder): Embedding(33278, 512) (rnn): LSTM(512, 256, num_layers=5, dropout=0.5) (decoder): Linear(in_features=256, out_features=33278, bias=True)) 量化后: LSTMModel( (drop): Dropout(p=0.5, inplace=False) (encoder): Embedding(33278, 512) (rnn): DynamicQuantizedLSTM(512, 256, num_layers=5, dropout=0.5) (decoder): DynamicQuantizedLinear(in_features=256, out_features=33278, dtype=torch.qint8, qscheme=torch.per_tensor_affine)) 首先,看到模型大小显著减少: def print_size_of_model(model): torch.save(model.state_dict(), "temp.p") print('Size (MB):', os.path.getsize("temp.p")/1e6) os.remove('temp.p') print_size_of_model(model) print_size_of_model(quantized_model) 量化前后模型大小,量化前Size (MB): 113.943637,量化后Size (MB): 79.738057 其次,看到推理时间更快,评估损失没有差异。对于单线程比较,将线程数设置为1,因为量化模型单线程运行。 torch.set_num_threads(1) def time_model_evaluation(model, test_data): s = time.time() loss = evaluate(model, test_data) elapsed = time.time() - s print('''loss: {0:.3f}\nelapsed time (seconds): {1:.1f}'''.format(loss, elapsed)) time_model_evaluation(model, test_data) time_model_evaluation(quantized_model, test_data) 输出结果 loss: 5.167elapsed time (seconds): 211.1loss: 5.168elapsed time (seconds): 111.0 原文链接 https://pytorch.org/tutorials/advanced/dynamic_quantization_tutorial.html https://github.com/pytorch/examples/tree/main/word_language_model ### PyTorch安装 创建环境 conda create -n env_name python=3.x 可以先使用conda create创建一个虚拟环境,env_name为虚拟环境的名字,3.x为python的版本,例如conda create -n pytorch python=3.8 使用conda activate pytorch激活虚拟环境,conda deactivate退出环境。 CUDA版本 首先查看cuda的版本,nvidia-smi cuda版本为10.2 官方安装 官方网站:https://pytorch.org/get-started/locally/ 在官方网站点选需求可以显示安装的命令,如: 由于没有需要的CUDA版本,因此在以前的版本中寻找https://pytorch.org/get-started/previous-versions/ 例如需要安装v1.12.1版本,使用Conda,Linux 或者Windows操作系统,使用如下命令: # CUDA 10.2 conda install pytorch==1.12.1 torchvision==0.13.1 torchaudio==0.12.1 cudatoolkit=10.2 -c pytorch 使用上述方式安装的torch版本如下: pytorch的后缀带有py3.8_cuda10.2_cudnn7.6.5_0;torchaudio、torchvision的后缀带有py38_cu102。选择y之后就会自动安装。 或者如果使用Wheel,Linux或者Windows操作系统,使用如下命令: # CUDA 10.2 pip install torch==1.12.1+cu102 torchvision==0.13.1+cu102 torchaudio==0.12.1 --extra-index-url https://download.pytorch.org/whl/cu102 但是官方网站安装pytorch的速度较慢,因此可以选择镜像安装。把-c pytorch表示的pytorch源,更改为国内的镜像。 镜像安装 镜像地址 清华大学:https://mirrors.tuna.tsinghua.edu.cn/ 阿里云:http://mirrors.aliyun.com/ 镜像地址可以参考https://blog.csdn.net/djzhao627/article/details/122999240 conda install pytorch==1.12.1 torchvision==0.13.1 torchaudio==0.12.1 cudatoolkit=10.2 -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/linux-64/ 检验 import torch print(torch.__version__) print(torch.version.cuda) print(torch.backends.cudnn.version()) torch.cuda.is_available() # cuda是否可用; torch.cuda.device_count() # 返回gpu数量; torch.cuda.get_device_name(0) # 返回gpu名字,设备索引默认从0开始; torch.cuda.current_device() # 返回当前设备索引 ### PyTorch数据加载(二) 处理数据样本的代码可能会混乱且难以维护;理想情况下,希望数据集代码与模型训练代码解耦,以获得更好的可读性和模块化。PyTorch提供了两个数据基元torch.utils.data.DataLoader 和 torch.utils.data.Dataset,它们允许使用预加载的数据集以及自己的数据。Dataset存储样本及其对应的标签,DataLoader在Dataset周围包装了一个可迭代对象,以方便访问样本。 PyTorch提供了许多预加载的数据集(FashionMNIST),它们是torch.utils.data.Dataset的子类,并实现了特定于特定数据的函数。它们可以用于模型的原型和基准测试。可以在这里找到它们:图像数据集(Image Datasets),文本数据集(Text Datasets)和音频数据集(Audio Datasets)。 加载数据 这是一个如何从TorchVision加载Fashion-MNIST数据集的示例。Fashion-MNIST是Zalando的文章图像数据集,包含60,000个训练示例和10,000个测试示例。每个示例包含一个28×28灰度图像和10个类别之一的关联标签。 用以下参数加载FashionMNIST数据集: root存储训练/测试数据的路径,train指定训练或测试数据集,download=True如果根目录下不可用,则从互联网上下载数据。transform和target_transform指定了特征变换和标签变换 import torch from torch.utils.data import Dataset from torchvision import datasets from torchvision.transforms import ToTensor import matplotlib.pyplot as plt training_data = datasets.FashionMNIST( root="data", train=True, download=True, transform=ToTensor() ) test_data = datasets.FashionMNIST( root="data", train=False, download=True, transform=ToTensor() ) 迭代和可视化数据集 可以像列表一样手动索引数据集:training_data[index]。使用matplotlib来可视化训练数据中的一些样本。 labels_map = { 0: "T-Shirt", 1: "Trouser", 2: "Pullover", 3: "Dress", 4: "Coat", 5: "Sandal", 6: "Shirt", 7: "Sneaker", 8: "Bag", 9: "Ankle Boot", } figure = plt.figure(figsize=(8, 8)) cols, rows = 3, 3 for i in range(1, cols * rows + 1): sample_idx = torch.randint(len(training_data), size=(1,)).item() img, label = training_data[sample_idx] figure.add_subplot(rows, cols, i) plt.title(labels_map[label]) plt.axis("off") plt.imshow(img.squeeze(), cmap="gray") plt.show() 为文件创建自定义数据集 一个自定义数据集类必须实现三个函数:__init__、__len__和__getitem__。FashionMNIST图像存储在目录img_dir中,它们的标签单独存储在CSV文件annotations_file中。 import os import pandas as pd from torchvision.io import read_image class CustomImageDataset(Dataset): def __init__(self, annotations_file, img_dir, transform=None, target_transform=None): self.img_labels = pd.read_csv(annotations_file) self.img_dir = img_dir self.transform = transform self.target_transform = target_transform def __len__(self): return len(self.img_labels) def __getitem__(self, idx): img_path = os.path.join(self.img_dir, self.img_labels.iloc[idx, 0]) image = read_image(img_path) label = self.img_labels.iloc[idx, 1] if self.transform: image = self.transform(image) if self.target_transform: label = self.target_transform(label) return image, label __init__ __init__函数在实例化Dataset对象时只运行一次。初始化包含图像、标注文件和两个transforms。标注文件如下: tshirt1.jpg, 0tshirt2.jpg, 0……ankleboot999.jpg, 9 def __init__(self, annotations_file, img_dir, transform=None, target_transform=None): self.img_labels = pd.read_csv(annotations_file) self.img_dir = img_dir self.transform = transform self.target_transform = target_transform __len__ __len__函数返回数据集中的样本数量。 def __len__(self): return len(self.img_labels) __getitem__ __getitem__函数从给定索引idx的数据集中加载并返回一个样本。根据索引,它确定图像在磁盘上的位置,使用read_image将其转换为张量,从csv数据中检索相应的标签。对它们调用transform函数(如果适用),并返回元组中的张量图像和相应的标签。 def __getitem__(self, idx): img_path = os.path.join(self.img_dir, self.img_labels.iloc[idx, 0]) image = read_image(img_path) label = self.img_labels.iloc[idx, 1] if self.transform: image = self.transform(image) if self.target_transform: label = self.target_transform(label) return image, label 使用DataLoaders为训练准备数据 Dataset每次检索数据集一个样本的特征和标签。在训练模型时,通常希望以“minibatches”的方式传递样本,在每个epoch重新shuffle数据以减少模型过拟合,并使用Python的多进程处理来加速数据检索。DataLoader是一个可迭代对象,它将这种复杂性抽象为一个简单的API。 from torch.utils.data import DataLoader train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True) test_dataloader = DataLoader(test_data, batch_size=64, shuffle=True) 遍历DataLoader 已经将该数据集加载到DataLoader中,并可以根据需要迭代该数据集。下面的每次迭代都会返回一批train_features和train_labels(分别包含batch_size=64个特征和标签)。因为指定了shuffle=True,所以在遍历完所有batches后,数据会被打乱(为了更细粒度地控制数据的加载顺序,可以看一下Samplers)。 # Display image and label. train_features, train_labels = next(iter(train_dataloader)) print(f"Feature batch shape: {train_features.size()}") print(f"Labels batch shape: {train_labels.size()}") img = train_features[0].squeeze() label = train_labels[0] plt.imshow(img, cmap="gray") plt.show() print(f"Label: {label}") 输出: Feature batch shape: torch.Size([64, 1, 28, 28])Labels batch shape: torch.Size([64])Label: 9 ### PyTorch快速入门 模型训练需要的几个基础部分包括:数据、模型、优化器、损失函数,这四个部分通过迭代不断让模型学习到输入到输出的映射关系。 数据 PyTorch有两个主要的处理数据的模块,torch.utils.data.DataLoarder 和torch.utils.data.Dataset。Dataset 存储样本及其对应的标签,DataLoader 对数据集封装了一个迭代器。 import torch import torch.nn as nn from torch.utils.data import DataLoader from torchvision import datasets from torchvision.transforms import ToTensor PyTorch提供了特定领域的库,如TorchText、TorchVision和TorchAudio,这些库都包含数据集。此文将使用TorchVision数据集。 torchvision.datasets 模块包含许多真实世界视觉数据的数据集对象,如CIFAR和COCO。在此使用FashionMNIST数据集。每个TorchVision数据集都包含两个参数:transform和target_transform,分别用于修改样本和标签。 # Download training data from open datasets. training_data = datasets.FashionMNIST( root="data", train=True, download=True, transform=ToTensor(), ) # Download test data from open datasets. test_data = datasets.FashionMNIST( root="data", train=False, download=True, transform=ToTensor(), ) 将 Dataset 作为参数传递给 DataLoader。它对数据集包装了一个迭代器,并支持自动批处理(batching)、采样(sampling)、混洗(shuffling)和多进程数据加载(multiprocess data loading)。在这里,batch size定义为64,即dataloader迭代器中的每个元素将返回64个特征和标签。 batch_size = 64 # Create data loaders. train_dataloader = DataLoader(training_data, batch_size=batch_size) test_dataloader = DataLoader(test_data, batch_size=batch_size) for X, y in test_dataloader: print(f"Shape of X [N, C, H, W]: {X.shape}") print(f"Shape of y: {y.shape} {y.dtype}") break 上述代码输出结果为: 模型 要在PyTorch中定义神经网络,需要创建一个继承 nn.Module 的类。在 __init__ 函数中定义网络的层,并在 forward 函数中指定数据如何通过网络。为了加速神经网络的操作,如果GPU可用的话,可以将其移动到GPU。 # Get cpu or gpu device for training. device = "cuda" if torch.cuda.is_available() else "cpu" print(f"Using {device} device") # Define model class NeuralNetwork(nn.Module): def __init__(self): super().__init__() self.flatten = nn.Flatten() self.linear_relu_stack = nn.Sequential( nn.Linear(28*28, 512), nn.ReLU(), nn.Linear(512, 512), nn.ReLU(), nn.Linear(512, 10) ) def forward(self, x): x = self.flatten(x) logits = self.linear_relu_stack(x) return logits model = NeuralNetwork().to(device) print(model) 上述代码的输出结果: 优化模型参数 为了训练模型,需要一个损失函数(loss function)和一个优化器(optimizer)。 loss_fn = nn.CrossEntropyLoss() optimizer = torch.optim.SGD(model.parameters(), lr=1e-3) 在单个训练循环中,模型对训练数据集进行预测(分批输入),并反向传播预测误差以调整模型的参数。 def train(dataloader, model, loss_fn, optimizer): size = len(dataloader.dataset) model.train() for batch, (X, y) in enumerate(dataloader): X, y = X.to(device), y.to(device) # Compute prediction error pred = model(X) loss = loss_fn(pred, y) # Backpropagation optimizer.zero_grad() loss.backward() optimizer.step() if batch % 100 == 0: loss, current = loss.item(), batch * len(X) print(f"loss: {loss:>7f} [{current:>5d}/{size:>5d}]") 还根据测试数据集检查模型的性能,以确保模型是在学习。 def test(dataloader, model, loss_fn): size = len(dataloader.dataset) num_batches = len(dataloader) model.eval() test_loss, correct = 0, 0 with torch.no_grad(): for X, y in dataloader: X, y = X.to(device), y.to(device) pred = model(X) test_loss += loss_fn(pred, y).item() correct += (pred.argmax(1) == y).type(torch.float).sum().item() test_loss /= num_batches correct /= size print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n") 训练过程经过多次迭代(epoch)。在每个epoch期间,模型学习参数以做出更好的预测。打印模型在每个epoch的精度(accuracy)和损失(loss);希望看到精度随着每次迭代而增加,损失减少。 epochs = 5 for t in range(epochs): print(f"Epoch {t+1}\n-------------------------------") train(train_dataloader, model, loss_fn, optimizer) test(test_dataloader, model, loss_fn) print("Done!") Epoch 1 Test Error: Accuracy: 45.2%, Avg loss: 2.149141 Epoch 2 Test Error: Accuracy: 51.7%, Avg loss: 1.872434 Epoch 3 Test Error: Accuracy: 59.0%, Avg loss: 1.519363 Epoch 4 Test Error: Accuracy: 62.4%, Avg loss: 1.262601 Epoch 5 Test Error: Accuracy: 64.3%, Avg loss: 1.098800 保存模型 保存模型的常用方法是序列化内部状态字典(包含模型参数)。 torch.save(model.state_dict(), "model.pth") print("Saved PyTorch Model State to model.pth") 加载模型 加载模型的过程包括重新创建模型结构并将状态字典加载到其中。 model = NeuralNetwork() model.load_state_dict(torch.load("model.pth")) 这个模型现在可以用来进行预测。 classes = [ "T-shirt/top", "Trouser", "Pullover", "Dress", "Coat", "Sandal", "Shirt", "Sneaker", "Bag", "Ankle boot", ] model.eval() x, y = test_data[0][0], test_data[0][1] with torch.no_grad(): pred = model(x) predicted, actual = classes[pred[0].argmax(0)], classes[y] print(f'Predicted: "{predicted}", Actual: "{actual}"') ### PyTorch定义神经网络 简介 深度学习使用人工神经网络(模型),它是由许多层相互连接的单元组成的计算系统。通过这些相互连接的单元传递数据,神经网络能够学习如何近似地计算将输入转换为输出所需的计算。在PyTorch中,可以使用 torch.nn 构建神经网络。 PyTorch提供了优雅设计的模块和类,包括 torch.nn,来帮助创建和训练神经网络。nn.Module 包含层,forward(input) 方法返回输出。 步骤 导入加载数据所需的所有库 定义并初始化神经网络 指定数据如何在模型中传递 [可选]将数据传递给模型进行测试 导入加载数据所需的库 import torch import torch.nn as nn import torch.nn.functional as F 定义并初始化神经网络 该网络用于识别图像。将使用PyTorch内置的卷积过程。卷积将图像的每个元素添加到其局部邻域,由核或小矩阵加权,这有助于从输入图像中提取某些特征(如边缘检测、清晰度、模糊度等)。 定义模型的Net类有两个要求。第一个是写一个 __init__ 函数来引用 nn.Module。这个函数用于定义神经网络中所有的连接层。使用卷积,将定义的模型以获取1个输入图像通道,输出与目标匹配,即表示数字0到9的10个标签。将遵循标准MNIST算法。 class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(1, 32, 3, 1) self.conv2 = nn.Conv2d(32, 64, 3, 1) self.dropout1 = nn.Dropout2d(0.25) self.dropout2 = nn.Dropout2d(0.5) self.fc1 = nn.Linear(9216, 128) self.fc2 = nn.Linear(128, 10) my_nn = Net() print(my_nn) 已经定义了神经网络,然后必须定义数据如何通过网络。 指定数据如何通过模型 当使用PyTorch构建模型时,需要定义 forward 函数,它将数据传递到计算图,表示前馈算法。可以在前向函数中使用任何张量运算。 class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(1, 32, 3, 1) self.conv2 = nn.Conv2d(32, 64, 3, 1) self.dropout1 = nn.Dropout2d(0.25) self.dropout2 = nn.Dropout2d(0.5) self.fc1 = nn.Linear(9216, 128) self.fc2 = nn.Linear(128, 10) def forward(self, x): x = self.conv1(x) x = F.relu(x) x = self.conv2(x) x = F.relu(x) x = F.max_pool2d(x, 2) x = self.dropout1(x) x = torch.flatten(x, 1) # Flatten x with start_dim=1 x = self.fc1(x) # Pass data through fc1 x = F.relu(x) x = self.dropout2(x) x = self.fc2(x) output = F.log_softmax(x, dim=1) # Apply softmax to x return output [可选]将数据传递给模型进行测试 为了确保收到所需的输出,通过传递一些随机数据来测试模型。 # Equates to one random 28x28 image random_data = torch.rand((1, 1, 28, 28)) my_nn = Net() result = my_nn(random_data) print (result) ### PyTorch数据加载(一) 简介 PyTorch具有广泛的神经网络构建模块,具有简单、直观和稳定的API。PyTorch包含为模型准备和加载公共数据集的包。 PyTorch数据加载实用工具的核心是torch.utils.data.DataLoader类。它表示数据集上的Python迭代器。PyTorch在torch.utils.data.Dataset类中提供了内置的高质量数据集。它们允许使用预加载的数据集以及自己的数据。Dataset存储样本及其对应的标签,DataLoader在Dataset周围包装了一个可迭代对象,以方便访问样本。这些数据集目前包含: torchvision torchaudio torchtext 使用torchaudio.datasets中的Yesno数据集演示如何高效地将数据从PyTorch Dataset 加载到PyTorch DataLoader 中。 安装 需要安装 torchaudio 以访问数据集,pip install torchaudio 要在谷歌Colab中运行,取消注释,!pip install torchaudio 步骤 导入加载数据所需的所有库 访问数据集中的数据 加载数据 遍历数据 [可选]可视化数据 导入加载数据所需的所有库 在这个样例中,使用 torch 和 torchaudio。根据使用的内置数据集,还可以安装和导入torchvision 或 torchtext import torch import torchaudio 访问数据集中的数据 torchaudio中的Yesno数据集有60段记录,记录的是一个人用希伯来语说“yes”或“no”;每段录音有8个单词长。torchaudio.datasets.YESNO为YesNo创建数据集 yesno_data = torchaudio.datasets.YESNO( root='./', url='http://www.openslr.org/resources/1/waves_yesno.tar.gz', folder_in_archive='waves_yesno', download=True) 数据集中的每一项都是元组:(waveform, sample_rate, labels)。对Yesno数据集必须设置root参数,用于存储训练和测试数据集。其他参数是可选的,显示了它们的默认值。 加载数据 现在可以访问数据集了,将它传递给torch.utils.data.DataLoader。DataLoader将数据集和采样器结合起来,返回一个遍历数据集的迭代器。 data_loader = torch.utils.data.DataLoader(yesno_data, batch_size=1, shuffle=True) 遍历数据 数据现在可以使用data_loader进行迭代了。当开始训练模型时,这将是必要的!现在data_loader对象中的每个数据条目都被转换为一个张量,其中包含表示waveform、sample rate和labels的张量。 for data in data_loader: print("Data: ", data) print("Waveform: {}\nSample rate: {}\nLabels: {}".format(data[0], data[1], data[2])) break 可视化数据 可以选择将数据可视化,以进一步理解DataLoader的输出 import matplotlib.pyplot as plt print(data[0][0].numpy()) plt.figure() plt.plot(waveform.t().numpy()) ### 论文笔记:Calculating Question Similarity is Enough: A New Method for KBQA Tasks 摘要 知识库问答(KBQA)的目的是在外部知识库的帮助下回答自然语言问题。其核心思想是找到问题背后的内部知识与知识库中已知三元组之间的联系。传统的KBQA任务piplines包含实体识别、实体链接、答案选择等步骤。在这种pipline方法中,任何过程中的错误都会不可避免地传播到最终的预测。为了解决这一问题,论文提出了一种基于预训练语言模型的语料库生成-检索方法(Corpus Generation - Retrieve Method,CGRM)。主要的创新之处在于新方法的设计。其中,知识增强T5 (kT5)模型旨在基于知识图谱三元组生成自然语言QA对,并通过检索合成数据集直接求解QA。该方法可以从PLM中提取更多的实体信息,提高精度,简化过程。我们在NLPCC-ICCPOL 2016 KBQA数据集上测试了我们的方法,结果表明该方法提高了KBQA的性能,并且与最先进的方法相比具有竞争力。 背景和挑战 问答系统是自然语言处理领域长期以来的研究热点。一个方向是将已有的知识库用于自然语言问题,称为知识库问答(Knowledge Base Question Answering, KBQA)。目前KBQA的主流方法是将当前问句的实体链接到KBQA实体中,利用句子中出现的关系归纳到知识图谱中得到最终答案。目前,KBQA任务主要通过一组分步处理的pipline来解决,包括实体识别、关系抽取、实体链接、答案选择,如图1所示。在应用上述pipline方法时,中途发生的任何错误都会影响到后续所有的pipline链接,从而在很大程度上降低整个KBQA系统的性能。 图1 解决方案 为了应对这一挑战,提出了一种语料库生成-检索方法(Corpus Generation-Retrieve Method, CGRM)。过程如图2所示。在该方法中,利用预训练好的模型对处理后的知识三元组进行查询,生成庞大的问答语料库。用户问题的最终答案可以通过检索语料库中生成的相似问题得到。 在mT5 模型的基础上,设计了两个新的预训练任务:知识掩码语言建模和基于段落的问题生成,以获得知识增强的T5 (kT5)模型。同时,用一个额外的稀有汉字列表扩展了模型的词汇列表。与mT5模型相比,kT5在单词知识和小样本学习方面得到了增强。此外,为了增强模型的语言生成能力,kT5将每个单词视为一个单元,可以有效缓解生成任务中的重复问题。 然后使用[MASK]符号替换知识图谱三元组中的任何实体,使该实体成为答案。将掩码后的三元组作为输入,借助kT5模型进行seq2seq微调,为这些三元组生成问题,并生成问题和相应的答案共同构成庞大的QA语料库。随后,使用微调后的模型生成详尽的问答对语料库。 最后,将KBQA任务转化为检索问题。通过计算问题相似度,可以在生成的问答数据集中检索用户问题的答案。 图2 贡献 综上,提出了一种新的面向KBQA任务的方法,该方法可以从PLM中提取更多关于实体的信息,从而提高准确率,简化过程,并消除错误传导问题。通过实验证明了其在问答中的性能优势。在线部署KBQA系统时,只需在一轮中使用用户查询检索生成的数据集,从而大大简化了操作过程,提高了部署系统的效率。主要贡献总结如下: 针对KBQA任务提出了一种CGRM方法,提高了流水线精度和部署效率。此外,该方法不需要领域知识和手工特征或模型结构设计。 针对KBQA任务,本文提出了预训练知识增强的mT5 (kT5),它显示了巨大的少样本学习能力。这种少秀学习能力有利于各种领域的KBQA部署,而无需大量训练。 在此基础上,该文提出了一个面向问答研究的语料库。 方法 kT5 在论文中,使用mT5作为预训练模型骨干。为了使mT5模型能够从知识图谱中生成更好的自然语言问答对,通过增加两个额外的预训练任务和词汇表配置来改进mT5模型,以获得知识增强的T5模型(kT5)。 预训练任务 根据目标任务,定义了两个预训练任务,应用谷歌mT5的基本结构和初始权重,如图3所示。 图3 基于段落的问题生成。使用WebQA和SogouQA的文章和答案对训练模型,以提高其问题生成能力。 知识掩蔽语言建模。该任务类似于为T5模型设计的无监督预训练任务,T5是对输入文本中被屏蔽实体的预测。论文使用WuDaoCarpora中百科条目的摘要作为训练数据。本文对百科全书条目摘要中的链接实体进行掩码,而不是随机掩码句子中的单词,以增强训练好的kT5模型的单词知识。 词汇表配置 在mT5模型中,文本使用SentencePiece编码为WordPiece tokens。对于所有实验,mT5使用了25万个单词的词汇表。然而,原SensePiece算法在中文环境下分词性能较差,效果不佳,且该算法会强制将一些全角符号转换为半角符号。因此,作者使用pkuseg将文本编码为WordPiece标记。此外,kT5模型主要针对中文任务设计,并支持英语作为辅助语言。然而,mT5模型使用了大量其他语言的词汇表,导致嵌入层的参数过度占用。因此,作者对中文词汇进行补充,并删除非中文或非英文词汇,将总词汇量减少到5万个。 问题自动生成 使用NLPCC-ICCPOL 2016 KBQA任务提供的知识库进行实验。它是事实的集合,以(主语、谓语、宾语)的形式存储为三元组。为了更高效地查询,将知识图谱存储在neo4j数据库中。为了通过使用预训练模型从知识图谱中生成一个大型QA语料库,遵循图4所示的几个重要步骤。 图4 知识提取后的流程如图4右侧所示。使用[MASK]作为特殊标记,对上述规则提取的知识三元组中的对象进行掩码。这些生成的问题和相应的知识元组共同构成kT5端到端微调的训练数据。具体来说,为了微调问题生成的模型,这些处理过的知识元组被用作kT5编码器的输入。生成的问题应该是解码器的输出。 接下来,如图4中间所示,微调后的模型查询处理后的完整知识元组集,这些生成的问题和掩码实体形成了一个大型的原始QA语料库。 最后,为了避免文本生成任务中偶尔出现的重复问题,使用启发式规则根据生成问题的质量对原始QA数据集进行过滤。在细节上,对每个生成的文本进行分割,并计算其中每个单词的比例。如果一个词在一个问题中的比例超过70%,相应的QA对将从数据集中删除。 检索 该文使用稀疏向量空间模型BM25结合BERT进行问句检索。之所以使用传统方法而不是更先进的方法,如密集嵌入模型,是因为作者想要排除检索算法的影响,从而证明所提出的CGRM系统的有效性。 召回算法 Best Match25 (BM25)是一种根据所提出的查询对信息检索系统中的文档进行评分的算法。 首先,解析查询词的语素,生成语素qi; 然后,针对每个检索结果D,计算每个语素qi与D的相关分数; 最后,将qi相对于D的相关分数加权求和,得到查询与D的相关分数。 其中Q是文档集合,D是查询语句。IDF(qi)表示文档集合Q中单词qi的IDF值,f(qi, D)表示文档D中单词qi的TF值,k1为词频饱和度,用于调节饱和度变化速率。b是字段长度缩减,用于将文档长度缩减到所有文档的平均长度。它的值在0到1之间,1表示所有归约,而0表示不归约。|D|表示查询语句的长度,avgdl表示所有文本的平均长度。论文实验结果表明,当从BM25中检索到350个问题时,模型的性能最好。 排序算法 在对话系统中,BERT可以用来选择和排序候选答案。将查询和候选问题串联成一个序列,以[CLS]作为序列的开始,在查询和候选问题之间插入[SEP]作为分隔符,通过计算tmax在[CLS]处的位置来判断答案是否合理,并根据正例的概率分布进行排序,得到最终结果。 结论 目前的KBQA方法大多使用pipline来提取或表示问题中的语义信息,并尝试将这些特征与知识库进行匹配以得到最终的预测结果,这可能会在pipline中积累误差。此外,手工设计的特征表示或模型结构需要精心设计,难以对语义差异具有鲁棒性。 为解决这些问题,论文提出了一种直接的解决方案,利用预训练模型和知识图谱。通过使用预训练的kT5模型,基于从知识图谱中召回的三元组生成了一个大型的问答语料库,该语料库可以用于召回,但也可以用于问答研究。基于该语料库,只需根据问题相似度在该语料库中进行检索,就可以直接得到用户问题的最终答案。在NLPCC KBQA数据集上验证了该方法。实验结果表明,该方法的F1值达到85.77%,远远超过了其他算法的性能。此外,在KBQA系统在线运行时,只需一轮检索就可以回答用户提出的问题,具有较高的部署效率,可以降低服务器负载和成本。此外,作者证明了kT5的小样本学习能力,可以在不进行太多训练的情况下轻松进行各种域部署。 ### 知识图谱文本生成调研 通过特定任务和开放世界知识增强文本生成 论文:TEGTOK: Augmenting Text Generation via Task-specific and Open-world Knowledge 代码:https://github.com/lxchtan/TEGTOK 摘要 生成自然且信息丰富的文本一直是NLP中的一个长期问题。许多工作致力于将预训练语言模型(PLMs)与各种开放世界知识(如知识图谱或wiki页面)结合起来。然而,他们访问和操作特定任务知识的能力仍然局限于下游任务,因为这种类型的知识通常在PLMs中没有很好地覆盖,并且很难获得。为解决该问题,论文提出在统一框架中通过特定任务和开放世界知识增强文本生成。该模型通过密集检索从两类知识源中选择知识条目,并基于PLMs分别注入到输入编码和输出解码阶段。在这两种知识的帮助下,模型可以学习生成什么以及如何生成。在对话生成和问题生成两个文本生成任务以及两个数据集上的实验表明,该方法取得了比各种基线模型更好的性能。 挑战 尽管目前的生成模型能够使用编码器-解码器架构或语言模型生成自然和流畅的目标序列,但它们通常只提供有限的知识来生成所需的输出。因此,在许多现实世界的场景中,文本生成的性能仍然远远不能令人满意。 尽管开放世界知识(如结构化知识库,ConceptNet或非结构化文档,如来自维基百科的文档)在大多数情况下可以改善生成过程,但其效果仍局限于涉及较少实体或抽象语义的情况。另一方面,人类生成文本的过程往往基于一种以上的知识感知。除了世界知识,特定于任务的知识也作为重要的信息源,通常在PLMs中没有很好地覆盖,并且很难通过微调获得。 解决方法 基于上述问题,本文提出了augmentation TExt Generation via Task-specific and Open-world Knowledge (TEGTOK)。 假设世界知识是非结构化的维基百科文档,提供了输入序列中提到的实体的补充信息或背景知识。特定于任务的知识是一个预先构建的索引,与领域相关,并作为指导文本生成的示范性信息源。它可以根据不同的任务或领域进行灵活调整,例如对话生成中的上下文-反应对或问题生成中的篇章-问题对。 受开放域问答任务中密集检索方法成功的启发,使用预训练编码器将输入文本和知识条目转换为密集表示向量,并采用快速最大内积搜索(MIPS)完成检索,以保证知识选择的有效性和效率。 最后,将这两类知识分别注入到源文本编码和目标文本解码阶段。通过这种方式,该模型可以在两类知识的帮助下,在统一的框架中学习如何生成以及生成什么。 基于图神经网络的子图引导的知识图谱问题生成 论文:Toward Subgraph Guided Knowledge Graph Question Generation with Graph Neural Networks 代码:https://github.com/liyuanfang/mhqg 摘要 知识图谱(KG)问题生成(QG)旨在从知识图谱和目标答案中生成自然语言问题。以往的工作大多集中在一个简单的设置上,即从单个KG三元组生成问题。本文专注于一个更现实的设置,旨在从KG子图中生成问题和目标答案。此外,之前的大多数工作都是基于RNN或Transformer模型来编码线性化的KG子图,这完全丢弃了KG子图的显式结构信息。为解决这个问题,本文建议应用双向Graph2Seq模型对KG子图进行编码。此外,用节点级复制机制增强了RNN解码器,允许直接将节点属性从KG子图复制到输出问题。自动和人工评估结果表明,所提出模型取得了新的最先进的分数,在两个QG基准上明显优于现有方法。实验结果还表明,作为一种数据增强的方法,QG模型可以持续地使问答(QA)任务受益。 挑战 第一个是如何学习KG子图的良好表示。KG子图具有复杂的底层结构,如节点属性和多关系边。每个节点和边可以有(很长)包含多个单词的关联文本。已有方法在生成简单问题时要么只考虑关键字列表或单个三元组,要么在生成多跳问题时简单地将KG子图视为三元组的集合,没有充分利用其丰富的结构信息。 第二个挑战是如何自动学习子图和自然语言问题之间的良好映射。例如,对于一个问题来说,直接从输入的KG子图中提到实体名称是很常见的。然而,对于以前的方法来说,精确地生成这样一个通常包含多个tokens的实体名称是具有挑战性的。 第三个挑战是如何有效地利用答案信息。给定一个包含多个三元组的KG子图,人们可以在不知道确切目标答案的情况下生成完全不同的问题。因此,有效利用答案信息对于生成更相关的问题至关重要。 解决方法 为了应对上述挑战,论文提出一种基于图神经网络(graph Neural Networks, GNNs)的子图引导的知识图谱问题生成方法。本文首次提出了具有新颖节点级复制机制的图到序列(Graph2Seq)架构,用于KG-QG任务,以应对第二个挑战。本文扩展了常规的基于的编GNN码器,以允许处理有向和多关系的KG子图,以解决第一个挑战。本文提出一种简单而优雅的方法,来利用答案中的上下文信息来有效处理第三个挑战。 贡献 针对子图引导的KG-QG问题,提出一种新的Graph2Seq模型。所提出的Graph2Seq模型采用双向图嵌入,设计了两种不同的GNN编码器,以有效编码具有有向和多关系边的KG子图。 用一种新的复制机制扩展了RNN解码器,允许在生成自然语言问题时从输入KG子图中采用整个节点属性。 研究了应用GNN编码器处理KG子图时初始化节点/边嵌入的两种不同方法。此外,研究了边缘方向对GNN编码器的影响。 实验结果表明,该模型在WebQuestions(WQ)和PathQuestions(PQ)上的BLEU-4得分分别从11.57和25.99提高到59.59。人工评估研究证实,与其他基线相比,该模型生成的问题更自然(在语义和语法上),也更相关。实验还表明,作为一种数据增强的方法,QG模型可以持续地使KBQA任务受益。 基于预训练语言模型的小样本知识图谱到文本生成 论文:Few-shot Knowledge Graph-to-Text Generation with Pretrained Language Models 代码:https://github.com/RUCAIBox/Few-Shot-KG2Text 摘要 研究如何自动生成描述知识图谱(KG)中事实的自然语言文本。考虑到小样本的设置,利用了预训练语言模型(PLMs)在语言理解和生成方面的出色能力。论文做出了三个主要的技术贡献,即表示对齐,以弥合知识图谱编码和PLMs之间的语义鸿沟;关系偏置的知识图谱线性化,以推导更好的输入表示;多任务学习用于学习知识图谱和文本之间的对应关系。在三个基准数据集上的广泛实验,证明了所提出模型在KG到文本生成任务上的有效性。特别是,所提出模型在全监督和小样本设置上都优于所有比较方法。 挑战 近年来,在众包平台和信息抽取(Information Extraction, IE)系统的帮助下,出现了大规模的知识图谱及其描述性文本的标注对,如WikiBio和WebNLG Challenge。可以为给定的知识图谱产生信息丰富和流畅的文本。将PLMs应用于小样本KG-to-text生成仍然面临两个挑战。 首先,PLMs通常在自然语言文本上进行预训练,而任务的KG输入是结构图。这种语义差距使得很难将知识图谱表示有效注入到PLMs中,特别是在标注实例有限的情况下。 其次,与许多其他文本生成任务不同,KG-to-text生成需要基于对KG事实的理解进行忠实的生成。它需要学习输入知识图谱和输出文本之间准确的语义对应关系,这在小样本的情况下会更加困难。 解决方法 为解决上述问题,论文提出一种基于PLMs的小样本知识图谱到文本的生成模型。 首先,为了弥合语义鸿沟,通过学习知识图谱表示(由GNN编码)和基于PLMs的实体表示之间的对应关系来加强表示对齐。 为了将KG输入到PLMs中,提出一种关系偏置的广度优先搜索(RBFS)策略,将KG线性化为一个良好规划的实体序列。 最后,在多任务学习框架下联合训练初级文本生成任务和辅助知识图谱重建任务。这一步进一步增强了输入KG和输出文本之间的语义对应关系,基于此模型可以生成关于KG的忠实文本。 面向知识图谱文本生成的图-文本联合表示学习 论文:JointGT: Graph-Text Joint Representation Learning for Text Generation from Knowledge Graphs 代码:https://github.com/thu-coai/JointGT 摘要 现有的用于知识图谱到文本(KG-to-text)生成的预训练模型只是在KG-to-text数据集上对文本到文本的预训练模型进行微调,如BART或T5,这些模型在编码期间在很大程度上忽略了图结构,并且缺乏详细的预训练任务来明确建模图-文本对齐。为解决这些问题,论文提出一种图-文本联合表示学习模型JointGT。在编码过程中,设计了一个结构感知的语义聚合模块,插入到每个Transformer层中,以保持图结构。提出了三个新的预训练任务,以明确地增强图-文本对齐,包括各自的文本/图重建,以及通过最优传输在嵌入空间中的图-文本对齐。实验表明,JointGT在各种KG-to-text数据集上获得了最新的性能。 背景 知识图谱到文本(KG-to-text)生成旨在生成与输入图一致的高质量文本。该任务需要同时对图结构和内容进行编码,并在解码过程中有效利用输入图。作为连接知识图谱和文本的主要自然语言生成(NLG)任务,该任务可以进一步促进知识图谱在更真实的NLG场景中的适用性,如以知识为基础的对话生成和故事生成。 由于图-文本并行数据量有限,典型的神经文本生成模型很难从头开始学习源实体/关系和目标标记之间的对齐。最近的工作依靠构建通用的预训练语言模型来进行知识图谱到文本的生成。最常见和最简单的方法是将输入图线性化为文本序列,并直接在KG-to-text数据集上微调基于文本到文本Transformer的预训练模型,如GPT, BART或T5。得益于在大规模未标记文本语料库上的自监督预训练,预训练语言模型可以通过简单的微调生成高质量的文本,并优于其他具有复杂结构的模型。 挑战 尽管微调预训练模型在KG-to-text数据集上的性能优越,但构建用于KG-to-text生成的预训练模型仍然面临两个主要挑战: 编码过程中结构信息丢失。大多数现有的预训练模型通过双向Transformer捕捉上下文信息,其中包括全注意力连接。由于没有明确考虑每对输入实体之间的关系,这种模型结构在对知识图谱进行编码时可能会忽略结构信息。 缺乏显式的图-文本对齐。现有的关于文本生成预训练模型的工作通常采用自编码或自回归文本重建来学习文本-文本对齐,对损坏的文本序列进行编码并解码原始序列。由于知识图谱可能具有比文本序列更复杂的结构,因此很难直接使用基于文本重构的预训练任务来显式地学习图-文本对齐。 解决方法 论文提出一种图-文本联合表示学习框架JointGT来应对上述挑战。 首先,为缓解编码过程中的结构信息损失,在每个Transformer层设计了一个简单的结构感知语义聚合模块,以根据图结构聚合上下文信息。 其次,提出了三个预训练任务,包括图增强文本重建、文本增强图重建和图-文本嵌入对齐,以显式地建立知识图谱和文本序列之间的联系。前两个任务期望增强离散词汇空间中的图-文本对齐,其中模型需要根据观察到的文本/图信息来预测图/文本的掩码信息。第三个任务旨在通过最优传输在连续嵌入空间中对图-文本对齐进行建模,以匹配图和文本的隐藏表示。 贡献 提出一种新的预训练模型JointGT,用于KG-to-text生成任务。该模型采用结构感知的语义聚合模块在每个Transformer层对输入图的结构进行建模,并利用三个预训练任务在离散和连续空间中显式学习图-文本对齐。 在WebNLG、WebQuestions和PathQuestions等KG-to-text生成数据集上进行了实验。结果表明,JointGT在知识图谱到文本的生成上取得了新的最先进的性能。 基于知识增强提示学习的统一会话推荐系统 论文:Towards Unified Conversational Recommender Systems via Knowledge-Enhanced Prompt Learning 代码:https://github.com/RUCAIBox/UniCRS 摘要 会话推荐系统(CRS)旨在通过自然语言对话主动得出用户偏好并推荐高质量items。通常,CRS由推荐模块和对话模块组成,前者用于预测用户的偏好项目,后者用于生成适当的回复。为了开发有效的CRS,必须将这两个模块无缝集成。现有工作要么设计语义对齐策略,要么在两个模块之间共享知识资源和表示。然而,这些方法仍然依赖于不同的体系结构或技术来开发这两个模块,使得难以进行有效的模块集成。 针对该问题,论文提出了一种基于知识增强提示学习的统一CRS模型UniCRS。该方法将推荐和对话子任务统一到提示学习范式中,并利用基于固定预训练语言模型的知识增强提示,以统一的方法完成这两个子任务。在提示设计中,融合了知识表示、任务特定的软tokens和对话上下文,可以提供足够的上下文信息使PLM适应CRS任务。此外,对于推荐子任务,还将生成的响应模板作为提示信息的重要组成部分,以增强两个子任务之间的信息交互。在两个公开的CRS数据集上进行了广泛的实验,证明了所提方法的有效性。 背景 随着智能助手的广泛应用,会话推荐系统(conversational recommender systems, CRSs)已经成为一个新兴的研究课题,它通过自然语言会话向用户提供推荐服务。从功能上看,CRSs应该能够完成两大子任务:从候选集预测items到用户的推荐子任务和生成合适问题或回答的对话子任务。 为了完成这两个子任务,现有方法通常为每个子任务设置两个独立的模块,即推荐模块和对话模块。由于这两个子任务是高度耦合的,一个有能力的CRS应该能够无缝地集成这两个模块,以便在它们之间共享有用的功能或知识。一类工作将共享的知识资源(如知识图谱和评论)及其表示结合起来,以增强语义交互。另一类工作设计了特殊的表示对齐策略,如预训练任务和正则化项(如互信息最大化和对比学习),以保证两个模块的语义一致性。 挑战 尽管现有的CRS方法取得了一定的进展,但推荐模块和会话模块之间的语义不一致问题并没有得到很好的解决。图1显示了一个典型的CRS模型KGSF的预测不一致的情况,该模型利用互信息最大化来对齐语义表示。虽然推荐模块预测了电影《冰雪奇缘2》(2019),但对话模块似乎没有意识到这样的推荐结果,并生成了一个不匹配的响应,其中包含另一部电影《漂亮女人》(1990)。即使可以利用启发式约束来强制推荐电影的生成,也无法从根本上解决两个模块之间的语义不一致问题。从本质上讲,这种问题是由现有方法中的两个主要问题引起的。首先,这些方法大多采用不同的体系结构或技术来开发两个模块,即使有一些共享的知识或组件,仍然难以有效地将两个模块无缝关联。其次,一个模块产生的结果不能被另一个模块感知和利用。例如,在KGSF中预测推荐结果时,无法利用生成的响应。综上所述,语义不一致的根源在于两个模块的架构设计和工作机制不同。 图1 解决方法 为了解决上述问题,论文旨在开发一个更有效的CRS,以统一的方式实现推荐和对话模块。对于CRS来说,由于这两个子任务的目标是在相同的会话语义基础上实现特定的目标,因此开发一种基于提示学习的统一CRS方法是可行的。论文提出了一种基于知识增强提示学习的统一CRS模型(UniCRS)。 论文中对于基础PLM,使用DialoGPT,因为它已经在大规模对话语料库上进行了预训练。在该方法中,基本PLM在解决两个子任务时是固定的,不需要微调或持续的预训练。为了更好地将任务知识注入到基础PLM中,首先设计了一个语义融合模块,用于捕获对话文本中单词与知识图谱中实体之间的语义关联;该方法的主要技术贡献在于以提示学习的形式制定了两个子任务,并为每个子任务设计了特定的提示。在提示设计中,包含对话上下文(specific tokens)、任务特定的软tokens(latent vectors)和融合的知识表示(latent vectors),可以提供关于对话上下文、任务指令和背景知识的充分语义信息。此外,在推荐时,将对话模块生成的回复模板整合到提示中,进一步增强了两个子任务之间的信息交互。 贡献 实验结果表明,所提出的UniCRS在推荐和对话子任务上的表现优于其他几种有竞争力的方法,特别是在训练数据有限的情况下。本文的主要贡献如下: 根据作者所知,这是第一次以一种通用的提示学习方式开发出一个统一的CRS。 将CRS的子任务规范化为提示学习的统一形式,并设计了针对特定任务的提示和相应的优化方法。 在两个公开的CRS数据集上进行了广泛的实验,证明了所提方法在推荐和对话任务上的有效性。 ### faiss的安装及简单使用 pip安装 直接使用pip安装pip install faiss 在python环境中import faiss会报错 使用如下的命令安装可以成功 pip --default-time=1000 install -i https://pypi.tuna.tsinghua.edu.cn/simple faiss-cpu conda安装 只安装cpu版本 $ conda install -c pytorch faiss-cpu 安装gpu+cpu版本 $ conda install -c pytorch faiss-gpu 指定cuda版本 $ conda install -c pytorch faiss-gpu cudatoolkit=10.2 # for CUDA 10.2 faiss的简单使用 构建数据集 使用xb矩阵建立索引,xb的每一行都是被检索的对象,维度是(nb,d) 。xq矩阵,是用来检索的向量集合的矩阵,对其中的每个向量,需要找到它的前K个相似向量,维度是(nq,d)。当只检索一个向量的时候,nq=1。 import numpy as np d = 64 # dimension nb = 100000 # database size nq = 10000 # nb of queries np.random.seed(1234) # make reproducible xb = np.random.random((nb, d)).astype('float32') xb[:, 0] += np.arange(nb) / 1000. xq = np.random.random((nq, d)).astype('float32') xq[:, 0] += np.arange(nq) / 1000. 建立索引并加入向量 Faiss中有多种类型的索引,使用最简单的索引类型:IndexFlatL2,这是暴力检索L2距离,也就是最常规的计算距离的方式。对于大部分索引类型而言,在建立它们时都包含训练阶段,但是对于IndexFlatL2这个索引,可以跳过这一步。 当索引被建立和训练之后,可以调用add和search这两种方法。 还可以使用两种查找索引状态的方法 is_trianed,来表明索引是否已经被训练过。ntotal来得到当前索引中向量的个数。对于一些类型的索引来说,还可以存储每个向量对应的整数ID(IndexFlatL2不行),如果没有提供ID,那么索引就是的第一个向量索引为0,第二个向量索引为1,以此类推。 import faiss # make faiss available index = faiss.IndexFlatL2(d) # build the index print(index.is_trained) index.add(xb) # add vectors to the index print(index.ntotal) 第一个print的结果:True,已经经过训练,IndexFlatL2不需要训练,因此为True 第二个print的结果:10000,xb得维度是10000 检索 对于每个检索向量,都能从索引中得到前K近个向量。 k = 4 # we want to see 4 nearest neighbors D, I = index.search(xb[:5], k) # sanity check print(I) print(D) D, I = index.search(xq, k) # actual search print(I[:5]) # neighbors of the 5 first queries 参考 faiss包安装_Alicesla的博客-CSDN博客_faiss包安装 Faiss库使用方法(一) - 知乎 (zhihu.com) ### 安装mxnet报错OSError: libcudnn.so.8 安装流程 pip install mxnet-cu102 安装好了之后在python中import mxnet,报了OSError: libcudnn.so.8: cannot open shared object file: No such file or directory错误 解决方案 方案1 改变mxnet的版本,卸载之后重新尝试了mxnet-cu100和mxnet-cu110,依然报相同的错误,未解决 方案2 网上看了很多解决方案,因为没有sudo权限所以没有尝试 在官网上看到docker的安装方法所以尝试 首先要安装NVIDIA的docker驱动,然后拉取镜像docker pull mxnet/python:gpu,mxnet/python 的docker hub里只有1.9版本的镜像,但在拉取镜像的过程中报错 这样可以选择拉取别人上传的镜像也可以拉取一个linux镜像重新搭建需要的cuda和cuDNN版本 拉取镜像构建容器参考Docker容器构建 方案3 最后的解决方法有点简单,兜兜转转一圈回来下载一个低版本的安装便成功了 pip install mxnet-cu102==1.6.0.post0 -i https://pypi.tuna.tsinghua.edu.cn/simple/ 直接pip install mxnet-cu102安装的版本是1.9.1会报错,1.6.0版本没有报错 其他方案 在虚拟环境下装个10.1 cudatookit 先执行conda install cudatoolkit=10.1 -c pytorch再pip install mxnet-cu101==1.5.1 -i https://pypi.tuna.tsinghua.edu.cn/simple/ 上述方案也可以将mxnet安装成功 ### Docker构建容器 Docker概述 docker docs docker hub docker架构 Docker使用客户端-服务器架构。Docker客户端(client )与Docker守护进程(daemon)通信,后者负责构建、运行和分发Docker容器的繁重工作。Docker客户端和Docker守护进程可以在同一个系统上运行,也可以将Docker客户端连接到远程的Docker守护进程。Docker客户端和守护进程使用REST API通过UNIX套接字(sockets )或网络接口通信。另一个Docker客户端是Docker Compose,它允许使用由一组容器组成的应用程序。 图片来自docker官方网站https://docs.docker.com/get-started/overview/ 镜像 镜像是一个只读的模板,包含创建Docker容器的说明。通常,一个镜像基于另一个镜像,并有一些额外的定制。例如,可以构建一个基于ubuntu镜像的镜像,安装Apache web服务器和应用程序,以及运行应用程序所需的配置细节。可以创建自己的镜像,也可以只使用其他人创建并在注册表中发布的镜像。要构建自己的镜像,需要创建一个Dockerfile,使用简单的语法定义创建和运行镜像所需的步骤。Dockerfile中的每一条指令都会在镜像中创建一个层。当更改Dockerfile并重建镜像时,只有那些更改的层会被重建。与其他虚拟化技术相比,这是使镜像轻量级、小巧和快速的部分原因。 容器 容器是镜像的可运行实例。 可以使用Docker API或CLI创建、启动、停止、移动或删除容器。 可以将容器连接到一个或多个网络,为其附加存储空间,甚至根据其当前状态创建新镜像。默认情况下,一个容器与其他容器及其宿主机是相对隔离的。 可以控制容器的网络、存储或其他底层子系统与其他容器或主机的隔离程度。 容器由它的镜像以及在创建或启动它时提供给它的配置选项定义。当容器被移除时,任何未存储在持久存储中的对其状态的更改都会消失。 容器是一个sandboxed process,与主机上的所有其他进程隔离。这种隔离利用了kernel namespaces和cgroups。可以在本地机器、虚拟机上运行或部署到云端;具有可移植性(可以在任何操作系统上运行);与其他容器隔离,并运行自己的软件、二进制文件和配置。 运行一个ubuntu容器,以交互方式连接到本地命令行会话,然后运行 /bin/bash docker run -i -t ubuntu /bin/bash 当您运行此命令时,将发生以下情况(假设您使用默认注册表配置): 如果您没有本地的ubuntu镜像,Docker将从配置的注册表中拉取它,就像手动运行Docker pull ubuntu一样。 Docker会创建一个新容器,就像手动运行Docker container create命令一样。 Docker为容器分配了一个读写文件系统,作为它的最后一层。这允许一个正在运行的容器在其本地文件系统中创建或修改文件和目录。 由于没有指定任何网络选项,Docker会创建一个网络接口将容器连接到默认网络。这包括为容器分配IP地址。默认情况下,容器可以使用主机的网络连接连接到外部网络。 Docker启动容器并执行/bin/bash.因为容器以交互方式运行,并且连接到终端(因为有-i和-t标志),所以可以使用键盘提供输入,同时将输出记录到终端。 当你输入exit来终止/bin/bash命令时,容器会停止,但不会被移除。您可以重新启动它或删除它。 容器镜像 当运行容器时,它使用一个隔离的文件系统。这个自定义文件系统由容器镜像提供。镜像包含容器的文件系统,它必须包含运行应用程序所需的一切——所有依赖项、配置、脚本、二进制文件等。镜像还包含容器的其他配置,如环境变量、要运行的默认命令和其他元数据。 Docker实践 第一步 在工程项目文件夹中写好Dockerfile,如下: FROM 安装好anaconda3的镜像 ADD ./ /opt/test/ WORKDIR /opt/test/ RUN apt update -y && apt install -y libopenblas-dev RUN /root/anaconda3/envs/cpu/bin/pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/ RUN chmod +x ./start.sh CMD ["/bin/sh","-c", "./start.sh"] 拉取一个具有相关配置和环境的镜像; 然后将当前文件夹中的文件添加到容器中/opt/test的目录下; 工作目录为/opt/text; 然后更新安装包并安装libopenblas-dev; 再安装项目依赖的第三方库; 运行当前目录下的start.sh文件 第二步 执行docker build -t image_name ./ --network=host 第三步 镜像构建好了之后使用docker run -itd -p 12345:12345 --name contrainer_name image_name构建容器 因为我的项目是一个web服务,因此设置了端口的对应关系,宿主机的端口12345,对应容器内的端口为12345 简单docker命令 列出本地镜像使用docker image命令 查看容器信息使用命令docker ps -a,得到容器ID(CONTAINER ID)。 使用命令docker exec -it CONTAINER ID /bin/bash进入容器 使用ctrl+D或者exit退出容器 使用docker start CONTAINER ID可以启动 复制本地文件到docker容器中docker cp docker cp 源文件夹/文件 容器ID:目标文件夹 宿主机和docker中的文件关联 ,使用-v 例如:使用镜像bitnami/mxnet docker run -itd --name mxnet1 -v /home/project/:/opt/test/ bitnami/mxnet 此命令将宿主机的文件和容器中的文件指向同一个地址,修改其中一个可以同步到另一个,比如在宿主机中添加一个文件,文件会同步到docker容器中 ### 论文笔记:LET: Linguistic Knowledge Enhanced Graph Transformer for Chinese Short Text Matching 前言 论文:https://arxiv.org/pdf/2102.12671.pdf 代码:https://github.com/lbe0613/LET 摘要 中文短文本匹配是自然语言处理中的一项基本任务。现有方法通常以汉字或词作为输入。它们有两个局限性:1)部分中文词语多义词多,语义信息没有得到充分利用;2)一些模型存在分词带来的潜在问题。该文引入HowNet作为一个外部知识库,并提出了一个语言知识增强图Transformer (LET)来处理单词歧义问题。此外,采用词格图作为输入,以维护多粒度信息。该模型还补充了预训练语言模型。在两个中文数据集上的实验结果表明,该模型优于多种典型的文本匹配方法。消融研究也表明,语义信息和多粒度信息对文本匹配建模都很重要。 挑战 大量的中文词语是多义词,给语义理解带来了很大的困难。短文本中的词多义现象比长文本中的词多义问题更严重,因为短文本通常具有较少的上下文信息,因此模型很难捕捉到正确的语义。 基于词的模型通常会遇到一些由分词引起的潜在问题 解决方法 为了整合词语的语义信息,引入了HowNet作为外部知识库。 许多研究人员提出了词格图,它保留了词库中存在的词,从而保留了不同的分词路径。研究表明,多粒度信息对于文本匹配具有重要意义。 前导知识 HowNet HowNet是一个人工为每个汉语词义标注一个或多个相关义原的外部知识库。HowNet哲学将义原视为一个原子的语义单位。与WordNet不同,义原强调概念的组成部分和属性可以用义原很好地表示。HowNet已被广泛应用于词语相似度计算、情感分析、词语表示学习和语言建模等自然语言处理任务中。图2给出了一个例子。“苹果”这个词有两个意思,包括苹果品牌和苹果。苹果品牌有五个义原,分别是computer、PatternValue、able、bring和SpecificBrand,这五个义原描述了sense的确切含义。 模型架构 LET由4部分组成:输入模块、语义感知图转换器(SaGT)、句子匹配层和关系分类器。输入模块输出每个单词wi的初始上下文表示和每个词义的初始语义表示。SaGT迭代更新单词表示和语义表示,并融合彼此的有用信息。句子匹配层首先将词表示融入字符级,然后利用双边多视角匹配机制对两个字符序列进行匹配。关系分类器将句子向量作为输入,预测两个句子之间的关系。 输入模块 Contextual Word Embedding 上下文词嵌入对于图中的每个节点xi,单词wi的初始表示是上下文字符表示的注意力池化。假设单词wi由一些连续的字符tokens{ct1, ct1+1,···,ct2} 组成,对于每个字符ck (t1≤k≤t2),用一个两层的前馈网络(FFN)计算一个基于特征的得分向量,然后用基于特征的多维softmax (MD-softmax)进行归一化。 公式7 将对应的字向量ck与归一化得分uk进行加权,得到上下文词向量。 公式8 使用Att-Pooling(·)将公式7和公式8重写为: Sense Embedding 上述的词嵌入vi只包含上下文字符信息,在中文中可能会出现多义词的问题。该文采用HowNet作为外部知识库来表示词语的语义信息。 对于每个单词wi,将词义集合表示为S (wi) = {si,1, si,2,···,si,K}。si,k是wi的第k个义原,表示其对应的义原为O(si,k)。为了得到每个义项si,k的嵌入向量,首先得到每个义原的多维注意力表示: 公式10 其中e是义原o的嵌入向量,由义原注意力目标模型(SAT)产生。然后,对于每个义原si,k,其嵌入通过对所有义原表示的注意力池化得到: 公式11 Semantic-aware Graph Transformer 对于图中的每个节点xi,词嵌入vi只包含上下文信息,而义原嵌入si,k只包含语言知识。为了从彼此中获取有用的信息,论文提出了SaGT。首先以vi和si,k分别作为单词wi的初始词表示hi和词义初始义表示gi,k,然后分两步迭代更新它们。 更新Sense Representation 对于多义的词,应该用哪个义通常是由句子中的上下文语境决定的。因此,在更新表示时,每个词义将首先从xi的前向和后向聚合单词的有用信息。 其中两个多维注意力函数MD-GAT(·)具有不同的参数。 [·,·]表示向量的拼接,值得注意的是,没有直接使用mi,k作为新表示gi,k。原因是mi,k只包含上下文信息,需要利用一个门,例如GRU,来控制上下文信息和语义信息的融合。 更新Word Representation 经过多次迭代,最终的词表示不仅包含上下文单词信息,还包含语义知识。对于每个句子,使用hai和hbi分别表示最终的单词表示。 句子匹配层(Sentence Matching Layer) 在获取了每个句子的语义知识增强的词表示后,将这些词信息融合到字符中。 从而得到语义知识增强的字符表示yt 其中LayerNorm(·)表示层规范化,cat是使用BERT获得的上下文字符表示。对于每个字符cat ,利用多维注意力分别从句子Ca和Cb中聚合信息。 上述多维注意力函数MD-GAT(·)共享相同的参数。通过这种共享机制,该模型具有一个很好的特性,即当两个句子完全匹配时有: 我们利用多角度余弦距离进行比较: 通过P个距离d1、d2、···、dP,可以得到最终的字符表示 最终的字符表示包含三种信息:上下文信息、词义知识和字符级相似度。对于每个句子Ca或Cb,使用句子所有最终字符表示的注意力池化得到句子表示向量ra或rb。 Relation Classifier 模型将预测两个句子的相似度: 其中FFN(·)是一个前馈网络,有两个隐藏层,在输出层之后有一个sigmoid激活。训练目标是最小化二元交叉熵损失: 数据集 LCQMC是一个大规模开放域问句匹配语料库。该系统由260068个中文句子对组成,其中训练样本238766个,验证样本8802个,测试样本12500个。每一对都有一个二值标签,表示两个句子是否具有相同的含义或共享相同的意图。正样本比负样本多30%。 BQ是一个面向特定领域的大规模银行问句匹配语料库。该系统由12万对汉语句子对组成,包括100000个训练样本、10000个验证样本和10000个测试样本。每一对还与一个二进制标签相关联,该标签表示两个句子是否具有相同的含义。正样本和负样本的数量相同。 结论 本文提出了一种新的linguistic knowledge enhanced graph transformer,用于中文短文本匹配。该模型以两个词格图作为输入,融合了HowNet中的语义信息,以缓解词的歧义性。所提方法在两个中文基准数据集上进行了评估,获得了最好的性能。消融研究还表明,语义信息和多粒度信息对文本匹配建模都很重要。 ### 论文笔记:BOND: BERT-Assisted Open-Domain Named Entity Recognition with Distant Supervision 前言 论文:https://arxiv.org/pdf/2006.15509.pdf 代码:https://github.com/cliang1453/BOND 摘要 论文研究了远程监督下的开放域命名实体识别(NER)问题。远程监督虽然不需要大量的人工标注,但通过外部知识库产生高度不完整和噪声的远程标签。为应对这一挑战,论文提出一种新的计算框架——BOND,利用预训练语言模型(如BERT和RoBERTa)的力量来提高NER模型的预测性能。具体而言,提出了一种两阶段的训练算法:第一阶段,使用远程标签使预训练语言模型适应NER任务,可以显著提高召回率和准确率;在第二阶段,删除了远程标签,并提出了一种自我训练的方法,以进一步提高模型的性能。在5个基准数据集上的彻底实验表明,BOND比现有的远程监督NER方法具有优越性。 挑战 第一个挑战是不完全标注,这是由现有知识库的覆盖率有限造成的。这个问题导致许多实体不匹配,并产生许多假阳性标签,这可能会显著损害后续的NER模型训练。 第二个挑战是噪声标注。由于标注的模糊性,标注往往是有噪声的——同一个实体可以映射到知识库中的多种实体类型。例如,实体提到“利物浦”可以映射到知识库中的“利物浦城市”(类型:LOC)和“利物浦足球俱乐部”(类型:ORG)。现有方法采用基于类型流行度的标签归纳方法,可能会导致对流行类型的匹配偏差。因此,它会导致许多假阳性样本,并损害NER模型的性能。 通常在标签精度和覆盖率之间存在权衡:生成高质量的标签需要设置严格的匹配规则,这可能不会对所有标签都很好地泛化,从而降低覆盖率并引入假阴性标签。另一方面,随着标注覆盖率的提高,由于标注歧义性,标注错误的数量也在不断增加。综上所述,生成对目标语料库具有高覆盖率的高质量标签仍然是非常具有挑战性的。 方法 为了充分利用预训练语言模型的力量来应对这两个挑战,本文提出一个两阶段的训练框架。 在第一阶段,我们使用远程匹配的标签微调RoBERTa模型,从本质上迁移RoBERTa中的语义知识,这将提高从远程监督的预测质量。值得注意的是,论文采用提前停止的方法来防止模型过度拟合不完整的标注标签,并显著提高召回率。然后使用RoBERTa模型来预测所有数据的一组伪软标签。 在第二阶段,将远匹配的标签替换为伪软标签,并设计一个教师-学生(teacher-student)框架来进一步提高召回率。学生模型首先由第一阶段学习到的模型初始化,并使用伪软标签进行训练。然后,从上一次迭代的学生模型中更新教师模型,为下一次迭代生成一组新的伪标签,以继续训练学生模型。这种教师-学生框架的优点是它逐步提高了模型对数据的置信度。此外,根据学生模型的预测置信度选择样本,进一步提高软标记的质量。这样可以更好地利用知识库信息和语言模型,提高模型的拟合能力。 两阶段框架(BOND) 在BOND的第一阶段,将BERT模型适应远程监督命名实体识别任务。在第二阶段,使用一种自训练的方法提高模型对训练数据的拟合。模型框架如图: 图1 两阶段BOND框架 第一阶段 开放域NER任务生成远程标签。该标签生成方案包含两个步骤:首先通过词性标注和手工规则识别潜在实体;然后,使用SPARQL从Wikidata查询以识别这些实体的类型,如图2所示。接下来,从多个在线资源中收集词典,以匹配数据中的更多实体。 图2 实体标签生成 NER模型f(·;θ)通过最小化损失来学习 NER模型的架构f(·,·)是一个基于token的NER分类器,建立在预训练BERT之上,如图3所示。NER分类器从预训练的BERT层中获取token级输出嵌入,并给出对每个token类型的预测。预训练的BERT包含丰富的语义和语法知识,并产生高质量的输出嵌入。使用这样的嵌入作为初始化,可以使用随机梯度算法有效地使预训练BERT适应目标NER任务,例如ADAM。自适应过程更新整个模型,包括NER分类层和预训练BERT层。 图3 图4说明了预训练BERT嵌入如何帮助模型适应远程监督NER任务。BERT是通过掩码语言模型(MLM)任务进行预训练的,能够利用上下文信息预测缺失的单词。这样的MLM任务与NER任务有很多相似之处。它们都是token-wise分类问题,并且严重依赖上下文信息(见图3)。这自然会使预训练BERT的语义知识迁移到NER任务中。因此,与仅使用远程标注数据从头训练的模型相比,由此产生的模型可以更好地预测实体类型。 图4 第一阶段 Early Stopping。我们在适应过程中使用的一个重要策略是早停法。由于模型容量大,且监督(远程标签)有限且有噪声,NER模型在没有任何干预的情况下,会对远程标签的噪声进行过拟合,从而忘记预先训练的BERT的知识。早停法本质上是一种强正则化,以防止这种过拟合,并提高对不可见数据的泛化能力。 第一阶段算法过程 第一阶段解决了远程监督命名实体识别任务中的两个主要挑战:噪声标注和不完整标注。通过将预训练BERT中的语义知识迁移到NER模型中,抑制噪声,提高预测精度。此外,早期停止可以防止模型过拟合不完整的标注标签,进一步提高召回率。 第二阶段(自训练) 首先描述了一个教师-学生的自训练框架来提高模型的拟合能力,然后提出使用高置信度软标签来进一步提高自训练能力。 教师-学生框架 使用第一阶段的参数初始化教师和学生模型 另一种选择为,使用BERT初始化学生模型,使用第一阶段训练的模型初始化教师模型 在第t次迭代时,教师模型通过下面公式生成伪标签 公式4 然后学生模型拟合这些伪标签。具体来说,给定教师模型 通过求解下面公式来学习学生模型 公式5 然后,使用ADAM优化提早停止的公式5。在第t次迭代结束时,更新教师模型和学生模型: 第二阶段算法过程 请注意,论文丢弃了(t-1)次迭代的所有伪标签,只使用教师模型在第t次迭代时生成的伪标签来训练学生模型。结合早停法,这种自训练方法可以提高模型拟合,减少伪标签的噪声,如图5所示。通过对伪标签的逐步细化,学生模型可以逐步利用伪标签中的知识,避免过拟合。 图5 重新加权高置信度软标签 由公式4生成的硬伪标签只保留每个token的置信度最高的类。为了避免丢失过多其他类别的信息,论文建议使用置信度重加权的软标签。对于第m个句子中的第n个词项,C类的输出概率为: 在第t次迭代时,教师模型生成软伪标签: 公式6 其中 公式6中这样的步骤本质上有利于置信度较高的类。然后通过最小化来优化学生模型: 基于KL散度的损失: 高置信度的选择 为了进一步解决数据中的不确定性,论文建议基于预测置信度选择tokens。高置信度选择本质上强制学生模型更好地拟合具有高置信度的tokens,因此能够提高模型对低置信度tokens的鲁棒性。 ### 论文笔记:Divide and Conquer: Text Semantic Matching with Disentangled Keywords and Intents 前言 论文:https://arxiv.org/pdf/2203.02898.pdf 代码:https://github.com/rowitzou/dc-match 摘要 文本语义匹配是一项基础任务,已被广泛应用于社区问答、信息检索和推荐等各种场景。大多数最先进的匹配模型,例如BERT,通过统一处理每个单词直接执行文本比较。但是,查询句通常包含要求不同级别匹配粒度的内容。具体来说,关键词代表事实信息,如行动、实体和事件,应该严格匹配,而意图传达抽象的概念和想法,可以转述为各种表达。在论文中,提出了一种简单有效的分而治之的文本语义匹配训练策略,将关键词与意图分离开来。该方法可以很容易地与预先训练的语言模型(PLM)相结合,而不影响它们的推理效率,在三个基准测试中,针对广泛的PLM实现稳定的性能改进。 简介 大多数现有的PLM旨在为各种下游任务建立基础,并专注于寻找一种编码文本序列的通用方法。在应用于文本语义匹配任务时,通常添加一个简单的分类目标进行微调,通过统一处理每个词直接进行文本比较。然而,每个句子通常可以被分解为具有不同匹配粒度的内容。例句对可以在图1中找到。主要内容是指反映实体或行为的事实信息的关键词,需要严格匹配。其他内容构成抽象的意图,一般可以用不同的表达方式来表达相同的概念或想法。 针对句子内容具有不同层次匹配粒度的情况,提出了一种简单而有效的分而治之的文本语义匹配训练机制DC-Match。具体地,我们将匹配问题分解为两个子问题:关键词匹配和意图匹配。给定一对输入文本序列,该模型通过利用远程监督的方法学习将关键词从意图中分离出来。 除了具有全局感受野的标准序列匹配外,进一步将关键词和意图分别进行匹配,以学习不同粒度级别下的内容匹配方式。最后,我们设计了一个特殊的训练目标,结合子问题的解,最小化全局匹配分布(原始问题)和关键词意图联合匹配分布(子问题)之间的KL散度。在推理时,我们期望全局匹配模型自动区分关键词和意图,然后根据不同匹配层次中分解的内容进行最终预测。 贡献 提出了一种新的文本匹配训练机制,基于不同层次的匹配粒度,以分而治之的方式将关键词从意图中分离出来。 所提方法简单有效,可以很容易地与PLM加上少量辅助训练参数相结合,而不改变其原始推理效率。 在两种语言的三个基准上的实验结果表明了所提出方法在不同方面的有效性。 方法 提出的DC-Match训练机制包括三个训练目标: 全局匹配模型的分类损失; 一种远程监督分类损失,它能学会区分关键词和意图; 一种遵循分而治之思想的特殊训练目标,利用KL散度(KL-divergence)保证全局匹配分布(原始问题)与分离的关键词和意图的组合解分布(子问题)相似。 总体框架如图2所示。 文本语义匹配 使用预训练语言模型常规的方法是将两个句子连接起来,例如[CLS]Sa[SEP]Sb输入到PLM模型编码层中: y可以是表示两个序列是否匹配的二分类目标,也可以是反映匹配程度不同的多分类目标。计算用于微调的标准分类损失如下: 使用远程监督将关键词从意图中分离 论文对文本语义匹配引入了一个特定任务的假设,假设每个句子可以分解为关键词和意图。直观地说,关键词代表了事实信息,如应该严格匹配的动作和实体,而意图传达了可以用不同方式表达的抽象概念或想法。通过对关键词和意图的分解,将匹配过程分解为两个简单的子问题,每个子问题需要不同粒度的匹配。 然而,由于缺乏人工标注的数据,关键词和意图的自动分解并不容易。为解决这个问题,根据最近对远程监督的研究,论文使用一种基于规则的方法,基于外部知识库中的实体,通过提取原始文本中的实体提及来自动生成关键词标签。 所有抽取的实体被标记为关键词,其余的句子单词被标记为意图。在获得弱标签信息后,添加一个辅助训练目标,迫使模型学习分解的关键词和意图表示。 关键词和意图分类损失定义如下: 其中,hk,hi分别对应于关键词和意图的表示。上述公式中的目标是推动编码器学习关键词和意图的表示,使它们彼此远离,在不同的匹配级别对分解的句子内容进行建模。 自动关键词标注 论文用启发式方法生成远程监督标签,用于关键词和意图的识别。 首先从NLTK中提取潜在的关键词,其中包含名词、动词和形容词的词性标签。 然后,我们使用外部知识库来匹配这些潜在的关键词:英语语料库的维基百科实体图,Chinese Medical-SM的搜狗知识图谱。 最后,使用二进制IO格式来标记token是否属于关键词或意图。 表2显示了三个基准测试的训练集上远程标注关键词的统计量。使用BLEU分数来衡量两个比较句子之间关键词的相关性,包括匹配对和不匹配对。匹配的句子对通常包含相关度较高的关键词。因此,通用模型可能只以匹配的关键词为条件而错误地输出高匹配分数,而不考虑上下文,因为模型倾向于学习数据中的统计偏差。 分而治之匹配策略 然而,远程监督中的辅助训练目标不能与原始文本匹配问题直接关联。为了促进关键词和意图对最终预测的真正贡献,引入了一种新的匹配策略,遵循分而治之的思想。具体地,将原匹配问题分解为两个简单的子问题:关键词匹配和意图匹配,并假设它们彼此独立。然后将子问题的解组合起来,得到原问题的解。 文本语义匹配的目标是学习y = ξ(Sa, Sb),其中y可以是二分类目标,也可以是多分类目标。假设每个子问题遵循相同类型的目标,由两个子问题P(yk, yi)的联合概率分布可以得到组合解Q(y)的概率分布: 公式5 其中,cn、cm表示反映匹配程度的目标类别,cm > cn表示cm比cn具有更高的匹配分数。例如,在一个三分类场景中,y∈{2,1,0}分别表示精确匹配、部分匹配和不匹配,Q(y = 0)是至少一个子问题被推断为不匹配的概率。 分别比较关键词和意图,得到条件概率: Sk和Si分别表示意图或关键词tokens被掩码的文本序列。那么,在子问题独立的假设下,yk和yi的条件联合分布为: 最后,可以结合子问题的解决方案,并使用公式5计算条件分布Q(y|Sa, Sb)。为了保证全局匹配分布(原问题)与子问题组合解的分布相似,我们使用双向KL散度损失来最小化P(y|Sa, Sb)与Q(y|Sa, Sb)之间的距离,如下所示: 公式7 通过这种方式,期望全局匹配模型学习做出具有更好可解释性的最终预测,这取决于需要不同级别匹配粒度的分解关键词和意图。 训练和推理 在训练阶段,结合三个损失函数Lsm,Lds,Ldc来联合训练模型: 在推理时,直接根据原问题的条件概率推断出句子对的匹配类别: 推理过程与PLM基线完全相同,而无需额外的计算。在这里,论文不从组合解Q(y|Sa, Sb)的概率推断匹配结果。由于推理时一般没有关键词和意图的标注信息,不能直接计算Q(y|Sa, Sb)。虽然使用外部语料库自动获取远程标注,但它可能会存在不完整和噪声的信号,给Q(y|Sa, Sb)近似带来偏差。因此,在训练阶段只使用远程标签作为全局匹配模型的辅助信息增强。然而,经过模型训练,P(y|Sa, Sb)与Q(y|Sa, Sb)高度一致。因此,通过更好地近似Q(y|Sa, Sb),一组高质量的关键字标签可能会带来额外的性能提升。 分而治之策略分析 模型在测试时无法获得标注的关键词,因此无法直接计算子问题Q(y)的组合解的概率。因此,公式7中的KL-divergence损失旨在最小化Q(y)与全局匹配概率P(y)之间的距离,旨在模拟推理时的分而治之过程。 论文为每个测试样本计算P(y)和Q(y)之间的KL-divergence分数,并在图4中说明结果。红点表示来自原始PLMs的分数,蓝点是来自DC-Match的分数。可以观察到P(y)和Q(y)表现出更高的一致性(较低的KL-Div分数)与原始PLMs相比,这再次证明了所设计的分治训练目标的有效性,该目标缩小了P(y)和Q(y)之间的差距。 结论 设计了一种分而治之的文本语义匹配训练策略DC-Match。它将匹配问题分解为两个子问题:关键词匹配和意图匹配。该模型学习将关键词从需要不同级别匹配粒度的意图中分离出来。提出的DC-Match简单有效,可以很容易地与PLMs结合,且只需少量额外参数。在三个不同语言的文本匹配数据集上进行了实验。实验结果表明,该方法不仅可以获得稳定的性能提升,而且对语义不变的文本变换具有鲁棒性。 ### 论文笔记:PromptKG: A Prompt Learning Framework for Knowledge Graph Representation Learning and Application 前言 论文:https://arxiv.org/pdf/2210.00305v1.pdf 代码:https://github.com/zjunlp/PromptKG 概述 知识图谱(KGs)通常有两个特征:异构的图结构和文本丰富的实体/关系信息。知识图谱表示学习旨在将关系和实体映射到连续的向量空间中,从而提高知识推理能力,并可应用于问答系统、推荐系统等下游任务。知识图谱表示模型需要考虑图结构和文本语义,但目前没有一个全面的开源框架主要针对知识图谱的信息性文本描述。论文提出了一个用于知识图谱表示学习和应用的开源提示学习框架PromptKG,它装备了前沿的基于文本的方法,集成了一个新的即时学习模型,并支持各种任务(如知识图补全、知识回答、推荐和知识探究)。 一些显著的开源和长期维护的知识体系表示工具包已经开发出来,如OpenKE, LibKGE, PyKEEN, CogKGE。然而,在不使用任何辅助信息的情况下,这些基于嵌入的方法在浅层网络架构的表达能力方面受到限制。 相比之下,基于文本的方法结合可用文本进行知识表示学习。随着提示学习(prompt learning)的快速发展,大量基于文本的模型被提出,这些模型可以通过预训练语言模型获得良好的性能。 基于文本的知识图谱表示 基于判别方法(Discrimination-based methods) 基于判别方法有两种模型: 一种(如KG- bert 和PKGC)利用单个编码器对带有文本描述的知识图谱三元组进行编码; 另一种(如StAR和SimKGC)利用孪生编码器(双塔模型)和预训练语言模型分别对实体和关系进行编码。 基于生成方法(Generation-based methods) 给定一个缺失尾实体的三元组(h, r, ?),给模型输入,输出?t。在训练过程中,生成模型最大化条件概率: 为了保证知识图谱中解码序列模式和tokens的一致性,GenKGC提出了一个实体感知的分层解码器来约束??。此外,受提示学习的启发,GenKGC采用与样例相同关系的三元组隐式编码结构化知识。此外,KGT5提出用文本描述对知识图谱表示进行预训练。 基于提示学习的知识表示学习 普通的预训练语言模型中有两个模块:一个用于将token id嵌入到语义空间的词嵌入层(a word embedding layer),另一个用于生成上下文感知嵌入的编码器(encoder)。该方法与普通的判别式方法共享相同的架构。论文采用掩码语言模型,在词嵌入层将实体和关系视为特殊标记。如图所示,该模型利用头部实体和关系及其描述的序列预测出正确的尾部实体。对于实体/关系嵌入,冻结编码器层(encoder),只微调实体嵌入层(entity embedding),以优化损失函数: 其中Θ表示模型的参数,?? 和 ?? 是实体?的描述和嵌入。 基于提示学的的知识图谱表示学习方法 PromptKG整体结构 PromptKG的设计原则 1)统一的知识图谱编码器:PromptKG利用统一的编码器封装图结构和文本语义; 2)模型枢纽:PromptKG集成了许多前沿的基于文本的KG表示模型; 3)灵活的下游任务:PromptKG将KG表示学习和下游任务进行了解耦 统一的知识图谱编码(Unified KG Encode) 一个统一的知识图谱编码器表示图结构和文本语义,支持不同类型的基于文本的知识图谱表示方法。 对于基于判别的方法,输入是建立在纯文本描述上的: 对于基于生成的模型,利用 ?ℎ 和 ?? 中的来优化带有标签 ?? 的模型。当预测头实体时,在输入序列中添加一个特殊的token( [reverse])以进行反向推理。用特殊的tokens表示知识图谱中的实体和关系,并得到如下输入: 其中,[Entity h]表示头实体的特殊token。为编码图结构,对1跳邻居实体进行采样,并将它们连接起来作为隐式结构信息的输入。 通过统一的知识图谱编码器,PromptKG可以编码异构的图结构和丰富的文本语义信息。 模型中心(Model Hub) PromptKG由一个模型中心组成,支持许多基于文本的知识图谱表示模型。例如,KG-BERT使用BERT对三元组及其描述进行评分。但KG-BERT具有较高的时间复杂度,StAR和SimKGC都引入了一种基于塔的方法( a tower-based method)来预计算实体嵌入并高效地检索top-?实体。此外,GenKGC和KGT5将知识图谱补全视为序列到序列(seq2seq)的生成方法。此外,?NN-KGE是一种通过k近邻线性插值其实体分布的知识图谱表示模型。 模型支持的下游任务 应用到下游任务(Applying to Downstream Task) 以基于提示学的知识图谱表示学习为例,如下图所示: 对于知识图谱补全,向模型输入头实体和关系的文本信息⟨?ℎ,??⟩,然后通过掩码token预测获得目标尾实体。 对于问答,向模型输入用自然语言编写的问题,并将其与[MASK] token连接起来,以获得目标答案(实体)的特殊token。 在推荐方面,将用户的交互历史作为实体嵌入的顺序输入,然后利用掩码token预测来获得推荐项目。 对于知识探测任务,采用实体嵌入作为额外的知识,以帮助模型更好地通过句子进行推理,并预掩码位置的token,遵循PELT。 知识图谱表示学习应用于不同的下游任务 Entity_t指不同任务的目标尾实体、答案实体、推荐项目和目标尾实体,它遵循预训练(获得嵌入)和微调范式(特定任务调优)。 实验结果 1)基于文本信息的知识图谱补全(链接预测)是知识图谱表示的直接下游任务;2)问答是一种直观的知识密集型任务;3)推荐涉及与真实世界知识图谱中的实体对齐,因此可以从知识图谱表示中受益;4)知识探究(LAMA)利用完形问句分析语言模型中包含的事实知识和常识知识。 知识补全(Knowledge Graph Completion) 针对知识图谱补全任务,论文在数据集WN18RR和FB15k-237上进行了链接预测实验,并在PromptKG上使用hits1和MRR指标对模型进行了评估。从表中,基于判别的方法SimKGC比其他基线模型取得了更高的性能。KGT5和GenKGC等基于生成的模型也产生了可比较的结果,并显示了知识图谱表示的潜在能力。?NN-KGE可以从知识存储中通过计算最近邻居实体嵌入空间的距离和两步训练策略的来获得最佳hits1分数。 知识问答(Question Answering) 将PromptKG应用于问答系统,并在MetaQA数据集上进行实验。由于计算资源的限制,论文只评估了1跳推断性能。从表中可以看出,PromptKG中的KGT5具有最佳性能。 知识推荐(Recommendation) 对于推荐任务,在一个完善的版本ML-20m上进行了实验。利用ML-20m与KB4Rec提供的Freebase的链接,获取ML-20m中电影的文本描述。在这些描述上预训练电影嵌入后,按照BERT4Rec的设置在顺序推荐任务上进行了实验。与BERT4Rec相比,PromptKG被证实是有效的推荐。 知识探索(Knowledge Probing) 知识探索检查语言模型(BERT、RoBERTa等)召回事实的能力。使用预训练BERT (BERT -base-uncase)和RoBERTa (RoBERTa -base)模型在LAMA上进行实验。为证明由知识图谱增强的实体嵌入有助于语言模型从预训练中获取更多事实知识,训练了一个遵循PELT的可插拔实体嵌入模块。如表所示,当使用实体嵌入模块时,性能有所提高。由于Squad中没有标注的主题实体,Concept Net中也没有对应主题实体的URI进行实体对齐,因此只对LAMA中的剩余数据进行实体嵌入。 结论 论文提出PromptKG,一种知识图谱表示学习和应用的提示学习框架。PromptKG建立了一个统一工具包,该工具包具有定义明确的模块和易于使用的接口,以支持在知识图谱上使用PLMs的研究。PromptKG为研究人员和开发人员提供有效和高效的训练代码,并支持下游任务。 ### PMP-十五至尊图 项目是为创造独特的产品、服务或成果而进行的临时性工作。 知识领域启动过程组规划过程组执行过程组监控过程组收尾过程组项目整合管理1. 制定项目章程2. 制定项目管理计划3. 指导与管理项目工作4. 管理项目知识5. 监控项目工作6. 实施整体变更控制7. 结束项目或阶段项目范围管理1. 规划范围管理2. 收集需求3. 定义范围4. 创建WBS5. 确认范围6. 控制范围项目进度管理1. 规划进度管理2. 定义活动3. 排序活动4. 顺序估算活动持续时间5. 制定进度计划5. 控制进度项目成本管理1. 规划成本管理2. 估算成本3. 制定预算4. 控制成本项目质量管理1. 规划质量管理2. 管理质量3. 控制质量项目资源管理1. 规划资源管理2. 估算活动资源3. 获取资源4. 建设团队5. 管理团队6. 控制资源项目沟通管理1. 规划沟通管理2. 管理沟通3. 监督沟通项目风险管理1. 规划风险管理2. 识别风险3. 实施定性风险分析4. 实施定量风险分析5. 规划风险应对6. 实施风险应对7. 监督风险项目采购管理1. 规划采购管理2. 实施采购3. 控制采购项目相关方管理1. 识别相关方2. 规划相关方参与3. 管理相关方参与4. 监督相关方参与十五至尊图 项目数据、信息和报告流向 ### 论文笔记:Semantics Driven Embedding Learning for Effective Entity Alignment 论文:https://github.com/zhongziyue/SDEA/tree/main/paper 代码:https://github.com/zhongziyue/SDEA SDEA一种语义驱动的实体嵌入方法,用于实体对齐。SDEA由属性嵌入和关系嵌入两个模块组成。属性嵌入通过预训练的基于transformer的语言模型从属性值中捕获语义信息。关系嵌入使用带有注意力机制的GRU模型选择性地聚合邻居节点的语义信息。属性嵌入和关系嵌入都以语义为驱动,在实体之间建立桥梁。 为了更好地捕捉实体内在的语义(主要体现在长文本属性值中),并有效识别邻居实体在对齐过程中的贡献,提出了一种语义驱动的实体嵌入方法SDEA。该文采用两个嵌入模块,即属性嵌入和关系嵌入。在属性嵌入模块中,使用基于transformer的预训练语言模型来获得每个实体的初始嵌入。最重要的是,该模块捕获了细粒度语义和实体的直接关联。进一步,关系嵌入模块通过注意力机制从属性嵌入的细粒度语义中学习邻近实体的贡献,并选择性地聚合来自邻近实体的信息。此外,从属性嵌入(保留实体的语义)和关系嵌入(聚合邻居实体的语义)进行联合表示学习,以发现实体之间的间接关联。 SDEA框架 属性嵌入模块(Attribute Embedding Module) 属性嵌入模块旨在从实体的属性值中捕获实体之间的语义关联。属性值不仅包括短文本和数字,还包括长句子。从属性值中捕获语义信息一直是一个挑战。为了解决这个问题,使用Transformer,该模型在捕获文本语义信息方面达到了最先进的性能,以处理文本中的异构性。此外,为了处理不同KG模式的异构性,通过将实体的所有属性值组合为一个整体来捕获细粒度语义,然后捕获两个实体之间的语义关联。将属性嵌入形式化为BERT的下游任务,即对预训练的BERT进行微调,将实体ei的属性值编码为属性嵌入Ha(ei)在以下两个阶段。 1)数据预处理:此阶段旨在将实体ei的属性值转换为序列,即一系列tokens,然后可以将其输入到BERT模型中。算法1描述了该步骤的总体过程。 例如,下图说明了算法1中的过程。 2)属性编码:此阶段将前一步生成的序列作为输入,目的是通过BERT模型将序列转换为嵌入。使用预训练的BERT模型和MLP (Multi-Layer Perceptron)层,通过对实体ei的属性序列S(ei)进行编码,得到属性嵌入向量Ha(ei)。 关系嵌入模块(Relation Embedding Module) 在关系嵌入模块中,使用基于GRU的注意机制来建模邻居的贡献,并选择性地聚合这些信息。直观地说,给定一个实体ei,它的邻居ej的贡献将取决于ei的其他邻居。替代方法包括平均邻居的嵌入、池化和直接使用注意机制。相比之下,Bidirectional GRU (BiGRU)模型能够捕捉ei不同邻居之间的相关性,从而可以根据上下文信息(周围邻居)对不同实体的同一邻居的不同贡献进行建模。 用BiGRU捕获邻居间的相关性,直观地,将实体ei的所有邻居作为BiGRU模型的输入序列。注意机制使模型能够利用每个邻居的重要性,从邻居中提取最相关的信息。 联合实体表示(Joint Entity Representation) 给定一个实体ei,属性嵌入模块和关系嵌入模块分别计算其属性嵌入Ha(ei)和关系嵌入Hr(ei),分别从属性和邻居中获取信息。为了联合建模来自属性和邻居的信息,计算了一个联合表示Hm(ei),通过一个MLP层将Ha(ei)和Hr(ei)结合在一起。 因此,三个嵌入Ha(ei)、Hr(ei)和Hm(ei),分别捕获属性信息、邻居信息以及联合属性和邻居信息。最后的实体嵌入内容(ei)是这些嵌入的连接,它捕获信息的所有三个方面。 ### 论文笔记:SelfKG: Self-Supervised Entity Alignment in Knowledge Graphs 前言 论文:https://arxiv.org/pdf/2203.01044.pdf 代码:https://github.com/THUDM/SelfKG 摘要 实体对齐是构建网络规模知识图谱的一个基本问题,旨在识别不同知识图谱中的等价实体。在其发展过程中,标签监督被认为是准确对齐的必要条件。受自监督学习最近进展的启发,探讨了在实体对齐中可以在多大程度上摆脱监督。通常,标签信息(正实体对)用于监督将每个正实体对中的对齐实体拉得更近的过程。然而,论文的理论分析表明,在实际实体对齐的学习中,将未标记的负对推远比将标记的正对拉近受益更多。利用这一发现,为实体对齐制定了自监督学习目标。论文提出SelfKG的有效策略来优化这一目标,以对齐没有标签监督的实体。在基准数据集上的广泛实验表明,无监督的SelfKG可以与最先进的有监督基线相匹配或取得相当的结果。SelfKG的性能表明,自监督学习为知识图谱中的实体对齐提供了巨大的潜力。 挑战 基于表示学习的对齐方法以其优越的灵活性和准确性成为实体对齐的主流解决方案。然而,它们的成功在很大程度上依赖于人工标注提供的监督信息,而对于网络规模的知识图谱来说,这可能是有偏差的,而且获取起来成本非常高。鉴于这一基本挑战,本文旨在探索在没有标签监督的情况下,跨知识图谱对齐实体的潜力。 分析过程 从概念上讲,对于来自两个知识图谱的每个成对实体,现有学习目标是,使相同的实体(即正对)彼此更相似,使不同的实体(即负对)不相似。在嵌入空间中,通过将对齐的实体拉得更近,将不同的实体推得更远来实现这一目标。 在上述实体对齐的过程中,首先,监督信息是为了拉近一致实体的距离。其次,另一个问题是标签感知负对的生成过程。对于知识图谱中的每个实体,在训练中,其负对是通过从其他知识图谱中随机抽样实体形成的,同时排除groundtruth。如果没有监督,隐式对齐的实体很可能被采样为负对,从而破坏训练(即碰撞)。 贡献及解决方案 论文介绍了知识图谱中的自监督实体对齐问题,提出了Self KG框架,不依赖于标记实体对来对齐实体。它由3个技术部分组成:1)相对相似性度量;2)自负采样;3)多负队列。 为了摆脱标签监督,从理论上提出了相对相似度度量(relative similarity metric, RSM)的概念,实现自监督学习目标。RSM的核心思想是,它不是直接将对齐的实体在嵌入空间中拉得更近,而是试图将未对齐的负向量推得更远,从而避免使用正向量对的监督。相对而言,在优化RSM时,(隐式)对齐的实体可以被认为是被拖在一起的。 为了解决标签感知负采样的监督和无标签感知负采样的假负样本碰撞之间的两难问题,SelfKG进一步提出了自负采样策略,即对于KG中的每个实体,通过直接从同一KG中采样实体来形成其负对。换句话说,SelfKG仅依赖于从输入KG中随机抽样的负实体对。理论上表明,这种策略对于跨知识图谱对齐实体仍然有效。 最后,理论分析还表明,随着负样本数量的增加,自监督损失误差项衰减得更快,即大量的负样本有利于SelfKG。然而,实时编码大量负样本的计算成本非常高。本文通过扩展MoCo技术来解决这个问题,以支持两个负队列,两个队列对应于两个对齐的KGs,确保负样本的有效增加。 问题定义 知识图谱可以表示为一组三元组的集合T,每个三元组表示为两个实体??∈?和??∈?之间及它们的关系???∈?。在这篇论文中,将一个知识图谱表示为?={?,?,?},其中?,?和?分别是它的实体集、关系集和三元组集。 对于两个知识图谱,??={??, ??, ?? }和??={??, ??, ?? },存在的实体对的集合定义为?={(??)|?∈??, ?∈??, ?⇔?},其中⇔表示等价。在??和??之间进行实体对齐的目标是,为?y中的每个实体从??中找到等价的实体,如果存在的话。 自监督实体对齐 SelfKG框架 统一空间学习(Uni-space learning) 简单地说,将来自不同知识图谱的实体嵌入到统一空间中可以极大地有利于对齐任务。通过标注的实体对,很自然地利用监督信息将不同的空间对齐为一个。例如,合并对齐的实体进行训练,或学习具有丰富的训练标签的投影矩阵,以将来自不同嵌入空间的实体投影到统一空间。 对于多语言数据集(如DBP15K),该问题更具挑战性。由于预训练语言模型,现在可以使用高质量的多语言初始嵌入。例如,多语言BERT在最近的工作中被使用。在SelfKG中,采用LaBSE——一种在109种不同语言上训练的最先进的多语言预训练语言模型——将不同的知识图谱嵌入到统一空间中。 邻域聚合(Neighborhood aggregator) 为了进一步改善实体嵌入,使用邻域聚合将邻居实体的信息聚合到中心实体。本文直接使用具有一层的单头图注意力网络来聚合一跳邻居的预训练嵌入。 实体对齐问题上很多工作探索了利用多跳图结构。虽然一些研究称它们从多跳邻居中受益,但另一些研究认为单跳邻居在大多数情况下提供了足够的信息。在消融研究中,发现多跳信息实际上损害了SelfKG的性能,这可能是由于在自监督设置中可能不可忽视的远邻噪声。为了证明实体对齐的自监督的最低要求,在聚合过程中只涉及一跳邻居实体。 相对相似度度量(Relative Similarity Metric) 在表示学习中,间隔损失和交叉熵损失被广泛采用作为相似度度量。它们可以表示为噪声对比估计(Noise Contrastive Estimation,NCE)的形式。 给定一对对齐的实体(x, y),有监督NCE损失为: 其中,f为编码器,满足∥?(·)∥=1。该公式,将- log(1/x),变为logx,再使用log(M/N)=logM-logN。推导出来。 其中“alignment”项是将正的对拉近,而“uniformity”项是将负的对推开。在图2(左)中可以找到知识图谱中的“拉”和“推”实体对的例子。 定理1:对于固定?>0,当负样本数量?→∞时,(归一化)对比损失LNCE(即LASM)收敛到极限,绝对偏差衰减时间为O(?−2/3)。如果一个完全均匀编码器?存在,它形成“uniformity”项的精确最小化。 上式NCE损失成为需要监督信息的绝对相似度度量。然而,尽管知识图谱中实体可能存在歧义和异构性,但对齐的实体对即使不是完全相同的名称,也应具有相似的语义。此外,预训练的词嵌入通过在嵌入空间中投影相似实体来捕获这种语义相似性,从而可以确保方程中相对较大的?(?)??(?),即“alignment”项。 因此,要优化NCE损失,主要任务是优化公式中的“uniformity”项,而不是“alignment”项。考虑到?的有界性质,得到LASM的无监督上界,如下所示: 总是有上式相对相似度度量加上一个由常数控制的绝对偏差作为LASM的上限。通过优化LRSM,通过未对齐的实体被推远,相当于对齐的实体被推近。换句话说,如果不能将对齐的实体拉近(例如,没有正标签),可以将那些未对齐的实体推得足够远。 通过分析实体对齐常用的NCE损失,发现将这些随机采样的(负)对推远比将对齐的(正)对拉近更能使训练受益。因此,在SelfKG中,只专注于试图将负实体对推远,这样就可以摆脱对正实体对(标注的实体对)的使用。 自负采样(Self-Negative Sampling) 实体对齐在没有监督的情况下,SelfKG的重点是抽样负实体对——一个实体来自知识图谱??,另一个实体来自另外的知识图谱??。在负采样过程中,如果没有对标签感知的负采样的监督,底层对齐的实体对很可能被采样为负对,即发生碰撞。通常情况下,如果采样的负数很少,则可以忽略这种碰撞概率;但大量的负样本对SelfKG的成功至关重要,在此情况下,碰撞概率不可忽略。为了缓解这个问题,考虑到??和??具有统一空间,论文建议对实体?∈??从??中提取负样本。通过这样做,可以简单地排除?,即自负采样,从而避免冲突。 然而,可能会因此引起另外两个问题。首先,由于现实世界的数据质量存在噪声,??中可能经常存在多个重复?,这些重复?可能被采样为负值。这也是监督设置面临的挑战,其中一些重复?也可能存在于??中。通过一些证明,表明一定数量的噪声不会影响NCE损失的收敛。 定理2:设平均复制因子?∈N +,?∈R +为常数。噪声ASM表示如下,它仍然收敛到相同的ASM极限,绝对偏差衰减为O(?−2/3)。 第二个问题是,通过将负样本?-?∈??改为?-?∈??,需要确认LRSM是否仍然对实体对齐有效。根据经验,对于选定的负样本?-j∈??,可以预期有一些部分相似的?-?∈??。由于??和??共享编码器?,?(?-?)的优化也将有助于?(?-j)的优化。为了证明这一点,提供以下定理。 定理3:让Ωx,Ωy为知识图谱三元组的空间,分别{?-?: Ωx→R?}??= 1,{?-?: Ωy→R?}??=1随机变量分别服从概率分布??和??,S?−1表示统一的空间。如果存在一个随机变量?:R?→S?−1,使?(?-?)和?(?-?)在S?−1满足相同的分布,1≤?≤?。则有: 在SelfKG中,统一空间学习条件保证了两个知识图谱的最终统一表示,初始??和??相似但不完全相同,这表明自负采样是必不可少的。然而,随着训练的继续,编码器将得到改进,因为定理2保证使两个知识图谱更对齐。换句话说,??和??的实体嵌入可以看作是来自更大空间中的单个分布的样本,即?? =??。这反过来又使?的存在更容易实现。在实践中,联合优化??和??上的损失,如下所示,图2(右)和图3也说明了这一点。 多负队列(Multiple Negative Queues) 增大负样本的数量自然会导致额外的计算成本,因为在运行中编码大量的负样本是计算成非常高。为解决该问题,论文提出将MoCo技术扩展到SelfKG。在MoCo中,维护一个负队列来存储之前编码的batches作为已编码的负样本,该队列以有限的代价存储数千个已编码的负样本。 为了适应SelfKG中的自负采样策略,实际上维护了两个负队列,分别与两个输入知识图谱相关联。图3显示了一个说明性的示例。一开始,不会实现梯度更新,直到其中一个队列达到预定义的长度1+?,其中“1”是当前batch,?是负样本以前batches的数量,|?|是知识图谱中实体的数量,N为batch size。 可以保证不会对当前批次中的实体进行采样。因此,当前批次实际使用的负样本数量为(1 +?)×?−1。 动量更新(Momentum update) 负队列带来的主要挑战是编码样本的过时,特别是那些在训练早期编码的样本,在此期间模型参数会发生巨大变化。因此,只使用一个经常更新的编码器的端到端训练实际上可能会损害训练。为缓解这一问题,采用动量训练策略,维护两个编码器——在线编码器和目标编码器。虽然在线编码器的参数?online通过反向传播即时更新,但用于编码当前批次并将其推入负队列的目标编码器?target则通过动量进行异步更新: 一个合适的动量不仅对稳定的训练很重要,而且还可以通过避免表示崩溃影响最终的表现。 总结 论文提出SelfKG用于自监督实体对齐。图2说明了这一点。相对相似度度量(RSM)将?的非对齐实体推得足够远,而不是直接将底层对齐的?拉到?(标记对)附近,从而实现无标签监督的学习;自负性抽样从??中为?采样负实体,以避免采样真实的?作为其负实体。图3展示了SelfKG的训练。它利用现有的技术——来自预训练语言模型的嵌入和邻域聚合器——来初始化统一空间中的实体嵌入。 论文目标是在不同的知识图谱中对齐具有相同含义的实体。基于得出的三个见解——统一空间学习、相对相似度度量和自负采样,开发了一种自监督实体对齐算法SelfKG,可以在不使用标注数据训练的情况下自动对齐实体。在两个广泛使用的基准数据集DWY100K和DBP15K上的实验表明,SelfKG能够击败或匹配大多数利用训练数据集的监督对齐方法。该发现表明,在实体对齐问题中摆脱监督具有巨大的潜力,希望进行更多的研究,以加深对自监督学习的理解。 ### 论文笔记:Supporting Clustering with Contrastive Learning 论文:https://arxiv.org/pdf/2103.12953.pdf 代码:https://github.com/amazon-science/sccl 概述 无监督聚类的目的是根据在表示空间中测量的一定距离发现数据的语义类别。然而,在学习过程的开始阶段,不同的类别往往在表示空间中相互重叠,这对基于距离的聚类在实现不同类别之间的良好分离提出了重大挑战。为此,论文提出了用对比学习支持聚类(SCCL)——一个利用对比学习促进更好分离的新框架。论文实验证明SCCL在利用自底向上实例识别和自顶向下聚类的优势方面的有效性,在使用真实聚类标签进行评估时,可以获得更好的簇内和簇间距离。 即使使用深度神经网络,在聚类开始之前,数据在不同类别之间仍然存在显著重叠。因此,通过优化各种基于距离或相似度的聚类目标学习到的聚类纯度较低。 实例对比学习(Instance-CL)通常在通过数据增强获得的辅助集上进行优化。顾名思义,对比损失用于将原始数据集中同一实例的增强样本拉到一起,而将不同实例的增强样本推开。Instance-CL将不同的实例分散开来,同时在某种程度上隐式地将相似的实例聚集在一起。通过将重叠的类别分散开来,可以利用这种有益的属性来支持聚类。然后进行聚类,从而更好地分离不同的簇,同时通过明确地将该簇中的样本集中在一起,使每个簇更紧密。如图所示: 挑战 由噪声和稀疏性引起的弱信号给短文本聚类带来了重大挑战。 贡献 提出了一种新的无监督聚类的端到端框架,在很大程度上提高了各种短文本聚类数据集的最先进结果。此外,该模型比现有的基于深度神经网络的短文本聚类方法要简单得多,这些方法通常需要多阶段独立训练。 深入分析并展示了SCCL如何有效地将自上而下的聚类与自下而上的实例对比学习相结合,以实现更好的簇间距离和簇内距离。 本文探索了SCCL的各种文本增强技术,表明与图像域不同,在文本域使用增强的组合并不总是有益的。 SCCL模型算法 算法架构 如图所示,模型由三个组件组成。神经网络ψ(·)首先将输入数据映射到表示空间,然后在表示空间中使用两个不同的头g(·)和f(·),分别应用对比损失和聚类损失。 实例级对比学习(Instance-wise Contrastive Learning) 假设在一个batch内有M个样本,每个样本增强两个样本后,增强的样本集为2M个样本,在增强的样本中,存在2个正例和2M-2个负例。对于增强的样本x1,通过最小化损失函数将样本x2区分开。zj = g(ψ(xj)), j = i1, i2 在增强后的样本集中,损失为 聚类 聚类这部分的算法主要来自于论文《Unsupervised Deep Embedding for Clustering Analysis》 通过无监督聚类将语义分类结构编码到表示中。与Instance-CL不同,聚类关注的是高层语义概念,并试图将同一语义类别的实例聚集在一起。 假设数据由K个语义类别组成,每个类别以其在表示空间中的质心为特征,表示为μK, K∈{1,…K}。让ej = ψ(xj)表示实例xj在原始集合B中的表示。使用学生的t分布来计算将xj分配到第k个簇的概率为 使用一个线性层,即图2中的簇头,来近似每个簇的质心,并通过利用Xie等人(2016)提出的辅助分布来迭代完善它。具体地,设pjk表示辅助概率定义为 在这里,fk可以被解释为在一个minibatch内近似的软簇频率。该目标分布首先对软分配概率qjk进行2次幂锐化,然后利用相关的簇频率对其进行归一化。这样做鼓励从高置信度的聚类分配中学习,同时克服不平衡聚类造成的偏差。通过优化簇分配概率之间的KL散度,将簇分配概率推向目标分布。 聚类的损失函数为 总体的目标函数为 ### 知识补全和问答方法调研 Pipeline方法 知识补全链接预测 SQUIRE: A Sequence-to-sequence Framework for Multi-hop Knowledge Graph Reasoning 数据集知识库比较小 SMORE: KNOWLEDGE GRAPH COMPLETION AND MULTI-HOP REASONING IN MASSIVE KNOWLEDGE GRAPHS 算法SMORE适用于较大知识库 知识问答 Subgraph Retrieval Enhanced Model for Multi-hop Knowledge Base Question Answering 子图检索和推理过程是解耦的,基于embedding-based KBQA的SOTA方法。子图检索和推理过程是pipline的。 基于pipeline方法总结 1、知识补全两篇论文中只做了链接预测相关的实验,没有给出下游问答的任务 联合训练 Sequence-to-Sequence Knowledge Graph Completion and Question KGT5算法:链接预测和问答联合训练 该算法的局限 KGT5在归纳不可见的事实方面表现较好,但在记忆事实方面表现较差。如果需要,可以使用KGT5与常规链路预测或KGQA系统的集成来缓解这个问题。 作者不建议使用KGT5作为独立的KGQA方法,只应在查询解析不能产生良好结果的情况下使用它。 ### 知识表示学习方法调研 NeuralKG https://github.com/zjukg/NeuralKG NeuralKG: An Open Source Library for Diverse Representation Learning of Knowledge Graphs ComponentsModelsKGEModelTransE(2013), TransH(2014), TransR(2015), ComplEx(2016), DistMult(2014), RotatE(2019), ConvE(2018), BoxE(2020), CrossE(2019), SimplE(2018)GNNModelRGCN(2017), KBAT(2019), CompGCN(2020), XTransE(2020)RuleModelComplEx-NNE+AER(2018), RUGE(2018), IterE(2019) 知识表示学习常用的方法 来自第四范式技术分享 https://yeu.h5.xeknow.com/sl/3BzAvx https://github.com/AutoML-Research Triplet-based models Translational Distance Model(TDM) 2013,TransE 2014,TransH 2019,TotatE Neural Network Model(NNM) 2014,MLP 2018,ConvE 2020,RSN BiLinear Model(BLM) 2011,RESCAL,关系为方阵 2014,DistMult,关系为对角矩阵 2016,CompIEx, 2017,Analogy 2018,SimpIE 2019,QuatE 2020,AutoSF,BLMs – unified form 2021,AutoSF+,An AutoML approach to design bilinear scoring functions Bilinear models are the best choice in triplet-based models Relational path 2015,PTransE 2019,RSN,Models long-term information well, but is limited in modeling short-term information 2020,Insterstellar,A NAS approach to recurrently process the relational paths The relational paths contain richer information than triplets GCN-variants 2017,R-GCN 2020,CompGCN 2020,GraIL,Enclosing subgraph -> Entity labeling - > GNN scoring 2021,KE-GCN 2022,RED-GNN ### 论文笔记:HAO Unity: A Graph-based System for Unifying Heterogeneous Data HAO Unity系统架构 基于图的异构数据集成系统HAO Unity,HAO Unity系统整体框架如下图所示,主要组件包括:物理统一组件、语义统一组件、数据探索组件。物理统一组件负责通过属性图交换不同格式的数据。语义统一组件则从两方面来统一属性图中的异构数据:schema和instance。数据探索组件提供一个查询元数据、实体和关系信息的接口,方面用户了解集成后的数据,并进一步构建下游应用。 物理统一组件 物理统一组件包括两部分:数据接入和数据导出。数据接入负责从不同数据源接收格式各异的数据(各种关系数据库、NoSQL数据库或者半结构化文件),并将这些数据转换至原始属性图。具体来说,采用了一些数据转换规则:(1) 关系表中的每个元组均转换为属性图中的节点;(2) 关系表中的属性转换为属性图中节点的属性;(3) 关系表中的外键转换为属性图中节点间的关系。数据导出是数据接入的逆向过程。 数据导入 多种格式数据分别对应我们的结构化和非结构化数据格式,目前没有考虑半结构化文件。结构化数据知识抽取论文可以解决CSV、Execl、Oracle、MySQL等数据。 数据导出 论文的数据导出是数据导入的逆向过程。 语义统一组件 在HAO Unity中,语义统一主要包括schema integration和entity consolidation。由于完全自动的异构数据处理效果不足够好,因此HAO Unity遵循HAO模型的理论,集成各类智能。 Schema Integration 具有相同含义但来自不同数据源的数据,可以被不同的schema描述,因此需要进行schema对齐来解决语义歧义的问题。该论文通过元数据中心的方式来实现schema对齐。过程主要包括:(1) 计算原始数据的schema与被对齐的元数据中心预定义的schema相似度;(2) 相似度高于阈值则对齐,低于阈值则更新元数据中心;(3) 自动集成后,提供一个接口支持手动纠正结果。经过schema integration后,所有实体均具有统一的schema描述。 对于业务图谱来说,图谱的实体和关系量级相对比较少,schema的也相对比较少,人工对schema进行校对可以更省时,更准确。所以目前对于小规模的图谱先采用人工校验schema对齐方案。 Entity Consolidation 为了提供实例级别的统一数据,需要将属性图中相同语义的实体进行去重,该问题可以被形式化为二分类问题。HAO Unity使用了基于预训练语言模型的SOTA方法,并在其基础上做了两项改进。第一个改进是改变文本的序列化方式;第二个改进是使用了一些剪枝策略来加速匹配的过程。首先,基于字符串相似度过滤一部分待匹配实体对;其次,使用SVM模型进行匹配;最后,将SVM匹配置信度不高的样本交由Ditto进行匹配。 除了自动化的方法外,论文中的系统还提供了一个用户接口来人工纠正实体匹配的结果。 HAO Human intelligence:人类智能是指其人员的领域专业知识,系统使用人类智能来确认和纠正统一结果。 Artificial intelligence:人工智能体现在机器人的智能表现上,人工智能帮助自动处理。 Organizational intelligence:组织智能涉及到自定义的文化规则和商业精神的商业目标和约束,组织智能预定义元数据,帮助关系抽取。 HAO Unity提供10项具体的功能,包括:数据导入、数据集成、实体消歧、实体分析、关系抽取、隐含关系抽取、数据概览、数据查询、数据导出和系统介绍。 数据探索组件 数据探索组件提供不同粒度的数据检索功能。用户可以直接查询属性图中的所有数据,也可以获取属性图中三种不同粒度的信息。(1) entity-level: 用户可以查询实体和其属性以及一些统计信息;(2) relationship-level: 用户可以查询一跳或者多跳关系;(3) 用户可以查询各类元数据。 ### D2R调研 RDB2RDF 官方 RDB2RDF 标准,W3C 的 RDB2RDF 工作小组制定的两个标准,用于将关系型数据库的数据转换为RDF格式的数据。 1、Direct Mapping of Relational Data to RDF The Direct Mapping is an automatic mapping of a relational database to RDF. ps:不可自定义的默认映射。当关系数据很好地规范化,有主键,外键等时,直接映射是合适的。缺点:不能把数据库的数据映射到自己定义的本体上。 2、R2RML: RDB to RDF Mapping Language R2RML is a customizable language to map a relational database to RDF. ps:可自定义的映射。 RDB2RDF工具 Ontop Ontop is a Virtual Knowledge Graph system. It exposes the content of arbitrary relational databases as knowledge graphs. These graphs are virtual, which means that data remains in the data sources instead of being moved to another database. Ontop translates SPARQL queries expressed over the knowledge graphs into SQL queries executed by the relational data sources. It relies on R2RML mappings and can take advantage of lightweight ontologies. SparqlMap A SPARQL to SQL rewriter based on R2RML specification. D2RQ D2RQ 提供了自己的 mapping language(D2RQ Mapping Language),其形式和 R2RML 类似。 Triplify  Triplify is a small PHP plugin for Web applications, which reveals the semantic structures encoded in relational databases by making database content available as RDF, JSON or Linked Data. D2RQ D2RQ的官方介绍是Accessing Relational Databases as Virtual RDF Graphs。http://d2rq.org/ D2RQ 工具,它的主要功能是提供以虚拟的、只读的RDF图形式进入到关系型数据库中。比如通过SPARQL端口查询,输入是SPARQL查询语言,D2RQ通过mapping文件将其转换为SQL语句在关系数据库上查询,因此实际上访问的是关系型数据库。同时也可以使用它从数据库创建 RDF 格式的文件。 D2RQ 由自己的mapping语言,R2RML-kit。它和W3C推荐的R2RML类似。可以通过D2RQ提供的工具来根据数据库自动生成mapping文件。也可以根据自己的需求去修改定制mapping文件。 D2RQ的框架 D2RQ主要包括 D2RQ Server, D2RQ Engine 以及 D2RQ Mapping 语言。 D2RQ Server是一个HTTP Server,它的主要功能提供对RDF数据的查询访问接口,以供上层的RDF浏览器、SPARQL查询客户端以及传统的HTML浏览器调用。 D2RQ Engine的主要功能是使用一个可定制的D2RQ Mapping文件将关系型数据库中的数据换成RDF格式。D2RQ engine并没有将关系型数据库发布成真实的RDF数据,而是使用D2RQ Mapping文件将其映射成虚拟的RDF格式。该文件的作用是在访问关系型数据时将RDF数据的查询语言SPARQL转换为RDB数据的查询语言 SQL,并将SQL查询结果转换为RDF三元组或者SPARQL查询结果。D2RQ Engine是建立在Jena(Jena是一个创建Semantic Web应用的Java平台,它提供了基于RDF,SPARQL等的编程环境)的接口之上。 D2RQ Mapping一种用于将关系数据库模式映射到 RDF 词汇表和 OWL 本体的声明性语言。 D2RQ提供的功能 使用 SPARQL 查询非 RDF 数据库 通过 Web 以链接数据的形式访问数据库的内容 以 RDF 格式创建数据库的自定义转储以加载到 RDF 存储中 使用 Apache Jena API 访问非 RDF 数据库中的信息 Ontop和D2RQ比较 R2RML的文档(W3C推荐的映射语言)相比D2RQ mapping的表达能力和文档更清晰,且表达能力更强。D2RQ翻译SPARQL2SQL的过程缺乏query optimization,在大数据情况下性能比较差,经常会出现timeout或OOM(内存消耗比较严重的情况)。此外,他不支持推理。虽然D2RQ可以将KG映射到Jena Model上,通过Jena Reasoner在其上做各种inference,但是效率有很大的问题。而Ontop是支持推理的(backward chaining)。 结论 以虚拟RDF图的方式访问关系数据库是其最主要的一个特性。它的机理就是通过mapping文件,把对RDF的查询等操作翻译成SQL语句,最终在RDB上实现对应操作。当对外提供服务,查询操作比较频繁的情况下,最好是将RDB的数据直接转为RDF,会节省很多SPARQL到SQL的转换时间。 ### KMeans跑满CPU 问题描述 在调用sklearn.cluster中的KMeans时,cpu占用达100%,比如40核的cpu,最高4000%的cpu占用率 尝试解决方案 scikit-learn版本为0.24.2 from sklearn.cluster import KMeans kmeans = KMeans(n_clusters=k, random_state=123) kmeans.fit(self.embeddings) cluster_centers = kmeans.cluster_centers_ labels = kmeans.labels_ scikit-learn在0.23版本时会有n_jobs参数可以设置线程数,但是在降低版本之后,设置n_jobs参数依然会跑满cpu。下图是官方文档对n_jobs的解读。 考虑到之后高版本不会再使用n_jobs参数,所以未深究其中原因。 最终解决方案 在启动服务时设置线程数量可以解决该问题,如果设置为4个线程 OMP_NUM_THREADS=4 python run.py ### 论文笔记:RNG-KBQA: Generation Augmented Iterative Ranking for Knowledge Base Question Answering 论文:https://arxiv.org/pdf/2109.08678.pdf 整体架构 Enumeration of Candidates 实体识别和实体链接 Logical Form Ranking 排名器是基于BERT双编码器(以输入问题-候选对作为输入)经过训练,以最大限度地提高基本事实逻辑形式的分数,同时最大限度地减少不正确候选者的分数。 扩展了提出的逻辑形式排序器,保持架构和逻辑相同,用于实体消歧任务,并展示其作为第二阶段实体排序器的有效性。 Target Logical Form Generation 有了候选的排名列表,引入了一个生成模型来组成以问题为条件的最终逻辑形式和我们的排名器返回的前 k 个逻辑形式。 生成器是从 Raffel 等人实例化的基于转换器的 seq-to-seq 模型,因为它在生成相关任务中表现出强大的性能。 如图所示,通过连接问题和由分号分隔的排名器返回的前 k 个候选者(即 [x; ct1; ...; ctk])来构建输入。 GRAILQA 是第一个评估零样本泛化的数据集。 具体来说,GRAILQA 总共包含 64,331 个问题,并仔细拆分数据,以评估 KBQA 任务中的三个泛化级别,包括 i.i.d. 设置、构图设置(泛化到看不见的构图)和零镜头设置(泛化到看不见的 KB 模式)。 我们在图中展示了组合泛化( compositional generalization )和零样本泛化( zero-shot generalization)的示例。测试集中每个设置的分数分别为 25%、25% 和 50%。 除了泛化挑战之外,GRAILQA 还存在额外的困难,包括大量涉及的实体/关系、逻辑形式的复杂组合性(最多 4 跳)以及问题中提到的实体的噪声。 ### 论文笔记:Complex Knowledge Base Question Answering: A Survey 论文:https://arxiv.org/pdf/2108.06688.pdf 这篇论文是复杂KBQA方面的一篇综述。介绍了复杂 KBQA 的两大主流方法,即基于语义解析( semantic parsing-based, SP-based)的方法和基于信息检索(information retrieval-based, IR-based)的方法。从这两个类别的角度回顾了先进的方法并解释了对典型挑战的解决方案。 论文动机 Who is the first wife of TV producer that was nominated for The Jeff Probst Show? 在知识图谱中做多跳推理 (multi-hop reasoning) 考虑题目中给的限制词 (constrained relations) 考虑数字运算的情况 (numerical operations) 知识图谱问答系统思路 实体连接 (entity linking),识别问题q的主题实体eq,其目的是将问题链接到知识库中的相关实体。在这一步中,进行命名实体识别、消歧和链接。通常使用一些现成的实体链接工具来完成,例如 S-MART、DBpediaSpotlight和 AIDA; 利用答案预测模块来预测答案 Aq。可以采用以下两种方法进行预测: 基于语义解析 (SP-based) 方法:将问题解析为逻辑形式,并针对知识库执行以找到答案; 基于信息检索 (IR-based) 方法:检索特定于问题的图并应用一些Rank算法从顶部位置选择实体。 最后,将 KBQA 预测得到的预测答案 Aq 作为系统输出返回给用户 遇到的挑战 直接将传统知识图谱问答模型运用到复杂问题上,不管是基于语义解析的方法还是信息检索的方法都将遇到新的挑战: 传统方法无法支撑问题的复杂逻辑: 现有的 SP-based 的方法中使用的解析器难以涵盖各种复杂的查询(例如,多跳推理、约束关系和数值运算)。 以前的 IR-based 的方法可能无法回答复杂的查询,因为它们排名是在没有可追溯推理的情况下对小范围实体进行的。 复杂问题包含了更多的实体,导致在知识图谱中搜索空间变大:这两种方法都将问题理解视为首要步骤。当问题在语义和句法方面变得复杂时,模型需要具有强大的自然语言理解和泛化能力。 通常 Complex KBQA 数据集缺少对正确路径的标注:这表明 SP-based 的方法和 IR-based 的方法必须分别在没有正确逻辑形式和推理路径注释的情况下进行训练。这种微弱的监督信号给这两种方法带来了困难。 ### 论文笔记:improving-multi-hop-question-answering-over-knowledge-graphs-using-knowledge-base-embeddings 论文:https://aclanthology.org/2020.acl-main.412.pdf 论文概述 这篇文章结合知识补全的思想来改善知识库问答。通过自然语言理解这个问句并从知识库中寻找正确答案实体。多跳的问答则需要对关系路径进行推理。由于知识库不充分原因,现有的工作引入额外的文本弥补知识库稀疏问题。另外也有工作是通过目标实体从知识库中抽取多跳范围内的子图,在该子图内进行答案的检索。作者认为这是一种启发式的划定答案所在的领域。 稀疏和不完整 KG 中多跳 QA over Knowledge Graphs (KGQA) 的挑战:当寻找对应的答案时,由于知识库不充分,“Louis Mellis”与答案“Crime”没有直接的边相连,则需要经过较长的路径推理。而启发式的设定跳数为3时(灰色区域),使得答案不在这个领域内,促使无法寻找到答案。 本文的贡献点在于: (1)首次将知识库表征引入知识库问答任务; (2)不需要启发式地设定具体的领域范围; 整体架构 KG Embedding Module 作者使用ComplEx算法对整个知识库中的实体和边嵌入到固定大小的向量,作者之所以选择ComplEx算法,是因为该算法采用的是张量分解法,更能够捕捉更全面的特征。但在具体实验中,只获取实部的参数。 Question Embedding Module 对于问句 q, 作者使用 RoBERTa 预训练语言模型获得768维度的向量,并通过4层全连接层(激活函数为ReLU)映射到与知识表示相同维度的空间。在微调过程中,作者将问句替换到得分函数中的关系,这是一个比较巧妙的创新点,其借助ComplEx(或者说是知识表示学习的方法)的得分函数 ϕ 和排序损失函数训练的机制,促使目标实体 h和答案 t 之间的语义关系是问句q在复数空间中的表示。因为在有的知识库比较大,候选关系非常多,因此作者使用标签平滑方法。 Answer Selection Module 这一部分是核心,在已知知识图谱表征、每个三元组的得分以及问句的表征后,需要对候选答案进行筛选。对于知识库不大的可以按照上述的方法计算,但对于知识库较大的,则需要对候选的答案集合进行缩减(pruning),作者借鉴了PullNet算法,提出一种简单的缩减方法(Relation Matching)。 ### PyTorch训练模型转ONNX 方法一 加载Pipeline 从transformers库中加载FeatureExtractionPipeline。如果知道要使用的模型的所有输入形状和配置,则可以不需要此步骤。使用 convert_graph_to_onnx 中的相应函数可以显着简化自定义模型的创建。生成的变量将用于torch导出调用。 from transformers import FeatureExtractionPipeline, AutoModel, AutoTokenizer, convert_graph_to_onnx model_access = "my_model_dir" model_pipeline = FeatureExtractionPipeline( model=AutoModel.from_pretrained(model_access), tokenizer=AutoTokenizer.from_pretrained(model_access, use_fast=True), framework="pt", device=-1) config = model_pipeline.model.config tokenizer = model_pipeline.tokenizer with torch.no_grad(): input_names, output_names, dynamic_axes, tokens = convert_graph_to_onnx.infer_shapes(model_pipeline, "pt") ordered_input_names, model_args = convert_graph_to_onnx.ensure_valid_input( model_pipeline.model, tokens, input_names) # 如果想添加更多的输出,则必须相应地修改dynamic_axes和output_names。 del dynamic_axes["output_0"] # Delete unused output del dynamic_axes["output_1"] # Delete unused output output_names = ["output"] dynamic_axes["output"] = {0: 'batch'} # 导出模型到ONNX model = torch.load("best_model.pth") output = "best_model.onnx" torch.onnx.export( model, model_args, f=output, input_names=input_names, output_names=output_names, dynamic_axes=dynamic_axes, do_constant_folding=True, use_external_data_format=False, enable_onnx_checker=True, opset_version=11) # 检查 onnx model onnx_model = onnx.load(output) onnx.checker.check_model(onnx_model) print('The model is checked!') 加载onnx模型进行推理 import numpy as np from transformers import AutoTokenizer import onnxruntime as rt import time onnx_model_path = "best_model.onnx" model_path = "best-checkpoint" # my_model_dir tokenizer = AutoTokenizer.from_pretrained(model_path, use_fast=False) span = "输入" # 输入的文本 opt = rt.SessionOptions() sess = rt.InferenceSession(onnx_model_path) # Loads the model t0 = time.perf_counter() model_input = tokenizer.encode_plus(span) model_input = {name: np.atleast_2d(value) for name, value in model_input.items()} onnx_result = sess.run(None, model_input) onnx_result = onnx_result[0] onnx_result = np.argmax(onnx_result, axis=-1) print(time.perf_counter() - t0) 方法二 pt2onnx import os import torch from transformers import BertTokenizer, get_linear_schedule_with_warmup,BertForSequenceClassification, AdamW import onnx def pt2onnx_bert(): pretrained_model = '../model/bert-base-chinese' onnx_path = 'api/onnx/bert-base-chinese-cls.onnx' device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') tokenizer = BertTokenizer.from_pretrained(pretrained_model) model = BertForSequenceClassification.from_pretrained(pretrained_model) model.eval() model.to(device) input_names = ['input_ids', 'attention_mask', 'token_type_ids'] outputs_names = ['output'] # 构造输入 inputs = '输入' encode_dict = tokenizer.encode_plus(text=inputs, max_length=512, pad_to_max_length=True, return_tensors='pt', return_token_type_ids=True, return_attention_mask=True) input_ids = encode_dict['input_ids'] attention_mask = encode_dict['attention_mask'] token_type_ids = encode_dict['token_type_ids'] dummy_input = { 'input_ids': input_ids, 'attention_mask': attention_mask, 'token_type_ids': token_type_ids } with torch.no_grad(): torch.onnx.export(model=model, args=tuple(dummy_input.values()), f=onnx_path, opset_version=11, input_names=input_names, output_names=outputs_names, dynamic_axes={'input_ids': {0: 'batch_size'}, 'attention_mask': {0: 'batch_size'}, 'token_type_ids': {0: 'batch_size'}, 'output': {0: 'batch_size'}} ) # 验证 print(onnx.checker.check_model(onnx_path)) 推理 from onnxruntime import GraphOptimizationLevel, InferenceSession, SessionOptions, get_all_providers def create_model_for_provider(model_path: str, provider: str) -> InferenceSession: assert provider in get_all_providers(), f"provider {provider} not found, {get_all_providers()}" options = SessionOptions() #控制线程数 options.intra_op_num_threads = 0 options.graph_optimization_level = GraphOptimizationLevel.ORT_ENABLE_ALL session = InferenceSession(model_path, options, providers=[provider]) return session def onnx_inference(x): #CPUExecutionProvider ort_session = create_model_for_provider(onnx_model_path, 'CUDAExecutionProvider') encode_dict = tokenizer.batch_encode_plus(batch_text_or_text_pairs=x, max_length=MAX_SEQ_LEN, pad_to_max_length=True, return_tensors='pt', return_token_type_ids=False, return_attention_mask=True) inputs = {k: v.numpy() for k, v in encode_dict.items()} outputs = ort_session.run(None, inputs) # 不同的模型对应不同的以下代码 outputs = outputs[0] outputs = np.argmax(outputs, axis=-1) result = [] for out in outputs: result.append(id2label[out]) return result Cython编译python from distutils.core import setup from Cython.Build import cythonize from distutils.extension import Extension extensions = [ Extension(name='name1', sources=['source file'], include_dirs=['head files'], libraries=['library names'], library_dirs=['library directories']), Extension(name='name2', sources=['source file'], include_dirs=['head files'], libraries=['library names'], library_dirs=['library directories']) ] # setup.py 放在 sources同目录 # 按照上述方式配置,会生成两个so,name1.so和name2.so # 命令:python setup.pu build_ext,同时生成c文件和so setup( name='name', # language_level=3指定python3 ext_modules=cythonize(extensions, language_level=3) ) 参考 https://github.com/oborchers/sentence-transformers/blob/72da7293186700bff4fed8e81adcfe40a03be1d2/examples/onnx_inference/onnx_inference.ipynb https://zhuanlan.zhihu.com/p/422290231 ### 服务器后台远程复制文件scp 正常执行scp命令 从一台服务器复制文件夹到另一台服务器 scp -r origin_dir ip:target_dir 输入Ctrl+z暂停任务 使用bg命令将其放入后台 bg %1 使用jobs查看状态,已经在后台运行了 disown -h 将这个作业忽略HUP信号 查看运行状态和父进程号 ps -ef | grep scp 测试会话中断,任务继续运行不受影响 首先退出 exit 然后再查看状态 ps -ef | grep scp 参考 https://blog.csdn.net/sraing/article/details/85121543 ### 论文笔记:Few-Shot Learning with Siamese Networks and Label Tuning 前言 论文:https://arxiv.org/pdf/2203.14655v2.pdf 代码:https://github.com/symanto-research/few-shot-learning-label-tuning 摘要 在训练数据很少或没有训练数据的情况下构建文本分类器的问题,通常称为零样本和小样本文本分类。近年来,一种基于神经文本蕴涵模型的方法已被发现在不同的任务范围内提供强大的结果。在这项工作中表明,通过适当的预训练,嵌入文本和标签的Siamese网络提供了一个有竞争力的替代方案。这些模型大大降低推理成本:标签数量恒定,而不是线性。此外,还引入了标签调优,是一种简单且计算效率高的方法,它允许通过仅更改标签嵌入来在少量设置中适应模型。虽然提供比模型微调更低的性能,但这种方法具有体系结构上的优势,即单个编码器可以由多个不同的任务共享。 动机 对于文本数据,任务的标签通常是通过文本形式来实现的,可以是标签的名称,也可以是简明的文本描述。 Cross Attention(CA)和Siamese Networks(SNs)还通过对小样本的模型进行微调来支持小样本学习。这通常是通过更新模型的所有参数来完成的,导致不能在不同任务之间共享模型。 作者提出Label Tuning(LT)作为完全微调的替代方案,它允许对许多任务使用相同的模型,从而极大地增加了方法的可伸缩性。 模型 图1 Overview of training and inference with Label Tuning (LT) 在训练时,输入和标签文本由编码器处理。然后Label Tuning(LT)使用交叉熵(CE)损失调优标签。在推理时,输入文本通过相同的编码器。然后使用调优的标签嵌入和相似度函数对每个标签进行评分。编码器保持不变,可以在多个任务之间共享。 方法 Fine-Tuning 在小样本学习的情况下,需要在小样本的基础上对模型进行调整。在基于梯度的小样本学习中,尝试提高带有标签的小样本的相似度分数。从概念上讲,希望增加每个文本及其正确标签之间的相似度,并减少每个其他标签之间的相似度。使用batch softmax作为目标: 其中B是batch size,S(x, y) = f(x)·f(y)是当前模型f下输入x与标签文本y的相似度。在batch中,所有其他元素都作为负样本。为此,使每个batch中只包含每个标签的一个样本。这类似于典型的softmax分类目标。唯一的区别是f(yi)是在前向传播中计算的,而不是作为一个简单的参数查找。 Label Tuning 定期微调的缺点是需要更新整个网络的权重。这导致每个新任务都需要较慢的训练和较大的内存需求,这反过来又增加了大规模部署新模型的挑战。作为一种替代方案,引入标签调优,它不改变编码器的权重。主要思想是首先预先计算每个类的标签嵌入,然后使用小样本对它们进行调优。有一个包含N对输入文本xi及其参考标签索引zi的训练集。预先计算输入文本的矩阵表示和标签的矩阵表示,X∈RN×d,Y∈RK×d,d为embedding的维数,K为标签集的大小。每个输入和标签组合的相似度分数定义为S = X × YT(S∈RN×K),并利用交叉熵进行调优。 为了避免过拟合,使用范数添加了一个正则化,惩罚偏离初始标签嵌入太远的位置。另外,还通过在标签embedding中每个梯度步上mask一些项实现dropout。通过在小样本上4折交叉验证进行微调。要为每个调优模型存储的唯一信息是d维标签嵌入。 Knowledge Distillation 标签微调产生的模型比实际的微调精确度低。可以通过知识蒸馏来补偿。首先训练一个标准的微调模型,并使用该模型为未标注的样本生成标签分布。之后,将该数据集用于训练未调优模型的新标签embedding。这增加了该方法的训练成本,并增加了对未标注数据的额外要求,但保留了在推断时可以跨多个任务共享一个模型的优点。 结论 论文证明了Cross Attention(CA)和Siamese Networks(SN)在不同的任务集和多种语言中提供了类似的结果。SNs的推理成本很低,因为标签embedding可以预先计算,而且与CA相比,SNs不随标签数量的增加而扩展。 只调优标签embeddings(LT)是可以替代微调(FT)方法的。当训练样本数量较少并且结合知识蒸馏时,即在小样本学习场景下,LT的性能接近FT。这与生产场景相关,因为它允许在任务之间共享相同的模型。LT是快速和可扩展的小样本学习下的一个选择。 ### 论文笔记:Exploiting Cloze Questions for Few Shot Text Classification and Natural Language Inference 前言 论文:https://arxiv.org/pdf/2001.07676.pdf%EF%BC%89 代码:https://github.com/timoschick/pet 摘要 一些NLP任务通过使用预先训练好的带有“任务描述”的语言模型,可以以完全无监督的方式解决。虽然这种方法的性能不如监督方法,但本篇工作中表明,这两种思想可以结合起来:作者引入了Pattern-Exploiting Training(PET),一种半监督的训练过程,它将输入示例重新定义为完形句式短语,以帮助语言模型理解给定的任务。然后使用这些短语为大量无标签的样本分配软标签。最后,对得到的训练集进行标准监督训练。对于一些任务和语言,PET在低资源环境下的表现远远优于监督训练和强半监督方法。 模型架构 图1 PET for sentiment classification PET的工作过程分为三个步骤: 首先,针对每个模式,在小训练集T上对单独的PLM进行微调。 然后,使用所有模型的集合对一个大型的无标签数据集D进行软标签标注。 最后,在软标记数据集上训练标准分类器。 作者还设计了iPET,这是PET的迭代变体,该过程随着训练集规模的增加而重复。 Pattern-Exploiting Training 参数概况 M为预训练语言模型,词表为V,mask token包含在V中,任务A的标签集合L,输入序列x包含k个短语或句子。 定义一个模型函数(pattern)P,将x作为输入,输出包含一个mask token的短语或句子P(x),输出可以看作是一个完形填空。 定义一个映射函数(verbalizer)v,L—>V将每个标签映射到M的词汇表中的一个单词。 将(P, v)称为pattern-verbalizer pair(PVP)。 PVP Training and Inference 给定某个输入x,我们定义标签l∈L的分数为: 并使用softmax获得标签上的概率分布: 使用交叉熵作为微调的损失。 Auxiliary Language Modeling 只使用少量样本进行训练,可能发生灾难性遗忘。对于基于PVP的PLM微调,核心仍然是一个语言模型,我们通过使用语言建模作为辅助任务来解决这个问题。LCE表示交叉熵损失,LMLM表示语言建模损失,计算最终损失为: LMLM通常比LCE大得多,令α = 10−4 Combining PVPs 该方法面临的一个关键挑战是,在缺乏大型验证集的情况下,很难确定哪个PVPs表现良好。为了解决这个问题,使用了类似于知识蒸馏的策略。定义一个PVPs的集合P,直观地理解给定任务A的意义,按如下方法使用这些PVPs: 为每个p∈P微调一个单独的语言模型Mp 使用经过优化的模型的集合M = {Mp | p∈P}来标注集合D(未标注数据集合)。将每个样本x∈D的非归一化类分数合并为: 使用softmax将上述分数转换为概率分布q。使用T = 2得到适当的软分布。所有(x, q)对收集在一个(软标记的)训练集TC中。 在TC上,使用PLM微调得到模型C。经过优化的模型C作为对A的分类器。 图2 Schematic representation of PET (1-3) and iPET (a-c) Iterative PET (iPET) 将所有单个模型的知识蒸馏到单个分类器中——意味着它们不能相互学习。由于有些模式的性能可能比其他模式差得多,因此最终模型的训练集TC可能包含许多标签错误的示例。为了弥补这个缺点,设计了iPET,它是PET的迭代变体。iPET的核心思想是在不断增大的数据集上训练几代模型。过程如图2所示。 对于每个模型,其他模型的一个随机子集通过标注D中的样本生成一个新的训练集。 使用更大的、特定于模型的数据集训练一组新的PET模型。 前两个步骤重复k次,每次都将生成的训练集的大小增加d倍。 经过训练k代PET模型,我们使用Mk创建TC和训练模型C在基本的PET。 结论 为预训练的语言模型提供任务描述可以与标准的监督训练相结合。PET方法包括定义成对的完形填空问题模式(patterns)和语言表达器(verbalizers),这有助于利用预训练的语言模型中包含的知识来完成下游任务。对所有模式-语言化器对的模型进行微调,并使用它们创建大型的带注释的数据集,标准分类器可以在这些数据集上进行训练。当初始训练数据量有限时,PET比标准监督训练和强半监督方法有很大的改进。 ### Git入门篇 git常用命令 请使用status命令确认工作树和索引的状态。 $ git status 将文件加入到索引,要使用add命令。在指定加入索引的文件。用空格分割可以指定多个文件。 $ git add .. 指定参数「.」,可以把所有的文件加入到索引。 $ git add . 请执行如下显示的commit命令。 $ git commit -m "" 执行commit命令之后确认状态。 $ git commit -m "first commit" $ git status # On branch master nothing to commit (working directory clean) 从status响应我们可以看到没有新的变更要提交。 使用log命令,我们可以在数据库的提交记录看到新的提交。 $ git log 可以将远程数据库命名为“origin”。 请使用remote指令添加远程数据库。在处输入远程数据库名称,在处指定远程数据库的URL。 $ git remote add git remote add origin 执行推送或者拉取的时候,如果省略了远程数据库的名称,则默认使用名为”origin“的远程数据库。因此一般都会把远程数据库命名为origin。 使用push命令向数据库推送更改内容。处输入目标地址,处指定推送的分支。我们将在高级篇详细地对分支进行说明。 $ git push ... 运行以下命令便可向远程数据库‘origin’进行推送。当执行命令时,如果您指定了-u选项,那么下一次推送时就可以省略分支名称了。但是,首次运行指令向空的远程数据库推送时,必须指定远程数据库名称和分支名称。 $ git push -u origin master 使用clone指令可以复制数据库,在指定远程数据库的URL,在指定新目录的名称。 $ git clone 执行以下指令后,会在目录(test2) 复制远程数据库。 $ git clone test2 用test2进行的操作 然后,推送此次变更,更新远程数据库。 当在克隆的数据库目录执行推送时,您可以省略数据库和分支名称。 $ git push 从“test2”推送到远程数据库的内容拉取到数据库目录“test” 使用pull指令进行拉取操作。省略数据库名称的话,会在名为origin的数据库进行pull。 $ git pull ... 用test进行的操作 请执行以下指令。 $ git pull origin master 使用log指令来确认历史记录是否已更新。 $ git log git远程分支覆盖本地分支 有时候同一个分支,远程的和本地的都被修改的面目全非了,如果想要把本地的替换成远程的,用下面的命令 git fetch --all git reset --hard origin/master (这里master要修改为对应的分支名) git pull Git 全局设置 Git的设定被存放在用户本地目录的.gitconfig档案里。虽然可以直接编辑配置文件,但在这个教程里我们使用config命令。 git config --global user.name "banyungong" git config --global user.email "banyungong@test.com" 以下命令能让Git以彩色显示。 git config --global color.ui auto 可以为Git命令设定别名。例如:把「checkout」缩略为「co」,然后就使用「co」来执行命令。 git config --global alias.co checkout 创建一个新仓库 git clone cd test touch README.md git add README.md git commit -m "add README" git push -u origin master 推到一个存在的文件夹中 cd existing_folder git init # 然后使用init命令把该existing_folder目录移动到本地Git数据库。 git remote add origin git add . git commit -m "Initial commit" git push -u origin master 推到现有的 Git 仓库 cd existing_repo git remote rename origin old-origin git remote add origin git push -u origin --all git push -u origin --tags git管理代码的工作原则 不同版本的代码,在同一个仓库,需要打上不同的tag 开发要在分支上,例如dev分支 使用开发分支上线,上线没有问题之后合入master,方便代码回滚(用master就可以代码回滚) master的代码需和线上代码保持一致 上线之后,给master打上tag,方便以后代码恢复 以上线的版本代码都能有办法恢复 版本迭代更新时,需要从master分支上拉代码到dev,再开始开发 上传代码到dev分支 git status git add . git commit -m "更新的功能" git push origin dev 合并分支到master git checkout master git pull origin master git merge dev git status git push origin master ### ImportError: /usr/lib/x86_64-linux-gnu/libstdc++.so.6: version `CXXABI_1.3.11' 报错信息 在docker中安装faiss后,测试可以正常导入,但是程序跑起来就会报错,报错信息如下: ImportError: /usr/lib/x86_64-linux-gnu/libstdc++.so.6: version `CXXABI_1.3.11' not found (required by /root/anaconda3/envs/cpu/lib/python3.6/site-packages/faiss/../../../libfaiss.so) 解决方案 首先查看导入错误的目录上有什么文件,发现确实没有需要的“`CXXABI_1.3.11” strings /usr/lib/x86_64-linux-gnu//libstdc++.so.6 | grep CXXABI ll /usr/lib/x86_64-linux-gnu//libstdc++.so.6 find /usr -name "libstdc++.so.*" find / -name "libstdc++.so.*" cp /root/anaconda3/envs/cpu/lib/libstdc++.so.6.0.29 /usr/lib/x86_64-linux-gnu/ cd /usr/lib/x86_64-linux-gnu/ find /usr -name "libstdc++.so.*" rm libstdc++.so.6 ln -s libstdc++.so.6.0.29 libstdc++.so.6 strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep CXXABI 参考 https://blog.csdn.net/wenroudebaozi/article/details/107564647 ### 论文笔记:Label Semantic Aware Pre-training for Few-shot Text Classification 前言 论文:https://aclanthology.org/2022.acl-long.570.pdf 代码:https://github.com/amazon-research/label-aware-pretrain 摘要 在文本分类任务中,有用的信息编码在标签名称中。标签语义感知系统利用这些信息在微调和预测期间提高了文本分类性能。然而,标签语义在预训练中的应用还没有得到广泛的探讨。因此,作者提出标签语义感知预训练(LSAP)来提高文本分类系统的泛化和数据效率。LSAP通过对来自不同领域的带有标签的句子进行二次预训练,将标签语义整合到预训练生成模型中。由于预训练需要大量的数据,作者开发了一个过滤和标注pipline来自动从未标记的文本创建sentence-label对。 模型架构 标签语义感知预训练(LSAP) 在预训练阶级将标签语义融入生成模型的方法。 一种从无标记噪声数据中为标签语义感知预训练创建话语-意图对的方法。 方法 LSAP方法在(伪)标记样本的大规模集合上使用T5进行二次预训练。 使用对话行为分类器(dialogue act classifier)过滤未标记的数据。 使用意图生成器(intent generator)对通过过滤器的话语进行伪标记。 使用T5对标记数据和伪标记数据进行二次预训练 pipeline for creating utterance-intent pairs Pre-training Formats label denoising is best Random span denoising,将每个意图标签(以自然语言格式)连接到其相关的话语。然后,我们随机干扰语音意图输入序列中15%的tokens,重建输出序列中连续的noised spans。这与T5使用的目标相同。 Intent classification,用下游监督微调使用的相同格式的话语和意图来监督微调T5。在这里,输入顺序是“intent classification: ”,然后是话语。输出序列是目的。 Label denoising,在训练话语和它们各自的意图前,确定性地对输入序列中的整个标签序列进行噪声化,并在输出序列中重构它。这是一种将意图分类任务框定为无监督去噪任务的方法。 总结 提出了一种利用标签中固有语义信息的预训练方法。提高了跨域小样本文本分类性能,同时在全资源设置下保持高性能。 ### 论文笔记:Differentiable Prompt Makes Pre-trained Language Models Better Few-shot Learners 前言 论文:https://arxiv.org/pdf/2108.13161.pdf 代码:https://github.com/zjunlp/DART 动机 优化prompt是提高预训练语言模型在小样本学习中是很有必要的。离散标记的模板可能陷入局部最优,并且不能充分表示特定的类别。为了提高prompt方法在各个领域的适用性,论文提出了可微分提示(DifferentiAble pRompT),简称DART,可以减少提示工程的要求。 模型框架 利用语言模型中的一些参数作为模板和标签标记,并通过反向传播对它们进行优化,而不引入模型之外的其他参数。因为有限样本的微调可能会受到不稳定性的影响。进一步引入了一个辅助流畅性约束对象,以确保prompt嵌入之间的关联。减少提示工程(包括模板和标签)和外部参数优化。此外,该算法可以使用任何预训练的语言模型并可扩展到广泛的分类任务。 模型架构 Differentiable Template Optimization 由于语言token是离散变量,用token搜索找到最优提示并不简单,很容易陷入局部极小值.为了克服这些限制,作者利用伪标记(pseudo tokens)构造模板,然后用反向传播优化它们。可微模板优化可以获得超越原始词汇V的表达模板。将模板映射为如下形式: DART利用辅助流畅性约束目标将提示嵌入彼此关联起来,从而促进模型专注于上下文表示学习。 Differentiable Label Optimization 暴力强制标签搜索:(1)由于验证集通常非常大,需要进行多轮评估,计算量大且繁琐。(2)可扩展性差,随着类数的增加呈指数型增长,因此很难处理。此外,类别的标签包含丰富而复杂的语义知识,一个离散的标记可能不足以表示这些信息。 DART将Yj映射到一个连续词汇空间,如下所示: 为了避免优化任何外部参数,{h1,…,hm,..,hm+n}替换为未使用的token Training Objectives 由于提示模板中的伪标记必须彼此相互依赖,作者引入了辅助的流畅性约束训练,而没有优化任何其他参数。总体而言,有两个目标:the class discrimination objective(LC)和the fluency constraint objective(LF) Class Discrimination Objective 是对句子进行分类的主要目标,CE是交叉熵损失函数。 Fluency Constraint Objective 为了确保模板标记之间的关联,并维护从PLMs继承来的语言理解能力,作者利用MLM的流畅性约束对象,对输入语句中的一个标记进行随机掩码,并进行掩码语言预测。该语言模型可以通过模板标记之间的丰富关联获得更好的上下文表示。 为了小样本微调的不稳定性,作者联合优化模板和标签。 结论 论文介绍了一种简单而有效的优化方法DART,它改进了fast-shot learning预训练语言模型。与传统的优化方法相比,所提出的方法可以在小样本的情况下产生令人满意的改进。该方法还可用于其他语言模型(如BART),并可扩展到其他任务,如意图检测和情感分析。 ### 论文笔记:Unified Structure Generation for Universal Information Extraction 前言 论文:https://arxiv.org/pdf/2203.12277.pdf 代码:https://github.com/universal-ie/UIE 动机 信息抽取旨在从非结构化文本中识别并结构化用户指定的信息(user-specified information) IE任务是高度多样化的,由于: varying targets(entity / relation / event / sentiment…) heterogeneous structures(span / triplet / record…) demand-specific schemas 然而,目前,多数方法是任务特定的(task-specialized),导致对于不同的IE任务需要构建: dedicated architectures(专门的结构) isolated models(孤立的模型) specialized knowledge sources(利用专门的知识源) 上述,任务特定的解决方案,阻碍了IE系统的: rapid architecture development(快速的结构发展):为大量的IE tasks / settings / scenarios 构建专门的结构过于复杂 effective knowledge sharing(有效的知识共享):孤立的模型限制了相关任务或者设置之间的知识共享 quick cross-domain adaptation(快速的跨领域适应):为不同的任务构建数据集:成本高、耗时 由此,论文提出Universal IE,即: 统一建模不同的IE任务 自适应地预测异构的结构 有效从不同数据源进行学习 挑战:如何自适应地控制抽取过程? 不同的目标结构 不同的schema 主要贡献 提出了a unified text-to-structure generation architecture 设计structured extraction language和structural schema instructor(SSI) 首个text-to-generation的预训练抽取模型,对后续研究有益 在低资源、少样本场景中表现出on-demand adaptation ability,验证了方法的有效性、通用性、可迁移性 方法创新(SSI+Text⇒SEL) UIE结构图 UIE方法 将所有的IE任务都建模为 text-to-structure transformations将不同的任务都分解为一系列的原子转化操作,包括:spotting:定位与给定的语义类型相关的span例如:steve是一个person实体associating:链接不同的span,并给他们分配预定义的schema涉及的语义角色例如:将steve和apple链接,分别将其视为work-for关系的arg1和arg2 上述做法可以使所有的IE任务都共享相同的、底层的spotting和associating操作 structured extraction language(SEL)(为了建模不同的IE结构) 基本元素:SpotName:span的类型AssoName:association的类型InfoSpan:具体的span内容  结构:( Spot Name:Info Span (Asso Name:Info Span) ) SEL结构 structural schema instructor(SSI)(为了自适应地生成不同的目标结构)a schema-based prompt mechanism,用来控制:what to spotwhat to associatewhat to generate构建提示作为输入,基本元素:SpotNameAssoNamespecial symbols([spot] / [asso] / [text]) 结构:[spot] xx [spot] xx … [asso] xx [asso] xx … [text] x_1, x_2, … , x_n A large-scale pre-trained text-to-structure model(为了从不同数据源学习通用的IE能力)三个预训练数据集D_pairD_textD_record三个任务,联合训练D_pair:同时优化编码器、解码器D_text:同时优化编码器、解码器(masked生成)D_record:仅优化解码器(语言模型) 微调:防止暴露偏差,引入rejection mechanism (RM),即引入噪音,例如(facility: [NULL]) 整体的流程: 根据任务 / schema,构建SSI 拼接SSI和text,输入模型 生成SEL 后处理形成最终结果 ### 论文笔记:FlipDA: Effective and Robust Data Augmentation for Few-Shot Learning 前言 论文:https://arxiv.org/abs/2108.06332 代码:https://github.com/zhouj8553/FlipDA 背景和挑战 大多数以前的文本数据增强方法都局限于简单的任务和弱基线。作者在困难任务(小样本的自然语言理解)和强基线(具有超过10亿个参数的预训练模型)上做数据增强。 在困难任务和强基线模型下,很多以前的文本数据增强方法,最多只带来边际增益,有时会大大降低性能。在许多情况下,使用数据增强会导致性能不稳定,甚至进入故障模式。 方法 作者提出了一种新的数据增强方法FlipDA,该方法联合使用生成模型和分类器来生成标签翻转数据。FlipDA的核心思想是发现生成标签翻转数据比生成标签保留数据对性能更重要。与保留原始标签的增强数据相比,标签翻转数据往往大大提高了预训练模型的泛化能力。实验表明,FlipDA在有效性和鲁棒性之间实现了很好的权衡,它在不影响其他任务的同时,极大地改善了许多任务。在小样本的设定下,有效性和鲁棒性是数据增强两个关键要求。 有效性(Effectiveness) 数据增强应该在某些特定任务上显著提高性能。 FlipDA: Automatic Label Flipping 1、在不使用数据增强的条件下,训练分类器(对预训练的模型进行微调) 2、生成标签保留和标签翻转的增强样本 首先使用完形填空模式将x和y组合成一个序列,然后随机mask一定百分比的输入的tokens。然后使用预训练的T5模型来填补空白,形成一个新的样本。如果T5不能预测出新样本的y值,删除这个样本是有益的。使用T5生成增强样本确实引入了额外的知识并减少了语法错误,但仅使用T5进行增强而不进行标签翻转和选择效果不佳。 基于prompt的增强算法保存/翻转标签数据 3、使用分类器为每个标签选择概率最大的生成样本 Si 是从原始样本 (xi , yi ) 增强的数据集合,y' 不等于 yi 样本 xi 的数据增强集合 删除预测为 yi 的增强的数据,保留标签翻转的增强的数据 增强的标签反转的数据 数据选择政策 4、用原始样本和附加的增强样本重新训练分类器 鲁棒性(Robustness) 数据增强方法在任何时候不应该遇到故障模式(failure mode)。故障模式在小样本学习中很常见,在这种情况下,一些微小的更改可能会导致性能大幅下降。 What Contribute to Failure Modes? 大多数增强方法都是基于标签保留的假设,而自动方法生成标签保留的样本是一个挑战。如果模型在保持标签的假设下,在有噪声的增强数据上进行训练,性能下降是可能的。作者将噪音数据总结为两类,一类是导致理解困难的语法错误,另一类是改变标签关键信息的修改。 结论 标签翻转提高了小样本的泛化性,论文提出具有自动标签翻转和数据选择的FilpDA算法。实验证明了FlipDA的优越性,在有效性和鲁棒性方面都优于以往的方法。在未来,从理论上理解在现有数据点附近生成标签翻转数据为什么以及如何提高泛化将是至关重要的。此外,增加增强数据生成的多样性和质量也是一个重要的长期目标。 代码运行顺序 1、运行基线模型(Run Baseline) bash scripts/run_pet.sh baseline 结果会保存在results/baseline/pet/_albert_model/result_test.txt文件中 2、产生增强的文件(Produce augmented files) CUDA_VISIBLE_DEVICES=0 python -m genaug.total_gen_aug --task_name --mask_ratio --aug_type --label_type --do_sample --num_beams --aug_num 备选方案:使用不带分类器的增强文件运行基线模型,如果希望将增强文件中的所有增强数据添加到模型中(不希望根据训练过的分类器更改标签或过滤增强样本),可以运行如下命令。 bash scripts/run_pet.sh boolq 0 3、用增强的文件运行FlipDA(Run FlipDA with augmented files) 如果允许分类器对增广数据的标签进行校正,则执行如下命令,其中是增强文件名,例如,"t5_flip_0.5_default_sample0_beam1_augnum10"。 bash scripts/run_pet.sh boolq 0 genaug__filter_max_eachla 如果不允许分类器纠正增强数据的标签,则运行如下命令 bash scripts/run_pet.sh boolq 0 genaug__filter_max_eachla_sep 选择哪个命令取决于增强模型和分类模型的relative power。如果增强模型足够精确,选择带有“sep”的命令会更好。否则,选择第一个。如果不确定,就两种都试试。 ### 论文笔记:STraTA: Self-Training with Task Augmentation for Better Few-shot Learning 前言 论文:https://arxiv.org/pdf/2109.06270.pdf 代码:https://github.com/google-research/google-research/tree/master/STraTA STraTA,Self-Training with Task Augmentation,基于任务增强的自训练 亮点 STraTA使用任务增强技术,首先训练一个生成模型,然后使用给定目标任务的无标签文本合成大量域内训练数据,用于辅助任务微调,得到辅助任务模型。STraTA自训练算法使用标记和伪标记的样本迭代学习更好的模型。在每次迭代中,利用teacher-student方法,从任务扩展产生的辅助任务模型开始,并在广泛分布的伪标记数据上进行训练。 模型 模型架构 模型主要分为两个部分,一个是Task augmentation,生成Auxiliary-task Model;另一个是Self-training,利用teacher-student模式迭代训练。 任务描述 假设给定任务T,包括带标签的数据集L和无标签的数据集U,无标签的数据集U可以通过L人工制造,或者可以来自目标域或相关数据集/域的未标记文本。 任务增强(Task augmentation) 任务增强模块使用自然语言推理(NLI)作为辅助(中间)训练任务,以提高下游性能。 任务增强建立在最近的关于中间任务训练的NLP研究的基础上,其中预先训练的语言模型(如BERT)在目标任务之前对辅助任务进行微调。 论文关于中间微调的工作,使用的辅助数据集是一个固定目标任务无关的数据集,如MNLI或SQuAD。这种选择的一个明显的限制是辅助任务和目标任务之间的域不匹配,使用任务增强方法解决这一问题。对于给辅助任务A,利用无标签数据U结合微调的预训练生成语言模型合成大量的目标任务T域内的数据,来提高目标任务T的性能。 Generating synthetic NLI data 使用预训练的T5模型在带标签的句子对上做微调。训练样本将(sentA, sentB)→label 转化为(label, sentA)→sentB,获得微调的样本。将目标任务的数据集的数据经过微调的T5数据生成器生成增强样本。在推理时,向模型输入一个NLI标签label 和一个来自目标域的未标注的句子xj,以生成一些输出句子xk : (label,xj)→xk 。然后通过创建(xj, xk)→label 这样的样本来形成用于中间任务(Synthetic In-domain Auxiliary-task Data)微调的数据。 自训练(Self-training) 任务增强使用未标注的文本为中间辅助任务生成合成数据,而自训练是一种补充方法,它通过使用伪标记示例直接在目标任务上进行训练来改进模型。 self-training伪代码 Starting with a strong base model 自训练算法的一个重要组成部分是基础模型f0。成功的自训练通常需要一个良好的基础模型,它可以在未标注的样本上提供很大比例的“正确”预测或伪标记;否则,错误会在后期的自训练中传播或放大。在每次自训练迭代中,总是从相同的基础模型f0开始,该模型使用来自预训练/中间微调阶段(例如,任务增强中的辅助任务训练阶段),然后使用标注数据和伪标注数据对模型进行微调。 Self-training on a broad distribution of pseudolabeled data 通过在每次自训练迭代中向原始标记数据集L中添加整个U伪标记示例集,鼓励从伪标记数据的“自然”广泛分布中学习在每次迭代t >1时,我们也用ft重新注释原始未标记数据池U中的所有示例,因为我们预期ft优于ft−1。 ### GPU资源释放 摘要 问题描述:起服务时没有占用gpu资源,但是程序执行一次后存在线程占用gpu显存的问题 解决方案一:手动释放gpu资源(未解决) torch.cuda.empty_cache() 解决方案二:升高scikit-learn版本,以为是版本较低设置n_jobs造成的,但应该不是(未解决) 解决方案三:不确定是否为异步程序造成,修改异步程序,异步从方案一修改到方案二(未解决) python异步执行 异步方案一:装饰器 from threading import Thread def async(f): def wrapper(*args, **kwargs): thr = Thread(target=f, args=args, kwargs=kwargs) thr.start() return wrapper @async def restart_fun(): # 需要异步执行的方法 pass 异步方案二:线程池 from concurrent.futures import ThreadPoolExecutor thread_pool = ThreadPoolExecutor(2) def exception_callback(fur): if fur.exception() != None: # 若线程正常退出,返回None logging.error(fur.result()) # 判断是否存在异常,存在则打印返回的值 def wait_f(): fur = thread_pool.submit(restart_fun,) # restart_fun为需要异步执行的方法 fur.add_done_callback(exception_callback) wait_f() 方案一和方案二是启了一个子进程,所以会显存会释放不掉 方案三是方案二的改进版本:使用os.system启了一个进程。bingo import os from concurrent.futures import ThreadPoolExecutor thread_pool = ThreadPoolExecutor(2) def exception_callback(fur): if fur.exception() != None: # 若线程正常退出,返回None logging.error(fur.result()) # 判断是否存在异常,存在则打印返回的值 def restart_fun(a, b): os.system("python xxx.py %s %s" % (a, b)) def wait_f(a, b): fur = thread_pool.submit(restart_fun, a, b) fur.add_done_callback(exception_callback) wait_f(a, b) # 如果有参数,一下为xxx.py代码文件 # ...... if __name__ == "__main__": import sys a = sys.argv[1] b = sys.argv[2] fun(a, b) 方案三在进程结束时会释放gpu资源,但在执行过程中会遇到一个问题,import numpy要在import torch之前,否则会报一下错误。 Error: mkl-service + Intel(R) MKL: MKL_THREADING_LAYER=INTEL is incompatible with libgomp-7c85b1e2.so.1 library.Try to import numpy first or set the threading layer accordingly. Set MKL_SERVICE_FORCE_INTEL to force it. 使用的查看gpu和进程的linux命令 nvidia-smi fuser -v /dev/nvidia* ps -ef | grep 端口号 ## 页面 ### 关于我 这是示范页面。页面和博客文章不同,它的位置是固定的,通常会在站点导航栏显示。很多用户都创建一个“关于”页面,向访客介绍自己。例如: 嗨,大家好!我白天是个邮递员,晚上就是个有抱负的演员。这是我的网站。我住在北京,养了条吉通人性的狗叫小黑,我喜欢艺术和旅行。 ……或这个: XYZ Doohickey公司成立于1971年,自从建立以来,我们一直向社会贡献着优秀doohickies。我们的公司总部位于天朝魔都,有着超过两千名员工,对魔都政府税收有着巨大贡献。 而您,作为一位 WordPress 新用户,我们建议您转到您站点的仪表盘,删除本页面,然后创建包含您自己内容的新页面。祝您使用愉快!