Fine-tuning a Keras model. Updated to the Keras 2.0 APIなど、Kerasのチュートリアルでは、学習画像に以下のような前処理を行っています。

# prepare data augmentation configuration
train_datagen = ImageDataGenerator(
    rescale=1. / 255,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True)

test_datagen = ImageDataGenerator(rescale=1. / 255)

これは、VGG16の学習済みのネットに対して、RGB順で0〜1.0の値を入力しています。

しかし、KerasにおけるVGG16の重みは、Oxford大学のVGGによりCreative Commons Attribution Licenseの下で公開されたものを移植しています。そのため、本来、期待する前処理は、BGR順で0〜255の値からImageNetのmeanを引いた値となります。ただし、CaffeModelからKerasModelへの変換の過程でRGB順への補正は行われているようですので、RGB順で0〜255がKerasとして期待する入力となります。

def preprocess_input(img):
  #img = img[...,::-1]  #RGB2BGR
  #img = img - (104,117,123) #BGR mean value of VGG16
  img = img - (123,117,104) #RGB mean value of VGG16
  return img

train_datagen = ImageDataGenerator(
   preprocessing_function=preprocess_input,
   shear_range=0.2,
   zoom_range=0.2,
   horizontal_flip=True
)

test_datagen = ImageDataGenerator(
   preprocessing_function=preprocess_input,
)

それでは、この変更でどれくらい性能が変わるのでしょうか。AgeGender NetのAgeのClassification問題に対して比較してみました。

変更前(0〜1.0、RGB順、lr=0.01)
agegender_age_vgg16


変更後(0〜255 - mean、RGB順、lr=0.01)
agegender_age_vgg16_with_preprocseeing_rgb

変更後(0〜255 - mean、BGR順、lr=0.01)
agegender_age_vgg16_with_preprocess


結論としては、ラーニングレートが変わるだけで、どちらを使っても問題ないようです。VGG16の畳み込みで画素間差分を取っている過程でDC値の意味が消失しがちなのと、最後の内積のパラメータで調整できてしまうのではないかと思います。個人的には、正しい前処理をした方が一貫性があって気分はよいです。

尚、本件は、チュートリアルの掲示板でも議論になっていますが、特に結論は出ていないようです。

a-ozbek commented on 6 Feb 2017 •  edited 
Excuse me if this issue was brought up before about this script. I couldn't find a resolution to this in the comments.

In this script, during the fine-tuning, the "train_generator" and "validation_generator" do not seem to do VGG16 pre-processing which is

      # 'RGB'->'BGR'  
        x = x[:, :, :, ::-1]  
        # Zero-center by mean pixel  
        x[:, :, :, 0] -= 103.939  
        x[:, :, :, 1] -= 116.779  
        x[:, :, :, 2] -= 123.68  
Isn't it wrong to do fine-tuning of VGG16 without this pre-processing step?
aidiary commented on 16 Feb 2017
@a-ozbek

I have the same question.
I have experimented with and without this pre-processing and get the slightly better result in the case of without this pre-processing...

without pre-processing => val_acc = 93.5%@ epoch 50
with pre-processing      => val_acc = 92.8% @ epoch 50