在TF2 keras中,我使用tensorflow.keras.losses.MeanSquaredError作为损失函数训练了一个自动编码器。现在,我想通过使用另一个损失函数来进一步训练这个模型,特别是tensorflow.keras.losses.KLDivergence。这是因为最初的无监督学习是为了表示学习而进行的。然后,有了生成的嵌入,我可以将它们聚类,并使用这些聚类进行自我监督,即标签,从而实现第二种监督损失,并进一步改进模型。
这不是迁移学习本身,因为没有向模型中添加新的层,只是改变损失函数,模型继续训练。
我所尝试的是使用带有MSE损失的预训练模型作为新模型的属性:
class ClusterBooster(tf.keras.Model):
def __init__(self, base_model, centers):
super(ClusterBooster, self).__init__()
self.pretrained = base_model
self.centers = centers
def train_step(self, data):
with tf.GradientTape() as tape:
loss = self.compiled_loss(self.P, self.Q, regularization_losses=self.losses)
# Compute gradients
gradients = tape.gradient(loss, self.trainable_variables)
# Update weights
self.optimizer.apply_gradients(zip(gradients, self.trainable_variables))
return {m.name: m.result() for m in self.metrics}
,其中损失是分布p和q之间的KL损失。分布是在回调函数中计算的,而不是模型train_step,因为我需要访问当前epoch (p每5个epoch更新一次,而不是每个epoch):
def on_epoch_begin(self, epoch, logs=None):
z = self.model.pretrained.embed(self.feature, training=True)
z = tf.reshape(z, [tf.shape(z)[0], 1, tf.shape(z)[1]]) # reshape for broadcasting
# CALCULATE Q FOR EVERY EPOCH
partial = tf.math.pow(tf.norm(z - self.model.centers, axis=2, ord='euclidean'), 2)
nominator = 1 / (1 + partial)
denominator = tf.math.reduce_sum(1 / (1 + partial))
self.model.Q = nominator / denominator
# CALCULATE P EVERY 5 EPOCHS TO AVOID INSTABILITY
if epoch % 5 == 0:
partial = tf.math.pow(self.model.Q, 2) / tf.math.reduce_sum(self.model.Q, axis=1, keepdims=True)
nominator = partial
denominator = tf.math.reduce_sum(partial, axis=0)
self.model.P = nominator / denominator
然而,当apply_gradients()被执行时,我得到:
ValueError: No gradients provided for any variable: ['dense/kernel:0', 'dense/bias:0', 'dense_1/kernel:0', 'dense_1/bias:0', 'dense_2/kernel:0', 'dense_2/bias:0', 'dense_3/kernel:0', 'dense_3/bias:0']
我认为这是由于预训练的模型没有被设置为在新模型中的某个地方进一步训练(只调用embed()方法,它不训练模型)。这是一个正确的方法,我只是错过了什么,或者有一个更好的方法?
似乎在回调中发生的任何计算都没有跟踪梯度计算和权重更新。因此,这些计算应该放在自定义Model类(ClusterBooster)的train_step()函数中。
假设我不能访问ClusterBooster的train_step()函数中的epoch数,我创建了一个没有Model类的自定义训练循环,在这里我可以使用普通的python代码(这是预先计算的)。