[Elasticsearch] Similar image search by image or natural language(1)

Elasticsearch(이하 ES)에서 자연어나 이미지로 유사한 이미지를 검색하는 방법에 대해서 알아보자.

유사한 이미지를 찾기 위해서는 먼저 이미지들 간의 유사도를 측정할 방법이 필요하고, 유사도를 측정하기 위해서 Vector를 사용할 수 있다. 이미지를 Embedding을 통해서 Vector로 변환하고, Vector 간의 유사도(Cosine similarity)를 계산해서 유사한 이미지를 찾을 수 있다.

그러면, 이미지를 Vector embedding하는 방법은 무엇이 있을까?

이미지를 vector embedding하는 방법과 이를 만들어주는 ML 모델들은 이미 많이 공개 되어 있다. ResNet, VGGNet, Inception 등 많은 모델들이 있다. 모델들의 성능을 잘 비교해서 알맞은 모델을 사용하면 된다.

여기에서는 OpenAI에서 공개한 CLIP model를 사용할 것이다.

CLIP을 사용해서 검색할 이미지들을 vector embedding해보자.
(사용할 이미지가 없다면 아래 첨부된 이미지를 사용해보자.)

from sentence_transformers import SentenceTransformer
from PIL import Image

img_model = SentenceTransformer('clip-ViT-B-32', device='cpu')

def vectorize(file_path):
    image = Image.open(file_path)
    embedding = img_model.encode(image)
    del image

    return embedding.tolist()

vectorize('./image/apple.jpg')

위 코드를 실행해보면 이미지가 vector embedding되어 512 차원의 vector를 반환하는 것을 확인 할 수 있다. 검색 대상의 이미지들을 모두 embedding 하면 된다.

vector embedding된 값들을 생성했다면, 이제 vector들을 ES에 올려서 유사 이미지 검색을 해보자.

먼저, ES에서 Vector를 담을 index를 생성한다.

PUT image_vector
{
  "mappings": {
    "properties": {
      "image_name": {
        "type": "keyword"
      },
      "vector": {
        "type": "dense_vector",
        "dims": 512,
        "index": true,
        "similarity": "cosine"
      }
    }
  }
}

생성한 인덱스에 만든 Vector 값들을 넣어준다.

from elasticsearch import Elasticsearch

def index(image_name, vector):
    es = Elasticsearch(cloud_id='elastic_cloud_id',
                       basic_auth=('elastic', 'elastic_secret'))
    doc = {     
        'image_name': image_name,
        'vector': vector
    }

    es.index(index='image_vector', body=doc)

index('apple.jpg', vector)

여기까지 하면 유사한 이미지를 검색하기 위한 준비는 끝난다.

원하는 이미지를 하나 골라서 검색을 해보자

GET image_vector/_search
{
  "knn": {
    "field": "vector",
    "k": 5,
    "num_candidates": 10,
    "query_vector": [
      0.5978590846061707,
      0.5926558375358582,
      -0.1243332028388977,
      0.11083680391311646,
      -0.6588778495788574,
     ... 생략 ...
      0.37319913506507874
    ]
  },
  "fields": [
    "image_name"
  ],
  "_source": false
}

왼쪽 끝의 사과 이미지와 위 예제 파일안에서 유사한 이미지 4개를 찾은 것을 확인 할 수 있다.

Next: Similar image search by image or natural language(2)