本文档是 实现/开发者指南:描述 easydistill T2I 文生图蒸馏管线的架构设计、 模块边界、阶段逻辑、后端协议和扩展指南。可运行的配置示例与字段速查表见使用指南: English · 中文。
T2I 蒸馏管线的核心目标:将种子文本 prompt 转化为多模态 SFT 训练数据 (prompt → image 对),其中图像由教师 T2I 模型生成,经 VLM 评测和质量过滤后 组装为 ShareGPT 格式。
seed prompt
│
▼
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ prompt_optimize │ ──> │ t2i_generate │ ──> │ t2i_eval │ ──> │ quality_filter │ ──> │ build_t2i_sft │
│ T2IPromptOptim │ │ T2IGenerationOp │ │ T2IImageEval │ │ run_quality_ │ │ T2ISFTBuilder │
│ (LLM 重写) │ │ (教师生成图像) │ │ (VLM 4维评分) │ │ filter_stage │ │ (多模态SFT格式) │
└─────────────────┘ └──────────────────┘ └─────────────────┘ └──────────────────┘ └──────────────────┘
backend t2i_backend eval_backend (纯函数) (纯函数)
qwen3.7-max Qwen-Image/Wanx qwen3.6-plus
5 个阶段可选可配,唯一约束:最后阶段必须是 build_t2i_sft。
管线使用三个独立后端,各自有不同的 API 协议:
| 后端 | 配置键 | 基类 | 用途 | 默认模型 |
|---|---|---|---|---|
| 文本模型 | backend |
ModelBackend |
prompt 优化(LLM 文本生成) | qwen3.7-max |
| VLM 评测 | eval_backend |
ModelBackend |
图像质量评分(多模态) | qwen3.6-plus |
| T2I 生成 | t2i_backend |
T2IBackend |
教师图像生成 | Qwen-Image / wanx2.1-t2i-turbo |
设计决策:为什么 T2I 后端单独抽象?
T2I 模型 API(图像生成)与 chat completions(文本对话)协议完全不同:
- chat:
POST /chat/completions→{choices: [{message: {content}}]} - T2I:
POST /images/generations→{data: [{url}]}或异步任务轮询
如果复用 ModelBackend,会导致接口污染(图像 URL 无法通过 text 字段传递)。
因此 T2IBackend 作为平行抽象存在,operator 代码通过后端类型隔离保持 clean。
eval_backend 可选:未配置时复用 backend(但推荐分离,因为 VLM 和纯文本模型
可能不是同一个)。
BaseDistillationPipeline (ABC) ← pipeline/base.py
└── T2IDistillationPipeline ← pipeline/t2i_distillation.py
PromptGenerationOperator (ABC) ← operators/prompt_base.py
└── T2IPromptOptimizer ← operators/t2i/prompt_optimizer.py
Operator (ABC) ← operators/base.py
└── T2ISFTBuilder ← operators/t2i/sft_builder.py
T2IGenerationOperator (独立类) ← operators/t2i/t2i_generation.py
(不继承 PromptGenerationOperator,因为 T2IBackend 协议不同)
LLMJudgeEvaluator (ABC) ← eval/base.py
└── T2IImageEvaluator ← eval/t2i.py
T2IBackend (ABC) ← backends/t2i_base.py
├── PAIDiffusionBackend ← backends/pai_diffusion_backend.py
├── WanxBackend ← backends/wanx_backend.py
└── QwenImageBackend ← backends/qwen_image_backend.py
pipeline/base.py 定义了管线的骨架:
__init__: 接收 backend、pipeline_config(阶段列表)、dataset_config 等run(): 从dataset_config["input_path"]加载数据,调用_run_stages()run_with_data(data): 直接传入数据(测试用)_run_stages(data): 逐阶段调度,每阶段调用_dispatch_stage+ 保存中间输出_dispatch_stage(stage_name, ...): 抽象方法,子类实现阶段分发
约束: 最后阶段必须匹配 _last_stage(T2I 管线为 "build_t2i_sft")。
中间输出:每个阶段可通过 output_path 保存 JSONL 快照,支持断点续跑。
pipeline/t2i_distillation.py 实现五阶段调度:
class T2IDistillationPipeline(BaseDistillationPipeline):
_last_stage = "build_t2i_sft"
_default_eval_metrics = ["prompt_consistency", "aesthetic_quality",
"detail_richness", "artifact_absence"]构造函数接收三后端 + 四配置:
backend: 文本模型(prompt 优化用)t2i_backend: T2I 后端(图像生成用)eval_backend: VLM 后端(评测用,缺省复用 backend)pipeline_config: 阶段列表generation_config/sft_config/eval_config: 全局默认配置
_dispatch_stage 按 stage_name 路由到对应处理函数:
| stage_name | 处理函数 | 所在文件 |
|---|---|---|
prompt_optimize |
_run_prompt_optimize_stage |
t2i_distillation.py |
t2i_generate |
_run_t2i_generate_stage |
t2i_distillation.py |
t2i_eval |
run_t2i_eval_stage |
pipeline/common.py |
quality_filter |
run_quality_filter_stage |
pipeline/common.py |
build_t2i_sft |
run_build_t2i_sft_stage |
pipeline/common.py |
配置合并:阶段级 config 覆盖全局 config({**global, **stage}),实现
"全局默认 + 阶段覆盖"的配置模式。
模块: T2IPromptOptimizer (operators/t2i/prompt_optimizer.py)
继承: PromptGenerationOperator[Dict, Dict]
输入: [{id, prompt}]
输出: [{id, raw_prompt, optimized_prompt, ...metadata}]
方法: 用 LLM 将简短种子 prompt 重写为详细 T2I prompt。优化模板
(T2I_PROMPT_OPTIMIZE_PROMPT)包含 7 条指导原则:
- 描述主体及其属性
- 构图、取景、 camera angle
- 艺术风格/媒介(照片级真实、油画、动漫、3D 渲染)
- 光照、色彩、氛围
- 质量增强词(highly detailed, 8K, sharp focus)
- 控制在 30-80 词
- 保留原始意图
关键实现:
_build_requests: 将每行 prompt 填入模板,构造GenerationRequest_parse_result: 从<answer>...</answer>标签提取优化后 prompt- 模板可自定义:
prompt_template(内联)或prompt_template_file(文件) - 并发: 继承
PromptGenerationOperator的max_workers线程池
设计决策: 保留 raw_prompt 在输出中,用于后续溯源和 Δ 计算
(IntentDistill 的解释鸿沟度量需要对比 raw_prompt 和 optimized_prompt)。
模块: T2IGenerationOperator (operators/t2i/t2i_generation.py)
继承: 不继承 PromptGenerationOperator(独立类)
输入: [{id, optimized_prompt, ...}]
输出: [{id, optimized_prompt, image_urls, t2i_model, ...}]
设计决策:为什么不继承 PromptGenerationOperator?
PromptGenerationOperator 的抽象假设是"调用 chat completions API 并解析文本响应"。
T2I 生成的 API 协议完全不同(返回图像 URL 而非文本),强行继承会导致大量 override
和空实现。因此 T2IGenerationOperator 自包含并发和重试逻辑,直接调用
T2IBackend.generate_image()。
关键实现:
_generate_one(row): 单行生成,带指数退避重试- 重试条件:
TimeoutError,ConnectionError,RateLimitError等 - 退避策略:
base * 2^(attempt-1) * (0.5 + random()),上限retry_max_wait - 失败处理:
raise_on_error=True抛异常,否则返回 None 跳过
- 重试条件:
run(data): 根据max_workers选择顺序或并发执行- 并发:
ThreadPoolExecutor,保持结果顺序(通过 index 映射)
- 并发:
prompt_key: 可配置从哪個字段读取 prompt(默认optimized_prompt)- 额外参数透传:
seed,negative_prompt,infer_steps,cfg_scale等 通过_extra_kwargs传给后端(Qwen-Image 特有参数)
模块: T2IImageEvaluator (eval/t2i.py)
继承: LLMJudgeEvaluator
输入: [{id, optimized_prompt, image_urls}]
输出: [{id, ..., prompt_consistency, aesthetic_quality, detail_richness, artifact_absence}]
方法: VLM-as-judge,对每张生成图打 4 维分数(0-9 整数)。各维度含义见使用指南中的“评测维度”章节;本文档只关注实现细节。
关键实现:
_extract_sample: 从行中提取 prompt(优先optimized_prompt)和首张图 URL_extract_images: 只评估每行的第一张图(image_urls[:1])- 评分模板: 每个维度有独立 prompt 模板(
T2I_EVAL_PROMPTS), 指示 VLM 输出<score>N</score>标签 - 可自定义:
eval.prompts_file指向自定义 YAML 模板 - 并发: 继承
LLMJudgeEvaluator的线程池
公共函数 run_t2i_eval_stage(pipeline/common.py):
- 构建 eval_samples(提取 prompt + image_urls)
- 调用 evaluator.run()
- 将分数合并回原行(按 id 匹配)
函数: run_quality_filter_stage (pipeline/common.py)
输入: 带 eval 分数的行 输出: 过滤后的行子集
两遍过滤:
-
min_scores 阈值过滤: 每个维度设最低分,低于阈值的行被淘汰
require_all_metrics=True(默认): 缺少任一维度分数的行也被淘汰- 例:
min_scores: {prompt_consistency: 6, aesthetic_quality: 5}
-
top-k / top-ratio 选择: 按多维度均分排序,保留前 K 条或前 N%
keep_top_k: 1000→ 保留均分最高的 1000 条keep_top_ratio: 0.8→ 保留均分前 80%- 均分计算:
compute_average_score(row, eval_metrics),跳过 None 值
设计决策: 两遍串行——先阈值粗筛,再排序精选。这样 min_scores 淘汰的 低质数据不参与排序,避免"低分但未达阈值"的数据挤占 top-k 名额。
模块: T2ISFTBuilder (operators/t2i/sft_builder.py)
继承: Operator[List[Dict], List[SFTSample]]
输入: [{id, optimized_prompt, image_urls, ...eval_scores}]
输出: [{messages: [...], metadata: {...}}](SFTSample.model_dump())
方法: 将 prompt-image 对转化为多模态 SFT 训练样本:
- user message:
optimized_prompt(或prompt)文本 - assistant message: 多模态列表
[{type: "image_url", image_url: {url: ...}}]
配置:
skip_empty: 跳过无图或空 prompt 的行(默认 True)min_prompt_length: 最短 prompt 字符数(默认 0)max_images_per_prompt: 每样本最多图片数(默认 1,只用首图)system_prompt: 可选系统 prompt
metadata 保留: SFT 样本的 metadata 包含溯源信息:
source: "t2i_distillation"t2i_model: 生成模型名raw_prompt: 原始种子 promptrequest_id: 原始行 id- 4 个 eval 分数(如果存在): 用于训练时加权采样
backends/t2i_base.py 定义 T2I 后端的抽象接口:
class T2IBackend(ABC):
@abstractmethod
def generate_image(self, prompt, model_id=None, size="1024*1024",
n=1, **kwargs) -> ImageGenerationResult:
...
def health_check(self) -> bool: ...
def close(self) -> None: ...
def __enter__(self): return self
def __exit__(self, ...): self.close()返回 ImageGenerationResult,包含 prompt, image_urls, model, usage,
metadata。支持上下文管理器协议。
backends/pai_diffusion_backend.py,支持 sync + async 双模式自动检测:
Sync 模式(SD/Flux via vLLM):
POST /images/generations立即返回{data: [{url | b64_json}]}- OpenAI 兼容协议
Async 模式(Qwen-Image on EAS):
POST /images/generations返回{task_id: "..."}- 轮询
GET /tasks/{task_id}/status直到completed - 下载
GET /tasks/{task_id}/image
自动检测: 响应含 task_id → async,否则 sync。一套代码兼容两种协议。
关键参数:
endpoint_url: PAI-EAS 端点(含/v1后缀)token: 认证令牌auth_prefix:"Bearer "(默认)或""(EAS 原始 token)output_dir: 异步模式图片保存目录(None 则返回 base64 data URL)poll_interval/max_poll_wait: 异步轮询参数
透传参数: seed, negative_prompt, infer_steps, cfg_scale 等
Qwen-Image 特有参数通过 **kwargs 透传到 API payload。
健康检查: 先试 GET /models(OpenAI 兼容),失败则检查 base URL
(部分 EAS 部署无 /models 端点)。
backends/wanx_backend.py,基于 dashscope SDK。配置示例(如 API key、默认模型)见使用指南;这里只说明实现要点:
- 异步任务: 提交 → 轮询 → 返回 OSS URL
- 默认模型:
wanx2.1-t2i-turbo
backends/qwen_image_backend.py,基于 dashscope SDK,协议与 Wanx 相同。配置示例见使用指南;实现要点:
- 异步任务: 提交 → 轮询 → 返回 OSS URL
- 默认模型:
qwen-image2.0-pro - 可通过
model_id覆盖为其他 Qwen-Image 模型(如qwen-image)
每阶段输出是下阶段输入,字段逐步累加:
Stage 0 (种子):
{id, prompt}
Stage 1 (prompt_optimize):
{id, raw_prompt, optimized_prompt}
Stage 2 (t2i_generate):
{id, raw_prompt, optimized_prompt, image_urls, t2i_model, [t2i_usage]}
Stage 3 (t2i_eval):
{id, raw_prompt, optimized_prompt, image_urls, t2i_model,
prompt_consistency, aesthetic_quality, detail_richness, artifact_absence}
Stage 4 (quality_filter):
(Stage 3 的子集,字段不变)
Stage 5 (build_t2i_sft):
{messages: [
{role: "user", content: optimized_prompt},
{role: "assistant", content: [{type: "image_url", image_url: {url: ...}}]}
],
metadata: {source, t2i_model, raw_prompt, request_id, [eval_scores]}}
最终输出兼容 LLaMA-Factory 和 ms-swift 多模态训练:
{
"messages": [
{"role": "user", "content": "A cat on the moon, cinematic lighting, 8K"},
{"role": "assistant", "content": [
{"type": "image_url", "image_url": {"url": "outputs/t2i_images/task_abc.png"}}
]}
],
"metadata": {
"source": "t2i_distillation",
"t2i_model": "Qwen-Image",
"raw_prompt": "一只在月球上的猫",
"request_id": "1",
"prompt_consistency": 8,
"aesthetic_quality": 7,
"detail_richness": 6,
"artifact_absence": 8
}
}本节展示完整管线的 YAML 结构与阶段覆盖机制。字段含义速查和可运行示例见使用指南。
完整管线配置(以 advanced_t2i_distill_pai_diffusion.yaml 为例):
job_type: advanced_t2i_distill # CLI 分发键
backend: # 文本模型(prompt优化)
type: pai_token
api_key: ${PAI_TOKEN_API_KEY}
model_id: qwen3.7-max
eval_backend: # VLM(评测,可选)
type: pai_token
model_id: qwen3.6-plus
t2i_backend: # T2I后端(图像生成)
type: pai_diffusion
endpoint_url: ${EAS_ENDPOINT_URL}
token: ${EAS_TOKEN}
model_id: Qwen-Image
generation: # 全局T2I默认参数
size: "1328*1328"
n: 1
max_workers: 4
seed: 42
infer_steps: 50
cfg_scale: 4
eval: # 全局评测默认参数
metrics: [prompt_consistency, aesthetic_quality, detail_richness, artifact_absence]
temperature: 0.0
sft: # 全局SFT默认参数
skip_empty: true
min_prompt_length: 5
pipeline: # 阶段列表(有序)
- stage: prompt_optimize
config: {temperature: 0.7, max_tokens: 1024}
output_path: outputs/stage1.jsonl
- stage: t2i_generate
config: {max_workers: 3}
output_path: outputs/stage2.jsonl
- stage: t2i_eval
output_path: outputs/stage3.jsonl
- stage: quality_filter
config:
min_scores: {prompt_consistency: 6, aesthetic_quality: 5}
keep_top_ratio: 0.8
output_path: outputs/stage4.jsonl
- stage: build_t2i_sft
config: {}
dataset:
input_path: examples/seed_t2i_prompts.jsonl
output_path: outputs/t2i_sft_final.jsonl
prompt_key: promptconfigs/t2i/ 下 10 个模板:
| 配置 | 后端 | 阶段数 | 用途 |
|---|---|---|---|
prompt_optimize_pai_token.yaml |
—(文本 LLM) | 1 (optimize) | 仅 prompt 优化 |
t2i_generation_wanx.yaml |
Wanx | 1 (generate) | 仅生成图片 |
t2i_generation_qwen_image.yaml |
Qwen-Image | 1 (generate) | 仅生成图片 |
t2i_distill_wanx.yaml |
Wanx | 2 (generate→SFT) | 最简流程 |
t2i_distill_qwen_image.yaml |
Qwen-Image | 2 (generate→SFT) | 最简流程 |
t2i_distill_pai_diffusion.yaml |
PAI-Diffusion | 2 | 最简流程 |
advanced_t2i_distill_wanx.yaml |
Wanx | 5 (全) | 完整管线 |
advanced_t2i_distill_qwen_image.yaml |
Qwen-Image | 5 (全) | 完整管线 |
advanced_t2i_distill_pai_diffusion.yaml |
PAI-Diffusion | 5 (全) | 完整管线 |
本节说明 CLI 如何按
job_type分发到 runner。各 job 的可运行 YAML 示例见使用指南的“独立操作”与“高级 T2I 蒸馏流水线”章节。
easydistill --config <yaml> 根据 job_type 字段分发到对应 runner:
| job_type | runner 函数 | 说明 |
|---|---|---|
t2i_distill |
run_t2i_distill |
基础流程:种子→生成→SFT |
prompt_optimize |
run_prompt_optimize |
单阶段:仅 prompt 优化 |
t2i_generation |
run_t2i_generation |
单阶段:仅图像生成 |
t2i_eval |
run_t2i_eval |
单阶段:仅 VLM 评测 |
advanced_t2i_distill |
run_advanced_t2i_distill |
完整 5 阶段管线 |
单阶段 runner(run_prompt_optimize / run_t2i_generation / run_t2i_eval):
- 加载配置 → 构建对应后端 → 调用单个 operator → 保存结果
完整管线 runner(run_advanced_t2i_distill):
- 加载配置 → 构建三后端(backend + t2i_backend + eval_backend)
- 构造
T2IDistillationPipeline→ 调用pipeline.run()
基础 runner(run_t2i_distill):
- 加载配置 → 构建 t2i_backend → T2IGenerationOperator → T2ISFTBuilder
- 不走管线调度,直接串联两步
- 继承
T2IBackend,实现generate_image():class MyT2IBackend(T2IBackend): def generate_image(self, prompt, model_id=None, size="1024*1024", n=1, **kwargs) -> ImageGenerationResult: # 调用你的 API return ImageGenerationResult(prompt=prompt, image_urls=[...], model=...)
- 在
cli/backend_factory.py的build_t2i_backend()注册新类型 - YAML 配置
t2i_backend.type: my_t2i
T2IGenerationOperator 无需修改——它通过 T2IBackend 接口操作,
与新后端的具体 API 无关。
- 在
configs/prompts/t2i_eval_prompts.yaml添加新维度模板:text_rendering: | You are an expert judge evaluating text rendering... <score>{N}</score> Text Prompt: {instruction} Generated Image: {output}
- YAML 配置
eval.metrics加入text_rendering quality_filter的min_scores可设新维度阈值
T2IImageEvaluator 会自动加载新维度模板并评分——评测器是 metric-driven 的,
新增维度无需改代码。
- 在
pipeline/common.py实现阶段处理函数:def run_my_stage(backend, stage_config, data, eval_metrics): # 处理 data,返回新 data return new_data
- 在
T2IDistillationPipeline._dispatch_stage注册:elif stage_name == "my_stage": data = run_my_stage(self.backend, stage_config, data, eval_metrics)
- YAML
pipeline列表加入{stage: my_stage, config: {...}}
阶段间数据通过 JSONL 中间输出衔接,支持断点续跑。
| 文件 | 职责 |
|---|---|
pipeline/base.py |
管线抽象基类 |
pipeline/t2i_distillation.py |
T2I 五阶段管线 |
pipeline/common.py |
共享阶段函数(eval/filter/SFT) |
operators/t2i/prompt_optimizer.py |
T2I prompt 优化 |
operators/t2i/t2i_generation.py |
T2I 图像生成 |
operators/t2i/sft_builder.py |
多模态 SFT 构造 |
eval/t2i.py |
VLM-as-judge 评测 |
backends/t2i_base.py |
T2I 后端抽象 |
backends/pai_diffusion_backend.py |
PAI-EAS 后端(sync+async) |
backends/wanx_backend.py |
通义万相后端 |
backends/qwen_image_backend.py |
Qwen-Image 后端 |
cli/runners/t2i.py |
CLI runner(5 个入口) |
prompts.py |
T2I 优化/评测 prompt 模板 |
configs/t2i/*.yaml |
10 个配置模板 |
configs/prompts/t2i_eval_prompts.yaml |
评测维度模板 |