【AI大模型】RAG 与 Embedding

季风泯灭的季节 2024-06-12 08:01:08 阅读 96

目录

什么是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 。这种检索的特点是只能完全字面匹配,同一个语义换个说法可以就无法检索到。

向量检索

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

向量间的相似度计算

 向量模型的训练: 

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

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

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

from openai import OpenAIimport numpy as npfrom numpy import dotfrom numpy.linalg import normclient = 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.00Query与Documents的余弦距离:0.76223769959812690.75644840350296180.74265583729982210.70779871352643950.7254230492369407Query与自己的欧氏距离: 0.00Query与Documents的欧氏距离:0.68958297470716250.6979278474290740.71741783922551480.76446233300849250.7410492267209755

向量数据

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

澄清几个关键概念:

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

主流向量数据库功能对比

FAISS: Meta 开源的向量检索引擎 https://github.com/facebookresearch/faissPinecone: 商用向量数据库,服务器在国外,使用非常简单。只有云服务 https://www.pinecone.io/Milvus: 开源向量数据库,同时有云服务 https://milvus.io/Weaviate: 开源向量数据库,同时有云服务 https://weaviate.io/Qdrant: 开源向量数据库,同时有云服务 https://qdrant.tech/PGVector: Postgres 的开源向量检索引擎 https://github.com/pgvector/pgvectorRediSearch: Redis 的开源向量检索引擎 https://github.com/RediSearch/RediSearchElasticSearch 也支持向量检索 https://www.elastic.co/enterprise-search/vector-search

    上述前四个相对来说是市面上主流的向量数据库。其中,FAISS 是 脸书 早期出的一个引擎,Pinecone、Milvus、Weaviate是近两年火起来的创业公司发布的。

基于向量检索的 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 jsonimport requestsimport os# 通过鉴权接口获取 access tokendef 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-largetext-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 系统的进阶知识

文本分割的粒度

缺陷

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

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

检索后排序

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

方案:

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

混合检索(Hybrid Search)

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

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

RAG-Fusion

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

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

rom sentence_transformers import SentenceTransformermodel_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))

重点:

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

RAG 效果差排查步骤

检查预处理效果:文档加载是否正确,切割的是否合理测试检索效果:问题检索回来的文本片段是否包含答案测试大模型能力:给定问题和包含答案文本片段的前提下,大模型能不能正确回答问题


声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。