案例目标
本案例展示如何使用LlamaIndex、Nomic Embed和Anthropic构建一个多模态RAG(检索增强生成)系统。该系统能够同时处理文本和图像数据,实现跨模态的信息检索和生成,为用户提供更全面、更准确的答案。
技术栈与核心依赖
- LlamaIndex: 用于构建多模态索引和检索系统
- Nomic Embed Text v1.5: 用于文本嵌入的模型
- Nomic Embed Vision v1.5: 用于图像嵌入的模型
- Anthropic Claude 3: 用于多模态推理和生成
- Qdrant: 向量数据库,用于存储文本和图像嵌入
- Wikipedia API: 用于获取维基百科文章和图像
环境配置
%pip install llama-index-vector-stores-qdrant llama-index-multi-modal-llms-anthropic llama-index-embeddings-nomic %pip install llama-index ftfy regex tqdm %pip install matplotlib scikit-image %pip install -U qdrant_client %pip install wikipedia
需要设置以下环境变量:
import os os.environ["NOMIC_API_KEY"] = "your_nomic_api_key" os.environ["ANTHROPIC_API_KEY"] = "your_anthropic_api_key"
案例实现
步骤1: 下载维基百科文章文本
从维基百科API获取指定主题的文章文本,并保存到本地文件夹。
from pathlib import Path import requests wiki_titles = [ "batman", "Vincent van Gogh", "San Francisco", "iPhone", "Tesla Model S", "BTS", ] data_path = Path("data_wiki") for title in wiki_titles: response = requests.get( "https://en.wikipedia.org/w/api.php", params={ "action": "query", "format": "json", "titles": title, "prop": "extracts", "explaintext": True, }, ).json() page = next(iter(response["query"]["pages"].values())) wiki_text = page["extract"] if not data_path.exists(): Path.mkdir(data_path) with open(data_path / f"{title}.txt", "w") as fp: fp.write(wiki_text)步骤2: 下载维基百科图像
从维基百科页面下载相关图像,并为每个图像分配唯一ID。
import wikipedia import urllib.request from pathlib import Path import time image_path = Path("data_wiki") image_uuid = 0 image_metadata_dict = {} MAX_IMAGES_PER_WIKI = 30 wiki_titles = [ "San Francisco", "Batman", "Vincent van Gogh", "iPhone", "Tesla Model S", "BTS band", ] if not image_path.exists(): Path.mkdir(image_path) for title in wiki_titles: images_per_wiki = 0 print(title) try: page_py = wikipedia.page(title) list_img_urls = page_py.images for url in list_img_urls: if url.endswith(".jpg") or url.endswith(".png"): image_uuid += 1 image_file_name = title + "_" + url.split("/")[-1] image_metadata_dict[image_uuid] = { "filename": image_file_name, "img_path": "./" + str(image_path / f"{image_uuid}.jpg"), } req = urllib.request.Request( url, data=None, headers={ "User-Agent": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36" }, ) with urllib.request.urlopen(req) as response, open( image_path / f"{image_uuid}.jpg", "wb" ) as out_file: out_file.write(response.read()) images_per_wiki += 1 if images_per_wiki > MAX_IMAGES_PER_WIKI: break time.sleep(1) except Exception as e: print(e) print(f"{images_per_wiki=}") continue步骤3: 构建多模态向量存储
使用Qdrant创建向量数据库,并分别存储文本和图像嵌入。
import qdrant_client from llama_index.core import SimpleDirectoryReader from llama_index.vector_stores.qdrant import QdrantVectorStore from llama_index.core import VectorStoreIndex, StorageContext from llama_index.core.indices import MultiModalVectorStoreIndex from llama_index.embeddings.nomic import NomicEmbedding # Create a local Qdrant vector store client = qdrant_client.QdrantClient(path="qdrant_db") text_store = QdrantVectorStore( client=client, collection_name="text_collection" ) image_store = QdrantVectorStore( client=client, collection_name="image_collection" ) storage_context = StorageContext.from_defaults( vector_store=text_store, image_store=image_store ) embedding_model = NomicEmbedding( model_name="nomic-embed-text-v1.5", vision_model_name="nomic-embed-vision-v1.5", ) # Create the MultiModal index documents = SimpleDirectoryReader("./data_wiki/").load_data() index = MultiModalVectorStoreIndex.from_documents( documents, storage_context=storage_context, embed_model=embedding_model, image_embed_model=embedding_model, )步骤4: 创建图像显示函数
定义一个函数来显示检索到的图像。
import matplotlib.pyplot as plt import os from PIL import Image def plot_images(image_paths): images_shown = 0 plt.figure(figsize=(16, 9)) for img_path in image_paths: if os.path.isfile(img_path): image = Image.open(img_path) plt.subplot(2, 3, images_shown + 1) plt.imshow(image) plt.xticks([]) plt.yticks([]) images_shown += 1 if images_shown >= 9: break
步骤5: 执行多模态检索
使用查询检索相关文本和图像。
test_query = "Who are the band members in BTS?" # generate retrieval results retriever = index.as_retriever(similarity_top_k=3, image_similarity_top_k=5) retrieval_results = retriever.retrieve(test_query)
步骤6: 显示检索结果
显示检索到的文本和图像。
retrieved_image = [] for res_node in retrieval_results: if isinstance(res_node.node, ImageNode): retrieved_image.append(res_node.node.metadata["file_path"]) else: display_source_node(res_node, source_length=200) plot_images(retrieved_image)
步骤7: 初始化多模态查询引擎
使用Anthropic的多模态模型创建查询引擎。
from llama_index.multi_modal_llms.anthropic import AnthropicMultiModal query_engine = index.as_query_engine( llm=AnthropicMultiModal(), similarity_top_k=2, image_similarity_top_k=1 )
步骤8: 执行多模态查询
使用查询引擎执行查询并获取结果。
response = query_engine.query( "What are Vincent van Gogh's famous paintings and popular subjects?" ) print(str(response))
案例效果
系统能够根据用户查询同时检索相关文本和图像,并使用Claude 3进行多模态推理。例如,当查询"梵高的著名画作和流行主题"时,系统能够检索到相关文本和图像,并生成以下回答:
"根据提供的上下文,文森特·梵高的一些最著名的画作和流行主题包括:
- 以大胆的颜色和戏剧性的笔触为特色的风景画、静物画、肖像画和自画像。这促进了现代艺术中表现主义的兴起。
- 在他的早期作品中,他主要描绘静物和农民劳动者。
- 1888年搬到法国南部的阿尔勒后,他的画作变得更加明亮,他开始关注描绘自然世界,包括当地的橄榄林、麦田和向日葵。
- 他一些售价超过1亿美元(按当今等价价格计算)的最昂贵的画作包括《加歇医生的肖像》、《约瑟夫·鲁林的肖像》和《鸢尾花》。
- 大都会艺术博物馆于1993年以5700万美元收购了他的画作《麦田与柏树》。
总而言之,梵高尤其以他居住过的地方(如阿尔勒)的充满活力、表现力强的风景画、肖像画以及向日葵、橄榄林和麦田等主题的静物画而闻名。他对色彩的大胆运用和厚重、戏剧性的笔触对后来的艺术运动产生了深远影响。"
案例实现思路
- 数据准备: 从维基百科获取多个主题的文本和图像数据,构建多模态数据集。
- 嵌入模型: 使用Nomic Embed的文本和视觉模型分别对文本和图像进行嵌入,将它们转换为向量表示。
- 向量存储: 使用Qdrant向量数据库分别存储文本和图像嵌入,创建两个不同的集合(collection)。
- 多模态索引: 使用LlamaIndex的MultiModalVectorStoreIndex创建多模态索引,将文本和图像关联起来。
- 检索机制: 根据查询同时检索相关文本和图像,使用不同的嵌入模型对查询进行编码。
- 多模态推理: 将检索到的文本和图像传递给Claude 3,进行多模态推理和生成。
扩展建议
- 更多数据源: 扩展到其他数据源,如新闻文章、科学论文、产品目录等。
- 高级检索策略: 实现更复杂的检索策略,如重排序、混合检索等。
- 多语言支持: 添加对多语言文本和图像的支持。
- 实时更新: 实现索引的实时更新机制,支持动态添加新数据。
- 用户界面: 开发友好的用户界面,支持图像上传和交互式查询。
- 性能优化: 优化嵌入和检索过程,提高系统响应速度。
- 评估指标: 开发专门的评估指标来衡量多模态RAG系统的性能。
总结
本案例展示了如何使用LlamaIndex、Nomic Embed和Anthropic构建一个高效的多模态RAG系统。该系统能够同时处理文本和图像数据,实现跨模态的信息检索和生成。通过将文本和图像嵌入存储在Qdrant向量数据库中,并使用Claude 3进行多模态推理,系统能够提供更全面、更准确的答案。这种多模态RAG方法在知识检索、内容分析和智能问答等领域具有广泛的应用前景。