LLM实战 | 基于本地知识库打造属于自己的New Bing!

ChatGPT狂飙160天,世界已经不是之前的样子。

新建了人工智能中文站https://ai.weoknow.com
每天给大家更新可用的国内可用chatGPT资源


今天分享下如何利用开源LLM大语言基座模型来管理自己本地的知识库,比如你电脑里的paper/学习资料等,打造属于自己的NewBing。

前言

ChatGPT等LLM大模型的知识延迟问题和幻想问题一直是两个非常难以解决的问题。而其背后的技术选型导致了这两个问题必然会存在。

  • 知识延迟:由于大模型的知识来自于训练时喂给它的训练数据,且大模型很难做到实时更新,一般更新一次也得花上好几个月,所有它能接收到的数据必然是延迟的。

  • 幻想问题:由于LLM采用的概率模型,即预测生成下一个字符概率是多少,所有或多或少的它在生成结果的时候都有定的可能出现错误。

那么如何解决这两个问题呢?目前比较好的方式就是利用大模型极强的语义理解能力,给大模型外挂一个知识库或者搜索引擎去解决这两个问题。如今的new_bing chat 则是采用这种技术路线,下图就是我问new_bing关于如何解决LLM知识延迟和幻想问题的相关截图:

img

它的整个流程应该是:

  • 1.利用bing去搜索和用户输入相关的文本。

  • 2.采用合适的prompt + 上述步骤搜索到的文本 + 用户输入 ,一并输入给chatgpt

    我猜是prompt是:利用上述资料回复用户问题(当然不可能这么简单)。

  • 3.利用chatgpt的语义理解能力和内容总结能力生成的结果 。

其实解决大模型知识延迟和幻想的问题在我问newbing搜索结果的第二条给出了和上述流程比较契合的回答:

使用更丰富和多样的数据源,例如结构化或半结构化的数据、知识图谱、多模态数据等,以增加模型的知识覆盖率和质量,提高模型的泛化能力和准确性。

而今天笔者就介绍一下如何利用非结构化的文本知识库去减轻LLM的幻想问题。

基于知识库 + LLM的知识问答系统的流程和架构

具体的流程和上述new_bing chat的流程很像,但是多了一个步骤

1.将知识库的文本分块,并进行向量化;

2.用户的query向量化,并在知识库中进行检索,返回最相关的TOPN的文本块;

3.采用合适的prompt + 上述步骤搜索到的文本 一并输入给LLM;

4.利用LLM的语义理解能力和知识问答能力,生成问题的答案。

下图则是最近github上很火的一个基于chatGLM实现的一个基于知识库的问答系统的架构图:

https://github.com/imClumsyPanda/langchain-ChatGLM

原理图

基于知识库 + LLM的知识问答系统实战

本项目地址:https://github.com/wp931120/LongChainKBQA/tree/main其中,

  • 1.文本分块模块:采用的是阿里开源nlp_bert_document-segmentation_chinese-base 语义分割模型对文本进行拆分;

  • 2.文本向量化模块:采用的是text2vec-large-chinese 模型对文本向量化;

  • 3.LLM模块:采用的是ChatYuan-large-v2 大语言模型;

  • 4.整合模块:langchain可将上述模块轻松的进行整合,构建知识问答LLM。

1.文本分块和向量化模块

具体技细节就是利用nlp_bert_document-segmentation_chinese-base模型将文本分割后,利用text2vec-large-chinese向量化,最终构建一个faiss索引,并持久化。

from langchain.document_loaders import UnstructuredFileLoader, TextLoader, DirectoryLoader
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
from config import Config
from utils.AliTextSplitter import AliTextSplitter


class DocumentService(object):
    def __init__(self):

        self.config = Config.vector_store_path
        self.embeddings = HuggingFaceEmbeddings(model_name=Config.embedding_model_name)
        self.docs_path = Config.docs_path
        self.vector_store_path = Config.vector_store_path
        self.vector_store = None

    def init_source_vector(self):
        """
        初始化本地知识库向量
        :return:
        """
        loader = DirectoryLoader(self.docs_path, glob="**/*.txt", loader_cls=TextLoader)
        # 读取文本文件
        documents = loader.load()
        text_splitter = AliTextSplitter()
        # 使用阿里的分段模型对文本进行分段
        split_text = text_splitter.split_documents(documents)
        # 采用embeding模型对文本进行向量化
        self.vector_store = FAISS.from_documents(split_text, self.embeddings)
        # 把结果存到faiss索引里面
        self.vector_store.save_local(self.vector_store_path)

    def load_vector_store(self):
        self.vector_store = FAISS.load_local(self.vector_store_path, self.embeddings)



if __name__ == '__main__':
    s = DocumentService()
    ###将文本分块向量化存储起来
    s.init_source_vector()

2.LLM模块

加载ChatYuan-large-v2 大语言模型,当然也可以选择ChatGLM或者Moss等。

from typing import List, Optional
from langchain.llms.base import LLM
from langchain.llms.utils import enforce_stop_tokens
from transformers import AutoModel, AutoTokenizer
from config import Config


class LLMService(LLM):
    max_token: int = 10000
    temperature: float = 0.1
    top_p = 0.9
    history = []
    tokenizer: object = None
    model: object = None

    def __init__(self):
        super().__init__()

    @property
    def _llm_type(self) -> str:
        return "LLM"

    def _call(self,
              prompt: str,
              stop: Optional[List[str]] = None) -> str:
        response, _ = self.model.chat(
            self.tokenizer,
            prompt,
            history=self.history,
            max_length=self.max_token,
            temperature=self.temperature,
        )
        if stop is not None:
            response = enforce_stop_tokens(response, stop)
        self.history = self.history + [[None, response]]
        return response

    def load_model(self, model_name_or_path: str = "ClueAI/ChatYuan-large-v2"):
        """
        加载大模型LLM
        :return:
        """
        self.tokenizer = AutoTokenizer.from_pretrained(
            Config.llm_model_name,
            trust_remote_code=True
        )
        self.model = AutoModel.from_pretrained(model_name_or_path, trust_remote_code=True)
        self.model = self.model.eval()

if __name__ == '__main__':
    chatLLM = LLMService()
    chatLLM.load_model()

3.知识问答模块

from langchain.chains import RetrievalQA
from langchain.prompts.prompt import PromptTemplate

from config import Config
from document import DocumentService
from llm import LLMService


class LangChainApplication(object):

    def __init__(self):
        self.config = Config
        self.llm_service = LLMService()
        ###加载llm和知识库向量
        print("load llm model ")
        self.llm_service.load_model(model_name_or_path=self.config.llm_model_name)
        self.doc_service = DocumentService()
        print("load documents")
        self.doc_service.load_vector_store()

    def get_knowledge_based_answer(self, query,
                                   history_len=5,
                                   temperature=0.1,
                                   top_p=0.9,
                                   top_k=1,
                                   chat_history=[]):
        #定义prompt
        prompt_template = """基于以下已知信息,简洁和专业的来回答用户的问题。
                                        如果无法从中得到答案,请说 "根据已知信息无法回答该问题" 或 "没有提供足够的相关信息",不允许在答案中添加编造成分,答案请使用中文。
                                        已知内容:
                                        {context}
                                        问题:
                                        {question}"""
        prompt = PromptTemplate(template=prompt_template,
                                input_variables=["context", "question"])
        self.llm_service.history = chat_history[-history_len:] if history_len > 0 else []

        self.llm_service.temperature = temperature
        self.llm_service.top_p = top_p
        # 声明一个知识库问答llm,传入之前初始化好的llm和向量知识搜索服务
        knowledge_chain = RetrievalQA.from_llm(
            llm=self.llm_service,
            retriever=self.doc_service.vector_store.as_retriever(
                search_kwargs={"k": top_k}),
            prompt=prompt)

        knowledge_chain.combine_documents_chain.document_prompt = PromptTemplate(
            input_variables=["page_content"], template="{page_content}")
        knowledge_chain.return_source_documents = True

        ### 基于知识库的问答
        result = knowledge_chain({"query": query})
        return result

    def get_llm_answer(self, query=''):
        prompt_template = """请回答下列问题:
                            {}""".format(query)
        ### 基于大模型的问答
        result = self.llm_service._call(prompt_template)
        return result


if __name__ == '__main__':
    application = LangChainApplication()
    print("大模型自己回答的结果")
    result = application.get_llm_answer('迪丽热巴的作品有什么')
    print(result)
    print("大模型+知识库后回答的结果")
    result = application.get_knowledge_based_answer('迪丽热巴的作品有什么')
    print(result)

最终上述代码运行的结果如下:加入知识库后结果比没加之前好了很多,而且也能给出参考的文档是哪一篇。

是不是和new_bing chat返回结果很像。

示例结果

当然如果知识库里面没有结果,问答系统还是很答非所问,甚至会乱拼接知识。

目前也没有特别好的方式去完全杜绝这个问题,唯一能做的就是微调大模型或者调整你的prompt,prompt的微调也是一个比较重要的课。

结语

至此本文介绍了一下:

  1. 如何利用 知识库+LLM去构建一个基于知识库的问答系统的基本架构,而其中很多模块都可以进行丰富和优化,比如文本分块模块,文本向量化模块,LLM模块都可以替换成其他的模型。

  2. 如何利用langchain整合这些模块,langchain现在在ai圈非常火,并且还在成长。大家可以关注一下这个开源库。

大模型现在还有很多问题待解决,而且其的应用之路还有很多地方可以去探索,希望大家借此文章了解一下大模型的缺陷以及一些应用方法。

参考

https://github.com/imClumsyPanda/langchain-ChatGLM

https://github.com/yanqiangmiff


ChatGPT狂飙160天,世界已经不是之前的样子。

新建了人工智能中文站https://ai.weoknow.com
每天给大家更新可用的国内可用chatGPT资源

猜你喜欢

转载自blog.csdn.net/zyqytsoft/article/details/131159174
今日推荐