自定义激活Keras层的子集?

时间:2018-12-07 23:45:42

标签: python tensorflow keras

我想将线性激活应用于我的Keras模型的大部分输出层,并将sigmoid激活应用于与张量中的其他数据交织的一组“列”。

根据this post on writing custom activationsthis post on sliced assignment中的this other post about sliced assignment和@jdehesa的答案,我写了以下内容:

from keras.layers import Activation
from keras import backend as K
from keras.utils.generic_utils import get_custom_objects
import tensorflow as tf

def selective_activation(x, start=0, end=None, skip_every=6):
    with tf.control_dependencies(x[:,start:end:skip_every].assign(K.sigmoid(x[:,start:end:skip_every]))):
        x = tf.identity(x)
    return x

model = Sequential() 
model.add(...bunch of layers...)
model.add(Dense(),name="Final Layer")
get_custom_objects().update({'selective_activation': Activation(selective_activation)})
model.add(Activation(selective_activation))
...

运行此命令时,在带有ValueError: Sliced assignment is only supported for variables上下文的行上收到错误“ tf.control_dependencies”。我很困惑:Keras层的输出不是变量吗?

有人可以建议一种方法来实现我要完成的任务吗?

我只想出三种解决方案:

  1. 我当前实现的解决方法是使用功能性API创建两个不同的输出层,分别赋予自己的激活,然后将它们连接在一起,然后乘以“置换矩阵”(一堆0和1)以重新排序列,以便它们最终到达代码的其余部分所期望的变量(即与其他线性激活变量交错)。但这似乎是一个过于复杂,冗长的hack。 (无需提交实现此问题的答案;我已经知道了,但是我不喜欢它。)
  2. tf.scatter_nd()tf.scatter_update()做点什么...?

  3. 我可以想到的另一种选择,即重写代码其余部分中的所有其他内容,以将“存在”变量保持在一起,而不是与其他变量交织在一起……这将需要大量工作我不急于着手。

(顺便说一下,这是用于对象检测器的,以前它对所有变量都使用MSE损失,现在我想对“是否存在对象”类别产生交叉熵损失。)

1 个答案:

答案 0 :(得分:0)

编辑documentation for tf.where()包含

  

“如果条件为等级1,则x可能具有更高的等级,但其第一个   尺寸必须与条件的大小匹配”

...因此,如果我们仅对x进行转置,则可以使用一维索引数组,而不必担心我们是否知道批处理大小,如下所示:

class SelectiveSigmoid(Layer):
    def __init__(self, **kwargs):
        self.start = kwargs.get('start', 0)
        self.end = kwargs.get('end', None)
        self.skip = kwargs.get('skip', 6)
        super(SelectiveSigmoid, self).__init__(**kwargs)

    def build(self, input_shape):
        self.indices = np.zeros(input_shape[-1]) # Note that tf.cast allows a numpy array!
        self.indices[self.start:self.end:self.skip] = 1

    def call(self, x):
        print("In call.... ")
        print("x.get_shape().as_list()[0] = ",tf.shape(x)[0])
        return tf.transpose(tf.where(tf.cast(self.indices, dtype=tf.bool), K.sigmoid(tf.transpose(x)), tf.transpose(x)))

    def compute_output_shape(self, input_shape):
        return input_shape

...然后这将毫无错误地运行,我们不需要需要预先知道批次的大小。

这似乎可以完成任务,但是我很乐意为别人可能提出的更好的方案投票!