RTX4090双卡本地布署QwenImage模型并生成OpenAI API - yi

news/2025/9/21 14:54:22/文章来源:https://www.cnblogs.com/yisheng163/p/19103667

 

 

#新建man.py文件,加入代码

from modelscope import DiffusionPipeline, FlowMatchEulerDiscreteScheduler, snapshot_download
import torch
import math
import os
from pathlib import Path
from datetime import datetime
import base64
from io import BytesIO
from typing import Optional, List, Union
import time
import uuid
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import uvicorn# 设置HF_ENDPOINT使用国内镜像加速模型下载
os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"# 减少显存碎片化
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"# 启用推理模式,减少显存占用
torch.inference_mode()# 定义请求和响应模型
class ImageGenerationRequest(BaseModel):prompt: strnegative_prompt: Optional[str] = ""width: Optional[int] = 1024height: Optional[int] = 1024num_inference_steps: Optional[int] = 8seed: Optional[int] = 0class ImageData(BaseModel):b64_json: strrevised_prompt: strclass ImageGenerationResponse(BaseModel):created: intdata: List[ImageData]# 初始化模型
def initialize_model():print("=" * 50)print("开始初始化模型...")print("=" * 50)scheduler_config = {'base_image_seq_len': 256,'base_shift': math.log(3),'invert_sigmas': False,'max_image_seq_len': 8192,'max_shift': math.log(3),'num_train_timesteps': 1000,'shift': 1.0,'shift_terminal': None,'stochastic_sampling': False,'time_shift_type': 'exponential','use_beta_sigmas': False,'use_dynamic_shifting': True,'use_exponential_sigmas': False,'use_karras_sigmas': False,}print("正在配置调度器...")scheduler = FlowMatchEulerDiscreteScheduler.from_config(scheduler_config)print("调度器配置完成")# 检测可用GPU数量num_gpus = torch.cuda.device_count()print(f"检测到 {num_gpus} 个GPU设备")# 根据项目规范,对于DiffusionPipeline模型,我们使用max_memory参数进行显存分配# 而不是手动指定每层的设备映射if num_gpus > 1:print("检测到多个GPU,正在进行显存分配...")# 获取每个GPU的显存信息并计算可分配的显存量max_memory = {}for i in range(num_gpus):free_mem, total_mem = torch.cuda.mem_get_info(i)# 按70%的空闲显存计算分配量,同时确保不超过22GBallocated_mem = min(int(free_mem * 0.7), 22 * 1024**3, free_mem)max_memory[i] = allocated_memprint(f"GPU {i}: 分配 {(allocated_mem / 1024**3):.2f} GB 显存")# 加载模型并指定显存分配print("正在加载模型到多个GPU...")pipe = DiffusionPipeline.from_pretrained('Qwen/Qwen-Image',scheduler=scheduler,torch_dtype=torch.bfloat16,max_memory=max_memory,  # 为每个GPU分配显存
        )print("多GPU模型加载完成")else:# 单GPU情况print("检测到单个GPU,正在加载模型...")pipe = DiffusionPipeline.from_pretrained('Qwen/Qwen-Image',scheduler=scheduler,torch_dtype=torch.bfloat16,)pipe = pipe.to("cuda")print("单GPU模型加载完成")print(f"模型已分配到{num_gpus}个GPU设备上")print("=" * 50)print("模型初始化完成")print("=" * 50)return pipe# 提前下载LoRA权重
def download_lora_weights():print("=" * 50)print("开始下载LoRA权重...")print("=" * 50)# 使用ModelScope的snapshot_download下载LoRA权重model_dir = snapshot_download('lightx2v/Qwen-Image-Lightning')print(f"LoRA权重已下载到: {model_dir}")# 查找.pt或.safetensors文件lora_files = list(Path(model_dir).glob("*.safetensors")) + list(Path(model_dir).glob("*.pt"))if not lora_files:raise FileNotFoundError("在下载的LoRA权重目录中未找到.safetensors或.pt文件")lora_file_path = lora_files[0]  # 使用找到的第一个文件print(f"使用LoRA文件: {lora_file_path}")print("=" * 50)print("LoRA权重下载完成")print("=" * 50)return str(lora_file_path)# 图像生成函数
def generate_image(request: ImageGenerationRequest, pipe):"""生成图像"""print("=" * 50)print("开始生成图像...")print("=" * 50)print(f"输入参数:")print(f"  - 提示词: {request.prompt}")print(f"  - 负面提示词: {request.negative_prompt}")print(f"  - 图像尺寸: {request.width}x{request.height}")print(f"  - 推理步数: {request.num_inference_steps}")print(f"  - 随机种子: {request.seed}")start_time = time.time()print("开始图像生成过程...")print("正在调用模型生成图像...")image = pipe(prompt=request.prompt,negative_prompt=request.negative_prompt,width=request.width,height=request.height,num_inference_steps=request.num_inference_steps,true_cfg_scale=1.0,generator=torch.manual_seed(request.seed)).images[0]generation_time = time.time() - start_timeprint(f"图像生成完成,耗时: {generation_time:.2f} 秒")print("正在将图像转换为base64编码...")# 将图像转换为base64编码buffered = BytesIO()image.save(buffered, format="PNG")img_str = base64.b64encode(buffered.getvalue()).decode()print("图像转换完成")print("=" * 50)print("图像生成流程结束")print("=" * 50)return img_str# 初始化FastAPI应用
app = FastAPI(title="Qwen-Image API",description="基于Qwen-Image模型的OpenAI兼容图像生成API",version="1.0.0"
)# 全局模型实例
pipe = None@app.on_event("startup")
async def startup_event():global pipe# 初始化模型pipe = initialize_model()# 下载LoRA权重lora_file_path = download_lora_weights()print("正在加载LoRA权重...")pipe.load_lora_weights(lora_file_path)print("LoRA权重加载完成")@app.post("/v1/images/generations", response_model=ImageGenerationResponse)
async def create_image(request: ImageGenerationRequest):try:# 生成图像image_data = generate_image(request, pipe)# 构造响应response = ImageGenerationResponse(created=int(time.time()),data=[ImageData(b64_json=image_data,revised_prompt=request.prompt)])return responseexcept Exception as e:raise HTTPException(status_code=500, detail=str(e))if __name__ == "__main__":uvicorn.run(app, host="0.0.0.0", port=8800)

 

测试调用

curl -X POST http://localhost:8800/v1/images/generations -H "Content-Type: application/json" -d '{"prompt": "a lovely cat"}' | grep -q '"data"' && echo "API 工作正常" || echo "API 出现问题"

 

调用时控制台截图:

image

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/908819.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Visual Studio 报错:“9_自定义命令”名称在默认命名空间“9_自定义命令”中无效。请更正项目文件中的 RootNamespace 标记值。

当修改项目文件名后,可能会遇到类似上面的报错,这时,需要在项目上点击右键,选择属性,在【默认名称空间】中,修改为程序中实际使用的名称空间(namespace)最后再保存就可以了。

ubuntu22.04下搭建iRedMail邮件服务器 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

图解23:datetime和timestamp的区别

除了之前开发国际化项目使用timestamp时区的特性,其他基本都是使用datetime

深入解析:SQL语句优化的步骤详解

深入解析:SQL语句优化的步骤详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco&q…

在Java中识别泛型信息

Java中泛型信息在编译时由于类型擦除的原因,通常是不可获取的。类型擦除是Java在编译泛型代码时采取的一种措施,用于保证新的泛型代码能与旧的非泛型代码兼容。它将泛型类型参数替换为它们的边界,如果类型参数是无边…

Kali Linux 光标与快捷键全攻略

Kali Linux 光标与快捷键全攻略1️⃣ 终端(Bash / Zsh / XFCE Terminal / GNOME Terminal) 光标移动快捷键 功能Ctrl + A 移到行首Ctrl + E 移到行尾Alt + B 向左移动一个单词Alt + F 向右移动一个单词Ctrl + B 向左…

图解22:扩展系统的最佳8种策略

进几年都是使用阿里云服务,扩展方面自己操心的也相对较少了

Winform项目添加WPF

#####愿你一寸一寸地攻城略地,一点一点地焕然一新#####

本地免费使用网页表格控件websheet

​ websheet本地localhost免费使用 代码样例地址: http://www.websheet.cn/xlsx/ 1.0.3 版本主要更新内容: 设置行隐藏下面代码隐藏第一行和第五行,行代码如下:let yourElement = document.getElementById(&quo…

图解21:Redis为什么这么快

使用AI处理了,看看豆包刚上线的4.0处理图片的能力,所以没有那么清晰了,原图是比较高清的, 其实看到一些项目,只用String类型

Docker - ZZH Ubuntu Image - Desktop

Docker - ZZH Ubuntu Image - DesktopHere’s the final all-in-one Dockerfile for your ubuntu-zzh-vnc container, ready to build and run on Windows. It includes:XFCE desktop (lightweight)zsh + Oh My Zsh wi…

C#文件操作入门

Unity游戏开发中对于文件操作是一个不可或缺的部分,尤其是需要存取玩家数据、配置信息或进行资源管理时,C#的文件操作成为了开发者的重要工具。下面将详细介绍Unity中如何使用C#进行基础的文件操作。 首先,C#中Syst…

Java课前问题列表-面向对象入门2与类的识别

Java课前问题列表-面向对象入门2与类的识别1.关于static修饰 什么样的方法应该用 static 修饰? 答: 当一个方法满足以下条件时,应该考虑使用 static 修饰:不依赖于特定对象的实例状态:方法的行为不依赖于或修改其…

ES——(一)基本概念 - 指南

ES——(一)基本概念 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco"…

意义感是完全主观的

意义感是完全主观的意义感是完全主观的 意义感是完全主观的,是人自己构建的。 因为我存在,一切才有了意义。告诉你们一个秘密,其实这个世界只有你一个人,你在,世界就在;你没了,世界也就消失了。 优绩主义。 旅居…

python2.7+pandas

python2.7+pandas版本: Python 2.7.18 (v2.7.18:8d21aa21f2, Apr 20 2020, 13:25:05) [MSC v.1500 64 bit (AMD64)] on win32Type "help", "copyright", "credits" or "license&q…

SAP集成HTTP接口(x-www-form-urlencoded格式)

SAP集成HTTP接口是一个重要且常见的企业信息系统扩展方式,特别是当我们需要将SAP与外部系统进行数据交换时。在这种场景中,通常使用HTTP协议来实现不同系统间的通信和数据传输。HTTP协议支持多种数据编码格式,其中之…

iText与OpenPDF使用差异及中文处理完全指南 - 实践

iText与OpenPDF使用差异及中文处理完全指南 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas"…