Keras: Как создать пользовательский слой с весами, когда форма ввода неизвестна во время компиляции? - PullRequest
0 голосов
/ 08 апреля 2019

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

Лямбда-слои не работают в моем случае, потому что я хочу сохранить модель, цель в том, что при применении к данным нет необходимости обрабатывать входные данные, поскольку это будет сделано на ранней стадии сети.

Использование K.variables для средних и переменных работает, но я бы хотел вместо этого использовать веса и установить trainable = False.Таким образом, они будут сохранены в весах сети, и мне не нужно каждый раз предоставлять их.

class PreprocessLayer(Layer):                                                                                                                                                                               
    """                                                                                                                                                                                                     
    Defines a layer that applies the preprocessing from a scaler                                                                                                                                            
    Needed because lambda layers are too fragile to be saved in a model                                                                                                                                     
    """                                                                                                                                                                                                     
    def __init__(self, batch_size, mean, var, **kwargs):                                                                                                                                                    
        self.b = batch_size                                                                                                                                                                                 
        self.m = mean                                                                                                                                                                                       
        self.v = var                                                                                                                                                                                        
        super(PreprocessLayer, self).__init__(**kwargs)                                                                                                                                                     

    def build(self, input_shape):                                                                                                                                                                           
        self.mean = self.add_weight(name='mean',                                                                                                                                                            
                                  shape=(self.b,input_shape[1]),                                                                                                                                            
                                  initializer=tf.constant_initializer(self.m),                                                                                                                              
                                  trainable=False)                                                                                                                                                          
        self.var = self.add_weight(name='var',                                                                                                                                                              
                                  shape=(self.b,input_shape[1]),                                                                                                                                            
                                  initializer=tf.constant_initializer(self.v),                                                                                                                              
                                  trainable=False)                                                                                                                                                          
        super(PreprocessLayer, self).build(input_shape)  # Be sure to call this at the end                                                                                                                  

    def call(self, x):                                                                                                                                                                                      
        return (x-self.mean)/self.var                                                                                                                                                                       

    def compute_output_shape(self, input_shape):                                                                                                                                                            
        return (input_shape[0],input_shape[1])                                                                                                                                                              
    def get_config(self):                                                                                                                                                                                   
        config = super(PreprocessLayer, self).get_config()                                                                                                                                                  
        config['mean'] = self.m                                                                                                                                                                             
        config['var'] = self.v                                                                                                                                                                              
        return config                                                                                                                                                                                       

И я называю этот слой с

L0 = PreprocessLayer(batch_size=20,mean=scaler.mean_,var=scaler.scale_)(IN)

Проблемавозникает в

shape=(self.b,input_shape[1]),

, что дает мне ошибку (когда batch_size равен 20)

tensorflow.python.framework.errors_impl.InvalidArgumentError: Incompatible shapes: [32,15] vs. [20,15]
     [[Node: preprocess_layer_1/sub = Sub[T=DT_FLOAT, _device="/job:localhost/replica:0/task:0/device:CPU:0"](_arg_IN_0_0, preprocess_layer_1/mean/read)]]

Из того, что я понимаю, так как мои веса (среднее и переменное) должны иметь одинаковую формув качестве входных данных x первая ось создает проблемы, когда batch_size не является делителем размера обучения, поскольку во время обучения он будет иметь разные значения.Это приводит к сбою, потому что форма должна быть определена во время компиляции, и я не могу оставить ее пустой.

Есть ли способ получить динамическое значение для первого значения формы?Если нет, обойти эту проблему?

Ответы [ 2 ]

0 голосов
/ 14 апреля 2019

Для тех, у кого есть та же проблема - которая отличается от batch_size в конце эпохи (из-за того, что размер обучения и тестирования не кратен размеру партии), что приводит к InvalidArgumentError: Incompatible shapes - здесьЭто мое исправление.

Так как этот остаток всегда будет иметь размер, меньший, чем batch_size, то, что я сделал в функции вызова, это нарезал веса следующим образом:

def call(self, x):                                                                                                                                            
        mean = self.mean[:K.shape(x)[0],:]                                                                                                                       
        std = self.std[:K.shape(x)[0],:]                                                                                                                        
        return (x-mean)/std

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

Вот почему я добавил в __init__: self.b = max(32,batch_size).

Поскольку предикат () по умолчанию использует batch_size = 32

0 голосов
/ 09 апреля 2019

Не думаю, что вам нужно добавлять mean и var в качестве весов. Вы можете рассчитать их в вашей функции call. Я также не совсем понимаю, почему вы хотите использовать это вместо BatchNormalization, но в любом случае, может быть, вы можете попробовать этот код

class PreprocessLayer(Layer):
    def __init__(self, eps=1e-6, **kwargs):
        self.eps = eps
        super(PreprocessLayer, self).__init__(**kwargs)
    def build(self, input_shape):
        super(PreprocessLayer, self).build(input_shape)
    def call(self, x):
        mean = K.mean(x, axis=-1, keepdims=True)
        std = K.std(x, axis=-1, keepdims=True)
        return (x - mean) / (std + self.eps)
    def compute_output_shape(self, input_shape):
        return input_shape

eps, чтобы избежать деления на 0.

Я не гарантирую, что это сработает, но, возможно, попробуйте.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...