摘要:在图像识别、以图搜图、图库去重等场景中,传统的关键词搜索已无法满足需求。本文将深入探讨如何利用 Elasticsearch 的向量检索能力,结合深度学习模型,打造高性能的相似图片搜索引擎。
1. 引言:为什么需要"以图搜图"?
想象一下这个场景:用户在电商APP上看到一张喜欢的椅子,但不知道品牌和型号,只需上传图片,系统就能立刻找出同款或风格相似的商品。或者在内容审核平台,需要快速识别并拦截与违规图片"长得像"的变体图片。
传统的基于标签(Keyword)的搜索在这里失效了,因为图片本身是非结构化数据。我们需要的是向量搜索(Vector Search)------将图片转化为计算机能理解的数字指纹(Embeddings),通过计算指纹的相似度来匹配结果。
而 Elasticsearch 不仅仅是全文检索的王者,从 7.x 版本开始,它引入了 dense_vector 字段和 HNSW 算法,使其成为了一个轻量级且强大的向量数据库。
2. 核心原理:图片是如何变成向量的?
要让 ES 搜索图片,首先要解决一个核心问题:如何把图片变成一串数字?
2.1 特征提取(Embedding)
我们需要一个预训练的深度学习模型(CNN、Transformer等),常见的有 ResNet、MobileNet、CLIP 等。
- 输入:一张图片(例如 224x224 像素)。
- 处理:通过神经网络的卷积层和池化层,提取图片的高层语义特征(颜色分布、纹理、形状、物体轮廓)。
- 输出:一个固定长度的浮点数数组(Vector)。例如 ResNet-50 输出的是 1024 维或 2048 维的向量。
通俗理解:这串数字就是图片的"DNA"。两张图片视觉上越像,它们的向量在高维空间中的距离(欧氏距离或余弦相似度)就越近。
2.2 相似度计算
在向量空间中,常用的计算方式有:
- 余弦相似度(Cosine Similarity):计算两个向量夹角的余弦值,越接近 1 越相似(ES 中最常用)。
- 欧氏距离(L2 Norm):计算空间中两点的直线距离,越小越相似。
- 点积(Dot Product):用于特定归一化后的向量。
3. 实战:使用 Elasticsearch 实现图片搜索
接下来,我们分三步走:建表、入库、搜索。
步骤一:定义 Mapping(索引映射)
在 ES 中,我们需要定义一个字段来存储图片向量。假设我们使用 ResNet-50 模型,输出向量维度为 2048。
json
PUT /image_index
{
"mappings": {
"properties": {
"image_id": { "type": "keyword" },
"image_url": { "type": "text" },
"image_vector": {
"type": "dense_vector",
"dims": 2048,
"index": true,
"similarity": "cosine"
}
}
}
}
关键点:
type: dense_vector:声明这是一个稠密向量。dims:必须与你使用的 AI 模型输出维度一致。similarity:定义评分算法,这里设为余弦相似度。
步骤二:数据入库(Python 示例)
你需要先用 Python (PyTorch/TensorFlow) 处理图片生成向量,再写入 ES。
python
from elasticsearch import Elasticsearch
import numpy as np
from PIL import Image
import torch
# 假设已加载预训练模型 model
es = Elasticsearch("http://localhost:9200")
# 1. 加载图片并预处理
img = Image.open("cat.jpg").resize((224, 224))
img_tensor = preprocess(img) # 转为Tensor并归一化
# 2. 模型推理生成向量
with torch.no_grad():
vector = model(img_tensor).numpy()[0]
# ES cosine similarity 要求向量归一化(重要!)
vector = vector / np.linalg.norm(vector)
# 3. 写入 ES
doc = {
"image_id": "img_001",
"image_url": "http://example.com/cat.jpg",
"image_vector": vector.tolist()
}
es.index(index="image_index", id="1", document=doc)
步骤三:执行相似搜索
当用户上传一张查询图时,流程同样是先生成查询向量 query_vector,然后在 ES 中执行 script_score 查询。
json
GET /image_index/_search
{
"query": {
"script_score": {
"query": { "match_all": {} },
"script": {
"source": "cosineSimilarity(params.query_vector, 'image_vector') + 1.0",
"params": {
"query_vector": [0.12, -0.98, ...]
}
}
}
},
"size": 10
}
注:+ 1.0 是为了避免相似度为负数导致评分小于 0(ES 不支持负分)。
进阶玩法:kNN 搜索(ES 8.x+)
新版本 ES 提供了更高效的 knn 语法,速度更快且更省内存:
json
GET /image_index/_knn_search
{
"knn": {
"field": "image_vector",
"query_vector": [0.12, -0.98, ...],
"k": 10,
"num_candidates": 100
},
"_source": ["image_url"]
}
4. 性能优化:HNSW 算法
如果你的图库有 1000 万张图,全量扫描(Brute Force)是不可接受的。ES 底层使用了 HNSW (Hierarchical Navigable Small World) 图算法。
- 原理:构建一个多层导航图,搜索时先在高层粗略找,再逐层细化,极大减少计算量。
- 调优参数 :
m:图的最大连接数,越大索引越准但内存占用越高(默认 16)。ef_construction:构建时的搜索范围,越大索引越慢但质量越好(默认 100)。
5. 优缺点分析
为什么选择 ES 做图片搜索?
-
一站式:既能存图片元数据(如上传时间、标签),又能存向量,不需要维护 ES + Milvus/Faiss 两套系统。
-
混合搜索 :这是 ES 的杀手锏。你可以实现"找红色的、且款式相似的连衣裙":
json"bool": { "must": [ { "match": { "color": "红色" } }, { "script_score": { ... } } ] } -
生态成熟:拥有完善的备份、监控、分片机制。
局限性:
- 相比专业的向量库(如 Milvus),在超大规模(亿级以上)和高并发写入场景下,ES 的资源消耗(尤其是内存)较大,且 HNSW 的图索引构建较慢。
6. 结语
利用 Elasticsearch 实现相似图片搜索,是"深度学习 + 搜索引擎"结合的经典应用。它不需要极其复杂的架构,就能在百万级、千万级数据规模下实现毫秒级的以图搜图。
如果你正在构建图库系统、电商推荐或内容风控平台,不妨试试 ES 的向量能力,让你的图片"活"起来,变得可被检索!
👇 互动话题 :
你在工作中遇到过哪些非结构化数据搜索的难题?欢迎在评论区留言讨论!
关键词:Elasticsearch, 向量搜索, 以图搜图, 深度学习, ResNet, HNSW