DeepLearning
Keras
ImageNet
物体検出
VGG16

猫がきつねになる理由?!

前回以下のコード書いて、猫がkit_foxになったのは、学習依存?!
としたが、そんな話は無くて、おかしいものはおかしいということがわかりました。
今回はそこだけ書こうと思います。

from keras.preprocessing import image
from keras.applications.vgg16 import VGG16, preprocess_input, decode_predictions
import numpy as np

model = VGG16(weights='imagenet', include_top=True)

img_path = '1004.jpg'
img = image.load_img(img_path, target_size=(224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)

model.summary()

preds = model.predict(preprocess_input(x))
print(preds)  #学習済1000個のマトリクスで確率出力
results = decode_predictions(preds, top=5)[0]  #上位5個出力
for result in results:
    print(result)

以下の猫の画像を入力とすると、出力例は以下のとおりです。

('n02119789', 'kit_fox', 0.38026658)
 ('n02119022', 'red_fox', 0.2352507)
 ('n02124075', 'Egyptian_cat', 0.1030893)
 ('n02123159', 'tiger_cat', 0.09078445)
 ('n02123045', 'tabby', 0.057852905)

1004.jpg

そもそも、kit_foxってこんな動物でした。
kit_fox.jpg

まあ、この猫はほぼほぼEgyptian_catが近いように思います。
だから、ちょっと変。。。

そして、実は上記のコードの中でpreprocess_input(x)がなんとなく何をやっているか気持ち悪い。。。
そして、実は

x=preprocess_input(x)
preds = model.predict(preprocess_input(x))

とすると、これが結果変わります。

('n02124075', 'Egyptian_cat', 0.46803296)
('n02119789', 'kit_fox', 0.21247572)
('n02127052', 'lynx', 0.07749934)
('n02123045', 'tabby', 0.05827256)
('n02123159', 'tiger_cat', 0.03905577)

おお、Egyptian_catが一番になりました。これが正解。。。きっと

x=preprocess_input(x)

はいいことしてるんだ。。。って、ことはなく。これを繰り返してみました。
つまり

for i in range(20):
    x = preprocess_input(x)
    #print(max(x))
    preds = model.predict(x)
# decode the results into a list of tuples (class, description, probability)
# (one such list for each sample in the batch)
#print('Predicted:', decode_predictions(preds, top=5)[0])
    print("Iteration = ",i)
    results = decode_predictions(preds, top=5)[0]
    for result in results:
        print(result)

結果

Iteration =  0
('n02119789', 'kit_fox', 0.38026658)
('n02119022', 'red_fox', 0.2352507)
('n02124075', 'Egyptian_cat', 0.1030893)
('n02123159', 'tiger_cat', 0.09078445)
('n02123045', 'tabby', 0.057852905)
Iteration =  1
('n02124075', 'Egyptian_cat', 0.46803296)
('n02119789', 'kit_fox', 0.21247572)
('n02127052', 'lynx', 0.07749934)
('n02123045', 'tabby', 0.05827256)
('n02123159', 'tiger_cat', 0.03905577)
Iteration =  2
('n02119789', 'kit_fox', 0.49166027)
('n02124075', 'Egyptian_cat', 0.2541342)
('n02119022', 'red_fox', 0.15589291)
('n02123159', 'tiger_cat', 0.069696754)
('n02123045', 'tabby', 0.020968592)
Iteration =  3
('n02124075', 'Egyptian_cat', 0.6946748)
('n02119789', 'kit_fox', 0.27540964)
('n02123597', 'Siamese_cat', 0.006340976)
('n02119022', 'red_fox', 0.0060817837)
('n02120505', 'grey_fox', 0.0057176016)
Iteration =  4
('n02124075', 'Egyptian_cat', 0.90539074)
('n02119789', 'kit_fox', 0.070943415)
('n02119022', 'red_fox', 0.013553328)
('n01877812', 'wallaby', 0.003125138)
('n02123159', 'tiger_cat', 0.002393669)
Iteration =  5
('n02124075', 'Egyptian_cat', 0.6094417)
('n02119789', 'kit_fox', 0.38328072)
('n02119022', 'red_fox', 0.0022710946)
('n01877812', 'wallaby', 0.0017033187)
('n02120505', 'grey_fox', 0.0013750028)
Iteration =  6
('n02124075', 'Egyptian_cat', 0.90677506)
('n01877812', 'wallaby', 0.072301954)
('n02119789', 'kit_fox', 0.017778885)
('n02119022', 'red_fox', 0.0017611642)
('n02127052', 'lynx', 0.00044032672)
Iteration =  7
('n02124075', 'Egyptian_cat', 0.755598)
('n02119789', 'kit_fox', 0.23770595)
('n01877812', 'wallaby', 0.004804316)
('n02119022', 'red_fox', 0.00084602175)
('n02120505', 'grey_fox', 0.0008449703)
Iteration =  8
('n01877812', 'wallaby', 0.8218272)
('n02124075', 'Egyptian_cat', 0.17070939)
('n02119789', 'kit_fox', 0.0056909528)
('n02119022', 'red_fox', 0.0006865931)
('n02326432', 'hare', 0.00061656494)
Iteration =  9
('n02124075', 'Egyptian_cat', 0.8019383)
('n02119789', 'kit_fox', 0.16958125)
('n01877812', 'wallaby', 0.024510682)
('n02120505', 'grey_fox', 0.003015715)
('n02119022', 'red_fox', 0.00087783736)
Iteration =  10
('n01877812', 'wallaby', 0.99810684)
('n02124075', 'Egyptian_cat', 0.00079449714)
('n02119789', 'kit_fox', 0.0005136845)
('n02325366', 'wood_rabbit', 0.00036146634)
('n02119022', 'red_fox', 0.00013147145)
Iteration =  11
('n02119789', 'kit_fox', 0.5800364)
('n02124075', 'Egyptian_cat', 0.1750644)
('n01877812', 'wallaby', 0.12934218)
('n02120505', 'grey_fox', 0.1008656)
('n02119022', 'red_fox', 0.0146404)
Iteration =  12
('n01877812', 'wallaby', 0.9995395)
('n02119022', 'red_fox', 0.00015985851)
('n02325366', 'wood_rabbit', 0.00015812213)
('n02119789', 'kit_fox', 9.874253e-05)
('n02328150', 'Angora', 1.8888597e-05)
Iteration =  13
('n02120505', 'grey_fox', 0.4043221)
('n02119789', 'kit_fox', 0.38889655)
('n01877812', 'wallaby', 0.13404985)
('n02119022', 'red_fox', 0.059551917)
('n02124075', 'Egyptian_cat', 0.013142543)
Iteration =  14
('n01877812', 'wallaby', 0.99780864)
('n02119022', 'red_fox', 0.001515317)
('n03590841', "jack-o'-lantern", 0.0003177198)
('n02119789', 'kit_fox', 0.00012793872)
('n02120505', 'grey_fox', 9.046813e-05)
Iteration =  15
('n02120505', 'grey_fox', 0.5143544)
('n02119789', 'kit_fox', 0.19364485)
('n02119022', 'red_fox', 0.16289496)
('n01877812', 'wallaby', 0.12433733)
('n02124075', 'Egyptian_cat', 0.0047152797)
Iteration =  16
('n03590841', "jack-o'-lantern", 0.8111243)
('n01877812', 'wallaby', 0.18663457)
('n02119022', 'red_fox', 0.0011289458)
('n02098286', 'West_Highland_white_terrier', 0.00072580855)
('n02948072', 'candle', 0.00024366134)
Iteration =  17
('n02120505', 'grey_fox', 0.4624094)
('n01877812', 'wallaby', 0.21870022)
('n02119022', 'red_fox', 0.1934696)
('n03590841', "jack-o'-lantern", 0.057867166)
('n02119789', 'kit_fox', 0.05589882)
Iteration =  18
('n03590841', "jack-o'-lantern", 0.99984145)
('n02948072', 'candle', 0.00015669552)
('n01877812', 'wallaby', 1.099516e-06)
('n02098286', 'West_Highland_white_terrier', 6.2512464e-07)
('n04286575', 'spotlight', 1.3702392e-07)
Iteration =  19
('n03590841', "jack-o'-lantern", 0.9997315)
('n02120505', 'grey_fox', 7.7555465e-05)
('n01877812', 'wallaby', 5.182098e-05)
('n02948072', 'candle', 4.708824e-05)
('n02119022', 'red_fox', 4.4961234e-05)

最初は調子いい感じですが、11回やったら、wallaby、そして17回からは、なんとjack-o'-lanternになってしまいました。
やはり、変ということでこのテンソルの出力を書き出してみると。。。
最後のテンソルが

[[-2236.1902 -2289.5806 -2256.1902]
  [-2238.1902 -2291.5806 -2260.1902]
  [-2239.1902 -2289.5806 -2259.1902]
  ...
  [-2052.1902 -2120.5803 -2076.1902]
  [-2075.1902 -2143.5803 -2099.1902]
  [-2082.1902 -2150.5803 -2108.1902]]

 [[-2240.1902 -2289.5806 -2257.1902]
  [-2241.1902 -2291.5806 -2263.1902]
  [-2237.1902 -2289.5806 -2263.1902]
  ...
  [-2067.1902 -2135.5803 -2091.1902]
  [-2092.1902 -2160.5803 -2116.1902]
  [-2100.1902 -2168.5803 -2126.1902]]

 [[-2234.1902 -2282.5806 -2257.1902]
  [-2232.1902 -2277.5806 -2254.1902]
  [-2231.1902 -2275.5806 -2257.1902]
  ...
  [-2047.1901 -2115.5803 -2071.1902]
  [-2055.1902 -2123.5803 -2079.1902]
  [-2083.1902 -2151.5803 -2109.1902]]]

って大きな数になっていました。これじゃ識別は無理ですね。
まあ、そんな想定でこの関数作られていないから。。。
ということで、この関数を外して普通に何も処理せずに予測してみました。
その結果は

('n02124075', 'Egyptian_cat', 0.31957385)
('n02119789', 'kit_fox', 0.18935515)
('n02127052', 'lynx', 0.10189656)
('n02123597', 'Siamese_cat', 0.08490238)
('n02120079', 'Arctic_fox', 0.07191841)

なんとも当たり前な結果。。このままでいいんです。

じゃ、あの関数はどんな処理しているんでしょう。
ということで、ググってみると。。
keras / keras / applications / imagenet_utils.py
ということで、

152 def preprocess_input(x, data_format=None, mode='caffe'): 
153     """Preprocesses a tensor or Numpy array encoding a batch of images. 
154  
155     # Arguments 
156         x: Input Numpy or symbolic tensor, 3D or 4D. 
157             The preprocessed data is written over the input data 
158             if the data types are compatible. To avoid this 
159             behaviour, `numpy.copy(x)` can be used. 
160         data_format: Data format of the image tensor/array. 
161         mode: One of "caffe", "tf" or "torch". 
162             - caffe: will convert the images from RGB to BGR, 
163                 then will zero-center each color channel with 
164                 respect to the ImageNet dataset, 
165                 without scaling. 
166             - tf: will scale pixels between -1 and 1, 
167                 sample-wise. 
168             - torch: will scale pixels between 0 and 1 and then 
169                 will normalize each channel with respect to the 
170                 ImageNet dataset. 
171  
172     # Returns 
173         Preprocessed tensor or Numpy array. 
174  
175     # Raises 
176         ValueError: In case of unknown `data_format` argument. 

ということをしているということです。

まあ、一応使ってもいいけど、影響あるので素性がわかるときは使わなくとも自分で前処理コードを書くのがいいと思います。

まとめ

・今回は、前処理しない方が、与えられた関数利用した場合よりまともな結果が得られた
・Kerasのimagenet向けのpreprocess_input(x)の処理が何をやっているかを見た
・基本は必要に応じて自分で前処理する方がリスクが少ない場合もあると考察する