Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ default_stages: [pre-commit, pre-push, manual]
# hooks must not rewrite it either.
# - unirl/models/boogu_image/vendor/ : pristine boogu-project/Boogu-Image model code
# (see VENDOR_COMMIT.txt); same re-vendor rationale as bagel/vendor/.
# - unirl/models/sensenova_u1/vendor/ : pristine OpenSenseNova/SenseNova-U1
# NEO-Unify model code (see VENDOR_COMMIT.txt).
# - benchmarks/*/*/data/ : vendored upstream benchmark prompt sets (GenEval,
# PartiPrompts, VBench, ...); kept byte-identical so upstream checksums verify.
exclude: ^(unirl-reward-service/|unirl/models/bagel/vendor/|unirl/models/boogu_image/vendor/|benchmarks/[^/]+/[^/]+/data/)
exclude: ^(unirl-reward-service/|unirl/models/bagel/vendor/|unirl/models/boogu_image/vendor/|unirl/models/sensenova_u1/vendor/|benchmarks/[^/]+/[^/]+/data/)

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
Expand Down
7 changes: 6 additions & 1 deletion INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ UniRL ships two mutually exclusive inference engines (`vllm` and `sglang`) — i

| Engine | CUDA | glibc |
|---|---|---|
| **vllm-omni** | 12.9 | ≥ 2.28 |
| **vllm-omni** | 12.9 | ≥ 2.31 |
| **sglang** | 13.0 | ≥ 2.34 |

## vllm-omni
Expand All @@ -15,6 +15,11 @@ export VLLM_USE_PRECOMPILED=1 # else 30+ min CUDA build
uv pip install -e ".[vllm,train,infer]"
```

The vLLM 0.22 CUDA wheel itself is `manylinux_2_28`, but its required
`llguidance` wheel is `manylinux_2_31`. On a glibc 2.28 image, use a newer
runtime/container or build `llguidance` from source with Rust; otherwise the
resolver falls back to an unsupported or stalled source build.

## sglang

```bash
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ dimension; all listed models are supported (✅).
| Prompt-Enhancer | LLM + diffusion | Text → Text → Image | ✅ |
| HunyuanImage3 | Unified AR + diffusion | Text → Image | ✅ |
| Bagel | Unified AR + diffusion | Text / Text + Image → Image | ✅ |
| SenseNova-U1.5 | Unified MoT pixel flow | Text → Image | ✅ |

</div>

Expand Down
4 changes: 2 additions & 2 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ entrypoint's built-in `config_name` — a safe place to start.

| Domain | Entrypoint | Default recipe (start here) | Models |
|---|---|---|---|
| [`diffusion/`](diffusion/) | `python -m unirl.train_diffusion` | `diffusion/sd3/sd3_trainside` | `sd3`, `qwen_image`, `flux2_klein`, `wan21`, `wan22`, `hunyuan_video10`, `hunyuan_video15` |
| [`diffusion/`](diffusion/) | `python -m unirl.train_diffusion` | `diffusion/sd3/sd3_trainside` | `sd3`, `qwen_image`, `flux2_klein`, `sensenova_u1_5`, `wan21`, `wan22`, `hunyuan_video10`, `hunyuan_video15` |
| [`ar/`](ar/) | `python -m unirl.train_ar` | `ar/qwen_vl_grpo_geo3k_mc_4x8`, `ar/qwen3_drpo_4b_base_dapo_sglang` | `qwen_vl` (vision-language), `qwen3` (text-only) |
| [`sft/`](sft/) | `python -m unirl.train_sft` | `sft/qwen3_sft` | `qwen3`, `qwen_vl`, `bagel`, `sd3`, `cosmos3`, `wan21` |
| [`pe/`](pe/) | `python -m unirl.train_pe` | `pe/pe_trainside_pickscore` | `pe` (Qwen3 rewriter + SD3, PickScore/WISE reward) |
Expand Down Expand Up @@ -95,7 +95,7 @@ related recipes sort together.

| Segment | Position | Values (examples) | Omit when |
|---|---|---|---|
| `model` | required, first | `sd3`, `qwen_image`, `flux2_klein`, `wan21`, `wan22`, `hunyuan_video10`, `hunyuan_video15`, `qwen_vl`, `qwen3`, `hi3` | never |
| `model` | required, first | `sd3`, `qwen_image`, `flux2_klein`, `sensenova_u1_5`, `wan21`, `wan22`, `hunyuan_video10`, `hunyuan_video15`, `qwen_vl`, `qwen3`, `hi3` | never |
| `task` | after model | `t2v`, `i2v` | text-to-image (the implicit default) |
| `size` | after task | `4b`, `14b` | only one size in the family |
| `algorithm` | middle | `dancegrpo`, `mixgrpo`, `nft`, `flowdppo`, `grpo`, `drpo` | plain FlowGRPO (diffusion default); GRPO (AR default) |
Expand Down
137 changes: 137 additions & 0 deletions examples/diffusion/sensenova_u1_5/sensenova_u1_5_trainside.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# @package _global_
# SenseNova-U1.5-8B-MoT-Preview pixel-flow GRPO with trainside rollout.
#
# Launch:
# SENSENOVA_U1_PATH=/path/to/SenseNova-U1.5-8B-MoT-Preview \
# python -m unirl.train_diffusion --config-name diffusion/sensenova_u1_5/sensenova_u1_5_trainside

num_devices: 8
batch_size: 8
adv_use_global_std: true
num_rollouts: 10000

logging:
report_to_wandb: false
project_name: unirl
run_name: sensenova-u1.5-grpo
entity: null
tags: [sensenova-u1.5, pixel-flow, trainside, fullft]

bundle:
_target_: unirl.models.sensenova_u1.bundle.SenseNovaU1Bundle.from_config
config:
_target_: unirl.models.sensenova_u1.config.SenseNovaU1PipelineConfig
pretrained_model_ckpt_path: ${oc.env:SENSENOVA_U1_PATH,sensenova/SenseNova-U1.5-8B-MoT-Preview}
# Qwen3DecoderLayer interleaves frozen und and trainable gen parameters.
# FSDP2 requires one original dtype per shard group, so keep the complete
# model in fp32 masters and use bf16 only through mixed-precision compute.
model_precision: fp32
autocast_precision: bf16
trajectory_precision: bf16
logprob_precision: fp32
timestep_shift: 3.0
attention_backend: auto
# Matches the released U1.5 pretraining recipe: freeze the shared /
# understanding path, train fm_modules + every *_mot_gen branch.
full_finetune_generation: true

pipeline:
_target_: unirl.models.sensenova_u1.pipeline.SenseNovaU1Pipeline
shift: ${bundle.config.timestep_shift}
autocast_precision: ${bundle.config.autocast_precision}
trajectory_precision: ${bundle.config.trajectory_precision}
logprob_precision: ${bundle.config.logprob_precision}
strategy:
_target_: unirl.sde.kernels.FlowSDEStrategy

backend:
_target_: unirl.train.backend.fsdp.FSDPBackend
block_class_names: ["Qwen3DecoderLayer", "NEOVisionModel"]
trainable_attr: transformer
fsdp_cfg:
_target_: unirl.train.configs.FSDPConfig
param_dtype: bf16
master_dtype: fp32
cpu_offload: false
mixed_precision: true
fsdp_mode: full
reshard_after_forward: true
activation_checkpointing: true
use_torch_compile: false
# The trainable fm_modules tree is outside the decoder blocks.
root_wrap: true
optimizer_cfg:
_target_: unirl.train.backend.base.OptimizerConfig
learning_rate: 2.0e-5
adam_beta1: 0.9
adam_beta2: 0.95
adam_epsilon: 1.0e-8
weight_decay: 0.0
scheduler_cfg:
_target_: unirl.train.backend.base.LrSchedulerConfig
type: constant
warmup_steps: 0
total_steps: ${num_rollouts}

rollout:
_target_: unirl.rollout.engine.trainside.engine.TrainsideRolloutEngine
stage_attrs: [diffusion]
# Prefix KV caches and pixel-flow trajectories are intentionally per-sample.
forward_batch_size: 1

reward:
_target_: unirl.reward.service.RewardService
backend:
_target_: unirl.reward.local.pickscore.PickScoreRewardScorer
base_device: cuda
config:
_target_: unirl.reward.local.pickscore.PickScoreSpec
batch_size: 4
device: auto
processor_id: laion/CLIP-ViT-H-14-laion2B-s32B-b79K
model_id: yuvalkirstain/PickScore_v1

algorithm:
_target_: unirl.algorithms.flowgrpo.FlowGRPO
stage_attr: diffusion
clip_range: 1.0e-4
clip_schedule: constant
conditions_cls:
_target_: hydra.utils.get_class
path: unirl.models.sensenova_u1.conditions.SenseNovaU1Conditions
params: ${sampling}

stack:
_target_: unirl.train.stack.TrainStack
micro_batch_size: 1
max_grad_norm: 1.0
num_updates_per_batch: 2

data_source:
_target_: unirl.data.data_source.MultimodalRLDataSource
args:
run:
data_path: datasets/pickscore/train.txt
eval_data_path: datasets/pickscore/test.txt
seed: 42
algorithm:
prompts_per_rollout: ${batch_size}

sampling:
_target_: unirl.models.sensenova_u1.diffusion.SenseNovaU1DiffusionParams
num_inference_steps: 20
guidance_scale: 4.0
cfg_norm: global
cfg_interval: [0.0, 1.0]
t_eps: 0.02
height: 512
width: 512
eta: 0.7
samples_per_prompt: 4
seed: 42
init_same_noise: false
scheduler:
_target_: unirl.sde.index_schedule.AllSDEScheduler
num_timesteps: ${..num_inference_steps}
num_sde_steps: 3
timestep_fraction: [0.0, 0.5]
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# @package _global_
# SenseNova-U1.5 pixel-flow GRPO with colocated vLLM-Omni rollout and
# bucketed CUDA-IPC full-weight synchronization.
#
# Launch:
# SENSENOVA_U1_PATH=/path/to/SenseNova-U1.5-8B-MoT-Preview \
# python -m unirl.train_diffusion \
# --config-name diffusion/sensenova_u1_5/sensenova_u1_5_vllmomni_full_ipc

num_devices: 8
batch_size: 8
adv_use_global_std: true
num_rollouts: 10000
weight_sync_interval: 1

logging:
report_to_wandb: false
project_name: unirl
run_name: sensenova-u1.5-grpo-vllmomni
entity: null
tags: [sensenova-u1.5, pixel-flow, vllm-omni, fullft, ipc]

bundle:
_target_: unirl.models.sensenova_u1.bundle.SenseNovaU1Bundle.from_config
config:
_target_: unirl.models.sensenova_u1.config.SenseNovaU1PipelineConfig
pretrained_model_ckpt_path: ${oc.env:SENSENOVA_U1_PATH,sensenova/SenseNova-U1.5-8B-MoT-Preview}
model_precision: fp32
autocast_precision: bf16
trajectory_precision: bf16
logprob_precision: fp32
timestep_shift: 3.0
attention_backend: auto
full_finetune_generation: true

pipeline:
_target_: unirl.models.sensenova_u1.pipeline.SenseNovaU1Pipeline
shift: ${bundle.config.timestep_shift}
autocast_precision: ${bundle.config.autocast_precision}
trajectory_precision: ${bundle.config.trajectory_precision}
logprob_precision: ${bundle.config.logprob_precision}
strategy:
_target_: unirl.sde.kernels.FlowSDEStrategy

backend:
_target_: unirl.train.backend.fsdp.FSDPBackend
block_class_names: ["Qwen3DecoderLayer", "NEOVisionModel"]
trainable_attr: transformer
fsdp_cfg:
_target_: unirl.train.configs.FSDPConfig
param_dtype: bf16
master_dtype: fp32
cpu_offload: false
mixed_precision: true
fsdp_mode: full
reshard_after_forward: true
activation_checkpointing: true
use_torch_compile: false
root_wrap: true
optimizer_cfg:
_target_: unirl.train.backend.base.OptimizerConfig
learning_rate: 2.0e-5
adam_beta1: 0.9
adam_beta2: 0.95
adam_epsilon: 1.0e-8
weight_decay: 0.0
scheduler_cfg:
_target_: unirl.train.backend.base.LrSchedulerConfig
type: constant
warmup_steps: 0
total_steps: ${num_rollouts}

rollout:
_target_: unirl.rollout.engine.vllm_omni.engine.VLLMOmniRolloutEngine
model_config: ${bundle.config}
config:
_target_: unirl.rollout.engine.vllm_omni.config.VLLMOmniEngineConfig
model_path: ${bundle.config.pretrained_model_ckpt_path}
modality: sensenova_u1_t2i
enable_sleep_mode: true

reward:
_target_: unirl.reward.service.RewardService
backend:
_target_: unirl.reward.local.pickscore.PickScoreRewardScorer
base_device: cuda
config:
_target_: unirl.reward.local.pickscore.PickScoreSpec
batch_size: 4
device: auto
processor_id: laion/CLIP-ViT-H-14-laion2B-s32B-b79K
model_id: yuvalkirstain/PickScore_v1

algorithm:
_target_: unirl.algorithms.flowgrpo.FlowGRPO
stage_attr: diffusion
old_logp_source: replay
clip_range: 1.0e-4
clip_schedule: constant
conditions_cls:
_target_: hydra.utils.get_class
path: unirl.models.sensenova_u1.conditions.SenseNovaU1Conditions
params: ${sampling}

stack:
_target_: unirl.train.stack.TrainStack
micro_batch_size: 1
max_grad_norm: 1.0
num_updates_per_batch: 2

data_source:
_target_: unirl.data.data_source.MultimodalRLDataSource
args:
run:
data_path: datasets/pickscore/train.txt
eval_data_path: datasets/pickscore/test.txt
seed: 42
algorithm:
prompts_per_rollout: ${batch_size}

sampling:
_target_: unirl.models.sensenova_u1.diffusion.SenseNovaU1DiffusionParams
num_inference_steps: 20
guidance_scale: 4.0
cfg_norm: global
cfg_interval: [0.0, 1.0]
t_eps: 0.02
height: 512
width: 512
eta: 0.7
samples_per_prompt: 4
seed: 42
init_same_noise: false
scheduler:
_target_: unirl.sde.index_schedule.AllSDEScheduler
num_timesteps: ${..num_inference_steps}
num_sde_steps: 3
timestep_fraction: [0.0, 0.5]

sync:
_target_: unirl.distributed.weight_sync.full.ipc.IPCWeightSync
bucket_size_mb: 2048
use_shm: false
flush_cache: true
wire_dtype: bf16
# Strip SenseNovaU1TrainableModel's facade prefix. vLLM-Omni exposes the
# same children directly as language_model.*, vision_model.*, fm_modules.*.
name_remap: {"model.*": "*"}
4 changes: 2 additions & 2 deletions examples/run_reward_curve_verification_taiji.sh
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,8 @@ else
require_torch_flavor "2.11.0+cu129"
fi
if [ "${PROFILE}" = "sd3-vllm-omni" ] || [ "${PROFILE}" = "qwen-omni" ]; then
require_dist_version "vllm" "0.20.0"
require_dist_version "vllm-omni" "0.20.0"
require_dist_version "vllm" "0.22.0+cu129"
require_dist_version "vllm-omni" "0.22.0"
fi

WANDB_OVERRIDES=(
Expand Down
Loading
Loading