画像の類似度を算出して検索を行う

# やりたいこと

・テキストで検索して大枠の画像のあたりをつけて、同じような画像を検索したい
・Elasticsearchで画像検索すればいいけどプラグインが更新されないし、結果もなんだか違う
・Elasticsearchのインデックスする時間、容量が我慢できないので・・画像なんてものを突っ込む余裕がない
・複数画像の特徴量を混ぜて位置をズラしたい。例えば好きな洋服を混ぜて好みの商品を探したいとか

2019/07/03追記
この記事に書いたことを使って[ファッション検索サービスファンネル](https://qiita.com/mokoenator/items/6807132ce59913186a9b)を作りました。



# アルゴリズムをためしてみる

画像の類似性でしらべたらORBとAKAZEというアルゴリズムがヒットするので試してみた。
西島秀俊、阿部寛、猫で類似度をみたら阿部寛より猫のほうが類似性が高かったのでこれは却下。

「Data-driven Visual Similarity for Cross-domain Image Matching」
これがすごいけど同じことはできそうにないので却下

そもそもこの手の方法ではElasticsearchに入れるときに全画像比較結果を入れる必要がある
つまりみんなが嫌いなO(n^2)の計算量。

#やっぱり機械学習ですね・・・現実的な落としどころはVGG16
CNNの最終出力をベクトルとして距離がだせそうなので調べてみると学習済みのVGG16という物があった。
ベクトルが出せるのであれば希望が見える！
すごい！
VGG16をつかえば1000クラス分類ができる。普通にためしてみる
・気持ち悪いくらいに分類してくれる
・猫、犬なんでもござれ。ほんとに気持ち悪い
CNNだから、1000分類の前に・・・もっともっと素敵な特徴量が出てるはず！
モデルの途中の層の値が引き出せないかGOOGLEで検索すると・・・
同じようなことしたい人がたくさんいる。

モデルを表示したところ↓

```
Backend Qt5Agg is interactive backend. Turning interactive mode on.
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 224, 224, 3)       0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 224, 224, 64)      1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 224, 224, 64)      36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 112, 112, 64)      0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 112, 112, 128)     73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 112, 112, 128)     147584    
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 56, 56, 128)       0         
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 56, 56, 256)       295168    
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 28, 28, 256)       0         
_________________________________________________________________
block4_conv1 (Conv2D)        (None, 28, 28, 512)       1180160   
_________________________________________________________________
block4_conv2 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_conv3 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_pool (MaxPooling2D)   (None, 14, 14, 512)       0         
_________________________________________________________________
block5_conv1 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv2 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv3 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_pool (MaxPooling2D)   (None, 7, 7, 512)         0         
_________________________________________________________________
flatten (Flatten)            (None, 25088)             0         
_________________________________________________________________
fc1 (Dense)                  (None, 4096)              102764544 
_________________________________________________________________
fc2 (Dense)                  (None, 4096)              16781312  
_________________________________________________________________
predictions (Dense)          (None, 1000)              4097000   
=================================================================
Total params: 138,357,544
Trainable params: 138,357,544
Non-trainable params: 0
```

全結合層のfc2ってところの値を抜いてあげればいいんですよ！
（CNNのパラメータ数ってすごいんですね・・・）

特徴量が引き出せればもう楽勝ですよ。
固定次元同士の比較だから楽勝ですよ。



#40個くらいの画像でためしてみた

![特徴.PNG](https://qiita-image-store.s3.amazonaws.com/0/185049/01380fa4-803f-c7b1-5411-e92efc22e4f9.png)
（ぱくたその画像をつかいました・・・）
無駄に4096次元の特徴量をグラフにしてみました
見えるはずのない4096次元の位置がみえてワクワクしてくる！

↑のような感じで複数画像の特徴量をつかって
↓のように類似度を出してみました


![ぱくたそ.PNG](https://qiita-image-store.s3.amazonaws.com/0/185049/c3dbaab4-8ae5-2b17-35fb-fb03da823a2c.png)
右の数字が類似度(１に近ければ似ている)
（ぱくたその画像をつかいました）


思ったよりいい感じに結果がでた。
トンネルの写真と建物の写真が引き合ってるところに、ニヤニヤしてしまう。

VGG16の感覚的な感想ですが、
・種類を判別するのは得意（犬、傘など）
・個体差を判別するのはきつい
そんな感じです。



#Elasticsearchにどのように連携させるか

・画像の4096次元特徴量を出しまくる。→Elasticsearchに入れる
・検索時に特徴量をコサイン類似度で算出して類似度が高い順に並べる

とりあえず100万画像を目標！

環境は・・・
「Fast Elasticsearch Vector Scoring」というプラグインを追加
サンプルにしたがって入れていけばいいはず。
よくこんなものがあるな・・・・
これはすごいことかもしれない！！！
「特徴量」がだせれば何でもござれやないか？？

時間を作ってやってみよう・・・・

つづき
https://qiita.com/mokoenator/items/91fcb9f438707680a2a8#_reference-099bc20d852b21d21202