Llamaindex+Llava实现多模态RAG(入门)

        最近在做RAG相关的内容,发现多模态的问答加上本地知识库在垂直领域具有非常广泛的应用。本文第一部分会简单介绍RAG的基本知识,第二部分讲一下llamaindex的基础知识(有两部分没有讲,请自行参考官方文档),第三部分构建多模态的RAG系统。(注意,本文RAG模型全部部署在本地,需要20G以上的显卡!!

目录

1、 RAG基本知识

2、Llama-index基础知识

2.1 Loading

2.2 Indexing

2.3 Storing

3、创建多模态的RAG


1、 RAG基本知识

        RAG(Retrieval-Augmented Generation)大白话来说就是:增强检索(RA)和生成(G)。之所以提出RAG的概念主要还是为了解决大语言模型(如chatGPT)的模型幻觉问题,说白了就是回答问题不准确。并且首先于先验知识的局限,很多LLM对最新的知识无法回答。由此,RAG就此诞生。以下是RAG的简单框架图:1)用户提问+指令;2)转换为query(Q);3)检索本地知识库+增强内容;4)发送到LLM对知识进行增强;5)更具检索增强后的知识生成回答。(图片来源于:什么是 RAG?— 检索增强生成详解 — AWS

2、Llama-index基础知识

        那么问题来了,如何高效的构建知识库以及优化RAG的完整流程呢?如今的技术栈例如Langchain、Llama-index、FlowiseAI以及AutoGen。这些技术栈优化了RAG的整体流程,但有些对于新手来说确实不是很友好,主要原因包括个人认为是内容过于冗杂。在经过多次尝试后,决定从llama-index入手(后面发现和langchain的原理差不多)。

        Llama-index是一个非常强大的技术栈,对RAG任务具有非常的针对性。简单来说,可以把RAG的流程拆解为:loading、indexing、storing、querying以及evaluation(非常精简,并且每一部分的操作性非常强,类似于一个骨架)。

2.1 Loading

        这部分我认为最重要的一个概念就是document和nodes的概念,有点类似于知识图谱。我做了一个简单的图方便大家理解,这里我以一个pdf文档为例。首先一个pdf文档通过llama-index内置的工具可以把每一页pdf拆分为若干个Documents。但每个Document会包含很多很多内容,这样其实不利于检索,即使检索也会产生很多无关的上下文,增加token的消耗。所以,llama-index进一步将document拆分为nodes,更加精细化检索内容。

        Node的概念十分重要,node可以简单的理解为chunk。每个chunk包含文字,你可以自定义chunk的大小以及overlap(为了上下文的连续性)的大小。huggingface上面有个space可以可视化chunk/node的概念https://huggingface.co/spaces/m-ric/chunk_visualizer

        Llama-index为我们提供了多种node的模式,包括:BaseNode、TextNode(储存文本)、ImageNode(储存图像)以及IndexNode(仅储存索引)。但我们将document划分为chunk/node之后,我们可以查看每个节点的具体信息,请查看我分享在github上面的jupyter代码:Llama-index LoadingContribute to MMHHRR/urban_llmRAG development by creating an account on GitHub.icon-default.png?t=N7T8https://github.com/MMHHRR/urban_llmRAG/blob/main/llama-index/1.%20llamaindex_loading.ipynb        可以发现,Llama-index为每个node配置了一个id_(也可以自己设置),以及metadata、relationship以及text。需要注意的是llama-index里面为每个node匹配了一个relationship,如下:

SOURCE: The node is the source document.

PREVIOUS: The node is the previous node in the document.

NEXT: The node is the next node in the document.

PARENT: The node is the parent node in the document.

CHILD: The node is a child node in the document.

2.2 Indexing

        另一个重要的概念是indexing,可以参考我的github。我们可以从document以及node直接创建索引(index),非常简单的一个示例:

Llama-index IndexingContribute to MMHHRR/urban_llmRAG development by creating an account on GitHub.icon-default.png?t=N7T8https://github.com/MMHHRR/urban_llmRAG/blob/main/llama-index/2.%20llamaindex_Indexing.ipynb

document1 = Document(
    text="This is a super-customized document",
    metadata={
        "file_name": "super_secret_document.txt",
        "category": "finance",
        "author": "LlamaIndex"})
document2 = Document(
    text="Hello world!",
    metadata={
        "file_name": "computersss.txt",
        "category": "RAG",
        "author": "Human"})
##或者这样写,把多个document用[.. , ..]连接
document = [document1, document2]
document

         你还可以从node创建index。此外,Llama-index还为我们提供了management工具,我们可以轻易的插入节点(insertation)、删除节点(delete)、升级节点(updata)以及更新节点(refresh)。当然还可以追踪节点的变化。

print(index.ref_doc_info)
2.3 Storing

        可以先跑一下storing的代码。Storing具有多种储存模式,你可以将Document、Index、Vector储存在本地或者其他数据库中(例如Qdrant),其目的就是为了更加便捷的对文档或者图像进行检索。可以想象一下,数据被embeding后储存在strore中,然后根据query进行搜索(余弦相似度等搜索方法)。比如MultiModalVectorStoreIndex()方法可以将多模态数据进行储存。Llama-index StoringContribute to MMHHRR/urban_llmRAG development by creating an account on GitHub.icon-default.png?t=N7T8https://github.com/MMHHRR/urban_llmRAG/blob/main/llama-index/3.%20llamaindex_storing.ipynb

3、创建多模态的RAG

        首先导入包,以及设置系统的embeding模型。(注意:Llama-index更新的太快了,很多文档都不一样,本文使用的是现在时间(2024-4-1)的最新版本!)。其次,本文使用ollama本地化LLM模型,所以要装一下包。我提供了完整的代码,小伙伴们可以自己尝试一下!urban_llmRAG/coda/llamaindex_multimodal.ipynb at main · MMHHRR/urban_llmRAG · GitHubContribute to MMHHRR/urban_llmRAG development by creating an account on GitHub.icon-default.png?t=N7T8https://github.com/MMHHRR/urban_llmRAG/blob/main/coda/llamaindex_multimodal.ipynb

pip install llama_index
pip install qdrant_client
pip install llama_index-vector_stores-qdrant
pip install llama_index-embeddings-clip
pip install llama_index-multi_modal_llms-ollama
pip install llama_index-embeddings-huggingface
import qdrant_client
from llama_index.core import (ServiceContext, 
                               SimpleDirectoryReader,
                               SimpleDirectoryReader,
                               StorageContext,
                               Settings)
from llama_index.core.schema import ImageNode
from llama_index.core.schema import ImageDocument
from llama_index.vector_stores.qdrant import QdrantVectorStore
from llama_index.core.response.notebook_utils import display_source_node
from llama_index.core.indices.multi_modal.base import MultiModalVectorStoreIndex
from llama_index.embeddings.clip import ClipEmbedding
from llama_index.multi_modal_llms.ollama import OllamaMultiModal
from llama_index.embeddings.huggingface import HuggingFaceEmbedding

import os
import io 
import json
from PIL import Image 
import matplotlib.pyplot as plt

##设置embeding 模型和llva模型
Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")
mm_model = OllamaMultiModal(model="llava")

        解析一下原始的json文档,由于是图像问答(多模态),图片的text和对应的图像路径会储存在json中。

import json
with open('E:/RAG_LLM/metadata.json') as f:
	doc = json.load(f)
      
document = []
for obj in doc:
    caption = obj.get("Caption")
    captions = str(caption).replace("'", "").replace("[", "").replace("]", "")
    obj['City'] = str(obj.get("Image Name")).split('_')[3].split('.')[0]
    image_path = obj.get("Relative Path")
    image_paths = str(image_path)

    obj.pop("Caption", None)
    obj.pop("Relative Path", None)
    
    document.append([image_paths, captions, obj])
document[0]

# print--> 
['./test_img\\1.274615_103.797243_50f561a0fdc9f065f0005614_Singapore.JPG',
 'a car driving down a street next to a large bridge over a highway with a lot of trees',
 {'Image Name': '1.274615_103.797243_50f561a0fdc9f065f0005614_Singapore.JPG',
  'livelier': '4.514021433',
  'more beautiful': '5.15325436',
  'more boring': '4.950422095',
  'more depressing': '6.346440519',
  'safer': '2.17046243',
  'wealthier': '4.583960576',
  'City': 'Singapore'}]

        好的,我们现在将每个json储存在node里面,接着创建多模态的索引。

test_image_nodess =[ImageNode(image_path=p, text=t,metadata=k) for p,t,k in document]
test_image_nodess[0]

##MultiModalVectorStoreIndex可以储存多模态vector
multi_index = MultiModalVectorStoreIndex(test_image_nodess, show_progress=True)

        接着我们需要创建一个检索器。llamaindex提供了多种检索方式,在多模态vectorstore的基础上,我们可以直接使用多模态进行检索(图像或文本)、图像到图像检索、以及文本到图像检索。

##使用.as_retriever创建一个检索器
urban_retrieve = multi_index.as_retriever(similarity_top_k=3, image_similarity_top_k=3)
query_str = 'a building on the side of street in Singapore?'
img_path = '20.672456_-103.411312_514135aafdc9f04926004ab6_Guadalajara.JPG'

img, txt, score, metadata = retrieve_display(urban_retrieve.retrieve(query_str))  ##多模态检索

# img, txt, score, metadata = retrieve_display(urban_retrieve.image_to_image_retrieve(img_path))  ##图像到图像检索

# img, txt, score, metadata = retrieve_display(urban_retrieve.text_to_image_retrieve(img_path))  ##文本到图像检索

##将检索完成的图像/本文/元数据储存在这里
image_documents = [ImageDocument(image_path=img_path)]
for res_img in img:
    image_documents.append(ImageDocument(image_path=res_img))
context_str = "".join(txt)
metadata_str = metadata

##可视化一下
print(score)
plot_images(img)  ## visualization

        最后使用多模态的llm生成结果或者进一步问答。

##使用prompt,融入检索信息进行生成回答
## LLM templet
qa_tmpl_str = (
    "Given the provided information, including retrieved contents and metadata, \
     accurately and precisely answer the query without any additional prior knowledge.\n"
    "Please ensure honesty and responsibility, refraining from any racist or sexist remarks.\n"
    "---------------------\n"
    "Context: {context_str}\n"     ## 将上下文信息放进去
    "Metadata: {metadata_str} \n"  ## 将原始的meta信息放进去
    "---------------------\n"
    "Query: {query_str}\n"
    "Answer: "
)
query_str = 'a building on the side of street in Singapore?'

## use'.complete' invoke LLM
response = mm_model.complete(
    prompt=qa_tmpl_str.format(
        context_str=context_str,
        metadata_str=metadata_str,
        query_str=query_str, 
        ),
    image_documents=image_documents,
    )

        当然你还可以评估RAG的性能,这里暂时不讲,建议参考一下博文:https://itnext.io/visualize-your-rag-data-eda-for-retrieval-augmented-generation-0701ee98768ficon-default.png?t=N7T8https://itnext.io/visualize-your-rag-data-eda-for-retrieval-augmented-generation-0701ee98768fhttps://towardsdatascience.com/visualize-your-rag-data-evaluate-your-retrieval-augmented-generation-system-with-ragas-fc2486308557icon-default.png?t=N7T8https://towardsdatascience.com/visualize-your-rag-data-evaluate-your-retrieval-augmented-generation-system-with-ragas-fc2486308557

本文参考以下内容:

【1】https://docs.llamaindex.ai/en/stable/getting_started/concepts/

【2】https://docs.llamaindex.ai/en/stable/examples/multi_modal/image_to_image_retrieval/

【3】https://arxiv.org/abs/2312.10997(入门必看的论文)

  • 20
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值