赞
踩
LangChain:我们不生产模型,我们只是大模型的搬运工。
基于LangChain实现本地知识库问答:
项目地址:
流程如上图,知识库嵌入-->问题嵌入-->相似度计算-->提示文本-->回答
共涉及两个大模型,分别为文本嵌入模型和大语言模型(LLM)。
文本嵌入模型作用:
1. 对知识库中的文件中的文本片段进行嵌入,存入知识向量库;
2. 对问题进行嵌入。
LLM作用:根据提示文本回答问题。
LangChain主打大模型间的调度,附带嵌入向量相似度计算,提示模板填充等功能;
官方提供了文本嵌入模型M3E的示例,但将其更换为BGE,初始化知识向量库时,弹出以下错误:
原因:断言出错,原向量库残留了一些模型信息,self.d对应M3E的嵌入维度768,但BGE的嵌入维度为1024,不能存库。
./Langchain-Chatchat-0.2.5/server/knowledge_base/kb_cache/base.py中137行左右:
- embeddings = HuggingFaceBgeEmbeddings(model_name=embedding_model_dict[model],
- model_kwargs={'device': device},
- query_instruction=query_instruction)
embeddding_model_dict未声明,改成下面即可:
- embeddings = HuggingFaceBgeEmbeddings(model_name=get_model_path(model),
- model_kwargs={'device': device},
- query_instruction=query_instruction)
需将baichuan-inc/Baichuan2-13B-Chat/tokenization_baichuan.py中的super()放至最后执行,如下:
- self.vocab_file = vocab_file
- self.add_bos_token = add_bos_token
- self.add_eos_token = add_eos_token
- self.sp_model = spm.SentencePieceProcessor(**self.sp_model_kwargs)
- self.sp_model.Load(vocab_file)
- super().__init__(
- bos_token=bos_token,
- eos_token=eos_token,
- unk_token=unk_token,
- pad_token=pad_token,
- add_bos_token=add_bos_token,
- add_eos_token=add_eos_token,
- sp_model_kwargs=self.sp_model_kwargs,
- clean_up_tokenization_spaces=clean_up_tokenization_spaces,
- **kwargs,
- )
ERROR | root | KeyError: Caught exception: 'choices'
可能是显存超了,减少匹配知识条数或减小kb_config.py中的CHUNK_SIZE等均可。
下面说一下在实际应用效果上的小问题:
例如,标题+'\n'+段落,LLM会因为'\n'认为标题和段落不存在关联,导致回答不准确。对于较为成熟的LLM(ChatGPT、文心等)并不会有太大影响,但对于ChatGLM是存在一定影响的。
./anaconda3/envs/LangChain/lib/python3.10/site-packages/langchain/text_splitter.py
若想在文本拆分前替换,将下面注释代码改为非注释代码即可,160行左右:
- # texts.append(doc.page_content)
- new_content = doc.page_content.replace('\n', ' ')
- texts.append(new_content)
若想在文本拆分后替换,将下面注释代码改为非注释代码即可,150行左右:
- # new_doc = Document(page_content=chunk, metadata=metadata)
- new_chunk = chunk.replace('\n', ' ')
- new_doc = Document(page_content=new_chunk, metadata=metadata)
替换前(检索出了,但未能准确回答):
替换后(能够准确回答):
对于相对困难的问题,回答差距较大,如下:
对于简单的问题,回答方式虽然不完全相同,但内容正确,如下:
解决方案:切换表现更好的LLM,百川2-13B表现较佳,但对算力设备要求较高,在算力一般(例如RTX 3090、2080ti)的设备上速度较慢。
尤其是表格数据被拦腰截断后,对于未含表头的数据则不可能被正确回答。
文本分割修改代码位置:~/Langchain-Chatchat-0.2.5/server/knowledge_base/utils.py中的KnowledgeFile.docs2texts()
文本分割函数 text_split_via_me() 内可包括文档的重新读取,代码太多就不贴细节了。
- def docs2texts(
- self,
- docs: List[Document] = None,
- zh_title_enhance: bool = ZH_TITLE_ENHANCE,
- refresh: bool = False,
- chunk_size: int = CHUNK_SIZE,
- chunk_overlap: int = OVERLAP_SIZE,
- text_splitter: TextSplitter = None,
- ):
- docs = docs or self.file2docs(refresh=refresh)
- if not docs:
- return []
-
- # if self.ext not in [".csv"]:
- # if text_splitter is None:
- # text_splitter = make_text_splitter(splitter_name=self.text_splitter_name, chunk_size=chunk_size, chunk_overlap=chunk_overlap)
- # if self.text_splitter_name == "MarkdownHeaderTextSplitter":
- # docs = text_splitter.split_text(docs[0].page_content)
- # for doc in docs:
- # # 如果文档有元数据
- # if doc.metadata:
- # doc.metadata["source"] = os.path.basename(self.filepath)
- # else:
- # docs = text_splitter.split_documents(docs)
-
- def text_split_via_me(docs):
- ...
- return ...
- docs = text_split_via_me(docs)
-
- print(f"文档切分示例:{docs[0]}")
- # if zh_title_enhance:
- # docs = func_zh_title_enhance(docs)
- self.splited_docs = docs
- return self.splited_docs
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。