当前位置:   article > 正文

基于ChatGLM+Langchain离线搭建本地知识库(免费)_chatglm 本地知识库

chatglm 本地知识库

目录

简介

服务部署

实现本地知识库

测试

番外


简介

ChatGLM-6B是清华大学发布的一个开源的中英双语对话机器人。基于 General Language Model (GLM) 架构,具有 62 亿参数。结合模型量化技术,用户可以在消费级的显卡上进行本地部署(INT4 量化级别下最低只需 6GB 显存)。

LangChain提供了丰富的生态,可以非常方便的封装自己的工具,并接入到LangcChain的生态中,从而实现语言模型的交互,将多个组件链接在一起,并集成额外的资源,例如 API 和数据库。

服务部署

我是在一台离线服务器上,GPU内存16G,其中python3.10以上,torch1.10以上。首先github下载ChatGLM-6B(https://github.com/THUDM/ChatGLM-6B),方便调用接口,里面有一个 requirements.txt文件,直接安装里面环境即可,然后在Huggingface下载模型chatglm-6b(https://huggingface.co/THUDM/chatglm3-6b),最后将下载好的模型离线打包到离线服务器上。如下所示,其中kownledge文件夹里面包含了我要输入的知识文档(自己的一些文档、pdf、csv文件等)。

当环境搭建好之后,进入ChatGLm-6B文件夹下,打开api.py文件,将tokenizer和model的模型路径修改成从Huggingface下载下来的chatglm-6b模型路径,这里我用的是相对路径。

然后在服务器上运行api.py文件,服务在端口8000运行。

写一个测试代码api_access.py,看看服务是否能被正常使用。值得注意的是,如果你是在本地运行,这里的url写localhost:8000或者127.0.0.1:8000,如果是服务器运行,则写服务器的ip地址。

  1. import requests
  2. def chat(prompt, history):
  3. resp = requests.post(
  4. #url = 'http://127.0.0.1:8000',
  5. url = 'http://172.27.171.194:8000',
  6. json = {"prompt": prompt, "history": history },
  7. headers = {"Content-Type": "application/json;charset=utf-8"}
  8. )
  9. return resp.json()['response'], resp.json()['history']
  10. history = []
  11. while True:
  12. response, history = chat(input("Question:"), history)
  13. print('Answer:',response)

运行结果如下所示,说明该api服务能够正常使用。

实现本地知识库

首先在Huggingface下载向量化模型,我选择了text2vec-base-chinese(https://huggingface.co/shibing624/text2vec-base-chinese/tree/main

然后编写一个embedding.py文件,主要存放各种方法,完整代码如下所示。值得注意的是,由于我的服务器有多张显卡,因此我将cuda设置为1,你如果只有一张显卡,就直接是cuda:0。

  1. from langchain_community.document_loaders import Docx2txtLoader, PyPDFLoader
  2. from langchain.text_splitter import RecursiveCharacterTextSplitter
  3. from transformers import BertModel, BertTokenizer
  4. import torch
  5. import os
  6. # -*- coding: utf-8 -*-
  7. class EmbeddingFunction:
  8. def __init__(self, embedding_function):
  9. self.embedding_function = embedding_function
  10. def embed_query(self, query):
  11. return self.embedding_function(query)
  12. def embed_documents(self, documents):
  13. return [self.embedding_function(doc) for doc in documents]
  14. class EmbeddingRetriever:
  15. def __init__(self):
  16. # 加载embedding
  17. self.embedding_model_dict = {
  18. "text2vec3": "shibing624/text2vec-base-chinese",
  19. "bert-base-chinese": "/home/ai/bert-base-chinese",
  20. }
  21. def load_documents(self,directory='kownledge'):
  22. documents = []
  23. for item in os.listdir(directory):
  24. if item.endswith("docx") or item.endswith("pdf"):
  25. split_docs = self.add_document(directory, item)
  26. documents.extend(split_docs)
  27. return documents
  28. def add_document(self, directory='kownledge', doc_name=''):
  29. file_path = os.path.join(directory, doc_name)
  30. if doc_name.endswith("docx"):
  31. loader = Docx2txtLoader(file_path=file_path)
  32. elif doc_name.endswith("pdf"):
  33. loader = PyPDFLoader(file_path=file_path)
  34. data = loader.load()
  35. text_spliter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100)
  36. split_docs = text_spliter.split_documents(data)
  37. return split_docs
  38. def load_embedding_mode(self,model_name="text2vec3"):
  39. model_path = self.embedding_model_dict[model_name]
  40. tokenizer = BertTokenizer.from_pretrained(model_path)
  41. model = BertModel.from_pretrained(model_path).to("cuda:1")
  42. def embedding_function(text):
  43. inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True, max_length=512).to("cuda:1")
  44. with torch.no_grad():
  45. outputs = model(**inputs)
  46. embedding = outputs.last_hidden_state.mean(dim=1).squeeze().cpu().numpy().tolist()
  47. return embedding
  48. return embedding_function
  49. def store_chroma(self,docs, db):
  50. db.add_documents(docs)
  51. db.persist()
  52. return db

简单解释如下,在load_documents和add_document方法中,由于我的知识文档是docx和pdf格式的,因此我就只写了两个类型,你如果有其他类型比如csv或者txt可以修改调用方式,如:

from langchain_community.document_loaders import  TextLoader,CSVLoader

测试

编写一个问答代码my_qa.py,完整代码如下所示,记得修改url地址。

  1. from langchain_community.vectorstores import Chroma
  2. import requests
  3. from embedding import EmbeddingRetriever,EmbeddingFunction
  4. # -*- coding: utf-8 -*-
  5. def chat(prompt, history=None):
  6. plyload = {
  7. "prompt": prompt, "history": [] if not history else history
  8. }
  9. headers = {"Content-Type": "application/json"}
  10. resp = requests.post(
  11. url = 'http://172.27.171.194:8000',
  12. json = plyload,
  13. headers = headers
  14. ).json()
  15. return resp['response']
  16. retriever = EmbeddingRetriever()
  17. embedding_function = EmbeddingFunction(retriever.load_embedding_mode())
  18. db = Chroma(embedding_function=embedding_function, persist_directory='VectorStore')
  19. # 添加知识文档
  20. if True:
  21. documents = retriever.add_document(doc_name='课程表.docx')
  22. #documents = retriever.load_documents()
  23. db = retriever.store_chroma(documents,db)
  24. while True:
  25. query = input("question:")
  26. similar_docs = db.similarity_search(query,k=3)
  27. prompt = "基于以下给出的资料,回答问题:\n"
  28. for doc in similar_docs:
  29. prompt += f"{doc.page_content}\n"
  30. prompt += f"问题: {query}"
  31. response = chat(prompt,[])
  32. print("Bot:", response)

简单解释如下:

在服务器上运行python my_qa.py,结果如下:

可以看到,准确度还是相当不错的。如果自己输入的知识库数量越多,回答越准确。

最后看看我的服务器上的文档位置。

可能出现的问题

1、解决transformers和sentence-transformers版本冲突问题

使用pip install -U sentence-transformers下载sentence-transformers时会下载最新版2.7.0并且把最新版的transformers4.39.3一起附带下载下来,但是在ChatGLM中要求的transformers版本是4.27.1,因此如果使用最新版的transformers在运行api.py接口时会报错提示没有xxxx属性。解决方式有两种,第一种就是手动降低版本(但可能会报出其他错误),第二种就是修改chatglm-6b的配置文件,如下所示:

就是将从Huggingface下载的模型chatglm-6b下的tokenization_chatglm.py文件进行修改,将第222行的代码注释,放在第196行,也就是super().__init__上面。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/寸_铁/article/detail/1009644
推荐阅读
相关标签
  

闽ICP备14008679号