【问题标题】:Adding regularizer to an existing layer of a trained model without resetting weights?将正则化器添加到训练模型的现有层而不重置权重?
【发布时间】:2018-01-18 20:54:33
【问题描述】:

假设我正在通过 Inception 进行迁移学习。我添加了几层并训练了一段时间。

这是我的模型拓扑的样子:

base_model = InceptionV3(weights='imagenet', include_top=False)
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(1024, activation='relu', name = 'Dense_1')(x)
predictions = Dense(12, activation='softmax', name = 'Predictions')(x)
model = Model(input=base_model.input, output=predictions)

我对这个模型进行了一段时间的训练,将其保存并再次加载以进行重新训练;这次我想在不重置权重的情况下将 l2-regularizer 添加到Dense_1?这可能吗?

path = .\model.hdf5
from keras.models import load_model
model = load_model(path)

文档仅显示了在初始化层时可以添加正则化器作为参数:

from keras import regularizers
model.add(Dense(64, input_dim=64,
                kernel_regularizer=regularizers.l2(0.01),
                activity_regularizer=regularizers.l1(0.01)))

这实际上是在创建一个新层,因此我的层的权重将被重置。

编辑:

所以过去几天我一直在玩代码,当我加载模型时,我的损失发生了一些奇怪的事情(在使用新的正则化器进行了一些训练之后)。

所以我第一次运行这段代码(第一次使用新的正则化器):

from keras.models import load_model
base_model = load_model(path)
x = base_model.get_layer('dense_1').output
predictions = base_model.get_layer('dense_2')(x)
model = Model(inputs = base_model.input, output = predictions)
model.get_layer('dense_1').kernel_regularizer = regularizers.l2(0.02) 

model.compile(optimizer=SGD(lr= .0001, momentum=0.90),
              loss='categorical_crossentropy',
              metrics = ['accuracy'])

我的训练输出似乎正常:

Epoch 43/50
 - 2918s - loss: 0.3834 - acc: 0.8861 - val_loss: 0.4253 - val_acc: 0.8723
Epoch 44/50
Epoch 00044: saving model to E:\Keras Models\testing_3\2018-01-18_44.hdf5
 - 2692s - loss: 0.3781 - acc: 0.8869 - val_loss: 0.4217 - val_acc: 0.8729
Epoch 45/50
 - 2690s - loss: 0.3724 - acc: 0.8884 - val_loss: 0.4169 - val_acc: 0.8748
Epoch 46/50
Epoch 00046: saving model to E:\Keras Models\testing_3\2018-01-18_46.hdf5
 - 2684s - loss: 0.3688 - acc: 0.8896 - val_loss: 0.4137 - val_acc: 0.8748
Epoch 47/50
 - 2665s - loss: 0.3626 - acc: 0.8908 - val_loss: 0.4097 - val_acc: 0.8763
Epoch 48/50
Epoch 00048: saving model to E:\Keras Models\testing_3\2018-01-18_48.hdf5
 - 2681s - loss: 0.3586 - acc: 0.8924 - val_loss: 0.4069 - val_acc: 0.8767
Epoch 49/50
 - 2679s - loss: 0.3549 - acc: 0.8930 - val_loss: 0.4031 - val_acc: 0.8776
Epoch 50/50
Epoch 00050: saving model to E:\Keras Models\testing_3\2018-01-18_50.hdf5
 - 2680s - loss: 0.3493 - acc: 0.8950 - val_loss: 0.4004 - val_acc: 0.8787

但是,如果我在这个迷你训练课之后尝试加载模型(我将从 epoch 00050 加载模型,所以应该已经实现了新的正则化器值,我得到一个非常高的损失值)

代码:

path = r'E:\Keras Models\testing_3\2018-01-18_50.hdf5' #50th epoch model

from keras.models import load_model
model = load_model(path)
model.compile(optimizer=SGD(lr= .0001, momentum=0.90),
              loss='categorical_crossentropy',
              metrics = ['accuracy'])

返回:

Epoch 51/65
 - 3130s - loss: 14.0017 - acc: 0.8953 - val_loss: 13.9529 - val_acc: 0.8800
Epoch 52/65
Epoch 00052: saving model to E:\Keras Models\testing_3\2018-01-20_52.hdf5
 - 2813s - loss: 13.8017 - acc: 0.8969 - val_loss: 13.7553 - val_acc: 0.8812
Epoch 53/65
 - 2759s - loss: 13.6070 - acc: 0.8977 - val_loss: 13.5609 - val_acc: 0.8824
Epoch 54/65
Epoch 00054: saving model to E:\Keras Models\testing_3\2018-01-20_54.hdf5
 - 2748s - loss: 13.4115 - acc: 0.8992 - val_loss: 13.3697 - val_acc: 0.8824
Epoch 55/65
 - 2745s - loss: 13.2217 - acc: 0.9006 - val_loss: 13.1807 - val_acc: 0.8840
Epoch 56/65
Epoch 00056: saving model to E:\Keras Models\testing_3\2018-01-20_56.hdf5
 - 2752s - loss: 13.0335 - acc: 0.9014 - val_loss: 12.9951 - val_acc: 0.8840
Epoch 57/65
 - 2756s - loss: 12.8490 - acc: 0.9023 - val_loss: 12.8118 - val_acc: 0.8849
Epoch 58/65
Epoch 00058: saving model to E:\Keras Models\testing_3\2018-01-20_58.hdf5
 - 2749s - loss: 12.6671 - acc: 0.9032 - val_loss: 12.6308 - val_acc: 0.8849
Epoch 59/65
 - 2738s - loss: 12.4871 - acc: 0.9039 - val_loss: 12.4537 - val_acc: 0.8855
Epoch 60/65
Epoch 00060: saving model to E:\Keras Models\testing_3\2018-01-20_60.hdf5
 - 2765s - loss: 12.3086 - acc: 0.9059 - val_loss: 12.2778 - val_acc: 0.8868
Epoch 61/65
 - 2767s - loss: 12.1353 - acc: 0.9065 - val_loss: 12.1055 - val_acc: 0.8867
Epoch 62/65
Epoch 00062: saving model to E:\Keras Models\testing_3\2018-01-20_62.hdf5
 - 2757s - loss: 11.9637 - acc: 0.9061 - val_loss: 11.9351 - val_acc: 0.8883

注意loss 的值非常高。这是正常的吗?我知道 l2 正则化器会增加损失(如果权重很大),但这不会反映在第一个小型培训课程中(我第一次实施正则化器的地方?)。不过,准确性似乎保持一致。

谢谢。

【问题讨论】:

    标签: python tensorflow keras


    【解决方案1】:

    你需要做两件事:

    1. 通过以下方式添加正则化器:

      model.get_layer('Dense_1').kernel_regularizer = l2(0.01) 
      
    2. 重新编译模型:

      model.compile(...)
      

    【讨论】:

    • 重新编译不会重置权重吗?
    • 实际上没有——我测试过它并没有重置权重值。它只初始化新图。
    • 这绝对有道理。你的损失现在有一个正则化项,这使它具有更大的价值。如果您想跟踪以前的损失,请添加 categorical_crossentropy 作为指标。
    • 显然这个技巧实际上不起作用。尝试在每个受影响的层编译后打印 layer.losses - 你不会在那里看到任何正则化器。但是 reg_loss 将显示在对象创建期间设置的层(即 Dens(N, kernel_regularizer = l2(0.1))
    • 正如@apatsekin 所说,这是行不通的。编译模型后,打印 layer.losses 不显示任何正则化器。
    【解决方案2】:

    对于 tensorflow 2.X,您只需要这样做:

    l2 = tf.keras.regularizers.l2(1e-4)
    for layer in model.layers:
        # if hasattr(layer, 'kernel'):
        # or
        # If you want to apply just on Conv
        if isinstance(layer, tf.keras.layers.Conv2D):
            model.add_loss(lambda layer=layer: l2(layer.kernel))
    

    希望对你有帮助

    【讨论】:

    • 似乎对我有用。同样对于 Mobilenet 中的 DepthwiseConv2D 层,应该使用 depthwise_kernel。但是,我遇到的问题是在保存tensorflow.python.saved_model.nested_structure_coder.NotEncodableError: No encoder for object [<tensorflow.python.keras.layers.convolutional.Conv2D object at 0x7fd2f00469d0>] of type [<class 'tensorflow.python.keras.layers.convolutional.Conv2D'>].
    【解决方案3】:

    Marcin 的解决方案对我不起作用。正如apatsekin 所提到的,如果您在按照Marcin 建议添加正则化器后打印layer.losses,您将得到一个空列表。

    我找到了一个我根本不喜欢的解决方法,但我在这里发布,以便更有能力的人可以找到一种更简单的方法来做到这一点。

    我相信它适用于大多数keras.application 网络。我将特定架构的.py文件从Github中的keras-application(例如InceptionResNetV2)复制到我机器中的本地文件regularizedNetwork.py。我不得不对其进行编辑以修复一些相关的导入,例如:

    #old version
    from . import imagenet_utils
    from .imagenet_utils import decode_predictions
    from .imagenet_utils import _obtain_input_shape
    
    backend = None
    layers = None
    models = None
    keras_utils = None
    

    到:

    #new version
    from keras import backend
    from keras import layers
    from keras import models
    from keras import utils as keras_utils
    
    from keras.applications import imagenet_utils
    from keras.applications.imagenet_utils import decode_predictions
    from keras.applications.imagenet_utils import _obtain_input_shape
    

    在解决了相对路径和导入问题后,我在每个所需的层中添加了正则化器,就像您在定义新的未经训练的网络时所做的那样。通常,在定义架构后,来自keras.application 的模型会加载预训练的权重。

    现在,在您的主代码/笔记本中,只需导入新的 regularizedNetwork.py 并调用 main 方法来实例化网络。

    #main code
    from regularizedNetwork import InceptionResNetV2
    

    正则化器应该全部设置好,您可以正常微调正则化模型。

    我确信有一种不那么花哨的方法,所以,如果有人找到它,请写一个新的答案和/或在这个答案中发表评论。

    为了记录,我还尝试从keras.application 实例化模型,使用regModel = model.get_config() 获取其架构,将正则化器添加为建议的Marcin,然后使用regModel.set_weights(model.get_weights()) 加载权重,但它仍然没有不行。

    编辑:拼写错误。

    【讨论】:

      【解决方案4】:

      试试这个:

      # a utility function to add weight decay after the model is defined.
      def add_weight_decay(model, weight_decay):
          if (weight_decay is None) or (weight_decay == 0.0):
              return
      
          # recursion inside the model
          def add_decay_loss(m, factor):
              if isinstance(m, tf.keras.Model):
                  for layer in m.layers:
                      add_decay_loss(layer, factor)
              else:
                  for param in m.trainable_weights:
                      with tf.keras.backend.name_scope('weight_regularizer'):
                          regularizer = lambda: tf.keras.regularizers.l2(factor)(param)
                          m.add_loss(regularizer)
      
          # weight decay and l2 regularization differs by a factor of 2
          add_decay_loss(model, weight_decay/2.0)
          return
      

      【讨论】:

        【解决方案5】:

        这有点hacky,但它应该可以工作。这适用于 Tensorflow 2.0 中的预训练模型。请注意,所有层都应在model.layers 中,即将跳过嵌套加权层。从这里选择解决方案https://sthalles.github.io/keras-regularizer/

        import os
        import tempfile
        
        def add_regularization(model, regularizer=tf.keras.regularizers.l2(0.0001)):
        
            if not isinstance(regularizer, tf.keras.regularizers.Regularizer):
              print("Regularizer must be a subclass of tf.keras.regularizers.Regularizer")
              return model
        
            for layer in model.layers:
                for attr in ['kernel_regularizer']:
                    if hasattr(layer, attr):
                      setattr(layer, attr, regularizer)
        
            # When we change the layers attributes, the change only happens in the model config file
            model_json = model.to_json()
        
            # Save the weights before reloading the model.
            tmp_weights_path = os.path.join(tempfile.gettempdir(), 'tmp_weights.h5')
            model.save_weights(tmp_weights_path)
        
            # load the model from the config
            model = tf.keras.models.model_from_json(model_json)
        
            # Reload the model weights
            model.load_weights(tmp_weights_path, by_name=True)
            return model
        

        【讨论】:

          【解决方案6】:

          Horovod 示例中的解决方法。这个想法是序列化模型,添加L2,然后将其恢复。

          model_config = model.get_config()
          for layer, layer_config in zip(model.layers, model_config['layers']):
              if hasattr(layer, 'kernel_regularizer'):
                  regularizer = keras.regularizers.l2(args.wd)
                  layer_config['config']['kernel_regularizer'] = \
                      {'class_name': regularizer.__class__.__name__,
                       'config': regularizer.get_config()}
              if type(layer) == keras.layers.BatchNormalization:
                  layer_config['config']['momentum'] = 0.9
                  layer_config['config']['epsilon'] = 1e-5
          
          model = keras.models.Model.from_config(model_config)
          

          【讨论】:

            猜你喜欢
            • 2018-10-08
            • 2017-09-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-03-12
            相关资源
            最近更新 更多