LangChain是一个强大的工具,用于构建由语言模型(LLM)驱动的应用程序,它提供了与多种不同语言模型交互的标准接口。在本书前面的内容中,绝大多数LangChain例子都是基于OpenAI的ChatGPT模型实现的,其实LangChain可以适配并使用我们的国产模型。在本章的内容中,将详细讲解使用LangChain操作国产模型的知识。
7.2.1 LangChain与ChatGLM实践
ChatGLM 是由智谱AI推出的一系列大模型,这些模型基于大规模预训练技术,具备强大的自然语言处理能力。因为ChatGLM已经开源了包括 ChatGLM-6B在内的多个模型版本,为了降低大家的学习成本,本书将以ChatGLM-6B为例介绍LangChain操作ChatGLM的知识。
1. ChatGLM介绍
ChatGLM-6B是ChatGLM大模型系列中的一个开源模型(开源地址https://github.com/THUDM/ChatGLM-6B),这个模型基于General Language Model (GLM)架构,拥有62亿参数,并且特别针对中文进行了优化。ChatGLM-6B在1:1比例的中英语料上训练了1T的token量,兼具双语能力,并且通过模型量化技术,可以在消费级的显卡上进行本地部署,INT4量化级别下最低只需6GB显存。
ChatGLM系列大模型包括但不限于对话生成、智能问答等任务,而LangChain可以适配并使用这些模型。ChatGLM的基本信息如下所示:
- 模型支持:LangChain支持ChatGLM模型,并且有专门的第三方实现集中在LangChain-Community库中。
- 集成ChatGLM:开发者可以通过继承LangChain提供的基类并实现相关方法来创建自定义的ChatModel,从而集成ChatGLM模型。
- API适配:为了适配ChatGLM的API,开发者可能需要自定义适配器,并确保与LangChain的接口兼容。
- 使用示例:有教程和代码示例说明了如何在本地环境中部署ChatGLM模型,并与LangChain结合使用,以创建知识库和执行问答任务。
- 微调和应用:LangChain不仅可以加载和运行ChatGLM模型,还可以用于模型的微调和在特定领域的应用。
- 开源社区:LangChain的开源社区提供了多种集成和应用示例,包括与ChatGLM模型的集成。
- 模型升级:LangChain能够支持ChatGLM模型的更新,并实现接口的兼容性,这表明LangChain能够适应模型的升级和变化,保持应用的连续性。
- 文档和教程:存在详细的文档和教程,帮助用户理解如何将ChatGLM与LangChain结合使用,包括如何加载模型、处理文件、向量化文本、以及如何使用LangChain进行问答。
2. 准备开发环境
建议直接参考ChatGLM-6B开源地址中的配置文件https://github.com/THUDM/ChatGLM-6B/blob/main/requirements.txt:
protobuf
transformers==4.27.1
cpm_kernels
torch>=1.10
gradio
mdtex2html
sentencepiece
accelerate
通过如下命令安装所需要的库:
pip install -r requirements.txt
3. 准备模型文件
在目前的就似乎条件下,可以通过如下三种方法准备文件:
(1)直接从huggingface模型仓库拉取模型文件,这种方法的缺点是需要先安装Git LFS,拉取速度很慢。
git clone https://huggingface.co/THUDM/chatglm-6b
(2)从huggingface模型残酷拉取模型实现,然后从清华仓库下载模型参数文件,然后替换到chatglm-6b文件夹中。
GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/THUDM/chatglm-6b
# 下载模型参数文件...
mv chatglm-6b/* THUDM/chatglm-6b/
(3)通过使用transformers库中的类AutoTokenizer和类AutoModel,自动实现ChatGLM-6B模型和分词器的下载及加载工作。
import os
from transformers import AutoTokenizer, AutoModel
tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True)
model = AutoModel.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True)
请看下面的实例,演示了实用第三种方法调用ChatGLM-6B模型进行对话的过程。
实例7-1:使用自定义的LLM返回输入信息中的前n个字符(源码路径:codes\7\qing.py)
实例文件qing.py的具体实现代码如下所示。
import os
from transformers import AutoTokenizer, AutoModel
# 指定使用编号为0的GPU
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
# 加载分词器和模型
tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True)
model = AutoModel.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True)
# 将模型发送到GPU,并设置为评估模式
model = model.to('cuda') # 这将模型移动到默认的GPU上
model = model.eval()
# 将模型转换为半精度
model.half()
human_input = "你好"
response, history = model.chat(tokenizer, human_input, history=[])
print(f"Human: {human_input}")
print(f"AI: {response}")
上述代码的目的是使用库transformers从Hugging Face模型库中加载并运行一个名为"THUDM/chatglm-6b"的预训练对话模型,以下是代码的详细步骤和功能:
(1)环境设置:os.environ["CUDA_VISIBLE_DEVICES"] = "0":这行代码设置了环境变量,指定程序只能看到编号为0的GPU。如果你的系统中有多个GPU,这可以用来决定哪个GPU用于模型的推理或训练。
(2)加载分词器和模型
- tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True):加载与"THUDM/chatglm-6b"模型配套的分词器。分词器用于将文本转换为模型可以理解的格式。
- model = AutoModel.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True):加载预训练的"THUDM/chatglm-6b"模型。trust_remote_code=True参数允许加载远程代码,这在加载自定义或第三方模型时是必需的。
(3)准备模型进行推理
- model = model.to('cuda'):将模型的权重移动到GPU上,以利用GPU加速模型的推理过程。这一步假定你的机器上安装了NVIDIA的GPU和CUDA。
- model = model.eval():将模型设置为评估模式,这通常关闭了模型训练时使用的特定功能,如dropout。
(4)model.half():将模型转换为半精度(float16),这可以减少模型在GPU上的内存占用,允许更大的模型或批量大小,但也需要硬件和深度学习框架支持半精度运算。
(5)生成对话响应
- human_input = "你好":定义人类的输入文本。
- response, history = model.chat(tokenizer, human_input, history=[]):调用模型的chat方法来生成响应。这个方法需要分词器、用户的输入,以及对话历史。
(6)打印结果:最后,代码打印输出了用户的输入和AI生成的回答响应。执行后会输出:
Loading checkpoint shards: 100%|██████████| 8/8 [00:38<00:00, 4.87s/it]
Human: 你好
AI: 你好��!我是人工智能助手 ChatGLM-6B,很高兴见到你,欢迎问我任何问题。
4. 模型服务调用接口
在大模型开发应用中,模型服务调用接口(API)扮演着至关重要的角色,主要体现在以下几个方面:
- 封装复杂性:API为终端用户或下游应用提供了一个简单、标准化的接口来与模型交互,隐藏了背后的复杂实现细节。
- 可访问性:通过API,用户可以远程访问模型的推理能力,而无需了解模型的内部工作原理或直接与模型文件交互。
- 扩展性:API允许模型服务水平扩展,以支持大量并发请求,满足不同规模用户的需求。
- 灵活性:API可以轻松地集成到不同的应用程序中,包括Web应用、移动应用、聊天机器人、以及其他第三方服务。
- 安全性:API服务器可以实现认证和授权机制,确保只有合法用户可以调用模型服务。
- 监控和管理:API服务器可以集成监控和日志记录工具,以跟踪模型的使用情况,及时发现并解决潜在问题。
- 版本控制:通过API,可以更容易地对模型进行版本控制和更新,而不会影响现有用户。
- 负载均衡:API服务器可以分配请求到多个模型实例,实现负载均衡,提高服务的可用性和响应速度。
- 计费和配额管理:API可以用于实现计费系统,对使用模型的频率和次数进行限制和跟踪。
- 跨平台兼容性:API使得模型服务可以跨越不同的操作系统和编程语言,提供广泛的兼容性。
- 简化部署:API允许开发者将模型作为服务(Model as a Service, MaaS)部署,简化了部署和运维流程。
- 促进协作:API可以作为团队或组织之间协作的基础,允许不同团队独立地开发、部署和使用模型。
在实际应用中,模型服务调用接口通常是通过HTTP协议实现的,使用RESTful或GraphQL等风格。这些接口可以返回JSON、XML或其他格式的数据,以便于客户端处理。通过精心设计API,可以提高大模型的可用性、灵活性和整体的用户体验。
请看下面的例子,使用Flask技术实现了一个简单的模型服务调用接口。在这个Web应用中,使用库transformers加载了一个预训练的对话模型ChatGLM-6B,并提供了一个HTTP端点来接收用户的输入并返回模型的响应。
实例7-2:使用Flask实现一个简单的模型服务调用接口(源码路径:codes\7\diao.py)
实例文件diao.py的具体实现代码如下所示。
import os
import json
from flask import Flask, request, jsonify
from transformers import AutoTokenizer, AutoModel
# 系统参数
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
# 加载分词器和模型
tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True)
model = AutoModel.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True)
model = model.to('cuda') # 将模型发送到GPU
model = model.eval() # 将模型设置为评估模式
model.half() # 将模型转换为半精度
app = Flask(__name__)
@app.route("/", methods=["GET"])
def root():
""“根路由响应。”""
return "这是主页."
@app.route("/chat", methods=["POST"])
def chat():
"""处理聊天请求."""
# 从POST请求中获取JSON数据
data = request.json
human_input = data.get("human_input", "")
# 从模型生成响应
response, _ = model.chat(tokenizer, human_input, history=[])
# 创建一个结果字典
result = {
"human_input": human_input,
"ai_response": response
}
# 将响应作为JSON返回
return jsonify(result)
if __name__ == "__main__":
app.run(host="127.0.0.1", port=8595, debug=False)
上述代码的实现流程如下所示:
(1)设置环境变量:使用os.environ指定程序只能看到编号为0的GPU,这样模型训练或推理时只会使用这个GPU。
- 加载模型和分词器
- 使用AutoTokenizer.from_pretrained和AutoModel.from_pretrained方法加载预训练的分词器和模型,将模型发送到GPU(如果可用)。
- 将模型设置为评估模式,关闭某些训练时使用的特性,如dropout。
- 将模型转换为半精度,以节省GPU内存。
(3)初始化Flask应用:创建一个Flask应用实例。
(4)定义路由和视图函数,使用@app.route装饰器定义两个路由:
- /:根路由,返回欢迎信息。
- /chat:聊天路由,处理POST请求,生成并返回聊天响应。
(5)聊天视图函数 (chat())
- 从POST请求的JSON数据中获取用户输入,使用模型和分词器生成AI的响应创建一个包含用户输入和AI响应的字典。
- 使用jsonify将结果字典转换为JSON格式的响应,并返回。
(6)运行Flask应用:使用app.run()启动Flask应用,监听127.0.0.1的8595端口,不开启调试模式。
(8)客户端请求处理:当客户端(如Web浏览器、API测试工具或自定义脚本)发送POST请求到/chat端点时,Flask应用将处理这个请求,并返回AI的响应。
执行实例文件diao.py后会输出:
Loading checkpoint shards: 100%|██████████| 8/8 [00:48<00:00, 6.12s/it]
* Serving Flask app 'diao'
* Debug mode: off
* Running on http://127.0.0.1:8595
打开一个新的命令行窗口(例如CMD或curl),然后运行以下命令:
curl -X POST -H "Content-Type: application/json" -d "{\"human_input\": \"你好\"}" http://127.0.0.1:8595/chat
上述命令会向Flask应用发送一个JSON格式的POST请求,请求体包含一个键为human_input,值为“你好”的字符串。按下回车键后会获得ChatGLM-6B的回复信息,完整的执行过程如下:
C:\Users\37197>curl -X POST -H "Content-Type: application/json" -d "{\"human_input\": \"你好\"}" http://127.0.0.1:8595/chat
{"response": "你好👋!我是人工智能助手 ChatGLM-6B,很高兴见到你,欢迎问我任何问题。"}
5. LangChain调用ChatGLM
在前面的内容中,已经使用Flask实现了一个ChatGLM-6B模型服务的调用接口,请看下面的例子,使用LangChain构建了一个聊天语言模型(LLM)的接口,并与运行在本地服务器上的模型进行交互。在本实例中,使用LLMs模块封装了ChatGLM模型。
实例7-3:使用LangChain构建一个聊天语言模型的接口(源码路径:codes\7\lang.py)
实例文件lang.py的具体实现代码如下所示。
import time
import logging
import requests
from typing import Optional, List, Dict, Mapping, Any
import langchain
from langchain.llms.base import LLM
from langchain.cache import InMemoryCache
logging.basicConfig(level=logging.INFO)
# 启动llm的缓存
langchain.llm_cache = InMemoryCache()
class ChatGLM(LLM):
# 模型服务url
url = "http://127.0.0.1:8595/chat"
@property
def _llm_type(self) -> str:
return "chatglm"
def _construct_query(self, prompt: str) -> Dict:
"""构造请求体
"""
query = {
"human_input": prompt
}
return query
@classmethod
def _post(cls, url: str,
query: Dict) -> Any:
"""POST请求
"""
_headers = {"Content_Type": "application/json"}
with requests.session() as sess:
resp = sess.post(url,
json=query,
headers=_headers,
timeout=60)
return resp
def _call(self, prompt: str,
stop: Optional[List[str]] = None) -> str:
"""_call
"""
# construct query
query = self._construct_query(prompt=prompt)
# post
resp = self._post(url=self.url,
query=query)
if resp.status_code == 200:
resp_json = resp.json()
predictions = resp_json["response"]
return predictions
else:
return "请求模型"
@property
def _identifying_params(self) -> Mapping[str, Any]:
"""Get the identifying parameters.
"""
_param_dict = {
"url": self.url
}
return _param_dict
if __name__ == "__main__":
llm = ChatGLM()
while True:
human_input = input("Human: ")
begin_time = time.time() * 1000
# 请求模型
response = llm(human_input, stop=["you"])
end_time = time.time() * 1000
used_time = round(end_time - begin_time, 3)
logging.info(f"chatGLM process time: {used_time}ms")
print(f"ChatGLM: {response}")
上述代码的实现流程如下所示:
(1)配置日志记录:使用 logging.basicConfig 设置日志记录的基本配置,日志级别为 INFO。
(2)初始化LLM缓存:langchain.llm_cache 被设置为 InMemoryCache,这意味着LLM的响应可能会被缓存在内存中,以加快后续请求的处理速度。
(3)定义类ChatGLM:该类继承自 langchain.llms.base.LLM,实现了一个与远程模型服务交互的聊天模型接口。
- 类变量url:包含了模型服务的 URL。
- 属性_llm_type:返回字符串 "chatglm",标识模型类型。
- 方法_construct_query:用于构造请求体,将提示(prompt)封装为一个字典。
- 方法_post,用于发送 POST 请求到模型服务,传递 JSON 数据。
- 方法_call:实现了基类 LLM 的抽象方法,用于构造查询,发送 POST 请求,并处理响应。如果响应状态码为200,它将解析 JSON 响应并返回预测结果;否则,它将返回字符串 "请求模型"。
- 属性_identifying_params:返回一个包含 URL 的字典,这些参数用于标识模型。
(4)主程序:在 if __name__ == "__main__": 块中,代码创建了一个 ChatGLM 实例,并进入一个无限循环,不断从用户那里接收输入,然后调用 llm 实例和用户的输入来获取响应。
(5)性能监控:使用函数time.time()来测量处理每个请求所需的时间,并以毫秒为单位打印出来。
(6)处理请求和响应:用户输入被传递给 ChatGLM 实例的调用,接收的响应被打印到控制台上。
首先运行前面的Flask程序文件diao.py,然后执行本实例程序文件,执行后会输出:
Human: 你好
INFO:root:chatGLM process time: 993.685ms
ChatGLM: 你好 !我是人工智能助手 ChatGLM-6B,很高兴见到你,欢迎问我任何问题。
Human: 你好
INFO:root:chatGLM process time: 0.116ms
ChatGLM: 你好 !我是人工智能助手 ChatGLM-6B,很高兴见到你,欢迎问我任何问题。