本帖最后由 mikemelon 于 2026-7-23 21:42 编辑
[md]
图像语义搜索用到了JINA的最新开源大模型jina-embeddings-v5-text-small-retrieval,在HF上地址:
https://hf-mirror.com/jinaai/jina-embeddings-v5-omni-small-retrieval
它可以将文本、图像、视频或音频进行多模态嵌入到一个共享向量空间中,因此可以使用任何模态进行搜索。
下载该模型,需要先安装Python包huggingface-hub(我使用Python 3.9安装的是huggingface_hub的1.8.0):
[Bash shell] 纯文本查看 复制代码 pip install huggingface_hub==1.8.0
可以从hf-mirror镜像下载:
[Bash shell] 纯文本查看 复制代码 set HF_ENDPOINT=https://hf-mirror.com
huggingface_hub包带有hf.exe命令行工具,利用它将jina-embeddings-v5-text-small-retrieval模型下载到本地,
下载命令如下(为节省空间,不让它使用默认的下载路径,--local-dir就是设置到E:\Models\jina-embeddings-v5-omni-small-retrieval
[Bash shell] 纯文本查看 复制代码 hf download jinaai/jina-embeddings-v5-omni-small-retrieval --local-dir E:\Models\jina-embeddings-v5-omni-small-retrieval
等待下载完毕,全部目录所占空间不到3G。
提示:如果下载速度较慢,可以 到huggingface网站 https://huggingface.co/ 上注册,创建并复制Access Token,
然后用hf auth login命令,登录后填入该AccessToken,可加速。
由于该大模型本身并不支持索引,因此可用FAISS建立图像文件索引并保存在本地,如下是建立索引的build_index.py代码:
[Python] 纯文本查看 复制代码 from sentence_transformers import SentenceTransformer
import torch
from PIL import Image
import numpy as np
import faiss
import pickle
from pathlib import Path
from tqdm import tqdm
LOCAL_MODEL_PATH = r"E:\Models\jina-embeddings-v5-omni-small-retrieval"
try:
model = SentenceTransformer(
LOCAL_MODEL_PATH,
trust_remote_code=True,
model_kwargs={
"torch_dtype": torch.float16,
}
)
print(f"✅ 已从本地加载模型: {LOCAL_MODEL_PATH}")
except Exception as e:
print(f"本地加载失败: {e}")
model = None
# ================== 配置区域 ==================
search_folders = [
Path(r"E:\照片\小米手机导出"),
Path(r"E:\照片\饭饭照片\2016-05"),
Path(r"E:\图片\Camera"),
]
image_extensions = {".jpg", ".jpeg", ".png", ".webp", ".bmp", ".tiff"}
# =============================================
embeddings = []
metadata = []
for folder in search_folders:
print(f"正在扫描: {folder}")
for img_path in tqdm(list(folder.rglob("*"))):
if img_path.suffix.lower() in image_extensions:
try:
# # 可选:跳过过大图片(防止内存爆炸)
# if img_path.stat().st_size > 20 * 1024 * 1024: # >20MB
# continue
img = Image.open(img_path).convert("RGB")
emb = model.encode(img, show_progress_bar=False)
embeddings.append(emb)
metadata.append({
"path": str(img_path),
"name": img_path.name,
"folder": str(img_path.parent)
})
except Exception as e:
print(f"跳过损坏图片: {img_path} - {e}")
# 建立索引
embeddings = np.array(embeddings).astype('float32')
faiss.normalize_L2(embeddings)
index = faiss.IndexFlatIP(embeddings.shape[1])
index.add(embeddings)
# 保存
faiss.write_index(index, "my_image_search.index")
with open("image_metadata.pkl", "wb") as f:
pickle.dump(metadata, f)
print(f"✅ 索引建立完成!共处理 {len(embeddings)} 张图片")
上面代码生成两个文件:
(1)my_image_search.index是FAISS索引文件
(2)image_metadata.pkl是图像的元数据(这里仅包括图像路径、文件名等信息)
建立索引的时间,我的机器上大概1到2秒一张图片,一开始建议可以先测试几个小的图片目录。
上述两个文件,可以用于后续搜索search.py程序:
[Python] 纯文本查看 复制代码 import faiss
import pickle
import numpy as np
from sentence_transformers import SentenceTransformer
from PIL import Image
import torch
# ================== 加载模型和文件 ==================
LOCAL_MODEL_PATH = r"E:\Models\jina-embeddings-v5-omni-small-retrieval"
try:
model = SentenceTransformer(
LOCAL_MODEL_PATH,
trust_remote_code=True,
model_kwargs={
"torch_dtype": torch.float16,
}
)
print(f"✅ 已从本地加载模型: {LOCAL_MODEL_PATH}")
except Exception as e:
print(f"本地加载失败: {e}")
model = None
# 加载索引和元数据
index = faiss.read_index("my_image_search.index")
with open("image_metadata.pkl", "rb") as f:
metadata = pickle.load(f)
print(f"索引加载成功!共 {len(metadata)} 张图片\n")
# ================================================
def search(query, k=5, is_image=False):
"""查询函数"""
if is_image:
# # 图像查询
# img = Image.open(query).convert("RGB")
# query_emb = model.encode(img) # print(f"使用图片查询: {query}")
# 图像查询
if isinstance(query, str): # 如果传入的是路径
img = Image.open(query).convert("RGB")
else:
img = query.convert("RGB") if hasattr(query, "convert") else query
print(f"使用图片查询: {query}")
query_emb = model.encode([img], convert_to_numpy=True)[0]
else:
# 文本查询
query_emb = model.encode(query)
print(f"使用文本查询: {query}")
# 搜索
query_emb = np.array([query_emb]).astype('float32')
faiss.normalize_L2(query_emb)
distances, indices = index.search(query_emb, k)
print(f"\n找到 Top-{k} 结果:\n")
for i, (idx, score) in enumerate(zip(indices[0], 1-distances[0])):
meta = metadata[idx]
print(f"{i + 1}. 相似度: {score:.4f}")
print(f" 文件名: {meta['name']}")
print(f" 路径: {meta['path']}")
print("-" * 80)
# ================== 使用示例 ==================
# 示例1:文本搜索图片
search("白色雪人", k=6)
# 示例2:用图片搜索相似图片
# search(r"C:\test\query.jpg", k=5, is_image=True)
上述程序支持两种方式搜索图片(如上代码最后的示例1和示例2):
(1)用文本描述搜索图片
(2)用图片搜索相似图片
其实,对于这个多模态嵌入大模型,还可以用音频、视频来搜索。
还可以给这俩个程序用Flask简单做个web网站,提供图片预览和下载,可能使用更方便些。
这里只是示意程序,且空间有限,就不再展示了,只给出几个运行结果截图。
|