当前位置:   article > 正文

阿里通义千问:本地部署Qwen1.5开源大模型

qwen1.5

一、Qwen1.5介绍

        通义千问为阿里云研发的大语言系列模型。千问模型基于Transformer架构,在超大规模的预训练数据上进行训练得到。预训练数据类型多样,覆盖广泛,包括大量网络文本、专业书籍、代码等。同时,在预训练模型的基础之上,使用对齐机制打造了模型的chat版本。

开源模型序列

模型

介绍

模型下载

qwen1.5-110b-chat

通义千问1.5对外开源的110B规模参数量的经过人类指令对齐的chat模型。

魔搭社区

qwen1.5-72b-chat通义千问1.5对外开源的72B规模参数量的经过人类指令对齐的chat模型。魔搭社区
qwen1.5-32b-chat通义千问1.5对外开源的32B规模参数量的经过人类指令对齐的chat模型魔搭社区
qwen1.5-14b-chat通义千问1.5对外开源的14B规模参数量的经过人类指令对齐的chat模型。魔搭社区
qwen1.5-7b-chat通义千问1.5对外开源的7B规模参数量是经过人类指令对齐的chat模型。魔搭社区
qwen1.5-4b-chat通义千问1.5对外开源的4B规模参数量是经过人类指令对齐的chat模型。魔搭社区
qwen1.5-0.5b-chat通义千问1.5对外开源的0.5B规模参数量的经过人类指令对齐的chat模型。魔搭社区

二、本地部署Qwen1.5

        由于资源问题,本机只能使用cpu运行qwen1.5_4b_chat,所以本次操作基于上述条件执行。

3.1 下载模型文件

      

可以Hugging Face Hub 下载模型,如果从 HuggingFace 下载比较慢,也可以从 ModelScope 中下载:

  1. #从ModelScope下载模型
  2. git clone https://www.modelscope.cn/qwen/Qwen1.5-4B-Chat.git

3.2 安装依赖

        可以通过 Conda 创建Python环境安装依赖。

  1. # vi requirements.txt
  2. # basic requirements
  3. fastapi>=0.110.0
  4. uvicorn>=0.29.0
  5. pydantic>=2.7.0
  6. tiktoken>=0.6.0
  7. sse-starlette>=2.0.0
  8. transformers>=4.37.0
  9. torch>=2.1.0
  10. sentencepiece>=0.2.0
  11. sentence-transformers>=2.4.0
  12. accelerate
  13. #安装依赖
  14. pip install requirements.txt

        安装过程可能重新网络问题,无法下载相关文件,可以重复执行多次;也可以去PyPI下载相关whl文件,离线安装。

3.2 编码实现

        通用python编码实现部署模型和提供RESTful API服务。

  1. # 文件名:api-server-llm.py
  2. # -*- coding: utf-8 -*-
  3. # This is a sample Python script.
  4. import argparse
  5. import datetime
  6. import uuid
  7. from threading import Thread
  8. import os
  9. from typing import List, Optional
  10. from pydantic import BaseModel
  11. # Press Shift+F10 to execute it or replace it with your code.
  12. # Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.
  13. import uvicorn
  14. from fastapi import FastAPI, Request, Response
  15. from fastapi.responses import JSONResponse
  16. from sse_starlette.sse import EventSourceResponse
  17. from transformers import AutoTokenizer, AutoModelForCausalLM, TextIteratorStreamer
  18. from utils import num_tokens_from_string
  19. # 声明API
  20. app = FastAPI(default_timeout=1000 * 60 * 10)
  21. # chat消息
  22. class ChatMessage(BaseModel):
  23. # 角色
  24. role: str = None
  25. # chat内容
  26. content: str = None
  27. # chat请求信息
  28. class ChatRequest(BaseModel):
  29. # 对话信息
  30. messages: List[ChatMessage]
  31. # 模型平台
  32. platform: str = None
  33. # 具体模型名称
  34. model: str = None
  35. # 客户端id
  36. clientId: str = None
  37. stream: Optional[bool] = False
  38. # 当前对话的模型输出内容
  39. class ChatResponseChoice(BaseModel):
  40. # 模型推理终止的原因
  41. finishReason: str
  42. # 消息内容
  43. messages: ChatMessage
  44. # tokens 统计数据
  45. class ResponseUsage(BaseModel):
  46. # 模型输入的 tokens 数量
  47. outputTokens: int
  48. # 用户输入的 tokens 数量
  49. inputTokens: int
  50. # chat返回信息
  51. class ChatResponse(BaseModel):
  52. clientId: str = None
  53. usage: ResponseUsage
  54. choices: List[ChatResponseChoice]
  55. # 统一异常处理
  56. @app.exception_handler(Exception)
  57. async def all_exceptions_handler(request: Request, exc: Exception):
  58. """
  59. 处理所有异常
  60. """
  61. return JSONResponse(
  62. status_code=500,
  63. content={
  64. "msg": str(exc)
  65. }
  66. )
  67. # 自定义中间件
  68. @app.middleware("http")
  69. async def unified_interception(request: Request, call_next):
  70. # 在这里编写你的拦截逻辑
  71. # 例如,检查请求的header或参数
  72. # 如果不满足条件,可以直接返回响应,不再调用后续的路由处理
  73. token = request.headers.get("Authorization")
  74. # if token is None:
  75. # return JSONResponse({"message": "Missing Authorization"}, status_code=401)
  76. # 如果满足条件,则继续调用后续的路由处理
  77. response = await call_next(request)
  78. # 在这里编写你的响应处理逻辑
  79. # 例如,添加或修改响应头
  80. # 返回最终的响应
  81. return response
  82. # 监控
  83. @app.get("/api/models")
  84. async def models():
  85. # 构造返回数据
  86. response = {
  87. "model": MODEL_NAME
  88. }
  89. return JSONResponse(response, status_code=200)
  90. # 构造 回复消息对象
  91. def create_chat_response(request_id: str, role: str, content: str, input_tokens: int, output_tokens: int,
  92. finish_reason: str):
  93. now = datetime.datetime.now() # 获取当前时间
  94. time = now.strftime("%Y-%m-%d %H:%M:%S") # 格式化时间为字符串
  95. chat_message = {
  96. "output": {
  97. "choices": [{
  98. "finish_reason": finish_reason,
  99. "message": {
  100. "role": role,
  101. "content": content
  102. }
  103. }]
  104. },
  105. "request_id": request_id,
  106. "usage": {
  107. "input_tokens": input_tokens,
  108. "output_tokens": output_tokens,
  109. "total_tokens": (input_tokens + output_tokens)
  110. },
  111. "time": time
  112. }
  113. return chat_message
  114. # 获取流式数据
  115. def predict_stream(generation_kwargs: dict, request_id: str, input_tokens: int):
  116. thread = Thread(target=model.generate, kwargs=generation_kwargs)
  117. thread.start()
  118. output_tokens = 0
  119. for new_text in streamer:
  120. output_tokens = output_tokens + num_tokens_from_string(str(new_text))
  121. # 构造返回数据
  122. yield str(create_chat_response(request_id=request_id, role="assistant", content=new_text,
  123. input_tokens=input_tokens, output_tokens=output_tokens, finish_reason=""))
  124. yield str(create_chat_response(request_id=request_id, role="assistant", content="", input_tokens=input_tokens,
  125. output_tokens=output_tokens, finish_reason="stop"))
  126. @app.post("/api/v1/chat/completions")
  127. async def completions(request: ChatRequest):
  128. # if MODEL_NAME != request.model:
  129. # return JSONResponse({"msg": "模型异常!"}, status_code=500)
  130. if len(request.messages) <= 0:
  131. return JSONResponse({"msg": "消息不能为空!"}, status_code=500)
  132. # 请求id
  133. request_id = str(uuid.uuid4())
  134. # 计算输入tokens
  135. input_tokens = sum(num_tokens_from_string(message.content) for message in request.messages)
  136. # 使用自带的聊天模板,格式化对话记录
  137. text = tokenizer.apply_chat_template(request.messages, tokenize=False, add_generation_prompt=True)
  138. model_inputs = tokenizer(text, return_tensors="pt").to("cpu")
  139. if request.stream:
  140. print("----流式-----")
  141. # 流式对象
  142. generation_kwargs = dict(**model_inputs, streamer=streamer, max_new_tokens=4096,
  143. pad_token_id=tokenizer.eos_token_id,
  144. num_beams=1, do_sample=True, top_p=0.8,
  145. temperature=0.3)
  146. generate = predict_stream(generation_kwargs=generation_kwargs, request_id=request_id, input_tokens=input_tokens)
  147. return EventSourceResponse(generate, media_type="text/event-stream")
  148. print("----非流式-----")
  149. # 非流式
  150. generated_ids = model.generate(
  151. model_inputs.input_ids
  152. , max_new_tokens=512
  153. )
  154. generated_ids = [
  155. output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
  156. ]
  157. response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
  158. # 计算输出tokens
  159. output_tokens = num_tokens_from_string(response)
  160. # 构造返回数据
  161. result = create_chat_response(request_id=request_id, role="assistant", content=response, input_tokens=input_tokens,
  162. output_tokens=output_tokens, finish_reason="stop")
  163. return JSONResponse(result, status_code=200)
  164. if __name__ == '__main__':
  165. # 定义命令行解析器对象
  166. parser = argparse.ArgumentParser(description='大语言模型参数解析器')
  167. # 添加命令行参数、默认值
  168. parser.add_argument("--host", type=str, default="0.0.0.0")
  169. parser.add_argument("--port", type=int, default=8860)
  170. parser.add_argument("--model_path", type=str, default="")
  171. parser.add_argument("--model_name", type=str, default="")
  172. # 从命令行中结构化解析参数
  173. args = parser.parse_args()
  174. # 模型路径
  175. MODEL_PATH = args.model_path
  176. MODEL_NAME = args.model_name
  177. if len(MODEL_PATH) <= 0:
  178. raise Exception("大语言模型路径不能为空!")
  179. # 如果没有传入模型名称,则从路径中获取
  180. if len(MODEL_NAME) == 0:
  181. MODEL_DIR, MODEL_NAME = os.path.split(MODEL_PATH)
  182. tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH, trust_remote_code=True)
  183. model = AutoModelForCausalLM.from_pretrained(
  184. MODEL_PATH, trust_remote_code=True, device_map='auto'
  185. ).eval()
  186. # 流式输出
  187. streamer = TextIteratorStreamer(tokenizer=tokenizer, skip_prompt=True, skip_special_tokens=True)
  188. # 启动 uvicorn 服务
  189. uvicorn.run(app, host=args.host, port=args.port)

  工具类:utils.py

  1. import tiktoken
  2. # 计算token数量
  3. def num_tokens_from_string(string: str) -> int:
  4. encoding = tiktoken.get_encoding('cl100k_base')
  5. num_tokens = len(encoding.encode(string))
  6. return num_tokens

3.3 运行服务

  1. # 运行脚本,指定模型路径
  2. python api-server-llm.py --model_path=F:\llm\model\Qwen1.5-4B-Chat

3.4 使用API访问

      

  1. curl -X POST -H "Content-Type: application/json" 'http://localhost:8860/api/v1/chat/completions' \
  2. -d '{
  3. "platform": "tongyi",
  4. "model": "Qwen1.5-4B-Chat",
  5. "stream":true,
  6. "messages":[
  7. {
  8. "role": "user",
  9. "content": "你好,你是谁?"
  10. }
  11. ]
  12. }'

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/908439
推荐阅读
相关标签
  

闽ICP备14008679号