• [codes] Writing Editing Networks Source Code Analysis


    Basic Information

    作者:Qingyun Wang

    论文:Paper Abstract Writing through Editing Mechanism (ACL)

    源码:https://github.com/EagleW/Writing-editing-Network

    Data Preprocessing

    1. Load Data

    1. 抽取出headline 和 abstract
    2. 分词(自己写了一个分词,当然也可以用NLTK等工具包)
    3. 构造corpus:列表(all sample)> 子列表(a sample: headline+abstract)> 子子列表 (tokens + sentence)

    2. Construct Vocabulary

    1. 根据训练数据构造词表;按照词频排序,删去低频词,将vocabulary大小设置为vocab_size
    2. 在单词、标点的基础上,增加<pad>,<unk>,<eos>,<bos>等几个标志位
    3. 将<pad>放在字典的第一位

    3. word2id,id2word

    1. 将corpus中的token映射成id(未进行padding)
    2. 在abstract的前后增加了两个标志位<eos>,<bos>,(当teacher forcing training时)作为解码输入相当于进行了shift,而标题不需要添加<eos>,<bos>
    3. 按照编码输入的长度对进行corpus进行排序(按照headline从长到短对corpus进行排序)
    4. 获得max_x_len,max_y_len

    Model Construction

    1. Embedding

    1. 随机初始化embedding matrix,传入nn.Embedding两个参数:vocab_size,embed_dim
    2. Embedding定义好之后,encoder for title,encoder for draft,decoder共用。即headline,draft,abstract共用同一个编码

    2. Encoder for Title

    1. single-layer bidirectional GRU
    2. 因为编码输入是按照title顺序排序好的,所以输入的时候可以利用nn.utils.rnn.pack_padded_sequence进行编码,得到了结果再利用nn.utils.rnn.pad_packed_sequence补出padding位。如此可以使结果更加准确,排除了padding位对双向编码的影响
    3. input_sorted_by_length ==> pack ==> encode ==> pad ==> encoder_output

    3. Encoder for Draft

    1. single-layer bidirectional GRU
    2. 这个encoder是对解码器输出的draft进行编码,输入的序列虽然在形状上都是 [batch_size, max_y_len],但是其中的有效序列元素个数参差不齐,此时再进行排序,编码,恢复batch中sample原来的位置就比较麻烦
    3. 所以就不用nn.utils.rnn.pack_padded_sequencenn.utils.rnn.pad_packed_sequence两个函数了,编码结果会受到padding位的影响,但是无伤大雅

    4. Decoder for All-pass Decoding

    1. single-layer unidirectional GRU
    2. 每个pass的解码,用的都是同一个decoder
    3. 包含两种attention:
      1. 对于上一pass中decoder hidden states的attention
      2. 当前pass的decoder hidden states,对于encoder hidden states的attention

    5. Complete Model

    包含以上提到的两种encoder,decoder,以及word probability layer

    6. Instantiate Model

    # 为模型设置可见GPU
    torch.cuda.set_device(0)
    
    # 打印GPU信息
    if torch.cuda.is_available():
        print("congratulations! {} GPU(s) can be used!".format(torch.cuda.device_count()))
        print("currently, you are using GPU" + str(torch.cuda.current_device()), 
              "named", torch.cuda.get_device_name(torch.cuda.current_device()))
    
        # 利用CUDA进行加速
        model = model.cuda()
        criterion = nn.CrossEntropyLoss(ignore_index=0).cuda()
        
    else:
        print("sadly, CUDA is not available!")
    
    

    输出:

    Congratulations! 1 GPU(s) can be used!
    Currently, you are using GPU0 named "GeForce GTX 1050 Ti"
    

    Training Process

    1. 进入train_epoch函数

      1. 进入train_batch函数

        1. 设置 previously generated draft 为 None

        2. input [batch_size, max_x_len], target [batch_size, max_y_len]

        3. 对输入进行编码,[batch_size, max_x_len]

        4. 进入multi-pass decoding循环

          1. 如果previously generated draft 为 None,说明是第一次进行解码,只进行decoder - encoder端的attention

            1. 解码得到隐含向量
            2. 映射到vocabulary size,得到概率分布
            3. 采样得到解码输出(greedy search)
            4. 作为previously generated draft
          2. 如果previously generated draft 不为 None,说明之前已经进行过解码了,所以要有decoder-encoder之间的attention,还要对生成的draft的隐含状态进行attend

          3. 该implementation中对每一pass的解码结果,即draft,都与ground truth进行了交叉熵的Loss计算,反向传播,更新参数(训练一个batch,反传多次,希望草稿也尽量接近目标序列)

      2. 记录batch的loss,在一个epoch完结之后打印average epoch loss

      3. 每个epoch中没有进行validation

    2. epoch循环结束之后保存一组参数

    3. 如果当前epoch的平均loss大于前一epoch的平均loss的话,需要停止训练防止过拟合,不过此处模型是利用Training set上的loss来计算的

    Inference Phase

    1. 载入训练好的模型参数:

      model.load_state_dict(torch.load(args.save))
      
      # 在GPU1上训练的模型,当前torch可见的GPU序号为0时不能够被正藏载入,需要进行映射
      model.load_state_dict(torch.load(args.save, map_location='cuda:0'))
      
    2. 进入batch循环,执行predict函数

      1. 输入:article ids,input length,num_pass
      2. 进入n_pass循环
        1. 对title进行编码,得到编码状态
        2. 解码(不是teacher forcing),进入max_y_len循环:
          1. 每次对一个batch的第i个字符进行解码
          2. 用encoder final hidden state作为decoder initial hidden state
          3. 根据current decoder state 与 encoder states 进行 attention计算
          4. 根据current decoder state 与 draft embedding 进行 attention计算
          5. 将attention vectors拼接起来通过一个全连接层,作为解码输入
          6. 将解码输出映射为单词表上的概率分布,进行采样获得当前解码时间步概率最大的单词(将UNK对应的概率人为设置为0,方法是把softmax之前的logits中UNK所对应的项改为-inf)此处进行的是greedy search,而不是beam search
          7. 得到[batch_size, 1]个symbols,保存在一个列表中;当循环结束后,将列表进行stack,得到[batch_size, max_y_len]
      3. 记录解码结果

    TO DO

    1. 每个epoch结束进行一次validation,当validation的平均loss大于之前时刻的loss,停止训练。

    2. 添加mixed objective function

    3. 将encoder放入循环外,对于一个batch只编码一次

    4. 将每一pass的decoder 换做不一样的。甚至可以一个是Transformer,一个是RNN(进行两次beam search)。这个实现中,前面几个pass的解码都是greedy search

    5. 搞清楚Transformer的解码原理,理清楚beam searcher应该输入什么维度Tensor,方便进行植入

    6. 原文中model类的forward函数里是:title encoder,draft encoder(条件),decoder。其中draft encoder使用不使用,需要根据是否有draft输入来确定。model的forward函数循环执行pass数次。

      但是完全可以执行model的forward函数一次,但是forward函数里写清楚步骤:进行一次title encoding,再进入pass数循环,上一pass的output draft,可以拿来用做当前pass的输入(如果previously generated draft = None),那么一定是第一次解码。这样就不用进行多次对title编码,效率会高一些

  • 相关阅读:
    codeforces 732E(贪心)
    codeforces 732D(二分)
    codeforces 731C(DFS)
    codeforces 651C(map、去重)
    codeforces 723D(DFS)
    codeforces 721C (拓排 + DP)
    2018 Multi-University Training Contest 3
    2018 Multi-University Training Contest 2
    2018 Multi-University Training Contest 1
    The 2018 ACM-ICPC Chinese Collegiate Programming Contest
  • 原文地址:https://www.cnblogs.com/lauspectrum/p/11256794.html
Copyright © 2020-2023  润新知