Re:ゼロから始めるML生活

Standing on the shoulders of Giants

Elasticsearchで画像検索をやってみる

f:id:nogawanogawa:20190209121544j:plain

この記事は 情報検索・検索エンジン Advent Calendar 2019 - Qiita 16日目の記事です。 初日から非常に勉強になる記事を拝見させていただいて日々勉強しております。レベルが高すぎて内心ビクビクしてます。。。
何も考えずこのカレンダーに登録した1ヶ月前のアホな自分をぶん殴りたい…

普段、Elasticsearchをつかっているのですが、最近では検索したい対象が文書だけとは限らず、画像を使った検索をしたいことがあります。 ただ、Elasticsearchは全文検索エンジンなので、基本的には画像検索はそのままだと実現は難しいです。 一方、検索機能を一つに集約できると何かと便利なことがあります。 ということで、今回はElasticsearchを使った画像検索をやってみます。

画像検索

方針

  • Elasticsearchには画像のEmbeddingと画像ファイル名をセットにして投入
    • 検索機能がすべてElasticsearchだけで完結していると何かと便利

ということで探してみると、まさにやろうとしたことをそのままやっていらっしゃる記事を発見。

logmi.jp

www.linkedin.com

もうちょっと探してみると、こちらの記事がありました。

www.elastic.co

In Elasticsearch 7.0, we introduced experimental field types for high-dimensional vectors, and now the 7.3 release brings support for using these vectors in document scoring.

どうやら、Elasticsearch7.0から高次元のベクトルをフィールドで使用可能になり、7.3以降ではベクトルを使用したスコアリング機能がついているみたいですね。 現在では1024次元までvector fieldが使えるらしく、それを使ってコサイン類似度を求めることが可能のようです。 ということは、画像を1024次元までのベクトルで表現できれば、コサイン類似度を使用してアイテムの類似性を評価できそうです

検索対象画像

出力された画像で評価をしたいので、The Oxford-IIIT Pet Datasetを使います。

www.robots.ox.ac.uk

特徴として、

  • 37種類のペットの画像
  • 画像サイズはまちまち
    • ちゃんとやるなら前処理で画像を一定サイズに区切ったりしたほうがいいとは思いますが、今回は強引にresizeで対応

という感じでしょうか。

予め、類似度がうまく機能しているかを確認するために、データの一部を検証用に除外しておきます。 今回は適当に通番の末尾が***0.jpgのものを検証用としました。

画像 -> Embedding

作業としては、はじめに画像をEmbeddingにする必要があります。

画像からEmbeddingを取得する手法としては、ShiftとかSurfやらいろいろありますが、調べてみるとAuto Encoderなんかを使う事例がありました。

datascience.stackexchange.com

ぶっちゃけると、このEmbeddingの作り方が検索精度にそのまま影響するのですが、今回は検索するまででご容赦下さい。

単純にEmbedding取るだけならこちらの方のように、学習済みのtensorflow hubを使うのが簡単そうなので、今回はそちらを採用します。

qiita.com

tfhub.dev

画像のEmbeddingの算出はこんな感じにしました。

今回は決め打ちで37クラスにしているので、37次元のベクトルが生成され、各クラスに属する確率を算出しているようです。(←あんまり良くわかってない)

Embeddingをいじって精度を改善する際にはこの辺のハイパーパラメータを調整してあげる必要がありそうです。(やるとは言ってない)

画像をembeddingにできるという目標は達成できました。

インデクシング

本題の検索の準備に入ります。

上で紹介した記事を参考にすると、mappingはこんな感じにできそうです。

先程計算した、embeddingをimage_vectorに、ファイル名をfilenameとして突っ込みます。

検索

Elasticsearchに突っ込んでしまえばあとは検索のクエリを投げるだけですね。 コサイン類似度はこんな感じで取れるみたいです。

結果を見てみるとこんな感じでした。

検索データ

f:id:nogawanogawa:20191214003656p:plain

応答

1件目

f:id:nogawanogawa:20191214003831j:plain

2件目

f:id:nogawanogawa:20191214003854j:plain

3件目

f:id:nogawanogawa:20191214003922j:plain

4件目

f:id:nogawanogawa:20191214003943j:plain

なんとなく猫っぽいのが取れているのはうまくいってるからでしょうか?

10件目

でも、検索結果の10件目は犬でした。笑

f:id:nogawanogawa:20191214004148j:plain

この辺は学習済みモデルを持ってきて適当に動かしてるからですかね。ちゃんと学習させたらもっとうまくいく気はします。

その他

使ったコード

github.com

参考にさせていただいた記事

この辺も参考にさせていただきました。

yabaiwebyasan.com

yag-ays.github.io

感想

今回は「検索エンジンを使ってこんな検索できるかな?」と思って即興で画像検索をやってみました。 猫という大きな枠では大方検索はできていた気がしますが、その中の種類については一致していなかったので、その辺はEmbeddingをきちんと考慮する必要がありそうです。 ただ、Embeddingさえ作れてしまえば、Elasticsearchで検索はできることがわかったので、純粋にEmbeddingの算出だけに集中すれば良くなりました。

Elasticsearchを使った画像検索のメリットとしては、テキストでフィルターをかけた結果に、さらに画像などのEmbeddingを使用してスコアリングできる点かなと思います。 カテゴリやキーワードは既知であるとき、テキストでフィルターをかけた中で画像を使って言葉にならないスコアリングをしたいときなど、使いたい用途は思い当たるフシがあります。

あとはベクトルだったら何でもOKなので、doc2vecなんか使ったEmbeddingを使ってもうまくいくみたいですね。 この辺りは、含有する単語の意味なんかを考慮してスコアリングするときに効いてきそうです。

お粗末でしたが、こんな感じで終わりにします。 今後も勉強して、柔軟な検索・推薦システムに携わりたいなーと思います。