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

glm4-9b-chat和LoRA微调模型merge之后,使用vLLM推理,工具调用功能报错。 #607

Open
2 tasks done
Jimmy-L99 opened this issue Oct 24, 2024 · 12 comments
Open
2 tasks done
Assignees

Comments

@Jimmy-L99
Copy link

Jimmy-L99 commented Oct 24, 2024

System Info / 系統信息

  • vllm 0.5.3
  • transformer 4.44.0
  • torch 2.3.1

Who can help? / 谁可以帮助到您?

@sixsixcoder @zr

Information / 问题信息

  • The official example scripts / 官方的示例脚本
  • My own modified scripts / 我自己修改的脚本和任务

Reproduction / 复现过程

1. LoRA微调

使用LLaMA-Factory,自定义数据集、yaml文件,llamafactory-cli train进行LoRA微调。

2.glm4-9b-chat和LoRA进行merge

使用LLaMA-Factory,llamafactory-cli export 得到merge模型。

3.工具调用测试代码

使用官方提供的openai_api_server.py, vLLM推理。

工具测试部分代码:

tools = {
    "weather": weather,
}

# 绑定工具
llm_with_tools = llm.bind_tools(list(tools.values()))

context = []
def process_query(query):
    
    global context
    # 将用户的查询添加到上下文中
    context.append({"role": "user", "content": query})
    
    # 调用 LLM
    response = llm_with_tools.invoke(context)
    print(response)
    
    if response.tool_calls:
        # 如果有工具调用,则执行工具调用
        tool_call = response.tool_calls[0]
        tool_name = tool_call["name"]
        tool = tools[tool_name]
        
        # 获取工具调用的参数并解包传递给工具函数
        tool_arguments = tool_call["args"]
        tool_result = tool(**tool_arguments) 
        
        # 将工具结果添加到上下文
        context.append({"role": "system", "content": f"你可以通过工具得到实时的天气信息,工具得到的结果是:\n\n{tool_result}\n\n,这个结果绝对准确,你可以直接使用该结果进行表述。"})
        
        # 工具调用后的上下文继续传递给 LLM,以生成最终响应
        response = llm.invoke(context)

    # 将 LLM 的响应添加到上下文中
    context.append({"role": "assistant", "content": response.content})
    
    return response.content

#测试
query_1 = "今天深圳的天气怎么样"
response_1 = process_query(query_1)
print(response_1)

4.模型测试

测试如下,用base和两种不同的lora调用方式测试

glm4-9b-chat模型

merge模型

glm4-9b-chat模型+lora_request参数

  • glm4-9b-chat模型
    输出结果:
content='' additional_kwargs={'tool_calls': [{'id': 'call_M7oa0Lip6JIV3yZ17D3ZDSiU', 'function': {'arguments': '{"city": "深圳"}', 'name': 'weather'}, 'type': 'function', 'index': 0}], 'refusal': None} response_metadata={'token_usage': {'completion_tokens': 9, 'prompt_tokens': 402, 'total_tokens': 411}, 'model_name': 'glm4-9b-chat', 'system_fingerprint': 'fp_jg9prlr6x', 'finish_reason': 'tool_calls', 'logprobs': None} id='run-14ea30ff-3a50-4996-931b-c38f19ee04bc-0' tool_calls=[{'name': 'weather', 'args': {'city': '深圳'}, 'id': 'call_M7oa0Lip6JIV3yZ17D3ZDSiU', 'type': 'tool_call'}] usage_metadata={'input_tokens': 402, 'output_tokens': 9, 'total_tokens': 411}


----------
tools result: 

深圳目前时刻的天气是多云,
温度为26.0℃,
湿度为38.0%,
风向为东北,
风力为≤3级
----------

LLM_response: 今天深圳的天气是多云,气温约为26摄氏度,相对湿度为38%,风向为东北风,风力较弱,风速不超过3级。
  • LoRA merge模型
输出结果:
INFO:     172.16.21.155:36244 - "POST /v1/chat/completions HTTP/1.1" 500 Internal Server Error
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/uvicorn/protocols/http/httptools_impl.py", line 401, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__
    return await self.app(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/fastapi/applications.py", line 1054, in __call__
    await super().__call__(scope, receive, send)
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/starlette/applications.py", line 113, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/starlette/middleware/errors.py", line 187, in __call__
    raise exc
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/starlette/middleware/errors.py", line 165, in __call__
    await self.app(scope, receive, _send)
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/starlette/middleware/cors.py", line 85, in __call__
    await self.app(scope, receive, send)
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/starlette/_exception_handler.py", line 62, in wrapped_app
    raise exc
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/starlette/_exception_handler.py", line 51, in wrapped_app
    await app(scope, receive, sender)
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/starlette/routing.py", line 715, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/starlette/routing.py", line 735, in app
    await route.handle(scope, receive, send)
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/starlette/routing.py", line 288, in handle
    await self.app(scope, receive, send)
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/starlette/routing.py", line 76, in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/starlette/_exception_handler.py", line 62, in wrapped_app
    raise exc
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/starlette/_exception_handler.py", line 51, in wrapped_app
    await app(scope, receive, sender)
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/starlette/routing.py", line 73, in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/fastapi/routing.py", line 301, in app
    raw_response = await run_endpoint_function(
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/fastapi/routing.py", line 212, in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/ljm/ChatGLM4/GLM-4/api_server/openai_api_server.py", line 389, in create_chat_completion
    async for response in generate_stream_glm4(gen_params):
  File "/root/ljm/ChatGLM4/GLM-4/api_server/openai_api_server.py", line 205, in generate_stream_glm4
    inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, tokenize=False)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/transformers/tokenization_utils_base.py", line 1844, in apply_chat_template
    rendered_chat = compiled_template.render(
                    ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/jinja2/environment.py", line 1304, in render
    self.environment.handle_exception()
  File "/root/anaconda3/envs/glm4_9b-chat-128k_vLLM/lib/python3.11/site-packages/jinja2/environment.py", line 939, in handle_exception
    raise rewrite_traceback_stack(source=source)
  File "<template>", line 1, in top-level template code
TypeError: can only concatenate str (not "NoneType") to str
  • glm4-9b-chat模型+lora_request参数
    结果同glm4-9b-chat模型,可正常调用工具。

Expected behavior / 期待表现

此外,merge模型不调用工具的话,简单的只是llm.chat.completions.create(..., messages)或者llm.invoke()是可以正常回答的,但涉及到工具调用会出现如上报错。

这是什么原因,还请劳烦大佬解答一下。

@243006306
Copy link

我这边试验的结果也是merge后异常

@Andy1018
Copy link

Andy1018 commented Nov 7, 2024

解决了吗?遇到同样的问题

@zRzRzRzRzRzRzR
Copy link
Member

zRzRzRzRzRzRzR commented Nov 7, 2024

想知道你们是怎么merge的,template还在吗?想查看一下tokenizer_config.json中的chat_template是否发生变化

@zRzRzRzRzRzRzR zRzRzRzRzRzRzR self-assigned this Nov 7, 2024
@Andy1018
Copy link

Andy1018 commented Nov 7, 2024

想知道你们是怎么merge的,template还在吗?想查看一下tokenizer_config.json中的chat_template是否发生变化

我是通过LLaMA-Factory工具微调的,自带导出模型的功能。

@Jimmy-L99
Copy link
Author

Jimmy-L99 commented Nov 7, 2024

@zRzRzRzRzRzRzR 我是直接按llama-factory的命令运行 llamafactory export xxxx.yaml
yaml文件如下:

### model
model_name_or_path: models/glm-4-9b-chat
adapter_name_or_path: finetune_model/lora_finetune
template: glm4
finetuning_type: lora

### export
export_dir: finetune_model/lora_finetune_merge
export_size: 2
export_device: cpu
export_legacy_format: false

chat_template我检查了一下:
原本的:

`"chat_template": "[gMASK]<sop>{% for item in messages %}{% if item['tools'] is defined %}<|system|>\n你是一个名为 GLM-4 的人工智能助手。你是基于智谱AI训练的语言模型 GLM-4 模型开发的,你的任务是针对用户的问题和要求提供适当的答复和支持。\n\n# 可用工具{% set tools = item['tools'] %}{% for tool in tools %}{% if tool['type'] == 'function' %}\n\n## {{ tool['function']['name'] }}\n\n{{ tool['function'] | tojson(indent=4) }}\n在调用上述函数时,请使用 Json 格式表示调用的参数。{% elif tool['type'] == 'python' %}\n\n## python\n\n当你向 `python` 发送包含 Python 代码的消息时,该代码将会在一个有状态的 Jupyter notebook 环境中执行。\n`python` 返回代码执行的输出,或在执行 60 秒后返回超时。\n`/mnt/data` 将会持久化存储你的文件。在此会话中,`python` 无法访问互联网。不要使用 `python` 进行任何网络请求或者在线 API 调用,这些在线内容的访问将不会成功。{% elif tool['type'] == 'simple_browser' %}\n\n## simple_browser\n\n你可以使用 `simple_browser` 工具。该工具支持以下函数:\n`search(query: str, recency_days: int)`:使用搜索引擎进行查询并显示结果,可以使用 `recency_days` 参数控制搜索内容的时效性。\n`mclick(ids: list[int])`:获取一系列指定 id 的页面内容。每次调用时,须选择3-10个页面。选择多个角度的页面,同时尽可能选择可信任的信息来源。考虑到部分页面是无法加载的,你也可以多打开一些可能有用的页面而不用担心内容过多。\n`open_url(url: str)`:打开指定的 URL。\n\n使用 `【{引用 id}†{引用文本}】` 来引用内容。\n\n操作步骤:1. 使用 `search` 来获得信息列表; 2. 使用 `mclick` 来获取指定 ID 页面的内容; 3. 根据获得的内容进行回复。在回复中应当引用信息来源。\n 如果用户提供了 URL,也可以用 `open_url` 直接打开页面。\n如果初次搜索结果没有找到合适的信息,也可以再次使用 `search` 进行搜索。{% elif tool['type'] == 'cogview' %}\n\n## cogview\n\n如果用户的请求中包含了对图像的描述,你可以使用 `cogview` 来生成图像并展示给用户。你需要向 `cogview` 发送图像描述,规则:\n- 发送给 `cogview` 的消息必须使用英语。用户的中文描述必须完全翻译为英语。\n- 应当尽可能详细地描述图像生成的需求,需求描述约 100 英文单词。\n- 保持用户原始描述的意图。不要虚构内容或者没见过的人物。\n- 如无特殊说明,所在地为中国,持有中国立场并遵循中国社会主义价值观。{% endif %}{% endfor %}{% endif %}{% if item['content'] %}<|{{ item['role'] }}|>{{ item['metadata'] }}\n{{ item['content'] }}{% endif %}{% endfor %}{% if add_generation_prompt %}<|assistant|>{% endif %}",`

merge之后:

"chat_template": "{{ '[gMASK]<sop>' }}{% if messages[0]['role'] == 'system' %}{% set loop_messages = messages[1:] %}{% set system_message = messages[0]['content'] %}{% else %}{% set loop_messages = messages %}{% endif %}{% if system_message is defined %}{{ '<|system|>\n' + system_message }}{% endif %}{% for message in loop_messages %}{% set content = message['content'] %}{% if message['role'] == 'user' %}{{ '<|user|>\n' + content + '<|assistant|>' }}{% elif message['role'] == 'assistant' %}{{ '\n' + content }}{% endif %}{% endfor %}",

这块我不太了解了,但确实有变化,会不会是这个的原因。

@243006306
Copy link

我是通过llama-factory的webui直接导出的,template选的是glm4

@sixsixcoder
Copy link
Collaborator

sixsixcoder commented Nov 7, 2024

能否提供微调数据集和工具测试的完整代码以供测试

@Jimmy-L99
Copy link
Author

能否提供微调数据集和工具测试的完整代码以供测试

貌似没法提供,毕竟都是按照llama-factory教程部署和操作的。数据集应该随便一个都行。

微调命令:

llamafactory-cli train xxx.yaml
### train.yaml:
### model
model_name_or_path: /root/ljm/models/glm-4-9b-chat

### method
stage: sft
do_train: true
finetuning_type: lora
lora_target: query_key_value
lora_rank: 16
lora_alpha: 32
lora_dropout: 0.05

### dataset
dataset: self_congnition_dataset1
template: glm4
cutoff_len: 4096
max_samples: 3000
overwrite_cache: true
preprocessing_num_workers: 16

###  output
output_dir: /root/finetune_model/
logging_steps: 10
save_steps: 500
plot_loss: true
overwrite_output_dir: true

### train
per_device_train_batch_size: 2
gradient_accumulation_steps: 2
learning_rate: 5.0e-4
num_train_epochs: 3.0
\# max_steps: 2000
lr_scheduler_type: cosine
warmup_ratio: 0.03
fp16: true

### eval
val_size: 0.1
per_device_eval_batch_size: 1
evaluation_strategy: steps
eval_steps: 100

merge命令:

llamafactory-cli export merge.yaml

merge.yaml

### model
model_name_or_path: models/glm-4-9b-chat
adapter_name_or_path: finetune_model/lora_finetune
template: glm4
finetuning_type: lora

### export
export_dir: finetune_model/lora_finetune_merge
export_size: 2
export_device: cpu
export_legacy_format: false

@sixsixcoder
Copy link
Collaborator

我成功复现你的问题了,问题出现在template模版上,目前的解决方案是修改llama-factory微调merge后权重的tokenizer_config.json文件,替换chat_template 为:

  "chat_template": "[gMASK]<sop>{% for item in messages %}{% if item['tools'] is defined %}<|system|>\n你是一个名为 GLM-4 的人工智能助手。你是基于智谱AI训练的语言模型 GLM-4 模型开发的,你的任务是针对用户的问题和要求提供适当的答复和支持。\n\n# 可用工具{% set tools = item['tools'] %}{% for tool in tools %}{% if tool['type'] == 'function' %}\n\n## {{ tool['function']['name'] }}\n\n{{ tool['function'] | tojson(indent=4) }}\n在调用上述函数时,请使用 Json 格式表示调用的参数。{% elif tool['type'] == 'python' %}\n\n## python\n\n当你向 `python` 发送包含 Python 代码的消息时,该代码将会在一个有状态的 Jupyter notebook 环境中执行。\n`python` 返回代码执行的输出,或在执行 60 秒后返回超时。\n`/mnt/data` 将会持久化存储你的文件。在此会话中,`python` 无法访问互联网。不要使用 `python` 进行任何网络请求或者在线 API 调用,这些在线内容的访问将不会成功。{% elif tool['type'] == 'simple_browser' %}\n\n## simple_browser\n\n你可以使用 `simple_browser` 工具。该工具支持以下函数:\n`search(query: str, recency_days: int)`:使用搜索引擎进行查询并显示结果,可以使用 `recency_days` 参数控制搜索内容的时效性。\n`mclick(ids: list[int])`:获取一系列指定 id 的页面内容。每次调用时,须选择3-10个页面。选择多个角度的页面,同时尽可能选择可信任的信息来源。考虑到部分页面是无法加载的,你也可以多打开一些可能有用的页面而不用担心内容过多。\n`open_url(url: str)`:打开指定的 URL。\n\n使用 `【{引用 id}†{引用文本}】` 来引用内容。\n\n操作步骤:1. 使用 `search` 来获得信息列表; 2. 使用 `mclick` 来获取指定 ID 页面的内容; 3. 根据获得的内容进行回复。在回复中应当引用信息来源。\n 如果用户提供了 URL,也可以用 `open_url` 直接打开页面。\n如果初次搜索结果没有找到合适的信息,也可以再次使用 `search` 进行搜索。{% elif tool['type'] == 'cogview' %}\n\n## cogview\n\n如果用户的请求中包含了对图像的描述,你可以使用 `cogview` 来生成图像并展示给用户。你需要向 `cogview` 发送图像描述,规则:\n- 发送给 `cogview` 的消息必须使用英语。用户的中文描述必须完全翻译为英语。\n- 应当尽可能详细地描述图像生成的需求,需求描述约 100 英文单词。\n- 保持用户原始描述的意图。不要虚构内容或者没见过的人物。\n- 如无特殊说明,所在地为中国,持有中国立场并遵循中国社会主义价值观。{% endif %}{% endfor %}{% endif %}{% if item['content'] %}<|{{ item['role'] }}|>{{ item['metadata'] }}\n{{ item['content'] }}{% endif %}{% endfor %}{% if add_generation_prompt %}<|assistant|>{% endif %}",

重新执行python openai_api_server.py服务后,向服务器发送工具调用的请求即可。
随后我们将在LLaMA-Factory仓库中提交修复代码。

@Andy1018
Copy link

Andy1018 commented Nov 8, 2024

@sixsixcoder 调用工具不报错了。通过llamafactory微调glm4后乱码是什么原因导致的啊,随便一个训练数据都是这个问题。
(glm4) [root@adsl-172-10-0-187 basic_demo]# python openai_api_request.py
ChatCompletion(id='chatcmpl-ZPaE1pu1lJo7XBetF5gIRHYH7LKBK', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!', refusal=None, role='assistant', audio=None, function_call=None, tool_calls=None))], created=1731026510, model='glm-4', object='chat.completion', service_tier=None, system_fingerprint='fp_xJsllLODf', usage=CompletionUsage(completion_tokens=256, prompt_tokens=47, total_tokens=303, completion_tokens_details=None, prompt_tokens_details=None))
ChatCompletion(id='chatcmpl-U25035HyRrY03K6JBO94XfLEN0pTh', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!', refusal=None, role='assistant', audio=None, function_call=None, tool_calls=None))], created=1731026517, model='glm-4', object='chat.completion', service_tier=None, system_fingerprint='fp_nXuYgjRcJ', usage=CompletionUsage(completion_tokens=256, prompt_tokens=166, total_tokens=422, completion_tokens_details=None, prompt_tokens_details=None))
None
您微调后要把openai_api_request.py 中的function_chat函数的tools变量修改成数据集同样格式的内容

@Jimmy-L99
Copy link
Author

@Andy1018 请教一下,你这边用的也是glm4-9b-chat吗

@Andy1018
Copy link

Andy1018 commented Nov 8, 2024

@Andy1018 请教一下,你这边用的也是glm4-9b-chat吗

是的

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants