Подтвердить что ты не робот

Keras извлекает значение node перед активацией

Представьте себе полностью связанную нейронную сеть с последними двумя слоями следующей структуры:

[Dense]
    units = 612
    activation = softplus

[Dense]
    units = 1
    activation = sigmoid

Выходное значение сети равно 1, но я хотел бы знать, что входной сигнал x для сигмоидальной функции (должно быть некоторое большое число, так как sigm (x) здесь 1).

Folllowing ответ 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 может быть отрицательным числом? В этом случае вывод последних слоев должен быть рядом ближе к 0.0, чем 1.0. Являются ли dense_0_out или dense_1_weights неправильными выходами или весами?

4b9b3361

Ответ 1

Поскольку вы используете get_value(), я предполагаю, что вы используете бэкэнд Theano. Чтобы получить значение node до активации сигмоида, вы можете пересечь граф вычислений.

График может быть пройден, начиная с выходов (результат некоторых вычислений) вплоть до его входов, используя поле владельца.

В вашем случае вам нужен вход x активации сигмоида op. Выход сигмоида op равен 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().

С небольшой модификацией вашего кода вы можете получить значение node перед активацией:

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].

Ответ 2

Я вижу простой способ немного изменить структуру модели. (См. В конце, как использовать существующую модель и изменить только окончание).

Преимущества этого метода заключаются в следующем:

  • Вам не нужно угадывать, выполняете ли вы правильные вычисления
  • Вам не нужно заботиться о слоях отсеивания и о том, как реализовать вычисление выпадения
  • Это чистое решение 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.

Ответ 3

(TF backend) Решение для слоев 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

Теперь нам нужно немного изменить основную функцию. Сначала определите слой по его имени. Затем получите активации из предыдущего слоя. И наконец, вычислите выходные данные из целевого слоя.

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