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개를 찾은 것을 확인 할 수 있다.
“[Elasticsearch] Similar image search by image or natural language(1)” 글에 관한 1개의 생각