在机器学习领域,模型曾经存在限制——它们一次只能处理一种类型的数据。然而,机器学习的最终愿望是与人类大脑的认知能力相媲美,人类大脑可以毫不费力地同时理解各种数据模式。最近的突破(以 GPT-4V 等模型为代表)现在已经证明了同时处理多种数据模式的卓越能力。这为开发人员打造能够无缝管理不同类型数据的人工智能应用程序(称为多模式应用程序)提供了令人兴奋的可能性。

多模态图像搜索是一个引人注目且广受欢迎的用例。它允许用户通过分析特征或视觉内容来找到相似的图像。由于计算机视觉和深度学习的快速发展,图像搜索变得异常强大。

在本文中,我们将使用 Hugging Face 库中的模型构建多模式图像搜索应用程序。在深入实际实施之前,让我们先回顾一些基础知识,为我们的探索奠定基础。

什么是多模式系统?

多模式系统是指可以使用多种交互或通信模式的任何系统。它意味着一个系统可以同时处理和理解不同类型的输入,例如文本、图像、语音,有时甚至是触摸或手势,并且还可以以多种方式返回结果。

例如,GPT-4V(打开新窗口,由OpenAI开发,是一种先进的多模态模型,可以同时处理文本和图像输入的多种“模态” .当提供带有描述性查询的图像时,模型可以根据提供的文本分析视觉内容。

什么是多模态嵌入?

多模态嵌入是一种先进的机器学习技术,是以矢量格式生成图像、文本和音频等多种模态的数字表示的过程。与在向量空间中仅表示一种数据类型的基本嵌入技术不同,多模态嵌入可以在统一的向量空间中表示各种数据类型。例如,这允许将文本描述与相应的图像相关联。借助多模态嵌入,系统可以分析图像并将其与相关文本描述相关联,反之亦然。

现在,我们来讨论一下如何开发这个项目以及我们将使用的技术。

工具和技术

我们将使用CLIP(打开新窗口MyScale(打开新窗口)Unsplash-25k 数据集 (打开新窗口) 在这个项目中。让我们详细看看它们。

  • CLIP:您将使用预先训练的多模态 CLIP (打开新窗口)由 Hugging Face 的 OpenAI 开发。该模型将用于集成文本和图像。
  • MyScale:MyScale 是一个 SQL 矢量数据库,用于以优化的方式存储和处理结构化和非结构化数据。您将使用 MyScale 存储矢量嵌入并查询相关图像。
  • Unsplash-25k 数据集:Unsplash 提供的数据集包含约 25,000 张图像。其中包括一些复杂的场景和物体。

如何设置 Hugging Face 和 MyScale

要在本地环境中开始使用 Hugging Face 和 MyScale,您需要安装一些 Python 包。打开终端并输入以下 pip 命令:

 

pip 安装数据集 clickhouse-connect 请求变压器 torch tqdm

安装完成后,您可以通过在终端中输入以下命令进行验证。

 

pip 冻结 | egrep '(数据集|clickhouse-connect|请求|变压器|火炬|tqdm)'

它将打印新安装的依赖项及其版本。

下载并加载数据集

第一步是下载数据集并将其提取到本地。您可以通过在终端中输入以下命令来完成此操作。

 

# 下载数据集
wget https://unsplash-datasets.s3.amazonaws.com/lite/latest/unsplash-research-dataset-lite-latest.zip

# 将下载的文件解压到临时目录中
解压 unsplash-research-dataset-lite-latest.zip -d tmp

让我们将提取的文件中所需的数据加载到 Python 数据框中。

Python

 

# 导入 pandas
将 pandas 导入为 pd
# 从目录加载照片文件
df_photos = pd.read_csv("tmp/photos.tsv", sep='\t', header=0)
df_photos

我们正在从目录加载 photos 文件,其中包含有关数据集中照片的信息。照片个人资料如下所示:

<表格样式=“最大宽度:100%;宽度:自动;表格布局:固定;显示:表格;”宽度=“自动”>
<标题>

photo_id photo_url photo_image_url


<正文>

xapxF7PcOzU https://unsplash.com/photos/wud-eV6Vpwo https://images.unsplash.com/photo-143924685475… psIMdj26lgw https://unsplash.com/photos/psIMdj26lgw https://images.unsplash.com/photo-144077331099…

photo_urlphoto_image_url 之间的区别在于 photo_url 包含图像描述页面的 URL,告诉作者和其他人照片的元信息。 photo_image_url 仅包含图像的 URL,我们将使用它来下载图像。

加载模型并获取嵌入

加载数据集后,我们首先加载 clip-vit -base-patch32 (打开新窗口)建模并编写一个 Python 函数以将图像转换为矢量嵌入。该函数将使用 CLIP 模型来表示嵌入。

Python

 

# 导入 pytorch
进口火炬
# 导入变压器以从 Hugging Face 加载模型和处理器
从 Transformer 导入 CLIPProcessor、CLIPModel

# 从 Hugging Face 加载 CLIP 模型
模型 = CLIPModel.from_pretrained('openai/clip-vit-base-patch32')
# 加载用于预处理图像的处理器并使它们与模型兼容
处理器 = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")

# 定义方法
def create_embeddings(图像=无,文本=无):
    # 初始化嵌入
    图像嵌入=无
    文本嵌入=无

    # 处理图像(如果提供)
    如果图像不是无:
        image_embeddings = extract_image_features(图像)
        image_embeddings = torch.tensor(image_embeddings)
        image_embeddings = image_embeddings / image_embeddings.norm(dim=-1, keepdim=True)

    # 处理文本(如果提供)
    如果文本不是“无”:
        text_inputs = 处理器(text=[text], return_tensors="pt", padding=True)
        使用 torch.no_grad():
            text_outputs = model.get_text_features(**text_inputs)
            text_embeddings = text_outputs / text_outputs.norm(dim=-1, keepdim=True)
            text_embeddings = text_embeddings.squeeze(0).tolist()

    # 如果同时提供了图像和文本,则合并嵌入,并标准化
    如果 image_embeddings 不是 None 并且 text_embeddings 不是 None:
        组合嵌入 = (图像嵌入 + torch.tensor(文本嵌入)) / 2
        组合嵌入 = 组合嵌入 / 组合嵌入.norm(dim=-1, keepdim=True)
        返回combined_embeddings.tolist()

    # 仅返回图像或文本嵌入(如果提供了其中之一)
如果 text_embeddings 是 None,则返回 image_embeddings.tolist() text_embeddings

上面的代码旨在单独或同时处理文本和图像输入,并返回相应的嵌入。让我们看看它们是如何工作的:

  • 如果您同时提供图像和文本,代码将返回一个结合了两者嵌入的向量。
  • 如果您提供文本或图像(但不能同时提供两者),则代码仅返回所提供文本或图像的嵌入。

注意:我们使用一种基本方法来合并两个嵌入,只是为了关注多模式概念。但是有一些更好的方法来合并嵌入,例如串联和注意机制。

我们将加载、下载数据集中的前 1000 张图像并将其传递到上面的 create_embeddings 函数。返回的嵌入将存储在新列 photo_embed 中。

Python

 

# 导入Image moduke进行图像处理
从 PIL 导入图像
# 导入requests模块用于发出HTTP请求
导入请求
# 导入tqdm用于处理条形图可视化
从 tqdm.auto 导入 tqdm

# 获取前1000张图像
photo_ids = df_photos['photo_id'][:1000]
# 过滤DataFrame以获取所需的列
df_photos = df_photos.loc[photo_ids.index, ['photo_id', 'photo_image_url']]
# 创建一个会话来发出HTTP请求
会话 = requests.Session()

# 定义用于下载和获取嵌入的 Python 函数
def process_image(url):
    尝试:
        # 发出 GET 请求来下载图像
        响应 = session.get(url, 流=True)
        响应.raise_for_status()
        图像 = Image.open(response.raw)
        # 获取嵌入并返回
        返回create_embeddings(图像)
    除了 requests.RequestException:
        返回无
# 构造一个URL来下载较小尺寸的图片
df_photos['photo_image_url'] = df_photos['photo_image_url'].apply(lambda x: x + "?q=75&fm=jpg&w=200&fit=max")
# 将图像一张一张地传递到“process_image”并将嵌入保存到新创建的列“photo_embed”
df_photos['photo_embed'] = [tqdm 中 url 的 process_image(url)(df_photos['photo_image_url'],total=len(df_photos))]

# 删除图像处理失败的行
df_photos.dropna(子集=['photo_embed'], inplace=True)
# 重置索引并将“id”列重命名为“index”
df_photos = df_photos[df_photos['photo_id'].isin(photo_ids)].reset_index().rename(columns={'index': 'id'})

# 关闭会话
session.close()

注意:此过程需要一些时间,并且还取决于您的网速。

经过此过程,我们的数据集就完成了。下一步是创建一个新表并将数据存储在 MyScale 中。

与 MyScale 连接

要将应用程序与 MyScale 连接,您需要完成几个设置和配置步骤。

获得连接详细信息后,您可以替换以下代码中的值:

Python

 

导入clickhouse_connect

客户端 = clickhouse_connect.get_client(
    主机='您的主机地址',
    端口=443,
    username='您的用户名',
    密码='您的密码'
)

创建表

建立连接后,下一步就是创建表。现在,让我们首先使用以下命令查看我们的数据框:

Python

 

df_photos

数据框如下所示:

<表格样式=“最大宽度:100%;宽度:自动;表格布局:固定;显示:表格;”宽度=“自动”>
<标题>

photo_id photo_image_url 照片嵌入


<正文>

wud-eV6Vpwo https://images.unsplash.com/uploads/1411949294… [0.0028754104860126972,0.02760922536253929,0… psIMdj26lgw https://images.unsplash.com/photo-141633941111… [0.019032524898648262,-0.04198262840509415,0… 2EDjes2hlZo https://images.unsplash.com/photo-142014251503… [-0.015412664040923119,0.01923416182398796,0…

让我们根据数据框创建一个表格。

Python

 

# 检查是否存在同名表。如果存在,则删除该表
client.command("如果存在则删除表default.myscale_photos")
# 创建一个照片表
客户端.命令("""
创建表default.myscale_photos
(
    id UInt64,
    照片_id 字符串,
    photo_image_url 字符串,
    photo_embed 数组(Float32),
    约束向量_len 检查长度(photo_embed)= 512
)
按 ID 排序
""")

上述命令将在您的 MyScale 集群中创建一个表。

插入数据

让我们将数据插入到新创建的表中:

Python

 

# 从数据集中上传数据
client.insert("default.myscale_photos", df_photos.to_records(index=False).tolist(),
              column_names=df_photos.columns.tolist())

# 检查插入数据的数量
print(f"照片数量:{client.command('SELECT count(*) FROM default.myscale_photos')}")

# 用余弦创建向量索引
客户端.命令("""
更改表default.myscale_photos
添加矢量索引 photo_embed_index photo_embed
类型 MSTG
('metric_type=余弦')
“””)

# 检查向量索引的状态,确保向量索引已准备好并处于“已构建”状态
get_index_status="从 system.vector_indices 中选择状态,其中 name='photo_embed_index'"
print(f"索引构建状态:{client.command(get_index_status)}")

上面的代码将数据插入表中并使用MSTG算法创建索引。创建索引是为了从表中快速检索数据。最后一条命令用于确认索引是否创建成功。如果是,您将看到“索引构建状态:已构建。”

注意:MSTG 算法 由 MyScale 创建,它比 IVF 和 HNSW 等其他索引算法要快得多。

如何查询 MyScale

插入数据后,我们就可以使用 MyScale 来查询数据并使用多模态来获取图像。因此,我们首先尝试从表中获取随机图像。

Python

 

导入请求
将 matplotlib.pyplot 导入为 plt
从 PIL 导入图像
从 io 导入 BytesIO

# 下载图片及其 url
默认下载(网址):
    响应 = requests.get(url)
    返回 Image.open(BytesIO(response.content))
def show_image(url, 标题=无):
    img = 下载(网址)
    图 = plt.figure(figsize=(4, 4))
    plt.imshow(img)
    plt.show()
random_image = client.query("SELECT * FROM default.myscale_photos ORDER BY rand() LIMIT 1")
target_image_url = random_image.first_item["photo_image_url"]
print("正在加载目标图像...")
显示图像(目标图像网址)

上面的代码应该从表格中随机搜索图像并将其显示在代码编辑器上。

如何使用文本和图像获取相关图像

正如您所知,多模态模型可以同时处理多种数据模态。同样,我们的模型可以同时处理图像和文本,提供相关图像。我们将提供以下图片和文字:“一个男人站在海滩上。”

相关图像

Python

 

image_url="https://images.unsplash.com/photo-1701443478334-c1a4bfda91ff?q=80&w=1936&auto=format&fit=crop&ixlib=rb-4.0.3&ixid= M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
query_text="一个男人站在海滩上"
embeddings=create_embeddings(download(url),query_text)

下一步是编写查询以从数据集中获取 top_k 相关结果。

Python

 

top_k = 5
# 查询从数据库中获取相关结果
结果 = client.query(f"""
选择 photo_id、photo_image_url、距离(photo_embed, {embeddings}) 作为 dist
来自default.myscale_photos
按地区 ASC 排序
限制 {top_k}
“””)

# 下载相关图片
图片网址 = []
对于 results.named_results() 中的 r:
    # 构造一个URL,通过修改图片URL来下载尺寸较小的图片
    url = r['photo_image_url'] + "?q=75&fm=jpg&w=200&fit=max"
    images_url.append(下载(url))

# 显示图像
print("正在加载候选图像...")
对于范围内的行(int(top_k / 5)):
    图, 轴 = plt.subplots(1, 5, Figsize=(20, 4))
    对于我来说,img in enumerate(images_url
): axs[i % 5].imshow(img) plt.show()

注意:距离函数计算查询向量与所有相关向量之间的欧氏距离。

上面的代码将生成类似于以下的结果:

上述代码的反射图像

注意:您可以使用更好的技术来合并嵌入,进一步改进结果。

您可能已经注意到,生成的图像看起来像是文本和图像的组合。您还可以通过仅向该模型提供图像或文本来获得结果,并且它会完美地工作。为此,您只需对 image_urlquery_text 代码行进行注释即可。

结论

传统模型仅用于获取单一数据类型的向量表示,但最新模型接受了更多数据的训练,现在它们能够在统一的向量空间中表示不同类型的数据。我们利用最新模型 CLIP 的功能来开发一个应用程序,该应用程序将文本和图像作为输入并返回相关图像。

多模态嵌入的功能不仅限于图像搜索应用;相反,您可以利用这种尖端技术来开发最先进的推荐系统、视觉问答应用程序(用户可以在其中提出与图像相关的问题)等等。开发这些应用程序时,请考虑使用 MyScale(opens new window,一个集成的 SQL 矢量数据库,使您能够通过超快速的数据检索功能存储数据集中的矢量嵌入和表格数据。

Comments are closed.