我的编程空间,编程开发者的网络收藏夹
学习永远不晚

chatglm实现基于知识库问答的应用

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

chatglm实现基于知识库问答的应用

背景

目前由于ChatGPT横空出世,互联网如雨后春笋冒出了非常多的类ChatGPT的大型语言模型。但是对于这些语言模型,我们应该如何将它应用到我们实际的生产中需要一个更加成熟的解决方案。

介绍

本文旨在通过介绍ChatGLM的使用来讲述如何将一个开源的语言模型应用于智能问答,知识库问答的场景中,通过一系列实操例子来理解整个应用思路。

前期准备

  • 一个开源语言模型,这里推荐ChatGLM-6B,开源的、支持中英双语的对话语言模型,并且要求的显存内存非常低,可以在个人PC中轻松部署。
  • python3.8+
  • milvus,向量索引库
  • pytorch以及运行ChatGLM-6B所需要的CUDA和NVIDIA驱动

基于文档的知识库问答

实现步骤

  1. 清洗知识库文档,将文档向量化并存入向量数据库
  2. 用户提问
  3. 将用户提问向量化并查询向量数据库得到匹配的N条知识
  4. 将匹配的知识构建prompt,并通过langchain处理用户的问题
  5. 调用llm搭配prompt回答用户的问题

向量索引

我们首先需要定义一个向量索引库,在这里我选用的是milvus作为向量索引库来实现我们的文档向量索引和相似度匹配的工作

为了更方便的部署,这里我采用了docker-compose来启动milvus服务。

大家可以在milvus的官方文档中看到最新版本的部署方式Install Milvus Standalone with Docker Compose
嫌麻烦也可以直接复制使用下面的yaml文件

version: '3.5'services:  etcd:    container_name: milvus-etcd    image: quay.io/coreos/etcd:v3.5.0    environment:      - ETCD_AUTO_COMPACTION_MODE=revision      - ETCD_AUTO_COMPACTION_RETENTION=1000      - ETCD_QUOTA_BACKEND_BYTES=4294967296      - ETCD_SNAPSHOT_COUNT=50000    volumes:      - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd    command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd  minio:    container_name: milvus-minio    image: minio/minio:RELEASE.2023-03-20T20-16-18Z    environment:      MINIO_ACCESS_KEY: minioadmin      MINIO_SECRET_KEY: minioadmin    volumes:      - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio:/minio_data    command: minio server /minio_data    healthcheck:      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]      interval: 30s      timeout: 20s      retries: 3  standalone:    container_name: milvus-standalone    image: milvusdb/milvus:v2.2.5    command: ["milvus", "run", "standalone"]    environment:      ETCD_ENDPOINTS: etcd:2379      MINIO_ADDRESS: minio:9000    volumes:      - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus    ports:      - "19530:19530"      - "9091:9091"    depends_on:      - "etcd"      - "minio"networks:  default:    name: milvus

当我们创建好docker-compose.yml文件之后就可以使用命令行docker-compose up -d来启动milvus服务。

接下来就是文档预处理

文档预处理

当我们收集到足够的文档之后,我们需要对文档进行一些清洗,方便我们之后的向量匹配更加精准。
这里,我们需要完成以下步骤:

  1. 连接milvus向量库
  2. 创建对应的connection
  3. 遍历读取文档
  4. 文档预处理
  5. 文档内容转向量
  6. 存入向量库

为此,我们编写代码如下

import osimport reimport jiebaimport torchimport pandas as pdfrom pymilvus import utilityfrom pymilvus import connections, CollectionSchema, FieldSchema, Collection, DataTypefrom transformers import AutoTokenizer, AutoModelconnections.connect(    alias="default",    host='localhost',    port='19530')# 定义集合名称和维度collection_name = "document"dimension = 768docs_folder = "./knowledge/"tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")model = AutoModel.from_pretrained("bert-base-chinese")# 获取文本的向量def get_vector(text):    input_ids = tokenizer(text, padding=True, truncation=True, return_tensors="pt")["input_ids"]    with torch.no_grad():        output = model(input_ids)[0][:, 0, :].numpy()    return output.tolist()[0]def create_collection():    # 定义集合字段    fields = [        FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True, description="primary id"),        FieldSchema(name="title", dtype=DataType.VARCHAR, max_length=50),        FieldSchema(name="content", dtype=DataType.VARCHAR, max_length=10000),        FieldSchema(name="vector", dtype=DataType.FLOAT_VECTOR, dim=dimension),    ]    # 定义集合模式    schema = CollectionSchema(fields=fields, description="collection schema")    # 创建集合    if utility.has_collection(collection_name):    # 如果你想继续添加新的文档可以直接 return。但你想要重新创建collection,就可以执行下面的代码        # return        utility.drop_collection(collection_name)        collection = Collection(name=collection_name, schema=schema, using='default', shards_num=2)        # 创建索引        default_index = {"index_type": "IVF_FLAT", "params": {"nlist": 2048}, "metric_type": "IP"}        collection.create_index(field_name="vector", index_params=default_index)        print(f"Collection {collection_name} created successfully")    else:        collection = Collection(name=collection_name, schema=schema, using='default', shards_num=2)        # 创建索引        default_index = {"index_type": "IVF_FLAT", "params": {"nlist": 2048}, "metric_type": "IP"}        collection.create_index(field_name="vector", index_params=default_index)        print(f"Collection {collection_name} created successfully")def init_knowledge():    collection = Collection(collection_name)    # 遍历指定目录下的所有文件,并导入到 Milvus 集合中    docs = []    for root, dirs, files in os.walk(docs_folder):        for file in files:            # 只处理以 .txt 结尾的文本文件            if file.endswith(".txt"):                file_path = os.path.join(root, file)                with open(file_path, "r", encoding="utf-8") as f:                    content = f.read()                # 对文本进行清洗处理                content = re.sub(r"\s+", " ", content)                title = os.path.splitext(file)[0]                # 分词                words = jieba.lcut(content)                # 将分词后的文本重新拼接成字符串                content = " ".join(words)                # 获取文本向量                vector = get_vector(title + content)                docs.append({"title": title, "content": content, "vector": vector})    # 将文本内容和向量通过 DataFrame 一起导入集合中    df = pd.DataFrame(docs)    collection.insert(df)    print("Documents inserted successfully")if __name__ == "__main__":    create_collection()    init_knowledge()

可以看到,我们创建了一个名为document的collection。它包含四个字段idtitlecontentvector其中vector储存的是content转化的向量。(当然,我们只是简单的实现了一个最原始的向量索引,如果你想要之后的匹配更加精准更加高效,你可以考虑将大文档按照段落切割并分别转化为向量,并且相互关联上。

于此同时,我们采用了jieba作为分词库,对文本进行清洗,还使用了正则去除了文档中不必要的一些特殊符号。这些操作可以让我们向量匹配更加精准。

当这些步骤全部执行完毕之后,我们就可以进行用户提问匹配向量库的操作了。

用户提问匹配知识库

首先,我们需要将用户提供的查询向量转换为blob对象,以便与数据库中的向量进行比较。我们在上个步骤实现了get_vector方法来将文本转为向量,现在可以继续调用该方法来实现。

其次我们需要将问题转化的向量用来查找向量库,并得出最为匹配的几个结果。编写代码如下:

import torchfrom document_preprocess import get_vectorfrom pymilvus import Collectioncollection = Collection("document")  # Get an existing collection.collection.load()DEVICE = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"# 定义查询函数def search_similar_text(input_text):    # 将输入文本转换为向量    input_vector = get_vector(input_text)# 查询前三个最匹配的向量ID    similarity = collection.search(        data=[input_vector],        anns_field="vector",        param={"metric_type": "IP", "params": {"nprobe": 10}, "offset": 0},        limit=3,        expr=None,        consistency_level="Strong"    )    ids = similarity[0].ids    # 通过ID查询出对应的知识库文档    res = collection.query(        expr=f"id in {ids}",        offset=0,        limit=3,        output_fields=["id", "content", "title"],        consistency_level="Strong"    )    print(res)    return resif __name__ == "__main__":question = input('Please enter your question: ')    search_similar_text(question)

上面我们通过向量索引库计算查询出了与问题最为接近的文档并打印了出来,接下来就到了最终的获取模型回答的环节了。

通过提示模板获取准确回答

在这一步,我们需要加载ChatGLM的预训练模型,并获取回答。

from transformers import AutoModel, AutoTokenizerfrom knowledge_query import search_similar_texttokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b-int4", trust_remote_code=True)model = AutoModel.from_pretrained("THUDM/chatglm-6b-int4", trust_remote_code=True).half().cuda()model = model.eval()def predict(input, max_length=2048, top_p=0.7, temperature=0.95, history=[]):res = search_similar_text(input)prompt_template = f"""基于以下已知信息,简洁和专业的来回答用户的问题。如果无法从中得到答案,请说 "当前会话仅支持解决一个类型的问题,请清空历史信息重试",不允许在答案中添加编造成分,答案请使用中文。已知内容:{res}问题:{input}"""query = prompt_templatefor response, history in model.stream_chat(tokenizer, query, history, max_length=max_length, top_p=top_p,               temperature=temperature):    chatbot[-1] = (parse_text(input), parse_text(response))    yield chatbot, history

上面使用了提示模板的方式,将我们查询出来的文档作为提示内容交给模型进行推理回答。到此,我们就简单实现了一个基于知识库的问答应用。

如果你想在web上像chatgpt一样提问,也可以丰富一下上面的代码

from transformers import AutoModel, AutoTokenizerimport gradio as grimport mdtex2htmlfrom knowledge_query import search_similar_texttokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b-int4", trust_remote_code=True)model = AutoModel.from_pretrained("THUDM/chatglm-6b-int4", trust_remote_code=True).half().cuda()model = model.eval()is_knowledge = True"""Override Chatbot.postprocess"""def postprocess(self, y):    if y is None:        return []    for i, (message, response) in enumerate(y):        y[i] = (            None if message is None else mdtex2html.convert((message)),            None if response is None else mdtex2html.convert(response),        )    return ygr.Chatbot.postprocess = postprocessdef parse_text(text):    """copy from https://github.com/GaiZhenbiao/ChuanhuChatGPT/"""    lines = text.split("\n")    lines = [line for line in lines if line != ""]    count = 0    for i, line in enumerate(lines):        if "```" in line:            count += 1            items = line.split('`')            if count % 2 == 1:                lines[i] = f'
{items[-1]}">'            else:                lines[i] = f'
'
else: if i > 0: if count % 2 == 1: line = line.replace("`", "\`") line = line.replace("<", "<") line = line.replace(">", ">") line = line.replace(" ", " ") line = line.replace("*", "*") line = line.replace("_", "_") line = line.replace("-", "-") line = line.replace(".", ".") line = line.replace("!", "!") line = line.replace("(", "(") line = line.replace(")", ")") line = line.replace("$", "$") lines[i] = "
"
+line text = "".join(lines) return textdef predict(input, chatbot, max_length, top_p, temperature, history): global is_knowledge chatbot.append((parse_text(input), "")) query = input if is_knowledge: res = search_similar_text(input) prompt_template = f"""基于以下已知信息,简洁和专业的来回答用户的问题。如果无法从中得到答案,请说 "当前会话仅支持解决一个类型的问题,请清空历史信息重试",不允许在答案中添加编造成分,答案请使用中文。已知内容:{res}问题:{input}""" query = prompt_template is_knowledge = False for response, history in model.stream_chat(tokenizer, query, history, max_length=max_length, top_p=top_p, temperature=temperature): chatbot[-1] = (parse_text(input), parse_text(response)) yield chatbot, historydef reset_user_input(): return gr.update(value='')def reset_state(): global is_knowledge is_knowledge = False return [], []with gr.Blocks() as demo: gr.HTML("""

ChatGLM

"""
) chatbot = gr.Chatbot() with gr.Row(): with gr.Column(scale=4): with gr.Column(scale=12): user_input = gr.Textbox(show_label=False, placeholder="Input...", lines=10).style( container=False) with gr.Column(min_width=32, scale=1): submitBtn = gr.Button("Submit", variant="primary") with gr.Column(scale=1): emptyBtn = gr.Button("Clear History") max_length = gr.Slider(0, 4096, value=2048, step=1.0, label="Maximum length", interactive=True) top_p = gr.Slider(0, 1, value=0.7, step=0.01, label="Top P", interactive=True) temperature = gr.Slider(0, 1, value=0.95, step=0.01, label="Temperature", interactive=True) history = gr.State([]) submitBtn.click(predict, [user_input, chatbot, max_length, top_p, temperature, history], [chatbot, history], show_progress=True) submitBtn.click(reset_user_input, [], [user_input]) emptyBtn.click(reset_state, outputs=[chatbot, history], show_progress=True)demo.queue().launch(share=False, inbrowser=True)

ChatGLM中的web_demo代码简单改写,我们就得到了一个一模一样的前端应用,不同的是它现在可以基于我们的知识库来回答问题。

小结

上述内容仅仅介绍了最简单的通过向量索引库加AI模型加提示工程来实现知识库问答的方案,其中向量索引和文档的处理非常原始与粗糙,想要实现更加精准的匹配还需要根据实际文档内容和场景来进行修改。

相关代码已上传github knowledge_with_chatglm感兴趣的同学可以 clone 下来跑一跑

使用langchain改进代码

langchain最为目前非常火的开源库,用于知识库问答也能极大的增加开发效率并且降低工作量。例如上述的文档预处理和用户提问匹配知识库两个步骤,我们用了很多代码编写来实现这个功能。但是当我们使用langchain之后就变得简单起来,下面给出代码示例:

from langchain.vectorstores import Milvusfrom langchain.document_loaders import DirectoryLoader, TextLoaderfrom langchain.text_splitter import CharacterTextSplitterfrom langchain.embeddings import HuggingFaceEmbeddings# 加载文件夹中的所有txt类型的文件loader = DirectoryLoader('./knowledge/', glob='**/*.txt', show_progress=True, loader_cls=TextLoader,                         loader_kwargs={"encoding": "utf-8"})documents = loader.load()# 初始化加载器text_splitter = CharacterTextSplitter(chunk_size=100, chunk_overlap=0)# 切割加载的 documentsplit_docs = text_splitter.split_documents(documents)embeddings = HuggingFaceEmbeddings(model_name="shibing624/text2vec-base-chinese")vector_db = Milvus.from_documents(split_docs, embeddings, connection_args={"host": "127.0.0.1", "port": "19530"},      collection_name="langchain_knowledge", drop_old=True)

通过上述代码可以看到,langchain为我们封装好了非常多的工具。例如DirectoryLoaderTextLoader可以直接让我们加载文档,配合CharacterTextSplitter可以将加载的文档分割成设定好的一片一片的集合。与此同时使用langchain提供的向量数据库工具,可以轻松将文档向量化并持久化储存。这样仅仅六行代码我们就完成了之前几十行代码才能完成的工作,且不必考虑如何创建字段,维护数据库等。

来源地址:https://blog.csdn.net/a914541185/article/details/130150101

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

chatglm实现基于知识库问答的应用

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何实现ERP基础知识问答

今天给大家介绍一下如何实现ERP基础知识问答。文章的内容小编觉得不错,现在给大家分享一下,觉得有需要的朋友可以了解一下,希望对大家有所帮助,下面跟着小编的思路一起来阅读吧。一、 ERP的实施1.ERP的实施分哪些阶段?ER
2023-06-04

深入解析Python中BeautifulSoup4的基础知识与实战应用

本文详细介绍了Python中BeautifulSoup4库的基础知识和实战应用。BeautifulSoup4用于从HTML/XML文档中提取数据,提供类似DOM的接口。安装后,可使用BeautifulSoup类解析HTML文档,通过find、find_all、select方法遍历和查找元素。提取数据可使用text、attrs、children、parent等方法。实战应用包括网络抓取、数据清理、网页分析和自动测试。最佳实践建议使用语义正确的文档、正确解析器、明确选择器和仔细检查提取数据。
深入解析Python中BeautifulSoup4的基础知识与实战应用
2024-04-02

SpringCloud基于Feign实现远程调用的问题小结

SpringCloudFeign是一种远程调用客户端,可简化服务间通信。它使用代理模式生成客户端代理,自动处理HTTP通信。Feign提供服务发现集成、负载均衡、异常处理和自定义注解支持。配置时,添加spring-cloud-starter-feign依赖项,并使用@FeignClient注解指定远程服务名称。使用时,注入代理类即可调用远程服务。常见问题包括HTTP状态码不正确、远程服务不可用等。解决方案包括检查HTTP状态码、确保服务可用性、正确定义和初始化FeignClient。最佳实践包括使用服务发
SpringCloud基于Feign实现远程调用的问题小结
2024-04-02

基于Redis实现分布式应用限流的方法

限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务。前几天在DD的公众号,看了一篇关于使用 瓜娃 实现单应用限流的方案 --》原文,参考《redis in action》 实
2023-05-30

Apache如何实现基于用户名的访问控制

Apache可以通过使用htpasswd文件和.htaccess文件来实现基于用户名的访问控制。创建htpasswd文件:首先要创建一个包含用户名和密码的htpasswd文件。可以使用htpasswd命令行工具来创建该文件。例如,运行以下命
Apache如何实现基于用户名的访问控制
2024-07-05

Android基于ViewPager实现的应用欢迎界面完整实例

本文实例讲述了Android基于ViewPager实现的应用欢迎界面。分享给大家供大家参考,具体如下: 有时候开发一个应用需要指导用户提示一些新功能,这样的欢迎界面的实现可以用一下方法 首先我们要用到ViewPager这个类,这个类是在An
2022-06-06

如何用生活里字典的实际应用来介绍Python基础中字典的知识

如何用生活里字典的实际应用来介绍Python基础中字典的知识,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。一、前言如果有列表 ,需要对"xiaoWang"这个名
2023-06-15

如何分析使用wxpy这个基于python实现的微信工具库的常见问题

本篇文章为大家展示了如何分析使用wxpy这个基于python实现的微信工具库的常见问题,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。使用如下的命令行安装:pip install wxpyCollec
2023-06-04

Sphinx搜索在智能问答系统中的应用探索(智能问答系统如何利用Sphinx实现高效搜索?)

Sphinx搜索在智能问答系统中至关重要,提供快速高效的全文搜索、相关性排序和查询扩展。通过高效的索引结构、丰富的排序算法、过滤和分组功能,Sphinx搜索增强了智能问答系统的功能。其分布式搜索、实时索引更新和扩展性确保了大规模系统的高性能和实时响应。具体应用包括StackOverflow、Quora和GoogleAssistant。
Sphinx搜索在智能问答系统中的应用探索(智能问答系统如何利用Sphinx实现高效搜索?)
2024-04-02

Spring框架基于注解的AOP之各种通知的使用与环绕通知实现详解

这篇文章主要介绍了Spring框架基于注解的AOP之各种通知的使用及其环绕通知,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
2022-11-13

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录