J'ai un modèle Keras défini de la manière suivante (j'ai essayé de ne garder que les pièces nécessaires):

temperature = 5.0

def knowledge_distillation_loss(y_true, y_pred, lambda_const):    
    y_true, logits = y_true[:, :10], y_true[:, 10:]
    y_soft = K.softmax(logits/temperature)
    y_pred, y_pred_soft = y_pred[:, :10], y_pred[:, 10:]    

    return lambda_const*logloss(y_true, y_pred) + logloss(y_soft, y_pred_soft)

def get_model(num_labels):
    #Some layers for model
    model.add(Dense(num_labels))
    logits = model.layers[-1].output
    probabilities = Activation('softmax')(logits)
    # softed probabilities
    logits_T = Lambda(lambda x: x/temperature)(logits)
    probabilities_T = Activation('softmax')(logits_T)

    output = concatenate([probabilities, probabilities_T])
    model = Model(model.input, output)

    lambda_const = 0.07

    model.compile(
    optimizer=optimizers.SGD(lr=1e-1, momentum=0.9, nesterov=True), 
    loss=lambda y_true, y_pred: knowledge_distillation_loss(y_true, y_pred, lambda_const), 
    metrics=[accuracy])
    return model

Je suis cette référence.

Ceci est implémenté en utilisant fit generator() sur Keras avec le backend tf. Évidemment, j'aurai du mal au chargement du modèle puisque temperature est codé en dur. Aussi, Je souhaite mettre à jour le paramètre temperature par rapport au numéro d'époque dans la fonction de perte et le modèle.

Comment définir un tel signal de commande ?

1
havakok 4 févr. 2020 à 14:51

1 réponse

Meilleure réponse

J'en ai fait un exemple complet d'une façon de procéder.

Vous pouvez créer une classe pour la fonction de perte.

class TemperatureLossFunction:
    def __init__(self, temperature):
        self.temperature = temperature
    def loss_fun(self, y_truth, y_pred):
        return self.temperature*keras.losses.mse(y_truth, y_pred)
    def setTemperature(self, t, session=None):
        if session:
            session.run(self.temperature.assign( t )
        elif tensorflow.get_default_session():
            tensorflow.get_default_session().run(self.temperature.assign( t ))

class TemperatureLossCallback(keras.callbacks.Callback):
    def __init__(self, temp_lf):
        self.temp_lf = temp_lf
    def on_epoch_end(self, epoch, params):
        self.temp_lf.setTemperature(epoch)

J'ai créé deux méthodes pour travailler avec cela, la première méthode crée et enregistre le modèle.

def init(session):
    global temperature #global for serialization issues
    temperature = tensorflow.Variable(5.0)
    tlo = TemperatureLossFunction(temperature)

    inp = keras.layers.Input((4,4))

    l1 = keras.layers.Lambda( lambda x: temperature*x )
    op = l1(inp)

    m = keras.models.Model(inputs=[inp], outputs=[op])
    m.compile( optimizer = keras.optimizers.SGD(0.01), loss=tlo.loss_fun)

    #make sure the session is the one your using!
    session.run(temperature.initializer)

Le premier test que j'exécute s'assure que nous modifions la valeur.

    m.evaluate( numpy.ones((1, 4, 4)), numpy.zeros((1, 4, 4)) )    
    session.run(temperature.assign(1))
    m.evaluate( numpy.ones((1, 4, 4)), numpy.zeros((1, 4, 4)) )

Le deuxième test que j'exécute s'assure que nous pouvons modifier les valeurs avec un rappel.

    cb = TemperatureLossCallback(tlo)
    def gen():
        for i in range(10):
            yield numpy.ones((1, 4, 4)), numpy.zeros((1, 4, 4))
    m.fit_generator(
            gen(), steps_per_epoch=1, epochs=10, callbacks=[cb]
        )
    m.save("junk.h5")

Enfin, pour démontrer le rechargement du fichier.

def restart(session):
    global temperature
    temperature = tensorflow.Variable(5.0)
    tlo = TemperatureLossFunction(temperature)
    loss_fun = tlo.loss_fun
    m = keras.models.load_model(
            "junk.h5", 
            custom_objects = {"loss_fun":tlo.loss_fun}
    )
    session.run(temperature.initializer)
    m.evaluate( numpy.ones((1, 4, 4)), numpy.zeros((1, 4, 4)) )
    session.run(temperature.assign(1))
    m.evaluate( numpy.ones( (1, 4, 4) ), numpy.zeros( ( 1, 4, 4) ) )

Ceci est juste le code que j'utilise pour démarrer le programme pour l'exhaustivité

import sys    
if __name__=="__main__":
    sess = tensorflow.Session()
    with sess.as_default():
        if "restart" in sys.argv:
            restart(sess)
        else:
            init(sess)

Un inconvénient de cette méthode, si vous l'exécutez, vous verrez que la variable de température n'est pas chargée à partir du fichier modèle. Il prend la valeur assignée dans le code.

Du côté positif, la fonction de perte et la couche font référence au même Variable

Une façon que j'ai trouvée pour enregistrer la valeur de la variable consiste à créer un nouveau calque et à utiliser la variable comme poids pour le nouveau calque.

class VLayer(keras.layers.Layer):
    def __init__(self, *args, **kwargs):
        super().__init__(**kwargs)
    def build(self, input_shape):
        self.v1 = self.add_weight(
                       dtype="float32", 
                       shape = (), 
                       trainable=False, 
                       initializer="zeros"
                   )
    def call(self, x):
        return x*self.v1
    def setValue(self, val):
        self.set_weights( numpy.array([val]) )

Maintenant, lorsque vous chargez le modèle, le poids sera chargé. Malheureusement, je n'ai pas pu trouver un moyen de lier le poids à une variable en charge. Il y aura donc deux variables, une pour la fonction de perte et une pour la couche. Les deux peuvent cependant être définis à partir d'un rappel. Je pense donc que cette méthode est sur une voie plus robuste.

1
matt 5 févr. 2020 à 20:03