【AI大模型】RAG 与 Embedding

目录

什么是RAG

Embedding

检索方式

文本检索

向量检索

向量间的相似度计算

向量数据库

主流向量数据库功能对比

基于向量检索的 RAG

更换国产模型¶

OpenAI 新发布的两个 Embedding 模型

实战 RAG 系统的进阶知识

文本分割的粒度

检索后排序

混合检索(Hybrid Search)

RAG-Fusion

向量模型的本地加载与运行

RAG 效果差排查步骤


什么是RAG

        RAG(Retrieval Augmented Generation)检索增强生成,顾名思义,通过检索的方法来增强生成模型的能力。RAG可以理解为是Function Calling 的一种水平补充。如果大模型需要调外部API才能完成任务,那就使用Function Calling。如果大模型需要检索知识库才能完成回答,那就使用 RAG。

Embedding

       Embedding  向量文本,是一种将高维复杂数据转换为低维向量表示的技术,解决了数据处理和语义捕捉的问题。比如,我们根据一句话找出与这句话语义相近的一段话,就用用到将这两段文字转为 Embedding ,存储在向量数据库中。

检索方式

文本检索

     文本检索即我们传统的文档数据库检索,比如 es 。这种检索的特点是只能完全字面匹配,同一个语义换个说法可以就无法检索到。

向量检索

    根据语句的相似度检索。语义相似的文本,他们的向量相关度会比高。这个特性依赖于向量模型的向量算法。

向量间的相似度计算

 向量模型的训练: 

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

     举例:使用OpenAI的 Embedding 模型接口:text-embedding-ada-002 来计算两个文档的相关度。

      OpenAI的 text-embedding-ada-002 是24年初为止,最好的文本向量模型,它甚至可以计算一个英文文本和中文文本的相关性。后来 OpenAI 已经发布新的更优秀的模型了。


from openai import OpenAI
import numpy as np
from numpy import dot
from numpy.linalg import norm
client = OpenAI()
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]


test_query = ["测试文本"]
vec = get_embeddings(test_query)[0]
print(f"Total dimension: {len(vec)}")
print(f"First 10 elements: {vec[:10]}")


# 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.00
Query与Documents的余弦距离:
0.7622376995981269
0.7564484035029618
0.7426558372998221
0.7077987135264395
0.7254230492369407

Query与自己的欧氏距离: 0.00
Query与Documents的欧氏距离:
0.6895829747071625
0.697927847429074
0.7174178392255148
0.7644623330084925
0.7410492267209755

向量数据库

向量数据库,是专门为向量检索设计的中间件。向量数据库解决的是数据存储和快速检索的功能,向量模型是解决数据相似度计算的问题。

澄清几个关键概念:

  • 向量数据库的意义是快速的检索;
  • 向量数据库本身不生成向量,向量是由 Embedding 模型产生的;
  • 向量数据库与传统的关系型数据库是互补的,不是替代关系,在实际应用中根据实际需求经常同时使用。

主流向量数据库功能对比

    上述前四个相对来说是市面上主流的向量数据库。其中,FAISS 是 脸书 早期出的一个引擎,它的上层应用封装不是很完善,所以不推荐使用。Pinecone、Milvus、Weaviate是近两年火起来的创业公司发布的,后两个都是开源的。Pinecone使用非常方便,但是它不开源,且云服务器在国外。Milvus性能上有很大的优势,推荐使用。

基于向量检索的 RAG

class RAG_Bot:
    def __init__(self, vector_db, llm_api, n_results=2):
        self.vector_db = vector_db
        self.llm_api = llm_api
        self.n_results = n_results

    def chat(self, user_query):
        # 1. 检索
        search_results = self.vector_db.search(user_query, self.n_results)

        # 2. 构建 Prompt
        prompt = build_prompt(
            prompt_template, context=search_results['documents'][0], query=user_query)

        # 3. 调用 LLM
        response = self.llm_api(prompt)
        return response
# 创建一个RAG机器人
bot = RAG_Bot(
    vector_db,
    llm_api=get_completion
)

user_query = "llama 2有多少参数?"

response = bot.chat(user_query)

print(response)
llama 2有7B, 13B, 和70B参数。

更换国产模型

import json
import requests
import os

# 通过鉴权接口获取 access token
def get_access_token():
    """
    使用 AK,SK 生成鉴权签名(Access Token)
    :return: access_token,或是None(如果错误)
    """
    url = "https://aip.baidubce.com/oauth/2.0/token"
    params = {
        "grant_type": "client_credentials",
        "client_id": os.getenv('ERNIE_CLIENT_ID'),
        "client_secret": os.getenv('ERNIE_CLIENT_SECRET')
    }

    return str(requests.post(url, params=params).json().get("access_token"))

# 调用文心千帆 调用 BGE Embedding 接口
def get_embeddings_bge(prompts):
    url = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/embeddings/bge_large_en?access_token=" + get_access_token()
    payload = json.dumps({
        "input": prompts
    })
    headers = {'Content-Type': 'application/json'}

    response = requests.request(
        "POST", url, headers=headers, data=payload).json()
    data = response["data"]
    return [x["embedding"] for x in data]


# 调用文心4.0对话接口
def get_completion_ernie(prompt):
    url = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro?access_token=" + get_access_token()
    payload = json.dumps({
        "messages": [
            {
                "role": "user",
                "content": prompt
            }
        ]
    })

    headers = {'Content-Type': 'application/json'}

    response = requests.request(
        "POST", url, headers=headers, data=payload).json()

    return response["result"]
# 创建一个向量数据库对象
new_vector_db = MyVectorDBConnector(
    "demo_ernie",
    embedding_fn=get_embeddings_bge
)
# 向向量数据库中添加文档
new_vector_db.add_documents(paragraphs)

# 创建一个RAG机器人
new_bot = RAG_Bot(
    new_vector_db,
    llm_api=get_completion_ernie
)
user_query = "how many parameters does llama 2 have?"

response = new_bot.chat(user_query)

print(response)
Llama 2的变体有7B、13B和70B三种参数。同时,还训练了34B的变体,但在本文中报告并未发布。所以,Llama 2的参数有7B、13B和70B三种可选择的变体。

OpenAI 新发布的两个 Embedding 模型

  2024 年 1 月 25 日,OpenAI 新发布了两个 Embedding 模型

  • text-embedding-3-large
  • text-embedding-3-small

  其最大特点是,支持自定义的缩短向量维度,从而在几乎不影响最终效果的情况下降低向量检索与相似度计算的复杂度。text-embedding-ada-002 只支持1536维计算。

通俗的说:越大越准、越小越快。 官方公布的评测结果:

注:MTEB 是一个大规模多任务的 Embedding 模型公开评测集 

model = "text-embedding-3-large"
dimensions = 128

# query = "国际争端"

# 且能支持跨语言
query = "global conflicts"

documents = [
    "联合国就苏丹达尔富尔地区大规模暴力事件发出警告",
    "土耳其、芬兰、瑞典与北约代表将继续就瑞典“入约”问题进行谈判",
    "日本岐阜市陆上自卫队射击场内发生枪击事件 3人受伤",
    "国家游泳中心(水立方):恢复游泳、嬉水乐园等水上项目运营",
    "我国首次在空间站开展舱外辐射生物学暴露实验",
]

query_vec = get_embeddings([query], model=model, dimensions=dimensions)[0]
doc_vecs = get_embeddings(documents, model=model, dimensions=dimensions)

print("向量维度: {}".format(len(query_vec)))

print()

print("Query与Documents的余弦距离:")
for vec in doc_vecs:
    print(cos_sim(query_vec, vec))

print()

print("Query与Documents的欧氏距离:")
for vec in doc_vecs:
    print(l2(query_vec, vec))

这种可变长度的 Embedding 技术背后的原理叫做 Matryoshka Representation Learning

实战 RAG 系统的进阶知识

文本分割的粒度

缺陷

  1. 粒度太大可能导致检索不精准,粒度太小可能导致信息不全面
  2. 问题的答案可能跨越两个片段

改进: 按一定粒度,部分重叠式的切割文本,使上下文更完整

检索后排序

问题: 有时,最合适的答案不一定排在检索的最前面

方案:

  1. 检索时多招回一部分文本
  2. 通过一个排序模型对 query 和 document 重新打分排序

混合检索(Hybrid Search)

在实际生产中,传统的关键字检索(稀疏表示)与向量检索(稠密表示)各有优劣。

举个具体例子,比如文档中包含很长的专有名词,关键字检索往往更精准而向量检索容易引入概念混淆。

RAG-Fusion

    将一个问题换几个角度提问,然后取各次返回结果都比较靠前的。

向量模型的本地加载与运行

rom sentence_transformers import SentenceTransformer

model_name = 'BAAI/bge-large-zh-v1.5' #中文
# model_name = 'moka-ai/m3e-base'  # 中英双语,但效果一般
# model_name = 'BAAI/bge-m3' # 多语言,但效果一般

model = SentenceTransformer(model_name)
query = "国际争端"
# query = "global conflicts"

documents = [
    "联合国就苏丹达尔富尔地区大规模暴力事件发出警告",
    "土耳其、芬兰、瑞典与北约代表将继续就瑞典“入约”问题进行谈判",
    "日本岐阜市陆上自卫队射击场内发生枪击事件 3人受伤",
    "国家游泳中心(水立方):恢复游泳、嬉水乐园等水上项目运营",
    "我国首次在空间站开展舱外辐射生物学暴露实验",
]

query_vec = model.encode(query)

doc_vecs = [
    model.encode(doc) for doc in documents
]

print("Cosine distance:")  # 越大越相似
# print(cos_sim(query_vec, query_vec))
for vec in doc_vecs:
    print(cos_sim(query_vec, vec))

重点:

  1. 不是每个 Embedding 模型都对余弦距离和欧氏距离同时有效
  2. 哪种相似度计算有效要阅读模型的说明(通常都支持余弦距离计算)

RAG 效果差排查步骤

  1. 检查预处理效果:文档加载是否正确,切割的是否合理
  2. 测试检索效果:问题检索回来的文本片段是否包含答案
  3. 测试大模型能力:给定问题和包含答案文本片段的前提下,大模型能不能正确回答问题
  • 14
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

季风泯灭的季节

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值