导读
将各种数据类型(例如文本和图像)集成到 LLM 中可增强其生成更全面的用户查询响应的能力
方案1:(多模态)数据源直接或简洁存储到向量数据库(embeding model -> vector),文本/视频通过embeding model ,将多模态数据向量化,存储到向量数据库;
方案2:将图片和视频预先经过LLM生成总结文本,将文本转向量,将多模态数据存储到向量数据库;
将query查询向量相似性计算,输出给LLM;
LLM生成response;
多模态人工智能站在下一波人工智能进步的最前沿。事实上,我对 2024 年生成式人工智能的预测之一是,到今年年底,多模态模型将进入主流并成为常态。传统的人工智能系统通常只能处理单一数据类型,例如文本,但环顾四周,世界并不是单模态的。人类使用多种感官与环境互动,多模态人工智能旨在在机器中模拟这种感知。理想情况下,我们应该能够同时采用任意类型的数据类型(文本、图像、视频、音频等)组合并将它们呈现给生成式人工智能模型。
当然,大型语言模型 (LLM) 也有其局限性,例如上下文窗口有限和知识截止日期。为了克服这些限制,我们可以使用一种称为检索增强生成 (RAG) 的过程,该过程由两个关键步骤组成:检索和生成。首先,检索与用户查询相关的数据。然后,这些数据帮助 LLM 生成特定的响应。向量数据库通过存储多种数据类型嵌入来支持检索阶段,从而实现高效的多模态数据检索。此过程确保 LLM 可以通过访问最相关的文本、图像、音频和视频数据的组合来生成对用户查询的响应。
嵌入模型会将各种数据 (例如文本、图像、图表和视频) 转换为数值向量,以便捕捉其在多维向量空间中的含义和细微差别。嵌入技术的选择取决于应用需求,同时要兼顾语义深度、计算效率、要编码的数据的类型、维度等因素。
man、king、woman 和 queen 这几个词映射到的向量空间 (来源:baeldung)
通过将向量映射到多维空间,可以对向量的语义相似性进行细致的分析,从而显著提高搜索和数据分类的准确性。在使用 AI 聊天机器人、大语言模型 (LLM)、检索增强生成 (RAG) 和向量数据库的 AI 应用中以及在搜索引擎和许多其他用例中,嵌入模型发挥着至关重要的作用。
在本文中,我们将深入探讨多模态 RAG 的两个阶段,即检索和生成。首先,我们将介绍两种使用矢量数据库存储和检索文本和图像数据的多模态检索方法。其次,对于生成阶段,我们将使用 LLM 根据检索到的数据和用户的查询生成响应。
RAG 的多模态检索
检索方法:
我们的目标是将图像和文本嵌入到统一的向量空间中,以便同时在两种媒体类型中进行向量搜索。我们通过嵌入数据(将数据转换为数字向量表示)并将其存储在 KDB.AI 向量数据库中来实现此目的。有几种方法可以做到这一点,今天我们将探讨其中两种:
1.使用多模态嵌入模型嵌入文本和图像。
2.使用多模态 LLM 总结图像,将总结和文本数据传递给文本嵌入模型,例如 OpenAI 的“text-embedding-3-small”。
在本文的最后,我们将介绍如何使用动物图像和描述数据集在理论和代码中实现多模态检索的每种方法。该系统将允许查询返回相关图像和文本,作为多模态检索增强生成 (RAG) 应用程序的检索机制。让我们开始吧!
方法 1:使用多模态嵌入模型嵌入文本和图像
通过多模态嵌入模型嵌入文本和图像的架构
多模态嵌入模型可以将文本、图像和各种数据类型集成到单个向量空间中,从而便于在KDB.AI向量数据库中进行跨模态向量相似性搜索。然后可以使用检索到的嵌入来增强多模态 LLM,以生成响应并完成 RAG 管道。
我们将要探讨的多模态嵌入模型称为“ ImageBind ”,由 Meta 开发。ImageBind 可以嵌入多种数据模态,包括文本、图像、视频、音频等。
ImageBind GitHub 存储库:https://github.com/facebookresearch/ImageBind
让我们来看看几个概述 ImageBind 用法的代码片段。请在此处查看完整笔记本。
首先,我们克隆 ImageBind,导入必要的包,并实例化 ImageBind 模型,以便我们可以在代码中稍后使用它:
!git clone https://github.com/facebookresearch/ImageBind
import pandas as pd
import os
import PIL
from PIL import Image
import torch
from imagebind import data
from imagebind.models import imagebind_model
from imagebind.models.imagebind_model import ModalityType
os.chdir('./ImageBind')
!pip install .
device = "cuda:0" if torch.cuda.is_available() else "cpu"
# Instantiate the ImageBind model
model = imagebind_model.imagebind_huge(pretrained=True)
model.eval()
model.to(device)
接下来,我们需要定义函数来帮助我们在数据上使用 ImageBind、提取多模态嵌入以及从文本文件中读取文本:
#Helper functions to create embeddings
def getEmbeddingVector(inputs):
with torch.no_grad():
embedding = model(inputs)
for key, value in embedding.items():
vec = value.reshape(-1)
vec = vec.numpy()
return(vec)
def dataToEmbedding(dataIn,dtype):
if dtype == 'image':
data_path = [dataIn]
inputs = {
ModalityType.VISION: data.load_and_transform_vision_data(data_path, device)
}
elif dtype == 'text':
txt = [dataIn]
inputs = {
ModalityType.TEXT: data.load_and_transform_text(txt, device)
}
vec = getEmbeddingVector(inputs)
return(vec)
# Helper function to read the text from a file
def read_text_from_file(filename):
try:
# Open the file in read mode ('r')
with open(filename, 'r') as file:
# Read the contents of the file into a string
text = file.read()
return text
except IOError as e:
# Handle any I/O errors
print(f"An error occurred: {e}")
return None
现在让我们设置一个空的数据框来存储文件路径、数据类型和嵌入。我们还将获得将存储在矢量数据库中的图像和文本的路径列表:
#Define a dataframe to put our embeddings and metadata into
#this will later be used to load our vector database
columns = ['path','media_type','embeddings']
df = pd.DataFrame(columns=columns)
#Get a list of paths for images, text
images = os.listdir("./data/images")
texts = os.listdir("./data/text")
是时候循环遍历我们的图像和文本并将它们发送到我们的辅助函数,该函数将使用 ImageBind 嵌入数据:
#loop through images, append a row in the dataframe containing each
# image's path, media_type (image), and embeddings
for image in images:
path = "./data/images/" + image
media_type = "image"
embedding = dataToEmbedding(path,media_type)
new_row = {'path': path,
'media_type':media_type,
'embeddings':embedding}
df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)
#loop through texts, append a row in the dataframe containing each
# text's path, media_type (text), and embeddings
for text in texts:
path = "../data/text/" + text
media_type = "text"
txt_file = read_text_from_file(path)
embedding = dataToEmbedding(txt_file,media_type)
new_row = {'path': path,
'media_type':media_type,
'embeddings':embedding}
df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)
我们现在有一个数据框,其中包含所有数据的路径、媒体类型和多模式嵌入!
接下来,我们将设置我们的 KDB.AI 矢量数据库。您可以在KDB.AI免费注册,并获取端点和 API 密钥。
# vector DB imports
import os
from getpass import getpass
import kdbai_client as kdbai
import time
#Set up KDB.AI endpoing and API key
#Go to kdb.ai to sign up for free if you don't already have an account!
KDBAI_ENDPOINT = (
os.environ["KDBAI_ENDPOINT"]
if "KDBAI_ENDPOINT" in os.environ
else input("KDB.AI endpoint: ")
)
KDBAI_API_KEY = (
os.environ["KDBAI_API_KEY"]
if "KDBAI_API_KEY" in os.environ
else getpass("KDB.AI API key: ")
)
#connect to KDB.AI
session = kdbai.Session(api_key=KDBAI_API_KEY, endpoint=KDBAI_ENDPOINT)
要将数据导入 KDB.AI 矢量数据库,我们需要设置表模式。在我们的例子中,我们将有一列用于文件路径、media_type 和嵌入。我们还定义了一个索引,该索引将应用于模式中的嵌入列。嵌入将有 1024 个维度,因为这是 ImageBind 输出的维数。相似性搜索方法定义为“cs”或余弦相似性,索引只是一个平面索引。这些参数中的每一个都可以根据您的用例进行自定义。
# Connect to the default database in KDB.AI
db = session.database('default')
#Set up a table with three columns, path, media_type, and embeddings
table_schema = [
{"name": "path", "type": "str"},
{"name": "media_type", "type": "str"},
{
"name": "embeddings",
"type": "float64s",
},
]
# Define the index
indexes = [
{
'type': 'flat',
'name': 'flat_index',
'column': 'embeddings',
'params': {'dims': 1024, 'metric': "CS"},
},
]
确保不存在同名的表,然后创建一个名为“multi_modal_demo”的表:
# 首先确保该表不存在
try :
db.table( "multi_modal_ImageBind" ).drop()
except kdbai.KDBAIException:
pass
#创建名为“multi_modal_demo”的表
table = db.create_table(table= "multi_modal_ImageBind" , schema=table_schema, indexes=indexes)
将我们的数据加载到我们的 KDB.AI 表中!
#将数据插入表中,拆分成2000行批次
from tqdm import tqdm
n = 2000 # 块行大小
for i in tqdm( range ( 0 , df.shape[ 0 ], n)):
table.insert(df[i:i+n].reset_index(drop= True ))
干得好,我们已将多模态数据加载到向量数据库中。这意味着文本和图像嵌入都存储在同一个向量空间中。现在我们可以执行多模态相似性搜索了。
我们将定义三个辅助函数。一个用于将自然语言查询转换为嵌入式查询向量。另一个用于从文件中读取文本。最后一个用于帮助我们清晰地查看相似性搜索的结果。
# 辅助函数,用于从自然语言查询创建查询向量
def QuerytoEmbedding ( text ):
text = [text]
input = {
ModalityType.TEXT: data.load_and_transform_text(text, device)
}
vec = getEmbeddingVector(inputs)
return (vec)
# 辅助函数,用于查看相似性搜索的结果
def viewResults ( results ):
for index, row in results[ 0 ].iterrows():
if row[ 1 ] == 'image' :
image = Image. open (row[ 0 ])
display(image)
elif row[ 1 ] == 'text' :
text = read_text_from_file(row[ 0 ])
print (text)
# 多模式搜索函数,在矢量数据库中识别最相关的图像和文本
def mm_search ( query ):
image_results = table.search(vectors={ "flat_index" :query}, n= 2 , filter =[( "like" , "media_type" , "image" )])
text_results = table.search(vectors={ "flat_index" :query}, n= 1 , filter =[( "like" , "media_type" , "text" )])
results = [pd.concat([image_results[ 0 ], text_results[ 0 ]], ignore_index= True )]
viewResults(results)
return (results)
让我们进行相似性搜索来检索与查询最相关的数据:
#创建查询向量进行相似性搜索
query_vector = [QuerytoEmbedding( "brown animal with antlers" ).tolist()]
#执行多模态相似性搜索
results = mm_search(query_vector)
结果!我们检索到了三个最相关的嵌入。结果显示效果非常好,返回了鹿的文本描述和两张鹿角鹿的图片:
方法 2:使用多模态 LLM 总结图像并嵌入文本摘要
文本嵌入模型嵌入的文本和图像摘要的架构
实现多模态检索和 RAG 的另一种方法是将所有数据转换为单一模态:文本。这意味着您只需使用文本嵌入模型将所有数据存储在同一个向量空间中。这需要额外的成本,即手动或使用 LLM 最初汇总其他类型的数据(如图像、视频或音频)。
通过将文本和图像摘要嵌入并存储在矢量数据库中,可以启用多模态相似性搜索。检索到最相关的嵌入后,我们可以将它们传递给 RAG 的 LLM。
现在我们对这个方法有了大致的了解,是时候深入研究一些代码了!查看此处的完整笔记本了解详细信息。
首先,我们需要一种总结图像的方法。一种方法是使用“base64”编码对图像进行编码,然后将该编码发送到 LLM,例如“gpt-4-vision-preview”。
import base64
# Helper function to convert a file to base64 representation
def encode_image(image_path):
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
# Takes in a base64 encoded image and prompt (requesting an image summary)
# Returns a response from the LLM (image summary)
def image_summarize(img_base64,prompt):
''' Image summary '''
response = openai.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": prompt},
{
"type": "image_url",
"image_url": {
"url": f"data:image/jpeg;base64,{img_base64}",
},
},
],
}
],
max_tokens=150,
)
content = response.choices[0].message.content
return content
要嵌入文本,无论是来自文件还是来自 LLM 生成的摘要,都可以使用“text-embedding-3-small”等嵌入模型:
def TexttoEmbedding(text):
embeddings = openai.Embedding.create(
input=text,
model="text-embedding-3-small"
)
embedding = embeddings["data"][0]["embedding"]
return(embedding)
现在让我们设置一个空的数据框来存储文件路径、数据类型、原始文本和嵌入。我们还将获得将存储在矢量数据库中的图像和文本的路径列表:
#Define a dataframe to put our embeddings and metadata into
#this will later be used to load our vector database
columns = ['path','media_type','text','embeddings']
df = pd.DataFrame(columns=columns)
#Get a list of paths for images, text
images = os.listdir("./data/images")
texts = os.listdir("./data/text")
# 循环遍历文本和图像文件,并通过我们的辅助函数发送它们以进行嵌入。为每个文本和图像文件的数据框附加一个新行,并添加相应的路径、# media_type、文本(或摘要)和嵌入:
# Embed texts, store relevant info in data frame
for text in texts:
path = "./data/text/" + text
media_type = "text"
text1 = read_text_from_file(path)
embedding = TexttoEmbedding(text1)
new_row = {'path': path,
'media_type':'text',
'text' : text1,
'embeddings': embedding}
df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)
# Encode images with base64 encoding,
# Get text summary for encoded images,
# Embed summary, store relevant info in data frame
for image in images:
path = "./data/images/" + image
media_type = "images"
base64_image = encode_image(path)
prompt = "Describe the image in detail."
summarization = image_summarize(base64_image,prompt)
embedding = TexttoEmbedding(summarization)
new_row = {'path': path,
'media_type':'image',
'text' : summarization,
'embeddings': embedding}
df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)
我们现在有一个数据框,其中包含所有数据的路径、媒体类型、文本和嵌入!我们现在将设置矢量数据库(参见方法 1)并创建具有以下模式的表:
database = session.database("default")
# Define table schema for our table with columns for path, media_type, text, and embeddings
table_schema = [
{"name": "path", "type": "str"},
{"name": "media_type", "type": "str"},
{"name": "text", "type": "str"},
{"name": "embeddings", "type": "float64s"}
]
# Define the index that will be applied to the embeddings column defined above
indexes = [
{
"name": "flat_index",
"column": "embeddings",
"type": "flat",
"params": {"dims": 1536, "metric": "CS"}
}
]
# First ensure the table does not already exist
try:
database.table("multi_modal_demo").drop()
except kdbai.KDBAIException:
pass
# Create the table called "multi_modal_demo"
table = database.create_table("multi_modal_demo", schema=table_schema, indexes=indexes)
让我们填充表格:
#将数据插入表中,拆分成2000行批次
from tqdm import tqdm
n = 2000 # 块行大小
for i in tqdm( range ( 0 , df.shape[ 0 ], n)):
table.insert(df[i:i+n].reset_index(drop= True ))
我们的矢量数据库已经加载了我们所有的数据,我们可以尝试相似性搜索:
query_vector = [TexttoEmbedding("animals with antlers")]
results = table.search({index_name: query_vector}, n=3)
viewResults(results)
结果表明该方法也适用于多模态检索!
多模态 RAG
这些方法中的每一种都可以充当多模态 RAG 管道中的检索阶段。最相关的数据(文本或图像)可以从向量搜索中返回,然后注入提示以发送到您选择的 LLM。如果使用多模态 LLM,文本和图像都可用于增强提示。如果使用基于文本的 LLM,只需按原样传入文本和图像的文本描述。
下面的架构展示了 RAG 的高级流程:
提取数据、分块/汇总、嵌入
将嵌入存储在向量数据库中
嵌入用户查询并针对向量数据库进行语义相似性搜索
检索到最相关的数据!(多模式 RAG 中为文本和图像)
检索到的数据和用户的查询被发送到 LLM
LLM 使用检索到的数据作为上下文来回答用户的查询,为用户生成响应
简单的 RAG 架构
使用本文中描述的检索方法后,您可以将检索到的数据以文本或图像的形式传递给 LLM 进行生成,以完成 RAG 过程。在下面的示例中,我们传入检索到的文本数据列表和用户的提示 — 该函数会构建查询并将其发送到 LLM,然后返回生成的响应。
对于使用接受文本和图像作为输入的多模式 LLM 进行生成,请参阅以下使用 Google 的 Gemini Vision Pro 模型的代码片段:
import google.generativeai as genai
genai.configure(api_key = os.environ['GOOGLE_API_KEY'])
# Use Gemini Pro Vision model to handle multimodal inputs
vision_model = genai.GenerativeModel('gemini-1.5-flash')
# Generate a response by passing in a list containing:
# the user prompt and retieved text & image data
response = vision_model.generate_content(retrieved_data_for_RAG)
print(response.text)
对于使用基于文本的 LLM 进行生成,我们将图像摘要和文本直接传递到 LLM,请参阅以下使用“OpenAI 的 gpt-4-turbo-preview”模型的代码片段:
# Helper function to execute RAG, we pass in a list of retrieved data, and the prompt
# The function returns the LLM's response
def RAG(retrieved_data,prompt):
messages = ""
messages += prompt + "\n"
if retrieved_data:
for data in retrieved_data:
messages += data + "\n"
response = openai.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": messages},
],
}
],
max_tokens=300,
)
content = response.choices[0].message.content
return content
结论
将各种数据类型(例如文本和图像)集成到 LLM 中可增强其生成更全面的用户查询响应的能力。本文重点介绍了 KDB.AI 等矢量数据库作为多模态检索系统核心的作用,使相关多模态数据与 LLM 耦合用于 RAG 应用程序。今天我们重点介绍了图像和文本,但有可能将更多类型结合在一起,为新的 AI 应用程序开辟更广阔的视野。多模态 RAG 的概念是朝着在机器中模拟类似人类的感知迈出的早期但重要的一步。
在LinkedIn上与我联系!
资料来源:
https://github.com/facebookresearch/ImageBind
https://platform.openai.com/docs/guides/embeddings
https://ai.google.dev/models/gemini
https://github.com/google/generative-ai-docs/blob/main/site/en/tutorials/python_quickstart.ipynb
评论区