はじめに
この記事ではMobileNet v3を用いた類似画像検索について記載します。
問題設定
クエリ画像と分類対象画像集合が与えられたときに、画像集合を類似画像と非類似画像の2種類に分類します。
手法
- MobileNetを用いて画像の特徴量を計算
- Tensor hubで公開されている学習済みモデルを使用。出力1024次元の特徴量。
- https://tfhub.dev/google/imagenet/mobilenet_v3_small_100_224/feature_vector/5
- 特徴量に重みをかける
- 特徴空間上のユークリッド距離で画像間の距離を計算し、距離が閾値以上の画像を類似画像と判断
評価方法
- SUN Database(シーン画像分類のベンチマーク)の部分データセットを使用(20クラス、各クラス100画像をランダムに選択)
- ある画像をクエリ画像として選択したときに、同じクラスの画像を類似画像、他のクラスの画像を非類似画像と分類する分類問題と考え、ROC AUCの値で評価
- これを、クエリ画像を様々に変えたときの平均値を求める
評価結果
特徴量の重みの計算方法について、何パターンか試してみました。
重み一定の場合
def calc_score(f1, f2):
```
クエリ画像の特徴量f1と比較画像の特徴量f2の類似度スコアを、特徴量のユークリッド距離で計算
```
dist = np.linalg.norm(((f1 - f2)))
return 1 / (dist + 0.000001)
MobileNetが出力する特徴量をそのまま使用した場合は下記結果になりました。
これは、各クラスの画像1枚をクエリ画像としたときのROC AUCの分布です。
全体の平均値は0.847です。
クエリ画像の選択によっては0.5付近のあまり良くない結果になるものもありますが、全体的にそれなりの結果になっているのではないかと思います。
最も悪い結果(ROC AUCが0.5に最も近い場合)のクエリ画像と、そのときに類似画像と誤って判断した画像は下の組み合わせです。
officeクラスの画像をクエリ画像にしたときに、wet_barクラス他の画像を類似画像として判定しています。
(見た感じ類似画像といっても良い気もします)
特徴量の標準偏差で重み付け
def calc_score(f1, f2):
```
特徴量を標準偏差で重み付け(特徴量ごとの標準偏差features_stdは事前に計算)
```
dist = np.linalg.norm(((f1 - f2) / features_std))
#dist = np.linalg.norm(((f1 - f2) ))
return 1 / (dist + 0.000001)
全体の平均値は0.834で、重み付けなしよりもやや悪い(誤差の範囲?)結果となりました。
偏差が小さい特徴量ほど重要視すべきという仮説でしたが、あたっていなかったようです。
全画像集合の分布を見たのが良くなかったのかもしれません。
クエリ画像の類似画像の集合の偏差を使えば良くなるのかもしれませんが、類似画像集合はわからないので実現不可能です。
クエリ画像の特徴量の大きさで重み付け
def calc_score(f1, f2):
```
クエリ画像の特徴量f1の大きさで重み付け
```
weight = np.maximum(f1, 0.5)
dist = np.linalg.norm(((f1 - f2) * weight))
return 1 / (dist + 0.000001)
クエリ画像の特徴量の大きさで重み付けした結果です。
(値の大きい特徴量ほど、その画像にとって重要な特徴量だという仮説)
全体の平均値は0.897となりました。
重みなしの結果に比べて5ポイントほど上昇しています。
また、waveやwindmillなど、重みなしでは分類困難だったクラスのスコアが大きく向上しています。
少なくとも今回使用したデータでは、値の大きい特徴量ほど重要だという仮説があたっていたということではないかと思います。
まとめ
- MobileNetを用いた特徴量を用いることで、それなりに類似画像の判断が可能
- 特徴量の重み付けで精度向上の可能性あり
- 特徴量の値が大きい特徴ほど重要
補足
この記事で紹介した方法は、AndroidTV用フォトフレームアプリ開発時に検討したものです。アプリのソースコードは下記で公開しています。
- Google photoの写真、動画を閲覧、スライドショーにするAndroidTVアプリ
- 類似画像検索機能を実装
また、このアプリは富士もくもく会の活動の一環で開発したものです。
ご興味のある方はぜひご参加ください。