UniGen: Enhanced Training & Test-Time Strategies for Unified Multimodal Understanding and Generation
Paper: arXiv:2505.14682 Code: apple/ml-unigen Code reference:
main@9f5b2490(2025-11-24)
1. Motivation (研究动机)
当前 unified MLLM 的主要矛盾不是“能不能同时接图、出图”,而是理解能力和生成能力在一个模型生命周期里如何协同训练、互相服务。Show-o、Janus / Janus-Pro、Vila-U 等模型已经证明了统一理解与生成的可行性,但训练 recipe 往往依赖私有数据、缺少端到端的公开消融,且生成质量在计数、属性、关系等细粒度语义对齐上仍明显落后于强 text-to-image 专家模型。更关键的是,已有 unified model 很少把自身的理解能力显式用于提升生成结果:理解头和生成头通常共享 backbone,但测试时仍像普通生成模型一样“一次采样后直接提交”。
UniGen 试图解决两个具体问题:第一,给出一个可复现的 unified MLLM 训练链路,从 multi-stage pre-training、SFT 到 DPO,说明每一阶段怎样影响理解与生成;第二,提出 Chain-of-Thought Verification (CoT-V),让同一个 UniGen 在测试时先生成多个候选图,再用自身理解能力逐项检查图文一致性,从而用“理解”筛掉生成错误。这一点直接对应用户关心的“理解帮助生成”:UniGen 的最终提升主要来自把模型的视觉问答/语义核验能力接入 Best-of-N test-time scaling。
研究价值在于:如果统一模型只是在参数层面共享理解与生成,而不能让两种能力在训练或推理时互相调用,它更像是“多任务拼接”。UniGen 的意义是把 unified model 从“同一个模型做两件事”推进到“同一个模型在生成后自我验证”,同时用完整 ablation 说明哪些数据、阶段和 verifier 设计真正起作用。
2. Idea (核心思想)
核心 insight 是:生成质量的瓶颈很大一部分来自语义选择,而不是候选空间本身;如果 unified MLLM 已经具备图像理解能力,就可以把它当作 test-time verifier 来选择更对齐 prompt 的候选图。 也就是说,UniGen 不只训练一个共享 backbone,还让模型在推理时形成 generate → understand/verify → select 的闭环。
方法上,UniGen 使用 decoupled visual encoders:理解分支用连续视觉 token(SigLIP),生成分支用离散视觉 token(MAGVITv2),中间共享 Qwen2.5-1.5B autoregressive LLM。训练 recipe 分成 PT-1、PT-2、SFT、DPO、CoT-V Post-Training 五阶段;其中 PT/SFT/DPO 建立统一模型的基本生成与理解能力,CoT-V Post-Training 则专门教模型输出 <think_start>...<think_end> 形式的视觉 fact-check 链条。
与 Janus-Pro / Show-o 的根本差异在于:UniGen 不满足于并列地优化理解和生成,而是在测试时把理解能力显式变成生成 reranker。与 rule-based verifier 相比,CoT-V 不需要把每个 prompt 手写拆成模板规则,而是让模型自己把复杂 prompt 拆成可回答的 visual questions,因此对自由文本和长 prompt 更通用。
3. Method (方法)
3.1 Overall framework:一个 LLM + 两套视觉 token 空间
Figure 2 解读:UniGen 的中心是一个 autoregressive LLM;理解侧使用 Und Encoder / Und Projector,把输入图像编码成连续视觉 embedding 后接入 LLM;生成侧使用 Gen Encoder / Gen Projector / Gen Decoder,把图像离散化成 MAGVITv2 token,并通过 masked token prediction 生成新图。这个 decoupled encoder 设计承认理解和生成对视觉表示的偏好不同:连续 embedding 更保留空间细节,离散 token 更适合图像重建与并行解码。
设理解输入图像为 ,理解 encoder 提取连续 token: 再经 projector 对齐到 LLM embedding space,用普通 next-token prediction 得到理解损失 。为避免 unified training 破坏语言建模,训练中还加入 text-only 数据,对应 。
生成侧把目标图像 经 离散化为长度 的 codebook token。训练时根据 mask schedule 采样 masking ratio ,构造二值 mask ,把被 mask 的 token 替换成 [MASK] 后,让模型预测原始离散 token。对应实现中,models/unigen.py 的 forward() 对 image token 位置做 F.cross_entropy(..., ignore_index=-100);training/train.py / training/train_w_clip_vit.py 负责调用 mask_or_random_replace_tokens() 生成 masked input 和 labels。
直觉上,这个框架把“统一”的负担放在 LLM 里,而不是强迫一个视觉 encoder 同时服务理解和生成。这样做的代价是视觉接口更多,但好处是避免离散 token 损失理解所需的细节、连续 embedding 又不适合像素级生成的冲突。
3.2 五阶段训练 recipe
Figure 1 解读:这张 teaser 把 UniGen 的定位放在 understanding 与 generation 两轴上:相对 Show-o-1.3B、Janus-1.3B、Janus-Pro-1.5B,UniGen-1.5B 同时在理解基准和生成基准上靠前。它不是单纯扩大模型规模,而是通过训练阶段和 test-time verification 改善 unified model 的平衡点。
PT-1:ImageNet caption warm-up
PT-1 的目标是把离散图像 token 与文本 token 对齐,同时不丢掉后续理解潜力。模型只优化 generation projector 和 LLM,其它模块冻结;训练数据包括 ImageNet 的 Qwen2.5-VL-7B re-caption 图文对、用于 image-to-text 的 CC-3M / CC-12M / SA-1B / ImageNet recap 组合,以及 RefinedWeb text-only 数据。论文强调不直接用类别名,而用细粒度 caption 作为 text-to-image 条件,因为 caption 更接近自然生成 prompt,也更利于收敛。
PT-2:更宽分布的 recap 图文数据
PT-2 在 PT-1 基础上加入 CC-3M、CC-12M、SA-1B 与 ImageNet 的长 caption / richer distribution,目的不是立刻提升 VQA,而是让生成分布更宽,能处理更复杂的文本条件。论文 Table 14 概括为:PT-1 的 Gen Data 是 IMN (Recap),PT-2 的 Gen/Und Data 都是 (CC+SA+IMN) (Recap),text-only 均为 RefinedWeb。
SFT:理解与生成联合指令微调
SFT 同时训练 generation projector、understanding projector 和 LLM,vision encoders 继续冻结。理解数据使用 SlowFast-LLaVA-1.5 的 4.67M single-image mixture;生成数据使用 JourneyDB 与 text-2-image-2M,以提升美学质量和自然 prompt 鲁棒性。SFT 是理解能力真正被激活的阶段:Table 3 中从 PT-2 到 SFT 后,GenEval 从 0.55 到 0.63,DPG-Bench 从 80.71 到 82.75,同时出现完整 understanding benchmark 数字。
DPO:用合成 preference 对齐生成
DPO 先用 UniGen-SFT 对每个 prompt 生成 20 张候选图,再选 preferred / rejected 对。prompt 覆盖三档:PARM 短 prompt 6K,对应 GenEval rule 打分;T2I-Comp 中等复杂 prompt 6K,用 Qwen2.5-7B 拆 visual questions、Qwen2.5VL-7B 做 yes/no fact-check;SA1B 长 caption prompt 6K,同样用 question decomposition 评估图文一致性。论文使用 vanilla DPO: 其中 / 是 preferred / rejected image token 序列, 控制偏离 SFT reference 的强度。DPO 阶段只优化 generation modules,冻结 understanding encoder/projector;论文报告 batch size 64、learning rate ,训练 1 epoch。
Figure 6 解读:图中展示由合成 preference pipeline 产生的正负样本:同一 prompt 下,候选图通过视觉事实核验获得不同一致性分数,最高分作为 preferred、最低分作为 rejected。它说明 DPO 数据并非人工标注,而是由 rule metric / Qwen decomposition / Qwen2.5VL fact-check 组合自动构造,因此后续性能依赖 pseudo-label 的可靠性。
CoT-V Post-Training:让模型会“逐项核验”
DPO 后的 UniGen 有理解能力,但未必会以 CoT 格式输出可解析的核验过程。CoT-V Post-Training 复用 DPO 阶段的图文对:PARM prompt 由模板规则提取 question-answer;T2I-Comp 由 Qwen2.5-7B 生成 atomic questions,再用 Qwen2.5VL-7B 生成 yes/no pseudo labels;SA1B 因复杂长 caption 的问题分解质量较低,被排除。最终采样 20K image-question-answer triplets,格式化为 instruction-following conversation,只优化 understanding projector 和 LLM,500 steps、learning rate 。
3.3 Test-Time Scaling:用理解能力帮助生成
Figure 3 解读:左侧展示 Best-of-N:UniGen 先为 prompt 生成多个候选图,然后把每张候选图和原 prompt 输入自身理解分支,得到语义一致性分数;右侧展示 CoT-V 的逐步核验,例如把 “dining table above a suitcase” 拆成是否有桌子、是否有行李箱、桌子是否在行李箱上方等问题。最终得分来自 yes/no 的平均值,因此模型能从多个候选中选择更符合 prompt 的图。
正式地,给定 text prompt 和候选图 ,CoT-V 输出问题集合 及答案 ,每个子问题得分为: 最终对每个 prompt 生成 张候选图,选 top-(论文设置 )送入 GenEval / DPG-Bench 评测。这里的协同关系非常直接:生成分支负责提供候选,理解分支负责做 verifier;理解越能拆解语义关系、属性和计数,越能帮助生成端在候选中选出正确图。
Figure 4 解读:Figure 4 对比三种 verification。Outcome Verification 只问图是否匹配 prompt,容易退化成 pattern matching;Rule-based Verification 手工拆问题,对短模板 prompt 有效但难覆盖自由文本;CoT-V 让模型自己生成/执行逐步核验,既保留了可解释的子问题,又不依赖固定规则,是最终采用的默认 verifier。
Figure 5 解读:Figure 5 给出 CoT-V 选出的图像示例,覆盖 counting、position、color attribute 与自由 prompt。第一列是 UniGen verifier 选出的候选图,说明 CoT-V 的作用不是改变单张图的像素,而是在多个候选图中用语义核验改变最终输出分布。
3.4 Pseudocode:关键组件的实现逻辑
Masked token prediction for T2I
def t2i_train_step(model, vq_encoder, batch, mask_schedule, prompt_template):
images, texts = batch["t2i_flow"]["images"], batch["t2i_flow"]["input_ids"]
image_tokens = vq_encoder.encode(images) # MAGVITv2 discrete ids
masked_ids, labels, _, mask_prob = mask_or_random_replace_tokens(
image_tokens,
mask_id=model.config.mask_token_id,
config=model.config,
mask_schedule=mask_schedule,
is_train=True,
)
input_ids, attention_mask, labels = prompt_template((texts, masked_ids, labels), "t2i")
img_logits, loss_t2i, loss_lm, loss_mmu = model(
input_ids=input_ids,
attention_mask=create_attention_mask_predict_next(input_ids),
labels=labels,
batch_size_t2i=images.shape[0],
batch_size_lm=0,
batch_size_mmu=0,
t2i_mode="mask",
)
return loss_t2iIterative MaskGIT-style generation with CFG
@torch.no_grad()
def maskgit_generate(model, input_ids, uncond_input_ids, timesteps, guidance_scale, noise_schedule):
ids = input_ids[:, -(model.config.num_vq_tokens + 1):-1].clone()
ids[:] = model.config.mask_token_id
for step in range(timesteps):
cond_logits = model(input_ids=input_ids_with(ids))
if guidance_scale > 1:
uncond_logits = model(input_ids=uncond_input_ids_with(ids))
logits = guidance_scale * (cond_logits - uncond_logits) + uncond_logits
else:
logits = cond_logits
probs = logits.softmax(dim=-1)
sampled_ids = torch.multinomial(probs.reshape(-1, probs.size(-1)), 1).view_as(ids)
sampled_ids = torch.where(ids == model.config.mask_token_id, sampled_ids, ids)
keep_prob = torch.gather(probs, -1, sampled_ids[..., None]).squeeze(-1)
next_mask_len = int(model.config.num_vq_tokens * noise_schedule((step + 1) / timesteps))
remask = mask_by_random_topk(next_mask_len, keep_prob)
ids = torch.where(remask, model.config.mask_token_id, sampled_ids)
return sampled_idsDPO over image token log-probabilities
def dpo_loss(policy, reference, chosen_batch, rejected_batch, beta=0.1, dpo_coef=1.0):
chosen_logps = get_batch_logps(policy(**chosen_batch).logits, chosen_batch["labels"])
rejected_logps = get_batch_logps(policy(**rejected_batch).logits, rejected_batch["labels"])
with torch.no_grad():
ref_chosen_logps = get_batch_logps(reference(**chosen_batch).logits, chosen_batch["labels"])
ref_rejected_logps = get_batch_logps(reference(**rejected_batch).logits, rejected_batch["labels"])
policy_logratio = chosen_logps - rejected_logps
reference_logratio = ref_chosen_logps - ref_rejected_logps
logits = policy_logratio - reference_logratio
return dpo_coef * (-F.logsigmoid(beta * logits)).mean()CoT-V scoring and selection
def cotv_select(prompt, generated_images, unigen, topk=4):
rows = []
for image_id, image in generated_images.items():
cot_text = unigen.answer_image_prompt(
image=image,
prompt=(
f"This image is generated by a prompt: {prompt}. "
"Please assess the image generation quality step by step."
),
)
qa_pairs = parse_qa_pairs(cot_text, start_token="<think_start>", end_token="<think_end>")
yes_count = sum(answer == "yes" for _, answer in qa_pairs)
score = yes_count / max(len(qa_pairs), 1)
rows.append((score, image_id, qa_pairs))
rows.sort(reverse=True)
return [image_id for score, image_id, _ in rows[:topk]]3.5 Code-to-paper mapping
Code reference:
main@9f5b2490(2025-11-24) — pseudocode and mapping based on this commit
| Paper Concept | Source File | Key Class/Function |
|---|---|---|
| UniGen backbone, dual visual interfaces | models/unigen.py | UniGen, add_vision_tower, add_mm_projector, get_gen_embed |
| Understanding input packing | models/unigen.py | prepare_inputs_for_mmu() |
| Generation input packing | models/unigen.py | prepare_inputs_for_t2i() |
| T2I / LM / MMU losses | models/unigen.py | forward() returns loss_t2i, loss_lm, loss_mmu |
| PT-1 / PT-2 training loop | training/train.py | prepare_inputs_and_labels(), main loop over t2i_flow, lm_flow, mmu_flow |
| SFT / CoT-V with SigLIP features | training/train_w_clip_vit.py | prepare_inputs_and_labels(), vision_tower(...), prepare_inputs_for_mmu() |
| DPO training | training/train_dpo.py | get_batch_logps(), DPODataset, concatenated_forward(), -F.logsigmoid(beta * logits) |
| MaskGIT-style sampling | models/unigen.py | t2i_generate(), t2i_generate_ar() |
| Mask schedules / remasking | models/sampling.py | cosine_schedule, mask_by_random_topk |
| CoT-V evaluation | evaluation/inference_unigen_cot.py | parse_qa_pairs(), generate_mmu_rating(), greedy_samples_byscore() |
| Question generation / pseudo-labeling helpers | scripts/dataflows/zeroshot_question.py, scripts/dataflows/zeroshot_vqa.py | visual-question generation and VQA pseudo-label flow |
| Stage configs | configs/unigen_1_5b/*.yaml | unigen_pt1.yaml, unigen_pt2.yaml, unigen_sft.yaml, unigen_dpo.yaml, unigen_cotv_post_sft.yaml |
论文配置与 released code 实现差异:论文 Table 13 报告的是完整实验设置(例如 PT-1/2 使用 32×H100、其它阶段 8×H100;SFT learning rate 为 、batch size 64、training steps 146k),而 released README 的训练脚本面向较小复现环境(PT-1 2×80GB、PT-2 4×80GB、SFT/DPO/CoT-V 1×80GB),configs/unigen_1_5b/unigen_sft.yaml 中 optimizer.params.learning_rate=5e-05、weight_decay=0.01,与论文 Table 13 的 SFT 1.0e-3 / 0.05 不一致。DPO 配置中 training.beta=0.1 与 dpo_coef=1,但 paper 只显式给出 的语义和 learning rate;因此笔记中的训练配置区分“paper-reported full run”和“released config defaults”。
4. Experimental Setup (实验设置)
Datasets
- PT-1:ImageNet (IMN) re-caption 作为 generation data;CC-3M、CC-12M、SA-1B、ImageNet re-caption 作为 image-to-text / understanding data;RefinedWeb 作为 text-only data。论文称 re-captioned datasets 与 ImageNet 形成约 40M image-text pair corpus。
- PT-2:CC-3M + CC-12M + SA-1B + ImageNet 的 re-caption 数据同时用于 generation 和 understanding;RefinedWeb 继续保留。
- SFT:generation data 为 JourneyDB + text-2-image-2M;understanding data 为 SlowFast-LLaVA-1.5 image mixture,规模 4.67M multimodal VQA samples;text-only 仍为 RefinedWeb。
- DPO:PARM 6K short prompts、T2I-Comp 6K medium prompts、SA1B 6K long prompts;每个 prompt 由 UniGen-SFT 生成 20 张图,再选 preferred / rejected。
- CoT-V Post-Training:从 PARM 和 T2I-Comp preference data 中采样 20K image-question-answer triplets;SA1B 因分解问题质量低被排除。
Baselines
理解侧主要比较 understanding MLLMs(LLaVA-OV-0.5B、MM1.5-1B、LLaVA-1.5-7B)与 unified MLLMs(Show-o、Janus、Janus-Pro、Vila-U、MMAR、UniToken)。生成侧比较 text-to-image specialists(DALLE-2、DALLE-3、Emu3、SDXL、SimpleAR、Infinity)以及 unified MLLMs(Show-o、Janus、Janus-Pro、ILLUME、UniToken、VARGPT-v1.1、TokenFlow-XL)。
Evaluation metrics
- Understanding:AI2D、GQA、POPE、MMMU、MathVista、RealWorld-QA、ScienceQA、SeedBench;使用 lmms-eval 工具。
- Generation:GenEval 和 DPG-Bench,主要衡量 text-image semantic alignment。GenEval 细分 Two Obj.、Counting、Position、Color Attri. 等;DPG-Bench 细分 Global、Entity、Attribute、Relation、Other。
- Test-time scaling evaluation:对每个 prompt 生成 candidates,用 CoT-V 选 top- 进入官方 GenEval / DPG-Bench evaluation。
Training config
Paper-reported full setting:UniGen 基于 Qwen2.5-1.5B;MAGVITv2 作为 discrete visual encoder,输入分辨率 256×256;SigLIP 作为 continuous visual encoder,理解分辨率 384。pre-training stages 使用 32×H100-80G,其它阶段使用 8×H100-80G。推理使用 classifier-free guidance scale 5.0,MaskGIT cosine masking scheduler,默认 generation steps 。
| Stage | LR | Warm-up | Steps | H100 hours | Batch size | Data ratio (Und:Text:Gen) |
|---|---|---|---|---|---|---|
| PT-1 | 6000 | 150k | 1.0k | 896 | 2:1:4 | |
| PT-2 | 5000 | 400k | 2.8k | 512 | 2:1:4 | |
| SFT | 1000 | 146k | 240 | 64 | 4:1:3 | |
| DPO | 500 | 1.6k | 5 | 80 | -:-:1 | |
| CoT-V Post-Training | 0 | 0.5k | 0.7 | 64 | 1:-:- |
Released config anchors:unigen_pt1.yaml 使用 batch_size_t2i=16, batch_size_mmu=8, max_train_steps=150000;unigen_pt2.yaml 使用 batch_size_t2i=8, batch_size_lm=2, batch_size_mmu=6, max_train_steps=400000;unigen_sft.yaml 使用 batch_size_t2i=3, batch_size_lm=1, batch_size_mmu=4;unigen_dpo.yaml 使用 batch_size_t2i=10, beta=0.1, generation_timesteps=12;unigen_cotv_post_sft.yaml 使用 batch_size_mmu=8, max_train_steps=500。
5. Experimental Results (实验结果)
Main results: understanding
Table 1 中 UniGen-1.5B 在 unified MLLM 组里整体最强或接近最强:AI2D 67.4、GQA 62.3、POPE 87.8、MMMU 32.3、MathVista 44.6、RealWorld-QA 56.7、ScienceQA 79.4、SeedBench 70.8。相对 Janus-Pro-1.5B,论文强调 UniGen 在 RealWorld-QA、AI2D、MathVista 分别高 +5.6、+3.7、+7.8;这说明 decoupled encoders 与更强 SFT image mixture 对理解侧有明显帮助。
Main results: generation
Table 2 / Table 11 / Table 12 给出生成侧核心数字。GenEval overall:UniGen 0.78,高于 Janus-Pro 0.73、Janus 0.61、Show-o 0.53,也高于 DALLE-2 0.52、Emu3 0.54、SDXL 0.55;Counting 子项 UniGen 0.78,高于 Janus-Pro 0.51。DPG-Bench overall:UniGen 85.19,高于 Janus-Pro 82.63、Janus 79.68、Show-o 71.70,也高于 DALLE-3 83.50 和 Infinity 83.46;其中 Global 91.95、Entity 89.68、Attribute 90.90、Relation 92.04、Other 90.91。 换句话说,最终 headline numbers 是 GenEval 0.78 与 DPG-Bench 85.19。
Figure 7 解读:这些 GenEval 示例说明 CoT-V 对 counting、颜色、位置等模板化语义约束有效。因为 GenEval 的 prompt 结构相对规则,CoT-V 能将 prompt 拆成若干 yes/no 检查,并把满足更多 atomic facts 的候选图选出来。
Figure 8 解读:DPG-Bench 的 prompt 更自由、更长,图中成功案例显示 CoT-V 不只是 GenEval 模板规则的替代品,而是能在复杂描述下检查实体、属性、关系和全局一致性。它也解释了为什么 Rule-based Verification 在论文中没有作为默认方法:自由文本下手工规则覆盖不充分。
Ablation: training stages
Table 3 展示了训练阶段的累积收益:PT-1 只有 GenEval 0.53 / DPG-Bench 78.14;PT-2 升到 0.55 / 80.71;SFT 升到 0.63 / 82.75 并激活理解 benchmark;DPO 进一步到 0.73 / 84.89,且 understanding 基本不降(例如 POPE 87.4→88.0,RealWorld-QA 58.6→59.0);CoT-V 最终到 0.78 / 85.19,但 RealWorld-QA 从 59.0 降到 56.7,论文归因于 CoT-V synthetic data 与真实图像问答分布存在 gap。
Ablation: verification methods and CoT-V post-training
Table 4 中,无 verifier 或 Outcome Verification 均为 GenEval 0.74、DPG-Bench 约 85.0,几乎没有提升;Rule-based Verification 把 GenEval 提到 0.75;CoT Verification 达到 0.78 / 85.19,是最强方法。Table 5 说明 CoT-V post-training 关键:对 UniGen 来说,不做 post-train 是 0.74 / 84.89,做 post-train 后是 0.78 / 85.19;对 Show-o,CoT-V post-train 也从 0.64 / 76.32 提到 0.66 / 77.09,说明这不是 UniGen-only trick。
Ablation: DPO data sources
Table 6 中,UniGen-SFT baseline 为 0.63 / 82.75;只加 PARM preference data 后变成 0.73 / 83.48,主要拉动 GenEval;再加 T2I-Comp 是 0.72 / 84.09,DPG-Bench 更受益;三类数据全加后达到 0.74 / 84.89。Show-o 也能从 DPO data 受益:0.56 / 71.70 提升到 0.64 / 76.32,说明 preference data 的价值可以迁移到其它 unified model。
Limitations and conclusion
Figure 9 解读:失败案例集中在困难短 prompt 和复杂长 prompt:CoT-V 可能 hallucinate、漏问关键问题,或者把自由描述分解成质量较差的 questions。论文明确承认 UniGen 仍不足以在复杂自由 prompt 下稳定生成准确 reasoning process;作者认为扩大模型规模、增强早期 reasoning/CoT 能力或用 RL 改进 CoT training 可能进一步提升。
UniGen 的结论可以概括为两点:训练上,open-source 数据也能通过分阶段 recipe 把 1.5B unified MLLM 推到强理解/强生成的折中点;推理上,CoT-V 证明“理解帮助生成”是可操作的 unified model 机制。相对而言,论文没有系统展示“生成帮助理解”的实验;它主要说明生成相关的 PT/SFT 数据不会破坏理解、DPO/CoT-V 后理解能力大体保持,而真正的互助证据集中在 understanding-as-verifier 提升 generation。