RAG
- jupyter lab的搭建使用
- RAG系统的基本搭建流程
- 文档的加载与切割
- LLM接口封装
- Prompt模板
- 向量检索
- 什么是向量
- 文本向量
- 文本向量怎么得到的
- 向量间的相似度计算
- 嵌入模型的选择
- 向量数据库
- Chroma向量数据库
- Chroma向量数据库服务
- 主流向量数据库功能对比
- 基于向量检索的RAG
- RAG系统进阶知识
- 文本分割的粒度
国内模型库: https://www.modelscope.cn
jupyter lab的搭建使用
首先需要创建一个conda环境 进到环境中再执行 不能再base环境下
pip install jupyterlab切换到想要保存的磁盘
cd F:\LLM
使用以下命令启动jupyterlab:
jupyter labRAG系统的基本搭建流程
搭建过程:
- 文档加载,并按一定条件切割成片段
- 将切割的文本片段灌入检索引擎
- 封装检索接口
- 构建调用流程:Query->检索->Prompt->LLM->回复
文档的加载与切割
安装下边的两个库
!pip install--upgrade openai# 安装 pdf 解析库!pip install pdfminer.sixfrompdfminer.high levelimportextract_pagesfrompdfminer.layoutimportLTTextContainerdefextract_text_from_pdf(filename,page_numbers=None,min_line_length=1):'''从 PDF 文件中(按指定页码)提取文字'''paragraphs=[]buffer=""full_text=""# 提取全部文本fori,page_layoutinenumerate(extract_pages(filename)):# 如果指定了页码范围,跳过范围外的页ifpage_numbersisnotNoneandinotinpage_numbers:continueforelementinpage_layout:ifisinstance(element,LTTextContainer):full_text+=element.get_text()+"\n"# 按空行分隔,将文本重新组织成段落Lines=full_text.split("\n")fortextinlines:iflen(text)>=min_line_length:buffer+=(""+text)ifnottext.endswith("")elsetext.strip("-")elifbuffer:paragraphs.append(buffer)buffer=""ifbuffer:paragraphs.append(buffer)returnparagraphs paragraphs=extract_text_from_pdf("llama2.pdf",min_line_length=10)forparainparagraphs[:4]:print(para+"\n")LLM接口封装
pip install-U python-dotenvfromopenaiimportOpenAIimportos#加载环境变量fromdotenvimportload_dotenv,find_dotenv _=load_dotenv(find_dotenv(),verbose=True)client=OpenAI()#封装openai接口defget_completion(prompt,model="gpt-3.5-turbo",temperature=0.7,max_tokens=150,top_p=1.0,frequency_penalty=0.0,presence_penalty=0.0,stop=["\n"]):messages=[{"role":"user","content":prompt}]response=client.chat.completions.create(messages=messages,model=model,temperature=temperature,#模型输出的随机性max_tokens=max_tokens,top_p=top_p,frequency_penalty=frequency_penalty,presence_penalty=presence_penalty,stop=stop)returnresponse.choices[0].message.contentPrompt模板
#创建提示词模板defbuild_prompt(prompt_template,**kwargs):inputs={}fork,vinkwargs.items():ifisinstance(v,list)andall(isinstance(i,str)foriinv):val='\n\n'.join(v)else:val=v inputs[k]=valreturnprompt_template.format(**inputs)prompt_template = “”
你是一个问答机器人。你的任务是根据下述给定的己知信息回答用户问题
已知信息: 从向量数据库检索出来的原始文档
{context}
用户问:
{query}
如果已知信息不包含用户问题的答案,或者已知信息不足以回答用户的问题,请直接回复"我无法回答您的问题"请不要输出已知信息中不包含的信息或答案。请用中文回答用户问题。
向量检索
什么是向量
向量是一种有大小和方向的数学对象。它可以表示为从一个点到另一个点的有向线段。例如:二维空间中的向量可以表示为(x,y),表示从原点(0,0)到点(x,y)的有向线段。
以此类推,我可以用一组坐标(x0,x1,x2…xn)表示一个 N 维空间中的向量,N 叫向量的维度
文本向量
将文本转成一组N维浮点数,即文本向量又叫Embeddings
向量之间可以计算距离,距离远近对应语义相似度大小
文本向量怎么得到的
1.构建相关(正例)与不相关(负例)的句子对样本
2.训练双塔式模型,让正例间的距离小,负例间的距离大
向量间的相似度计算
importnumpyasnpfromnumpyimportdotfromnumpy.linalgimportnormdefcos_sim(a,b):#余弦距离 越大越相似'''余弦距离 越大越相似'''returndot(a,b)/(norm(a)*norm(b))defl2(a,b):'''欧式距离 越小越相似'''x=np.asarray(a)-np.asarray(b)returnnorm(x)defget_embeddings(texts,model="text-embedding-ada-002",dimensions=None):'''封装openai的Embedding模型接口'''ifmodel=="text-embedding-ada-002":dimensions=Noneifdimensions:data=client.embeddings.create(input=texts,model=model,dimensions=dimensions).dataelse:data=client.embeddings.create(input=texts,model=model).datareturn[x.embeddingforxindata]test_query=["测试文本"]vec=get_embeddings(test_query)[0]print(f"Total dimensions:{len(vec)}")print(f"First 10 dimensions:{vec[:10]}")嵌入模型的选择
- 标准:找需求相关的语料库来进行文本向量的转换测试,进行评估
维度越大,标识特征细节提取越丰富
大多数场景下,开源的嵌入模型使用体验都很差
要提升检索召回率,建议对模型进行微调
向量数据库
向量数据库,是专门为向量检索设计的中间件!
向量数据库其实最早在传统的人工智能和机器学习场景中就有所应用。在大模型兴起后,由于目前大模型的token数限制,很多开发者倾向于将数据量庞大的知识、新闻、文献、语料等先通过嵌入(embedding)算法转变为向量数据,然后存储在Chroma等向量数据库中。当用户在大模型、输入问题后,将问题本身也embedding,转化为向量,在向量数据库中査找与之最匹配的相关知识,组成大模型的上下文,将其输入给大模型,最终返回模型处理后的文本给用户,这种方式不仅降低大模型的计算量,提高响应速度,也降低成本,并避免了大模型的tokens限制,是一种简单高效的处理手段。此外向量数据库还在大模型记忆存储等领域发挥其不可替代的作用。
Chroma向量数据库
官方文档:https://docs.trychroma.com/docs/overview/introduction
安装数据库
pip install chromadb#为了演示方便 取两页paragraphs=extract_text_from_pdf("llama2.pdf",page_numbers=[2,3],min_line_length=10)importchromadbfromchromadb.configimportSettingsclassMyVectorDBConnector:def__init__(self,collection_name,embedding_fn):#内存模式 实际不能这么用 只能自己玩用 否则服务器一出问题 全丢chroma_client=chromadb.Client(Settings(allow_reset=True))#数据持久化#chroma_client = chromadb.PersistentClient(path="./chroma")#注意:为了演示,实际不需要每次都reset() 不可逆chroma_client.reset()# reset重置数据库 慎用self.collection=chroma_client.get_or_create_collection(name=collection_name)self.embedding_fn=embedding_fndefadd_documents(self,documents):# 向collection中添加文档与向量self.collection.add(embeddings=self.embedding_fn(documents),#每个文档的向量documents=documents#文档的原文ids=[f"id{i}"foriinrange(len(documents))]#每个文档的 id)defsearch(self,query,top_k=10):# 检索向量数据库results=self.collection.query(query_embedding=self.embedding_fn([query]),n_results=top_n)returnresults#创建一个向量数据库对象vector_db=MyVectorDBConnector(collection_name="llama2",embedding_fn=get_embeddings)#向向量数据库中添加文档vector_db.add_documents(paragraphs)user_query="Llama 2有多少参数"#user_query = "Does Llama 2 have a conversational variant?"results=vector_db.search(user_query,2)forparainresults['documents'][0]:print(para+"\n")澄清几个关键概念:
- 向量数据库的意义是快速的检索;
- 向量数据库本身不生成向量,向量是由 Embedding 模型产生的:
- 向量数据库与传统的关系型数据库是互补的,不是替代关系,在实际应用中根据实际需求经常同时使用。
Chroma向量数据库服务
Server 端
chroma run--path/db_pathClient 端
importchromadb chromadb_client=chromadb.HttpClient(host='localhost',port=8000)主流向量数据库功能对比
- FAISS: Meta 开源的向量检索引擎 https://github已添加到剪贴板research/faiss。
- Pinecone:商用向量数据库,只有云服务 https:/www.pinecone.io/
Milvus: 开源向量数据库,同时有云服务 https://milvus.io/- Weaviate:开源向量数据库,同时有云服务 https://weaviate.io/
Qdrant: 开源向量数据库,同时有云服务 https://qdrant.tech/- PGVector: Postgres 的开源向量检索引擎 https://github.com/pgvector/pgvector
- RediSearch: Redis 的开源向量检索引擎 https://qithub.com/RediSearch/Redisearch
- ElasticSearch 也支持向量检索 https://www.elastic.co/enterprise-search/vector-search
企业推荐标示的向量数据库
基于向量检索的RAG
classRAG_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_resultsdefchat(self,user_query):# 检索search_results=self.vector_db.search(user_query,self.n_results)# 构建 Promptprompt=build_prompt(prompt_template,search_results=search_results['documents'][0],query=user_query)#调用LLMresponse=self.llm_api(prompt)returnresponse#创建一个RAG机器人bot=RAG_Bot(vector_db,llm_api=get_completion)user_query="Llama 2有多少参数?"response=bot.chat(user_query)print(response)RAG系统进阶知识
文本分割的粒度
缺陷
- 粒度太大可能导致检索不精准,粒度太小可能导致信息不全面
- 问题的答案可能跨越两个片段