包含示例 (Python) 代码的初学者友好指南
这是有关多模式人工智能的大型系列文章中的第三篇文章。在之前的文章中,我们分别讨论了多模式法学硕士和嵌入模型。在本文中,我们将结合这些想法来实现多模式 RAG 系统的开发。我将首先回顾关键概念,然后分享用于实现此类系统的示例代码。
GPT、LLaMA 和 Claude 等语言模型通过预训练学习了大量的世界知识。这使它们成为解决自定义问题和回答复杂问题的强大工具。
然而,有一些知识即使是最先进的语言模型也不了解。这包括组织内的专有信息、模型预训练数据收集后发生的事件以及互联网上不流行的专业知识。
尽管这种无知限制了模型的开箱即用功能,但有一种流行的技术可以克服这些限制:检索增强生成(或简称 RAG)。
RAG 是一种通过动态提供给定提示的相关上下文来提高模型响应质量的方法。下面是一个示例,说明何时这可能会有所帮助。
比如说,我忘记了一位同事在昨天的会议上提到的一个 Python 库的名称。 ChatGPT 无法帮助我解决此问题,因为它不知道会议的内容。
然而,RAG 可以通过回答我的问题(例如“Rachel 在昨天的会议中提到的 Python 库的名称是什么?”)来帮助解决此问题,自动提取会议记录,然后将我的原始查询和记录提供给法学硕士。
尽管使用 RAG 改进法学硕士可以解锁一些实际用例,但在某些情况下,相关信息以非文本格式存在,例如图像、视频、图表和表格。在这种情况下,我们可以更进一步,构建多模态 RAG 系统,即能够处理文本和非文本数据的 AI 系统。
多模态 RAG 可以实现超出仅通过文本传达的内容的更复杂的推理。例如,它可以分析某人的面部表情和讲话语气,为会议转录提供更丰富的背景信息。
虽然实现多模式 RAG (MRAG) 系统的方法有多种,但在这里我将重点关注三种基本策略,以提高复杂程度。
- 将模式翻译为文本。
- 纯文本检索 + MLLM
- 多模态检索+MLLM
以下讨论假设您已经对 RAG 和多模式模型有基本的了解。以下文章讨论了这些主题:RAG、多模态法学硕士和多模态嵌入。
第 1 级:将模式翻译为文本
使 RAG 系统成为多模态的一个简单方法是在将新模态存储到知识库之前将其转换为文本。这可以像将会议录音转换为文本笔录、使用现有的多模式 LLM (MLLM) 生成图像标题或将表格转换为可读文本格式(例如 .csv 或 .json)一样简单。
这种方法的主要优点是它只需要对现有 RAG 系统进行最少的更改。此外,通过显式生成非文本模态的文本表示,人们可以更好地控制要提取的数据的特征。例如,分析数据的标题可能包括描述和关键见解。
当然,这种策略的缺点是模型的响应不能直接使用非文本数据,这意味着从图像到文本的转换可能会造成关键的信息瓶颈。
级别 2:纯文本检索 + MLLM
另一种方法是生成知识库中所有项目的文本表示(例如描述和元标签)以供检索,但将原始模态传递给多模态LLM(MLLM)。例如,图像元数据用于检索步骤,并将关联图像传递给模型进行推理。
这保留了 1 级的许多优点,同时减轻了其局限性。也就是说,知识库中项目的文本特征可以针对搜索进行优化,但下游模型可以使用每个项目原始模态的全部丰富性。
这种方法的主要区别在于它需要 MLLM,即能够处理非文本数据的 LLM。这解锁了更高级的推理能力,正如 GPT-4o 或 LLaMA 3.2 Vision 等模型所证明的那样。
第3级:多模态检索+MLLM
虽然我们可以在第 1 级和第 2 级的检索过程中使用基于关键字的搜索,但通常的做法是使用所谓的矢量搜索。这包括生成知识库中项目的向量表示(即嵌入),然后通过计算输入查询与知识库中每个项目之间的相似性分数来执行搜索。
传统上,这要求查询和知识库项目是基于文本的。然而,正如我们在本系列的上一篇文章中看到的,存在多模态嵌入模型,可以生成文本和非文本数据的对齐向量表示。
因此,我们可以使用多模态嵌入来执行多模态检索。这与基于文本的向量搜索的工作方式相同,但现在嵌入空间将类似的概念独立于其原始模态而共同定位。这种检索策略的结果可以直接传递到 MLLM。
基本了解 Multimodal RAG 的工作原理后,让我们看看如何构建这样的系统。在这里,我将创建一个问答助手,它可以访问本系列前两篇博客中的文本和图形。
此示例的 Python 代码可在 GitHub 存储库中免费获取。
导入和数据加载
我们首先导入一些方便的库和模块。
导入 json
从转换器导入 CLIPProcessor、CLIPTextModelWithProjection
来自 torch import load、matmul、argsort
从 torch.nn.function 导入 softmax
接下来,我们将从 Multimodal LLM 和 Multimodal Embeddings 博客文章中导入文本和图像块。它们保存在 .json 文件中,可以作为字典列表加载到 Python 中。
# 加载文本块
使用 open('data/text_content.json', 'r',encoding='utf-8') 作为 f:
文本内容列表 = json.load(f)
# 加载图像
使用 open('data/image_content.json', 'r', encoding='utf-8') 作为 f:
image_content_list = json.load(f)
虽然我不会在这里回顾数据准备过程,但我使用的代码位于 GitHub 存储库上。
我们还将为text_content_list和image_content_list中的每个项目加载多模式嵌入(来自 CLIP)。这些被保存为 pytorch 张量。
# 加载嵌入
text_embeddings = load('data/text_embeddings.pt',weights_only=True)
image_embeddings = load('data/image_embeddings.pt',weights_only=True)
打印(text_embeddings.shape)
打印(image_embeddings.shape)
# >> 火炬.Size([86, 512])
# >> 火炬.Size([17, 512])
打印这些张量的形状,我们看到它们是通过 512 维嵌入表示的。我们有 86 个文本块和 17 个图像。
多模式搜索
加载知识库后,我们现在可以定义矢量搜索的查询。这将包括使用 CLIP 将输入查询转换为嵌入。我们的做法与上一篇文章中的示例类似。
# 询问
query = "CLIP 的对比损失函数是什么?"
# 嵌入查询(4步)
# 1) 加载模型
模型 = CLIPTextModelWithProjection.from_pretrained("openai/clip-vit-base-patch16")
# 2) 加载数据处理器
处理器 = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch16")
# 3) 预处理文本
输入=处理器(文本=[文本],return_tensors =“pt”,填充= True)
# 4) 使用 CLIP 计算嵌入
输出=模型(**输入)
# 提取嵌入
query_embed = 输出.text_embeds
打印(query_embed.shape)
# >> 火炬.Size([1, 512])
打印形状,我们看到有一个代表查询的向量。
要对知识库执行向量搜索,我们需要执行以下操作。
- 计算查询嵌入与所有文本和图像嵌入之间的相似度。
- 通过 softmax 函数将相似度重新调整为从 0 到 1 的范围。
- 对缩放后的相似度进行排序并返回前 k 个结果。
- 最后,过滤结果以仅将项目保留在预定义的相似性阈值之上。
下面是文本块的代码。
# 定义 k 和相似度阈值
k = 5
阈值 = 0.05
# 文章的多模式搜索
text_similarities = matmul(query_embed, text_embeddings.T)
# 通过softmax重新调整相似度
温度=0.25
text_scores = softmax(text_similarities/temp, dim=1)
# 返回前k个过滤后的文本结果
isorted_scores = argsort(text_scores, 降序=True)[0]
排序分数 = 文本分数[0][排序分数]
itop_k_filtered = [idx.item()
对于 idx,zip 中的分数(isorted_scores,sorted_scores)
if Score.item() >= 阈值][:k]
top_k = [text_content_list[i] for i in itop_k_filtered]
打印(top_k)
# 前 k 个结果
[{'article_title': '多模态嵌入:简介',
'section': '对比学习',
'text': 'CL 的两个关键方面有助于其有效性'}]
在上面,我们看到了顶部的文本结果。请注意,即使k = 5,我们也只有一项。这是因为第 2-5 项低于 0.1 阈值。
有趣的是,这个项目似乎对我们最初询问“CLIP 的对比损失函数是什么?”没有帮助。这凸显了矢量搜索的主要挑战之一:与给定查询相似的项目不一定有助于回答它。
我们可以缓解这个问题的一种方法是通过增加k并降低相似性阈值来对我们的搜索结果进行不那么严格的限制,然后希望法学硕士能够找出什么是有用的,什么是无用的。
为此,我首先将向量搜索步骤打包到 Python 函数中。
def相似性_搜索(query_embed, target_embeddings, content_list,
k=5,阈值=0.05,温度=0.5):
”“”
对嵌入执行相似性搜索并返回前 k 个结果。
”“”
# 计算相似度
相似度 = torch.matmul(query_embed, target_embeddings.T)
# 通过softmax重新调整相似度
分数= torch.nn.function.softmax(相似度/温度,暗淡= 1)
# 获取排序后的索引和分数
Sorted_indices = Scores.argsort(降序=True)[0]
排序分数 = 分数[0][排序索引]
# 按阈值过滤并获取前k个
过滤索引 = [
idx.item() for idx, zip 中的分数(sorted_indices,sorted_scores)
如果 Score.item() >= 阈值
][:k]
# 获取对应的内容项和分数
top_results = [filtered_indices 中 i 的 content_list[i]]
result_scores = [scores[0][i].item() for i infiltered_indices]
返回 top_results, result_scores
然后,设置更具包容性的搜索参数。
# 搜索文本块
文本结果,文本分数=相似性_搜索(查询嵌入,文本嵌入,
text_content_list,k=15,阈值=0.01,温度=0.25)
# 搜索图像
image_results, image_scores =相似性_搜索(query_embed, image_embeddings,
image_content_list,k=5,阈值=0.25,温度=0.5)
这会产生 15 个文本结果和 1 个图像结果。
1 - CL 的两个关键方面有助于其有效性
2 - 要进行类别预测,我们必须提取图像逻辑并评估
哪个类别对应于最大值。
3 - 接下来,我们可以导入剪辑模型的版本及其关联数据
处理器。注意:处理器处理输入文本和图像的标记
准备。
4 - 使用 CLIP 进行 0-shot 图像分类的基本思想是
将图像以及一组可能的类标签传递到模型中。然后,
可以通过评估哪个文本输入最相似来进行分类
输入图像。
5 - 然后我们可以通过提取文本将最佳图像与输入文本进行匹配
logits 并评估最大值对应的图像。
6 - 这些示例的代码可在 GitHub 存储库上免费获取。
7 - 我们(再次)看到模型完美地解决了这个简单的例子。但让我们尝试一下
一些更棘手的例子。
8 - 接下来,我们将预处理图像/文本输入并将它们传递到模型中。
9 - CLIP 等模型的另一个实际应用是多模态 RAG,它
包括将多模式上下文自动检索到法学硕士。在
本系列的下一篇文章,我们将了解其幕后工作原理以及
看一个具体的例子。
10 - CLIP 的另一个应用本质上是用例 1 的反面。
我们可以不识别哪个文本标签与输入图像匹配,而是
评估哪张图像(一组中)最匹配文本输入(即查询)——
换句话说,对图像进行搜索。
11 - 这引发了扩展法学硕士功能的努力,包括
多种方式。
12 - GPT-4o — 输入:文本、图像和音频。输出:text.FLUX — 输入:文本。
输出:images.Suno — 输入:文本。输出:音频。
13 - 对齐不同嵌入空间的标准方法是
对比学习(CL)。 CL 的一个关键直觉是代表不同的
对相同信息的看法类似[5]。
14 - 虽然模型对此预测的信心较低,为 54.64%
概率,它正确地暗示该图像不是模因。
15 - [8] Mini-Omni2:迈向具有视觉、语音和双工功能的开源 GPT-4o
能力
提示 MLLM
尽管大多数文本项结果似乎对我们的查询没有帮助,但图像结果正是我们正在寻找的。尽管如此,鉴于这些搜索结果,让我们看看 LLaMA 3.2 Vision 如何响应此查询。
我们首先将搜索结果构建为格式良好的字符串。
文本上下文=“”
对于 text_results 中的文本:
如果文本结果:
text_context = text_context + "**文章标题:** "
+ 文本['文章标题'] + "n"
text_context = text_context + "**部分:** "
+ 文本['节'] + "n"
text_context = text_context + "**片段:** "
+ 文本['文本'] + "nn"
图像上下文=“”
对于 image_results 中的图像:
如果图像结果:
image_context = image_context + "**文章标题:** "
+ 图片['文章标题'] + "n"
image_context = image_context + "**部分:** "
+ 图片['部分'] + "n"
image_context = image_context + "**图片路径:** "
+ 图像['图像路径'] + "n"
image_context = image_context + "**图像标题:** "
+ 图片['标题'] + "nn"
请注意每个文本和图像项目附带的元数据。这将帮助 LLaMA 更好地理解内容的上下文。
接下来,我们将文本和图像结果交错生成提示。
# 构造提示模板
提示 = f"""给定查询“{query}”和以下相关片段:
{文本上下文}
{图像上下文}
请对查询提供简洁、准确的答案,包括
尽可能从所提供的片段中获取相关信息。
”“”
最后的提示很长,这里就不打印了。但是,它在 GitHub 上的示例笔记本中完整显示。
最后,我们可以使用 ollama 将这个提示传递给 LLaMA 3.2 Vision。
ollama.pull('llama3.2-vision')
响应 = ollama.chat(
model='llama3.2-vision',
消息=[{
'角色':'用户',
'内容':提示,
'images': [image["image_path"] 用于 image_results 中的图像]
}]
)
print(响应['消息']['内容'])
该图像描绘了用于对齐文本和图像的对比损失函数
多模态模型中的表示。该功能旨在最大限度地减少
正对(文本-图像)和负对的相似度之间的差异
对(文本-文本或图像-图像)。这个损失函数在CLIP中常用,
它代表对比语言-图像预训练。
**关键组件:**
* **正对:** 文本-图像对,其中文本描述图像。
* **负对:** 不属于的文本-文本或图像-图像对
同一个班级。
* **对比损失函数:** 计算正值之间的差异
和负对的相似性。
**它是如何工作的:**
1. **文本-图像嵌入:** 生成文本和图像的嵌入
使用多模式编码器(例如,CLIP)。
2. **正对相似度:** 计算每个之间的相似度得分
文本-图像对。
3. **负对相似度:** 计算所有负对之间的相似度分数
负对。
4. **对比损失计算:** 通过以下方式计算对比损失
最小化正负对相似性之间的差异。
**好处:**
* **多模式对齐:** 对齐文本和图像表示以获得更好的效果
从文本描述中理解视觉内容。
* **提高性能:** 增强下游任务的性能,例如
图像分类、检索和生成。
该模型正确地识别出图像包含其所需的信息,并解释了其工作原理的一般直觉。然而,它误解了正负对的含义,认为负对对应于相同模态的一对。
当我们逐步了解实现细节时,我使用 GitHub 存储库上此笔记本中的 Gradio 将所有内容打包到一个漂亮的 UI 中。
YouTube-Blog/multimodal-ai/3-multimodal-rag 位于 main · ShawhinT/YouTube-Blog
用于补充 Medium 上的 YouTube 视频和博客文章的代码。 – YouTube-Blog/multimodal-ai/3-multimodal-rag 主要 ·…
github.com
多模态 RAG 系统可以综合以多种格式存储的知识,扩展人工智能的可能性。在这里,我们回顾了开发此类系统的 3 个简单策略,然后看到了多模式博客 QA 助手的示例实现。
尽管该示例对于本次演示来说效果足够好,但搜索过程存在明显的局限性。一些可以改进这一点的技术包括使用重新排序器来细化相似性搜索结果并通过微调的多模态嵌入来提高搜索质量。
如果您想查看有关这些主题的未来帖子,请在评论中告诉我:)
有关多式联运模型的更多信息👇
肖·塔勒比
多模态人工智能
查看列表 3 个故事
版权声明
本文为本站原创内容,转载需注明文章来源(https://www.eiefun.com),另:文中部分素材可能会引用自其他平台,如有侵权或其它,请联系 admin@eiefun.com,我们会第一时间配合删除