从Keras的dropout层中提取dropout掩码?

3
我想从Sequential Keras模型中的dropout层中提取和存储每个批次的dropout掩码[1/0数组]。我想知道是否有一种简单的方法在Keras中完成这件事,或者我需要切换到tensorflow (如何在Tensorflow中获取dropout掩码)。
希望得到任何帮助!我对TensorFlow和Keras都很新。
dropout_layer.get_output_mask()和dropout_layer.get_input_mask()是用于dropout层的一些函数,我尝试使用它们,但在调用之前的层后得到了None。
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(...)
2个回答

0

我发现了一种非常巧妙的方法,通过简单地扩展提供的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

0

在Keras中它不容易被暴露出来。它会深入到调用Tensorflow的dropout。

因此,尽管您正在使用Keras,它也将是图中的张量,可以通过名称获取(找到其名称:在Tensorflow中获取图中所有张量的名称)。

当然,这种选择将缺少一些Keras信息,您可能需要在Lambda层内部执行此操作,以便Keras向张量添加某些信息。并且您必须格外小心,因为即使在不训练时(跳过掩码),张量也将存在。

现在,您还可以使用一种不太繁琐的方法,可能会消耗一些处理能力:

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%准确的(即如果输入和输出都为零,则您无法确定相应的神经元是否已被删除)。 - today
有趣... :) - 感谢您的提示。我可能会在一段时间内更新答案,但是在没有系统强制的情况下,某些东西完全变成零的概率非常低。 - Daniel Möller
非常感谢!问题:您需要采取任何特殊措施才能从Lambda(getMask)(outputs)中获取mask吗?我假设Lambda层将出现在模型摘要中(但实际上没有),我可以使用model.layer.output[0]来获取它吗?我需要进行回调以提取掩码变量吗?还是我错过了一些明显的东西?(我正在使用tensorflow 2.0)再次感谢您的帮助! - holighost
既然这是一个模型输出,你需要为它准备数据(目标)。y_train 必须是一个列表 [y_train_main_output, y_train_mask]。因此,你可以创建一个仅用于预测的带有掩码的单独模型,或者为该输出传递虚拟目标,并定义虚拟损失。 - Daniel Möller
有了这个解释,我建议的方法是使用即时执行来创建自己的训练循环。(Keras没有以任何简单的方式公开训练结果) - Daniel Möller
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接