代码阅读-DualUG


原代码GitHub 地址
README中给出了几种执行命令,我罗列并标记了相应参数的含义。
在这里插入图片描述
我们仍然从 main.py看起,主要分为数据、模型几个部分

一、数据处理

定义了一个DataEngine的类,这个类是torch.utils.data.DataSet的子类,用来进行数据集的梳理,这个处理过程主要在prepare_data函数中。

1.1 prepare_data

1.1.1 数据加载及格式化

使用的是data/E2ENLG.py加载、处理数据集。核心函数如下,我加了自己的注释理解.

def E2ENLG(
        data_dir, is_spacy, is_lemma, fold_attr,
        use_punct, min_length=-1, train=True):
    """
    Args:
        data_dir: string E2E数据集路径
        is_spacy: int 1or0 无使用
        is_lemma: int 1or0 无使用
        fold_attr: int 1or0 是否将属性值及句子进行小写
        min_length: int, 输出句子最小长度阈值
        train: bool, use trainset.csv or testset_w_refs.csv
    Returns:
         input_data: list, [[槽位1:值1, 槽位2:值2, 槽位3:值3]]
         input_attr_seqs: list [[槽位1,值1,槽位2,值2,槽位3,值3]]
         output_labels: list [[词,词,词,...],[词,词,词,...]]
         refs_list: list [[词,词,词,...],[词,词,词,...]]
         sf_data: list [{槽位1:值1, 槽位2:值2, 槽位3:值3}]
    """

但是我还是有几个地方是不确定的:

  1. E2E并不是一个对话数据集,但是在处理过程中却考虑了相邻样本是否有相同的MR,即相同属性用不同的句子描述;
  2. 变量也使用了dialogue这种让人很困惑的名字。

1.1.2 处理词汇表

  • build_vocab: 建立词-索引映射,并保存在指定路径下
  • shrink_vocab: 并根据输入参数限制词汇表大小。
  • add_unk: 并将不在词汇表中的那些词在输出标签中,置为特殊词_UNK

一处疑问:

self.training_set_label_samples = self.input_data

暂时没明白要干嘛

1.2 类的get属性

重新定义了__getitem__功能,根据索引返回一条样本的相关数据。

def __getitem__(self, idx):
   return (
            self.input_data[idx],
            self.output_labels[idx],
            self.refs[idx],
            self.sf_data[idx],
            self.input_attr_seqs[idx]
        )

二、对偶模型

Dual类在module_dual.py文件中,

初始化时,定义了nlu、nlg模型及模型的优化方式

训练阶段可以关注train_nlu train_nlg train_joint 三个函数

2.0 nn.Module

第一次使用pytorch,先了解一下.

torch.nn是专门为神经网络设计的模块化接口。nn构建于autograd之上,可以用来定义和运行神经网络。
nn.Module是nn中十分重要的类,包含网络各层的定义及forward方法。

这篇博客讲的很好,可以看一下。

2.1 NLU模型

src/module.py中。NLURNN是继承了RNNModel,而RNNModel又是继承了nn.Module,隐藏层使用的是双向的GRU单元。其forward过程就是计算隐层,再加一个线性变换输出logits

2.2 NLG模型

src/module.py中。NLGRNN也是继承了RNNModel,隐层使用的是单向GRU单元。(存疑:必须使用单向).其forward过程其实调用的是自定义的forward_greedy函数(因为beam_size就是传入的mid_sample_size,默认为1),和NLU的forward过程不同,在NLG的forward中,隐层先进行了一次线性变换,为的是后续decode这个循环的过程(每一次循环,输入都是当前输入和上一次隐层的输出,最后的输出再过一个softmax)。

三、训练过程

基准模型和对偶监督学习模型的训练过程都是分开训练nlu_loss和nlg_loss。核心函数为train_nlutrain_nlg

联合对偶学习模型的训练过程则是将二者连一起。核心函数是train_joint

3.1 train_nlu

因为在这里讲NLU任务定义为一个多分类问题,所以用f1值来作为最后的指标。后面理由criterion_nlu来完成相关指标计算,criterion_nlu也是集成了nn.Module,所以后续直接使用criterion_nlu(**args)来完成Criterion.forward过程(相当于调用了__call__方法)。
直接贴代码:

sup_loss, rl_loss, nlu_joint_prob, reward = criterion_nlu(
            logits.cpu().unsqueeze(1).expand(-1, sample_size, -1),
            targets.cpu(),
            decisions=samples.cpu(),
            n_supervise=1,
            calculate_reward=reinforce
        )
  • sup_loss, rl_loss 的计算过程见Criterion.get_scheduled_loss(实际什么都没做直接返回了)

  • nlu_joint_prob 的计算过程见Criterion.get_log_joint_prob_nlu (存疑)。

  • reward的计算过程中,由于使用的是默认的args.primal_reinforce=False,所以通过get_reward这个函数得到reward(存疑,计算是需要知道reward_type,但无论nlu、nlg在执行main函数时都没有指定这个参数,所以用的是默认参数none。而get_reward中没有这个类型的reward计算,所以reward是0)

3.2 train_nlg

对比着看nlu和nlg的核心train_batch函数更加清晰:输入、传播、计算loss
在这里插入图片描述

直接贴计算loss的过程:

sup_loss, rl_loss, nlg_joint_prob, reward = criterion(
        logits.cpu(),
        labels.cpu(),
        decisions=decisions.cpu(),
        n_supervise=1,
        calculate_reward=reinforce
    )
  • nlg_joint_prob计算过程见get_log_joint_prob_nlg,主要是一个softmax操作
  • 其他loss和nlu的loss计算过程一样,

碎碎念

  • 其实还是没有很理解,尤其是在NLU作为分类任务时的评价相关
  • 个人对代码的一些注释GitHub
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值