web-dev-qa-db-ja.com

Kerasはアクティブ化関数の前にノードの値を取得します

次の構造の最後の2つの層を持つ完全に接続されたニューラルネットワークを想像してみてください。

[Dense]
    units = 612
    activation = softplus

[Dense]
    units = 1
    activation = sigmoid

ネットの出力値は1ですが、シグモイド関数への入力xが何であるかを知りたいです(ここではsigm(x)が1であるため、高い数値である必要があります)。

以下 indraforyou's 回答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_outまたはdense_1_weights間違った出力または重み?

19
johk95

get_value()を使用しているので、Theanoバックエンドを使用していると仮定します。シグモイドアクティブ化の前にノードの値を取得するには、 計算グラフをトラバースする

グラフは、所有者フィールドを使用して、出力(計算の結果)から入力に至るまでトラバースできます。

あなたの場合、あなたが望むのは、シグモイド活性化演算の入力xです。 S状結腸手術の出力は_model.output_です。これらをまとめると、変数xは_model.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]_を使用してください。

8
Yu-Yang

モデルの構造を少し変えるだけで簡単な方法がわかります。 (最後に既存のモデルの使用方法を参照し、エンディングのみを変更してください)。

この方法の利点は次のとおりです。

  • 正しい計算を行っているかどうかを推測する必要はありません
  • ドロップアウトレイヤーやドロップアウト計算の実装方法について気にする必要はありません
  • これは純粋なKerasソリューションです(TheanoまたはTensorflowのいずれかのバックエンドに適用されます)。

以下の2つの可能な解決策があります。

  • オプション1-提案された構造で最初から新しいモデルを作成する
  • オプション2-エンディングのみを変更して既存のモデルを再利用する

モデル構造

最後に、最後のデンスを2つのレイヤーに分割することができます。

_[Dense]
    units = 612
    activation = softplus

[Dense]
    units = 1
    #no activation

[Activation]
    activation = sigmoid
_

次に、最後の密なレイヤーの出力を取得するだけです。

2つのモデルを作成する必要があると思います。1つはトレーニング用で、もう1つはこの値をチェックするためのものです。

オプション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)
_

それから2つのモデルを作成します。1つはトレーニング用、もう1つはdenseの出力をチェックするためのものです。

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

#if using the sequential model:
    checkingModel = model   

trainingModel = Model(checkingModel.inputs, sigmoidOut)   
_

通常のトレーニングにはtrianingModelを使用します。 2つのモデルは重みを共有するため、一方をトレーニングすることはもう一方をトレーニングすることです。

checkingModel.predict(X)を使用して、Denseレイヤーの出力を表示するためだけにcheckingModelを使用します

オプション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)
_

新しい高密度レイヤーを作成したので、古いレイヤーから重みを取得することが重要です。

_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.predict(X)で必要な値を確認するには、checkingModelを使用します。そして、trainingModelをトレーニングします。

5
Daniel Möller

(TFバックエンド)コンバージョンレイヤーのソリューション。

同じ質問があり、モデルの構成を書き直すことはできませんでした。単純なハックは、呼び出し関数を手動で実行することです。アクティベーションを制御できます。

Kerasからのコピーアンドペースト sourceselflayerに変更。他のレイヤーでも同じことができます。

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

すべてのネガをゼロに設定して、VGG16ReLu操作に埋め込まれた後に取得された結果と比較します。

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