Изменить пользовательские параметры потерь и NN параметры относительно эпохи - PullRequest
1 голос
/ 04 февраля 2020

У меня есть модель Keras, определенная следующим образом (Пытался сохранить только необходимые детали):

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

Я следую по этой ссылке .

Это реализовано с использованием fit generator() на Keras с tf backend. Очевидно, у меня будут проблемы при загрузке модели, так как temperature зашифрован. Кроме того, я буду sh обновлять параметр temperature относительно числа эпох как в функции потерь, так и в модели.

Как определить такой сигнал управления?

1 Ответ

1 голос
/ 05 февраля 2020

Я превратил это в полный пример одного из способов сделать это.

Вы можете создать класс для функции потерь.

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)

Я создал два метода для работы с этим первый метод создает и сохраняет модель.

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)

Первый тест, который я запускаю, проверяет, что мы меняем значение.

    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)) )

Второй тест, который я запускаю гарантирует, что мы можем изменить значения с помощью обратного вызова.

    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")

Наконец, чтобы продемонстрировать перезагрузку файла.

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) ) )

Это просто код, который я использую для запуска программы для полноты

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

Один недостаток этого метода: если вы запустите его, вы увидите, что переменная температуры не загружается из файла модели. Он принимает значение, назначенное в коде.

С положительной стороны, и функция потерь, и слой ссылаются на один и тот же Variable

Один способ, который я нашел для сохранения значения переменной это создать новый слой и использовать переменную в качестве веса для нового слоя.

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]) )

Теперь, когда вы загружаете модель, вес будет загружен. К сожалению, я не смог найти способ связать вес с переменной под нагрузкой. Таким образом, будет две переменные, одна для функции потерь и одна для слоя. Оба из них могут быть установлены из обратного вызова, хотя. Поэтому я чувствую, что этот метод находится на более надежном пути.

...