llm的部署及调用(基于transformers、vllm、fastapi部署、awq量化)

参考:https://github.com/datawhalechina/self-llm

1 环境配置

  • pip全局换源
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
  • 使用modelscope进行模型的下载
pip install modelscope
  • 使用vllm完成模型部署和调用
pip install vllm
  • 使用fastapi完成部署
pip install fastapi uvicorn requests

2 模型下载(国内推荐从魔塔社区下载)

python文件脚本

from modelscope import snapshot_download
model_dir = snapshot_download('model_name', cache_dir='/path/to/model_store', revision='model_version')
  • snapshot_download 参数说明
    • 第一个参数为模型的名字,例如:Qwen/Qwen3-0.6B
    • cache_dir 为模型下载后的本地存放位置
    • revision 为指定下载的模型版本,可不设置

命令行下载

modelscope download --model 'Qwen/Qwen3-0.6B' --local_dir '/path/to/model_store'

3 使用vllm完成模型部署及调用

python脚本文件(不使用OpenAI API接口)

from vllm import LLM, SamplingParams
from transformers import AutoTokenizer
import os
import json

# 自动下载模型时,指定使用modelscope; 否则,会从HuggingFace下载
os.environ['VLLM_USE_MODELSCOPE']='True'

def get_completion(prompts, model, tokenizer=None, temperature=0.6, top_p=0.95, top_k=20, min_p=0, max_tokens=4096, max_model_len=8192):
    stop_token_ids = [151645, 151643]
    # 创建采样参数。temperature 控制生成文本的多样性,top_p 控制核心采样的概率,top_k 通过限制候选词的数量来控制生成文本的质量和多样性, min_p 通过设置概率阈值来筛选候选词,从而在保证文本质量的同时增加多样性
    sampling_params = SamplingParams(temperature=temperature, top_p=top_p, top_k=top_k, min_p=min_p, max_tokens=max_tokens, stop_token_ids=stop_token_ids)  # max_tokens 用于限制模型在推理过程中生成的最大输出长度
    # 初始化 vLLM 推理引擎
    llm = LLM(model=model, tokenizer=tokenizer, max_model_len=max_model_len,trust_remote_code=True)  # max_model_len 用于限制模型在推理过程中可以处理的最大输入和输出长度之和。
    outputs = llm.generate(prompts, sampling_params)
    return outputs


if __name__ == "__main__":
    # 初始化 vLLM 推理引擎
    model='/path/to/model' # 指定模型路径
    tokenizer = AutoTokenizer.from_pretrained(model, use_fast=False) # 加载分词器

    prompt = "给我一个关于大模型的简短介绍。"
    messages = [
        {"role": "user", "content": prompt}
    ]
    text = tokenizer.apply_chat_template(
        messages,
        tokenize=False,
        add_generation_prompt=True,
        enable_thinking=True  # 是否开启思考模式,默认为 True
    )

    outputs = get_completion(text, model, tokenizer=None, temperature=0.6, top_p = 0.95, top_k=20, min_p=0)  # 对于思考模式,官方建议使用以下参数:temperature = 0.6,TopP = 0.95,TopK = 20,MinP = 0。

    # 输出是一个包含 prompt、生成文本和其他信息的 RequestOutput 对象列表。
    # 打印输出。
    for output in outputs:
        prompt = output.prompt
        generated_text = output.outputs[0].text
        print(f"Prompt: {prompt!r}, \nResponse: {generated_text!r}")

使用OpenAI API接口创建服务器后再调用

  • 首先需要开启服务器,在命令行输入如下代码:
vllm serve /path/to/model --port 8000 --max-model-len 8192 --enable-reasoning --reasoning-parser deepseek_r1 --api-key EMPTY
或者
python -m vllm.entrypoints.openai.api_server \
    --model /path/to/model \
    --max-model-len 8192 \
    --enable-reasoning --reasoning-parser deepseek_r1 \
    --api-key EMPTY \
    --port 8000
  • 参数说明
    • --model MODEL_NAME_OR_PATH: Hugging Face模型库中的模型名称(例如Qwen/Qwen3-0.6B)或已下载到本地的模型路径。
    • --tokenizer MODEL_NAME_OR_PATH_OR_TOKENIZER_NAME (可选): 指定分词器的名称或路径。如果未提供,vLLM会尝试从模型路径或名称自动推断。
    • –revision REVISION_STRING (可选): 指定模型的特定版本(如分支名、标签或commit ID)。
    • --tokenizer-revision REVISION_STRING (可选): 指定分词器的特定版本。
    • --tensor-parallel-size N: 用于张量并行的GPU数量。例如,设为2表示使用2张GPU进行张量并行。默认值为1。
    • --pipeline-parallel-size M: 用于流水线并行的GPU数量(或阶段数)。默认值为1。通常与张量并行结合使用于超大模型。
    • --dtype {auto,half,float16,bfloat16,float,float32}: 模型权重和激活值的数据类型。auto会根据模型配置自动选择(通常FP16或BF16)。half等同于float16。(vLLM Docs - CLI Reference for API Server)
    • --quantization {awq, gptq, squeezellm, fp8, None}: 指定模型使用的量化方法。例如,awq, gptq。默认为None(不量化)。
    • --kv-cache-dtype {auto,fp8}: KV Cache的数据类型。CUDA 11.8+支持fp8。默认auto(同模型dtype)。
    • --gpu-memory-utilization 0.XX: GPU显存用于模型权重、激活和KV缓存的比例(0到1之间)。例如,0.9表示使用90%的可用GPU显存。调高此值可以增加KV缓存空间,从而可能提高吞吐量,但过高可能导致OOM错误。
    • --max-model-len XXXX: 模型支持的最大序列长度(上下文窗口大小)。如果未指定,vLLM会尝试从模型配置中推断。
    • --max-num-batched-tokens YYYY (可选): 一个批次中所有序列的总Token数上限,影响调度策略。
    • --max-num-seqs ZZZ (可选): 服务器并发处理的最大序列(请求)数。
    • --trust-remote-code: 当模型需要执行Hugging Face Hub上的自定义代码时(例如某些Qwen, ChatGLM模型),必须添加此标志。请确保信任代码来源。
    • --port PPPP: API服务器监听的端口号,默认为8000。
    • --host X.X.X.X: API服务器监听的IP地址,默认为localhost。设为0.0.0.0以允许外部访问。
    • --served-model-name YOUR_MODEL_NAME_IN_API (可选): 在OpenAI API端点中为此模型指定一个自定义名称。
    • --enable-reasoning 开启思考模式
    • --reasoning-parser 指定如何解析模型生成的推理内容。设置 --enable-reasoning 参数时,--reasoning-parser 是必需的。推理模型会在输出中包含一个额外的 reasoning_content 字段,该字段包含导致最终结论的推理步骤。通过指定合适的解析器,可以正确提取和格式化这些推理内容。例如 deepseek_r1 解析器适用于 DeepSeek R1 系列模型,能够解析 … 格式的内容
      • Qwen3也可以使用deepseek_r1 解析器
  • 再用python脚本请求OpenAI Chat Completions API使用
from openai import OpenAI
# 设置 OpenAI API 密钥和 API 基础地址,这里指向 vLLM 的 API 服务器
openai_api_key = "EMPTY"  # 使用 vLLM 时不需要真实的 OpenAI API 密钥
openai_api_base = "http://localhost:8000/v1"  # vLLM 服务器的默认地址
# 创建 OpenAI 客户端实例,连接到本地 vLLM 服务
client = OpenAI(
    api_key=openai_api_key,
    base_url=openai_api_base,
)
# 调用聊天 API 生成回复
chat_response = client.chat.completions.create(
    # 指定要使用的模型路径
    model="/path/to/model",
    # 用户与模型的对话消息列表,role 指定角色,content 是消息内容
    messages=[
        {"role": "user", "content": "简单介绍一下你自己。"},
    ],
    # 可选参数:控制生成文本的长度、随机性等
    max_tokens=32768,  # 限制生成文本的最大 token 数
    # 温度参数,控制生成文本的随机性(0.0 表示确定性,1.0 表示最大随机性)
    temperature=0.6,
    # 核采样参数,只考虑累积概率达到 top_p 的 token
    top_p=0.95,
    
    # vLLM 扩展参数:在采样时考虑概率最高的前 k 个 token
    extra_body={
        "top_k": 20,
    }
)
# 打印完整的 API 响应对象
print("Chat response:", chat_response)
# 打印模型生成的回复消息
print(chat_response.choices[0].message)

4 使用fastapi完成模型部署及调用

  • 创建python脚本,命名为api.py
from fastapi import FastAPI, Request
from transformers import AutoTokenizer, AutoModelForCausalLM, GenerationConfig
import uvicorn
import json
import datetime
import torch

# 设置设备参数
DEVICE = "cuda"  # 使用CUDA
DEVICE_ID = "1"  # CUDA设备ID,如果未设置则为空
CUDA_DEVICE = f"{DEVICE}:{DEVICE_ID}" if DEVICE_ID else DEVICE  # 组合CUDA设备信息

# 清理GPU内存函数
def torch_gc():
    if torch.cuda.is_available():  # 检查是否可用CUDA
        with torch.cuda.device(CUDA_DEVICE):  # 指定CUDA设备
            torch.cuda.empty_cache()  # 清空CUDA缓存
            torch.cuda.ipc_collect()  # 收集CUDA内存碎片

# 创建FastAPI应用
app = FastAPI()

# 处理POST请求的端点
@app.post("/")
async def create_item(request: Request):
    global model, tokenizer  # 声明全局变量以便在函数内部使用模型和分词器
    json_post_raw = await request.json()  # 获取POST请求的JSON数据
    json_post = json.dumps(json_post_raw)  # 将JSON数据转换为字符串
    json_post_list = json.loads(json_post)  # 将字符串转换为Python对象
    prompt = json_post_list.get('prompt')  # 获取请求中的提示
    max_length = json_post_list.get('max_length')  # 获取请求中的最大长度
    
    # 构建 messages      
    messages = [
        {"role": "user", "content": prompt}
    ]
    # 构建输入     
    input_tensor = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt")
    # 通过模型获得输出
    outputs = model.generate(input_tensor.to(model.device), max_new_tokens=max_length)
    result = tokenizer.decode(outputs[0][input_tensor.shape[1]:], skip_special_tokens=True)
    
    now = datetime.datetime.now()  # 获取当前时间
    time = now.strftime("%Y-%m-%d %H:%M:%S")  # 格式化时间为字符串
    # 构建响应JSON
    answer = {
        "response": result,
        "status": 200,
        "time": time
    }
    # 构建日志信息
    log = "[" + time + "] " + '", prompt:"' + prompt + '", response:"' + repr(result) + '"'
    print(log)  # 打印日志
    torch_gc()  # 执行GPU内存清理
    return answer  # 返回响应

# 主函数入口
if __name__ == '__main__':
    mode_name_or_path = "/path/to/model"
    # 加载预训练的分词器和模型
    tokenizer = AutoTokenizer.from_pretrained(mode_name_or_path, trust_remote_code=True)
    model = AutoModelForCausalLM.from_pretrained(mode_name_or_path, trust_remote_code=True,torch_dtype=torch.bfloat16,  device_map="auto")
    model.generation_config = GenerationConfig.from_pretrained(mode_name_or_path)
    model.generation_config.pad_token_id = model.generation_config.eos_token_id
    model.eval()  # 设置模型为评估模式
    # 启动FastAPI应用
    # 用6006端口可以将autodl的端口映射到本地,从而在本地使用api
    uvicorn.run(app, host='0.0.0.0', port=6006, workers=1)  # 在指定端口和主机上启动应用
  • 执行api.py后再执行如下脚本完成调用
import requests
import json

def get_completion(prompt):
    headers = {'Content-Type': 'application/json'}
    data = {"prompt": prompt}
    response = requests.post(url='http://127.0.0.1:6006', headers=headers, data=json.dumps(data))
    return response.json()['response']

if __name__ == '__main__':
    print(get_completion('你好'))
  • 还可以通过POST 方法进行调用,使用curl调用
curl -X POST "http://127.0.0.1:6006" \
     -H 'Content-Type: application/json' \
     -d '{"prompt": "你好"}'

5 使用transformers进行模型部署

参考:https://qwen.readthedocs.io/zh-cn/latest/getting_started/quickstart.html

  • transformers推理Qwen3
from transformers import AutoModelForCausalLM, AutoTokenizer

model_name = "path/to/Qwen3-0.6B"

# load the tokenizer and the model
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype="auto",
    device_map="auto" # 依赖accelerate库
)
tokenizer = AutoTokenizer.from_pretrained(model_name)

# prepare the model input
prompt = "Give me a short introduction to large language models."
messages = [
    {"role": "user", "content": prompt},
]
text = tokenizer.apply_chat_template(
    messages,
    tokenize=False, # 指示函数返回一个格式化好的字符串,而不是直接分词后的 ID。
    add_generation_prompt=True, # 在模板末尾添加提示符,告诉模型现在轮到它来生成内容了
    enable_thinking=True, # Switches between thinking and non-thinking modes. Default is True.
)
model_inputs = tokenizer([text], return_tensors="pt").to(model.device)

# conduct text completion
generated_ids = model.generate(
    #  这是一个 Python 的解包语法。它会将 model_inputs 字典中的键值对(如 input_ids, attention_mask)作为独立的关键字参数传递给 generate 函数。
    **model_inputs,
    max_new_tokens=32768
)
# generated_ids包含了我们输入的prompt和模型新生成的部分。这行代码通过切片操作,去掉了原始输入的部分,只保留模型新生成部分的 token ID。
output_ids = generated_ids[0][len(model_inputs.input_ids[0]):].tolist() 

# parse thinking content
try:
    # rindex finding 151668 (</think>)
    index = len(output_ids) - output_ids[::-1].index(151668)
except ValueError:
    index = 0

thinking_content = tokenizer.decode(output_ids[:index], skip_special_tokens=True).strip("\n")
content = tokenizer.decode(output_ids[index:], skip_special_tokens=True).strip("\n")

print("thinking content:", thinking_content)
print("content:", content)

6 使用AWQ完成模型量化

  • python脚本如下
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer

model_path = "models/Qwen3-0.6B"
quant_path = "models/Qwen3-0.6B-awq-4bit"

# 创建本地校准数据
calib_data = [
    "Qwen3 is a powerful AI assistant developed by DeepSeek company.",
    "Quantization reduces model size and improves inference speed.",
    "Activation-aware Weight Quantization (AWQ) is an efficient method.",
    "The weather is sunny today in Beijing, perfect for outdoor activities.",
    "Machine learning models can be optimized for edge devices."
    # 可添加更多领域相关文本
] * 20

# 加载模型
model = AutoAWQForCausalLM.from_pretrained(model_path)
tokenizer = AutoTokenizer.from_pretrained(model_path)

# 量化配置
quant_config = {
    "zero_point": True,
    "q_group_size": 128,
    "w_bit": 4,
    "version": "GEMM"
}

# 使用本地校准文件
model.quantize(
    tokenizer,
    quant_config=quant_config,
    calib_data=calib_data  # 使用本地数据
)

# 保存量化模型
model.save_quantized(quant_path)
tokenizer.save_pretrained(quant_path)
print(f"量化完成,模型已保存至: {quant_path}")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值