乐趣区

关于数据库:从零到一教你搭建CLIP-以文搜图搜索服务二5-分钟实现原型

在上一篇文章中,咱们理解了对于搜寻技术、以文搜图,以及 CLIP 模型的基础知识。本篇咱们将花上 5 分钟工夫,对这些基础知识进行一次入手实际,疾速构建一个「以文搜图」的搜寻服务原型。
Notebook 链接
https://github.com/towhee-io/…

这里咱们选取“搜萌宠”这个小例子:面对成千上万的萌宠,帮忙用户在海量的图片中疾速找到心仪的那只猫咪或修勾~

让咱们来看看做这样一个原型都须要些什么:

  • 一个宠物的小型图片库。
  • 一个能将宠物图片的语义特色编码成向量的数据处理流水线。
  • 一个能将查问文本的语义特色编码成向量的数据处理流水线。
  • 一个能够撑持向量近邻搜寻的向量数据库。
  • 一段能将上述所有内容串起来的 python 脚本程序。

接下来,咱们会陆续实现这张图的要害组件,开始干活~

装置根底工具包

咱们用到了以下工具:

  • Towhee 用于构建模型推理流水线的框架,对于老手十分敌对。
  • Faiss 高效的向量近邻搜寻库。
  • Gradio 轻量级的机器学习 Demo 构建工具。

创立一个 conda 环境

conda create -n lovely_pet_retrieval python=3.9
conda activate lovely_pet_retrieval

装置依赖

pip install towhee gradio
conda install -c pytorch faiss-cpu

筹备图片库的数据


咱们选取 ImageNet 数据集的子集作为本文所应用的“小型宠物图片库”。首先,下载数据集并解压:

curl -L -O https://github.com/towhee-io/examples/releases/download/data/pet_small.zip

unzip -q -o pet_small.zip

数据集的组织如下:

  • img: 蕴含 2500 张猫狗宠物图片
  • info.csv:蕴含 2500 张图片的根底信息,如图像的编号(id)、图片文件名(file_name)、以及类别(label)。
import pandas as pd
df = pd.read_csv('info.csv')
df.head()

到这里,咱们曾经实现了对于图片库的筹备工作。

将图片的特色编码成向量


咱们通过 Towhee 调用 CLIP 模型推理来生成图像的 Embedding 向量:

import towhee

img_vectors = (towhee.read_csv('info.csv')
      .image_decode['file_name', 'img']()
      .image_text_embedding.clip['img', 'vec'](model_name='clip_vit_b32', modality='image')
      .tensor_normalize['vec','vec']() # normalize vector
      .select['file_name', 'vec']())

这里对代码做一些简要的阐明:

  • read_csv('info.csv')读取了三列数据到 data collection,对应的 schema 为 (id,file_name,label)。
  • image_decode['file_name', 'img']()通过每行的 file_name 读取图片文件,解码并将图片数据放入 img 列。
  • image_text_embedding.clip['img', 'vec'](model_name='clip_vit_b32',modality='image')clip_vit_b32img列的每个图像的语义特色编码成向量,向量放到 vec 列。
  • tensor_normalize['vec','vec']()vec 列的向量数据做归一化解决。
  • select['file_name', 'vec']()选中 file_namevec两列作为最终后果。

创立向量库的索引

咱们应用 Faiss 对图像的 Embedding 向量构建索引:

img_vectors.to_faiss['file_name', 'vec'](findex='./index.bin')

img_vectors 蕴含两列数据,别离是file_namevec。Faiss 对其中的 vec 列构建索引,并将每行的 file_name 与 vec 相关联。在向量搜寻的过程中,file_name 信息会随后果返回。这一步可能会花上一些工夫。

查问文本的向量化

查问文本的向量化过程与图像语义的向量化相似:

req = (towhee.dc['text'](['a samoyed lying down'])
      .image_text_embedding.clip['text', 'vec'](model_name='clip_vit_b32', modality='text')
      .tensor_normalize['vec', 'vec']()
      .select['text','vec']())

这里对代码做一些简要的阐明:

  • dc['text'](['a samoyed lying down'])创立了一个 data collection,蕴含一行一列,列名为text,内容为 ‘a samoyed lying down’。
  • image_text_embedding.clip['text', 'vec'](model_name='clip_vit_b32',modality='text')clip_vit_b32 将文本 ‘query here’ 编码成向量,向量放到 vec 列。留神,这里咱们应用同样的模型(model_name='clip_vit_b32'),但抉择了文本模态(modality='text')。这样能够保障图片和文本的语义向量存在于雷同的向量空间。
  • tensor_normalize['vec','vec']()vec 列的向量数据做归一化解决。
  • select['vec']()选中 textvec 列作为最终后果。

查问

咱们首先定义一个依据查问后果读取图片的函数read_images,用于反对召回后对原始图片的拜访。

import cv2
from towhee.types import Image

def read_images(anns_results):
    imgs = []
    for i in anns_results:
        path = i.key
        imgs.append(Image(cv2.imread(path), 'BGR'))
    return imgs

接下来是查问的流水线:

results = (req.faiss_search['vec', 'results'](findex='./index.bin')
        .runas_op['results', 'result_imgs'](func=read_images)
        .select['text', 'result_imgs']())

results.show()

  • faiss_search['vec', 'results'](findex='./index.bin', k = 5)应用文本对应的 Embedding 向量对图片的向量索引 index.bin 进行查问,找到与文本语义最靠近的 5 张图片,并返回这 5 张图片所对应的文件名results
  • runas_op['results', 'result_imgs'](func=read_images)其中的 read_images 是咱们定义的图片读取函数,咱们应用 runas_op 将这个函数结构为 Towhee 推理流水线上的一个算子节点。这个算子依据输出的文件名读取图片。
  • select['text', 'result_imgs']()选取 textresult_imgs两列作为后果。

到这一步,咱们以文搜图的残缺流程就走完了,接下来,咱们应用 Grado,将下面的代码包装成一个 demo。

应用 Gradio 打造 demo

首先,咱们应用 Towhee 将查问过程组织成一个函数:

search_function = (towhee.dummy_input()
        .image_text_embedding.clip(model_name='clip_vit_b32', modality='text')
        .tensor_normalize()
        .faiss_search(findex='./index.bin')
        .runas_op(func=lambda results: [x.key for x in results])
        .as_function())

而后,创立基于 Gradio 创立 demo 程序:

import gradio

interface = gradio.Interface(search_function, 
                             gradio.inputs.Textbox(lines=1),
                             [gradio.outputs.Image(type="file", label=None) for _ in range(5)]
                            )

interface.launch(inline=True, share=True)

Gradio 为咱们提供了一个 Web UI,点击 URL 进行拜访(或间接与 notebook 下方呈现的界面进行交互):

点击这个 URL 链接,就会跳转到咱们「以文搜图」的交互界面,输出你想要的文字,即可呈现出与文字对应的图片。例如,咱们输出 “puppy Corgi”(柯基小奶狗)即可失去:

能够看到 CLIP 对于文本和图像的语义编码还是很粗疏的,像“小奶狗”这样的概念也被蕴含在了图片与文本的 Embedding 向量中。

总结

在本篇文章中,咱们构建了一个以文搜图的服务原型(只管十分小,但五脏俱全),并应用 Gradio 创立了可交互的 demo 程序。

在明天的这个原型中,咱们用到了 2500 张图片,并用 Faiss 库对向量构建索引。但在实在的生产环境中,向量底库的数据量个别在千万级到十亿级,仅应用 Faiss 库难以满足大规模向量搜寻所须要的性能、可扩展性、可靠性。在下一篇中,咱们将进入进阶内容:学习应用 Milvus 向量数据库进行大规模向量的存储、索引、查问。敬请期待!

更多我的项目更新及具体内容请关注咱们的我的项目(https://github.com/towhee-io/…),您的关注是咱们用爱发电的弱小能源,欢送 star, fork, slack 三连 :)


作者简介

余卓然,Zilliz 算法实习
郭人通,合伙人兼技术总监
陈室余,零碎工程师

编辑简介

熊烨,社区经营实习

退出移动版