Keras在激活功能之前检索节点的值

时间:2017-08-03 18:50:43

标签: python neural-network keras keras-layer sigmoid

想象一个完全连接的神经网络,其最后两层具有以下结构:

.table-row-cell .table-cell {
    -fx-font-weight: normal;
}

.table-row-cell:warning .table-cell {
    -fx-font-weight: bold;
}

网络的输出值是1,但是我想知道sigmoidal函数的输入x是什么(必须是一些高数字,因为sigm(x)在这里是1)。

以下indraforyou's回答我设法检索了Keras图层的输出和权重:

[Dense]
    units = 612
    activation = softplus

[Dense]
    units = 1
    activation = sigmoid

x如何成为负数?在这种情况下,最后一层输出应该是一个接近0.0而不是1.0的数字。 outputs = [layer.output for layer in model.layers[-2:]] functors = [K.function( [model.input]+[K.learning_phase()], [out] ) for out in outputs] test_input = np.array(...) layer_outs = [func([test_input, 0.]) for func in functors] print layer_outs[-1][0] # -> array([[ 1.]]) dense_0_out = layer_outs[-2][0] # shape (612, 1) dense_1_weights = model.layers[-1].weights[0].get_value() # shape (1, 612) dense_1_bias = model.layers[-1].weights[1].get_value() x = np.dot(dense_0_out, dense_1_weights) + dense_1_bias print x # -> -11.7 dense_0_out输出或权重是否错误?

5 个答案:

答案 0 :(得分:7)

由于您正在使用get_value(),因此我假设您正在使用Theano后端。要在sigmoid激活之前获取节点的值,您可以traverse the computation graph

  

可以使用owner字段从输出(某些计算的结果)开始遍历到其输入。

在您的情况下,您想要的是sigmoid激活操作的输入x。 sigmoid op的输出为model.output。将这些变量放在一起,变量xmodel.output.owner.inputs[0]

如果您打印出此值,则会看到Elemwise{add,no_inplace}.0,这是一个按元素添加的操作。可以通过Dense.call()的{​​{3}}验证:

def call(self, inputs):
    output = K.dot(inputs, self.kernel)
    if self.use_bias:
        output = K.bias_add(output, self.bias)
    if self.activation is not None:
        output = self.activation(output)
    return output

激活功能的输入是K.bias_add()的输出。

通过对代码进行少量修改,您可以在激活之前获取节点的值:

x = model.output.owner.inputs[0]
func = K.function([model.input] + [K.learning_phase()], [x])
print func([test_input, 0.])

对于使用TensorFlow后端的任何人:请改用x = model.output.op.inputs[0]

答案 1 :(得分:4)

我可以看到一个简单的方法只是改变一点模型结构。 (最后请参见如何使用现有模型并仅更改结尾)。

这种方法的优点是:

  • 您不必猜测您是否正在进行正确的计算
  • 您无需关心辍学图层以及如何实施辍学计算
  • 这是一个纯粹的Keras解决方案(适用于任何后端,Theano或Tensorflow)。

下面有两种可能的解决方案:

  • 选项1 - 从建议的结构开始创建新模型
  • 选项2 - 重用仅更改其结尾的现有模型

模型结构

你最后可以将最后一个密集分成两层:

[Dense]
    units = 612
    activation = softplus

[Dense]
    units = 1
    #no activation

[Activation]
    activation = sigmoid

然后你只需得到最后一个密集层的输出。

我会说你应该创建两个模型,一个用于训练,另一个用于检查这个值。

选项1 - 从头开始​​构建模型:

from keras.models import Model

#build the initial part of the model the same way you would
#add the Dense layer without an activation:

#if using the functional Model API
    denseOut = Dense(1)(outputFromThePreviousLayer)    
    sigmoidOut = Activation('sigmoid')(denseOut)    

#if using the sequential model - will need the functional API
    model.add(Dense(1))
    sigmoidOut = Activation('sigmoid')(model.output)

从中创建两个模型,一个用于训练,一个用于检查密集输出:

#if using the functional API
    checkingModel = Model(yourInputs, denseOut)

#if using the sequential model:
    checkingModel = model   

trainingModel = Model(checkingModel.inputs, sigmoidOut)   

正常使用trianingModel进行培训。这两个模型共享权重,因此训练一个是训练另一个。

使用checkingModel仅使用checkingModel.predict(X)

查看Dense图层的输出

选项2 - 从现有模型中构建:

from keras.models import Model

#find the softplus dense layer and get its output:
softplusOut = oldModel.layers[indexForSoftplusLayer].output
    #or should this be the output from the dropout? Whichever comes immediately after the last Dense(1)

#recreate the dense layer
outDense = Dense(1, name='newDense', ...)(softPlusOut)

#create the new model
checkingModel = Model(oldModel.inputs,outDense)

重要的是,因为您创建了一个新的Dense图层,以便从旧图层中获取权重:

wgts = oldModel.layers[indexForDense].get_weights()
checkingModel.get_layer('newDense').set_weights(wgts)

在这种情况下,训练旧模型不会更新新模型中的最后一个密集层,因此,让我们创建一个trainingModel:

outSigmoid = Activation('sigmoid')(checkingModel.output)
trainingModel = Model(checkingModel.inputs,outSigmoid)

使用checkingModel通过checkingModel.predict(X)检查所需的值。并训练trainingModel

答案 2 :(得分:2)

这对其他Google员工来说,自发布可接受的答案以来,keras API的工作已发生了显着变化。在激活之前(针对tensorflow后端)提取层输出的工作代码为:

model = Your_Keras_Model()
the_tensor_you_need = model.output.op.inputs[0] #<- this is indexable, if there are multiple inputs to this node then you can find it with indexing.

在我的情况下,最后一层是激活为softmax的致密层,因此我需要的张量输出为<tf.Tensor 'predictions/BiasAdd:0' shape=(?, 1000) dtype=float32>

答案 3 :(得分:0)

(TF后端) 转换层的解决方案。

我有同样的问题,并且重写模型的配置不是一种选择。 简单的技巧是手动执行调用功能。它可以控制激活。

从Keras source复制粘贴,其中self更改为layer。您可以对任何其他图层执行相同操作。

def conv_no_activation(layer, inputs, activation=False):

    if layer.rank == 1:
        outputs = K.conv1d(
            inputs,
            layer.kernel,
            strides=layer.strides[0],
            padding=layer.padding,
            data_format=layer.data_format,
            dilation_rate=layer.dilation_rate[0])
    if layer.rank == 2:
        outputs = K.conv2d(
            inputs,
            layer.kernel,
            strides=layer.strides,
            padding=layer.padding,
            data_format=layer.data_format,
            dilation_rate=layer.dilation_rate)
    if layer.rank == 3:
        outputs = K.conv3d(
            inputs,
            layer.kernel,
            strides=layer.strides,
            padding=layer.padding,
            data_format=layer.data_format,
            dilation_rate=layer.dilation_rate)

    if layer.use_bias:
        outputs = K.bias_add(
            outputs,
            layer.bias,
            data_format=layer.data_format)

    if activation and layer.activation is not None:
        outputs = layer.activation(outputs)

    return outputs

现在我们需要对main函数进行一些修改。首先,通过名称来标识图层。然后从上一层检索激活。最后,计算目标层的输出。

def get_output_activation_control(model, images, layername, activation=False):
    """Get activations for the input from specified layer"""

    inp = model.input

    layer_id, layer = [(n, l) for n, l in enumerate(model.layers) if l.name == layername][0]
    prev_layer = model.layers[layer_id - 1]
    conv_out = conv_no_activation(layer, prev_layer.output, activation=activation)
    functor = K.function([inp] + [K.learning_phase()], [conv_out]) 

    return functor([images]) 

这是一个小小的测试。我正在使用VGG16模型。

a_relu = get_output_activation_control(vgg_model, img, 'block4_conv1', activation=True)[0]
a_no_relu = get_output_activation_control(vgg_model, img, 'block4_conv1', activation=False)[0]

print(np.sum(a_no_relu < 0))
> 245293

将所有负数设置为零,以与嵌入VGG16 ReLu操作后检索到的结果进行比较。

a_no_relu[a_no_relu < 0] = 0
print(np.allclose(a_relu, a_no_relu))
> True

答案 4 :(得分:0)

使用新的激活功能定义新层的简便方法:

def change_layer_activation(layer):

    if isinstance(layer, keras.layers.Conv2D):

        config = layer.get_config()
        config["activation"] = "linear"
        new = keras.layers.Conv2D.from_config(config)

    elif isinstance(layer, keras.layers.Dense):

        config = layer.get_config()
        config["activation"] = "linear"
        new = keras.layers.Dense.from_config(config)

    weights = [x.numpy() for x in layer.weights]

    return new, weights