【论文阅读】Deep Semantic Role Labeling with Self-Attention

碎碎念

被迫要使用语义角色标注(Deep Semantic Role Labeling)技术,本来想直接使用github上的项目Tagger,奈何作者说自己本来用tensorflow写的,后来改成pytorch但没完全改(Model ensemble不可用,且预训练好的模型只提供了tensorflow版本的,需要用pytorch来调用),所以计划来读这篇文章,方便日后的改动

任务设置

给定一个句子,SRL(Semantic Role Labeling)的目标是定义每个目标动词的论元(可以简单理解为与目标动词有关系的词),并将他们分类为语义角色。如下图这个例子:
在这里插入图片描述
SRL就需要为“发表”这个目标动词定义论元,并指定这些论元的语义角色(包括who did what to whom, when and where等)

所以,SRL中就包括定义论元和分类论元这两个任务。有时在定义论元前,也会对明显的非候选论元进行剪枝,或者是在分类论元后,应用后处理流程去调整不一致的预测。在测试中,动态规划算法经常被用来寻找全局最优解。

问题:训练阶段是需要指定目标动词和论元角色的,但是测试阶段需不需要也指定目标动词呢?

引入

背景:最初的SRL方法是需要句法输入的(也就是把输入句子的结构同时告诉算法),后来提出了堆叠的LSTM网络并获得了很好的效果,这种方法能够端到端地处理潜在的句法结构

动机:现在的RNN方法存在两个问题:1)内存压缩问题,也就是处理长句和短句都使用单个固定大小的向量,这种不平衡的方式导致网络在长句上表现差、短句上浪费内存 2)缺乏处理树结构输入的方式(?)

提出的方法:1)能建立任意两个token之间的直接关系 2)提供了一个更灵活的方式案例挑选、表示和合成输入信息 3)这使得网络在领域外的数据集上表现也很好

网络

简介:输入原本的句子和动词mask,首先转化为embedding,然后将其放入深度注意力神经网络(由多个相同的层组成,每层包括非线性子层和注意力子层)来获取句子的嵌套结构和label之间的潜在关系,其中,非线性子层可选RNN/CNN/FNN或合成,分别称为DeepAtt-RNN/CNN/FFN以及Model ensemble。测试阶段,只有最高层的attention子层输出被放入逻辑回归层来进行最终的预测(训练阶段不也是?)
在这里插入图片描述
其中每个模块在当时看来可能是比较新的技术,但在2022年已经屡见不鲜了,这里就不再一一赘述了

代码学习

./tagger/bin/predictor.py 目测要测试的代码应该从这里找,简单学习下

# coding=utf-8
# Copyright 2017-2019 The THUMT Authors

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import argparse
import logging
import os
import six
import time
import torch

import tagger.data as data
import tagger.models as models
import tagger.utils as utils


def parse_args():
    # 创建ArgumentParser()对象,用于之后添加参数
    parser = argparse.ArgumentParser(
        description="Predict using SRL models",
        usage="translator.py [<args>] [-h | --help]"
    )

    # input files
    parser.add_argument("--input", type=str, required=True,
                        help="Path of input file")
    parser.add_argument("--output", type=str, required=True,
                        help="Path of output file")
    parser.add_argument("--checkpoint", type=str, required=True,
                        help="Path of trained models")
    # 源词汇和目标词汇的路径?不太懂
    parser.add_argument("--vocabulary", type=str, nargs=2, required=True,
                        help="Path of source and target vocabulary")

    # model and configuration
    parser.add_argument("--model", type=str, required=True,
                        help="Name of the model")
    parser.add_argument("--parameters", type=str, default="",
                        help="Additional hyper parameters")
    # 编码时是否使用半精度
    parser.add_argument("--half", action="store_true",
                        help="Use half precision for decoding")

    return parser.parse_args()


def default_params():
    # utils: import tagger.utils as utils
    # utils.HParams: utils/__init__.py
    # from tagger.utils.hparams import HParams
    # HParams类接收不定长度的dict,依次解析,存入self._hparam_types中
    params = utils.HParams(
        input=None,
        output=None,
        vocabulary=None,
        embedding="",
        # vocabulary specific
        pad="<pad>",
        bos="<bos>",
        eos="<eos>",
        unk="<unk>",
        device=0,
        decode_batch_size=128
    )

    return params


def merge_params(params1, params2):
    params = utils.HParams()

    for (k, v) in six.iteritems(params1.values()):
        params.add_hparam(k, v)

    params_dict = params.values()

    for (k, v) in six.iteritems(params2.values()):
        if k in params_dict:
            # Override
            setattr(params, k, v)
        else:
            params.add_hparam(k, v)

    return params


def import_params(model_dir, model_name, params):
    model_dir = os.path.abspath(model_dir)
    m_name = os.path.join(model_dir, model_name + ".json")

    if not os.path.exists(m_name):
        return params

    with open(m_name) as fd:
        logging.info("Restoring model parameters from %s" % m_name)
        json_str = fd.readline()
        params.parse_json(json_str)

    return params


def override_params(params, args):
    params.parse(args.parameters)

    src_vocab, src_w2idx, src_idx2w = data.load_vocabulary(args.vocabulary[0])
    tgt_vocab, tgt_w2idx, tgt_idx2w = data.load_vocabulary(args.vocabulary[1])

    params.vocabulary = {
        "source": src_vocab, "target": tgt_vocab
    }
    params.lookup = {
        "source": src_w2idx, "target": tgt_w2idx
    }
    params.mapping = {
        "source": src_idx2w, "target": tgt_idx2w
    }

    return params


def convert_to_string(inputs, tensor, params):
    inputs = torch.squeeze(inputs)
    inputs = inputs.tolist()
    tensor = torch.squeeze(tensor, dim=1)
    tensor = tensor.tolist()
    decoded = []

    for wids, lids in zip(inputs, tensor):
        output = []
        for wid, lid in zip(wids, lids):
            if wid == 0:
                break
            output.append(params.mapping["target"][lid])
        decoded.append(b" ".join(output))

    return decoded

# 所以args包含parse_args上定义的所有参数,可以通过args.来调用
def main(args):
    # Load configs
    # models: import tagger.models as models
    # models.get_model: models/__init__,py
    # def get_model() 中调用DeepAtt类
    model_cls = models.get_model(args.model)
    # 设置参数
    params = default_params()
    params = merge_params(params, model_cls.default_params())
    params = import_params(args.checkpoint, args.model, params)
    params = override_params(params, args)
    torch.cuda.set_device(params.device)
    torch.set_default_tensor_type(torch.cuda.FloatTensor)

    # Create model
    with torch.no_grad():
        model = model_cls(params).cuda()

        if args.half:
            model = model.half()
            torch.set_default_tensor_type(torch.cuda.HalfTensor)

        model.eval()
        model.load_state_dict(
            torch.load(utils.best_checkpoint(args.checkpoint),
                       map_location="cpu")["model"])

        # Decoding
        dataset = data.get_dataset(args.input, "infer", params)
        fd = open(args.output, "wb")
        counter = 0

        if params.embedding is not None:
            embedding = data.load_glove_embedding(params.embedding)
        else:
            embedding = None

        for features in dataset:
            t = time.time()
            counter += 1
            features = data.lookup(features, "infer", params, embedding)

            labels = model.argmax_decode(features)
            batch = convert_to_string(features["inputs"], labels, params)

            for seq in batch:
                fd.write(seq)
                fd.write(b"\n")

            t = time.time() - t
            print("Finished batch: %d (%.3f sec)" % (counter, t))

        fd.close()


if __name__ == "__main__":
    # 以parse_args()函数返回的结果作为参数传递给main
    main(parse_args())
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值