【问题标题】:Extracting the dropout mask from a keras dropout layer?从keras dropout层中提取dropout mask?
【发布时间】:2019-09-20 19:01:32
【问题描述】:

我想在训练时从 Sequential Keras 模型中的每一个批次的 dropout 层中提取并存储 dropout 掩码 [1/0 数组]。我想知道在 Keras 中是否有一种直接的方法可以做到这一点,或者我是否需要切换到 tensorflow (How to get the dropout mask in Tensorflow)。

不胜感激!我对 TensorFlow 和 Keras 很陌生。

有几个函数(dropout_layer.get_output_mask()、dropout_layer.get_input_mask())用于我尝试使用但在调用上一层后得到None 的dropout 层。

model = tf.keras.Sequential()
model.add(tf.keras.layers.Flatten(name="flat", input_shape=(28, 28, 1)))
model.add(tf.keras.layers.Dense(
    512,
    activation='relu',
    name = 'dense_1',
    kernel_initializer=tf.keras.initializers.GlorotUniform(seed=123),
    bias_initializer='zeros'))
dropout = tf.keras.layers.Dropout(0.2, name = 'dropout') #want this layer's mask

model.add(dropout)
x = dropout.output_mask
y = dropout.input_mask
model.add(tf.keras.layers.Dense(
    10,
    activation='softmax',
    name='dense_2',
    kernel_initializer=tf.keras.initializers.GlorotUniform(seed=123),
    bias_initializer='zeros'))

model.compile(...)
model.fit(...)

【问题讨论】:

    标签: python tensorflow keras


    【解决方案1】:

    在 Keras 中不容易暴露。它深入到它调用 TensorFlow 辍学。

    因此,尽管您使用的是 Keras,但它也将是图中的一个张量,可以通过名称获取(找到它的名称:In Tensorflow, get the names of all the Tensors in a graph)。

    这个选项当然会缺少一些 keras 信息,您可能必须在 Lambda 层中这样做,以便 Keras 将某些信息添加到张量。而且您必须格外小心,因为即使不训练(跳过掩码)张量也会存在

    现在,您还可以使用不那么 hacky 的方式,这可能会消耗一些处理:

    def getMask(x):
        boolMask = tf.not_equal(x, 0)
        floatMask = tf.cast(boolMask, tf.float32) #or tf.float64
        return floatMask
    

    使用Lambda(getMasc)(output_of_dropout_layer)

    但您需要一个函数式 API Model,而不是使用 Sequential 模型。

    inputs = tf.keras.layers.Input((28, 28, 1))
    outputs = tf.keras.layers.Flatten(name="flat")(inputs)
    outputs = tf.keras.layers.Dense(
        512,
        #    activation='relu', #relu will be a problem here
        name = 'dense_1',
        kernel_initializer=tf.keras.initializers.GlorotUniform(seed=123),
        bias_initializer='zeros')(outputs)
    
    outputs = tf.keras.layers.Dropout(0.2, name = 'dropout')(outputs)
    mask = Lambda(getMask)(outputs)
    #there isn't "input_mask"
    
    
    #add the missing relu: 
    outputs = tf.keras.layers.Activation('relu')(outputs)
    outputs = tf.keras.layers.Dense(
        10,
        activation='softmax',
        name='dense_2',
        kernel_initializer=tf.keras.initializers.GlorotUniform(seed=123),
        bias_initializer='zeros')(outputs)
    
    model = Model(inputs, outputs)
    model.compile(...)
    model.fit(...)
    

    训练和预测

    既然你不能训练面具(它没有任何意义),它不应该是训练模型的输出。

    现在,我们可以试试这个:

    trainingModel = Model(inputs, outputs)    
    predictingModel = Model(inputs, [output, mask])    
    

    但预测中不存在掩码,因为 dropout 仅应用于训练。所以这最终不会给我们带来任何好处。

    训练的唯一方法是使用虚拟损失和虚拟目标:

    def dummyLoss(y_true, y_pred):
        return y_true #but this might evoke a "None" gradient problem since it's not trainable, there is no connection to any weights, etc.    
    
    model.compile(loss=[loss_for_main_output, dummyLoss], ....)
    
    model.fit(x_train, [y_train, np.zeros((len(y_Train),) + mask_shape), ...)
    

    不保证这些会起作用。

    【讨论】:

    • 一种或多或少准确的方法是将 dropout 层的输入传递给Lambda 层,并以其非零元素为条件。否则,如果 dropout 的输出为零,则不一定是该神经元已被丢弃(即它本身可能为零)。即使这也不是 %100 准确的(即如果输入和输出都为零,那么您不知道相应的神经元是否已被丢弃)。
    • 有趣... :) - 谢谢你的提示。我可能会在一段时间内更新答案,但是在没有系统强制的情况下,某事完全为零的可能性非常低。
    • 非常感谢!问题:您是否需要做任何特别的事情才能从 `Lambda(getMask)(outputs) 中获取 mask?我假设 Lambda 层会出现在模型摘要中(它没有出现),我可以使用 model.layer.output[0] 来获取它吗?我需要进行回调以提取掩码变量吗?还是我错过了一些明显的东西? (我使用的是 tensorflow 2.0)再次感谢您的帮助!
    • 嗯,你没有使用mask 做任何事情,所以它不在你的模型输出的路径中。这就是它不出现的原因。您可以将其输入到另一层并像与任何其他层输出一样正常使用它。如果您希望它成为模型的输出之一,只需将其设为模型的输出:model = Model(inputs, [outputs, mask])
    • 我尝试将其作为模型的输出之一。但是,我不能再调用model.fit()(检查模型目标时出错:您传递给模型的 Numpy 数组列表不是模型预期的大小。)我根本没有更改输入,所以我不明白为什么会这样,尤其是lambda 层与模型的其余部分共享输入。我使用tf.data.Dataset object 作为输入。说实话,我想做的只是在训练期间将 dropout 掩码保存为每批的一个 numpy 数组(对于菜鸟来说很难)。非常感谢您的帮助!
    【解决方案2】:

    我发现了一种非常老套的方法,方法是简单地扩展提供的 dropout 层。 (几乎所有代码都来自TF。)

    class MyDR(tf.keras.layers.Layer):
    def __init__(self,rate,**kwargs):
        super(MyDR, self).__init__(**kwargs)
    
        self.noise_shape = None
        self.rate = rate
    
    
    def _get_noise_shape(self,x, noise_shape=None):
        # If noise_shape is none return immediately.
        if noise_shape is None:
            return array_ops.shape(x)
        try:
            # Best effort to figure out the intended shape.
            # If not possible, let the op to handle it.
            # In eager mode exception will show up.
            noise_shape_ = tensor_shape.as_shape(noise_shape)
        except (TypeError, ValueError):
            return noise_shape
    
        if x.shape.dims is not None and len(x.shape.dims) == len(noise_shape_.dims):
            new_dims = []
            for i, dim in enumerate(x.shape.dims):
                if noise_shape_.dims[i].value is None and dim.value is not None:
                    new_dims.append(dim.value)
                else:
                    new_dims.append(noise_shape_.dims[i].value)
            return tensor_shape.TensorShape(new_dims)
    
        return noise_shape
    
    def build(self, input_shape):
        self.noise_shape = input_shape
        print(self.noise_shape)
        super(MyDR,self).build(input_shape)
    
    @tf.function
    def call(self,input):
        self.noise_shape = self._get_noise_shape(input)
        random_tensor = tf.random.uniform(self.noise_shape, seed=1235, dtype=input.dtype)
        keep_prob = 1 - self.rate
        scale = 1 / keep_prob
        # NOTE: if (1.0 + rate) - 1 is equal to rate, then we want to consider that
        # float to be selected, hence we use a >= comparison.
        self.keep_mask = random_tensor >= self.rate
        #NOTE: here is where I save the binary masks. 
        #the file grows quite big!
        tf.print(self.keep_mask,output_stream="file://temp/droput_mask.txt")
    
        ret = input * scale * math_ops.cast(self.keep_mask, input.dtype)
        return ret
    

    【讨论】:

      猜你喜欢
      • 2021-03-27
      • 2019-05-22
      • 1970-01-01
      • 2019-08-09
      • 2019-11-04
      • 2019-06-16
      • 2019-08-09
      • 2020-08-05
      • 2018-02-25
      相关资源
      最近更新 更多