Слой Keras BatchNorm, дающий странные результаты во время тренировок и умозаключений - PullRequest
0 голосов
/ 09 января 2019

У меня возникла проблема с Keras, когда функция оценки дает другое значение потери при обучении (намного выше) и значение точности (намного ниже) по сравнению со значением, которое я получаю во время обучения. Я знаю, что этот вопрос уже задавался в нескольких местах ( здесь , здесь ), но я думаю, что моя проблема другая и до сих пор не ответил на этих форумах.

Объяснение задачи

Это должно быть очень простым заданием. Все, что я делаю, это переписываю свой собственный набор данных из 256 изображений (29x29x3) с 256 выходными классами (по одному на каждое изображение).

Dataset

Дело 1
x_train = Все значения пикселей на изображении = i, где я переходит от 0 до 255.
y_train = i

Дело 2
x_train = Центр 5 * 5 патча значений пикселей на изображении = i, где i изменяется от 0 до 255. Все остальные значения пикселей одинаковы для всех изображений.
y_train = i

Это дает мне 256 изображений для тренировочных данных в каждом случае. (Было бы более понятно, если вы просто посмотрите на код)

Вот мой код для воспроизведения проблемы -

from __future__ import print_function

import os

import keras
from keras.datasets import mnist
from keras.models import Sequential, load_model
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D, Activation
from keras.layers.normalization import BatchNormalization
from keras.callbacks import ModelCheckpoint, LearningRateScheduler, Callback
from keras import backend as K
from keras.regularizers import l2

import matplotlib.pyplot as plt
import PIL.Image
import numpy as np
from IPython.display import clear_output

# The GPU id to use, usually either "0" or "1"
os.environ["CUDA_DEVICE_ORDER"]="PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"]="1"

# To suppress the warnings
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'

## Hyperparamters
batch_size = 256
num_classes = 256
l2_reg=0.0
epochs = 500


## input image dimensions
img_rows, img_cols = 29, 29

## Train Image (I took a random image from ImageNet)
train_img_name = 'n01871265_279.JPEG'
ret = PIL.Image.open(train_img_name) #Opening the image
ret = ret.resize((img_rows, img_cols)) #Resizing the image
img = np.asarray(ret, dtype=np.uint8).astype(np.float32) #Converting it to numpy array
print(img.shape) # (29, 29, 3)

## Creating the training data
#############################
x_train = np.zeros((256, img_rows, img_cols, 3))
y_train = np.zeros((256,), dtype=int)
for i in range(len(y_train)):
    temp_img = np.copy(img)
    ## Case1 of dataset
    # temp_img[:, :, :] = i # changing all the pixel values
    ## Case2 of dataset
    temp_img[12:16, 12:16, :] = i # changing the centre block of 5*5 pixels
    x_train[i, :, :, :] = temp_img
    y_train[i] = i
##############################

## Common stuff in Keras
if K.image_data_format() == 'channels_first':
    print('Channels First')
    x_train = x_train.reshape(x_train.shape[0], 3, img_rows, img_cols)
    input_shape = (3, img_rows, img_cols)
else:
    print('Channels Last')
    x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 3)
    input_shape = (img_rows, img_cols, 3)

## Normalizing the pixel values
x_train = x_train.astype('float32')
x_train /= 255
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')

## convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)

## Model definition
def model_toy(mom):
    model = Sequential()

    model.add( Conv2D(filters=64, kernel_size=(7, 7), strides=(1,1), input_shape=input_shape, kernel_regularizer=l2(l2_reg)) )
    model.add(Activation('relu'))
    model.add(BatchNormalization(momentum=mom, epsilon=0.00001))
    #Default parameters kept same as PyTorch
    #Meaning of PyTorch momentum is different from Keras momentum.
    # PyTorch mom = 0.1 is same as Keras mom = 0.9

    model.add( Conv2D(filters=128, kernel_size=(7, 7), strides=(1, 1), kernel_regularizer=l2(l2_reg)))
    model.add(Activation('relu'))
    model.add(BatchNormalization(momentum=mom, epsilon=0.00001))

    model.add(Conv2D(filters=256, kernel_size=(5, 5), strides=(1, 1), kernel_regularizer=l2(l2_reg)))
    model.add(Activation('relu'))
    model.add(BatchNormalization(momentum=mom, epsilon=0.00001))

    model.add(Conv2D(filters=512, kernel_size=(5, 5), strides=(1, 1), kernel_regularizer=l2(l2_reg)))
    model.add(Activation('relu'))
    model.add(BatchNormalization(momentum=mom, epsilon=0.00001))

    model.add(Conv2D(filters=1024, kernel_size=(5, 5), strides=(1, 1), kernel_regularizer=l2(l2_reg)))
    model.add(Activation('relu'))
    model.add(BatchNormalization(momentum=mom, epsilon=0.00001))

    model.add( Conv2D( filters=2048, kernel_size=(3, 3), strides=(1, 1), kernel_regularizer=l2(l2_reg) ) )
    model.add(Activation('relu'))
    model.add(BatchNormalization(momentum=mom, epsilon=0.00001))

    model.add(Conv2D(filters=4096, kernel_size=(3, 3), strides=(1, 1), kernel_regularizer=l2(l2_reg)))
    model.add(Activation('relu'))
    model.add(BatchNormalization(momentum=mom, epsilon=0.00001))


    # Passing it to a dense layer
    model.add(Flatten())

    model.add(Dense(1024, kernel_regularizer=l2(l2_reg)))
    model.add(Activation('relu'))
    model.add(BatchNormalization(momentum=mom, epsilon=0.00001))

    # Output Layer
    model.add(Dense(num_classes, kernel_regularizer=l2(l2_reg)))
    model.add(Activation('softmax'))

    return model

mom = 0.9 #0
model = model_toy(mom)
model.summary()

model.compile(loss=keras.losses.categorical_crossentropy,
              optimizer=keras.optimizers.Adam(lr=0.001),
              #optimizer=keras.optimizers.SGD(lr=0.01, momentum=0.9, decay=0.0, nesterov=True),
              metrics=['accuracy'])

history = model.fit(x_train, y_train,
                    batch_size=batch_size,
                    epochs=epochs,
                    verbose=1,
                    shuffle=True,
                   )

print('Training results')
print('-------------------------------------------')
score = model.evaluate(x_train, y_train, verbose=1)
print('Training loss:', score[0])
print('Training accuracy:', score[1])
print('-------------------------------------------')

Небольшая заметка - Мне удалось успешно выполнить эту задачу в PyTorch. Просто моя настоящая задача требует, чтобы у меня была модель Keras. Вот почему я изменил значения по умолчанию для слоя BatchNorm (основная причина проблемы) в соответствии со значениями, которые я использовал для обучения модели PyTorch.

Вот изображение, которое я использовал в своем коде. enter image description here

Вот результаты тренировок.
Case1 набора данных
Case2 набора данных

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

Затем я посмотрел исходный код Keras, чтобы узнать, можно ли каким-либо образом заставить слой BatchNorm использовать статистику пакета вместо скользящего среднего и дисперсии.
Здесь - формула обновления, которую Keras (backend - TF) использует для обновления среднего значения и дисперсии.

#running_stat -= (1 - momentum) * (running_stat - batch_stat) 

Так что, если я установлю значение импульса равным 0, это будет означать, что значение, присвоенное runing_stat, всегда будет равно batch_stat на этапе обучения. Таким образом, значение, которое он будет использовать в режиме вывода, также будет таким же (близким), что и статистика пакета / набора данных.
Вот результаты этого небольшого эксперимента с той же проблемой, которая все еще возникает.
Case1 набора данных
Case2 набора данных

Среда программирования - Python-3.5.2, tenorflow-1.10.0, keras-2.2.4

Я пробовал то же самое с tenorflow-1.12.0, keras-2.2.2, но это все равно не решило проблему.

...