本地安装部署运行 ChatGLM-6B 的常见问题解答以及后续优化

报错 No module named ‘transformers_modules.THUDM/chatglm-6b’

报错本身的意思是,没有在指定的路径THUDM/chatglm-6b找到推理用模型
一般常见于自己手动下载模型,而不是通过下边这些文件直接启动,自动下载的情况
你需要修改web_demo.pyweb_demo.pyold_web_demo2.pycli_demo.pyapi.py等文件中涉及模型路径部分的代码,一般在文件的开头或者结尾附近。

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

将里面的"THUDM/chatglm-6b"改成实际模型存放路径名称即可,注意反斜杠问题

注意win系统下子文件夹的路径应该写成\"THUDM\chatglm-6b"), 原代码写的是linux文件系统用的/ ("THUDM/chatglm-6b")。

报错 not enough memory: you tried to allocate 123456789 bytes.

内存不足,显存不足,最简单就是换个更好的电脑吧,或者看后面ChatGLM-6B 减少显存与内存占用部分内容。

报错 AssertionError: Torch not compiled with CUDA enabled

原因是 你试图用GPU跑,但是你安装的 Torch 不支持CUDA,是仅支持CPU的版本

先执行命令:

python -c "import torch; print(torch.cuda.is_available())"

如果返回的是False,说明安装的PyTorch不支持CUDA,是仅支持CPU的,需要执行了下面的命令安装支持cuda的版本:

pip install torch==2.0.0+cu117 torchvision==0.15.1+cu117 -f https://download.pytorch.org/whl/cu117/torch_stable.html

其中里面的3处 cu117 需要根据你自己的CUDA版本修改

命令行执行nvidia-smi,命令,看下自己显卡支持的 CUDA版本(比如我这里是11.7)

本地安装部署运行 ChatGLM-6B 的常见问题解答以及后续优化_第1张图片

ChatGLM-6B 减少显存与内存占用

全量模型运行加载 GPU运行模式下需要13GB显存+14G内存,CPU运行模式下需要28GB内存,如果你电脑没这么大显存或者内存,可以通过加载量化模型减少显存与内存占用

ChatGLM-6B 加载量化模型

加载量化模型运行,其实就是用性能的下降换硬件配置需求的下降,量化模型的运行速度会低于全量模型。ChatGLM-6B 在 4-bit 量化下仍然能够进行还算自然流畅的生成。

本地将全量模型转化为量化模型加载

优点嘛,你只需要下载一个全量模型,就可以自己选加载全量,INT4还是INT8
缺点是,量化过程需要在内存中首先加载 FP16 格式的模型,会消耗大概 13GB 的内存。
你电脑要是16GB内存,可能就不能同时运行太多其他程序了

你需要修改web_demo.pyweb_demo.pyold_web_demo2.pycli_demo.pyapi.py等文件中涉及模型部分的代码,一般在文件的开头或者结尾附近。(你需要那个文件就改哪个文件,不需要全改)

#原始代码
tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True)
model = AutoModel.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True).half().cuda()
#INT4量化 显卡GPU加载
tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True)
model = AutoModel.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True).quantize(4).half().cuda()
  • 全量模型多轮对话后,大概需要14~15GB显存,IN8多轮对话后大概需要10GB显存,INT4多轮对话后大概需要6~7G显存
  • 量化过程需要在内存中首先加载 FP16 格式的模型,消耗大概 13GB 的内存。如果你的内存不足的话,可以看下一步直接加载量化后的模型

直接加载量化模型

如果你电脑内存实在捉襟见肘的话,可以选择直接使用现成的INT4量化模型,这样内存中只需要占用5.5GB左右了,使用GPU运行时,8G内存的电脑也可以一战了,使用CPU运行时,可以允许24GB甚至16GB内存的电脑运行,显著降低运行配置。

CPU运行量化模型需要安装GCC与openmp,多数 Linux 发行版默认已安装。对于 Windows ,可在安装 TDM-GCC 时勾选 openmp(你不一定非要用TDM-GCC,用MinGW-w64也可以的,主要是记得装openmp)。

#下载模型放在chatglm-6b-int4文件夹内
git clone -b int4 https://huggingface.co/THUDM/chatglm-6b.git chatglm-6b-int4

然后修改相关代码
你需要修改web_demo.pyweb_demo.pyold_web_demo2.pycli_demo.pyapi.py等文件中涉及模型部分的代码,一般在文件的开头或者结尾附近。(你需要哪个文件就改那个文件,不需要全改)

#原始代码
tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True)
model = AutoModel.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True).half().cuda()
#INT4量化 显卡GPU加载
tokenizer = AutoTokenizer.from_pretrained("chatglm-6b-int4", trust_remote_code=True)
model = AutoModel.from_pretrained("chatglm-6b-int4", trust_remote_code=True).quantize(4).half().cuda()
#INT4量化 处理器CPU加载
tokenizer = AutoTokenizer.from_pretrained("chatglm-6b-int4", trust_remote_code=True)
model = AutoModel.from_pretrained("chatglm-6b-int4",trust_remote_code=True).float()

注意这里我改了"THUDM/chatglm-6b"是因为我上边下载INT4的命令,指定下载到chatglm-6b-int4文件夹中,如果你改动了代码,请根据你自己的实际情况修改对应部分。

ChatGLM-6B 优化多轮对话后的内存/显存占用大,解决爆显存问题

通过修改代码,减少多轮后的返回对话数量,简单粗暴的解决问题,下边以web_demo_old.py为例,其他文件可以参照修改

修改代码,只保留最近3~4轮对话的内容

很简单粗暴的方式,不然有时候,很容易在进行到5~6轮对话就炸显存了

from transformers import AutoModel, AutoTokenizer
import gradio as gr

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

MAX_TURNS = 20
MAX_BOXES = MAX_TURNS * 2


def predict(input, max_length, top_p, temperature, history=None):
    if history is None:
        history = []
    for response, history in model.stream_chat(tokenizer, input, history, max_length=max_length, top_p=top_p,
                                               temperature=temperature):
        updates = []
        for query, response in history:
            updates.append(gr.update(visible=True, value="用户:" + query))
            updates.append(gr.update(visible=True, value="ChatGLM-6B:" + response))
        if len(updates) < MAX_BOXES:
            updates = updates + [gr.Textbox.update(visible=False)] * (MAX_BOXES - len(updates))
        yield [history] + updates

原始代码如上,请把第15行修改为这样的

for response, history in model.stream_chat(tokenizer, input,  max_length=max_length, top_p=top_p, temperature=temperature, history=(history if len(history) <= 3 else history[-3:])):
  • 历史对话轮数小于等于3(<= 3)那么直接返回history的内容
  • 否则返回最后3轮的内容(history[-3:]
  • 如有需要请自己改这两个数字

修改代码,只保留第一条,第二条,以及最近3轮对话

很多本地部署用户都有将 ChatGLM-6B 先设定人设,进而接入vits、智能音响、物联网设备等,而第一句话和第二句话通常用于树立和补充人设,是非常重要的,需要保留。

from transformers import AutoModel, AutoTokenizer
import gradio as gr

tokenizer = AutoTokenizer.from_pretrained("chatglm-6b", trust_remote_code=True)
model = AutoModel.from_pretrained("chatglm-6b", trust_remote_code=True).float()
model = model.eval()

MAX_TURNS = 20
MAX_BOXES = MAX_TURNS * 2

first_ans = ('','')
second_ans = ('','')

def predict(input, max_length, top_p, temperature, history=None):
    global first_ans
    global second_ans
    if history is None:
        history = []
    for response, history in model.stream_chat(tokenizer, input, max_length=max_length, top_p=top_p,
    temperature=temperature, history=(history if len(history) <= 4 else [first_ans] + [second_ans] + history[-3:])):
        if len(history) <= 1 :
            first_ans = history[0]
        if len(history) <= 2 and len(history) > 1 :
            second_ans = history[1]
        updates = []
        for query, response in history:
            updates.append(gr.update(visible=True, value="用户:" + query))
            updates.append(gr.update(visible=True, value="ChatGLM-6B:" + response))
        if len(updates) < MAX_BOXES:
            updates = updates + [gr.Textbox.update(visible=False)] * (MAX_BOXES - len(updates))
        yield [history] + updates
  • 第11和第12行为新增,意思是:搞一个分别名为first_ans与second_ans的全局变量,并把他们置空
  • 第15和第16行为新增,意思是:在函数内部声明first_ans与second_ans这两个全局变量,这样才能在函数predict中对它们进行修改,并且在函数外部也可以使用修改后的值。
  • 第20行为对原始代码第15行的修改,意思是:
    • 如果对话轮数小于等于4轮(<= 4),那么直接返回history的内容
    • 如果对话轮数大于4轮,那么将first_ans储存的第一句对话和second_ans储存的第二轮对话,拼接在最后3轮对话(history[-3:])前返回给history
  • 第21~24行为新增,意思是:判断对话轮数,在正确的轮数,保存对应的对话在全局变量first_ans和second_ans中。

ChatGLM-6B 的推理参数含义

Maximum length 参数

通常用于限制输入序列的最大长度,因为 ChatGLM-6B 是2048长度推理的,一般这个保持默认就行
太大可能会导致性能下降

Top P 参数

Top P 参数是指在生成文本等任务中,选择可能性最高的前P个词的概率累加和。这个参数被称为Top P,也称为Nucleus Sampling。
例如,如果将Top P参数设置为0.7,那么模型会选择可能性排名超过70%的词进行采样。这样可以保证生成的文本准确性较高,但可能会缺乏多样性。相反,如果将Top P参数设置为0.3,则会选择可能性超过30%的词进行采样,这可能会导致生成文本的准确性下降,但能够更好地增加多样性。

Temperature 参数

Temperature参数通常用于调整softmax函数的输出,用于增加或减少模型对不同类别的置信度。
具体来说,softmax函数将模型对每个类别的预测转换为概率分布。Temperature参数可以看作是一个缩放因子,它可以增加或减少softmax函数输出中每个类别的置信度。
比如将 Temperature 设置为 0.05 和 0.95 的主要区别在于,T=0.05 会使得模型更加自信,更加倾向于选择概率最大的类别作为输出,而 T=0.95 会使得模型更加不确定,更加倾向于输出多个类别的概率值较大。

ChatGLM-6B 其他设置

ChatGLM-6B 开启网页远程分享

可以让个人电脑部署的网页端,在互联网上也可以远程访问。

你需要修改web_demo.pyweb_demo.pyold_web_demo2.py等文件中涉及网络部分的代码,一般在文件结尾附近。(你需要哪个文件就改那个文件,不需要全改)

一般类似于demo.queue().launch(share=False, inbrowser=True)
share=False改成share=True
会得到一个 Gradio 服务器转发地址,就可以远程访问了,注意由于国内 Gradio 的网络访问较为缓慢,导致网页端的打字机体验大幅下降

ChatGLM-6B 开启局域网访问/开启公网访问

如果你的机器本身有公网,或者想在局域网内其他设备访问

你需要修改web_demo.pyweb_demo.pyold_web_demo2.py等文件中涉及网络部分的代码,一般在文件结尾附近。(你需要哪个文件就改那个文件,不需要全改)

一般类似于demo.queue().launch(share=False, inbrowser=True)
修改为demo.queue().launch(share=False, inbrowser=True, server_name='0.0.0.0', server_port=8070)
这将在所有可用的网络端口开放8070端口号,并且不启用Gradio 服务器转发。

ChatGLM-6B 修改本地网页端口号

因为默认是127.0.0.1:7860嘛,可能会和其他本地程序冲突

你需要修改web_demo.pyweb_demo.pyold_web_demo2.py等文件中涉及网络部分的代码,一般在文件结尾附近。(你需要哪个文件就改那个文件,不需要全改)

一般类似于demo.queue().launch(share=False, inbrowser=True)
修改为demo.queue().launch(share=False, inbrowser=True, port=7870)
这样端口就变成7870了。

后续调试

  • 减少爆显存问题
  • 解决对话超过10~20轮后开始大量复读的问题
  • 研究微调模型,自行输入数据集,训练情感支持对话机器人?

你可能感兴趣的:(ChatGLM,GPT,python,开发语言)