基于知识库的QA

基于知识库的QA

​ 基于知识库的QA,即用户给定相关文档,将文档处理成为知识库,在知识库的基础上进行领域问答。这种技术可以对于用户的领域问题做出更好地领域回答。同时对于模型给出的回答,可以做到回溯这些答案在知识库中的出处。是得模型的回答有理有据。

原理说明

​ 基于知识库的QA分为以下几个步骤:

  • 对输入的文档进行处理
    • 加载文件:对于不同格式的文件(例如.md、.txt、.pdf、.doc等)的加载。
    • 文本分割:该步骤中对于文本的分割大小的设定是个可以考量的问题。个人人为文本分割的适当小,有利于提高答案出处的精确性。
    • 构建向量库:为了后续对问题(query)进行相似度匹配.
  • 问题向量化(Embedding)
  • 向量相似度匹配
  • 构造prompt并对LLMs进行问答

代码实现

代码参考LangChain+ChatChat,并在原有代码上改动。

整体代码:

from transformers import AutoModel, AutoTokenizer
from langchain.document_loaders import UnstructuredFileLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import FAISS
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
import torch

EMBEDDING_DEVICE = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"

tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True)
model = AutoModel.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True).half().cuda()
chatglm = model.eval()

# test知识库文本
filepath = "test.txt"

# 加载文件
loader = UnstructuredFileLoader(filepath)
docs = loader.load()

# 文本分割
text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=200)
# docs = text_splitter.split_text(docs[0].page_content)
docs = text_splitter.split_documents(docs)

# 构建向量库
embeddings = HuggingFaceEmbeddings(model_name="m3e-base",
                                   model_kwargs={'device':EMBEDDING_DEVICE})
vector_store = FAISS.from_documents(docs, embeddings)

# 根据提问匹配上下文
query = "用户的问题"
docs = vector_store.similarity_search(query)
context = [doc.page_content for doc in docs]

# 构造 Prompt
prompt = f"已知信息:\n{context}\n根据已知信息回答问题:\n{query}"

# llm回答问题
response, history = chatglm.chat(tokenizer, prompt, history=[])
# 输出LLMs的回答
print(response)

加载文件:

# test知识库文本
filepath = "test.txt"
# 加载文件
loader = UnstructuredFileLoader(filepath)
docs = loader.load()

文本分割以及文本向量化:

# 文本分割
text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=200)
docs = text_splitter.split_documents(docs)

# 构建向量库
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2",
                                   model_kwargs={'device':EMBEDDING_DEVICE})
vector_store = FAISS.from_documents(docs, embeddings)

​ 第2行设置文本分割器,其中设置两个参数chunk_sizechunk_overlap。其中chunk_size=500设置分割大小为500个字符,chunk_overlap=200即设置文本块之间的最大重叠量,保留一些重叠可以保持文本块之间的连续性。

注意:第3行,在LangChain+ChatChat 项目中写错了,这里做了修改。

​ embedding库使用的HuggingFaceEmbeddings(),将该嵌入模型参数下载本地实现。

​ 通过读取源码可以知道获得模型回答的出处这一功能的实现原理:在文本分割那一步中,分割好的文本块以List[Document]结构存储,Document类中有两个成员变量如下所示:

class Document(Object):
	page_content: str
    """String 类型的文本"""
    metadata: dict = Field(default_factory=dict)
    """存放每个文本块(page_content)对应的文档出处
    例如
    metadata={'source': 'test.txt'}
    """

​ 出处功能实现路径明了:将用户输入向量化,匹配向量库中相似的向量,匹配到的向量都会找到存放在Document类中的成员变量metadata,即完成了出处的功能。

提问

# 根据提问匹配上下文
query = "哪些航母参与了“海轨行动”?"
docs = vector_store.similarity_search(query, k=4)
# context存放了前k个相似度最高的文本,类型为List[Document]
context = [doc.page_content for doc in docs]
# 构造 Prompt
prompt = f"已知信息:\n{context}\n根据已知信息回答问题:\n{query}"
# llm回答问题
response, history = chatglm.chat(tokenizer, prompt, history=[])
# 输出LLMs的回答
print(response)

​ 调用LLMs,结合用户输入和搜索到的相似向量进行问答。

similarity_search(): 相似度搜索

    def similarity_search(
        self,
        query: str,
        k: int = 4,
        filter: Optional[Dict[str, Any]] = None,
        fetch_k: int = 20,
        **kwargs: Any,
    ) -> List[Document]:
        """Return docs most similar to query.

        Args:
            query: Text to look up documents similar to.
            k: 匹配相似的向量,取前k个,默认为4
            filter: (Optional[Dict[str, str]]): Filter by metadata. Defaults to None.
            fetch_k: (Optional[int]) Number of Documents to fetch before filtering.
                      Defaults to 20.

        Returns:
            Document类的List
        """
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值