基于langchain 的文档问答 最佳实践
大模型开发/实践案例
- LLM
2023.11.072297看过
文档问答的原理
-
文档读取并切割,用句向量 向量化,存入向量数据库
-
问题向量化,在向量数据库中进行相似性检索,并存出top K
-
把问题和top K 答案组成 prompt 并发给大模型,等大模型答案
这里面涉及到的技术点有:
-
文档加载和切分
-
句向量
-
向量存储
-
向量相似度计算
-
promot 生成
-
大模型(LLM)
langchain 把这些技术都整合到一起,让我们可以方便的搭建自己的应用。
文档问答的原型搭建
网上有很多demo ,最简单的是用llama-index,openai,gradio 进行搭建
llama-index 是基于文件的向量数据库,gradio 是web 服务器,实现了基本的ui页面,还可以提供域名服务。句向量和大模型用的openai.
这种demo 需要一台服务器,能连上openai. langchain 的安装有也些bug. 现在还是0.5的版本。 我在window2012 上安装,用miniconda, 和 visual studio,langchain 中有些c++的代码编译需要
这种应用搭demo 还可以的,但是在生产环境是不可行
-
很费钱。使用openai 的2个服务,embedding, gpt-3.5-turbo, 我训练了3篇doc 文档,就花了0.4$.
-
使用llama-index 并不是数据库,它是文件存储。检索速度慢很多。
下面我基于demo 进行了改进
-
句向量 使用 HuggingFace 'm3e-base',这是目前测试效果不错的句向量模型,不需要GPU 也可以跑。
-
向量数据库选用了Annoy,它是Facebook 推出的向量数据库,基于 k 树算法进行检索。
标题
|
demo
|
改进
|
句向量
|
openai embedding
|
HuggingFace 'm3e-base'
|
大模型
|
openai gpt-3.5-turbo
|
gpt-3.5-turbo
|
向量数据库
|
llama_index
|
Annoy
|
import osfrom langchain.embeddings.openai import OpenAIEmbeddingsfrom langchain.vectorstores import Annoyfrom langchain.text_splitter import CharacterTextSplitterfrom langchain import OpenAI, VectorDBQAfrom langchain.chat_models import ChatOpenAIfrom langchain.document_loaders import DirectoryLoaderfrom langchain.chains import RetrievalQAfrom langchain.indexes import VectorstoreIndexCreatorimport timefrom langchain import PromptTemplatefrom langchain.embeddings import HuggingFaceEmbeddings,HuggingFaceInstructEmbeddings# openAI的Keyos.environ["OPENAI_API_KEY"] = 'xxxx'def create_index(path):loader = DirectoryLoader('D:/download/', glob='**/*.docx')documents = loader.load()text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)split_docs = text_splitter.split_documents(documents)embeddings = HuggingFaceEmbeddings(model_name='moka-ai/m3e-base')vector_store_path = r"./storage4"docsearch = Annoy.from_documents(documents=split_docs,embedding=embeddings,persist_directory=vector_store_path)docsearch.save_local(vector_store_path)def search(txt):embeddings = HuggingFaceEmbeddings(model_name='moka-ai/m3e-base')vector_store_path = r"./storage4"docsearch = Annoy.load_local(vector_store_path,embeddings=embeddings)start = time.time()prompt_template = """请注意:请谨慎评估query与提示的Context信息的相关性,只根据本段输入文字信息的内容进行回答,如果query与提供的材料无关,请回答"对不起,我不知道",另外也不要回答无关答案:Context: {context}Question: {question}Answer:"""PROMPT = PromptTemplate(template=prompt_template, input_variables=["context", "question"])# qa = VectorDBQA.from_chain_type(llm=ChatOpenAI(temperature=0, model_name="gpt-3.5-turbo-16k"), chain_type="stuff", vectorstore=docsearch, return_source_documents=True)# result = qa({"query": txt})qa = RetrievalQA.from_chain_type(llm=ChatOpenAI(temperature=0, model_name="gpt-3.5-turbo"), chain_type="stuff", retriever=docsearch.as_retriever(search_kwargs={"k": 8}),chain_type_kwargs={"prompt": PROMPT})result = qa.run(txt)print(result)print(time.time() - start)if __name__=="__main__":create_index('')search('xxx')
优化
-
文档的切分
文档切分对句向量的生成有很大影响。最理想的效果把相拟的段落切到一起,想实现这样的效果需要对文档内容比较了解,进而切分。
使用默认的 langchain CharacterTextSplitter chunk_size = 1000,这种切分的效果不是很好。它的分割符是 \n\n,先按chunk 切,再按分割符切。这样会把段落切错。
-
大模型的选型
使用openai gpt-3.5-turbo 它是有字数限制,4096个字符,top K选出的答案多了,它都答不上来。可以换用 gpt-3.5-turbo-16k, 它有16k个字符,大大的满足冲破答的需要。
换用国产大模型,如chatGLM, 它有6B 的小模型,单张GPU上就可以跑。这样也不用国外服务器。
版权声明:本文为稀土掘金博主「dony7247」的原创文章如有侵权,请联系千帆社区进行删除
评论