阿里通义千问:本地部署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 中下载:

bash 复制代码
#从ModelScope下载模型
git clone https://www.modelscope.cn/qwen/Qwen1.5-4B-Chat.git

3.2 安装依赖

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

bash 复制代码
# vi requirements.txt

# basic requirements

fastapi>=0.110.0
uvicorn>=0.29.0
pydantic>=2.7.0
tiktoken>=0.6.0
sse-starlette>=2.0.0

transformers>=4.37.0
torch>=2.1.0
sentencepiece>=0.2.0
sentence-transformers>=2.4.0
accelerate


#安装依赖
pip install requirements.txt

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

3.2 编码实现

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

python 复制代码
# 文件名:api-server-llm.py

# -*- coding: utf-8 -*-
# This is a sample Python script.

import argparse
import datetime
import uuid
from threading import Thread
import os

from typing import List, Optional
from pydantic import BaseModel
# Press Shift+F10 to execute it or replace it with your code.
# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.
import uvicorn
from fastapi import FastAPI, Request, Response
from fastapi.responses import JSONResponse
from sse_starlette.sse import EventSourceResponse
from transformers import AutoTokenizer, AutoModelForCausalLM, TextIteratorStreamer
from utils import num_tokens_from_string


# 声明API
app = FastAPI(default_timeout=1000 * 60 * 10)


# chat消息
class ChatMessage(BaseModel):
    # 角色
    role: str = None
    # chat内容
    content: str = None


# chat请求信息
class ChatRequest(BaseModel):
    # 对话信息
    messages: List[ChatMessage]
    # 模型平台
    platform: str = None
    # 具体模型名称
    model: str = None
    # 客户端id
    clientId: str = None
    stream: Optional[bool] = False


# 当前对话的模型输出内容
class ChatResponseChoice(BaseModel):
    # 模型推理终止的原因
    finishReason: str
    # 消息内容
    messages: ChatMessage


# tokens 统计数据
class ResponseUsage(BaseModel):
    # 模型输入的 tokens 数量
    outputTokens: int
    # 用户输入的 tokens 数量
    inputTokens: int


# chat返回信息
class ChatResponse(BaseModel):
    clientId: str = None
    usage: ResponseUsage
    choices: List[ChatResponseChoice]


# 统一异常处理
@app.exception_handler(Exception)
async def all_exceptions_handler(request: Request, exc: Exception):
    """
    处理所有异常
    """
    return JSONResponse(
        status_code=500,
        content={
            "msg": str(exc)
        }
    )


# 自定义中间件
@app.middleware("http")
async def unified_interception(request: Request, call_next):
    # 在这里编写你的拦截逻辑
    # 例如,检查请求的header或参数
    # 如果不满足条件,可以直接返回响应,不再调用后续的路由处理
    token = request.headers.get("Authorization")

    # if token is None:
    #     return JSONResponse({"message": "Missing Authorization"}, status_code=401)

    # 如果满足条件,则继续调用后续的路由处理
    response = await call_next(request)

    # 在这里编写你的响应处理逻辑
    # 例如,添加或修改响应头

    # 返回最终的响应
    return response


# 监控
@app.get("/api/models")
async def models():
    # 构造返回数据
    response = {
        "model": MODEL_NAME
    }
    return JSONResponse(response, status_code=200)


# 构造 回复消息对象
def create_chat_response(request_id: str, role: str, content: str, input_tokens: int, output_tokens: int,
                         finish_reason: str):
    now = datetime.datetime.now()  # 获取当前时间
    time = now.strftime("%Y-%m-%d %H:%M:%S")  # 格式化时间为字符串
    chat_message = {
        "output": {
            "choices": [{
                "finish_reason": finish_reason,
                "message": {
                    "role": role,
                    "content": content
                }
            }]
        },
        "request_id": request_id,
        "usage": {
            "input_tokens": input_tokens,
            "output_tokens": output_tokens,
            "total_tokens": (input_tokens + output_tokens)
        },
        "time": time
    }
    return chat_message


# 获取流式数据
def predict_stream(generation_kwargs: dict, request_id: str, input_tokens: int):
    thread = Thread(target=model.generate, kwargs=generation_kwargs)
    thread.start()
    output_tokens = 0

    for new_text in streamer:
        output_tokens = output_tokens + num_tokens_from_string(str(new_text))
        # 构造返回数据
        yield str(create_chat_response(request_id=request_id, role="assistant", content=new_text,
                                       input_tokens=input_tokens, output_tokens=output_tokens, finish_reason=""))

    yield str(create_chat_response(request_id=request_id, role="assistant", content="", input_tokens=input_tokens,
                                   output_tokens=output_tokens, finish_reason="stop"))


@app.post("/api/v1/chat/completions")
async def completions(request: ChatRequest):
    # if MODEL_NAME != request.model:
    #     return JSONResponse({"msg": "模型异常!"}, status_code=500)

    if len(request.messages) <= 0:
        return JSONResponse({"msg": "消息不能为空!"}, status_code=500)

    # 请求id
    request_id = str(uuid.uuid4())
    # 计算输入tokens
    input_tokens = sum(num_tokens_from_string(message.content) for message in request.messages)

    # 使用自带的聊天模板,格式化对话记录
    text = tokenizer.apply_chat_template(request.messages, tokenize=False, add_generation_prompt=True)
    model_inputs = tokenizer(text, return_tensors="pt").to("cpu")

    if request.stream:
        print("----流式-----")
        # 流式对象

        generation_kwargs = dict(**model_inputs, streamer=streamer, max_new_tokens=4096,
                                 pad_token_id=tokenizer.eos_token_id,
                                 num_beams=1, do_sample=True, top_p=0.8,
                                 temperature=0.3)
        generate = predict_stream(generation_kwargs=generation_kwargs, request_id=request_id, input_tokens=input_tokens)

        return EventSourceResponse(generate, media_type="text/event-stream")

    print("----非流式-----")
    # 非流式
    generated_ids = model.generate(
        model_inputs.input_ids
        , max_new_tokens=512
    )
    generated_ids = [
        output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
    ]

    response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
    # 计算输出tokens
    output_tokens = num_tokens_from_string(response)
    # 构造返回数据
    result = create_chat_response(request_id=request_id, role="assistant", content=response, input_tokens=input_tokens,
                                  output_tokens=output_tokens, finish_reason="stop")
    return JSONResponse(result, status_code=200)


if __name__ == '__main__':
    # 定义命令行解析器对象
    parser = argparse.ArgumentParser(description='大语言模型参数解析器')
    # 添加命令行参数、默认值
    parser.add_argument("--host", type=str, default="0.0.0.0")
    parser.add_argument("--port", type=int, default=8860)
    parser.add_argument("--model_path", type=str, default="")
    parser.add_argument("--model_name", type=str, default="")
    # 从命令行中结构化解析参数
    args = parser.parse_args()

    # 模型路径
    MODEL_PATH = args.model_path
    MODEL_NAME = args.model_name

    if len(MODEL_PATH) <= 0:
        raise Exception("大语言模型路径不能为空!")
    # 如果没有传入模型名称,则从路径中获取
    if len(MODEL_NAME) == 0:
       MODEL_DIR, MODEL_NAME = os.path.split(MODEL_PATH)

    tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH, trust_remote_code=True)

    model = AutoModelForCausalLM.from_pretrained(
        MODEL_PATH, trust_remote_code=True, device_map='auto'
    ).eval()
    # 流式输出
    streamer = TextIteratorStreamer(tokenizer=tokenizer, skip_prompt=True, skip_special_tokens=True)

    # 启动 uvicorn 服务
    uvicorn.run(app, host=args.host, port=args.port)

3.3 运行服务

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

3.4 使用API访问

bash 复制代码
curl -X POST -H "Content-Type: application/json" 'http://localhost:8860/api/v1/chat/completions' \
-d '{
	"platform": "tongyi", 
	"model": "Qwen1.5-4B-Chat",  
	"stream":true, 
	"messages":[ 
		{
			"role": "user", 
			"content": "你好,你是谁?" 
		} 
	]
  }'

相关推荐
deephub1 天前
LEC: 基于Transformer中间层隐藏状态的高效特征提取与内容安全分类方法
人工智能·深度学习·transformer·大语言模型·特征提取
流穿2 天前
WebSocket vs SSE:实时通信技术的对比与选择
网络·websocket·网络协议·大语言模型·sse
python_知世3 天前
基于LLaMA-Factory微调Llama3
人工智能·深度学习·程序人生·自然语言处理·大语言模型·llama·大模型微调
知来者逆3 天前
基于大语言模型的多代理下一代制造系统能灵活动态管理制造资源的高效调度方法
人工智能·深度学习·自然语言处理·llm·大语言模型·制造
lzl20404 天前
【深度学习总结】使用PDF构建RAG:结合Langchain和通义千问
深度学习·langchain·通义千问·qwen·rag
MatpyMaster5 天前
大模型QLoRA微调——基于Qwen2-7B的自动化病历摘要生成系统
人工智能·大模型·qwen·量化微调
李好秀6 天前
大语言模型
python·ai·llm·qwen
知来者逆9 天前
MSciNLI—— 针对科学自然语言推理任务提出的多样化数据集用于训练语言模型和大规模语言模型建立基线
人工智能·深度学习·机器学习·语言模型·自然语言处理·大语言模型
知来者逆9 天前
LAVE——基于大语言模型的新型代理辅助视频编辑工具允许用户根据自己的编辑风格进行调整
人工智能·深度学习·计算机视觉·语言模型·自然语言处理·大语言模型·智能算法
农夫山泉2号12 天前
【OCR】——端到端文字识别GOT-OCR2.0不香嘛?
ocr·qwen·got-ocr