Qwen2-VL-2B-Instruct FastApi 部署调用:基于《开源大模型食用指南》的多模态图像与视频问答服务实战
2026/9/12 7:32:46 网站建设 项目流程

Qwen2-VL-2B-Instruct FastApi 部署调用:基于《开源大模型食用指南》的多模态图像与视频问答服务实战

【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm

《开源大模型食用指南》项目(self-llm)为 Qwen2-VL 提供了完整的多模态大模型(MLLM)部署教程,本篇文章以其中 01-Qwen2-VL-2B-Instruct FastApi 部署调用 为核心,完整梳理从环境准备、模型下载到 FastAPI 图像/视频问答服务启动与请求测试的全过程。读完本文,你将能够独立搭建一个可对外提供 HTTP 接口的多模态推理服务,同时理解底层视觉信息处理(图像缩放、视频抽帧)的实现原理。

一、技术背景与服务方案概览

Qwen2-VL 是通义千问第二代视觉语言模型,Qwen2-VL-2B-Instruct是其 2B 参数的指令微调版本,具备图片理解、视频理解等多模态能力。在生产或实验环境中,通常需要把模型的推理能力封装成 HTTP 服务,供上层应用(Web 前端、机器人、知识库助手等)调用。

本文采用的技术栈组合为:

  • FastAPI:提供 RESTful HTTP 服务,处理请求路由与参数校验;
  • Uvicorn:ASGI 服务器,驱动 FastAPI 应用运行;
  • Transformers:加载Qwen2VLForConditionalGeneration模型与AutoProcessor处理器;
  • qwen-vl-utilsprocess_vision_info函数,负责从请求中抽取图像/视频并做预处理;
  • ModelScope:国内友好的模型下载通道,使用snapshot_download拉取模型权重。

整体调用链路为:客户端POST /generate→ FastAPI 解析messagesapply_chat_template组装对话模板 →process_vision_info预处理视觉输入 → Processor 编码 → 模型生成 → 解码返回文本。

二、环境准备

教程默认基础环境如下,请确保已提前安装好对应版本的 PyTorch(CUDA)环境:

ubuntu 22.04 python 3.12 cuda 12.1 pytorch 2.3.0

首先为pip换源加速下载,并安装依赖包。以下命令与版本号来自 models/Qwen2-VL/01-Qwen2-VL-2B-Instruct FastApi 参考代码/requirements.txt,版本锁定明确,可直接复制执行:

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple pip install modelscope==1.20.0 pip install fastapi==0.115.4 pip install uvicorn==0.32.0 pip install transformers==4.46.2 pip install accelerate==1.1.1 pip install torchvision==0.19.0 pip install av==13.1.0

各依赖的职责说明:

依赖包版本作用
modelscope1.20.0提供snapshot_download下载模型权重
fastapi0.115.4Web 框架,提供/generate路由与请求体校验
uvicorn0.32.0ASGI 服务器,负责启动与监听端口
transformers4.46.2加载 Qwen2-VL 模型与 Processor
accelerate1.1.1支持device_map="auto"自动分配设备
torchvision0.19.0视频读取(torchvision.io.read_video)与图像缩放
av13.1.0PyAV 视频解码库,支撑视频帧提取

三、模型下载

使用 ModelScope 提供的snapshot_download函数下载模型,该方法对国内用户十分友好。新建model_download.py文件并写入以下代码,然后运行python model_download.py

# model_download.py from modelscope import snapshot_download model_dir = snapshot_download('Qwen/Qwen2-VL-2B-Instruct', cache_dir='/root/autodl-tmp', revision='master')

参数说明:

  • 第一个参数'Qwen/Qwen2-VL-2B-Instruct':模型名称,对应 ModelScope 上的模型仓库标识;
  • cache_dir='/root/autodl-tmp':模型下载的本地缓存路径,请务必修改为你自己的模型下载路径
  • revision='master':指定模型分支版本。

该文件在参考代码目录中同样提供:model_download.py。

四、代码准备:构建图像问答 API 服务

新建api_image.py(参考代码中命名为 api_server_image.py),写入以下内容:

# api_server_image.py from transformers import Qwen2VLForConditionalGeneration, AutoProcessor from qwen_vl_utils.vision_process import process_vision_info from fastapi import FastAPI, Request import uvicorn from pydantic import BaseModel from typing import List, Dict, Union # 创建FastAPI应用 app = FastAPI() # 下载好的模型本地路径 model_name_or_path = '/root/autodl-tmp/Qwen/Qwen2-VL-2B-Instruct' # 初始化模型和处理器(保持在全局范围内,这样只需加载一次) model = Qwen2VLForConditionalGeneration.from_pretrained( model_name_or_path, torch_dtype="auto", device_map="auto" ) processor = AutoProcessor.from_pretrained(model_name_or_path) # 定义请求体模型 class MessageContent(BaseModel): type: str text: str = None image: str = None class ChatMessage(BaseModel): messages: List[Dict[str, Union[str, List[Dict[str, str]]]]] # 处理POST请求的端点 @app.post("/generate") async def generate_response(chat_message: ChatMessage): # 直接使用请求中的 messages text = processor.apply_chat_template( chat_message.messages, tokenize=False, add_generation_prompt=True ) # 预先写好的辅助函数,位于参考代码中 image_inputs, video_inputs = process_vision_info(chat_message.messages) inputs = processor( text=[text], images=image_inputs, videos=video_inputs, padding=True, return_tensors="pt", ) inputs = inputs.to("cuda") # 生成输出 generated_ids = model.generate(**inputs, max_new_tokens=1024) generated_ids_trimmed = [ out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids) ] output_text = processor.batch_decode( generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False ) return {"response": output_text[0]} if __name__ == "__main__": # 启动FastAPI应用,端口为8000 uvicorn.run(app, host="0.0.0.0", port=8000)

4.1 关键代码逐段拆解

(1)模型与处理器初始化

model = Qwen2VLForConditionalGeneration.from_pretrained( model_name_or_path, torch_dtype="auto", device_map="auto" ) processor = AutoProcessor.from_pretrained(model_name_or_path)
  • torch_dtype="auto":自动选用模型权重自身的精度(如 bfloat16),避免默认 FP32 带来的显存浪费;
  • device_map="auto":由 accelerate 自动将模型分配到可用设备(GPU/CPU),可降低显存不足的风险;
  • 模型放在全局作用域初始化,服务启动时只加载一次,避免每个请求都重新加载权重,这是 FastAPI 部署 LLM 的标准做法;
  • AutoProcessor负责将文本与视觉输入编码为模型所需的多模态张量。

(2)对话模板组装

text = processor.apply_chat_template( chat_message.messages, tokenize=False, add_generation_prompt=True )

apply_chat_template将 OpenAI 风格的messages列表(role+content)转换为 Qwen2-VL 的对话格式,add_generation_prompt=True会在末尾追加生成提示符。

(3)视觉信息预处理

image_inputs, video_inputs = process_vision_info(chat_message.messages)

这是本次服务的关键辅助函数,来自参考代码目录中的 qwen_vl_utils/vision_process.py。它遍历messages中的content列表,抽取image/image_url/video类型的元素并分别处理。

(4)生成与解码

generated_ids = model.generate(**inputs, max_new_tokens=1024)

max_new_tokens=1024限制最多生成 1024 个新 token,可根据实际场景调整(数值越大回复越长,推理耗时越长)。随后通过切片去掉输入 token,仅保留新生成的输出并batch_decode解码为文本。

(5)服务启动

uvicorn.run(app, host="0.0.0.0", port=8000)

监听0.0.0.0表示允许外部访问,端口为 8000。

五、图像问答 API 服务启动与请求测试

在终端输入以下命令启动 API 服务:

python api_server_image.py

加载完毕后出现类似下图的信息说明服务启动成功:

5.1 使用 requests 调用服务

新建fastapi_request_image.py文件,使用 Pythonrequests库向/generate端点发送 POST 请求:

# fastapi_request_image.py import requests url = "http://localhost:8000/generate" payload = { "messages": [ { "role": "user", "content": [ { "type": "image", "image": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg" }, { "type": "text", "text": "Describe this image." } ] } ] } response = requests.post(url, json=payload) print(response.json())

请求体结构与 OpenAI Chat Completions 接口保持一致:messages[].content是一个数组,其中type: "image"的元素携带图片地址(支持 URL 或本地路径),type: "text"的元素携带用户提问。执行:

python fastapi_request_image.py

得到的返回结果如下:

{'response': "The image depicts a serene beach scene with a woman and a dog. The woman is sitting on the sand, wearing a plaid shirt and black pants, and appears to be smiling. She is holding the dog's paw in a high-five gesture. The dog, which is a large breed, is sitting on the sand with its front paws raised, possibly in response to the woman's gesture. The background shows the ocean with gentle waves, and the sky is clear with a soft light, suggesting it might be either sunrise or sunset. The overall atmosphere is peaceful and joyful."}

对比示例图片demo.jpeg可以观察到,模型不仅正确识别了"海滩上的女人和狗"这一主体,还准确描述了人物动作(击掌姿势)、环境背景(海浪、天空光线)以及整体氛围,回复质量非常高,说明 2B 规模的视觉语言模型在图文理解上已具备相当能力。

六、进阶实践:扩展视频问答能力

Qwen2-VL-2B-Instruct除了图片问答,同样支持视频形式的交互。只需在原代码基础上做少量修改:在MessageContent中增加video字段,process_vision_info便会自动抽取并处理视频帧。

新建api_server_image_and_video.py,复制如下代码:

# api_server_image_and_video.py from transformers import Qwen2VLForConditionalGeneration, AutoProcessor from qwen_vl_utils.vision_process import process_vision_info from fastapi import FastAPI, Request import uvicorn from pydantic import BaseModel from typing import List, Dict, Union app = FastAPI() model_name_or_path = '/root/autodl-tmp/Qwen/Qwen2-VL-2B-Instruct' model = Qwen2VLForConditionalGeneration.from_pretrained( model_name_or_path, torch_dtype="auto", device_map="auto" ) processor = AutoProcessor.from_pretrained(model_name_or_path) # 定义请求体模型 class MessageContent(BaseModel): type: str text: str = None image: str = None video: str = None # 添加对video的支持 class ChatMessage(BaseModel): messages: List[Dict[str, Union[str, List[Dict[str, str]]]]] @app.post("/generate") async def generate_response(chat_message: ChatMessage): # 直接使用请求中的 messages text = processor.apply_chat_template( chat_message.messages, tokenize=False, add_generation_prompt=True ) image_inputs, video_inputs = process_vision_info(chat_message.messages) inputs = processor( text=[text], images=image_inputs, videos=video_inputs, padding=True, return_tensors="pt", ) inputs = inputs.to("cuda") # 生成输出 generated_ids = model.generate(**inputs, max_new_tokens=1024) generated_ids_trimmed = [ out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids) ] output_text = processor.batch_decode( generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False ) return {"response": output_text[0]} if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000)

与图像版本相比,唯一的改动就是MessageContent中新增了video: str = None字段,其余逻辑完全复用——这得益于process_vision_info已经统一处理了图像与视频两条分支,服务端无需为视频单独编写处理逻辑。

6.1 视频问答服务启动

python api_server_image_and_video.py

加载完毕后出现如下信息说明服务启动成功:

6.2 视频请求测试

使用requests库发送视频问答请求(参考代码见 fastapi_request_video.py):

import requests url = "http://localhost:8000/generate" payload = { "messages": [ { "role": "user", "content": [ { "type": "video", "video": "./space_woaudio.mp4" }, { "type": "text", "text": "Describe this video." } ] } ] } response = requests.post(url, json=payload) print(response.json())

代码中,messages.content添加了一个视频元素,type: "video"video字段指向本地视频文件路径。执行请求:

python fastapi_request_video.py

模型返回结果如下:

{'response': "The video shows a man standing in a Mission Control Center, speaking to the camera. The center is equipped with various monitors and control panels, and there are several large screens displaying maps and data. The man appears to be giving a presentation or explaining something related to the center's operations."}

从结果看,模型准确捕捉到了视频的核心场景——"任务控制中心里讲话的人",并进一步描述了监控屏、控制台、大屏幕地图数据等环境细节,说明其对视频内容的理解(包括时序信息)是有效的。

七、源码级原理剖析:qwen-vl-utils 视觉预处理

为了让服务真正可用,理解 vision_process.py 的底层实现很有必要。该模块定义了多个关键常量,直接决定多模态输入的分辨率与帧数策略:

IMAGE_FACTOR = 28 # 图像边长必须能被 28 整除 MIN_PIXELS = 4 * 28 * 28 # 最小像素数 MAX_PIXELS = 16384 * 28 * 28 # 最大像素数 MAX_RATIO = 200 # 宽高比上限 VIDEO_MIN_PIXELS = 128 * 28 * 28 VIDEO_MAX_PIXELS = 768 * 28 * 28 VIDEO_TOTAL_PIXELS = 24576 * 28 * 28 FRAME_FACTOR = 2 # 帧数需为偶数 FPS = 2.0 # 默认抽帧帧率 FPS_MIN_FRAMES = 4 # 最少抽帧数 FPS_MAX_FRAMES = 768 # 最多抽帧数

7.1 图像处理:fetch_image 与 smart_resize

fetch_image支持四种图像输入来源:本地路径、http(s)://URL、file://前缀路径、data:imagebase64 编码,以及直接的PIL.Image对象(对应fetch_imageimage.startswith("http://") or image.startswith("https://")等分支)。教程请求示例中传入的https://...demo.jpeg正是通过requests.get(image, stream=True).raw流式读取的。

smart_resize负责智能缩放,保证满足三个约束:

  1. 宽高都能被factor(默认 28)整除;
  2. 总像素数落在[min_pixels, max_pixels]区间内;
  3. 尽可能保持原始宽高比。

其核心逻辑:先对宽高做round_by_factor对齐,若乘积超过max_pixels则按面积比例缩小(floor_by_factor),若不足min_pixels则按比例放大(ceil_by_factor)。这也是 Qwen2-VL 对任意分辨率、任意宽高比图像"原生支持"的关键所在——无需统一缩放到固定尺寸,避免信息丢失。若宽高比超过MAX_RATIO(200)会直接抛出异常。

7.2 视频处理:fetch_video 与 smart_nframes

视频路径处理分两步:

第一步,选择解码后端get_video_reader_backend通过lru_cache缓存结果,优先使用decord(若已安装),否则回退到torchvisionio.read_video。也可以设置环境变量FORCE_QWENVL_VIDEO_READER强制指定后端。两者均将视频读取为(T, C, H, W)张量。

第二步,计算抽帧数smart_nframes支持两种配置方式:

  • nframes:直接指定抽取帧数(会取偶数对齐);
  • fps:按帧率抽帧,默认FPS = 2.0,即每秒抽取 2 帧,帧数被限制在[FPS_MIN_FRAMES, FPS_MAX_FRAMES](4~768)之间,并取偶数。

随后通过torch.linspace(0, total_frames - 1, nframes)均匀取帧索引,保证抽帧在时间轴上均匀分布。视频帧同样经过smart_resize缩放,并使用双三次插值(InterpolationMode.BICUBIC)与antialias=True抗锯齿处理。

7.3 入口函数 process_vision_info

def process_vision_info(conversations): vision_infos = extract_vision_info(conversations) image_inputs, video_inputs = [], [] for vision_info in vision_infos: if "image" in vision_info or "image_url" in vision_info: image_inputs.append(fetch_image(vision_info)) elif "video" in vision_info: video_inputs.append(fetch_video(vision_info)) ... return image_inputs, video_inputs

它通过extract_vision_info遍历对话内容,收集所有含imageimage_urlvideo的元素,再分别调用fetch_image/fetch_video处理;若某一类输入为空则返回None。这正是 FastAPI 服务中同一份messages既能喂给apply_chat_template又能喂给process_vision_info的原因,也是"图像版→视频版"只需加一个字段即可扩展的根本所在。

八、参考代码与目录结构

本次教程涉及多个代码文件,仓库在 01-Qwen2-VL-2B-Instruct FastApi 参考代码 目录下提供了完整参考代码,包含:

文件作用
model_download.pyModelScope 模型下载脚本
api_server_image.py图像问答 FastAPI 服务
api_server_image_and_video.py图像+视频问答 FastAPI 服务
fastapi_request_image.py图像请求测试脚本
fastapi_request_video.py视频请求测试脚本
qwen_vl_utils/vision_process.py视觉预处理辅助库(图像缩放、视频抽帧)
requirements.txt依赖版本清单
space_woaudio.mp4视频测试样例

完成上述所有教程后的目录结构应类似下图,具体文件路径请根据实际存放情况修正:

建议初学者先理解各文件的职责与调用关系,再结合自身路径动手实践,避免盲目照抄路径导致报错。

九、常见问题与注意事项

  1. 模型路径必须修改model_download.py中的cache_dirapi_server_image.py中的model_name_or_path都要改成你自己的实际路径,否则会加载失败;
  2. process_vision_info的导入来源:代码中from qwen_vl_utils.vision_process import process_vision_info依赖参考代码目录下的qwen_vl_utils包,运行服务前请确认该包与api_server_image.py在同一工作目录(或已安装qwen-vl-utils依赖),并保持python api_server_image.py的启动目录正确;
  3. 视频解码依赖torchvision==0.19.0av==13.1.0是视频读取的关键依赖,版本过低可能导致http/https视频路径不受支持(源码中已明确提示torchvision < 0.19.0不支持网络视频路径);
  4. 显存占用:模型在全局加载一次,多请求共享同一份权重;若显存紧张,可关注torch_dtypedevice_map的配置,或减少并发请求数;
  5. 网络图片的可用性:请求测试使用了在线示例图片,若网络受限可改为本地图片路径,fetch_image同样支持。

至此,你已经完成了 Qwen2-VL-2B-Instruct 的 FastAPI 多模态服务部署,掌握了图像问答与视频问答两种服务的构建方法,并对底层视觉预处理的缩放与抽帧机制有了源码级理解。这套服务模式可以平滑迁移到仓库中其他视觉模型(如 Qwen2-VL Lora 微调后的模型)的部署上,相关微调实践可参考 04-Qwen2-VL-2B Lora 微调。

【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询