Почему мой пользовательский слой keras хорошо подходит для тренировочных данных, но дает плохие результаты при проверке? - PullRequest
0 голосов
/ 13 июня 2019

Я пытаюсь понять, как работают пользовательские слои Keras, но я сталкиваюсь с проблемой точности проверки моей модели.

Я попытался воспроизвести простую сверточную сеть на наборе данных MNIST, но с пользовательским слоем, объединяющим оператор Conv2D и BatchNormalisation.

Во-первых, я использовал данные:

from keras.datasets import mnist
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = np.array([x.reshape(28, 28, 1) for x in X_train])
X_test = np.array([x.reshape(28, 28, 1) for x in X_test])
y_train = pd.get_dummies(y_train)
y_test = pd.get_dummies(y_test)

Вот оригинальная реализация, которая хорошо работает:

def get_model():
    input_ = Input(shape=(28, 28, 1))
    x = Conv2D(filters=64, kernel_size=3, activation="relu", input_shape=(28,28,1))(input_)
    x = BatchNormalization()(x)
    x = MaxPool2D(pool_size=(2,2))(x)
    x = Conv2D(filters=128, kernel_size=3, activation="relu")(input_)
    x = BatchNormalization()(x)
    x = MaxPool2D(pool_size=(2,2))(x)
    x = Conv2D(filters=256, kernel_size=3, activation="relu")(input_)
    x = BatchNormalization()(x)
    x = MaxPool2D(pool_size=(2,2))(x)
    x = Flatten()(x)
    x = Dense(128, activation="relu")(x)
    x = Dense(64, activation="relu")(x)
    x = Dense(10, activation="softmax")(x)
    mod = Model(inputs=input_, outputs=x)
    return mod

optim = Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.0, clipvalue=K.epsilon())
model = get_model()
model.compile(optimizer=optim, loss='categorical_crossentropy', metrics=["accuracy"])
model.fit(X_train, y_train, batch_size=128, epochs=3, validation_data=(X_test, y_test))

С этой исходной моделью, после 3 эпох, я получаю точность поезда 97% и подтверждение 97%

А вот и мой пользовательский слой:

class Conv2DLayer(Layer):
    def __init__(self, filters, kernel_size, dropout_ratio=None, strides=(1, 1), activation="relu", use_bn=True, *args, **kwargs):
        self._filters = filters
        self._kernel_size = kernel_size
        self._dropout_ratio = dropout_ratio
        self._strides = strides
        self.use_bn = use_bn
        self._activation = activation
        self._args = args
        self._kwargs = kwargs
        super(Conv2DLayer, self).__init__(*args, **kwargs)

    def build(self, input_shape):

        self.conv = Conv2D(self._filters,
                           kernel_size=self._kernel_size,
                           activation=self._activation,
                           strides=self._strides,
                           input_shape=input_shape,
                           *self._args,
                           **self._kwargs)
        self.conv.build(input_shape)
        self.out_conv_shape = self.conv.compute_output_shape(input_shape)
        self._trainable_weights = self.conv._trainable_weights
        self._non_trainable_weights = self.conv._non_trainable_weights

        if self.use_bn:
            self.bn = BatchNormalization()
            self.bn.build(self.out_conv_shape)
            self._trainable_weights.extend(self.bn._trainable_weights)
            self._non_trainable_weights.extend(self.bn._non_trainable_weights)

        if self._dropout_ratio is not None:
            self.dropout = Dropout(rate=self._dropout_ratio)
            self.dropout.build(self.out_conv_shape)
            self._trainable_weights.extend(self.dropout._trainable_weights)
            self._non_trainable_weights.extend(self.dropout._non_trainable_weights)

        super(Conv2DLayer, self).build(input_shape)

    def call(self, inputs):
        x = self.conv(inputs)
        if self.use_bn:
            x = self.bn(x)
        if self._dropout_ratio is not None:
            x = self.dropout(x)
        return x

    def compute_output_shape(self, input_shape):
        return self.out_conv_shape

Наконец, вот модифицированная модель:

def get_model():
    input_ = Input(shape=(28, 28, 1))
    x = Conv2DLayer(filters=64, kernel_size=3, activation="relu")(input_)
    x = MaxPool2D(pool_size=(2,2))(x)
    x = Conv2DLayer(filters=128, kernel_size=3, activation="relu")(input_)
    x = MaxPool2D(pool_size=(2,2))(x)
    x = Conv2DLayer(filters=256, kernel_size=3, activation="relu")(input_)
    x = MaxPool2D(pool_size=(2,2))(x)
    x = Flatten()(x)
    x = Dense(128, activation="relu")(x)
    x = Dense(64, activation="relu")(x)
    x = Dense(10, activation="softmax")(x)
    mod = Model(inputs=input_, outputs=x)
    return mod

Для этой модели с пользовательским слоем мне удалось получить ту же точность поезда (97%), но точность проверки застряла примерно на 50%.

EDIT

Благодаря Матиасу Вальденегро я решил проблему, изменив метод call:

def call(self, inputs):
    training = K.learning_phase()
    x = self.conv(inputs)
    if self.use_bn:
        x = self.bn(x, training=training)
    if self._dropout_ratio is not None:
        x = self.dropout(x, training=training)
    return x

С K модулем keras.backend.

1 Ответ

0 голосов
/ 13 июня 2019

Как при отбрасывании, так и при пакетной нормализации ведут себя по-разному во время обучения и тестирования / вывода, и ваш слой не имеет такого поведения, поэтому его использование этих внутренних слоев в качестве режима обучения во время вывода дает неверные результаты.

Я не уверен, но я думаю, что вы можете это исправить, передав параметр training в вызове функции call через слои, что-то вроде:

def call(self, inputs, training=None):
    x = self.conv(inputs)
    if self.use_bn:
        x = self.bn(x, training=training)
    if self._dropout_ratio is not None:
        x = self.dropout(x, training=training)
    return x

Это должно заставить внутренние слои работать по-разному во времяфазы обучения и тестирования / вывода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...