基于图像的搜索引擎
eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1cXFlT28hcdTAwMWF951ekuK9jTe/LvLFcdTAwMDXMbnZ871RK2MKWLUuKLGPMVP77/dpcdTAwMTBLSPJCwCCm4lRcdTAwMTFQy9Kn7u+cPqdcdTAwMTf9s/Lly2o8XG6d1b++rDr3XHLbc5uRPVxc/cNcdTAwMWO/c6K+XHUwMDFi+FBExn/3g0HUXHUwMDE4n9mO47D/159/Jt+wXHUwMDFhQe/xW47n9Fx1MDAxYz/uw3n/hb+/fPln/DN1n8hpxLbf8pzxXHUwMDE3xkXJrTDh2aOHgT++LVZMc82FXHUwMDE0kzNcXL/p3Jtr3lx1MDAxMNRLLuf2NyGM2GlC0a3t9Z2kxFx1MDAxY1o9P+TsXFxccuvVQe9Ir6GL3aPGdTX5+q3reafxyFx1MDAxYkfbXHUwMDBm4Fx0k7J+XHUwMDFjXHUwMDA1XefSbcZtKGWZ49O+XHUwMDE1XHUwMDA1g1bbd/qmVtDkaFx1MDAxMNpccjdcdTAwMWWZR0PJ0ceqSZ9nnrDCXHUwMDE01Vx1MDAxNsJcdTAwMWEzzrVEXHUwMDEyT4rNXHUwMDA1XHUwMDE0XHUwMDE3yEJacikkJVxik0xgXHUwMDFigVx1MDAxN0QmsP/cjj9JaDd2o9uC+Pxmclx1MDAwZVx1MDAxYX+Sc4ZPjyuwsJikXHUwMDFhIaGlUDx5/LbjttoxnFx1MDAwMje3XHUwMDE0VkhrSrkgVNAkXHUwMDEyZ9wgmFx1MDAxMabgI9SkxNw/rDbHOfN30lxmkd1zquYr/sDz0nXpN5/q8mduJdlFn478SFx1MDAxZdCcv5XKyuRcdTAwMGWDsGk/Zlx0llgriqVcdTAwMTKIJk/luX43e3svaHSTxFpJ3euFic45npbpUiBGXHUwMDE0V6gw0f3FXHUwMDEzPWhcdTAwMWP31El8QVx1MDAwZnUj2Lql9LB92S57oktcIiyNNOJEMkgglkl0iS1BXHRHXHUwMDFjM6GweE2ix5Ht90M7gszIJztG2pJcYqLQXHUwMDAy8CRcdTAwMDB1+WxcdTAwMTfcYpQzXGY/OCGYk2yyXHUwMDFitlJEksJUT5J372AjaG2MuuFB73I7btVcdTAwMWX2KmdXk5DghLrPvIe9m5uafz7a9p1atX2Nr1afypdcdTAwMGaZyXeSb09yLKyFtb2aUod7Z3731FOuXHUwMDE3XGaDVOwpQNhRXHUwMDE0XGZXJyU/cvFcdTAwMTdcdTAwMDBcdTAwMTJcdTAwMDHpv1x1MDAxNSBj5z4uxmIqy7K9juRUS4aFLlx1MDAwNGP4gl5nUFx1MDAxOX7bPOBniPrd46/7VXaFz0pcdTAwMGZGQIGmXHUwMDFjKY5cdTAwMDRGKTZ/XHUwMDA0o2ZcdTAwMDBGoVx1MDAxOVKMwGlLXHUwMDAzo6RcdTAwMTbPw49gi1x1MDAwMzQpkVRcdTAwMTFGRFx1MDAwZX6QQ4hLpVJcdTAwMGZaQlx1MDAwMJLX9FnQY2EqX9JnJcFcdTAwMDZ+fOo+PGa5pVx0XFyPaqY4kFx1MDAxOVbPzvpq91xcz7R40v5jLEGzbdqx3XdSTWZcbtY8t2WAtdqAXHUwMDA3cKJnmItdXHUwMDEwjZNcdTAwMTPiIIWgXHUwMDA23Mp2fSfK12BcdTAwMTC5Lde3vbNpt7VcdTAwMDdxcOL0XHUwMDFmXHUwMDFmJ45cdTAwMDZOukacnZ8pgy3ya1x1MDAxNMGoylx1MDAxZZ5QXHUwMDA0Z9BdQ91cdTAwMTVTxPfFKeKENd1u4240PLvs7p6s2ztXt5VvZadcYk6UZUhcdTAwMTKDKkVSJz3ymFwiXHUwMDA0plx1MDAxNshByZDmXHUwMDE0xON0ikCqkVx1MDAxNp1FwjRLIz/pgUqLpp3DhCG0xVx1MDAwNYbAMOLKaGSZY1xirqRmppeew1x1MDAxMP9yXHUwMDAyXHUwMDAw/EMlMOjtmEZQXHUwMDFmqcqcQ1x1MDAwMM1HJPbfm1x1MDAwMfL3fSNcbpgt2zWealA1OFRcdTAwMDWZlnRDaVx1MDAxYYhcdTAwMTanXHUwMDAxp9dcdTAwMWTq9nm1M8Lfo7C2vlx1MDAxNq7fdEpPXHUwMDAzXGZbilwiRaE7XHUwMDE2NEtcdTAwMDNcdTAwMTJoXHUwMDAwJDJcdTAwMDMlQbHCy1NcbppYiFx1MDAwYiG5XHUwMDEyglOqXHUwMDBiSIGDameUYKlcdTAwMTUh4LJStfRIXG5cdTAwMTAlYdBcdTAwMTPO44ST0U5/92GLo7VTR+jdQ3tcdTAwMWKtVz+JbD9cdTAwMTRXfu9uva4uKi7h1fb3blXufDLZrlx0mYpFhrHS0C1cdTAwMTViMV5cdTAwMWOLXHUwMDE39c7N90r1WFx1MDAxZl5cXO9ddGLn+n5YLz1cdTAwMTYpscCRMs5cdTAwMTlcdTAwMDVW11x1MDAxOSxcdTAwMTJkcUWF6Vx1MDAxM1x1MDAxMZWvXHUwMDFhK/pcdTAwMDXVzqiFXGJcdTAwMDdHzSA0XHUwMDA1Ueb6ZDBcdTAwMTRcdTAwMTJRmlx1MDAxYegrIf5cdTAwMTbotGfh715fqdrRjnPQP633zr+ygyqS+4vh749Z110mrj/aa2z1bpym0/yfX+3ZLee9XHUwMDA1x7S7v5HseGyNXHUwMDAymmNcdTAwMDDSqdZDKcVcdTAwMTj0Y8VDhYPFeW52PpaU5yRWXHUwMDE2wlx1MDAwNDEyXHUwMDFlXHUwMDEzz/JcdTAwMWPjllJSQbJxzJc4Uig5XHUwMDE4IIkglbFpkCSfXHUwMDEzyWE8XHUwMDEwJ4bUtFx1MDAxNlx1MDAwNCeR/pRcdTAwMWNcYiFcdTAwMTAkKS3yyWzIy/v8VHvYUbxcdTAwMGV56/qt54E9TSaNXHUwMDFmZJXvVqpOvXPssVx1MDAxZdry6T1cdTAwMGVblWdsf1x1MDAxYjRcdTAwMDbjbLIwYFx1MDAwNlGNQHUqrpVIndSyQ/OoliBcdTAwMTRpgjmFXHUwMDFjXHUwMDEyMi+mXHUwMDFjvzk/pNni4HlIXFxQZoaCidRUQPeXi8lcZjdcdTAwMTPouVx1MDAxMVx1MDAwM1x1MDAxM4YpZ7mYPLtcdTAwMWZvXHUwMDA0vZ5cdTAwMWJDzVx1MDAxZlx1MDAwN65cdTAwMWZna3hcXJVrhknajp1rfnimdFmWckJzxef5lvz2JVx1MDAwMd/4j8nvf/9RePZUUJhPJYeH5HIr6f+nkaXjeW7YnzKDOH0w11xiQok0Lp5BvFucLWfnYknZUmFtXHUwMDExXHUwMDEwXFxCm/klnei+x4FcdTAwMWGtQFx1MDAxNVx1MDAwMmKQQEJcbplcdOxcclx1MDAxZJqC/lx1MDAxZlx1MDAwMfiIolx1MDAxY6fQmdCl4lx1MDAxNlVIMkRcdTAwMDTgXHUwMDE3tHyOLqlcdTAwMTnUpXNHbVx1MDAwNnvH0frGXHUwMDAzXHUwMDFidLtRvVx1MDAxOVxc43rroPXvV4hzldx7OTRKpzo0wZFcdTAwMDY8XHUwMDE2XCLxfnEk7t/E/kV9XHUwMDE0r1x1MDAxZFx1MDAxZY/ujsiF+Nbha2VHolx1MDAwNFxmUHBnRIP6hf4go1sospCZNlx1MDAwNOeG0Fx1MDAxMoVcdTAwMGInvzarXHUwMDAyXHUwMDFjXG5qZb5YKTv6yut0PsTgvLevIXg6OSiilFx1MDAxMLp4KPVhcXqYbYdLSlx1MDAwZoxJS1GqhSRcdTAwMTjBv7ytXHUwMDAxfcg4XHUwMDAz4UT48oZSsUCWhm5YMY1BvkJ75MlCXGIjtrFCglHEkEpF80hcdTAwMTaUXGIz5vvb2LyJi6BcXFxu4F6NucKY8WRgYOJcIjDg0cxDKDBcdTAwMWGcgfn9RWuzjTqK74RrXHUwMDE3w+tcdTAwMTFvbp9cZpAzqkxcdEpxXHUwMDAxqppJsOCmpXk+KGaZkMBcdTAwMGVAd4chb3kuqE/lbZiypFx1MDAxNOAlJFx1MDAxMkSJtMFcdTAwMDRzg5VcdTAwMDXMJVxiXGJpZlaOKDX3glORZj7AXHUwMDA2oFx1MDAxNTg0PHTIXHUwMDFh2p4l11tJ//9iXG6mKGWFckNLWoA3o8Wr0PDiXHUwMDE0PHuBUEkp2Cx1JEBnhGKGkU467kevhIxCXHUwMDAzeUZcdTAwMThIIbY8XG4mXFxZRiRcImOWmJCJK5swMDahUPBzXHUwMDAyS8owljmzRDSINdOh/qbgaWw3e6Fkiu0qyMKg5Sj0vFxujDT8SMxpQne/Rrm8u3Ghb9v7w+ODuzo68E56/lqnmHLhzpqC/zVTqYZYi1x1MDAxOFx1MDAxNyhIXHUwMDAxXHUwMDFkSzPXXHUwMDAyyfOpXHUwMDE5dypcdTAwMGXMJ4+AXHUwMDE38qPRs4X2XHUwMDE1p8gvS49cdTAwMDRpXHUwMDEwNkRcdTAwMTTTI1ucXHUwMDFm73prXXFd47V4r75cdTAwMWJsXHLXOpth6fmRXCIzWiS4XHUwMDE5e9dSZ0beXHUwMDA1NFx1MDAxN3RcdTAwMWaUaKVBoupMYG/Hj1x1MDAwMltAzlx1MDAxOG6EjchcdTAwMTSiYEn6WKJcdTAwMDL7SVwixJhPsvyoJSCE8t9cdTAwMTI1XHUwMDBm8Cklb0xcdTAwMWTPyt6UNyrTXHUwMDEzxHxcdTAwMDRcdTAwMDGxrKFcdTAwMTJcdTAwMDXmVEgl516PWFxugbSjXGbSmyNO1DMmyqXaXHUwMDBiiWj20qP0yqJcZlx1MDAxYnFcbqEgopJcdTAwMTROk5FanIykuN8+JV3dcavRKKC7W161ykpPRpxZnEPfoLWZPElE0qNf1lDKODRcdTAwMTl4arBOy/PL0Fx1MDAxMTHCkVx1MDAxNFx1MDAxOFHNi7hIYpBcdTAwMGZgXHUwMDFkQMVLqlx0ydllXGa2X2Mw0nO4aERseXS9v0kuO43+zeUl2q+Htd9Lj95tYDs90pHdvWNaXHUwMDFmXHUwMDAx/lx1MDAwYrG4vThcdTAwMTZnW/CyYlx1MDAxMdwq+GNCpaZmXHUwMDBi2HMsgse1JLRcdTAwMTEwrpmXX1x1MDAxYVx1MDAxNFxy5PPoy648yu9N44BNw9zzNux8KPxeOa/0XHRXXGKxZ2fNXHUwMDE4Nz9x4sh17swqXHUwMDFkqFxu+PXdlyXPiuA9XHUwMDE2KFx1MDAxM5Za5J5VXHRcdTAwMWNRSVx1MDAwMZ2FzHS0ODN9PTy4XHUwMDBmak3vdj+MXHS7cf1NWd8uPTNx4Fx1MDAxZbNCXHUwMDE5XGaLYiSjXHUwMDEyuKZgWcAsm1x1MDAxMS80a59CXHUwMDE59lx1MDAxNUpJpCbp7Y/FPHU3bJ5HXHUwMDFidXFWU/jr9yN5ft05OP8kMuFfsLGQsOlrUaCMK6xZ4VxuZXzxgrUoM4eNylxuRkYsrcZD6IrjlPV+XHUwMDFhX1x1MDAxNZaSXFxxoZVkcnnjXHUwMDA3qkCjz12fXGbCXHUwMDA2pP78LUNcdTAwMWZcbr5XioRlgu+jJ9fHXHUwMDBieccreT9o8+KMXHUwMDAwlr2Nkevp79dgkPZGtFx1MDAxN1x1MDAxYpf64ozUvMfbV+tdXHUwMDE5XHUwMDFlVMhu/+zebu9He2VnJG62XHUwMDA1XHTzzlxuKTXDXHUwMDE5RuJSWEKYRS9cdTAwMTL6jVk7nV+xjVx1MDAxMVNuXHUwMDExLtUvbmUkhFLFXHUwMDEw+7TzPFx1MDAxZr2VMVxiXHUwMDFk33b/bHhuWLlz44pnRy2nXHUwMDEy2nGjnVx1MDAxZdB/XHUwMDFmklgwmGVcdTAwMTOGTu1cdTAwMWXKXHUwMDEwXHUwMDA2dN9aXHUwMDFiMVXIXHUwMDE3zcX5otZlqtPvtlqNjnN6veXQbVXlZeNcdTAwMGJhXHUwMDExXHUwMDAx6Vx1MDAwNJJcXEB6XHUwMDExePhcZoFIXHUwMDEwLVx1MDAxOCqEXHUwMDAxXHUwMDA0eXpS+GlcdTAwMDMkXFyAaYpBU1x1MDAwMNcsZ1x1MDAxZvQrXHRcdTAwMDSBL5FY6nmjXHUwMDFmJdYty1xcNPubnspHT1xuTZ1cdTAwMTMhyLxlStPiN7dg71x1MDAwNTO0+1x1MDAxYjudYbxPb/jZ3TGrXFx/651cdTAwMGbKyE9aXG7BpcbprctPfotoy+wsgVx1MDAwNFx1MDAwNi2jslMkSFlISFx1MDAwNOqQMzFjwfHHvKNcdTAwMDFpXHUwMDAx7PqJXHUwMDE3sFx1MDAwMHNInnqByO93NCyLXHUwMDBmXHUwMDA0mfpWNa3Mgnosi+3NXHUwMDBitoRTN7pcdTAwMWW27uLOyVx1MDAwM1x1MDAxNfvhYWe0tXlbNjrIXHK4XHUwMDEwamGlOZFcXHDFsmuKqbIoheOESMWWQ1x1MDAwMFx1MDAxMFx1MDAwMCVoMVx1MDAwZcit0cBaakokm/sqtVx1MDAxMnNcdTAwMDA2q1/fmFx1MDAwM+izk2ZQwK3t9t9cdTAwMWT/mZu+XHUwMDE0/CtPLbZqh+FpXGa1OWmV1TvXXHUwMDE5rk9PvpWnRjJgdMaN+WPlx/9cdTAwMDFqSMcnIn0=
Dataset datasets Embeded Image Image Retrieved entries Embedded Dataset openai/clip-vit-large-patch14 openai/clip-vit-large-patch14 datasets faiss
在视觉数据领域,高效准确的图像检索是关键。这篇博客文章提供了一步步的指南,教你如何使用开源工具构建一个基于图像的搜索引擎。学完本文,你将掌握创建强大、可定制的搜索引擎以实现精确图像检索的技能。这篇博客将助你以全新且激动人心的方式探索视觉数据。准备好投入图像检索的世界,使用开源工具解锁高效视觉数据搜索的秘密!
嵌入数据
嵌入对于构建基于图像的搜索引擎至关重要,因为它将图像转换为易于比较和处理的格式。此步骤通过将图像表示为高维空间中的唯一向量来实现高效图像检索。
我们首先下载所需的库。至于 `loadimg`,它是我开发的一个 Python 库,用于轻松读取和转换图像。如果你愿意,可以跳过这个库。如果你对它感兴趣并希望为其发展做出贡献,可以查看我的 GitHub 仓库 。
pip install -qU datasets accelerate loadimg faiss-cpu
然后我们可以继续加载我们的数据集。
from datasets import load_dataset
data = load_dataset("not-lain/pokemon" ,split="train" )
data
>>> Dataset({
features: ['image' , 'text' ],
num_rows: 898
})
之后,我们来加载模型,我这里用的是 CLIP,但你可以用任何其他类似的模型。
import torch
from transformers import AutoProcessor, AutoModelForZeroShotImageClassification
device = 'cuda' if torch.cuda.is_available() else 'cpu'
processor = AutoProcessor.from_pretrained("openai/clip-vit-large-patch14" )
model = AutoModelForZeroShotImageClassification.from_pretrained("openai/clip-vit-large-patch14" , device_map = device)
现在到了最重要的部分,我们将嵌入数据集并把嵌入数据添加到新列`embeddings`中。建议你在此处使用GPU或任何其他加速器,因为这是一个非常缓慢的过程。
def embed (batch ):
pixel_values = processor(images = batch["image" ], return_tensors="pt" )['pixel_values' ]
pixel_values = pixel_values.to(device)
img_emb = model.get_image_features(pixel_values)
batch["embeddings" ] = img_emb
return batch
embedded_dataset = dataset.map (embed, batched=True , batch_size=16 )
建议将嵌入的数据存储在数据库中,无论是本地的、HF、Pinecone、ChromaDB 还是任何其他替代方案,以避免再次嵌入数据集。
💡提示
尽管这与我们这里的工作无关,但你也可以使用相同的模型来创建文本类型数据的嵌入。首先通过 `tokens = processor(text = "some text here", padding=True, return_tensors="pt").to(device)` 处理输入,然后将其传递给你的模型,通过 `text_emb = model.get_text_features(**tokens)` 创建嵌入。
embedded_dataset.push_to_hub("not-lain/embedded-pokemon" )
检索图像
一旦你的数据集嵌入并存储在数据库中,我们现在可以开始定义检索逻辑。我们首先需要在此处加载与上一节中使用的相似模型。
import torch
from transformers import AutoProcessor, AutoModelForZeroShotImageClassification
device = 'cuda' if torch.cuda.is_available() else 'cpu'
processor = AutoProcessor.from_pretrained("openai/clip-vit-large-patch14" )
model = AutoModelForZeroShotImageClassification.from_pretrained("openai/clip-vit-large-patch14" , device_map = device)
我们还需要加载我们的嵌入式 数据集。
from datasets import load_dataset
dataset = load_dataset("not-lain/embedded-pokemon" , split="train" )
你需要给嵌入列添加一个 Faiss 索引,以便将其设置为相似性搜索索引。
Faiss 是一个用于高效相似性搜索和密集向量聚类的库,它对于大规模图像检索任务特别有用。通过向嵌入列添加 Faiss 索引,可以实现快速准确的最近邻搜索,从而有效地根据图像嵌入检索相似图像。这一步显著提高了图像搜索引擎的性能,尤其是在处理大量图像时。
dataset = dataset.add_faiss_index("embeddings" )
现在要检索最相似的图像,你需要首先创建新图像的嵌入,然后从数据集中检索最相似的条目。
import numpy as np
def search (query: str , k: int = 4 ):
"""a function that embeds a new image and returns the most probable results"""
pixel_values = processor(images = query, return_tensors="pt" )['pixel_values' ]
pixel_values = pixel_values.to(device)
img_emb = model.get_image_features(pixel_values)[0 ]
img_emb = img_emb.cpu().detach().numpy()
scores, retrieved_examples = dataset.get_nearest_examples(
"embeddings" , img_emb,
k=k
)
return retrieved_examples
我们来测试一下我们的算法,你可以从加载一张图片开始。
from loadimg import load_img
image = load_img("https://img.pokemondb.net/artwork/large/charmander.jpg" )
image
之后,你可以检索最相似的条目。
这些条目按降序排列,第一个条目与我们的输入图像最相似。
retrieved_examples = search(image)
我们来可视化一下结果。
import matplotlib.pyplot as plt
f, axarr = plt.subplots(2 ,2 )
for index in range (4 ):
i,j = index//2 , index%2
axarr[i,j].set_title(retrieved_examples["text" ][index])
axarr[i,j].imshow(retrieved_examples["image" ][index])
axarr[i,j].axis('off' )
plt.show()
演示
现在让我们把所有东西整合到一个应用程序中,看看我们的工作效果如何。你可以将以下应用程序视为一个很好的参考:https://huggingface.co/spaces/not-lain/image-retriever
致谢
我在此感谢 Pinecone 的 文档 ,它们在开发本博客中使用的脚本方面给予了我极大的帮助 🌲
我要感谢 HuggingFace Discord 服务器中所有支持我工作的人,特别是 lunarflu 、christopher 和 tomaarsen ❤️
如果你喜欢这篇博客文章,请考虑点赞,这将有助于我展示我的工作 🤗
最后,如果你想请求其他博客文章,可以通过 Twitter 、电子邮件 或 LinkedIn 与我联系 ✉️