简单解析transformer代码

2023-10-26

详解transformer代码

1.代码下载:

在github下载了比较热门的transformer代码的实现,其gith地址为:https://github.com/Kyubyong/transformer

2.prepro.py

  • 主要负责生成对应的预处理语料文件,并利用sentencepiece包来处理原始语料。

2.1 首先进行语料预处理阶段

    # train
    _prepro = lambda x:  [line.strip() for line in open(x, 'r').read().split("\n") \
                      if not line.startswith("<")]
    prepro_train1, prepro_train2 = _prepro(train1), _prepro(train2)
    assert len(prepro_train1)==len(prepro_train2), "Check if train source and target files match."

    # eval
    _prepro = lambda x: [re.sub("<[^>]+>", "", line).strip() \
                     for line in open(x, 'r').read().split("\n") \
                     if line.startswith("<seg id")]
    prepro_eval1, prepro_eval2 = _prepro(eval1), _prepro(eval2)
    assert len(prepro_eval1) == len(prepro_eval2), "Check if eval source and target files match."

    # test
    prepro_test1, prepro_test2 = _prepro(test1), _prepro(test2)
    assert len(prepro_test1) == len(prepro_test2), "Check if test source and target files match."

代码中可以看到,针对train,eval和test数据集进行了预处理,把其中的一些标点符号去掉

2.2 生成预处理过后的对应数据集

    def _write(sents, fname):
    with open(fname, 'w') as fout:
        fout.write("\n".join(sents))
    _write(prepro_train1, "iwslt2016/prepro/train.de")
    _write(prepro_train2, "iwslt2016/prepro/train.en")
    _write(prepro_train1+prepro_train2, "iwslt2016/prepro/train")
    _write(prepro_eval1, "iwslt2016/prepro/eval.de")
    _write(prepro_eval2, "iwslt2016/prepro/eval.en")
    _write(prepro_test1, "iwslt2016/prepro/test.de")
    _write(prepro_test2, "iwslt2016/prepro/test.en")

其中生成的“train”文件,是结合了“train.de”和“train.en”,以“de”结尾的是德语,以“en”结尾的是翻译成的英语。

2.3 sentencepiece处理

  • 在代码中,利用了sentencepiece中的bpe算法来进行分词。
  • BPE(Byte Pair Encoding,双字节编码)。2016年应用于机器翻译,解决 集外词(OOV)和罕见词(Rare word)问题。
  • BPE算法属于的是预处理中中的subword算法
    import sentencepiece as spm
    train = '--input=iwslt2016/prepro/train --pad_id=0 --unk_id=1 \ #输入训练文件
             --bos_id=2 --eos_id=3\
             --model_prefix=iwslt2016/segmented/bpe --vocab_size={} \ #输出文件的前缀名陈
             --model_type=bpe'.format(hp.vocab_size)
    spm.SentencePieceTrainer.Train(train)

    logging.info("# Load trained bpe model")
    sp = spm.SentencePieceProcessor()
    sp.Load("iwslt2016/segmented/bpe.model")    

    logging.info("# Segment")
    def _segment_and_write(sents, fname):
        with open(fname, "w") as fout:
            for sent in sents:
                pieces = sp.EncodeAsPieces(sent)    #对文件进行编码
                fout.write(" ".join(pieces) + "\n")

(1) BPE algorithm

BPE(字节对)编码或二元编码是一种简单的数据压缩形式,其中最常见的一对连续字节数据被替换为该数据中不存在的字节。 后期使用时需要一个替换表来重建原始数据。OpenAI GPT-2 与Facebook RoBERTa均采用此方法构建subword vector.

  • 优点:可以有效地平衡词汇表大小和步数(编码句子所需的token数量)。
  • 缺点:基于贪婪和确定的符号替换,不能提供带概率的多个分片结果。

(2)BPE算法过程

1)准备足够大的训练语料

2)确定期望的subword词表大小

3)将单词拆分为字符序列并在末尾添加后缀“ </ w>”,统计单词频率。 本阶段的subword的粒度是字符。 例如,“ low”的频率为5,那么我们将其改写为“ l o w </ w>”:5

4)统计每一个连续字节对的出现频率,选择最高频者合并成新的subword

5)重复第4步直到达到第2步设定的subword词表大小或下一个最高频的字节对出现频率为1

停止符"“的意义在于表示subword是词后缀。举例来说:“st"字词不加”“可以出现在词首如"st ar”,加了”“表明改字词位于词尾,如"wide st”,二者意义截然不同。

每次合并后词表可能出现3种变化:

  • +1,表明加入合并后的新字词,同时原来在2个子词还保留(2个字词不是完全同时连续出现)
  • +0,表明加入合并后的新字词,同时原来2个子词中一个保留,一个被消解(一个字词完全随着另一个字词的出现而紧跟着出现)
  • -1,表明加入合并后的新字词,同时原来2个子词都被消解(2个字词同时连续出现)

实际上,随着合并的次数增加,词表大小通常先增加后减小。

(3)BPE算法例子

输入:
{'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w e s t </w>': 6, 'w i d e s t </w>': 3}

Iter 1, 最高频连续字节对"e""s"出现了6+3=9次,合并成"es"。输出:
{'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w es t </w>': 6, 'w i d es t </w>': 3}

Iter 2, 最高频连续字节对"es""t"出现了6+3=9, 合并成"est"。输出:
{'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w est </w>': 6, 'w i d est </w>': 3}

Iter 3, 以此类推,最高频连续字节对为"est""</w>" 输出:
{'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w est</w>': 6, 'w i d est</w>': 3}

Iter n, 继续迭代直到达到预设的subword词表大小或下一个最高频的字节对出现频率为1

3.data_load.py

  • 主要负责生成batch数据

3.1 主方法:get_batch

  • 用来生成多个batch数据。
  • 主要使用了“tf.data.Dataset.from_generator”加载数据的方法,把句子中的词语生成在词典中的id。
def get_batch(fpath1, fpath2, maxlen1, maxlen2, vocab_fpath, batch_size, shuffle=False):
    '''Gets training / evaluation mini-batches
    fpath1: source file path. string.
    fpath2: target file path. string.
    maxlen1: source sent maximum length. scalar.
    maxlen2: target sent maximum length. scalar.
    vocab_fpath: string. vocabulary file path.
    batch_size: scalar
    shuffle: boolean

    Returns
    batches
    num_batches: number of mini-batches
    num_samples
    '''
    sents1, sents2 = load_data(fpath1, fpath2, maxlen1, maxlen2)
    batches = input_fn(sents1, sents2, vocab_fpath, batch_size, shuffle=shuffle)
    num_batches = calc_num_batches(len(sents1), batch_size)
    return batches, num_batches, len(sents1)
参数返回 描述
batches tf.data.Dataset的一种形式,包含了元组xs()和元组ys
num_batches 总共有多个个batches进行迭代,也就是有多少轮epoch
len(sents1) 数据集的大小

3.2 load_data

  • 加载数据
def load_data(fpath1, fpath2, maxlen1, maxlen2):
    '''Loads source and target data and filters out too lengthy samples.
    fpath1: source file path. string.
    fpath2: target file path. string.
    maxlen1: source sent maximum length. scalar.
    maxlen2: target sent maximum length. scalar.

    Returns
    sents1: list of source sents
    sents2: list of target sents
    '''
    sents1, sents2 = [], []
    with open(fpath1, 'r') as f1, open(fpath2, 'r') as f2:
        for sent1, sent2 in zip(f1, f2):
            if len(sent1.split()) + 1 > maxlen1: continue # 1: </s>
            if len(sent2.split()) + 1 > maxlen2: continue  # 1: </s>
            sents1.append(sent1.strip())
            sents2.append(sent2.strip())
    return sents1, sents2
  • 当句子超过长度maxlen1或者maxlen2的时候,则抛弃
  • 保存句子,返回输入和输入句子的列表

3.3 input_fn

def encode(inp, type, dict):
    '''Converts string to number. Used for `generator_fn`.
    inp: 1d byte array.
    type: "x" (source side) or "y" (target side)
    dict: token2idx dictionary

    Returns
    list of numbers
    '''
    inp_str = inp.decode("utf-8")
    if type=="x": tokens = inp_str.split() + ["</s>"]
    else: tokens = ["<s>"] + inp_str.split() + ["</s>"]

    x = [dict.get(t, dict["<unk>"]) for t in tokens]
    return x

def generator_fn(sents1, sents2, vocab_fpath):
    '''Generates training / evaluation data
    sents1: list of source sents
    sents2: list of target sents
    vocab_fpath: string. vocabulary file path.

    yields
    xs: tuple of
        x: list of source token ids in a sent
        x_seqlen: int. sequence length of x
        sent1: str. raw source (=input) sentence
    labels: tuple of
        decoder_input: decoder_input: list of encoded decoder inputs
        y: list of target token ids in a sent
        y_seqlen: int. sequence length of y
        sent2: str. target sentence
    '''
    token2idx, _ = load_vocab(vocab_fpath)
    for sent1, sent2 in zip(sents1, sents2):
        x = encode(sent1, "x", token2idx)
        y = encode(sent2, "y", token2idx)
        decoder_input, y = y[:-1], y[1:]

        x_seqlen, y_seqlen = len(x), len(y)
        yield (x, x_seqlen, sent1), (decoder_input, y, y_seqlen, sent2)

def input_fn(sents1, sents2, vocab_fpath, batch_size, shuffle=False):
    '''Batchify data
    sents1: list of source sents
    sents2: list of target sents
    vocab_fpath: string. vocabulary file path.
    batch_size: scalar
    shuffle: boolean

    Returns
    xs: tuple of
        x: int32 tensor. (N, T1) # 句子中每个词语转换为id
        x_seqlens: int32 tensor. (N,) # 句子原有长度
        sents1: str tensor. (N,) # 单个句子
    ys: tuple of
        decoder_input: int32 tensor. (N, T2) # 句子中每个词语转换为id,decoder输入
        y: int32 tensor. (N, T2) # 句子中每个词语转换为id,decoder输出
        y_seqlen: int32 tensor. (N, ) # 句子原有长度
        sents2: str tensor. (N,) # 单个句子
    '''
    shapes = (([None], (), ()),
              ([None], [None], (), ()))
    types = ((tf.int32, tf.int32, tf.string),
             (tf.int32, tf.int32, tf.int32, tf.string))
    paddings = ((0, 0, ''),
                (0, 0, 0, ''))

    dataset = tf.data.Dataset.from_generator(
        generator_fn,
        output_shapes=shapes,
        output_types=types,
        args=(sents1, sents2, vocab_fpath))  # <- arguments for generator_fn. converted to np string arrays

    if shuffle: # for training
        dataset = dataset.shuffle(128*batch_size)

    dataset = dataset.repeat()  # iterate forever
    dataset = dataset.padded_batch(batch_size, shapes, paddings).prefetch(1)

    return dataset
  • 用了“tf.data.Dataset.from_generator”加载数据,生成器方法为:generator_fn
  • 返回的dataset中,有两个变量xs和ys。其中xs中包含:x,x_seqlens,sents1;ys中包含:decoder_input,y,y_seqlen,sents2
  • 在encoder输入句子x中,添加了结尾符号“</s>”;在输出句子y中,添加了开头符号“<s>”和结尾符号“</s>”
  • decoder模型中,decoder_input:用了y[:-1]
  • decoder模型中,输出:用了y[1:]
  • 最后再用“0” padding 句子,把所有的词语转换成对应的词典id
  • xs和ys的描述:
xs: tuple of
    x: int32 tensor. (N, T1) # 句子中每个词语转换为id
    x_seqlens: int32 tensor. (N,) # 句子原有长度
    sents1: str tensor. (N,) # 单个句子
ys: tuple of
    decoder_input: int32 tensor. (N, T2) # 句子中每个词语转换为id,decoder输入
    y: int32 tensor. (N, T2) # 句子中每个词语转换为id,decoder输出
    y_seqlen: int32 tensor. (N, ) # 句子原有长度
    sents2: str tensor. (N,) # 单个句子

4.model.py

  • 实现transformer模型的主要代码

4.1 初始化

    def __init__(self, hp):
        self.hp = hp
        self.token2idx, self.idx2token = load_vocab(hp.vocab)
        self.embeddings = get_token_embeddings(self.hp.vocab_size, self.hp.d_model, zero_pad=True)
  • get_token_embeddings:生成词向量矩阵,这个矩阵是随机初始化的,且self.embeddings设置成了tf.get_variable共享参数
  • self.embeddings:其维度为(vocab_size,d_model),作者默认为维度是(32001,512)

4.2 encode模型

这部分代码是实现encode模型的

    def encode(self, xs, training=True):
        '''
        Returns
        memory: encoder outputs. (N, T1, d_model)
        '''
        with tf.variable_scope("encoder", reuse=tf.AUTO_REUSE):
            x, seqlens, sents1 = xs

            # src_masks
            src_masks = tf.math.equal(x, 0) # (N, T1)

            # embedding
            enc = tf.nn.embedding_lookup(self.embeddings, x) # (N, T1, d_model)
            enc *= self.hp.d_model**0.5 # scale

            enc += positional_encoding(enc, self.hp.maxlen1)
            enc = tf.layers.dropout(enc, self.hp.dropout_rate, training=training)

            ## Blocks
            for i in range(self.hp.num_blocks):
                with tf.variable_scope("num_blocks_{}".format(i), reuse=tf.AUTO_REUSE):
                    # self-attention
                    enc = multihead_attention(queries=enc,
                                              keys=enc,
                                              values=enc,
                                              key_masks=src_masks,
                                              num_heads=self.hp.num_heads,
                                              dropout_rate=self.hp.dropout_rate,
                                              training=training,
                                              causality=False)
                    # feed forward
                    enc = ff(enc, num_units=[self.hp.d_ff, self.hp.d_model])
        memory = enc
        return memory, sents1, src_masks

(1)超参数的含义:

超参数名称 含义
N batch_size
T1 句子长度
d_model 词向量维度

(2)实现的功能

  • 输入词向量+positional_encoding
  • encode中共有6个blocks进行连接,每个encode中有multihead attention和全连接层ff进行连接

(3)具体的分析

4.2.1 positional encoding

transformer模型中缺少一种解释输入序列中单词顺序的方法,它跟序列模型还不不一样。为了处理这个问题,transformer给encoder层和decoder层的输入添加了一个额外的向量Positional Encoding,维度和embedding的维度一样,这个向量采用了一种很独特的方法来让模型学习到这个值,这个向量能决定当前词的位置,或者说在一个句子中不同的词之间的距离。这个位置向量的具体计算方法有很多种,论文中的计算方法如下:

其中pos是指当前词在句子中的位置,i是指向量中每个值的index,可以看出,在偶数位置,使用正弦编码,在奇数位置,使用余弦编码。

def positional_encoding(inputs,
                        maxlen,
                        masking=True,
                        scope="positional_encoding"):
    '''Sinusoidal Positional_Encoding. See 3.5
    inputs: 3d tensor. (N, T, E)
    maxlen: scalar. Must be >= T
    masking: Boolean. If True, padding positions are set to zeros.
    scope: Optional scope for `variable_scope`.

    returns
    3d tensor that has the same shape as inputs.
    '''

    E = inputs.get_shape().as_list()[-1] # static
    N, T = tf.shape(inputs)[0], tf.shape(inputs)[1] # dynamic
    with tf.variable_scope(scope, reuse=tf.AUTO_REUSE):
        # position indices
        position_ind = tf.tile(tf.expand_dims(tf.range(T), 0), [N, 1]) # (N, T)

        # First part of the PE function: sin and cos argument
        position_enc = np.array([
            [pos / np.power(10000, (i-i%2)/E) for i in range(E)]
            for pos in range(maxlen)])

        # Second part, apply the cosine to even columns and sin to odds.
        position_enc[:, 0::2] = np.sin(position_enc[:, 0::2])  # dim 2i
        position_enc[:, 1::2] = np.cos(position_enc[:, 1::2])  # dim 2i+1
        position_enc = tf.convert_to_tensor(position_enc, tf.float32) # (maxlen, E)

        # lookup
        outputs = tf.nn.embedding_lookup(position_enc, position_ind)

        # masks
        if masking:
            outputs = tf.where(tf.equal(inputs, 0), inputs, outputs)

        return tf.to_float(outputs)

最后得到的positional encoding加在原始的初始化词向量中。

4.2.2 multihead_attention

multihead attention的主要公式为:
在这里插入图片描述

def scaled_dot_product_attention(Q, K, V, key_masks,
                                 causality=False, dropout_rate=0.,
                                 training=True,
                                 scope="scaled_dot_product_attention"):
    '''See 3.2.1.
    Q: Packed queries. 3d tensor. [N, T_q, d_k].
    K: Packed keys. 3d tensor. [N, T_k, d_k].
    V: Packed values. 3d tensor. [N, T_k, d_v].
    key_masks: A 2d tensor with shape of [N, key_seqlen]
    causality: If True, applies masking for future blinding
    dropout_rate: A floating point number of [0, 1].
    training: boolean for controlling droput
    scope: Optional scope for `variable_scope`.
    '''
    with tf.variable_scope(scope, reuse=tf.AUTO_REUSE):
        d_k = Q.get_shape().as_list()[-1]

        # dot product
        outputs = tf.matmul(Q, tf.transpose(K, [0, 2, 1]))  # (N, T_q, T_k)

        # scale
        outputs /= d_k ** 0.5

        # key masking
        outputs = mask(outputs, key_masks=key_masks, type="key")

        # causality or future blinding masking
        if causality:
            outputs = mask(outputs, type="future")

        # softmax
        outputs = tf.nn.softmax(outputs)
        attention = tf.transpose(outputs, [0, 2, 1])
        tf.summary.image("attention", tf.expand_dims(attention[:1], -1))

        # # query masking
        # outputs = mask(outputs, Q, K, type="query")

        # dropout
        outputs = tf.layers.dropout(outputs, rate=dropout_rate, training=training)

        # weighted sum (context vectors)
        outputs = tf.matmul(outputs, V)  # (N, T_q, d_v)

    return outputs

def multihead_attention(queries, keys, values, key_masks,
                        num_heads=8, 
                        dropout_rate=0,
                        training=True,
                        causality=False,
                        scope="multihead_attention"):
    '''Applies multihead attention. See 3.2.2
    queries: A 3d tensor with shape of [N, T_q, d_model].
    keys: A 3d tensor with shape of [N, T_k, d_model].
    values: A 3d tensor with shape of [N, T_k, d_model].
    key_masks: A 2d tensor with shape of [N, key_seqlen]
    num_heads: An int. Number of heads.
    dropout_rate: A floating point number.
    training: Boolean. Controller of mechanism for dropout.
    causality: Boolean. If true, units that reference the future are masked.
    scope: Optional scope for `variable_scope`.
        
    Returns
      A 3d tensor with shape of (N, T_q, C)  
    '''
    d_model = queries.get_shape().as_list()[-1]
    with tf.variable_scope(scope, reuse=tf.AUTO_REUSE):
        # Linear projections
        Q = tf.layers.dense(queries, d_model, use_bias=True) # (N, T_q, d_model)
        K = tf.layers.dense(keys, d_model, use_bias=True) # (N, T_k, d_model)
        V = tf.layers.dense(values, d_model, use_bias=True) # (N, T_k, d_model)
        
        # Split and concat
        Q_ = tf.concat(tf.split(Q, num_heads, axis=2), axis=0) # (h*N, T_q, d_model/h)
        K_ = tf.concat(tf.split(K, num_heads, axis=2), axis=0) # (h*N, T_k, d_model/h)
        V_ = tf.concat(tf.split(V, num_heads, axis=2), axis=0) # (h*N, T_k, d_model/h)

        # Attention
        outputs = scaled_dot_product_attention(Q_, K_, V_, key_masks, causality, dropout_rate, training)

        # Restore shape
        outputs = tf.concat(tf.split(outputs, num_heads, axis=0), axis=2 ) # (N, T_q, d_model)
              
        # Residual connection
        outputs += queries
              
        # Normalize
        outputs = ln(outputs)
 
    return outputs
  • 其中d_k参数的值为:512/8 = 64
  • 用到了residual方法和layer normalization方法
  • 可以看到,在encode模型中Q,K,V三个参数都是一致的。

4.3 decode模型

这部分代码是实现decode模型的

    def decode(self, ys, memory, src_masks, training=True):
        '''
        memory: encoder outputs. (N, T1, d_model)
        src_masks: (N, T1)

        Returns
        logits: (N, T2, V). float32.
        y_hat: (N, T2). int32
        y: (N, T2). int32
        sents2: (N,). string.
        '''
        with tf.variable_scope("decoder", reuse=tf.AUTO_REUSE):
            decoder_inputs, y, seqlens, sents2 = ys

            # tgt_masks
            tgt_masks = tf.math.equal(decoder_inputs, 0)  # (N, T2)

            # embedding
            dec = tf.nn.embedding_lookup(self.embeddings, decoder_inputs)  # (N, T2, d_model)
            dec *= self.hp.d_model ** 0.5  # scale

            dec += positional_encoding(dec, self.hp.maxlen2)
            dec = tf.layers.dropout(dec, self.hp.dropout_rate, training=training)

            # Blocks
            for i in range(self.hp.num_blocks):
                with tf.variable_scope("num_blocks_{}".format(i), reuse=tf.AUTO_REUSE):
                    # Masked self-attention (Note that causality is True at this time)
                    dec = multihead_attention(queries=dec,
                                              keys=dec,
                                              values=dec,
                                              key_masks=tgt_masks,
                                              num_heads=self.hp.num_heads,
                                              dropout_rate=self.hp.dropout_rate,
                                              training=training,
                                              causality=True,
                                              scope="self_attention")

                    # Vanilla attention
                    dec = multihead_attention(queries=dec,
                                              keys=memory,
                                              values=memory,
                                              key_masks=src_masks,
                                              num_heads=self.hp.num_heads,
                                              dropout_rate=self.hp.dropout_rate,
                                              training=training,
                                              causality=False,
                                              scope="vanilla_attention")
                    ### Feed Forward
                    dec = ff(dec, num_units=[self.hp.d_ff, self.hp.d_model])

        # Final linear projection (embedding weights are shared)
        weights = tf.transpose(self.embeddings) # (d_model, vocab_size)
        logits = tf.einsum('ntd,dk->ntk', dec, weights) # (N, T2, vocab_size)
        y_hat = tf.to_int32(tf.argmax(logits, axis=-1))

        return logits, y_hat, y, sents2

encode模型中与encode不同之处在于,实现了两个multihead attention结构。

  • 第一个multihead attention结构和encode模型中的一样,都为self-attention结构
  • 第二个multihead attention结构,在Q,K,V的输入就不同了,其输入memory实际上是encode模型的输入结果。
  • tf.einsum函数是实现矩阵相乘的方式,一般来说如果用mutual的话,不能进行三维矩阵和二维矩阵的相乘,而einsum则可以,通过设置参数,如“ntd,dk->ntk”可以得到矩阵维度是[n,t,k]。

4.4 train函数

  • 用来训练模型的函数
  • label_smoothing函数用来进行one hot函数的丝滑处理:
def label_smoothing(inputs, epsilon=0.1):
    '''Applies label smoothing. See 5.4 and https://arxiv.org/abs/1512.00567.
    inputs: 3d tensor. [N, T, V], where V is the number of vocabulary.
    epsilon: Smoothing rate.
    
    For example,
    
    ''
    import tensorflow as tf
    inputs = tf.convert_to_tensor([[[0, 0, 1], 
       [0, 1, 0],
       [1, 0, 0]],

      [[1, 0, 0],
       [1, 0, 0],
       [0, 1, 0]]], tf.float32)
       
    outputs = label_smoothing(inputs)
    
    with tf.Session() as sess:
        print(sess.run([outputs]))
    
    >>
    [array([[[ 0.03333334,  0.03333334,  0.93333334],
        [ 0.03333334,  0.93333334,  0.03333334],
        [ 0.93333334,  0.03333334,  0.03333334]],

       [[ 0.93333334,  0.03333334,  0.03333334],
        [ 0.93333334,  0.03333334,  0.03333334],
        [ 0.03333334,  0.93333334,  0.03333334]]], dtype=float32)]   
    ''   
    '''
    V = inputs.get_shape().as_list()[-1] # number of channels
    return ((1-epsilon) * inputs) + (epsilon / V)
  • loss函数用到了交叉熵函数,但是在计算的时候去掉了padding的影响。
    y_ = label_smoothing(tf.one_hot(y, depth=self.hp.vocab_size))
    ce = tf.nn.softmax_cross_entropy_with_logits_v2(logits=logits, labels=y_)
    nonpadding = tf.to_float(tf.not_equal(y, self.token2idx["<pad>"]))  # 0: <pad>
    loss = tf.reduce_sum(ce * nonpadding) / (tf.reduce_sum(nonpadding) + 1e-7)
  • 同时多学习率进行调整,用到了warmup操作,初始阶段lr慢慢上升,迭代后期则慢慢下降
    global_step = tf.train.get_or_create_global_step()
    lr = noam_scheme(self.hp.lr, global_step, self.hp.warmup_steps)

4.5 eval函数

  • 大致和train函数差不多
  • 在decoder输入的时候,没有用到输出数据集,而是用了xs的输入数据集进行构造,这个decoder_input实际的大小为[N,1],其中N为batch size,也即是仅仅输入了<s>开头标记。同时在推断下一个词语,一个一个词语进行拼接
    decoder_inputs, y, y_seqlen, sents2 = ys

    decoder_inputs = tf.ones((tf.shape(xs[0])[0], 1), tf.int32) * self.token2idx["<s>"]
    ys = (decoder_inputs, y, y_seqlen, sents2)

    memory, sents1, src_masks = self.encode(xs, False)

    logging.info("Inference graph is being built. Please be patient.")
    for _ in tqdm(range(self.hp.barrages_maxlen2)):
        logits, y_hat, y, sents2 = self.decode(ys, memory, src_masks, False)
        if tf.reduce_sum(y_hat, 1) == self.token2idx["<pad>"]: break

        _decoder_inputs = tf.concat((decoder_inputs, y_hat), 1)
        ys = (_decoder_inputs, y, y_seqlen, sents2)

上述代码最后会生成 y h a t y_{hat} yhat,其矩阵大小为 [ N , T 2 ] [N,T2] [N,T2],其中 N N N b a t c h _ s i z e batch\_size batch_size T 2 T2 T2为句子长度。

5.train.py

  • 用来训练模型,生成的模型在log文件夹中
  • 同时计算出BELU的分数值:利用了文件’multi-bleu.perl’
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

简单解析transformer代码 的相关文章

  • Pytorch 损失为 nan

    我正在尝试用 pytorch 编写我的第一个神经网络 不幸的是 当我想要得到损失时遇到了问题 出现以下错误信息 RuntimeError Function LogSoftmaxBackward0 returned nan values in
  • 隐藏控制台并执行 python 脚本

    我正在尝试使用 pyinstaller 在 Windows 10 上使用 pyqt5 模块编译在 python 3 中构建的 python 脚本 该脚本在运行时隐藏窗口 为了编译我的脚本 我执行了以下命令 pyinstaller onefi
  • 在 django 中构建动态表单

    我正在尝试根据存储在数据库中的字段及其定义动态构建一个表单 在我的数据库中 我定义了 1 个带有一些标签的复选框和 1 个带有一些标签的文本字段 如何根据数据库中的数据在我的视图中动态构建表单 Thanks 以下是我在 EuroDjango
  • 识别 Windows 版本

    我正在编写一个打印出详细 Windows 版本信息的函数 输出可能是这样的元组 32bit XP Professional SP3 English 它将支持 Windows XP 及更高版本 我一直坚持获取 Windows 版本 例如 专业
  • 通过 pyodbc 连接到 Azure SQL 数据库

    我使用 pyodbc 连接到本地 SQL 数据库 该数据库工作正常 SQLSERVERLOCAL Driver SQL Server Native Client 11 0 Server localdb v11 0 integrated se
  • 这是我尝试安装 pip3 时得到的结果

    这是我尝试安装 pip3 时得到的结果 sudo apt get install python3 pip Reading package lists Done Building dependency tree Reading state i
  • 使用python同时播放两个正弦音

    我正在使用 python 来播放正弦音 音调基于计算机的内部时间 以分钟为单位 但我想根据秒同时播放一个音调 以获得和谐或双重的声音 这就是我到目前为止所拥有的 有人能指出我正确的方向吗 from struct import pack fr
  • Flask-httpauth: get_password 装饰器如何为 basic-auth 工作?

    我想知道有没有人用过这个烧瓶延伸 https github com miguelgrinberg flask httpauth简化 http basic auth 基本上我不明白这个example https github com migu
  • 您必须为 MNIST 数据集的占位符张量“Placeholder”提供一个值,dtype float 和 shape [?,784]

    这是我在 MNIST 数据集上测试量化的示例 我正在使用以下代码测试我的模型 import tensorflow as tf from tensorflow examples tutorials mnist import input dat
  • 设置区域设置和字符串模块

    这个简单的脚本 from locale import LC ALL setlocale print setlocale LC ALL from string import letters print letters 给我这个输出 tr TR
  • 如何将 Jinja 与 Twisted 一起使用?

    我正在计划使用 Python 与 Twisted Storm 和 Jinja 一起开发一个讨论软件 问题是 Jinja 不是为 Twisted 或异步套接字库而设计的 并且使用 Twisted 提供的性能是我不打算使用 Flask 的原因
  • __subclasses__ 没有显示任何内容

    我正在实现一个从适当的子类返回对象的函数 如果我搬家SubClass from base py 没有出现子类 subclasses 它们必须在同一个文件中吗 也许我从来没有直接导入subclass py对Python隐藏子类 我能做些什么
  • Python 对象属性 - 访问方法

    假设我有一个具有某些属性的类 在 Pythonic OOP 中 如何访问这些属性是最好的 就像obj attr 或者也许编写 get 访问器 此类事物可接受的命名风格是什么 Edit 您能否详细说明使用单下划线或双前导下划线命名属性的最佳实
  • 使用 Beautifulsoup 解析时保持 XML 文件的缩进

    我正在使用 BS4 解析 XML 文件并尝试将其写回新的 XML 文件 输入文件
  • 可变和不可变类变量如何初始化?

    运行下面的示例代码 class S i 0 a def init self self i 1 self a append 1 s1 S print s1 i s1 a s2 S print s2 i s2 a 输出将是 1 1 1 1 1
  • 为什么在 python 控制台中对 SparkSession.builder.getOrCreate() 的调用被视为命令行 Spark-submit?

    代替python console我正在尝试创建一个Spark Session 我没有使用pyspark以隔离依赖关系 为什么是spark submit命令行提示并生成错误 NOTE SPARK PREPEND CLASSES is set
  • 将 Matlab MEX 文件中的函数直接嵌入到 Python 中

    我正在使用专有的 Matlab MEX 文件在 Matlab 中导入一些仿真结果 当然没有可用的源代码 Matlab 的接口实际上非常简单 因为只有一个函数 返回一个 Matlab 结构体 我想知道是否有任何方法可以直接从Python调用M
  • 在 python 中计时时,我应该如何考虑 subprocess.Popen() 开销?

    编码社区的成员比我更聪明 我有一个 python 问题要问你们 我正在尝试优化一个 python 脚本 该脚本 除其他外 返回子进程执行和终止的挂钟时间 我想我已经接近这样的事情了 startTime time time process s
  • Windows 10 上的 Tensorflow 安装问题

    我正在尝试在 Win 10 计算机上安装 Tensorflow 我成功安装了Python 3 7 然后尝试按照tensorflow org上的安装说明进行操作 执行时 pip install tensorflow 我收到以下错误消息 错误
  • 使用 PuLP 进行线性优化,变量附加条件

    我必须用 Pull 解决 Python 中的整数线性优化问题 我解决了基本问题 现在我必须添加额外的约束 有人可以帮助我用逻辑指示器添加条件吗 逻辑限制是 如果 A gt 20 则 B gt 5 这是我的代码 from pulp impor

随机推荐

  • 电子闹钟设计

    摘要 本设计是以89c51单片机作为控制核心的闹铃系统 本文大致可以分三个章节 第一章讲用单片机制作电子闹钟带来的优势 还有电子闹钟在未来的电子产品中的趋势以及本次设计所要实现的课题目标 第二章讲一些设计思路和硬件组成 第三讲程序代码 最后
  • CVE10大漏洞总结【网络安全】

    1 OpenSSL心脏出血漏洞 漏洞描述 这项严重缺陷 CVE 2014 0160 的产生是由于未能在memcpy 调用受害用户输入内容作为长度参数之前正确进行边界检查 攻击者可以追踪OpenSSL所分配的64KB缓存将超出必要范围的字节信
  • std::string::replace使用小计

    ctrl c ctrl v 真是害死人 看起来简单的东西 往往却又很容易出问题 究其原因 还在于只知其表不知其里 容易想当然 今天遇到的问题是简单调用string replace替换子串的问题 如 std string sTest1 12x
  • web服务器开发课程项目实训,Web前端开发实训案例教程(初级)

    目 录 第1章 实践概述 1 1 1 实践目标 1 1 2 实践知识地图 1 1 3 实施安排 6 1 3 1 实验部分 技术专题 6 1 3 2 综合实践部分 11 第2章 网页设计与制作 19 2 1 实验目标 19 2 2 实验任务
  • FTP命令详解

    FTP命令是Internet用户使用最频繁的命令之一 不论是在DOS还是UNIX操作系统下使用FTP 都会遇到大量的FTP内部命令 熟悉并灵活应用FTP的内部命令 可以大大方便使用者 并收到事半功倍之效 FTP的命令行格式为 ftp v d
  • bytebuffer 使用demo

    pom文件
  • 微信小程序路由

    wx reLaunch Object object 关闭所有页面 打开到应用内的某个页面 一般是跳转到首页使用 例 wx reLaunch url url wx navigateTo Object object 保留当前页面 跳转到应用内的
  • Java时间转换问题 [Failed to convert property value of type ‘java.lang.String‘ to required type ‘java.

    1 错误提示代码 default message Failed to convert property value of type java lang String to required type java 2 分析原因 遇到java接收
  • macOS 系统下安装Lua及lua-cjson

    macOS 系统下安装Lua及lua cjson lua安装及部署 具体操作步骤如下 curl R O http www lua org ftp lua 5 2 3 tar gz tar zxf lua 5 2 3 tar gz cd lu
  • 豆瓣读书top250数据爬取与可视化

    爬虫 scrapy 题目 根据豆瓣读书top250 根据出版社对书籍数量分类 绘制饼图 搭建环境 import scrapy import numpy as np import pandas as pd import matplotlib
  • UE5《Electric Dreams》项目PCG技术解析 之 PCGCustomNodes详解(三)SG_CopyPointsWithHierarchy

    继续解析 Electric Dreams 项目中的自定义节点和子图 SG CopyPointsWithHierarchy和PostCopyPoints OffsetIndices 文章目录 前导文章 标准组合拳 SG CopyPointsW
  • STM32开发中各库函数的主要作用和关系。

    STM32开发中各库函数的主要作用和关系 STM32各库函数关系的简单解析 您好 这是我第一次使用 CSDN来发布文章 如果有排版不合理 结构凌乱 欢迎私信我交流经验 文章内容如有错误 欢迎读者指正 首先我们了解一下什么是库函数 众所周知
  • 常见的几种开源协议

    在学习中经常能看到一些词 例如 GPL LGPL等等 自打上学那会就遇见过 对它们的具体含义却不了解 今天给它们总结一下 说到开源协议 不得不提GNU 课本上给的定义是 GNU is Not Unix 这是官方给出的递归定义 永远也找不到本
  • Linux基础服务3——samba

    文章目录 一 基本了解 1 1 服务安装 1 2 服务进程和端口 1 3 samba用户 1 4 配置文件 1 4 1 主配置文件 1 4 2 配置文件参数 1 5 安全级别 二 访问samba 2 1 参数测试 2 2 交互式访问 2 3
  • 多线程进阶学习10------AQS详解

    AbstractQueuedSynchronizer 来自于JDK1 5 位于JUC包 由并发编程大师Doug Lea编写 字面翻译就是 抽象队列同步器 简称为AQS AQS作为一个抽象类 是构建JUC包中的锁 比如ReentrantLoc
  • Netty工作原理最详细分析

    NIO通讯服务端步骤 1 创建ServerSocketChannel 为它配置非阻塞模式 2 绑定监听 配置TCP参数 录入backlog大小等 3 创建一个独立的IO线程 用于轮询多路复用器Selector 4 创建Selector 将之
  • 面试嵌入式工程师过程中的常见问题和回答

    1 请介绍一下你的嵌入式系统开发经验 an 首先 回答此类问题时应该尽可能地详细和具体 可以从以下方面介绍自己的嵌入式系统开发经验 1 开发环境和工具 介绍自己使用过哪些开发环境和工具 例如Keil IAR Eclipse等 可以说明自己对
  • Java之变量、标识符、保留字、变量

    文章目录 1 关键字与保留字 2 标识符 2 1 什么是标识符 Identifier 2 2 定义合法标识符规则 重要 2 3 Java 中的名称命名规范 3 变量 3 1 变量的声明与使用 3 2 基本数据类型 3 2 1 整数类型 by
  • Java---TCP通信

    目录 1 TCP通信 快速入门 编写客户端代码 步骤 客户端发送消息 总结 需求 服务端实现步骤 总结 2 TCP通信 多发多收消息 案例 使用TCP通信实现 多发多收消息 总结 3 TCP通信 同时接受多个客户端消息 重点 总结 4 TC
  • 简单解析transformer代码

    详解transformer代码 文章目录 详解transformer代码 1 代码下载 2 prepro py 2 1 首先进行语料预处理阶段 2 2 生成预处理过后的对应数据集 2 3 sentencepiece处理 3 data loa