Qwen2-VL-2B-Instruct FastApi 部署调用:基于《开源大模型食用指南》的多模态图像与视频问答服务实战
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 服务处理请求路由与参数校验UvicornASGI 服务器驱动 FastAPI 应用运行Transformers加载Qwen2VLForConditionalGeneration模型与AutoProcessor处理器qwen-vl-utilsprocess_vision_info函数负责从请求中抽取图像/视频并做预处理ModelScope国内友好的模型下载通道使用snapshot_download拉取模型权重。整体调用链路为客户端POST /generate→ FastAPI 解析messages→apply_chat_template组装对话模板 →process_vision_info预处理视觉输入 → Processor 编码 → 模型生成 → 解码返回文本。二、环境准备教程默认基础环境如下请确保已提前安装好对应版本的 PyTorchCUDA环境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 modelscope1.20.0 pip install fastapi0.115.4 pip install uvicorn0.32.0 pip install transformers4.46.2 pip install accelerate1.1.1 pip install torchvision0.19.0 pip install av13.1.0各依赖的职责说明依赖包版本作用modelscope1.20.0提供snapshot_download下载模型权重fastapi0.115.4Web 框架提供/generate路由与请求体校验uvicorn0.32.0ASGI 服务器负责启动与监听端口transformers4.46.2加载 Qwen2-VL 模型与 Processoraccelerate1.1.1支持device_mapauto自动分配设备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, revisionmaster)参数说明第一个参数Qwen/Qwen2-VL-2B-Instruct模型名称对应 ModelScope 上的模型仓库标识cache_dir/root/autodl-tmp模型下载的本地缓存路径请务必修改为你自己的模型下载路径revisionmaster指定模型分支版本。该文件在参考代码目录中同样提供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_dtypeauto, device_mapauto ) 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, tokenizeFalse, add_generation_promptTrue ) # 预先写好的辅助函数位于参考代码中 image_inputs, video_inputs process_vision_info(chat_message.messages) inputs processor( text[text], imagesimage_inputs, videosvideo_inputs, paddingTrue, return_tensorspt, ) inputs inputs.to(cuda) # 生成输出 generated_ids model.generate(**inputs, max_new_tokens1024) 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_tokensTrue, clean_up_tokenization_spacesFalse ) return {response: output_text[0]} if __name__ __main__: # 启动FastAPI应用端口为8000 uvicorn.run(app, host0.0.0.0, port8000)4.1 关键代码逐段拆解1模型与处理器初始化model Qwen2VLForConditionalGeneration.from_pretrained( model_name_or_path, torch_dtypeauto, device_mapauto ) processor AutoProcessor.from_pretrained(model_name_or_path)torch_dtypeauto自动选用模型权重自身的精度如 bfloat16避免默认 FP32 带来的显存浪费device_mapauto由 accelerate 自动将模型分配到可用设备GPU/CPU可降低显存不足的风险模型放在全局作用域初始化服务启动时只加载一次避免每个请求都重新加载权重这是 FastAPI 部署 LLM 的标准做法AutoProcessor负责将文本与视觉输入编码为模型所需的多模态张量。2对话模板组装text processor.apply_chat_template( chat_message.messages, tokenizeFalse, add_generation_promptTrue )apply_chat_template将 OpenAI 风格的messages列表rolecontent转换为 Qwen2-VL 的对话格式add_generation_promptTrue会在末尾追加生成提示符。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_tokens1024)max_new_tokens1024限制最多生成 1024 个新 token可根据实际场景调整数值越大回复越长推理耗时越长。随后通过切片去掉输入 token仅保留新生成的输出并batch_decode解码为文本。5服务启动uvicorn.run(app, host0.0.0.0, port8000)监听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, jsonpayload) 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 dogs 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 womans 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_dtypeauto, device_mapauto ) 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, tokenizeFalse, add_generation_promptTrue ) image_inputs, video_inputs process_vision_info(chat_message.messages) inputs processor( text[text], imagesimage_inputs, videosvideo_inputs, paddingTrue, return_tensorspt, ) inputs inputs.to(cuda) # 生成输出 generated_ids model.generate(**inputs, max_new_tokens1024) 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_tokensTrue, clean_up_tokenization_spacesFalse ) return {response: output_text[0]} if __name__ __main__: uvicorn.run(app, host0.0.0.0, port8000)与图像版本相比唯一的改动就是MessageContent中新增了video: str None字段其余逻辑完全复用——这得益于process_vision_info已经统一处理了图像与视频两条分支服务端无需为视频单独编写处理逻辑。6.1 视频问答服务启动python api_server_image_and_video.py加载完毕后出现如下信息说明服务启动成功6.2 视频请求测试使用requests库发送视频问答请求参考代码见 fastapi_request_video.pyimport 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, jsonpayload) print(response.json())代码中messages.content添加了一个视频元素type: videovideo字段指向本地视频文件路径。执行请求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 centers 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_resizefetch_image支持四种图像输入来源本地路径、http(s)://URL、file://前缀路径、data:imagebase64 编码以及直接的PIL.Image对象对应fetch_image中image.startswith(http://) or image.startswith(https://)等分支。教程请求示例中传入的https://...demo.jpeg正是通过requests.get(image, streamTrue).raw流式读取的。smart_resize负责智能缩放保证满足三个约束宽高都能被factor默认 28整除总像素数落在[min_pixels, max_pixels]区间内尽可能保持原始宽高比。其核心逻辑先对宽高做round_by_factor对齐若乘积超过max_pixels则按面积比例缩小floor_by_factor若不足min_pixels则按比例放大ceil_by_factor。这也是 Qwen2-VL 对任意分辨率、任意宽高比图像原生支持的关键所在——无需统一缩放到固定尺寸避免信息丢失。若宽高比超过MAX_RATIO200会直接抛出异常。7.2 视频处理fetch_video 与 smart_nframes视频路径处理分两步第一步选择解码后端。get_video_reader_backend通过lru_cache缓存结果优先使用decord若已安装否则回退到torchvision的io.read_video。也可以设置环境变量FORCE_QWENVL_VIDEO_READER强制指定后端。两者均将视频读取为(T, C, H, W)张量。第二步计算抽帧数。smart_nframes支持两种配置方式nframes直接指定抽取帧数会取偶数对齐fps按帧率抽帧默认FPS 2.0即每秒抽取 2 帧帧数被限制在[FPS_MIN_FRAMES, FPS_MAX_FRAMES]4768之间并取偶数。随后通过torch.linspace(0, total_frames - 1, nframes)均匀取帧索引保证抽帧在时间轴上均匀分布。视频帧同样经过smart_resize缩放并使用双三次插值InterpolationMode.BICUBIC与antialiasTrue抗锯齿处理。7.3 入口函数 process_vision_infodef 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遍历对话内容收集所有含image、image_url、video的元素再分别调用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视频测试样例完成上述所有教程后的目录结构应类似下图具体文件路径请根据实际存放情况修正建议初学者先理解各文件的职责与调用关系再结合自身路径动手实践避免盲目照抄路径导致报错。九、常见问题与注意事项模型路径必须修改model_download.py中的cache_dir与api_server_image.py中的model_name_or_path都要改成你自己的实际路径否则会加载失败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的启动目录正确视频解码依赖torchvision0.19.0与av13.1.0是视频读取的关键依赖版本过低可能导致http/https视频路径不受支持源码中已明确提示torchvision 0.19.0不支持网络视频路径显存占用模型在全局加载一次多请求共享同一份权重若显存紧张可关注torch_dtype与device_map的配置或减少并发请求数网络图片的可用性请求测试使用了在线示例图片若网络受限可改为本地图片路径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),仅供参考