当前位置:   article > 正文

基于LangChain实现本地知识库问答问题总结_keyerror: caught exception: 'choices

keyerror: caught exception: 'choices

LangChain:我们不生产模型,我们只是大模型的搬运工。

基于LangChain实现本地知识库问答:

项目地址:

GitHub - chatchat-space/Langchain-Chatchat: Langchain-Chatchat(原Langchain-ChatGLM)基于 Langchain 与 ChatGLM 等语言模型的本地知识库问答 | Langchain-Chatchat (formerly langchain-ChatGLM), local knowledge based LLM (like ChatGLM) QA app with langchainLangchain-Chatchat(原Langchain-ChatGLM)基于 Langchain 与 ChatGLM 等语言模型的本地知识库问答 | Langchain-Chatchat (formerly langchain-ChatGLM), local knowledge based LLM (like ChatGLM) QA app with langchain - GitHub - chatchat-space/Langchain-Chatchat: Langchain-Chatchat(原Langchain-ChatGLM)基于 Langchain 与 ChatGLM 等语言模型的本地知识库问答 | Langchain-Chatchat (formerly langchain-ChatGLM), local knowledge based LLM (like ChatGLM) QA app with langchainicon-default.png?t=N7T8https://github.com/chatchat-space/Langchain-Chatchat

流程如上图,知识库嵌入-->问题嵌入-->相似度计算-->提示文本-->回答

共涉及两个大模型,分别为文本嵌入模型大语言模型(LLM)。

文本嵌入模型作用:

1. 对知识库中的文件中的文本片段进行嵌入,存入知识向量库;

2. 对问题进行嵌入。

LLM作用:根据提示文本回答问题。

LangChain主打大模型间的调度,附带嵌入向量相似度计算,提示模板填充等功能;

下面总结下部署时遇到的一些小问题(v0.2.5):

1. 切换文本嵌入模型时报错。

官方提供了文本嵌入模型M3E的示例,但将其更换为BGE,初始化知识向量库时,弹出以下错误:

原因:断言出错,原向量库残留了一些模型信息,self.d对应M3E的嵌入维度768,但BGE的嵌入维度为1024,不能存库。

解决方案:删掉原来存储库所在目录或重命名即可,单纯在命令行添加--recreate-vs不起作用。

2. BGE加载代码存在问题。

./Langchain-Chatchat-0.2.5/server/knowledge_base/kb_cache/base.py中137行左右:

  1. embeddings = HuggingFaceBgeEmbeddings(model_name=embedding_model_dict[model],
  2. model_kwargs={'device': device},
  3. query_instruction=query_instruction)

embeddding_model_dict未声明,改成下面即可:

  1. embeddings = HuggingFaceBgeEmbeddings(model_name=get_model_path(model),
  2. model_kwargs={'device': device},
  3. query_instruction=query_instruction)

3. 使用百川13B大模型时,报错AttributeError: 'BaichuanTokenizer' object has no attribute 'sp_model'

需将baichuan-inc/Baichuan2-13B-Chat/tokenization_baichuan.py中的super()放至最后执行,如下:

  1. self.vocab_file = vocab_file
  2. self.add_bos_token = add_bos_token
  3. self.add_eos_token = add_eos_token
  4. self.sp_model = spm.SentencePieceProcessor(**self.sp_model_kwargs)
  5. self.sp_model.Load(vocab_file)
  6. super().__init__(
  7.    bos_token=bos_token,
  8.    eos_token=eos_token,
  9.    unk_token=unk_token,
  10.   pad_token=pad_token,
  11.     add_bos_token=add_bos_token,
  12.    add_eos_token=add_eos_token,
  13.   sp_model_kwargs=self.sp_model_kwargs,
  14. clean_up_tokenization_spaces=clean_up_tokenization_spaces,
  15.   **kwargs,
  16. )

4. web运行时,出现KeyError: Caught exception: 'choices'

ERROR | root | KeyError: Caught exception: 'choices' 

可能是显存超了,减少匹配知识条数或减小kb_config.py中的CHUNK_SIZE等均可。

下面说一下在实际应用效果上的小问题:

1. 读取pdf时,不合宜的换行符'\n'会影响LLM对语义的理解。

例如,标题+'\n'+段落,LLM会因为'\n'认为标题和段落不存在关联,导致回答不准确。对于较为成熟的LLM(ChatGPT、文心等)并不会有太大影响,但对于ChatGLM是存在一定影响的。

解决方案:在构建知识向量库时,将全部'\n'替换为空格。替换代码所在目录:

./anaconda3/envs/LangChain/lib/python3.10/site-packages/langchain/text_splitter.py
若想在文本拆分前替换,将下面注释代码改为非注释代码即可,160行左右:

  1. # texts.append(doc.page_content)
  2. new_content = doc.page_content.replace('\n', ' ')
  3. texts.append(new_content)

若想在文本拆分后替换,将下面注释代码改为非注释代码即可,150行左右:

  1. # new_doc = Document(page_content=chunk, metadata=metadata)
  2. new_chunk = chunk.replace('\n', ' ')
  3. new_doc = Document(page_content=new_chunk, metadata=metadata)
效果对比:

替换前(检索出了,但未能准确回答):

替换后(能够准确回答):

2. LLM存在一定随机性,问题相同,答复却不同。

对于相对困难的问题,回答差距较大,如下:

对于简单的问题,回答方式虽然不完全相同,但内容正确,如下:

解决方案:切换表现更好的LLM,百川2-13B表现较佳,但对算力设备要求较高,在算力一般(例如RTX 3090、2080ti)的设备上速度较慢。

3. 不适宜的文本、表格拆分对语义的影响严重。

尤其是表格数据被拦腰截断后,对于未含表头的数据则不可能被正确回答。

解决方案:替换文本分割方案,采用三方库docx读取.docx,能够单独读取表格,对于PDF和doc则需事先转为docx格式。

文本分割修改代码位置:~/Langchain-Chatchat-0.2.5/server/knowledge_base/utils.py中的KnowledgeFile.docs2texts()

文本分割函数 text_split_via_me() 内可包括文档的重新读取,代码太多就不贴细节了。

  1. def docs2texts(
  2. self,
  3. docs: List[Document] = None,
  4. zh_title_enhance: bool = ZH_TITLE_ENHANCE,
  5. refresh: bool = False,
  6. chunk_size: int = CHUNK_SIZE,
  7. chunk_overlap: int = OVERLAP_SIZE,
  8. text_splitter: TextSplitter = None,
  9. ):
  10. docs = docs or self.file2docs(refresh=refresh)
  11. if not docs:
  12. return []
  13. # if self.ext not in [".csv"]:
  14. # if text_splitter is None:
  15. # text_splitter = make_text_splitter(splitter_name=self.text_splitter_name, chunk_size=chunk_size, chunk_overlap=chunk_overlap)
  16. # if self.text_splitter_name == "MarkdownHeaderTextSplitter":
  17. # docs = text_splitter.split_text(docs[0].page_content)
  18. # for doc in docs:
  19. # # 如果文档有元数据
  20. # if doc.metadata:
  21. # doc.metadata["source"] = os.path.basename(self.filepath)
  22. # else:
  23. # docs = text_splitter.split_documents(docs)
  24. def text_split_via_me(docs):
  25. ...
  26. return ...
  27. docs = text_split_via_me(docs)
  28. print(f"文档切分示例:{docs[0]}")
  29. # if zh_title_enhance:
  30. # docs = func_zh_title_enhance(docs)
  31. self.splited_docs = docs
  32. return self.splited_docs

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号