ELECTRA:Efficiently Learning an Encoder that Classifies Token Replacements Accurately

看上去是一個GAN的結構,在生成器的訓練中,由於GAN模型在文本上的應用困難,因此,使用的是極大似然的方法。預訓練結束後,在下游的應用上,去掉了生成器,僅使用判別器進行微調。

生成器部分:softmax函數

生成器的輸入:

生成器的輸出:probs Tensor("generator_predictions/Softmax:0", shape=(128, 19, 30522), dtype=float32)

模型代碼的部分:

def model_fn_builder(config: configure_pretraining.PretrainingConfig):
  """Build the model for training."""

  def model_fn(features, labels, mode, params):
    """Build the model for training."""
    # print("config",config)
    # print("feature",features)
    model = PretrainingModel(config, features,
                             mode == tf.estimator.ModeKeys.TRAIN)

    utils.log("Model is built!") #構建模型成功

    if mode == tf.estimator.ModeKeys.TRAIN: #如果是訓練的階段
      train_op = optimization.create_optimizer(  #優化函數
          model.total_loss, config.learning_rate, config.num_train_steps,
          weight_decay_rate=config.weight_decay_rate,
          use_tpu=config.use_tpu,
          warmup_steps=config.num_warmup_steps,
          lr_decay_power=config.lr_decay_power
      )

      output_spec = tf.estimator.tpu.TPUEstimatorSpec(
          mode=mode,
          loss=model.total_loss,
          train_op=train_op,
          training_hooks=[training_utils.ETAHook(
              {} if config.use_tpu else dict(loss=model.total_loss),
              config.num_train_steps, config.iterations_per_loop,
              config.use_tpu)]
      )

    elif mode == tf.estimator.ModeKeys.EVAL: #如果是驗證的階段
      output_spec = tf.estimator.tpu.TPUEstimatorSpec(
          mode=mode,
          loss=model.total_loss,
          eval_metrics=model.eval_metrics,
          evaluation_hooks=[training_utils.ETAHook(
              {} if config.use_tpu else dict(loss=model.total_loss),
              config.num_eval_steps, config.iterations_per_loop,
              config.use_tpu, is_training=False)])
    else:
      raise ValueError("Only TRAIN and EVAL modes are supported")
    return output_spec
  return model_fn #返回函數

features:

features :

Inputs(input_ids=<tf.Tensor 'StopGradient_1:0' shape=(128, 128) dtype=int32>, 輸入序列
input_mask=<tf.Tensor 'IteratorGetNext:1' shape=(128, 128) dtype=int32>, 掩碼後的輸入
segment_ids=<tf.Tensor 'IteratorGetNext:2' shape=(128, 128) dtype=int32>, 句子分段部分,第一句和第二句
masked_lm_positions=<tf.Tensor 'mul_2:0' shape=(128, 19) dtype=int32>,  掩碼的位置
masked_lm_ids=<tf.Tensor 'mul_4:0' shape=(128, 19) dtype=int32>,  掩碼對應的字的序列
masked_lm_weights=<tf.Tensor 'Cast_3:0' shape=(128, 19) dtype=float32>) #需要掩碼的位置的權重


生成器的代碼

 generator = self._build_transformer(
          masked_inputs, is_training,
          bert_config=get_generator_config(config, self._bert_config),
          embedding_size=(None if config.untied_generator_embeddings
                          else embedding_size),
          untied_embeddings=config.untied_generator_embeddings,
          name="generator")
masked_inputs:爲輸入部分等同於features
Inputs(input_ids=<tf.Tensor 'StopGradient_1:0' shape=(128, 128) dtype=int32>, 
input_mask=<tf.Tensor 'IteratorGetNext:1' shape=(128, 128) dtype=int32>, 
segment_ids=<tf.Tensor 'IteratorGetNext:2' shape=(128, 128) dtype=int32>, masked_lm_positions=<tf.Tensor 'mul_2:0' shape=(128, 19) dtype=int32>, 
masked_lm_ids=<tf.Tensor 'mul_4:0' shape=(128, 19) dtype=int32>, 
masked_lm_weights=<tf.Tensor 'Cast_3:0' shape=(128, 19) dtype=float32>)

生成器的輸入:

generator = self._build_transformer(
    masked_inputs, is_training, embedding_size=embedding_size)

輸入爲:masked_inputs

標籤爲:

oh_labels = tf.one_hot(
    inputs.masked_lm_ids, depth=self._bert_config.vocab_size,
    dtype=tf.float32)

生成器的softmax層輸出概率:

e(xt)爲t位置上的向量表示,給定x生成xt的概率

微信æªå¾_20181022153759.png-66.5kB

微信æªå¾_20181023174230.png-94kB

softmax:probs = tf.nn.softmax(logits)

log-softmax:   log_probs = tf.nn.log_softmax(logits)

交叉熵:

label_log_probs = -tf.reduce_sum(log_probs * oh_labels, axis=-1)

 

Loss函數:

 

與GAN的不同點

注意:生成器生成的數據正好是對的,這時依然認爲是真實的而不是生成的數據。

更重要的是,生成器是用最大似然法訓練的,而不是訓練來愚弄鑑別器。不能像GAN一樣,用帶有噪聲的向量輸入

 logits = tf.matmul(hidden, model.get_embedding_table(),
                     transpose_b=True)
  logits = tf.nn.bias_add(logits, output_bias)
#print("===",inputs.masked_lm_ids) #Tensor("mul_4:0", shape=(128, 19), dtype=int32)
oh_labels = tf.one_hot(
    inputs.masked_lm_ids, depth=self._bert_config.vocab_size,
    dtype=tf.float32)
#one_hot標籤
#print(inputs) #Inputs(input_ids=<tf.Tensor 'StopGradient_1:0' shape=(128, 128) dtype=int32>,
#print("======",oh_labels) #Tensor("generator_predictions/one_hot:0", shape=(128, 19, 30522), dtype=float32)

probs = tf.nn.softmax(logits)
#print("probs",probs)
#probs Tensor("generator_predictions/Softmax:0", shape=(128, 19, 30522), dtype=float32)
log_probs = tf.nn.log_softmax(logits)
label_log_probs = -tf.reduce_sum(log_probs * oh_labels, axis=-1)

numerator = tf.reduce_sum(inputs.masked_lm_weights * label_log_probs)
denominator = tf.reduce_sum(masked_lm_weights) + 1e-6
loss = numerator / denominator  #分子/分母
preds = tf.argmax(log_probs, axis=-1, output_type=tf.int32)

數據生成的流程

隨機mask輸入生成器,訓練模型。生成器生成數據x^替換mask。

判別器:

判別器是二分類

判別器的損失函數

代買實現

losses = tf.nn.sigmoid_cross_entropy_with_logits(
    logits=logits, labels=labelsf) * weights
  def _get_discriminator_output(self, inputs, discriminator, labels):
    """Discriminator binary classifier.判別器二分類"""
    with tf.variable_scope("discriminator_predictions"):

      hidden = tf.layers.dense(
          discriminator.get_sequence_output(), #判別器的數據輸出
          units=self._bert_config.hidden_size,
          activation=modeling.get_activation(self._bert_config.hidden_act),
          kernel_initializer=modeling.create_initializer(
              self._bert_config.initializer_range))

      logits = tf.squeeze(tf.layers.dense(hidden, units=1), -1)
      weights = tf.cast(inputs.input_mask, tf.float32)
      labelsf = tf.cast(labels, tf.float32)
      losses = tf.nn.sigmoid_cross_entropy_with_logits(
          logits=logits, labels=labelsf) * weights
      per_example_loss = (tf.reduce_sum(losses, axis=-1) /
                          (1e-6 + tf.reduce_sum(weights, axis=-1)))
      loss = tf.reduce_sum(losses) / (1e-6 + tf.reduce_sum(weights))
      probs = tf.nn.sigmoid(logits)
      preds = tf.cast(tf.round((tf.sign(logits) + 1) / 2), tf.int32)
      DiscOutput = collections.namedtuple(
          "DiscOutput", ["loss", "per_example_loss", "probs", "preds",
                         "labels"])
      return DiscOutput(
          loss=loss, per_example_loss=per_example_loss, probs=probs,
          preds=preds, labels=labels,
      )

labels :Tensor("mul_18:0", shape=(128, 128), dtype=int32),此處的標籤是整個原始文本與生成的文本做對比,判斷每個詞是生成的還是真實的

同生成器的標籤是不一樣的生成器的標籤爲mask掉的19個詞,但每個詞都有30522種選擇,也就是字典的大小-vocab_size

Tensor("generator_predictions/one_hot:0", shape=(128, 19, 30522), dtype=float32)
#使用生成器生成的數據替換掉mask位置上的數據,作爲判別器的輸入
updated_input_ids, masked = pretrain_helpers.scatter_update(
    inputs.input_ids, sampled_tokids, inputs.masked_lm_positions)

論文中的公式解析:

上面是求期望

期望的公式是:

可以得出,其中損失函數等價與交叉熵

代碼的實現

probs = tf.nn.softmax(logits)
log_probs = tf.nn.log_softmax(logits)
#print(log_probs) #Tensor("generator_predictions/LogSoftmax:0", shape=(128, 19, 30522), dtype=float32)
label_log_probs = -tf.reduce_sum(log_probs * oh_labels, axis=-1) #axis:指定的維,如果不指定,則計算所有元素的總和;交叉熵
#print(label_log_probs) #Tensor("generator_predictions/Neg:0", shape=(128, 19), dtype=float32)
numerator = tf.reduce_sum(inputs.masked_lm_weights * label_log_probs)
denominator = tf.reduce_sum(masked_lm_weights) + 1e-6
#print(masked_lm_weights) #Tensor("Cast_3:0", shape=(128, 19), dtype=float32)
loss = numerator / denominator
preds = tf.argmax(log_probs, axis=-1, output_type=tf.int32)

典型的交叉熵的實現啊

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章