参考: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}")