看上去是一個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的概率
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)
典型的交叉熵的實現啊