DLAI-多模态搜索和-RAG-笔记-全-
DLAI 多模态搜索和 RAG 笔记(全)
001:课程介绍 🎬

在本节课中,我们将要学习《构建多模态搜索和RAG》这门短期课程的概述,了解其核心目标、技术背景以及你将掌握的关键技能。
欢迎来到这门短期课程《构建多模态搜索和RAG》。本课程与Weaviate合作推出。RAG,即检索增强生成系统,为大型语言模型提供包含你专有数据信息的上下文,并要求模型在生成回答时使用该上下文。
构建RAG应用的一种常见方法是使用向量数据库来存储文本文档的嵌入向量。然后,给定一个查询,你从向量数据库中检索相关信息,并将其作为文本上下文添加到你的提示词中。
但是,如果你想要的上下文包含一张演示文稿的图片、一段音频剪辑,甚至可能是一段视频呢?本课程将教你使用此类多模态数据实现RAG背后的技术细节。
第一个关键点是找到一种计算嵌入向量的方法,使得不同模态下但主题相关的数据能够被相似地嵌入。例如,一段关于狮子的文字、一张显示狮子的图片、一段狮子吼叫的视频或音频,它们的嵌入向量应该彼此接近,这样关于狮子的查询就能检索到所有这些数据。换句话说,我们希望概念的嵌入表示是模态无关的。
在下一节视频中,你将学习如何通过一个称为对比学习的过程来实现这一点。在你拥有了这样的多模态检索模型之后,你将用它来检索与用户查询相关的上下文。这样,你现在就可以构建一个多模态搜索应用,例如,用一张狮子的图片来检索与该图片相关的视频、音频和文本。
现在,如果你有一个支持多模态输入的生成模型,你可以将检索结果作为上下文提供给模型,并要求它基于相关的多模态上下文信息来回答查询。我很高兴能与Darren Schter共同讲授这门课程。Sebastian将在这里解释多模态应用在底层是如何工作的。
Sebastian是Weaviate的开发者关系负责人,他是向量数据库领域的专家,在开发者关系领域工作了十多年。事实上,他的全职工作就是帮助像你这样的开发者成功使用向量数据库。

感谢Andrew,我非常高兴能与你合作这门课程。在这门课程中,你将首先学习如何教会计算机理解多模态数据的概念。然后,你将构建一个“文本到任意模态”以及“任意模态到任意模态”的搜索系统。接下来,你将学习如何将语言模型和多模态模型结合成能够理解图像和文本的语言视觉模型。
之后,你将专注于多模态RAG,将多模态搜索与多模态生成和推理相结合。作为最后一步,你将通过实现不同的现实案例(包括分析发票和流程图)来学习多模态在工业界是如何应用的。
许多人共同努力创建了这门课程。我要感谢来自Weaviate的Zen Hasan以及来自DeepLearning.AI的Emael Gegari对本课程的贡献。那么,带着这些激动人心的主题,让我们进入下一个视频,正式开始学习。

本节课中,我们一起学习了《构建多模态搜索和RAG》课程的总体介绍。我们了解了RAG系统的基本概念、处理多模态数据(如图像、音频、视频)的挑战与目标,以及通过对比学习实现模态无关嵌入的核心思想。课程将引导我们从理解概念开始,逐步构建多模态搜索,最终实现结合了检索与生成功能的多模态RAG应用。
002:多模态概述 🧠


在本节课中,我们将学习多模态的概念,了解什么是多模态模型,并重点探讨如何通过对比表征学习的过程,教会计算机理解多模态数据。



多模态搜索演示 🚀
为了激发你的想象力,我们先来看一个实时的多模态搜索演示,了解如何跨不同类型的内容进行搜索。在这个演示中,我可以输入文本(例如“一群狮子”)进行搜索,并得到图像、音频和视频文件的返回结果。本课程将深入讲解其背后的工作原理、支撑该应用的核心技术,并让你亲手实践如何构建类似的功能。我们不一定完全复现这个应用,但学完本课程后,你将掌握足够的知识来构建此类应用甚至更多。




为何要学习多模态?🤔
多媒体内容无处不在。无论是搜索喜欢的歌曲、寻找想看的电影、浏览想购买的商品,还是查找维基百科文章,我们的一切行为都始于搜索。但我们不仅想搜索文本,还想搜索歌曲、电影预告片、产品图片等,即我们想搜索多模态数据。


那么,什么是多模态数据?多模态数据是来自不同来源的数据,可以包括文本、图像、音频、视频等。这些不同的模态通常描述相似的概念。例如,一张狮子的图片、一段描述“丛林之王”的文字、一段狮子奔跑的视频,甚至一声狮吼。每种模态都携带不同类型的信息。通过结合这些信息,我们能更好地理解它们所代表的概念。
试想一下,同时看到和听到狮子咆哮,远比仅仅安静地看着它更令人印象深刻,甚至感到震撼。毕竟,只有当你既看到又听到狮子时,才能真正理解它为何是“丛林之王”。
我们想要从多模态数据中学习的另一个动机,是思考人类如何学习。想象一个婴儿在学会说话前的第一年,他们的大量学习是通过与物体的互动完成的——触摸、闻、感受质地,甚至品尝(即使是肥皂)。同时,他们也通过观察和倾听周围的一切来学习。这种基础知识的建立是通过与世界的多模态互动,而非仅通过语言完成的。因此,如果你想构建更智能、能力更强的AI,它也需要像人类一样学习和思考不同模态的信息。
多模态嵌入与对比学习 🧩
为了让计算机处理多模态数据,我们首先需要了解多模态嵌入。多模态嵌入允许我们在同一个向量空间中表示多模态数据,使得一张狮子的图片、一段描述狮子的文字、一声狮吼或一段狮子奔跑的视频的向量表示都彼此接近。
多模态嵌入模型能生成一个理解所有模态的联合嵌入空间。它可以理解电子邮件、图像、音频文件等。这里的核心概念是,该模型保留了模态内部和跨模态的语义相似性。这意味着,无论模态如何,相似对象的向量都会彼此接近(例如一张狮子图片和相关的文字描述),而不同概念(如狮子和喇叭)的向量在多模态空间中则相距甚远。

为了开始训练多模态嵌入模型,我们需要从理解单一模态的模型开始。这些独立的模型专门用于理解文本、图像、音频或视频。接下来的任务是将这些模型统一起来,使得无论何种模态,相似概念都能产生接近的向量。这个过程是通过对比表征学习完成的。
对比表征学习是一种通用过程,可用于训练任何嵌入模型,而不仅仅是多模态嵌入模型。具体到这里,它也可以用于将多个模型统一成一个多模态嵌入模型。其主要思想是,我们希望为多种模态创建一个统一的向量空间表示。我们通过为模型提供相似概念和不同概念的正例和负例来实现这一点。然后,我们训练模型,将相似示例的向量拉近,将不同概念的向量推远。
让我们通过一个文本示例来理解其工作原理。首先,我们需要一个锚点,这可以是任何数据点,例如句子“他能闻到玫瑰花香”。然后,我们需要一个正例,即与锚点相似的示例,例如“一片芬芳的花田”。最后,我们需要一个负例,即与锚点不同的示例,例如“狮子威严地咆哮”。现在,我们可以获取每个数据点的向量嵌入,并希望将负例向量推离锚点,同时将正例向量拉近锚点。
我们可以用同样的方法训练一个图像模型。锚点可以是一张德国牧羊犬的图片,正例可以是同一张图片的灰度版本,而负例可以是一张苹果的图片。任务同样是推远负例,拉近正例。
这种“推拉”过程是通过对比损失函数实现的。首先,我们需要将锚点和示例编码成向量嵌入,然后计算锚点与示例之间的距离。在训练过程中,我们希望最小化锚点向量与正例向量之间的距离,同时最大化与负例向量之间的距离。
现在,让我们将对比学习扩展到多模态数据。我们可以提供不同模态的正例和负例。例如,给定我们的锚点是一段狮子奔跑的视频,我们可以提供图像和文本作为对比示例。然后,我们可以跨模态应用“推拉”操作,从而使模型在所有模态的同一向量空间中对齐。
一个棘手的部分可能是找到足够的锚点和对比示例。在2021年的一篇CLIP论文中,研究人员将图像及其对应的标题(各自代表不同模态)配对。图片及其标题代表锚点和正例(如矩阵对角线所示),而任何其他随机的图片-标题配对则很可能是负例。通过这种方式,他们能够应用对比损失函数来训练那个文本-图像多模态模型。
现在,让我们看看对比损失函数的样子。首先,你需要编码函数,将锚点和对比示例转换为相同维度的向量。
假设我们有一个函数 F,它接收一张图像并返回一个向量 Q。
同时,我们有一个函数 G,它接收一段视频并生成一个向量 K。
然后,我们取这些向量表示。在分子部分,我们有正例之间的相似度(例如一张狮子图片和一段狮子奔跑的视频),我们希望这个相似度尽可能高。在分母部分,我们有一个负例(例如一张狮子图片和一段骑自行车的猫的视频)。实际上,这是你需要求和的众多负例之一。我们希望这个公式返回一个概率值,因此在分母中,我们通过再次加入分子的正例来进行归一化。公式前面有一个负号,这意味着我们实际上希望最小化这个损失函数。通过最小化它,正例视频的嵌入将被拉近锚点图像的嵌入,而负例视频的嵌入将被推离锚点图像的嵌入。
你可以对音频、文本、视频等所有模态逐一进行这个过程。这正是ImageBind论文中所做的。
实践:在实验室中训练嵌入模型 💻
现在,让我们在实践中看看这一切。在实验室中,你将使用对比损失训练一个嵌入模型,然后可视化学习到的向量空间。
在这个实验中,你将训练一个神经网络来学习MNIST图像数据集的嵌入。首先,运行代码以忽略任何对我们分析不重要的警告。
首先,导入所需的库。你将使用PyTorch训练模型以及一系列支持库,其中最重要的是用于可视化的库,如Plotly和UMAP。最后,你需要导入MNISTDataset类,用于获取训练和测试所需的正例和负例。

MNIST数据集基于0到9的数字图像,每个数字图像都标有其对应的值,这样我们就知道它代表的是0、5还是9。MNISTDataset类为你提供一个锚点(一个数字)以及正例和负例(也是数字图像),正如我们在幻灯片中讨论的那样。例如,如果锚点是5,那么正例将是另一个5的图像,而负例可能是6或7的图像。

现在,让我们从MNISTDataset加载数据。这里需要加载训练数据和验证数据。数据加载后,你需要设置PyTorch数据加载器,以便为神经网络提供训练数据。
现在,使用上述数据加载器来可视化锚点及相应的正例和负例。首先,添加一个辅助函数来显示提供的图像。然后,你需要遍历训练数据加载器中的批次数据,从中获取锚点图像、对比图像、它们的理想距离和标签。运行此代码,你应该能看到每个示例的四张图像。
接下来,定义一个神经网络架构,该架构将接收MNIST图像并输出64维向量。这是一个简单的架构,包含两个卷积层(用于提取数字的视觉特征)和两个前向线性层(用于将卷积层学习到的视觉特征转换为64维表示)。所有这些都在前向传播函数中组合。
为了训练神经网络,我们使用对比损失函数。该函数将接收锚点和正例或负例的64维向量表示,并确保满足理想距离。如前所述,正例的理想余弦距离为1,负例为0。这是本实验代码中最重要的部分。
对比损失被定义为计算两点之间的余弦相似度。在前向函数中,你有锚点和一个对比示例(可以是正例或负例)以及理想距离。前向函数的实现分为两步:首先计算锚点和对比图像之间的余弦相似度得分;第二步,将此得分与期望的理想距离进行比较。这两个得分之间的距离越小,损失越低,我们最终希望最小化这个损失。
这里使用CPU作为默认训练选项,但如果GPU可用,则将使用GPU。你还需要配置神经网络的训练参数,包括优化器(执行梯度下降)、对比损失函数和学习率调度器。
现在进入有趣的部分:执行模型训练的代码。我们需要一个检查点文件夹来保存训练结果,以便后续重新加载。训练循环可以设置为运行任意数量的周期。在每个周期中,循环通过前向传播和反向传播训练模型。关键部分是损失函数,它接收锚点和对比向量及其期望距离,并计算每个数据点的损失。损失被累加,在每个周期结束时用于计算平均损失,从而判断模型训练是否在改进。每个周期完成后,循环将结果保存到检查点文件夹。
请注意,训练过程相当缓慢,每个周期可能需要2到3分钟。因此,运行10、20甚至100个周期可能需要相当长的时间。为了节省时间,我们提供了一个预训练了100个周期的模型检查点,建议直接加载这个预训练模型。
运行以下代码来获取你的模型。默认情况下,它将尝试从检查点加载模型。如果你希望自己进行完整训练,可以将训练标志更改为True,但请记住这是一个漫长的过程,需要耐心等待。
如果你选择自己训练模型,可以绘制损失随周期变化的曲线。在我们的案例中,训练大约在20个周期后基本稳定,大部分学习发生在前5到10个周期。
可视化向量空间 📊
现在你有了一个训练好的神经网络,可以可视化向量空间,看看实际学到了什么。
为了可视化向量空间,你需要获取训练数据集,并用模型对所有数据进行编码。结果,你将得到数据的64维向量表示。你还需要每个项目的标签,以便用不同颜色显示每个数字。
因为我们无法看到64维的世界,所以需要进一步进行降维。这里你将使用主成分分析,将维度从64降至3,这样我们就可以看到了。
现在你有了3D向量,可以在交互式散点图中可视化这些数据。使用在X、Y和Z轴上降维后的三维数据,创建一个绘图对象布局并显示它。在绘制的图表中,你会看到向量被绘制在图上,每种颜色代表不同数字的点。由于你在训练中使用了余弦距离度量,相似数字的嵌入呈放射状或线状排列,而不是聚集成簇。这是因为余弦距离依赖于嵌入之间的角度,不像欧几里得距离那样最小化邻近性。
你可以移动和旋转这个3D图,从不同角度观察空间。
接下来,你可以使用UMAP技术在2D空间上可视化训练好的嵌入。请注意,这里提供的度量设置为cosine,这一点非常重要,因为UMAP默认使用欧几里得距离,这不会给你准确的向量嵌入空间视图。运行此代码,UMAP可能需要半分钟到一分钟来完成。最终,2D图将显示出来。
在这个图表中,每个数字由不同的颜色表示,你可以看到数字如何以类似水母触手的模式聚集。这证明了对比训练是成功的,每个数字的表示都聚集在向量空间的不同部分。
出于好奇,如果你运行相同的代码但不指定距离度量,UMAP将使用欧几里得距离,你会得到一串串的点。你仍然可以看到这些“串”彼此靠近,并相互跟随。
最后,你可以播放一个视频,亲眼观看对比学习在100个训练周期中的过程。你可以看到,在开始时,所有数据点都聚集在一起,但随着时间的推移,相似的嵌入逐渐对齐,而不同的数字则向其他方向扩散。这正是拉近相似示例、推远不同示例的效果,这就是对比训练的工作原理。
总结 📝
在本节课中,你学习了如何实现对比学习来在图像数据集上训练神经网络。你也了解了推远负例和拉近正例如何帮助训练模型。最后,你使用PCA和UMAP绘制了降维后的向量嵌入,以分析向量空间训练的结果。

在下一课中,你将学习如何将向量数据库与多模态模型结合使用,以向量化图像和视频,并进行文本、图像和视频输入的搜索。
003:3.L2 多模态搜索
概述
在本节课中,我们将学习如何理解跨越多种模态(如文本、图像、视频)的同一概念,并利用开源向量数据库 Weaviate 实现一个多模态检索系统。我们将构建“文本到任意模态”以及“任意到任意模态”的搜索功能。
人类非常擅长跨模态推断信息。即使听不到视频的声音,你也能在脑海中“听到”狮子的吼声或火车经过的“呜呜”声。这是因为我们能通过多种感官(视觉、听觉、触觉等)理解同一个数据点。
机器要获得类似的多模态理解能力,需要创建一个共享的多模态向量空间。在这个空间里,无论数据属于哪种模态,只要内容相似,其向量表示就会被放置在彼此接近的位置。
有了统一的多模态嵌入空间,你就可以执行文本到任意模态搜索。例如,搜索“在草原上咆哮的狮子”,可以返回代表最相似内容的多模态数据,无论是文本、图像、音频还是视频。你甚至可以执行任意到任意搜索,即查询和检索结果都可以是任意模态。
多模态搜索的工作原理
上一节我们介绍了多模态搜索的概念,本节中我们来看看它的具体实现步骤。

以下是多模态搜索的工作流程:
- 将数据(如“丛林之王”文本或狮子视频)输入多模态模型,得到向量嵌入。
- 对海量数据重复此过程,最终构建一个包含所有向量嵌入的向量空间。
- 进行搜索时,将查询(如一张狮子图片)输入同一模型,得到其向量嵌入。
- 在向量空间中查找与该查询向量最接近的向量,返回对应的数据对象(如图片或视频)。
实践:构建多模态搜索
现在,让我们通过代码实践,将多模态数据添加到向量数据库,并执行任意到任意搜索。
1. 环境设置与数据库连接
首先,我们需要导入必要的库并设置环境。

# 忽略不必要的警告
import warnings
warnings.filterwarnings('ignore')
# 加载API密钥(用于大模型)
import os
os.environ['OPENAI_API_KEY'] = 'your-api-key-here'
# 连接至Weaviate向量数据库(内存嵌入式版本)
import weaviate
from weaviate.embedded import EmbeddedOptions
client = weaviate.Client(
embedded_options=EmbeddedOptions()
)
2. 创建多模态集合
接下来,我们创建一个用于存储向量嵌入的集合(Collection)。
# 定义集合模式
collection_name = "animals"
# 定义多模态向量化器配置
vectorizer_config = weaviate.classes.config.Configure.Vectorizer.multi2vec_clip(
image_fields=["image"],
video_fields=["video"]
)
# 定义集合配置
data_collection = client.collections.create(
name=collection_name,
vectorizer_config=vectorizer_config,
properties=[
weaviate.classes.config.Property(name="name", data_type=weaviate.classes.config.DataType.TEXT),
weaviate.classes.config.Property(name="path", data_type=weaviate.classes.config.DataType.TEXT),
weaviate.classes.config.Property(name="media_type", data_type=weaviate.classes.config.DataType.TEXT),
]
)
3. 数据准备与导入
在导入数据前,我们需要一个辅助函数将媒体文件转换为Base64格式,这是模型处理所需的格式。
import base64
def file_to_base64(file_path):
"""将文件转换为Base64字符串。"""
with open(file_path, "rb") as f:
return base64.b64encode(f.read()).decode('utf-8')
以下是导入图像数据的步骤:
- 获取目标集合。
- 遍历图像文件夹中的所有文件。
- 使用批处理方式导入数据,将每个图像转换为Base64并生成向量嵌入。
# 获取集合
animals = client.collections.get(collection_name)
# 设置图像文件夹路径
image_folder = "./source_images/"
# 开始批处理导入
with animals.batch.dynamic() as batch:
batch.batch_size = 100
for filename in os.listdir(image_folder):
file_path = os.path.join(image_folder, filename)
# 将图像转换为Base64
image_b64 = file_to_base64(file_path)
# 创建数据对象
data_object = {
"name": filename,
"path": file_path,
"media_type": "image",
"image": image_b64 # 关键:将Base64图像传递给向量化器
}
# 添加到批处理
batch.add_object(properties=data_object)
导入视频数据的流程类似,但需要将Base64数据传递给video属性,并将media_type标记为video。
video_folder = "./source_videos/"
with animals.batch.dynamic() as batch:
batch.batch_size = 100
for filename in os.listdir(video_folder):
file_path = os.path.join(video_folder, filename)
video_b64 = file_to_base64(file_path)
data_object = {
"name": filename,
"path": file_path,
"media_type": "video",
"video": video_b64 # 关键:将Base64视频传递给向量化器
}
batch.add_object(properties=data_object)
4. 执行多模态搜索
数据导入后,我们就可以执行搜索了。首先,我们需要一些辅助函数来美观地展示结果。
from IPython.display import Image, display, Video
def display_media(result_object):
"""根据media_type显示图像或视频。"""
media_type = result_object.properties['media_type']
file_path = result_object.properties['path']
if media_type == 'image':
display(Image(filename=file_path))
elif media_type == 'video':
display(Video(filename=file_path, width=300))
文本到任意搜索
我们可以用文本来搜索所有模态的数据。
# 执行文本查询
query_text = "叼着棍子跑的狗"
response = animals.query.near_text(
query=query_text,
limit=3,
return_properties=["name", "path", "media_type"]
)
# 显示结果
for obj in response.objects:
print(f"名称: {obj.properties['name']}")
display_media(obj)
图像到任意搜索
我们也可以用一张图片作为查询输入。
# 将查询图像转换为Base64
query_image_path = "./query_images/dog_with_stick.jpg"
query_image_b64 = file_to_base64(query_image_path)
# 执行图像查询
response = animals.query.near_image(
near_image=query_image_b64,
limit=3,
return_properties=["name", "path", "media_type"]
)
# 显示结果
for obj in response.objects:
print(f"名称: {obj.properties['name']}")
display_media(obj)
视频到任意搜索
最复杂的任务之一是用视频进行搜索。
# 将查询视频转换为Base64
query_video_path = "./query_videos/meerkats_chilling.mp4"
query_video_b64 = file_to_base64(query_video_path)
# 执行视频查询
response = animals.query.near_media(
near_media=query_video_b64,
media_type="video", # 指定查询模态为视频
limit=3,
return_properties=["name", "path", "media_type"]
)
# 显示结果
for obj in response.objects:
print(f"名称: {obj.properties['name']}")
display_media(obj)
5. 可视化向量空间
为了更直观地理解多模态向量空间,我们可以将高维向量降维并可视化。
以下是使用UMAP库进行降维和可视化的步骤:
- 从集合中获取所有对象的向量嵌入和元数据。
- 使用UMAP将向量从1400维降至2维。
- 使用散点图绘制降维后的向量,相似内容会聚集在一起。
import pandas as pd
import umap
import plotly.express as px


# 1. 获取所有对象的向量和元数据
all_objects = animals.iterator(include_vector=True)
embeddings = []
labels = []
for obj in all_objects:
embeddings.append(obj.vector['default'])
labels.append(obj.properties['media_type'])

# 2. 使用UMAP降维
reducer = umap.UMAP(n_components=2, random_state=42)
embeddings_2d = reducer.fit_transform(embeddings)
# 3. 创建DataFrame并绘图
df = pd.DataFrame(embeddings_2d, columns=['x', 'y'])
df['media_type'] = labels

fig = px.scatter(df, x='x', y='y', color='media_type',
title='多模态向量空间可视化 (UMAP降维)')
fig.show()
总结
本节课中我们一起学习了多模态搜索的核心原理与实践。我们了解到,通过创建共享的多模态向量空间,可以让机器像人类一样理解跨越不同模态的同一概念。我们使用Weaviate向量数据库,实现了以下功能:
- 向量化与存储:将图像和视频通过多模态模型转化为向量嵌入,并与元数据一同存储。
- 多模态搜索:实现了“文本到任意”、“图像到任意”和“视频到任意”的搜索,并能返回混合模态的结果。
- 空间可视化:通过降维技术可视化了向量空间,直观展示了相似概念(无论其原始模态如何)在向量空间中彼此靠近的现象。

这为构建更智能的检索增强生成(RAG)系统奠定了坚实基础。在下一节课中,我们将深入探讨大语言模型(LLM)和多模态大模型(LMM)的工作原理及训练过程。
004:4.L3.LMMs



在本节课中,我们将学习大语言模型的工作原理及其如何理解文本。接着,我们将了解如何通过一个名为“视觉指令微调”的过程,将大语言模型与多模态模型结合成语言视觉模型。最后,我们将实际应用这些模型。
大语言模型的工作原理 🤖
当前的大语言模型都是基于生成式预训练变换器构建的,例如 Llama 2、ChatGPT 或 Mixtral。这类模型是自回归的,因为它们一次只生成一个词元或一个词片段。后续生成的词元仅依赖于先前提供或生成的词元。
这些模型通过预测数万亿词元中的下一个词,以无监督的方式进行训练。在此训练过程中,模型会输出所有可能的下一个词元的概率分布,训练的目标是让这个概率分布变得准确。
让我们看一个例子。对于输入“Jack and Jill went up the ____”,模型会为每个候选词元输出一个分数。像“mountain”和“hill”这样更可能的词元会获得较高的分数,而像“apple”和“llama”这样不太可能的词元则分数较低。你可以将这些分数视为未归一化的概率。这些分数随后会被归一化为百分比。
生成过程详解 🔄
给定一个提示词“the rock”,我们想看看模型如何补全它。我们使用独热向量来表示每个单词,然后查找每个词元的嵌入向量。一旦获得词元的嵌入,变换器模型会通过“关注”第一个词来尝试生成下一个词。它总是以句子开始标记作为起点。
因为我们知道前两个词是“the rock”,我们可以强制模型输出它们。实际上,模型正是这样被训练的。一旦模型输出了“rock”,我们将这个词的表示作为独热向量重新输入。
现在,模型会查找“rock”的嵌入向量,并关注同一句子中的前一个词,然后输出下一个可能词元的概率分布,我们可以从中采样。在这里,我们采样得到了单词“rolls”。我们将其向前传递,生成下一个词“along”。这个过程可以持续进行,直到达到词元限制或遇到句子结束标记,从而完成生成。由于输出是概率性的,每次生成都可能得到不同的结果。
让我们看另一个可能的补全示例。假设我们得到了单词“skips”,并将其向前传递。我们得到的下一个词是“fast”。如果继续这个过程,我们可能会得到一个完全不同的生成响应。
视觉模型基础 👁️

这是一个简单的图像分类模型示例,它接收一张图像并输出一个类别标签。


视觉变换器在此分类任务中表现良好。它将图像作为图块而非单个像素进行处理,这使得分析效率更高。让我们更详细地看一下。图像的每个图块被向量化并传入变换器模型。变换器可以选择关注任何图块,并被优化以输出正确的标签。
视觉指令微调 🎨
现在,让我们看看如何使用视觉指令微调来训练一个语言模型,使其能够同时处理图像和文本。给定一张图像和一个文本指令,例如图像是《星夜》,问题是“谁画了这幅画?”,你可以训练模型以文本形式输出正确答案,即文森特·梵高。




让我们看一个视觉指令微调的具体例子。你将从图像《星夜》开始,将其切割成图块。同时,你有一个文本指令“谁画了这幅画?”。接下来,你将图像的图块嵌入为向量,并将句子中的词元也嵌入为向量。
然后,语言模型将被训练去理解和关注图像图块词元以及语言词元,并且必须为答案“文森特·梵高”输出正确的词元。这被称为视觉指令微调,因为你同时提供了视觉信息和指令,并且知道正确答案,你可以优化模型生成正确输出词元的概率。在此过程中,模型学会了理解图像。
大语言视觉模型的应用 💡
在通过视觉指令微调训练语言模型之后,它现在可以同时处理图像和文本。你现在可以将这个模型视为一个大型多模态模型。你也可以询问关于图像中物体的问题。例如,要求对图像内容进行详细的结构描述,比如“为我描述这张图片”。

实践环节:代码示例 💻
现在让我们在实践中看看这一切。在这个实验中,你将使用图像和文本作为输入,然后让大语言视觉模型对其进行推理。

首先,我们添加一个命令来忽略所有不必要的警告。然后进行一些设置。在本课中,我们将使用 Gemini Pro。我们需要加载 API 密钥,并导入一个生成库,我们需要将密钥传入其中。
我们加载一些辅助函数。现在我们需要一个函数,它能接收一段文本并将其提取并转换为可读的 Markdown 格式。
接着,我们构建一个允许我们调用大语言视觉模型的函数。该函数将接收一个图像路径和一个提示词。我们需要做的第一件事是加载该图像,接下来需要调用 Gemini Pro 的生成模型,传入提示词和加载的图像。最后,我们需要返回结果,这里我们将使用那个转换为 Markdown 的函数,它将从响应中提取文本并解析成美观易读的格式。
现在我们可以开始分析一些图像了。我们将从这张漂亮的历史指数图表开始。
我们调用大语言视觉模型函数,提供文件和一个提示词:“解释你在这张图片中看到了什么”。我们将尝试让大语言视觉模型分析这个图表,这通常需要几秒钟。
现在我们看到了一个很好的描述,基本上说图像显示了标普500指数的历史图表,并为我们提供了相当不错的分析。这可能非常有帮助。
现在让我们尝试分析一些更复杂的内容。我们将使用幻灯片中用过的图表,并要求大语言视觉模型帮助我们解释这个图表的实际用途。

这是结果。在这里你可以看到,模型识别出这是一张用于对比预训练框架的图像,这实际上非常准确,然后解释了关于文本和图像的不同编码器类型等等。也许我应该用它来准备这节课。

隐藏信息的趣味示例 🕵️
这里有一个有趣的例子。如果你看这个,它只是一个绿色方块,没有什么特别的。我很好奇当大语言视觉模型试图分析这个时能得出什么结论。让我们问问大语言视觉模型,看看它是否能发现这张图片有什么特别之处。

执行后,我们可以看到大语言视觉模型识别出有一条隐藏信息,写着“你可以用 Weaviate 向量化整个世界”。让我们尝试运行一个函数来显示这条信息隐藏在哪里。

在这里,我们实际上是在寻找第一个通道中值超过 120 的任何部分。



运行这个,我们可以看到这就是真正隐藏在那里的信息。任何超过 120 的部分变成了白色,低于 120 的部分是黑色。这就是大语言视觉模型能够解码信息并告诉我们它的方式。
所以,大语言视觉模型看待事物的方式与我们不同,它们实际上能看到更多,并对正在查看的图像更具探究性,这就是它们能够解码这类信息的原因。我将在本笔记的资源中包含一个函数,如果你想创建像这样带有隐藏信息的图像,你将能够轻松做到,并发送给你的朋友。
总结 📝
在本节课中,我们学习了如何使用图像视觉模型,以及如何结合文本提示词实际分析图像。在下一节课中,你将构建一个多模态 RAG 系统。下节课见。


005:5.L4 多模态检索增强生成 (MM-RAG)


在本节课中,我们将学习多模态检索增强生成的概念,其核心是将多模态搜索与语言视觉模型相结合。然后,我们将使用 Weaviate 和一个大型多模态模型来实现完整的多模态 RAG 流程。
概述


在本节课中,我们将要学习多模态检索增强生成。我们将首先理解大型语言模型在知识上的局限性,然后介绍检索增强生成作为解决方案。接着,我们将把多模态向量数据库的能力与大型多模态模型结合,构建一个完整的 MM-RAG 工作流。最后,我们将通过代码实践,实现从查询到检索图像,再到生成描述的完整过程。

大型语言模型的局限性
大型语言模型的一个根本问题是,它们不具备训练数据之外的信息知识。


因此,如果你试图向它们询问它们没有的信息,它们要么回答不知道,要么更可能地,它们会“幻觉”并编造一个答案,这通常更糟糕。
解决方案:检索增强生成
针对上述问题,一个潜在的解决方案是检索增强生成。
在这里,你做的不是仅仅向语言模型提供一个提示形式的问题,而是同时给它一个问题以及检索到的相关信息。
现在,模型可以执行“读取-生成”操作,即在回答问题前先读取相关信息。这样,输出结果就能基于你提供的信息进行定制。
扩展 RAG 解决方案
通常,如果你想扩展 RAG 解决方案,你会希望将所有文档存入像 Weaviate 这样的向量数据库中。
然后,你可以使用提示词从向量数据库中检索最相关的文档。
并将这些相关文档与提示词一起传递到大型语言模型的上下文窗口中。
这种方式帮助你的语言模型基于提供的上下文生成回答。
迈向多模态 RAG
正如你已经看到的,向量数据库能够检索的远不止文本。
因此,让我们利用 Weaviate 的多模态知识库来存储和搜索图像、视频和文本。
在上图中,你可以看到从我们的多模态向量数据库中检索图像,然后将该图像与文本指令一起传递给大型多模态模型,从而得到一个基于对世界的多模态理解的回答。
这个过程被称为多模态检索增强生成,因为你正在用多模态数据的检索来增强生成过程。
实践环节
现在让我们在实践中看看这一切。在这个实验中,你将使用图像和文本作为输入。
然后让大型多模态模型对其进行推理,从而完成完整的 RAG 工作流。
和之前的课程一样,让我们从一个忽略所有不必要警告的小命令开始,然后我们就可以开始了。
现在我们需要做的是加载必要的 API 密钥。实际上,我们将使用前两节课中使用的两个密钥的组合,即嵌入 API 密钥和我们用于视觉模型的密钥。

连接到 Weaviate 实例
现在你有了所需的 API 密钥,是时候连接到 Weaviate 实例了。这次我们要做的是使用一个特殊的备份系统,这是因为我们创建了一个包含 30,000 张预向量化图像的数据集,这样你就可以非常快速地导入它们,而无需等待。
为了恢复我们承诺的这些图像,我们基本上需要运行下面这个小命令,我们指定了要连接的资源,但最重要的是,我们将加载新数据集的集合名称是 resources。我们可以执行这个命令,这大约需要 5 到 10 秒,然后就应该准备好了。

现在我们可以非常快速地预览集合中对象的数量。
我们获取集合对象,运行一个聚合函数来统计内部的所有对象,并按媒体类型分组。然后我们可以根据每个分组得到的结果进行打印。运行这个,我们可以看到我们有超过 13000 张图片和 200 个视频。本节课我们不一定会使用视频,但如果你愿意,之后可以尝试查询它们。
实现完整的多模态 RAG
现在我们进入有趣的部分:分两步运行完整的多模态 RAG。
第一步是发送查询并从数据库检索内容。我们将把它作为一个名为 retrieve_image 的函数来实现。给定一个查询,我们想要获取一张图片。
在第一部分,我们基本上需要获取我们的 resources 集合。
现在我们有了 resources 集合,我们调用一个近文本查询。给定函数中的查询,我们还提供了一个过滤器,因为在这种情况下,我们只想获取稍后将传递给视觉模型的图像,并且我们只对图像的路径感兴趣,我们只返回一个对象。
然后,一旦我们得到结果,我们将获取第一个对象,获取其属性,并从该函数中只返回我们通过近文本查询找到的图像路径。
总之,如果我们运行这个函数并给定一个查询,我们应该得到一个与查询匹配的图像 URL。
测试检索功能

现在你可以测试 retrieve_image 函数。尝试查询“fish with my buddies”怎么样?
然后如果你运行这个,你应该会得到类似这样的结果:你可以看到这里有一个男人拿着一条鱼,可能图片上的狗被识别为那个男人的伙伴。
如果你运行一个不同的查询,并且你的查询在数据集中没有完全对应的表示,你可能会得到令人惊讶的结果,但不要因此而气馁,这是游戏的一部分。


所以请随意尝试不同类型的查询,只是有一个小提醒:你得到的结果类型取决于数据集中已有的内容,所以如果你搜索不存在或没有完全表示的东西,你可能会得到一些令人惊讶的结果。现在你已经完成了检索部分,我们可以继续进行生成部分。
生成部分
对于生成部分,你将遵循与上一课相同的步骤。
你需要为生成模型设置 API 密钥。
并且像上一课一样,设置辅助函数来将输出转换为 Markdown 格式,以及 call_llm 函数,该函数给定一个图像路径和提示词,可以生成对图像的漂亮描述。
最后,为了完成循环,你将调用 call_llm 函数,参数是来自检索步骤的图像路径(那是第一步)和描述。你应该能够执行这个,这需要几秒钟,然后你应该得到一张描述一个男人拿着鱼、旁边有一只狗的图片的描述。
在这里你可以看到我得到的描述,它谈到一个戴着绿色帽子、穿着卡其色背心的男人手里拿着一条大鱼,等等,甚至谈到了站在男人旁边的狗。你可能得到的描述与我的不同,但这是大型语言模型逐令牌生成回答的一部分。
整合所有功能
现在你可以把所有功能整合在一起。让我们创建一个 mm_rag 函数,第一步是调用 retrieve_image 函数,输出将保存在 source_image 变量中。
第二步是调用 call_llm 函数,参数是上一步的 source_image 和提示词,这应该会返回描述。
让我们执行这个。
最后,你可以调用完整的 mm_rag 函数。你可以搜索类似“paragliding through the mountains”的内容,这应该既能抓取一张图片,最后也能提供对该图片的描述,就像这样。看,效果很好,你有一张某人滑翔伞的漂亮图片,并且有一段描述:有人在郁郁葱葱的绿色山脉上空滑翔伞。我相信你会得到非常相似的结果。


就这样,你能够将第二课和第三课的两个不同部分——检索部分和生成部分——结合起来,从而实际获得一个多模态 RAG 功能。


现在,Weaviate 实例已关闭。
总结
在本节课中,我们一起学习了如何将检索与生成模型结合起来。尽管这两个是完全不同的模型,但你能够构建出将两者结合成一个强大功能的东西。在下一课中,你将学习如何将其应用到工业应用中,并在许多不同的现实用例中尝试。我们下节课见。


006:行业应用 🏢


在本节课中,你将通过实现现实生活中的例子,学习多模态技术如何在工业界被应用。你将分析像发票和流程图这样的图像内容,以生成不同格式和风格的结构化输出。让我们开始构建。



在本节课中,你将探索多模态在工业界的三种不同应用。


在第一个例子中,输入是一张包含结构化数据的图像,例如收据或发票。然后,你将从这张图像中提取结构化的字段和值,并将其转换为 JSON 格式。



在第二个例子中,你将从一个公司的投资者演示文稿中的表格开始,提取出一个 Markdown 表格表示形式,这个表格随后可以被处理和使用。
在第三个例子中,你将让一个语言视觉模型对逻辑流程图进行推理,并让它输出实现该逻辑流程的文本甚至 Python 代码。


好的,让我们开始编码。在这里,你将使用与之前课程几乎相同的设置。
我们需要忽略警告,加载 API 密钥,然后设置 GenI 库。这很好。同样,你将使用几乎相同的辅助函数,其中一个函数将输出转换为 Markdown。
但对 call_LMM 函数有一个小的修改,增加了一个 plain_text 布尔参数。这样做的目的是,根据我们的需求,我们可以选择将输出作为纯文本返回,或者作为 Markdown 输出,这在后面会很有用。
def call_LMM(prompt, image_path, plain_text=False):
# ... 函数实现 ...
if plain_text:
return response.text
else:
return markdown(response.text)
作为第一个例子,你将使用视觉模型来分析这张发票。现在让我们提一个问题:给定发票文件,尝试识别发票上的项目,然后将结果以 JSON 格式输出。我们寻找的是数量、描述、单价和金额。让我们运行这个,看看视觉模型能从这张图中提取出什么。
prompt = """
Given this invoice image, identify the items and output the results as JSON.
Look for: quantity, description, unit price, amount.
"""
response = call_LMM(prompt, "invoice.png", plain_text=True)
print(response)
现在,哇,我们看到第二个项目是“一套新的踏板臂”,单价 15,总金额 30,这与表格上的内容完全匹配,非常准确。这真是太棒了。
那么,如何基于输入提出一个推理问题呢?也许你可以检查一下,购买四套踏板臂和六小时的劳动力需要多少钱。这实际上很有趣,因为视觉模型需要从描述中提取信息,然后计算价格。
让我们运行这个,看看它在类似计算上的表现如何。哦,看这个,它实际上能够推导出踏板臂的价格,但可能我认为最棘手的部分是“6小时劳动力,每小时5美元”,它竟然能正确计算出总价为30,然后全部加起来是90美元。太棒了。


对于第二个例子,你将分析这个包含不同业务单元财务信息的表格。你可以看到一些关于收入、利润率和同比增长的信息。让我们给它一个提示。
第一个任务是将你在这里看到的内容打印成 Markdown 表格。这样它应该能够分析我们看到的每个部分,并以良好的结构进行格式化。
prompt = "Print the contents of this table as a Markdown table."
response = call_LMM(prompt, "financial_table.png")
print(response)
如果你只看第一项,“食品配送”对应 17%, 12p 和 15%,这非常准确。


现在让我们尝试更多。运行一个类似的查询,但这次添加一个推理信息。我们想找出哪个业务单元的收入增长最高。
prompt = """
Print the contents of this table as a Markdown table.
Then, reason about which business unit has the highest revenue growth.
"""
response = call_LMM(prompt, "financial_table.png")
print(response)
现在我们看到结果,可以看到“分类广告”以 32% 被模型识别出来。如果我们快速扫描一下,可以发现这确实是正确答案。它实际上与“支付和金融科技”持平,但这仍然是正确答案,所以我给它两个赞。
作为最后一个例子,你将使用视觉模型来分析这个流程图。让我们尝试问它一些问题。


为了分析这个流程图,让我们提供这样一个提示:要求视觉模型以编号列表的形式,提供图像中流程图的总结性分解。
prompt = "Provide a summarized breakdown of the flowchart in the image in the format of a numbered list."
response = call_LMM(prompt, "flowchart.png", plain_text=True)
print(response)
在这里,我们逐一得到了从开始到结束的每个项目。如果我们看一下,从客户下订单开始,然后进入支付环节,这是发票的一部分,依此类推。


你甚至可以进一步推动视觉模型。你可以要求它分析图像中的流程图,并输出实现该流程的单个函数的代码。
prompt = "Analyze the flowchart in the image and output Python code that would implement this as a single function."
response = call_LMM(prompt, "flowchart.png", plain_text=True)
print(response)
问题是,由于视觉模型存在一定的随机性,如果我们重新运行相同的函数,每次运行都应该能得到一个不同的函数。
在我的第二次尝试中,我得到了一个非常不同的代码,这个看起来详细得多。如果我向下滚动,可以看到函数被完整地实现了。从技术上讲,我们应该能够在另一个单元格中执行它。
让我们看看这个函数是否能执行。我可以运行这个,它可能会中断,因为它期望像 client 这样的参数。但至少这对我们来说是一个很好的起点,我们可以以此为基础来实现我们自己的代码。这实际上非常强大,对我们许多人来说可能非常有帮助。
😊
总结
在本节课中,你学习了如何在不同类型的图像上使用视觉模型,要求它提供不同的解释并提取各种信息,同时还添加了额外的推理命令,看看是否能推动这些视觉模型进一步工作,例如基于原始值计算新值。这非常强大。
我鼓励你自己动手尝试其他事情。在下一节课中,你将学习一个多模态推荐系统,在该系统中,你将能够并排使用两个不同的模型来执行跨不同模态的搜索。
很棒,我们下节课见。


007:L6 多模态推荐系统 🎬

在本节课中,我们将学习如何构建一个多模态推荐系统。我们将使用 Weaviate 来创建一个多向量推荐引擎,该系统能够通过搜索项目的不同模态表示来推荐相关项目。

搜索与推荐的区别 🔍
首先,我们来思考搜索与推荐有何不同。搜索是客观的。它不依赖于搜索者是谁。只要结果在语义上与查询匹配,搜索就可以被认为是成功的,无需为用户个性化结果。例如,如果用户搜索“篮球球衣”,返回以下任何一件球衣都是正确的。


推荐则相反,它是主观的。它非常依赖于提问者是谁。假设你的商店销售篮球球衣,一位顾客来询问球衣。你需要了解这位顾客的更多信息才能提供正确的回应。例如,如果来的是湖人队球迷,那么你能推荐的最佳球衣就是这些湖人队球衣。另一方面,如果来的是波士顿凯尔特人队球迷,那么最佳推荐就是凯尔特人队球衣。
多模态数据的优势 📊
多模态数据为个性化推荐系统提供了一个绝佳的解决方案,它允许你利用所有可用的模态来捕捉用户的兴趣。让我们看一个例子。如果我让你描述你心目中的完美汉堡,你可以用文字写下描述,也可以给我看一张图片,或者告诉我关于这个汉堡的营养信息。

我可以利用关于你完美汉堡的所有这些信息来识别你的好恶。我还可以利用这些独立的模态来个性化推荐。为此,你可以将所有信息捕获到一个向量,甚至是多向量表示中,其中每个向量都来自一个专门的模型,并允许你搜索该模态。这很重要,因为有些人可能因为产品的外观而购买,另一些人可能因为详细的描述而购买,而其他人则可能基于营养信息来选择。
实践环节:构建多模态电影推荐系统 🛠️
现在,让我们在实践中看看这一切。在本实验中,你将加载一个嵌入了多模态电影数据的数据集,使用两个独立的机器学习模型,然后在不同的向量空间中运行文本和多模态查询。
环境设置

与之前的所有课程一样,你将从忽略所有不必要的警告开始,并加载必要的 API 密钥。这次我们将使用两个不同的密钥:一个用于多模态嵌入(与我们之前使用的模型相同),另一个用于 OpenAI 的文本嵌入。然后像这样加载它们。
import weaviate
import openai
# 加载 API 密钥
openai.api_key = "your_openai_api_key"
weaviate_api_key = "your_weaviate_api_key"
接下来,连接到 Weaviate 的嵌入式实例。这次我们将使用两个不同的模块:一个用于文本,一个用于多模态。我们必须将这两个 API 密钥作为请求头的一部分传递。
client = weaviate.Client(
embedded_options=weaviate.embedded.EmbeddedOptions(),
additional_headers={
"X-OpenAI-Api-Key": openai.api_key,
"X-MultiModal-Api-Key": weaviate_api_key
}
)
运行此代码后,你将连接到一个嵌入式的 Weaviate 实例。


创建集合
现在你已连接到 Weaviate,接下来将创建一个名为 Movie 的新集合。为了帮助你,这里已经预定义了一些属性,我们可以预期有 title、overview、vote_average、release_year 等。还有一个非常重要的属性:poster_path,用于存放图片路径。
因为这是一个多向量集合,首先需要为你的文本嵌入配置一个命名向量。你将这个向量空间称为 text_vector。具体来说,对于这个命名向量,你希望对 title 和 overview 进行向量化,并忽略所有其他属性。
对于第二个向量空间,你将添加第二个命名向量,这将是多模态向量。我们希望在 poster 字段内的图像字段上运行它,同时指定我们需要运行的特定模型和所有附加信息。
为了澄清,这个命名向量空间将被称为 poster_vector。

# 定义集合模式
schema = {
"classes": [{
"class": "Movie",
"properties": [
{"name": "title", "dataType": ["string"]},
{"name": "overview", "dataType": ["text"]},
{"name": "vote_average", "dataType": ["number"]},
{"name": "release_year", "dataType": ["int"]},
{"name": "poster_path", "dataType": ["string"]}
],
"vectorizers": [
{
"name": "text_vector",
"vectorizer": "text2vec-openai",
"properties": ["title", "overview"]
},
{
"name": "poster_vector",
"vectorizer": "multi2vec-clip",
"properties": ["poster_path"]
}
]
}]
}
client.schema.create(schema)

执行此操作将为你创建一个新集合。
如果需要重新运行并再次创建此集合,可以取消注释下面这行代码,它将为你删除集合,然后你可以重新创建它。请注意,如果运行此代码,你也会丢失当时该集合中的所有数据。
# client.schema.delete_class("Movie") # 谨慎使用
导入数据
现在你有了一个准备好的集合,可以开始导入数据了。在这个项目中,有一个 movies_data.json 文件,其中列出了 20 部不同的电影及其海报。让我们看看里面有什么。你可以看到有一个指向海报的链接,在这里称为 poster_path。如果我滚动浏览列,你可以看到你已经拥有了所有必要的数据,如原始标题、概述、流行度,以及非常重要的海报路径等。
接下来,你需要加载一个辅助函数,该函数将获取文件路径并将其转换为 base64 表示,这你在之前的课程中已经做过了。
import base64
def image_to_base64(file_path):
with open(file_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
现在你拥有了所有构建模块,是时候导入数据了。首先需要做的事情之一是获取 movies 集合,然后在该集合上创建一个批处理对象,速率限制为 20,这基本上意味着你希望每分钟只上传 20 张图片,这有助于我们应对服务的速率限制。然后,使用数据框,我们可以遍历预加载数据集中的所有电影(只有 20 部,这也符合我们设定的速率限制)。
这里的这个语句非常有用,因为使用在顶部导入的 generate_uuid,我们可以提供一个电影 ID,如果该电影 ID 已经存在于我们的数据集中,那么我们可以跳过它。因此,即使你重新运行这个单元格,也不会导入同一部电影两次,这非常有用。
接下来,当你遍历数据框中的所有对象时,你需要解析海报文件的路径(该路径位于 poster 文件夹内),然后逐一将其转换为 base64 表示。
然后,利用这些信息构建一个电影对象,该对象将包含标题、概述、平均评分、ID、海报路径和海报的 base64 编码。
最后,你现在需要做的就是调用批处理对象的 add 方法,传递电影对象作为属性,然后从电影 ID 生成一个 UUID(该 ID 在早期阶段用于验证我们没有重复导入相同的内容)。运行此代码,导入我们所有的图片。

import pandas as pd
import uuid
# 加载数据
df = pd.read_json('movies_data.json')
# 获取集合
movies = client.collections.get("Movie")
# 创建批处理对象
with movies.batch.dynamic() as batch:
batch.batch_size = 20 # 速率限制
for index, row in df.iterrows():
# 检查是否已存在
existing = movies.query.fetch_object_by_id(str(row['id']))
if existing:
continue
# 转换图片为 base64
poster_b64 = image_to_base64(row['poster_path'])
# 构建数据对象
movie_object = {
"title": row['title'],
"overview": row['overview'],
"vote_average": row['vote_average'],
"release_year": row['release_year'],
"poster_path": row['poster_path'],
"poster_b64": poster_b64
}
# 添加到批处理
batch.add_object(
properties=movie_object,
uuid=uuid.uuid5(uuid.NAMESPACE_DNS, str(row['id']))
)
现在正在发生的是,这 20 部电影已被发送到两个不同的向量化器。在这里,我们将海报发送到多模态模型进行向量化,同时标题和描述也被发送到 OpenAI 的文本模型,对标题和描述进行向量化。这大约需要一分钟。
之后,你应该可以开始查询这个数据集了。作为最后一步,你需要检查导入是否顺利。你可以通过进入 movies 批处理对象并查找失败的对象(如果有的话)来做到这一点。因为这是一个列表,你可以打印它,或者打印第一个。如果没有错误,我们应该会得到一条“导入完成,无错误”的快乐消息,这很好。
执行查询

现在,是时候进入有趣的部分了,你可以在数据上运行一些查询。让我们搜索一些关于可爱宠物的电影。这个查询与你之前运行的非常相似,但是,因为我们使用的是多向量空间,集合配置了两个不同的向量,你需要指定要搜索哪个向量空间。对于这个查询,你希望跨 text_vector 空间进行搜索。
# 在文本向量空间搜索
response = movies.query.near_text(
query="lovable cute pets",
limit=3,
target_vector="text_vector"
)


for obj in response.objects:
print(f"Title: {obj.properties['title']}")
print(f"Overview: {obj.properties['overview']}")
# 显示海报(假设有方法显示 base64 图片)
display_poster(obj.properties['poster_b64'])
请注意,这个特定的查询只会尝试根据标题和概述中的内容进行匹配,因为文本向量就是基于这些构建的。运行这个查询。



你可以看到,我的第一个结果是《101 忠狗》,以及描述和电影海报。我还得到了《精灵鼠小弟》的海报及其标题和描述,第三个回应是《虫虫特工队》,同样附有其描述和电影海报。


现在,让我们在同一个向量空间上尝试另一个查询,但这次让我们寻找“史诗级超级英雄”。
response = movies.query.near_text(
query="epic superheroes",
limit=3,
target_vector="text_vector"
)
你可以看到,我们得到了《钢铁侠》、《超人总动员 2》等结果。

你现在可能在想,这个查询是基于文本向量嵌入的,但我们仍然在这里得到了海报。是的,确实如此。查询仍然只在标题和概述上运行。然而,我们返回的对象包含有关海报的信息。这就是我们能够显示它们的原因,但海报尚未用于查询。这将在下一步中实现。

跨海报搜索
要实际跨海报进行搜索,你需要运行类似的查询,但在 poster_vector 向量空间上运行。通过运行这个,我们将尝试匹配那些海报反映了“可爱宠物”这一查询的电影。



# 在多模态向量空间搜索
response = movies.query.near_text(
query="lovable cute pets",
limit=3,
target_vector="poster_vector"
)
你可以看到,结果非常相似,但并不完全相同。然而,它们确实与原始查询非常匹配,所以“可爱宠物”的主题在我们返回的这三张海报中都清晰可见。


让我们在这里尝试另一个查询,再次在 poster_vector 空间搜索,但这次你想寻找“史诗级超级英雄”,以便与在文本向量空间中搜索时得到的结果进行比较。
同样,结果非常相似,但又不完全相同,因为我们在海报中得到的信息与电影标题和描述中的信息略有不同。如果你有更多的数据,两者之间的差异可能会更大。但在我们拥有的这个只有 20 张不同图片的数据集中,很可能会有重复。但如果你有成千上万张图片,那么两者可能都匹配得很好,但不一定会返回完全相同的对象。
使用图片进行搜索
你可以对数据做的另一件事是使用图片在 poster_vector 空间上跨海报进行搜索。让我们从这张“恐怖场景”的图片开始。



要使用这张恐怖图片进行搜索,你需要调用 near_image 方法,将这张恐怖图片转换为 base64,并希望在 poster_vector 空间上进行搜索。完成后,让我们只显示标题和海报,使输出更清晰,然后执行此操作。
你得到的是恐怖电影的海报,比如《圣诞夜惊魂》、《女巫也疯狂》和《惊声尖叫》。

作为最后一个例子,让我们尝试根据这张图片搜索一些超级英雄。

搜索非常相似,但这次只是使用不同的图片。我们在这里得到的是《蝙蝠侠大战超人:正义黎明》,还有《钢铁侠》。

好吧,我们得承认,这只追逐坚果的松鼠绝对是我心目中的超级英雄。



清理工作

现在你已经完成了所有查询,可以关闭客户端并完成本课。
client.close()
总结 📝

本节课到此结束,本课程也告一段落。在本节课中,你学习了如何使用多向量空间,然后跨两个不同的向量空间进行搜索:一个是文本向量空间,另一个是多模态空间,并从不同的角度理解数据。正如你所看到的,不同向量空间之间的一些结果可能是相似的,但每个向量空间和每个模型都可能给你不同类型的结果,这可能非常强大,也非常有用。我相信你将会有很多想法和用例,来利用这一点并充分发挥其潜力。
008:总结 🎉

在本课程中,我们学习了多模态搜索与检索增强生成(RAG)的核心概念与实践方法。从理解多模态的基础知识开始,到构建实际的搜索系统,再到集成大型多模态模型,并最终扩展至工业应用,我们完成了一次全面的技术探索。
课程回顾
上一节我们探讨了多模态RAG在工业场景中的实际应用。现在,让我们对整个课程内容进行总结。
以下是我们在本课程中逐步掌握的核心知识与技能:
- 多模态概念:我们首先学习了多模态的基本理念,即系统能够同时处理和整合来自不同模态(如文本、图像、音频)的信息。
- 构建多模态搜索:接着,我们运用这些概念,动手构建了一个多模态搜索系统。其核心原理可以概括为:将不同模态的数据(如图片和文本)映射到同一个向量空间,然后通过计算向量之间的相似度(例如使用余弦相似度公式:
similarity = (A·B) / (||A|| * ||B||))来检索相关内容。 - 大型多模态模型基础:然后,我们深入了解了大型多模态模型(LMMs) 的工作原理。这些模型,如GPT-4V,能够理解和生成跨模态的内容,是多模态RAG系统的“大脑”。
- 实现多模态RAG:我们将以上所有知识整合,构建了一个完整的多模态RAG系统。该系统的基本流程是:
用户查询 -> 多模态检索 -> 获取相关上下文 -> LMM生成答案。 - 扩展至工业应用:最后,我们进一步将多模态RAG系统扩展到了更实际的工业应用场景中,探讨了其解决复杂现实问题的潜力。
结语
本节课中,我们一起学习了从多模态基础理论到构建高级多模态RAG应用的全过程。你已经掌握了相关的核心概念与实践技能。
期待看到你运用这些知识创造出令人惊叹的应用。😊


浙公网安备 33010602011771号