掘金 人工智能 2024年07月06日
大模型之RAG-基于向量检索的理论与实战,对比关键字检索方案
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文介绍了基于向量检索的RAG实现方案,并与关键字检索方案进行了对比。首先解释了向量检索的基本原理,包括向量定义、文本向量化以及向量相似度计算。接着介绍了向量数据库的概念和特点,并以chromadb为例,展示了如何使用向量数据库进行向量检索。最后,通过一个实战例子,演示了如何使用OpenAI的Embedding模型和chromadb构建一个基于文档向量检索的RAG系统。

😊 **向量检索的原理**:向量检索是基于文档和查询之间的相似度计算来进行检索的,它将文档和查询表示为高维空间中的向量,通过计算它们之间的相似度来确定最相关的文档。 向量检索的优势在于能够考虑文档和查询之间的语义相似度,而不是仅仅依靠关键字匹配,从而提高检索精度。 例如,对于查询“国际争端”,向量检索可以识别出包含“全球冲突”等同义词的文档,而关键字检索则可能只匹配包含“国际争端”字眼的文档。

🧐 **向量数据库的介绍**:向量数据库是一种专门用于存储和检索向量数据的数据库,它能够高效地管理高维大数据,并利用索引技术和向量检索算法实现快速响应。 向量数据库与传统数据库的不同之处在于它支持对向量数据的检索,而传统数据库则主要针对结构化数据的检索。 向量数据库的应用场景包括图像、文本和音视频等非结构化数据的相似性搜索和检索,例如根据图像内容查找相似图片,根据文本语义查找相关文档。

🚀 **RAG实战例子**:本文通过一个实战例子演示了如何使用OpenAI的Embedding模型和chromadb构建一个基于文档向量检索的RAG系统。 整个流程包括: 1. 文档加载:读取PDF文件并提取文本内容。 2. 文档切割:将文本内容分割成多个片段,并考虑交叠以确保上下文完整性。 3. 向量化:使用OpenAI的Embedding模型将文本片段转换成向量。 4. 灌入向量库:将向量数据存储到chromadb中。 5. 检索向量数据库:根据查询内容检索向量库,获取最相关的文档片段。 6. 将检索数据带入提示词:构建包含检索结果的提示词,用于引导大模型生成回复。 7. 调用大模型:使用OpenAI的ChatCompletion接口,根据提示词生成回复。

🤔 **向量检索与关键字检索的对比**:向量检索和关键字检索是两种不同的检索方式,各有优劣。 向量检索更适合处理复杂的语义匹配问题,而关键字检索则更适合简单的关键字匹配需求。 在选择检索方式时,需要根据具体的应用场景进行权衡。

💪 **总结**:本文介绍了基于向量检索的RAG实现方案,并与关键字检索方案进行了对比,帮助读者更好地理解RAG的两种实现方式,以及它们之间的优劣。 RAG的核心在于检索增强,而检索增强的主要手段是知识库。通过引入外部知识库,可以提高大模型的性能和效果。 随着大模型技术的不断发展,RAG将成为AIGC领域中越来越重要的技术方向。

前言

RAG系列的讲解,我们之前和大家分享了RAG的流程、文档切分、基于关键字检索的方案。

关键字检索的认识与实战一文中,我们讲到了基于关键字检索的局限性:关键字检索可能会受到一些问题的影响,例如同义词、拼写错误等,这可能会导致一些相关的文档被漏掉或者一些不相关的文档被检索到。

今天再来和大家一起分享基于向量检索的方案与实战,再结合关键字检索方案做一下多维度的对比。让我们对RAG的实现方案能够加深一些理解,在面对不同场景中,选择合适的方案。

向量检索的定义与原理

什么是向量

向量是一种有大小和方向的数学对象。它可以表示为从一个点到另一个点的有向线段。例如,二维空间中的向量可以表示为 (?,?)(?,?),表示从原点 (0,0)(0,0) 到点 (?,?)(?,?) 的有向线段。

以此类推,我可以用一组坐标 (?0,?1,…,??−1)(?0,?1,…,??−1) 表示一个 ?? 维空间中的向量,?? 叫向量的维度。

文本向量(Text Embeddings)

    将文本转成一组 ?? 维浮点数,即文本向量又叫 Embeddings向量之间可以计算距离,距离远近对应语义相似度大小

文本向量是怎么得到的

    构建相关(正立)与不相关(负例)的句子对儿样本训练双塔式模型,让正例间的距离小,负例间的距离大

向量间的相似度计算

我们用检索关键词和一组文本的样例来看下效果

余弦距离和欧氏距离的核心逻辑

def cos_sim(a, b):    '''余弦距离 -- 越大越相似'''    return dot(a, b)/(norm(a)*norm(b))def l2(a, b):    '''欧氏距离 -- 越小越相似'''    x = np.asarray(a)-np.asarray(b)    return norm(x)    def get_embeddings(texts, model="text-embedding-ada-002", dimensions=None):    '''封装 OpenAI 的 Embedding 模型接口'''    if model == "text-embedding-ada-002":        dimensions = None    if dimensions:        data = client.embeddings.create(            input=texts, model=model, dimensions=dimensions).data    else:        data = client.embeddings.create(input=texts, model=model).data    return [x.embedding for x in data]    # query = "国际争端"# 且能支持跨语言query = "global conflicts"documents = [    "联合国就苏丹达尔富尔地区大规模暴力事件发出警告",    "土耳其、芬兰、瑞典与北约代表将继续就瑞典“入约”问题进行谈判",    "日本岐阜市陆上自卫队射击场内发生枪击事件 3人受伤",    "国家游泳中心(水立方):恢复游泳、嬉水乐园等水上项目运营",    "我国首次在空间站开展舱外辐射生物学暴露实验",]    

执行并输出结果

query_vec = get_embeddings([query])[0]doc_vecs = get_embeddings(documents)print("Query与自己的余弦距离: {:.2f}".format(cos_sim(query_vec, query_vec)))print("Query与Documents的余弦距离:")for vec in doc_vecs:    print(cos_sim(query_vec, vec))print()print("Query与自己的欧氏距离: {:.2f}".format(l2(query_vec, query_vec)))print("Query与Documents的欧氏距离:")for vec in doc_vecs:    print(l2(query_vec, vec))

我们来看下执行的效果:

Query与自己的余弦距离: 1.00Query与Documents的余弦距离:0.7622749944010915(越大越相似)0.75630381064935840.74266658025790380.70792736996080060.7254355321045072Query与自己的欧氏距离: 0.00Query与Documents的欧氏距离:0.6895288502682277(越小越相似)0.69813496379987690.71740287464922770.76429398336368290.7410323668625171

向量数据库

向量数据库(Vector Database),也叫矢量数据库,主要用来存储和处理向量数据。

再结合刚才我们对向量定义的描述,图像、文本和音视频这种非结构化数据都可以通过某种变换或者嵌入学习转化为向量数据存储到向量数据库中,从而实现对图像、文本和音视频的相似性搜索和检索。

这意味着您可以使用向量数据库根据语义或上下文含义查找最相似或相关的数据,而不是使用基于精确匹配或预定义标准查询数据库的传统方法。也就是我们提到的关键字检索的局限性。

向量数据库的特点

这里我们为了方便使用向量数据库完成向量检索的方案,简单介绍下向量数据库的特点:

向量数据库的主要特点是高效存储与检索。利用索引技术和向量检索算法能实现高维大数据下的快速响应。向量数据库也是一种数据库,除了要管理向量数据外,还是支持对传统结构化数据的管理。实际使用时,有很多场景会同时对向量字段和结构化字段进行过滤检索,这对向量数据库来说也是一种挑战。

严格来说数据向量化本不属于向量数据库,但是数据向量化又是一项很重要的工作,为了流程的完整性暂且放进去。区别与传统数据库主要有以下几个地方不相同:数据向量化,向量检索和相似度计算。

chromadb的简单介绍

之所以介绍一下chromadb,下面我们的实战demo就是基于chromadb来实现。

Chroma的目标是帮助用户更加便捷地构建大模型应用,更加轻松的将知识(knowledge)、事实(facts)和技能(skills)等我们现实世界中的文档整合进大模型中。

Chroma提供的工具:

流向量数据库功能对比

由于大模型的火热,现在市面上的向量数据库众多,主流的向量数据库对比如下所示:

向量数据库URLGitHub StarLanguage
chromahttps://github.com/chroma-core/chroma7.4KPython
milvushttps://github.com/milvus-io/milvus21.5KGo/Python/C++
pineconehttps://www.pinecone.io/
qdranthttps://github.com/qdrant/qdrant11.8KRust
typesensehttps://github.com/typesense/typesense12.9KC++
weaviatehttps://github.com/weaviate/weaviate6.9KGo

表格引用自:一文全面了解向量数据库的基本概念、原理、算法、选型

一个基于文档向量检索的RAG实战例子

我们再回顾RAG的基本流程,对照如下例子,大家就可以更好理解了

RAG系统搭建的基本流程

    准备对应的垂域资料文档的读取解析,进行文档切分将分割好的文本灌入检索引擎(向量数据库)封装检索接口构建流程:Query -> 检索 -> Prompt -> LLM -> 回复文档加载
def extract_text_from_pdf(filename,page_numbers=None,min_line_length=10):    """从 PDF 文件中(按指定页码)提取文字"""    paragraphs = []    buffer = ''    full_text = ''    # 提取全部文本    for i, page_layout in enumerate(extract_pages(filename)):        # 如果指定了页码范围,跳过范围外的页        if page_numbers is not None and i not in page_numbers:            continue        for element in page_layout:            if isinstance(element, LTTextContainer):                full_text += element.get_text() + '\n'    # 按空行分隔,将文本重新组织成段落    lines = full_text.split('\n')    for text in lines:        if len(text) >= min_line_length:            buffer += (' '+text) if not text.endswith('-') else text.strip('-')        elif buffer:            paragraphs.append(buffer)            buffer = ''    if buffer:        paragraphs.append(buffer)    return paragraphs
    文档切割(交叠切割防止问题的答案跨两个片段,使上下文更完整)
def split_text(paragraphs,chunk_size=300,overlap_size=100):    """按指定 chunk_size 和 overlap_size 交叠割文本"""    sentences = [s.strip() for p in paragraphs for s in sent_tokenize(p)]    chunks = []    i= 0    while i < len(sentences):        chunk = sentences[i]        overlap = ''        prev_len = 0        prev = i - 1        # 向前计算重叠部分        while prev >= 0 and len(sentences[prev])+len(overlap) <= overlap_size:            overlap = sentences[prev] + ' ' + overlap            prev -= 1        chunk = overlap+chunk        next = i + 1        # 向后计算当前chunk        while next < len(sentences) and len(sentences[next])+len(chunk) <= chunk_size:            chunk = chunk + ' ' + sentences[next]            next += 1        chunks.append(chunk)        i = next    return chunks
    向量化(这里使用openai的向量化模型)
def get_embedding(text, model="text-embedding-ada-002"):    """封装 OpenAI 的 Embedding 模型接口"""    return openai.Embedding.create(input=[text], model=model)['data'][0]['embedding']
    灌入向量库(使用chromadb)
def __init__(self, name="demo"):        self.chroma_client = chromadb.Client(Settings(allow_reset=True))        self.chroma_client.reset()        self.name = name        self.collection = self.chroma_client.get_or_create_collection(name=name)def add_documents(self, documents):        self.collection.add(            embeddings=[get_embedding(doc) for doc in documents],            documents=documents,            metadatas=[{"source": self.name} for _ in documents],            ids=[f"id_{i}" for i in range(len(documents))]        )
    检索向量数据库
def search(self, query, top_n):        """检索向量数据库"""        results = self.collection.query(            query_embeddings=[get_embedding(query)],            n_results=top_n        )        return results['documents'][0]
    将检索数据带入提示词
def build_prompt(template=prompt_template, **kwargs):    """将 Prompt 模板赋值"""    prompt = template    for k, v in kwargs.items():        if isinstance(v, str):            val = v        elif isinstance(v, list) and all(isinstance(elem, str) for elem in v):            val = '\n'.join(v)        else:            val = str(v)        prompt = prompt.replace(f"__{k.upper()}__", val)    return prompt
    调用大模型
def get_completion(prompt, context, model="gpt-3.5-turbo"):    """封装 openai 接口"""    messages = context + [{"role": "user", "content": prompt}]    response = openai.ChatCompletion.create(        model=model,        messages=messages,        temperature=0,  # 模型输出的随机性,0 表示随机性最小    )    return response.choices[0].message["content"]

向量检索与关键字检索的对比

维度向量检索关键字检索
检索方式基于文档和查询之间的相似度计算来进行检索。文档和查询通常被表示为高维空间中的向量,通过计算它们之间的相似度来确定最相关的文档。是通过匹配查询中的关键字与文档中的关键字来进行检索。当用户输入一个查询时,系统会在文档集合中查找包含这些关键字的文档,并将它们返回给用户。
表示方式需要将文档和查询转换成向量形式,这通常通过词袋模型或词嵌入等技术实现,其中每个维度代表一个词汇项,而向量的值通常表示词频或TF-IDF权重。不需要对文档和查询进行特殊的表示,而是直接基于文档中的关键字与查询中的关键字进行匹配。
匹配精度通常能够提供更精确的匹配,因为它考虑了文档和查询之间的语义相似度,而不仅仅是关键字的匹配。可能会受到一些问题的影响,例如同义词、拼写错误等,这可能会导致一些相关的文档被漏掉或者一些不相关的文档被检索到。
使用范围通常在需要考虑语义相似度的情况下使用,例如在自然语言处理领域中的文档检索、语义搜索等方面。更适合简单的检索场景,例如在搜索引擎中用户输入关键字进行网页检索。

总的来说,向量检索更适合处理复杂的语义匹配问题,而关键字检索则更适合简单的关键字匹配需求

总结

本文的分享结束,也代表着我们对向量检索的RAG如何实现,向量化以及向量数据库,同时用一个实战的例子讲解了向量检索的RAG如何完成。

再结合之前的文章我们对于关键字检索的RAG的讲解,我们能够充分的了解RAG的两种实现方式,以及他们之间的对比。

RAG的核心在于检索增强,而检索增强的主要手段是知识库,我们引入外部知识库可以是ES类似的关键字检索,也可以是Chroma类似的向量检索。

RAG已经是AIGC当中热门又尤为重要的一个方向,希望我们对于我们提高我们大模型的性能有所帮助。

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

RAG 向量检索 关键字检索 向量数据库 chromadb OpenAI 大模型
相关文章