1、概览
本文主要介绍RAG的基础实现过程,给初学者提供一些帮助,RAG即检索增强生成,主要是两个步骤:检索、生成,下面将基于这两部分进行介绍。
2、检索
检索的主要目的是在自定义的知识库kb中查询到与问题query相关的候选答案。过程中主要涉及的几个关键内容是:文本向量化模型、向量数据库,文本向量化模型如GTE、BGE等、向量数据库如faiss、weaviate、milvus等,对于选型本文不作介绍,读者可自行了解。
向量数据库主要用于存放文本向量化模型处理的知识库向量,并为检索相似候选答案提供快速的方法,下面以bge-large-zh及faiss库讲解一下检索的主要内容。
2.1 向量库构建
向量库构建前需要构建知识库,对知识库的处理五花八门,可以是pdf、docx、txt、pptx、web等等,本文以构建好的txt为例介绍,txt内容举例如下:
文本内容
文本内容
注:文本格式并无要求,可先随机复制一些文本内容,对于质量下文说明
文本内容的读取使用 langchain_community 工具封装好的方法,如下:
loader = TextLoader('test.txt')
当我们的文本内容校多时,可以遍历文件夹读取txt文件。
然后需要将文本内容转化为向量,内容如下,直接给出全部代码:
from langchain_community.document_loaders import TextLoader
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
import os
embed_path = 'path/to/bge_model_weight'
faiss_path = 'faiss_db.index'
filename = os.listdir('data')
data = []
for fn in filename:
loader = TextLoader(f'data/{fn}')
data.append(loader.load()[0])
embeddings = HuggingFaceEmbeddings(model_name=embed_path, model_kwargs={'device': 'cuda'})
db = FAISS.from_documents(data, embeddings)
db.save_local(faiss_path)
其中,FAISS.from_documents(data, embeddings) 方法即为向量数据库构建,通过传入需要构建的数据及向量化模型,然后调用save_local方法将向量存储到向量数据库中,至此,向量数据库已构建完成。
2.2 向量库检索
向量库检索主要使用Faiss的 as_retriever() 方法,如下:
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
embed_path = 'path/to/bge_model_weight'
faiss_path = 'faiss_db.index'
embeddings = HuggingFaceEmbeddings(model_name=embed_path, model_kwargs={'device': 'cuda'})
db = FAISS.load_local(faiss_path, embeddings, allow_dangerous_deserialization=True)
result = db.as_retriever().invoke('什么是RAG', return_scores=True)
print(result)
其中,result默认会包含最相似的top4个候选答案,至此,向量库构建及检索均已完成,下面介绍生成应用。
3、生成
以qwen2.5-14b模型为例,搭建rag应用,实现如下:
from modelscope import AutoModelForCausalLM, AutoTokenizer
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
model_name = "path/to/Qwen2.5-14B-Instruct"
embed_path = 'path/to/bge_model_weight'
faiss_path = 'faiss_db.index'
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype="auto",
device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained(model_name)
RAG_prompt = """
请根据输入内容总结回答
"""
messages = [
{"role": "system", "content": RAG_prompt}
# {"role": "user", "content": prompt}
]
embeddings = HuggingFaceEmbeddings(model_name=embed_path, model_kwargs={'device': 'cuda'})
db = FAISS.load_local(faiss_path, embeddings, allow_dangerous_deserialization=True)
while True:
query = input("question: ")
if query == "exit":
break
result = db.as_retriever().invoke(query)
test_text = f"按照要求回答用户问题\n。参考文档:{result[0].page_content}.\n 用户问题是:{query}\n "
messages.append({"role": "user", "content": test_text})
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
model_inputs = tokenizer([text], return_tensors="pt").to(model.device)
generated_ids = model.generate(
**model_inputs,
max_new_tokens=512
)
generated_ids = [
output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
]
response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
print("AI: " + response)
print('*'*30)
生成过程的关键处理是构建test_text = f"按照要求回答用户问题\n。参考文档:{result[0].page_content}.\n 用户问题是:{query}\n "
即在构建prompt时传入候选答案,示例中使用了top1,即最相关的参考答案,然后其余步骤是利用大模型的语言能力根据参考答案回答用户问题。
4、思考
4.1 知识库质量
知识库质量是影响检索效果的关键因素,如上述的txt文件,每个相对独立的文件我们称为document,要注意你知识库的多个document并不是完全独立的,而是相对独立的,换句话说,至少上下文的语义是有相关性的,因此我们在处理document时要考虑重叠overlap,即docment1中要包含一些document2的内容;反之如果文档都是完全独立的,则不需要考虑,但这在实际情况中较少。
4.2 检索效果调整
检索效果调整我认为可以分为几个方面,无先后顺序
a、向量库质量-上文已说明
b、系统、问答提示词,不断优化prompt增强对大模型的要求
c、参考文档数量,可以增加参考文档以增强效果,如增加到4个,丰富候选集
d、重排reranker,增加重排模型,对检索的结果再次进行相似度处理,提升候选集准确率
e、增加llm参数,换用参数量大的llm,能力强