25

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

[Dense]
    units = 612
    activation = softplus

[Dense]
    units = 1
    activation = sigmoid

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

按照 indraforyou回答,我设法检索了 Keras 层的输出和权重:

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

x怎么可能是负数?在这种情况下,最后一层输出应该是一个比 1.0 更接近 0.0 的数字。是dense_0_outdense_1_weights错误的输出或权重?

4

6 回答 6

11

由于您使用的是get_value(),我假设您使用的是 Theano 后端。要获取 sigmoid 激活前节点的值,可以遍历计算图

可以使用所有者字段从输出(某些计算的结果)到其输入遍历该图。

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

如果你打印出这个值,你会看到Elemwise{add,no_inplace}.0,这是一个元素相加操作。可以从以下源码验证Dense.call()

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]改为使用。

于 2017-08-18T17:49:21.313 回答
6

我可以看到一种简单的方法,只需稍微改变模型结构。(见最后如何使用现有模型并仅更改结尾)。

这种方法的优点是:

  • 您不必猜测您是否进行了正确的计算
  • 您无需关心 dropout 层以及如何实现 dropout 计算
  • 这是一个纯粹的 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查看 Dense 层的输出,使用checkingModel.predict(X)

选项 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.

于 2017-08-17T14:19:34.270 回答
2

所以这是针对谷歌同事的,自从发布了接受的答案以来,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>

于 2020-09-23T17:39:37.550 回答
1

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

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

从 Keras复制粘贴,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
于 2019-07-23T22:02:18.183 回答
1

用新的激活函数定义新层的简单方法:

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
于 2020-12-18T17:23:40.667 回答
0

我有同样的问题,但没有其他答案对我有用。我正在使用更新版本的 Keras 和 Tensorflow,所以现在有些答案不起作用。还给出了模型的结构,所以我不能轻易改变它。总体思路是创建原始模型的副本,该副本将与原始模型完全相同,但将激活与输出层分开。完成此操作后,我们可以在应用激活之前轻松访问输出值。

首先,我们将创建原始模型的副本,但在输出层上没有激活。这将使用 Kerasclone_model函数完成(请参阅文档)。

from tensorflow.keras.models import clone_model
from tensorflow.keras.layers import Activation

original_model = get_model()

def f(layer):
  config = layer.get_config()
  if not isinstance(layer, Activation) and layer.name in original_model.output_names:
    config.pop('activation', None)
  layer_copy = layer.__class__.from_config(config)
  return layer_copy

copy_model = clone_model(model, clone_function=f)  

仅此一项只会使用新权重进行克隆,因此我们必须将权重复制到新权original_model重:

copy_model.build(original_model.input_shape)
copy_model.set_weights(original_model.get_weights())

现在我们将添加激活层:

from tensorflow.keras.models import Model

old_outputs = [ original_model.get_layer(name=name) for name in copy_model.output_names ]
new_outputs = [ Activation(old_output.activation)(output) if old_output.activation else output 
                for output, old_output in zip(copy_model.outputs, old_outputs) ]
copy_model = Model(copy_model.inputs, new_outputs)

最后,我们可以创建一个新模型,其评估将是未应用激活的输出:

no_activation_outputs = [ copy_model.get_layer(name=name).output for name in original_model.output_names ]
no_activation_model = Model(copy.inputs, no_activation_outputs)

现在我们可以使用copy_model和访问预激活输出。实际上,您甚至可以修改代码以拆分一组自定义层而不是输出。original_modelno_activation_model

于 2021-10-10T17:53:01.857 回答