“基于常识知识的推理问题”源代码分析-convert脚本

2021SC@SDUSC

本次源代码分析的工作集中在convert脚本源码分析上。在DrFact模型中,共有两个convert脚本,一个是convert_add_links.py,另一个则是convert_dpr_index.py。以下是对于这两个convert脚本的源码分析。

一、convert_add_links.py脚本源码分析

convert_add_links.py的功能是将命令行上输入的两个目标路径进行处理。具体结果是将第一个目标路径上的文件内容进行拆分组合,生成一个字典,然后将其python字典数据结构内容转化为一个JSON文件。具体代码解析如下:

这个脚本非常简单,只是一个文件处理与转换,因此只使用几个很基础的python库。

import sys
import json

然后构建三个空列表,用于后续生成字典使用。

rows = []
cols = []
vals = []

使用系统库sys,用于获得命令行的第一个参数,然后根据这个路径参数打开目标文件,将其中内容读取并按行拆分,再将每行内容拆分成为两个部分——f1和f2,分别填充进rows列表和cols列表之中,vals列表随后填充数值1。

with open(sys.argv[1]) as f: 
  for line in f.read().splitlines():
    if not line:
      continue
    f1, f2 = line.split()
    rows.append(f1)
    cols.append(f2)
    vals.append(1)

最后,根据上述填充完的列表构建字典数据机构,再将该python字典数据结构编码转化成为一个JSON文件,有待于后续使用。

with open(sys.argv[2], "w") as f:
  json.dump(dict(rows=rows, cols=cols, vals=vals), f)

二、convert_dpr_index.py脚本源码分析

convert_dpr_index.py这个脚本的用途在于将DPR索引信息转化为DrFact模型的信息。在正式对源代码进行分析之前,我需要稍微介绍一下DPR究竟是个何方神圣。

2.1 DPR

DPR,全称Dense Passage Retrieval,翻译过来的意思即是“密集文本段检索”,DPR在开放式问答系统中有着极为重要的作用。根据我们已经知晓的知识可知,开放式问答依赖于有效的文本检索来选择候选内容。传统的方法TF-IDF、BM25采用的是稀疏向量空间方法(这里的BM25正是我们在对这个项目开篇讲述时描述到的一个用于和我们当前正在研究的DrFact模型进行比对正确率的一个模型)。

而我们发现当我们使用密集表达的时候,其中的词向量可以在双重编码器的框架下学习小数量的问题与文章来获得,我们采用的BERT来学习文本的词向量。

密集的(隐藏层语义编码)是稀疏表达的补充。如回答问题“Who is the bad guy in lord of the rings?”(谁是指环王中坏蛋?),我们可以通过文本“Sala Baker is best known for portraying the villain Sauron in the Lord of the Rings trilogy.”( Sala Baker在指环王中扮演反面角色Sauron)找到答案,但是term-based的方法很难找到正确答案。不过,通过密集方法,即使用DPR,我们可以匹配出“bad”和“villain”(反面角色)这两格具有鲜明态度的词语,从而判断得出有关正确回答的概念信息。通过学习密集向量表达,我们可以对开放式问答系统有一个更加清晰而准确的认识。

2.2 源代码分析

接下来进入正题,对脚本内容进行分析。

首先还是老生常谈的调用模块以及使用absl.flags设置的全局参数。可以看到,index_result_path和dpr_pkl_path初始值均为None,且一个是指向输出文件的路径另一个是指向pickle文件的路径。而剩下的embed_prefix有初始值,为dpr_bert_base,它表示词向量文件的前缀。

from absl import app
from absl import flags
import pickle
import numpy as np
import os
from language.labs.drkit import search_utils
import tensorflow.compat.v1 as tf

FLAGS = flags.FLAGS
flags.DEFINE_string("index_result_path", None, "Path to output files.")
flags.DEFINE_string("dpr_pkl_path", None, "Path to dpr pickle file.")
flags.DEFINE_string("embed_prefix", "dpr_bert_base",
                    "The prefix of the embedding files.")

接下来谈谈唯一的一个函数——主函数mian。

在主函数mian中,首先将dpr_pkl_path指向的文件打开,将其使用pickle.load()函数读取并存放于doc_vertors中。然后将doc_vecors索引为(0,1)的值的大小保存在dim中,将其总体长度保存到num_facts中,然后构造一个num_facts行dim列的一个空二维数组fact_emb,数据类型为float32。

然后对doc_vectors中的内容进行遍历,获取每段以及其索引,再将doc中的词向量取出存放于doc_vector中,最后,将doc_vector与当前索引对应,嵌入fact_emb二维数组中,即当前多维数组中的index行即为目标词向量。通过如此遍历,即将doc_vectors中的所有词向量嵌入到了fact_emb这个二维数组中。

完成上述步骤后,拼接路径,将得到的词向量数组(或者矩阵)写入检查点中,保存在输出的目标路径中。

def main(_):
  """Main fuction."""
  with open(FLAGS.dpr_pkl_path, "rb") as reader:
    doc_vectors = pickle.load(reader)

  dim = doc_vectors[0][1].shape[0]
  num_facts = len(doc_vectors)
  fact_emb = np.empty((num_facts, dim), dtype=np.float32)

  for ind, doc in enumerate(doc_vectors):
    _, doc_vector = doc
    fact_emb[ind, :] = doc_vector

  output_index_path = os.path.join(FLAGS.index_result_path, "%s_fact_feats" %FLAGS.embed_prefix)
  tf.logging.info("Saving %d fact features to tensorflow checkpoint.",
                        fact_emb.shape[0])
  with tf.device("/cpu:0"):
    search_utils.write_to_checkpoint("fact_db_emb", fact_emb, tf.float32, output_index_path)

由此,我们可以得知这个源文件的主要功能是对词向量进行提取并整合成为一个矩阵的形式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值