千问图像生成16Bit(Qwen-Turbo-BF16)开源大模型部署教程:从PyTorch到Flask
本文介绍了如何在星图GPU平台上自动化部署千问图像生成 16Bit (Qwen-Turbo-BF16)镜像,快速搭建高性能AI绘画应用。该平台简化了部署流程,用户可轻松利用该镜像实现4步极速生成高质量图片,典型应用于数字艺术创作、社交媒体配图等场景。
千问图像生成16Bit(Qwen-Turbo-BF16)开源大模型部署教程:从PyTorch到Flask
想体验4步就能生成高清大图的AI绘画吗?还在为FP16精度生成“黑图”而烦恼?今天,我们就来手把手部署一个专为RTX 4090等现代显卡优化的高性能图像生成系统——千问图像生成16Bit(Qwen-Turbo-BF16)。
这个系统基于强大的Qwen-Image-2512模型,并集成了Wuli-Art Turbo LoRA,通过BFloat16(BF16)全链路推理,不仅解决了传统FP16的“黑图”和“溢出”问题,还能在保持16位精度高性能的同时,获得媲美32位精度的色彩表现。最吸引人的是,它只需要4步迭代就能输出1024x1024的高质量图像,真正实现了“秒级出图”。
无论你是AI绘画爱好者,还是希望将AI图像生成能力集成到自己应用中的开发者,这篇教程都将带你从零开始,完成从环境搭建到Web界面部署的全过程。
1. 系统核心亮点与准备工作
在开始动手之前,我们先快速了解一下这个系统的几个核心优势,这能帮你更好地理解后续的部署步骤。
1.1 为什么选择这个系统?
这个系统有几个让你无法拒绝的理由:
第一是速度极快。 传统的Stable Diffusion模型通常需要20-50步采样才能生成清晰的图像,而这个系统集成了Wuli-Art V3.0 Turbo LoRA,只需要4步迭代就能输出高质量的1024px图像。这意味着生成一张图的时间从几分钟缩短到了几十秒,体验上的提升是巨大的。
第二是稳定性强。 如果你用过其他16位精度的图像生成模型,可能遇到过生成的图片全黑或者颜色异常的问题。这是因为FP16精度范围有限,在复杂的计算过程中容易“溢出”。这个系统采用了BFloat16(BF16)数据类型,专门针对RTX 4000系列显卡优化,不仅节省显存,更重要的是大幅提升了数值稳定性,再复杂的提示词也不怕出“黑图”。
第三是界面美观易用。 系统采用了现代化的玻璃拟态设计,半透明的毛玻璃质感和动态流光背景看起来非常酷。交互布局参考了ChatGPT和Midjourney的习惯,输入框在底部,操作起来很顺手。更重要的是,它会自动保存当前会话生成的所有图片缩略图,你可以快速回溯查看历史作品。
1.2 部署前需要准备什么?
在开始部署之前,你需要确保满足以下条件:
硬件要求:
- 显卡:推荐RTX 4090(24GB显存),RTX 4080、RTX 3090等大显存显卡也可以
- 内存:至少32GB系统内存
- 存储:至少50GB可用磁盘空间(用于存放模型文件)
软件环境:
- 操作系统:Ubuntu 20.04/22.04或Windows 11(WSL2)
- Python:3.8-3.10版本
- CUDA:11.7或11.8(与你的PyTorch版本匹配)
如果你使用的是云服务器,确保选择带有RTX 4090等高性能显卡的实例。如果是本地部署,请提前更新显卡驱动到最新版本。
2. 环境搭建与依赖安装
现在我们来一步步搭建运行环境。整个过程分为三个主要步骤:安装基础依赖、配置Python环境、下载模型文件。
2.1 第一步:创建并激活Python虚拟环境
首先,我们创建一个独立的Python环境,避免与系统其他Python包产生冲突。
打开终端,执行以下命令:
# 创建项目目录
mkdir qwen-turbo-bf16 && cd qwen-turbo-bf16
# 创建Python虚拟环境(使用venv)
python3 -m venv venv
# 激活虚拟环境
# Linux/macOS
source venv/bin/activate
# Windows
venv\Scripts\activate
激活后,你的命令行提示符前面应该会出现(venv)字样,表示已经进入了虚拟环境。
2.2 第二步:安装PyTorch与核心依赖
接下来安装PyTorch和其他必要的Python包。这里需要特别注意PyTorch版本与CUDA版本的匹配。
# 安装PyTorch(根据你的CUDA版本选择)
# CUDA 11.8
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
# 或者CUDA 11.7
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117
# 安装Diffusers和Transformers(图像生成核心库)
pip install diffusers transformers accelerate
# 安装Flask(Web界面框架)
pip install flask
# 安装其他辅助库
pip install pillow requests tqdm
安装完成后,可以通过以下命令验证PyTorch是否正确识别了你的GPU:
import torch
print(f"PyTorch版本: {torch.__version__}")
print(f"CUDA可用: {torch.cuda.is_available()}")
print(f"GPU数量: {torch.cuda.device_count()}")
print(f"当前GPU: {torch.cuda.get_device_name(0)}")
如果一切正常,你应该能看到你的GPU型号被正确识别。
2.3 第三步:下载模型文件
模型文件比较大,我们需要从Hugging Face下载。系统需要两个主要模型:底座模型和Turbo LoRA。
from huggingface_hub import snapshot_download
import os
# 创建模型缓存目录
os.makedirs("models", exist_ok=True)
# 下载Qwen-Image-2512底座模型
print("正在下载Qwen-Image-2512底座模型...")
snapshot_download(
repo_id="Qwen/Qwen-Image-2512",
local_dir="models/Qwen-Image-2512",
local_dir_use_symlinks=False
)
# 下载Wuli-Art Turbo LoRA
print("正在下载Wuli-Art Turbo LoRA...")
snapshot_download(
repo_id="Wuli-Art/Qwen-Image-2512-Turbo-LoRA",
local_dir="models/Qwen-Turbo-LoRA",
local_dir_use_symlinks=False
)
print("模型下载完成!")
下载过程可能需要一些时间,Qwen-Image-2512大约15-20GB,Turbo LoRA大约200-300MB。请确保有足够的磁盘空间和稳定的网络连接。
如果下载速度较慢,可以考虑使用镜像源或者提前下载好模型文件,然后直接放到对应的目录中。
3. 核心代码解析与配置
环境准备好后,我们来编写系统的核心代码。主要分为三个部分:图像生成引擎、Web服务器、前端界面。
3.1 图像生成引擎(image_generator.py)
这是系统的核心,负责加载模型并生成图像。
import torch
from diffusers import StableDiffusionPipeline
from PIL import Image
import time
class QwenTurboBF16Generator:
def __init__(self, model_path="models/Qwen-Image-2512", lora_path="models/Qwen-Turbo-LoRA"):
"""初始化图像生成器"""
print("正在加载模型...")
start_time = time.time()
# 加载基础管道
self.pipe = StableDiffusionPipeline.from_pretrained(
model_path,
torch_dtype=torch.bfloat16, # 使用BF16精度
safety_checker=None, # 禁用安全检查器以提升速度
requires_safety_checker=False
)
# 加载Turbo LoRA
self.pipe.load_lora_weights(lora_path)
# 启用顺序CPU卸载(节省显存)
self.pipe.enable_sequential_cpu_offload()
# 启用VAE分块解码(支持大尺寸生成)
self.pipe.vae.enable_tiling()
# 将管道移动到GPU
self.pipe.to("cuda")
load_time = time.time() - start_time
print(f"模型加载完成,耗时: {load_time:.2f}秒")
def generate_image(self, prompt, negative_prompt="", steps=4, guidance_scale=1.8, width=1024, height=1024):
"""生成图像
参数:
prompt: 正面提示词
negative_prompt: 负面提示词
steps: 采样步数(默认4步)
guidance_scale: 指导尺度(默认1.8)
width: 图像宽度(默认1024)
height: 图像高度(默认1024)
"""
print(f"开始生成图像: {prompt[:50]}...")
# 设置生成参数
generator = torch.Generator(device="cuda").manual_seed(int(time.time()))
# 生成图像
with torch.autocast("cuda"):
image = self.pipe(
prompt=prompt,
negative_prompt=negative_prompt,
num_inference_steps=steps,
guidance_scale=guidance_scale,
width=width,
height=height,
generator=generator
).images[0]
return image
def generate_multiple(self, prompts, **kwargs):
"""批量生成图像"""
images = []
for i, prompt in enumerate(prompts):
print(f"生成第 {i+1}/{len(prompts)} 张图像...")
image = self.generate_image(prompt, **kwargs)
images.append(image)
return images
# 测试函数
def test_generation():
"""测试图像生成"""
generator = QwenTurboBF16Generator()
# 测试提示词
test_prompt = "A beautiful sunset over mountains, digital art, masterpiece, 8k resolution"
# 生成图像
image = generator.generate_image(test_prompt)
# 保存图像
image.save("test_output.jpg")
print("测试图像已保存为 test_output.jpg")
return image
if __name__ == "__main__":
test_generation()
这个类的关键点:
- 使用
torch.bfloat16数据类型,这是BF16精度的关键 - 加载LoRA权重来启用4步Turbo生成
- 启用顺序CPU卸载,让大模型能在24GB显存上流畅运行
- 启用VAE分块解码,支持生成大尺寸图像
3.2 Web服务器(app.py)
接下来我们创建Flask Web服务器,提供API接口和前端页面。
from flask import Flask, render_template, request, jsonify, send_file
from image_generator import QwenTurboBF16Generator
from PIL import Image
import io
import os
import uuid
from datetime import datetime
app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB限制
# 初始化图像生成器
generator = None
try:
generator = QwenTurboBF16Generator()
print("✅ 图像生成器初始化成功")
except Exception as e:
print(f"❌ 图像生成器初始化失败: {e}")
# 创建输出目录
output_dir = "static/generated"
os.makedirs(output_dir, exist_ok=True)
# 存储生成历史
generation_history = []
@app.route('/')
def index():
"""渲染主页面"""
return render_template('index.html', history=generation_history[-10:]) # 显示最近10条记录
@app.route('/generate', methods=['POST'])
def generate_image():
"""生成图像API"""
if generator is None:
return jsonify({"error": "图像生成器未初始化"}), 500
try:
# 获取请求参数
data = request.json
prompt = data.get('prompt', '')
negative_prompt = data.get('negative_prompt', '')
steps = int(data.get('steps', 4))
guidance_scale = float(data.get('guidance_scale', 1.8))
width = int(data.get('width', 1024))
height = int(data.get('height', 1024))
if not prompt:
return jsonify({"error": "提示词不能为空"}), 400
# 生成图像
print(f"收到生成请求: {prompt}")
image = generator.generate_image(
prompt=prompt,
negative_prompt=negative_prompt,
steps=steps,
guidance_scale=guidance_scale,
width=width,
height=height
)
# 保存图像
filename = f"{uuid.uuid4().hex[:8]}.jpg"
filepath = os.path.join(output_dir, filename)
image.save(filepath, quality=95)
# 记录生成历史
history_entry = {
"id": len(generation_history) + 1,
"prompt": prompt,
"negative_prompt": negative_prompt,
"filename": filename,
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"steps": steps,
"guidance_scale": guidance_scale,
"size": f"{width}x{height}"
}
generation_history.append(history_entry)
# 返回结果
return jsonify({
"success": True,
"filename": filename,
"url": f"/generated/{filename}",
"history": history_entry
})
except Exception as e:
print(f"生成图像时出错: {e}")
return jsonify({"error": str(e)}), 500
@app.route('/generated/<filename>')
def get_generated_image(filename):
"""获取生成的图像"""
filepath = os.path.join(output_dir, filename)
if os.path.exists(filepath):
return send_file(filepath, mimetype='image/jpeg')
return jsonify({"error": "图像不存在"}), 404
@app.route('/history')
def get_history():
"""获取生成历史"""
return jsonify(generation_history[-20:]) # 返回最近20条记录
@app.route('/batch_generate', methods=['POST'])
def batch_generate():
"""批量生成图像"""
if generator is None:
return jsonify({"error": "图像生成器未初始化"}), 500
try:
data = request.json
prompts = data.get('prompts', [])
if not prompts:
return jsonify({"error": "提示词列表不能为空"}), 400
results = []
for prompt in prompts:
image = generator.generate_image(prompt)
filename = f"{uuid.uuid4().hex[:8]}.jpg"
filepath = os.path.join(output_dir, filename)
image.save(filepath, quality=95)
results.append({
"prompt": prompt,
"filename": filename,
"url": f"/generated/{filename}"
})
return jsonify({"success": True, "results": results})
except Exception as e:
return jsonify({"error": str(e)}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
这个Web服务器提供了几个关键功能:
- 主页展示和图像生成界面
- 单张图像生成API
- 批量图像生成API
- 生成历史记录和查看功能
3.3 前端界面(templates/index.html)
最后,我们创建一个美观的前端界面。这里使用简单的HTML、CSS和JavaScript实现。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>千问图像生成16Bit (Qwen-Turbo-BF16)</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
background: linear-gradient(135deg, #0f0c29, #302b63, #24243e);
color: #fff;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
}
.header {
grid-column: 1 / -1;
text-align: center;
margin-bottom: 30px;
padding: 20px;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.header h1 {
font-size: 2.5em;
margin-bottom: 10px;
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.badges {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 15px;
}
.badge {
padding: 5px 15px;
border-radius: 20px;
font-size: 0.9em;
font-weight: bold;
}
.badge.version { background: #8a2be2; }
.badge.hardware { background: #28a745; }
.badge.precision { background: #fd7e14; }
.badge.framework { background: #007bff; }
.input-panel {
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(10px);
border-radius: 15px;
padding: 25px;
border: 1px solid rgba(255, 255, 255, 0.15);
}
.output-panel {
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(10px);
border-radius: 15px;
padding: 25px;
border: 1px solid rgba(255, 255, 255, 0.15);
display: flex;
flex-direction: column;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #4ecdc4;
}
textarea, input, select {
width: 100%;
padding: 12px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
color: #fff;
font-size: 14px;
resize: vertical;
}
textarea:focus, input:focus, select:focus {
outline: none;
border-color: #4ecdc4;
box-shadow: 0 0 0 2px rgba(78, 205, 196, 0.2);
}
.params-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-top: 20px;
}
.btn {
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
color: white;
border: none;
padding: 14px 28px;
border-radius: 8px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
width: 100%;
margin-top: 10px;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.image-container {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.3);
border-radius: 10px;
overflow: hidden;
min-height: 400px;
}
#generatedImage {
max-width: 100%;
max-height: 500px;
border-radius: 8px;
display: none;
}
.loading {
display: none;
text-align: center;
color: #4ecdc4;
}
.loading-spinner {
border: 3px solid rgba(255, 255, 255, 0.1);
border-top: 3px solid #4ecdc4;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.history {
grid-column: 1 / -1;
margin-top: 30px;
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(10px);
border-radius: 15px;
padding: 25px;
border: 1px solid rgba(255, 255, 255, 0.15);
}
.history h3 {
margin-bottom: 15px;
color: #ff6b6b;
}
.history-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 15px;
}
.history-item {
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
overflow: hidden;
cursor: pointer;
transition: transform 0.2s;
}
.history-item:hover {
transform: translateY(-5px);
}
.history-img {
width: 100%;
height: 150px;
object-fit: cover;
}
.history-prompt {
padding: 10px;
font-size: 12px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.status {
padding: 10px;
border-radius: 8px;
margin-top: 15px;
display: none;
}
.status.success {
background: rgba(40, 167, 69, 0.2);
border: 1px solid #28a745;
}
.status.error {
background: rgba(220, 53, 69, 0.2);
border: 1px solid #dc3545;
}
@media (max-width: 768px) {
.container {
grid-template-columns: 1fr;
}
.params-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🎨 千问图像生成16Bit (Qwen-Turbo-BF16)</h1>
<p>基于 Qwen-Image-2512 与 Wuli-Art Turbo LoRA 的高性能图像生成系统</p>
<div class="badges">
<span class="badge version">Version 3.0</span>
<span class="badge hardware">RTX 4090</span>
<span class="badge precision">BFloat16</span>
<span class="badge framework">Flask + PyTorch</span>
</div>
</div>
<div class="input-panel">
<h2>🖋️ 提示词输入</h2>
<div class="form-group">
<label for="prompt">正面提示词 (必填)</label>
<textarea id="prompt" rows="4" placeholder="描述你想要生成的图像...">A beautiful sunset over mountains, digital art, masterpiece, 8k resolution</textarea>
</div>
<div class="form-group">
<label for="negativePrompt">负面提示词 (可选)</label>
<textarea id="negativePrompt" rows="2" placeholder="描述你不希望在图像中出现的内容...">blurry, low quality, watermark, text</textarea>
</div>
<div class="params-grid">
<div class="form-group">
<label for="steps">采样步数</label>
<select id="steps">
<option value="4" selected>4步 (Turbo模式)</option>
<option value="8">8步</option>
<option value="12">12步</option>
<option value="20">20步</option>
</select>
</div>
<div class="form-group">
<label for="guidance">指导尺度</label>
<input type="number" id="guidance" value="1.8" step="0.1" min="1.0" max="10.0">
</div>
<div class="form-group">
<label for="width">图像宽度</label>
<select id="width">
<option value="512">512px</option>
<option value="768">768px</option>
<option value="1024" selected>1024px</option>
</select>
</div>
<div class="form-group">
<label for="height">图像高度</label>
<select id="height">
<option value="512">512px</option>
<option value="768">768px</option>
<option value="1024" selected>1024px</option>
</select>
</div>
</div>
<button class="btn" onclick="generateImage()" id="generateBtn">
🚀 生成图像 (4步极速)
</button>
<div class="loading" id="loading">
<div class="loading-spinner"></div>
<p>正在生成图像中... 通常需要10-30秒</p>
</div>
<div class="status" id="status"></div>
</div>
<div class="output-panel">
<h2>🖼️ 生成结果</h2>
<div class="image-container">
<img id="generatedImage" alt="生成的图像">
<p id="placeholder">图像将在这里显示</p>
</div>
<div class="form-group" style="margin-top: 20px;">
<label>图像信息</label>
<div id="imageInfo" style="padding: 10px; background: rgba(255,255,255,0.05); border-radius: 8px;">
等待生成...
</div>
</div>
<button class="btn" onclick="downloadImage()" id="downloadBtn" style="display: none;">
💾 下载图像
</button>
</div>
<div class="history">
<h3>📜 生成历史</h3>
<div class="history-grid" id="historyGrid">
<!-- 历史记录将通过JavaScript动态加载 -->
</div>
</div>
</div>
<script>
let currentImageUrl = '';
let currentFilename = '';
async function generateImage() {
const prompt = document.getElementById('prompt').value.trim();
if (!prompt) {
showStatus('请输入提示词', 'error');
return;
}
// 显示加载状态
document.getElementById('loading').style.display = 'block';
document.getElementById('generateBtn').disabled = true;
document.getElementById('status').style.display = 'none';
const data = {
prompt: prompt,
negative_prompt: document.getElementById('negativePrompt').value,
steps: parseInt(document.getElementById('steps').value),
guidance_scale: parseFloat(document.getElementById('guidance').value),
width: parseInt(document.getElementById('width').value),
height: parseInt(document.getElementById('height').value)
};
try {
const response = await fetch('/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
const result = await response.json();
if (result.success) {
// 显示生成的图像
currentImageUrl = result.url;
currentFilename = result.filename;
const img = document.getElementById('generatedImage');
img.src = result.url + '?t=' + new Date().getTime(); // 添加时间戳避免缓存
img.style.display = 'block';
document.getElementById('placeholder').style.display = 'none';
// 显示图像信息
document.getElementById('imageInfo').innerHTML = `
<strong>提示词:</strong> ${result.history.prompt}<br>
<strong>时间:</strong> ${result.history.timestamp}<br>
<strong>参数:</strong> ${result.history.steps}步, 尺度${result.history.guidance_scale}, 尺寸${result.history.size}
`;
// 显示下载按钮
document.getElementById('downloadBtn').style.display = 'block';
// 更新历史记录
loadHistory();
showStatus('图像生成成功!', 'success');
} else {
showStatus('生成失败: ' + (result.error || '未知错误'), 'error');
}
} catch (error) {
showStatus('请求失败: ' + error.message, 'error');
} finally {
// 隐藏加载状态
document.getElementById('loading').style.display = 'none';
document.getElementById('generateBtn').disabled = false;
}
}
function downloadImage() {
if (!currentImageUrl) return;
const link = document.createElement('a');
link.href = currentImageUrl;
link.download = currentFilename || 'generated_image.jpg';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
function showStatus(message, type) {
const statusEl = document.getElementById('status');
statusEl.textContent = message;
statusEl.className = 'status ' + type;
statusEl.style.display = 'block';
// 3秒后自动隐藏
setTimeout(() => {
statusEl.style.display = 'none';
}, 3000);
}
async function loadHistory() {
try {
const response = await fetch('/history');
const history = await response.json();
const historyGrid = document.getElementById('historyGrid');
historyGrid.innerHTML = '';
// 只显示最近9条记录
const recentHistory = history.slice(-9).reverse();
recentHistory.forEach(item => {
const historyItem = document.createElement('div');
historyItem.className = 'history-item';
historyItem.onclick = () => {
document.getElementById('prompt').value = item.prompt;
document.getElementById('negativePrompt').value = item.negative_prompt || '';
};
historyItem.innerHTML = `
<img src="/generated/${item.filename}" class="history-img" alt="${item.prompt}">
<div class="history-prompt">${item.prompt.substring(0, 30)}${item.prompt.length > 30 ? '...' : ''}</div>
`;
historyGrid.appendChild(historyItem);
});
} catch (error) {
console.error('加载历史记录失败:', error);
}
}
// 页面加载时获取历史记录
document.addEventListener('DOMContentLoaded', loadHistory);
// 示例提示词
const examplePrompts = [
"A futuristic cyberpunk city street at night, heavy rain, neon signs reflecting on wet ground, cinematic lighting",
"A beautiful Chinese goddess in flowing silk hanfu, standing on a lotus leaf, ethereal atmosphere, golden sunset light",
"Epic landscape of a floating castle above the clouds, giant waterfalls, dragons flying, cinematic scale",
"Close-up portrait of an elderly craftsman with deep wrinkles, hyper-realistic skin texture, bokeh background"
];
// 随机选择一个示例提示词
document.getElementById('prompt').value = examplePrompts[Math.floor(Math.random() * examplePrompts.length)];
</script>
</body>
</html>
这个界面采用了现代化的玻璃拟态设计,主要特点包括:
- 渐变背景和毛玻璃效果
- 响应式布局,适配不同屏幕尺寸
- 实时生成状态显示
- 生成历史记录浏览
- 一键下载功能
4. 部署与启动脚本
所有代码准备好后,我们需要创建启动脚本和配置文件,让部署更加简单。
4.1 创建启动脚本(start.sh)
为了方便启动服务,我们创建一个Shell脚本:
#!/bin/bash
# 千问图像生成16Bit启动脚本
echo "========================================"
echo " 千问图像生成16Bit (Qwen-Turbo-BF16)"
echo "========================================"
echo ""
# 检查Python环境
if ! command -v python3 &> /dev/null; then
echo "❌ 未找到Python3,请先安装Python3.8或更高版本"
exit 1
fi
# 检查CUDA
if ! command -v nvidia-smi &> /dev/null; then
echo "⚠️ 未检测到NVIDIA显卡驱动,将使用CPU模式(性能较差)"
export CUDA_VISIBLE_DEVICES=""
else
echo "✅ 检测到NVIDIA显卡"
nvidia-smi --query-gpu=name,memory.total --format=csv
fi
# 检查虚拟环境
if [ ! -d "venv" ]; then
echo "📦 创建Python虚拟环境..."
python3 -m venv venv
fi
# 激活虚拟环境
echo "🔧 激活虚拟环境..."
source venv/bin/activate
# 安装依赖
echo "📥 安装Python依赖..."
pip install -r requirements.txt
# 检查模型文件
echo "🔍 检查模型文件..."
if [ ! -d "models/Qwen-Image-2512" ]; then
echo "⚠️ 未找到Qwen-Image-2512模型,请先下载模型文件"
echo "运行: python download_models.py"
read -p "是否现在下载?(y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
python download_models.py
else
echo "请手动下载模型文件后重试"
exit 1
fi
fi
if [ ! -d "models/Qwen-Turbo-LoRA" ]; then
echo "⚠️ 未找到Turbo LoRA模型,请先下载模型文件"
echo "运行: python download_models.py"
read -p "是否现在下载?(y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
python download_models.py
else
echo "请手动下载模型文件后重试"
exit 1
fi
fi
# 创建必要的目录
echo "📁 创建目录结构..."
mkdir -p static/generated
mkdir -p templates
# 启动服务
echo "🚀 启动图像生成服务..."
echo "访问地址: http://localhost:5000"
echo "按 Ctrl+C 停止服务"
echo ""
python app.py
# 服务停止后
deactivate
echo "服务已停止"
4.2 创建依赖文件(requirements.txt)
torch==2.1.0
torchvision==0.16.0
torchaudio==2.1.0
diffusers==0.24.0
transformers==4.36.0
accelerate==0.25.0
flask==3.0.0
pillow==10.1.0
huggingface-hub==0.19.4
4.3 创建模型下载脚本(download_models.py)
#!/usr/bin/env python3
"""
模型下载脚本
用于下载Qwen-Image-2512和Turbo LoRA模型
"""
import os
from huggingface_hub import snapshot_download
import sys
def download_model(repo_id, local_dir, description):
"""下载模型文件"""
print(f"开始下载: {description}")
print(f"仓库ID: {repo_id}")
print(f"保存到: {local_dir}")
print("-" * 50)
try:
snapshot_download(
repo_id=repo_id,
local_dir=local_dir,
local_dir_use_symlinks=False,
resume_download=True
)
print(f"✅ {description} 下载完成")
return True
except Exception as e:
print(f"❌ {description} 下载失败: {e}")
return False
def main():
"""主函数"""
print("=" * 60)
print("千问图像生成16Bit - 模型下载工具")
print("=" * 60)
print()
# 创建模型目录
os.makedirs("models", exist_ok=True)
# 下载Qwen-Image-2512
success1 = download_model(
repo_id="Qwen/Qwen-Image-2512",
local_dir="models/Qwen-Image-2512",
description="Qwen-Image-2512 底座模型"
)
print()
# 下载Turbo LoRA
success2 = download_model(
repo_id="Wuli-Art/Qwen-Image-2512-Turbo-LoRA",
local_dir="models/Qwen-Turbo-LoRA",
description="Wuli-Art Turbo LoRA"
)
print()
print("=" * 60)
if success1 and success2:
print("🎉 所有模型下载完成!")
print("现在可以运行: bash start.sh 启动服务")
else:
print("⚠️ 部分模型下载失败,请检查网络连接后重试")
print("或者手动下载模型文件到对应目录")
sys.exit(1)
if __name__ == "__main__":
main()
4.4 项目结构整理
最终的项目目录结构应该是这样的:
qwen-turbo-bf16/
├── app.py # Flask Web服务器
├── image_generator.py # 图像生成引擎
├── download_models.py # 模型下载脚本
├── start.sh # 启动脚本
├── requirements.txt # Python依赖
├── models/ # 模型文件目录
│ ├── Qwen-Image-2512/ # 底座模型
│ └── Qwen-Turbo-LoRA/ # Turbo LoRA
├── static/
│ └── generated/ # 生成的图像
├── templates/
│ └── index.html # 前端界面
└── venv/ # Python虚拟环境
5. 运行与使用指南
一切准备就绪后,让我们启动服务并开始使用。
5.1 启动服务
在项目根目录下,给启动脚本添加执行权限并运行:
# 添加执行权限
chmod +x start.sh
# 启动服务
./start.sh
脚本会自动执行以下步骤:
- 检查Python和CUDA环境
- 创建并激活虚拟环境
- 安装所有依赖包
- 检查模型文件(如果不存在会提示下载)
- 启动Flask服务器
看到以下输出表示启动成功:
🚀 启动图像生成服务...
访问地址: http://localhost:5000
按 Ctrl+C 停止服务
5.2 使用Web界面
打开浏览器,访问 http://localhost:5000,你会看到美观的生成界面。
基本使用步骤:
- 输入提示词:在"正面提示词"框中描述你想要生成的图像
- 设置参数(可选):
- 负面提示词:描述不希望出现的内容
- 采样步数:推荐使用4步(Turbo模式)
- 指导尺度:控制生成与提示词的匹配程度,默认1.8
- 图像尺寸:选择生成图像的大小
- 点击生成:点击"🚀 生成图像"按钮
- 查看结果:等待10-30秒,图像会显示在右侧
- 下载图像:点击"💾 下载图像"保存结果
5.3 提示词技巧
为了获得最佳效果,这里有一些提示词写作技巧:
基础结构:
[主体描述], [风格描述], [质量词], [技术参数]
示例:
- 写实人像:
A portrait of a wise old man with wrinkles, detailed skin texture, studio lighting, 8k resolution, photorealistic - 动漫风格:
Anime girl with blue hair, wearing school uniform, cherry blossoms in background, anime style, masterpiece, vibrant colors - 风景摄影:
Mountain landscape at sunrise, misty valley, golden hour lighting, national geographic photo, 8k, ultra detailed
常用质量词:
masterpiece, best quality- 提升整体质量8k, ultra detailed- 增加细节cinematic lighting- 电影感光照sharp focus- 清晰对焦professional photography- 专业摄影风格
5.4 高级功能
批量生成: 系统支持批量生成,你可以通过API一次性生成多张图像:
import requests
import json
# 批量生成API
url = "http://localhost:5000/batch_generate"
prompts = [
"A beautiful sunset over mountains",
"A cyberpunk city at night",
"A fantasy castle in the clouds"
]
response = requests.post(url, json={"prompts": prompts})
results = response.json()
for result in results["results"]:
print(f"提示词: {result['prompt']}")
print(f"图像URL: http://localhost:5000{result['url']}")
API调用: 你也可以直接调用生成API,集成到自己的应用中:
import requests
def generate_via_api(prompt, negative_prompt="", steps=4):
url = "http://localhost:5000/generate"
data = {
"prompt": prompt,
"negative_prompt": negative_prompt,
"steps": steps,
"guidance_scale": 1.8,
"width": 1024,
"height": 1024
}
response = requests.post(url, json=data)
return response.json()
# 使用示例
result = generate_via_api("A beautiful digital art of a forest")
if result["success"]:
image_url = f"http://localhost:5000{result['url']}"
print(f"图像生成成功: {image_url}")
6. 常见问题与优化建议
在部署和使用过程中,你可能会遇到一些问题。这里整理了一些常见问题的解决方法。
6.1 常见问题解答
Q: 启动时显示"CUDA不可用"怎么办? A: 检查以下几点:
- 确认已安装NVIDIA显卡驱动:
nvidia-smi - 确认已安装CUDA工具包:
nvcc --version - 确认PyTorch版本与CUDA版本匹配
- 如果确实没有GPU,系统会自动回退到CPU模式,但速度会很慢
Q: 模型下载太慢或失败怎么办? A: 可以尝试以下方法:
- 使用国内镜像源:修改
download_models.py中的下载地址 - 手动下载:从Hugging Face网站直接下载模型文件,放到对应目录
- 使用代理:设置HTTP_PROXY环境变量
Q: 生成图像时显存不足怎么办? A: 系统已经内置了显存优化,但如果还是不足,可以:
- 减小生成图像尺寸(如从1024x1024降到768x768)
- 启用更激进的CPU卸载:在代码中启用
enable_model_cpu_offload() - 使用更低精度的VAE解码
Q: 生成的图像质量不理想怎么办? A: 尝试以下优化:
- 使用更详细的提示词,包含风格、质量、光照等描述
- 适当增加采样步数(如从4步增加到8步)
- 调整指导尺度(1.5-2.5之间尝试)
- 添加负面提示词排除不想要的内容
6.2 性能优化建议
针对RTX 4090的优化:
# 在image_generator.py中添加以下优化
def optimize_for_4090(pipe):
"""针对RTX 4090的优化设置"""
# 启用TF32精度(RTX 30/40系列支持)
torch.backends.cuda.matmul.allow_tf32 = True
torch.backends.cudnn.allow_tf32 = True
# 使用更快的注意力机制
pipe.enable_xformers_memory_efficient_attention()
# 启用VAE切片解码,进一步节省显存
pipe.vae.enable_slicing()
# 设置更快的调度器
from diffusers import DPMSolverMultistepScheduler
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
return pipe
批量生成优化: 如果你需要批量生成图像,可以修改代码启用批处理:
# 修改generate_multiple方法,启用批处理
def generate_batch(self, prompts, batch_size=2, **kwargs):
"""批量生成图像(批处理优化版)"""
images = []
for i in range(0, len(prompts), batch_size):
batch_prompts = prompts[i:i+batch_size]
print(f"生成批次 {i//batch_size + 1}/{(len(prompts)+batch_size-1)//batch_size}")
# 批处理生成
with torch.autocast("cuda"):
batch_images = self.pipe(
prompt=batch_prompts,
num_inference_steps=kwargs.get('steps', 4),
guidance_scale=kwargs.get('guidance_scale', 1.8),
width=kwargs.get('width', 1024),
height=kwargs.get('height', 1024)
).images
images.extend(batch_images)
return images
6.3 监控与日志
添加监控功能,帮助了解系统运行状态:
# 在app.py中添加监控端点
@app.route('/status')
def system_status():
"""系统状态监控"""
import psutil
import torch
status = {
"system": {
"cpu_percent": psutil.cpu_percent(),
"memory_percent": psutil.virtual_memory().percent,
"disk_usage": psutil.disk_usage('/').percent
},
"gpu": {
"available": torch.cuda.is_available(),
"device_count": torch.cuda.device_count() if torch.cuda.is_available() else 0,
"current_device": torch.cuda.get_device_name(0) if torch.cuda.is_available() else None,
"memory_allocated": torch.cuda.memory_allocated(0) / 1024**3 if torch.cuda.is_available() else 0,
"memory_reserved": torch.cuda.memory_reserved(0) / 1024**3 if torch.cuda.is_available() else 0
},
"service": {
"total_generations": len(generation_history),
"model_loaded": generator is not None
}
}
return jsonify(status)
# 添加定时清理旧图像的功能
import schedule
import time
import threading
from datetime import datetime, timedelta
def cleanup_old_images():
"""清理7天前的生成图像"""
cutoff_time = datetime.now() - timedelta(days=7)
for filename in os.listdir(output_dir):
filepath = os.path.join(output_dir, filename)
if os.path.isfile(filepath):
file_time = datetime.fromtimestamp(os.path.getmtime(filepath))
if file_time < cutoff_time:
os.remove(filepath)
print(f"清理旧文件: {filename}")
# 在单独线程中运行定时任务
def run_scheduler():
schedule.every().day.at("03:00").do(cleanup_old_images)
while True:
schedule.run_pending()
time.sleep(60)
scheduler_thread = threading.Thread(target=run_scheduler, daemon=True)
scheduler_thread.start()
7. 总结
通过这篇教程,我们完整地部署了一个基于Qwen-Turbo-BF16的高性能图像生成系统。让我们回顾一下关键要点:
系统核心优势:
- 极速生成:4步Turbo采样,秒级出图
- 稳定可靠:BF16精度彻底解决黑图问题
- 显存友好:智能卸载策略,24GB显存轻松运行
- 界面美观:现代化设计,操作流畅
部署要点回顾:
- 环境准备:确保有RTX 4090等高性能显卡和足够的存储空间
- 模型下载:从Hugging Face获取Qwen-Image-2512和Turbo LoRA
- 代码配置:按照教程编写三个核心文件
- 启动运行:使用提供的脚本一键启动服务
使用技巧:
- 提示词要具体,包含主体、风格、质量描述
- 从4步采样开始,不满意再增加步数
- 指导尺度在1.5-2.5之间调整效果最佳
- 利用历史记录功能快速回溯和复用提示词
这个系统不仅适合个人使用,也可以作为企业级AI绘画应用的基础。你可以基于这个框架,添加更多功能,比如:
- 用户系统和管理后台
- 风格模板和提示词库
- 高级编辑和后期处理
- API服务商业化
最重要的是,通过BF16精度的使用,你获得了接近FP32的生成质量,同时保持了FP16的推理速度,这在需要高质量图像生成的场景中非常有价值。
现在,你可以开始创作属于自己的AI艺术作品了。从简单的风景到复杂的场景,从写实风格到艺术创作,这个系统都能帮你快速实现创意。记住,好的提示词是成功的一半,多尝试不同的描述组合,你会发现AI绘画的无限可能。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
更多推荐
所有评论(0)