From 47f6ebeaad4bc26e64cbf09beb1a667f1d5daaa3 Mon Sep 17 00:00:00 2001 From: duzx16 <904663169@qq.com> Date: Sun, 25 Jun 2023 12:30:59 +0800 Subject: [PATCH] Init commit --- README.md | 268 ++++++++++++++++++++++++++++++++++++++++++++++++++++ cli_demo.py | 11 ++- web_demo.py | 16 ++-- 3 files changed, 283 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index e69de29..c0b735d 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,268 @@ +

+🤗 HF Repo • 🐦 Twitter • 📃 [GLM@ACL 22] [GitHub] • 📃 [GLM-130B@ICLR 23] [GitHub]
+

+

+ 👋 加入我们的 SlackWeChat +

+ +## 介绍 + +ChatGLM**2**-6B 是开源中英双语对话模型 [ChatGLM-6B](https://github.com/THUDM/ChatGLM-6B) 的第二代版本,在保留了初代模型对话流畅、部署门槛较低等众多优秀特性的基础之上,ChatGLM**2**-6B 引入了如下新特性: + +1. **更强大的性能**:基于 ChatGLM 初代模型的开发经验,我们全面升级了 ChatGLM2-6B 的基座模型。ChatGLM2-6B 使用了 [GLM](https://github.com/THUDM/GLM) 的混合目标函数,经过了 1.4T 中英标识符的预训练与人类偏好对齐训练,[评测结果](README.md#评测结果)显示,相比于初代模型,ChatGLM2-6B 在 MMLU(+23%)、CEval(+33%)、GSM8K(+571%) 、BBH(+60%)等数据集上的性能取得了大幅度的提升,在同尺寸开源模型中具有较强的竞争力。 +2. **更长的上下文**:基于 [FlashAttention](https://github.com/HazyResearch/flash-attention) 技术,我们将基座模型的上下文长度(Context Length)由 ChatGLM-6B 的 2K 扩展到了 32K,并在对话阶段使用 8K 的上下文长度训练,允许更多轮次的对话。但当前版本的 ChatGLM2-6B 对单轮超长文档的理解能力有限,我们会在后续迭代升级中着重进行优化。 +3. **更高效的推理**:基于 [Multi-Query Attention](http://arxiv.org/abs/1911.02150) 技术,ChatGLM2-6B 有更高效的推理速度和更低的显存占用:在官方的模型实现下,推理速度相比初代提升了 42%,INT4 量化下,6G 显存支持的对话长度由 1K 提升到了 8K。 +4. **更开放的协议**:ChatGLM2-6B 权重对学术研究**完全开放**,在获得官方的书面许可后,ChatGLM2-6B 的权重亦**允许商业使用**。如果您发现我们的开源模型对您的业务有用,我们欢迎您对下一代模型 ChatGLM3 研发的捐赠。 + +----- + +ChatGLM2-6B 开源模型旨在与开源社区一起推动大模型技术发展,恳请开发者和大家遵守[开源协议](MODEL_LICENSE),勿将开源模型和代码及基于开源项目产生的衍生物用于任何可能给国家和社会带来危害的用途以及用于任何未经过安全评估和备案的服务。**目前,本项目团队未基于 ChatGLM2-6B 开发任何应用,包括网页端、安卓、苹果 iOS 及 Windows App 等应用。** + +尽管模型在训练的各个阶段都尽力确保数据的合规性和准确性,但由于 ChatGLM2-6B 模型规模较小,且模型受概率随机性因素影响,无法保证输出内容的准确性,且模型易被误导。**本项目不承担开源模型和代码导致的数据安全、舆情风险或发生任何模型被误导、滥用、传播、不当利用而产生的风险和责任。** + +## 评测结果 +我们选取了部分中英文典型数据集进行了评测,以下为 ChatGLM2-6B 模型在 [MMLU](https://github.com/hendrycks/test) (英文)、[C-Eval](https://cevalbenchmark.com/static/leaderboard.html)(中文)、[GSM8K](https://github.com/openai/grade-school-math)(数学)、[BBH](https://github.com/suzgunmirac/BIG-Bench-Hard)(英文) 上的测评结果。 + +### MMLU + +| Model | Average | STEM | Social Sciences | Humanities | Others | +| ----- | ----- | ---- | ----- | ----- | ----- | +| ChatGLM-6B | 40.63 | 33.89 | 44.84 | 39.02 | 45.71 | +| ChatGLM2-6B (base) | 47.86 | 41.20 | 54.44 | 43.66 | 54.46 | +| ChatGLM2-6B | 45.46 | 40.06 | 51.61 | 41.23 | 51.24 | + +> Chat 模型使用 zero-shot CoT (Chain-of-Thought) 的方法测试,Base 模型使用 few-shot answer-only 的方法测试 + +### C-Eval + +| Model | Average | STEM | Social Sciences | Humanities | Others | +| ----- | ---- | ---- | ----- | ----- | ----- | +| ChatGLM-6B | 38.9 | 33.3 | 48.3 | 41.3 | 38.0 | +| ChatGLM2-6B (base) | 51.7 | 48.6 | 60.5 | 51.3 | 49.8 | +| ChatGLM2-6B | 50.1 | 46.4 | 60.4 | 50.6 | 46.9 | + +> Chat 模型使用 zero-shot CoT 的方法测试,Base 模型使用 few-shot answer only 的方法测试 + +### GSM8K + +| Model | Accuracy | Accuracy (Chinese)* | +| ----- | ----- | ----- | +| ChatGLM-6B | 4.82 | 5.85 | +| ChatGLM2-6B (base) | 32.37 | 28.95 | +| ChatGLM2-6B | 28.05 | 20.45 | + +> 所有模型均使用 few-shot CoT 的方法测试,CoT prompt 来自 http://arxiv.org/abs/2201.11903 +> \* 我们使用翻译 API 翻译了 GSM8K 中的 500 道题目和 CoT prompt 并进行了人工校对 + + +### BBH + +| Model | Accuracy | +| ----- | ----- | +| ChatGLM-6B | 18.73 | +| ChatGLM2-6B (base) | 33.68 | +| ChatGLM2-6B | 30.00 | + +> 所有模型均使用 few-shot CoT 的方法测试,CoT prompt 来自 https://github.com/suzgunmirac/BIG-Bench-Hard/tree/main/cot-prompts + +## 推理性能 +ChatGLM2-6B 使用了 [Multi-Query Attention](http://arxiv.org/abs/1911.02150),提高了生成速度。生成 2000 个字符的平均速度对比如下 + +| Model | 推理速度 (字符/秒) | +| ---- | ----- | +| ChatGLM-6B | 31.49 | +| ChatGLM2-6B | 44.62 | + +> 使用官方实现,Batch size = 1,测试硬件为 A100-SXM-80G,软件环境为 PyTorch 2.0 + +Multi-Query Attention 同时也降低了生成过程中 KV Cache 的显存占用,此外,ChatGLM2-6B 采用 Causal Mask 进行对话训练,连续对话时可复用前面轮次的 KV Cache,进一步优化了显存占用。因此,使用 6GB 显存的显卡进行 INT4 量化的推理时,初代的 ChatGLM-6B 模型最多能够生成 1119 个字符就会提示显存耗尽,而 ChatGLM2-6B 能够生成至少 8192 个字符。 + +| **量化等级** | **编码 2048 长度的最小显存** | **生成 8192 长度的最小显存** | +| -------------- | --------------------- | --------------- | +| FP16 / BF16 | 13.1 GB | 14 GB | +| INT8 | 9 GB | 9 GB | +| INT4 | 6 GB | 6 GB | + +> ChatGLM2-6B 利用了 PyTorch 2.0 引入的 `torch.nn.functional.scaled_dot_product_attention` 实现高效的 Attention 计算,如果 PyTorch 版本较低则会 fallback 到朴素的 Attention 实现,出现显存占用高于上表的情况。 + +我们也测试了量化对模型性能的影响。 + + + +## ChatGLM2-6B 示例 + +相比于初代模型,ChatGLM2-6B 多个维度的能力都取得了提升,以下是一些对比示例。更多 ChatGLM2-6B 的可能,等待你来探索发现! + +
数理逻辑 + +![](examples/math.png) + +
+ +
知识推理 + +![](examples/knowledge.png) + +
+ +
长文档理解 + +![](examples/long-context.png) + +
+ +## 使用方式 +### 环境安装 + +使用 pip 安装依赖:`pip install -r requirements.txt`,其中 `transformers` 库版本推荐为 `4.27.1`,`torch` 推荐使用 2.0 以上的版本,以获得最佳的推理性能。 + + +我们提供了一个网页版 Demo 和一个命令行 Demo。使用时首先需要下载本仓库: + +```shell +git clone https://github.com/THUDM/ChatGLM2-6B +cd ChatGLM2-6B +``` + +### 代码调用 + +可以通过如下代码调用 ChatGLM2-6B 模型来生成对话: + +```python +>>> from transformers import AutoTokenizer, AutoModel +>>> tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm2-6b", trust_remote_code=True) +>>> model = AutoModel.from_pretrained("THUDM/chatglm2-6b", trust_remote_code=True, device='cuda').eval() +>>> response, history = model.chat(tokenizer, "你好", history=[]) +>>> print(response) +你好👋!我是人工智能助手 ChatGLM2-6B,很高兴见到你,欢迎问我任何问题。 +>>> response, history = model.chat(tokenizer, "晚上睡不着应该怎么办", history=history) +>>> print(response) +晚上睡不着可能会让你感到焦虑或不舒服,但以下是一些可以帮助你入睡的方法: + +1. 制定规律的睡眠时间表:保持规律的睡眠时间表可以帮助你建立健康的睡眠习惯,使你更容易入睡。尽量在每天的相同时间上床,并在同一时间起床。 +2. 创造一个舒适的睡眠环境:确保睡眠环境舒适,安静,黑暗且温度适宜。可以使用舒适的床上用品,并保持房间通风。 +3. 放松身心:在睡前做些放松的活动,例如泡个热水澡,听些轻柔的音乐,阅读一些有趣的书籍等,有助于缓解紧张和焦虑,使你更容易入睡。 +4. 避免饮用含有咖啡因的饮料:咖啡因是一种刺激性物质,会影响你的睡眠质量。尽量避免在睡前饮用含有咖啡因的饮料,例如咖啡,茶和可乐。 +5. 避免在床上做与睡眠无关的事情:在床上做些与睡眠无关的事情,例如看电影,玩游戏或工作等,可能会干扰你的睡眠。 +6. 尝试呼吸技巧:深呼吸是一种放松技巧,可以帮助你缓解紧张和焦虑,使你更容易入睡。试着慢慢吸气,保持几秒钟,然后缓慢呼气。 + +如果这些方法无法帮助你入睡,你可以考虑咨询医生或睡眠专家,寻求进一步的建议。 +``` +模型的实现仍然处在变动中。如果希望固定使用的模型实现以保证兼容性,可以在 `from_pretrained` 的调用中增加 `revision="v1.0"` 参数。`v1.0` 是当前最新的版本号,完整的版本列表参见 [Change Log](https://huggingface.co/THUDM/chatglm2-6b#change-log)。 + +### 网页版 Demo + +![web-demo](resources/web-demo.gif) + +首先安装 Gradio:`pip install gradio`,然后运行仓库中的 [web_demo.py](web_demo.py): + +```shell +python web_demo.py +``` + +程序会运行一个 Web Server,并输出地址。在浏览器中打开输出的地址即可使用。 +> 由于国内 Gradio 的网络访问较为缓慢,启用 `demo.queue().launch(share=True, inbrowser=True)` 时所有网络会经过 Gradio 服务器转发,导致打字机体验大幅下降,现在默认启动方式已经改为 `share=False`,如有需要公网访问的需求,可以重新修改为 `share=True` 启动。 + +### 命令行 Demo + +![cli-demo](resources/cli-demo.png) + +运行仓库中 [cli_demo.py](cli_demo.py): + +```shell +python cli_demo.py +``` + +程序会在命令行中进行交互式的对话,在命令行中输入指示并回车即可生成回复,输入 `clear` 可以清空对话历史,输入 `stop` 终止程序。 + +### API 部署 +首先需要安装额外的依赖 `pip install fastapi uvicorn`,然后运行仓库中的 [api.py](api.py): +```shell +python api.py +``` +默认部署在本地的 8000 端口,通过 POST 方法进行调用 +```shell +curl -X POST "http://127.0.0.1:8000" \ + -H 'Content-Type: application/json' \ + -d '{"prompt": "你好", "history": []}' +``` +得到的返回值为 +```shell +{ + "response":"你好👋!我是人工智能助手 ChatGLM2-6B,很高兴见到你,欢迎问我任何问题。", + "history":[["你好","你好👋!我是人工智能助手 ChatGLM2-6B,很高兴见到你,欢迎问我任何问题。"]], + "status":200, + "time":"2023-03-23 21:38:40" +} +``` +## 低成本部署 + +### 模型量化 + +默认情况下,模型以 FP16 精度加载,运行上述代码需要大概 13GB 显存。如果你的 GPU 显存有限,可以尝试以量化方式加载模型,使用方法如下: + +```python +# 按需修改,目前只支持 4/8 bit 量化 +model = AutoModel.from_pretrained("THUDM/chatglm2-6b", trust_remote_code=True).quantize(8).cuda() +``` + +模型量化会带来一定的性能损失,经过测试,ChatGLM2-6B 在 4-bit 量化下仍然能够进行自然流畅的生成。 + + + + + +### CPU 部署 + +如果你没有 GPU 硬件的话,也可以在 CPU 上进行推理,但是推理速度会更慢。使用方法如下(需要大概 32GB 内存) +```python +model = AutoModel.from_pretrained("THUDM/chatglm2-6b", trust_remote_code=True).float() +``` + + + +### Mac 部署 + +对于搭载了 Apple Silicon 或者 AMD GPU 的 Mac,可以使用 MPS 后端来在 GPU 上运行 ChatGLM2-6B。需要参考 Apple 的 [官方说明](https://developer.apple.com/metal/pytorch) 安装 PyTorch-Nightly(正确的版本号应该是2.1.0.dev2023xxxx,而不是 2.0.0)。 + +目前在 MacOS 上只支持[从本地加载模型](README.md#从本地加载模型)。将代码中的模型加载改为从本地加载,并使用 mps 后端: +```python +model = AutoModel.from_pretrained("your local path", trust_remote_code=True).to('mps') +``` + +加载半精度的 ChatGLM2-6B 模型需要大概 13GB 内存。内存较小的机器(比如 16GB 内存的 MacBook Pro),在空余内存不足的情况下会使用硬盘上的虚拟内存,导致推理速度严重变慢。 + + + +## 引用 + +如果你觉得我们的工作有帮助的话,请考虑引用下列论文,ChatGLM2-6B 的论文会在近期公布,尽情期待~ + +``` +@article{zeng2022glm, + title={Glm-130b: An open bilingual pre-trained model}, + author={Zeng, Aohan and Liu, Xiao and Du, Zhengxiao and Wang, Zihan and Lai, Hanyu and Ding, Ming and Yang, Zhuoyi and Xu, Yifan and Zheng, Wendi and Xia, Xiao and others}, + journal={arXiv preprint arXiv:2210.02414}, + year={2022} +} +``` +``` +@inproceedings{du2022glm, + title={GLM: General Language Model Pretraining with Autoregressive Blank Infilling}, + author={Du, Zhengxiao and Qian, Yujie and Liu, Xiao and Ding, Ming and Qiu, Jiezhong and Yang, Zhilin and Tang, Jie}, + booktitle={Proceedings of the 60th Annual Meeting of the Association for Computational Linguistics (Volume 1: Long Papers)}, + pages={320--335}, + year={2022} +} +``` \ No newline at end of file diff --git a/cli_demo.py b/cli_demo.py index b749e31..b103c7b 100644 --- a/cli_demo.py +++ b/cli_demo.py @@ -4,8 +4,8 @@ import signal from transformers import AutoTokenizer, AutoModel import readline -tokenizer = AutoTokenizer.from_pretrained("/mnt/vepfs/workspace/zxdu/chatglm-6b-v2-dev", trust_remote_code=True) -model = AutoModel.from_pretrained("/mnt/vepfs/workspace/zxdu/chatglm-6b-v2-dev", trust_remote_code=True, device='cuda') +tokenizer = AutoTokenizer.from_pretrained("chatglm2-6b", trust_remote_code=True) +model = AutoModel.from_pretrained("chatglm2-6b", trust_remote_code=True, device='cuda') model = model.eval() os_name = platform.system() @@ -29,7 +29,7 @@ def signal_handler(signal, frame): def main(): past_key_values, history = None, [] global stop_stream - print("欢迎使用 ChatGLM-6B 模型,输入内容即可进行对话,clear 清空对话历史,stop 终止程序") + print("欢迎使用 ChatGLM2-6B 模型,输入内容即可进行对话,clear 清空对话历史,stop 终止程序") while True: query = input("\n用户:") if query.strip() == "stop": @@ -37,11 +37,12 @@ def main(): if query.strip() == "clear": past_key_values, history = None, [] os.system(clear_command) - print("欢迎使用 ChatGLM-6B 模型,输入内容即可进行对话,clear 清空对话历史,stop 终止程序") + print("欢迎使用 ChatGLM2-6B 模型,输入内容即可进行对话,clear 清空对话历史,stop 终止程序") continue count = 0 for response, history, past_key_values in model.stream_chat(tokenizer, query, history=history, - past_key_values=past_key_values): + past_key_values=past_key_values, + return_past_key_values=True): if stop_stream: stop_stream = False break diff --git a/web_demo.py b/web_demo.py index 17e85d2..e208311 100644 --- a/web_demo.py +++ b/web_demo.py @@ -2,8 +2,8 @@ from transformers import AutoModel, AutoTokenizer import gradio as gr import mdtex2html -tokenizer = AutoTokenizer.from_pretrained("/mnt/vepfs/workspace/zxdu/chatglm-6b-v2-dev", trust_remote_code=True) -model = AutoModel.from_pretrained("/mnt/vepfs/workspace/zxdu/chatglm-6b-v2-dev", trust_remote_code=True, device='cuda') +tokenizer = AutoTokenizer.from_pretrained("chatglm2-6b", trust_remote_code=True) +model = AutoModel.from_pretrained("chatglm2-6b", trust_remote_code=True, device='cuda').bfloat16() model = model.eval() """Override Chatbot.postprocess""" @@ -59,7 +59,9 @@ def parse_text(text): def predict(input, chatbot, max_length, top_p, temperature, history, past_key_values): chatbot.append((parse_text(input), "")) for response, history, past_key_values in model.stream_chat(tokenizer, input, history, past_key_values=past_key_values, - max_length=max_length, top_p=top_p, temperature=temperature): + return_past_key_values=True, + max_length=max_length, top_p=top_p, + temperature=temperature): chatbot[-1] = (parse_text(input), parse_text(response)) yield chatbot, history, past_key_values @@ -74,7 +76,7 @@ def reset_state(): with gr.Blocks() as demo: - gr.HTML("""

ChatGLM

""") + gr.HTML("""

ChatGLM2-6B

""") chatbot = gr.Chatbot() with gr.Row(): @@ -86,9 +88,9 @@ with gr.Blocks() as demo: 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) + max_length = gr.Slider(0, 32768, value=32768, step=1.0, label="Maximum length", interactive=True) + top_p = gr.Slider(0, 1, value=0.8, step=0.01, label="Top P", interactive=True) + temperature = gr.Slider(0, 1, value=0.8, step=0.01, label="Temperature", interactive=True) history = gr.State([]) past_key_values = gr.State(None)