Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] glm-4-9b-chat模型运行问题 #4141

Closed
gz-simon opened this issue Jun 6, 2024 · 33 comments
Closed

[BUG] glm-4-9b-chat模型运行问题 #4141

gz-simon opened this issue Jun 6, 2024 · 33 comments
Labels
bug Something isn't working

Comments

@gz-simon
Copy link

gz-simon commented Jun 6, 2024

运行glm-4-9b-chat模型时,输出结果一直在自问自答,听不下来
Uploading 微信截图_20240606145256.png…

@gz-simon gz-simon added the bug Something isn't working label Jun 6, 2024
@gz-simon gz-simon changed the title [BUG] 简洁阐述问题 / Concise description of the issue [BUG] glm-4-9b-chat模型运行问题 Jun 6, 2024
@zmalqp189
Copy link

这个是fastchat尚未兼容glm4导致的,这个库在将用户问题输入给大模型时使用了错误的prompt和问答格式。
目前我自己的解决方案是需要修改:
1、 \fastchat\model\model_chatglm.py
2、 \fastchat\model\model_adapter.py
这两个文件。我已经跑通,效果如下:
1717746272980

@lzt9746
Copy link

lzt9746 commented Jun 7, 2024

model_config.py 里面配置的glm-4路径不要有chatglm字样,不然fastchat会用chatglm的promt格式,导致回答停不下来。

"llm_model": {
    "glm-4-9b-chat": "/home/model/glm-4-9b-chat/",
}

@chenyu-l
Copy link

chenyu-l commented Jun 9, 2024

这个是fastchat尚未兼容glm4导致的,这个库在将用户问题输入给大模型时使用了错误的prompt和问答格式。 目前我自己的解决方案是需要修改: 1、 \fastchat\model\model_chatglm.py 2、 \fastchat\model\model_adapter.py 这两个文件。我已经跑通,效果如下: 1717746272980

请问具体该如何修改呢?能麻烦给一下示例嘛,非常感谢

@chenyu-l
Copy link

chenyu-l commented Jun 9, 2024

model_config.py 里面配置的glm-4路径不要有chatglm字样,不然fastchat会用chatglm的promt格式,导致回答停不下来。

"llm_model": {
    "glm-4-9b-chat": "/home/model/glm-4-9b-chat/",
}

按照这种方法试了一下,还是会一直输出英文。。

@ImmNaruto
Copy link

@zmalqp189 您好请问下具体是怎么修改的呢 我尝试改了一下template模版不生效

@ZamboLin
Copy link

你们是怎么在这个框架下运行pydantic 2.0版本的,,???不是不支持吗

@zmalqp189
Copy link

这个是fastchat尚未兼容glm4导致的,这个库在将用户问题输入给大模型时使用了错误的prompt和问答格式。 目前我自己的解决方案是需要修改: 1、 \fastchat\model\model_chatglm.py 2、 \fastchat\model\model_adapter.py 这两个文件。我已经跑通,效果如下: 1717746272980

请问具体该如何修改呢?能麻烦给一下示例嘛,非常感谢

1、在\fastchat\model\model_chatglm.py文件的91行添加一个判断:
elif "glm-4" in model_type:
message_list = recover_message_list(prompt)
inputs = tokenizer.apply_chat_template(
message_list, tokenize=True, return_tensors="pt", return_dict=True, add_generation_prompt=True
).to(model.device)
2、在\fastchat\model\model_adapter.py文件的835行判断条件改为:
if "chatglm3" or 'glm-4' in model_path.lower():
3、把权重的文件夹名更改成:chatglm-4-9b-chat

以上只是我个人临时写的解决方案,一定不是最优解,有更好的方案可以解决或者是等官方更新支持glm4。

@zmalqp189
Copy link

@zmalqp189 您好请问下具体是怎么修改的呢 我尝试改了一下template模版不生效

1、在\fastchat\model\model_chatglm.py文件的91行添加一个判断:
elif "glm-4" in model_type:
message_list = recover_message_list(prompt)
inputs = tokenizer.apply_chat_template(
message_list, tokenize=True, return_tensors="pt", return_dict=True, add_generation_prompt=True
).to(model.device)
2、在\fastchat\model\model_adapter.py文件的835行判断条件改为:
if "chatglm3" or 'glm-4' in model_path.lower():
3、把权重的文件夹名更改成:chatglm-4-9b-chat

以上只是我个人临时写的解决方案,一定不是最优解,有更好的方案可以解决或者是等官方更新支持glm4。

@ImmNaruto
Copy link

@zmalqp189 非常感谢!

@vincentyan2023
Copy link

@zmalqp189 您好请问下具体是怎么修改的呢 我尝试改了一下template模版不生效

1、在\fastchat\model\model_chatglm.py文件的91行添加一个判断: elif "glm-4" in model_type: message_list = recover_message_list(prompt) inputs = tokenizer.apply_chat_template( message_list, tokenize=True, return_tensors="pt", return_dict=True, add_generation_prompt=True ).to(model.device) 2、在\fastchat\model\model_adapter.py文件的835行判断条件改为: if "chatglm3" or 'glm-4' in model_path.lower(): 3、把权重的文件夹名更改成:chatglm-4-9b-chat

以上只是我个人临时写的解决方案,一定不是最优解,有更好的方案可以解决或者是等官方更新支持glm4。

参考你这个建议改了,不会出英文了,但还是自问自答,不知是否还有别的地方还需要修改?

@litterGuy
Copy link

@zmalqp189 您好请问下具体是怎么修改的呢 我尝试改了一下template模版不生效

1、在\fastchat\model\model_chatglm.py文件的91行添加一个判断: elif "glm-4" in model_type: message_list = recover_message_list(prompt) inputs = tokenizer.apply_chat_template( message_list, tokenize=True, return_tensors="pt", return_dict=True, add_generation_prompt=True ).to(model.device) 2、在\fastchat\model\model_adapter.py文件的835行判断条件改为: if "chatglm3" or 'glm-4' in model_path.lower(): 3、把权重的文件夹名更改成:chatglm-4-9b-chat

以上只是我个人临时写的解决方案,一定不是最优解,有更好的方案可以解决或者是等官方更新支持glm4。

这样修改后,推理倒是不会出现停不下来的情况了。但是问答存在问题、回答不会依据询问内容、模型自行乱答一个长文本答案。
image

@web3aipro
Copy link

web3aipro commented Jun 12, 2024

不会出现上文这样的,但每个回复末尾有个,这个问题,我觉得是我们提示词模板没设置对,glm-4默认提示词模板跟chat3-glm可能不一样,我根据@zmalqp189 改的,但还需要改fastchat最外面conversation.py,我调试下要是能解决末尾这个符号,我会提交到到fastchat
image

@litterGuy
Copy link

litterGuy commented Jun 12, 2024

自行回答的原因找到了,是

message_list = recover_message_list(prompt)


def recover_message_list(prompt):
    role_token_pattern = "|".join(
        [re.escape(r) for r in ["<|system|>", "<|user|>", "<|assistant|>"]]
    )
    role = None
    last_end_idx = -1
    message_list = []
    for match in re.finditer(role_token_pattern, prompt):
        if role:
            messge = {}
            if role == "<|system|>":
                messge["role"] = "system"
            elif role == "<|user|>":
                messge["role"] = "user"
            else:
                messge["role"] = "assistant"
            messge["content"] = prompt[last_end_idx + 1: match.start()]
            message_list.append(messge)

        role = prompt[match.start(): match.end()]
        last_end_idx = match.end()

    return message_list

这里包装message时,因为使用的还是glm3的处理方式,所以query直接发送为空导致的自问自答。


这个格式如何修改暂时直接写死了做测试

message_list.append({"role": "user", "content": prompt})

@web3aipro 结尾出现<|user|>结束符,参照glm4官方给的文档,在decode时添加一个skip_special_tokens=True

for total_ids in model.stream_generate(**inputs, **gen_kwargs):
        total_ids = total_ids.tolist()[0]
        total_len = len(total_ids)
        if echo:
            output_ids = total_ids
        else:
            output_ids = total_ids[input_echo_len:]
        response = tokenizer.decode(output_ids, skip_special_tokens=True)
        response = process_response(response)

@web3aipro
Copy link

自行回答的原因找到了,是

message_list = recover_message_list(prompt)


def recover_message_list(prompt):
    role_token_pattern = "|".join(
        [re.escape(r) for r in ["<|system|>", "<|user|>", "<|assistant|>"]]
    )
    role = None
    last_end_idx = -1
    message_list = []
    for match in re.finditer(role_token_pattern, prompt):
        if role:
            messge = {}
            if role == "<|system|>":
                messge["role"] = "system"
            elif role == "<|user|>":
                messge["role"] = "user"
            else:
                messge["role"] = "assistant"
            messge["content"] = prompt[last_end_idx + 1: match.start()]
            message_list.append(messge)

        role = prompt[match.start(): match.end()]
        last_end_idx = match.end()

    return message_list

这里包装message时,因为使用的还是glm3的处理方式,所以query直接发送为空导致的自问自答。

这个格式如何修改暂时直接写死了做测试

message_list.append({"role": "user", "content": prompt})

@web3aipro 结尾出现<|user|>结束符,参照glm4官方给的文档,在decode时添加一个skip_special_tokens=True

for total_ids in model.stream_generate(**inputs, **gen_kwargs):
        total_ids = total_ids.tolist()[0]
        total_len = len(total_ids)
        if echo:
            output_ids = total_ids
        else:
            output_ids = total_ids[input_echo_len:]
        response = tokenizer.decode(output_ids, skip_special_tokens=True)
        response = process_response(response)

@litterGuy 厉害确实解决了!

@web3aipro
Copy link

web3aipro commented Jun 12, 2024

我这里没有写死,也没有出现自问自答,参照chatglm3改的
image
需要对fastchat的三个文件做如下修改:
1、需要修改model_chatglm.py,如下:

def generate_stream_chatglm(
    model,
    tokenizer,
    params,
    device,
    context_len=2048,
    stream_interval=2,
    judge_sent_end=False,
):
    prompt = params["prompt"]
    temperature = float(params.get("temperature", 1.0))
    repetition_penalty = float(params.get("repetition_penalty", 1.0))
    top_p = float(params.get("top_p", 1.0))
    max_new_tokens = int(params.get("max_new_tokens", 256))
    echo = params.get("echo", True)

    model_type = str(type(model)).lower()
    if "peft" in model_type:
        model_type = str(type(model.base_model.model)).lower()

    if "chatglm3" in model_type:
        message_list = recover_message_list(prompt)
        inputs = tokenizer.build_chat_input(
            query=message_list[-1]["content"], history=message_list[:-1], role="user"
        ).to(model.device)
    elif "chatglm4" in model_type:
        message_list = recover_message_list(prompt)
        inputs = tokenizer.apply_chat_template(
            message_list, tokenize=True, return_tensors="pt", return_dict=True, add_generation_prompt=True
        ).to(model.device)
    else:
        inputs = tokenizer([prompt], return_tensors="pt").to(model.device)
    input_echo_len = len(inputs["input_ids"][0])

    gen_kwargs = {
        "max_length": max_new_tokens + input_echo_len,
        "do_sample": True if temperature > 1e-5 else False,
        "top_p": top_p,
        "repetition_penalty": repetition_penalty,
        "logits_processor": [invalid_score_processor],
    }
    if temperature > 1e-5:
        gen_kwargs["temperature"] = temperature

    total_len = 0
    for total_ids in model.stream_generate(**inputs, **gen_kwargs):
        total_ids = total_ids.tolist()[0]
        total_len = len(total_ids)
        if echo:
            output_ids = total_ids
        else:
            output_ids = total_ids[input_echo_len:]
        response = tokenizer.decode(output_ids,skip_special_tokens=True)
        response = process_response(response)
        print(response)
        yield {
            "text": response,
            "usage": {
                "prompt_tokens": input_echo_len,
                "completion_tokens": total_len - input_echo_len,
                "total_tokens": total_len,
            },
            "finish_reason": None,
        }

    # TODO: ChatGLM stop when it reach max length
    # Only last stream result contains finish_reason, we set finish_reason as stop
    ret = {
        "text": response,
        "usage": {
            "prompt_tokens": input_echo_len,
            "completion_tokens": total_len - input_echo_len,
            "total_tokens": total_len,
        },
        "finish_reason": "stop",
    }
    yield ret

2、对model_adapter.py,做如下修改:



if "chatglm4" in model_path.lower():
		tokenizer = AutoTokenizer.from_pretrained(
			model_path,
			encode_special_tokens=True,
			trust_remote_code=True,
			revision=revision,
		)

    def get_default_conv_template(self, model_path: str) -> Conversation:
        model_path = model_path.lower()
        if "chatglm2" in model_path.lower():
            return get_conv_template("chatglm2")
        if "chatglm3" in model_path.lower():
            return get_conv_template("chatglm3")
        if "chatglm4" in model_path.lower():
            return get_conv_template("chatglm4")
        return get_conv_template("chatglm")

3、需要对conversation.py,做如下修改:

class SeparatorStyle(IntEnum):
    """Separator styles."""

    ADD_COLON_SINGLE = auto()
    ADD_COLON_TWO = auto()
    ADD_COLON_SPACE_SINGLE = auto()
    NO_COLON_SINGLE = auto()
    NO_COLON_TWO = auto()
    ADD_NEW_LINE_SINGLE = auto()
    LLAMA2 = auto()
    LLAMA3 = auto()
    CHATGLM = auto()
    CHATML = auto()
    CHATINTERN = auto()
    DOLLY = auto()
    RWKV = auto()
    PHOENIX = auto()
    ROBIN = auto()
    FALCON_CHAT = auto()
    CHATGLM3 = auto()
    CHATGLM4 = auto()
    DEEPSEEK_CHAT = auto()
    METAMATH = auto()
    YUAN2 = auto()
    GEMMA = auto()
    CLLM = auto()
    DEFAULT = auto()
	elif self.sep_style == SeparatorStyle.CHATGLM4:
		ret = ""
		if self.system_message:
			ret += system_prompt
		for role, message in self.messages:
			if message:
				ret += role + "\n" + message
			else:
				ret += role
		return ret
register_conv_template(
    Conversation(
        name="chatglm4",
        system_template="<|system|>\n{system_message}",
        roles=("<|user|>", "<|assistant|>"),
        sep_style=SeparatorStyle.CHATGLM4,
         stop_token_ids=[
             64795,
             64797,
            2,
        ],
    )
)

4、将权重文件名称替换为,glm-4-chat--->chatglm4-9b-chat

@vincentyan2023
Copy link

model_path

这个模型类型的判断有点奇葩了,前面按模型类型判断,后面按文件夹名判断。。。

@Panda-Panda-new
Copy link

自行回答的原因找到了,是

message_list = recover_message_list(prompt)


def recover_message_list(prompt):
    role_token_pattern = "|".join(
        [re.escape(r) for r in ["<|system|>", "<|user|>", "<|assistant|>"]]
    )
    role = None
    last_end_idx = -1
    message_list = []
    for match in re.finditer(role_token_pattern, prompt):
        if role:
            messge = {}
            if role == "<|system|>":
                messge["role"] = "system"
            elif role == "<|user|>":
                messge["role"] = "user"
            else:
                messge["role"] = "assistant"
            messge["content"] = prompt[last_end_idx + 1: match.start()]
            message_list.append(messge)

        role = prompt[match.start(): match.end()]
        last_end_idx = match.end()

    return message_list

这里包装message时,因为使用的还是glm3的处理方式,所以query直接发送为空导致的自问自答。
这个格式如何修改暂时直接写死了做测试

message_list.append({"role": "user", "content": prompt})

@web3aipro 结尾出现<|user|>结束符,参照glm4官方给的文档,在decode时添加一个skip_special_tokens=True

for total_ids in model.stream_generate(**inputs, **gen_kwargs):
        total_ids = total_ids.tolist()[0]
        total_len = len(total_ids)
        if echo:
            output_ids = total_ids
        else:
            output_ids = total_ids[input_echo_len:]
        response = tokenizer.decode(output_ids, skip_special_tokens=True)
        response = process_response(response)

@litterGuy 厉害确实解决了!

这段代码在哪里放着呢,请教一下

@Panda-Panda-new
Copy link

我这里没有写死,也没有出现自问自答,参照chatglm3改的 image 需要对fastchat的三个文件做如下修改: 1、需要修改model_chatglm.py,如下:

def generate_stream_chatglm(
    model,
    tokenizer,
    params,
    device,
    context_len=2048,
    stream_interval=2,
    judge_sent_end=False,
):
    prompt = params["prompt"]
    temperature = float(params.get("temperature", 1.0))
    repetition_penalty = float(params.get("repetition_penalty", 1.0))
    top_p = float(params.get("top_p", 1.0))
    max_new_tokens = int(params.get("max_new_tokens", 256))
    echo = params.get("echo", True)

    model_type = str(type(model)).lower()
    if "peft" in model_type:
        model_type = str(type(model.base_model.model)).lower()

    if "chatglm3" in model_type:
        message_list = recover_message_list(prompt)
        inputs = tokenizer.build_chat_input(
            query=message_list[-1]["content"], history=message_list[:-1], role="user"
        ).to(model.device)
    elif "chatglm4" in model_type:
        message_list = recover_message_list(prompt)
        inputs = tokenizer.apply_chat_template(
            message_list, tokenize=True, return_tensors="pt", return_dict=True, add_generation_prompt=True
        ).to(model.device)
    else:
        inputs = tokenizer([prompt], return_tensors="pt").to(model.device)
    input_echo_len = len(inputs["input_ids"][0])

    gen_kwargs = {
        "max_length": max_new_tokens + input_echo_len,
        "do_sample": True if temperature > 1e-5 else False,
        "top_p": top_p,
        "repetition_penalty": repetition_penalty,
        "logits_processor": [invalid_score_processor],
    }
    if temperature > 1e-5:
        gen_kwargs["temperature"] = temperature

    total_len = 0
    for total_ids in model.stream_generate(**inputs, **gen_kwargs):
        total_ids = total_ids.tolist()[0]
        total_len = len(total_ids)
        if echo:
            output_ids = total_ids
        else:
            output_ids = total_ids[input_echo_len:]
        response = tokenizer.decode(output_ids,skip_special_tokens=True)
        response = process_response(response)
        print(response)
        yield {
            "text": response,
            "usage": {
                "prompt_tokens": input_echo_len,
                "completion_tokens": total_len - input_echo_len,
                "total_tokens": total_len,
            },
            "finish_reason": None,
        }

    # TODO: ChatGLM stop when it reach max length
    # Only last stream result contains finish_reason, we set finish_reason as stop
    ret = {
        "text": response,
        "usage": {
            "prompt_tokens": input_echo_len,
            "completion_tokens": total_len - input_echo_len,
            "total_tokens": total_len,
        },
        "finish_reason": "stop",
    }
    yield ret

2、对model_adapter.py,做如下修改:



if "chatglm4" in model_path.lower():
		tokenizer = AutoTokenizer.from_pretrained(
			model_path,
			encode_special_tokens=True,
			trust_remote_code=True,
			revision=revision,
		)
    def get_default_conv_template(self, model_path: str) -> Conversation:
        model_path = model_path.lower()
        if "chatglm2" in model_path.lower():
            return get_conv_template("chatglm2")
        if "chatglm3" in model_path.lower():
            return get_conv_template("chatglm3")
        if "chatglm4" in model_path.lower():
            return get_conv_template("chatglm4")
        return get_conv_template("chatglm")

3、需要对conversation.py,做如下修改:

class SeparatorStyle(IntEnum):
    """Separator styles."""

    ADD_COLON_SINGLE = auto()
    ADD_COLON_TWO = auto()
    ADD_COLON_SPACE_SINGLE = auto()
    NO_COLON_SINGLE = auto()
    NO_COLON_TWO = auto()
    ADD_NEW_LINE_SINGLE = auto()
    LLAMA2 = auto()
    LLAMA3 = auto()
    CHATGLM = auto()
    CHATML = auto()
    CHATINTERN = auto()
    DOLLY = auto()
    RWKV = auto()
    PHOENIX = auto()
    ROBIN = auto()
    FALCON_CHAT = auto()
    CHATGLM3 = auto()
    CHATGLM4 = auto()
    DEEPSEEK_CHAT = auto()
    METAMATH = auto()
    YUAN2 = auto()
    GEMMA = auto()
    CLLM = auto()
    DEFAULT = auto()
	elif self.sep_style == SeparatorStyle.CHATGLM4:
		ret = ""
		if self.system_message:
			ret += system_prompt
		for role, message in self.messages:
			if message:
				ret += role + "\n" + message
			else:
				ret += role
		return ret
register_conv_template(
    Conversation(
        name="chatglm4",
        system_template="<|system|>\n{system_message}",
        roles=("<|user|>", "<|assistant|>"),
        sep_style=SeparatorStyle.CHATGLM4,
         stop_token_ids=[
             64795,
             64797,
            2,
        ],
    )
)

4、将权重文件名称替换为,glm-4-chat--->chatglm4-9b-chat

我按照您的方式修改了,但还是会出现回答重复的问题。

@315930399
Copy link

发现一个简单的方法,模型的保存路径不要出现'chatglm'这几个字就可以

@Panda-Panda-new
Copy link

好像不太行

@Panda-Panda-new
Copy link

是我没写对嘛

@315930399
Copy link

是我没写对嘛

好像确实不行,你尝试一下用这个分支从源码安装吧https://github.com/litterGuy/FastChat/tree/main

@Panda-Panda-new
Copy link

好的,我试一下,我把前面大家提到的方法都试了也都没有解决

@Panda-Panda-new
Copy link

您调通没

@315930399
Copy link

315930399 commented Jun 18, 2024

您调通没

好吧,这个分支也没用,还是停不下来。。。我看还有是通过改客户端请求的
vllm-project/vllm#5306 (comment)

@Panda-Panda-new
Copy link

还没呢

@Panda-Panda-new
Copy link

用官方给的框架就可以用,客户端请求?

@wll0307
Copy link

wll0307 commented Jun 19, 2024

现在改好了但是如果想要glm4-9b-chat回答一次对话带角色扮演的还是只能等

@Panda-Panda-new
Copy link

现在改好了但是如果想要glm4-9b-chat回答一次对话带角色扮演的还是只能等

哥们,你是咋改好的

@wll0307
Copy link

wll0307 commented Jun 20, 2024

现在改好了但是如果想要glm4-9b-chat回答一次对话带角色扮演的还是只能等

哥们,你是咋好的
panda4444ssdfs
1、在\fastchat\model\model_chatglm.py 文件的第91行添加一个判断条件:
elif "glm-4" in model_type:
message_list = recovery_message_list(prompt)
input = tokenizer.apply_chat_template(
message_list, tokenize=True, return_tensors="pt", return_dict=True, add_generation_prompt=True
).to(model.device)
2、在\fastchat\model\model_adapter.py 文件的第835行判断条件修改:
if "chatglm3" or 'glm-4' in model_path.lower():
3、把身高的文件夹名更改为:chatglm-4-9b-chat

只是我个人临时写的解决方案,一定不是最理想的解决方案,或者有更好的方案可以解决或更新支持glm4。
我是把他们发的都尝试了一遍

@quantbruce
Copy link

mark

@liunux4odoo
Copy link
Collaborator

最好的办法是找一个支持 glm4 能提供 openai 兼容 API 的框架,比如 xinference/ollama,然后按照 openai 的格式把模型配置到chatchat里。

@danny-zhu
Copy link

danny-zhu commented Aug 9, 2024

修改方法:

  1. 拉取fastchat 0.2.35
  2. 按照web3aipro提供的方法修改;
  3. 修改后的fastchat安装到本地python虚拟环境中 pip install -e . 可以解决chatglm4-9b循环回答的问题。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests