Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bc59bd939 |
No files matched your search
@@ -457,6 +457,51 @@ steps:
|
|||||||
path: /mnt/hf-cache
|
path: /mnt/hf-cache
|
||||||
type: DirectoryOrCreate
|
type: DirectoryOrCreate
|
||||||
|
|
||||||
|
- label: "Diffusion · Helios Distilled Acc/Perf Compare"
|
||||||
|
commands:
|
||||||
|
- |
|
||||||
|
timeout 90m bash -c "
|
||||||
|
set -euxo pipefail
|
||||||
|
export VLLM_IMAGE_FETCH_TIMEOUT=60
|
||||||
|
pytest -s -v tests/e2e/accuracy/test_helios_distilled.py::test_helios_distilled_pr_matches_main_acc_perf \
|
||||||
|
-m 'full_model and benchmark' \
|
||||||
|
--run-level 'full_model'
|
||||||
|
"
|
||||||
|
- buildkite-agent artifact upload "tests/e2e/accuracy/artifacts/helios_distilled_acc_perf/result.json"
|
||||||
|
- buildkite-agent artifact upload "tests/e2e/accuracy/artifacts/helios_distilled_acc_perf/artifacts/**/*.mp4"
|
||||||
|
agents:
|
||||||
|
queue: "mithril-h100-pool"
|
||||||
|
plugins:
|
||||||
|
- kubernetes:
|
||||||
|
podSpec:
|
||||||
|
containers:
|
||||||
|
- image: 936637512419.dkr.ecr.us-west-2.amazonaws.com/vllm-ci-pull-through-cache/q9t5s3a7/vllm-ci-test-repo:$BUILDKITE_COMMIT
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
nvidia.com/gpu: 1
|
||||||
|
volumeMounts:
|
||||||
|
- name: devshm
|
||||||
|
mountPath: /dev/shm
|
||||||
|
- name: hf-cache
|
||||||
|
mountPath: /root/.cache/huggingface
|
||||||
|
env:
|
||||||
|
- name: HF_HOME
|
||||||
|
value: /root/.cache/huggingface
|
||||||
|
- name: HF_TOKEN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: hf-token-secret
|
||||||
|
key: token
|
||||||
|
nodeSelector:
|
||||||
|
node.kubernetes.io/instance-type: gpu-h100-sxm
|
||||||
|
volumes:
|
||||||
|
- name: devshm
|
||||||
|
emptyDir:
|
||||||
|
medium: Memory
|
||||||
|
- name: hf-cache
|
||||||
|
hostPath:
|
||||||
|
path: /mnt/hf-cache
|
||||||
|
type: DirectoryOrCreate
|
||||||
|
|
||||||
- label: "Diffusion · Qwen Image Test"
|
- label: "Diffusion · Qwen Image Test"
|
||||||
commands:
|
commands:
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ async def async_request_v1_videos(
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
# invoke a poll request (GET /v1/videos/{video_id})
|
# invoke a poll request (GET /v1/videos/{video_id})
|
||||||
poll_interval = 2.0 # Unit(s)
|
poll_interval = 0.2 # Unit(s)
|
||||||
timeout_seconds = 600.0
|
timeout_seconds = 600.0
|
||||||
deadline = time.perf_counter() + timeout_seconds
|
deadline = time.perf_counter() + timeout_seconds
|
||||||
job_url = f"{input.api_url}/{job_id}"
|
job_url = f"{input.api_url}/{job_id}"
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ import glob
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import pathlib
|
||||||
import random
|
import random
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
@@ -598,6 +599,13 @@ class RandomDataset(BaseDataset):
|
|||||||
if self.enable_negative_prompt:
|
if self.enable_negative_prompt:
|
||||||
extra_body["negative_prompt"] = f"Negative prompt {idx} for benchmarking diffusion models"
|
extra_body["negative_prompt"] = f"Negative prompt {idx} for benchmarking diffusion models"
|
||||||
|
|
||||||
|
request_fields = {
|
||||||
|
"width",
|
||||||
|
"height",
|
||||||
|
"num_frames",
|
||||||
|
"num_inference_steps",
|
||||||
|
"fps",
|
||||||
|
}
|
||||||
params = {
|
params = {
|
||||||
"width": self.args.width,
|
"width": self.args.width,
|
||||||
"height": self.args.height,
|
"height": self.args.height,
|
||||||
@@ -607,9 +615,11 @@ class RandomDataset(BaseDataset):
|
|||||||
}
|
}
|
||||||
if self._sampled_requests:
|
if self._sampled_requests:
|
||||||
profile = self._sampled_requests[idx]
|
profile = self._sampled_requests[idx]
|
||||||
params.update(profile)
|
params.update({key: value for key, value in profile.items() if key in request_fields})
|
||||||
|
extra_body.update({key: value for key, value in profile.items() if key not in request_fields})
|
||||||
|
prompt = self.args.random_prompt or f"Random prompt {idx} for benchmarking diffusion models"
|
||||||
return RequestFuncInput(
|
return RequestFuncInput(
|
||||||
prompt=f"Random prompt {idx} for benchmarking diffusion models",
|
prompt=prompt,
|
||||||
api_url=self.api_url,
|
api_url=self.api_url,
|
||||||
model=self.model,
|
model=self.model,
|
||||||
seed=self.args.seed,
|
seed=self.args.seed,
|
||||||
@@ -869,6 +879,35 @@ def calculate_metrics(
|
|||||||
return metrics
|
return metrics
|
||||||
|
|
||||||
|
|
||||||
|
def _response_suffix(endpoint: str) -> str:
|
||||||
|
if endpoint == "/v1/videos":
|
||||||
|
return ".mp4"
|
||||||
|
return ".json"
|
||||||
|
|
||||||
|
|
||||||
|
def _save_success_responses(
|
||||||
|
outputs: list[RequestFuncOutput],
|
||||||
|
requests_list: list[RequestFuncInput],
|
||||||
|
output_dir: str,
|
||||||
|
endpoint: str,
|
||||||
|
) -> list[str]:
|
||||||
|
path = pathlib.Path(output_dir).expanduser().resolve()
|
||||||
|
path.mkdir(parents=True, exist_ok=True)
|
||||||
|
saved_paths: list[str] = []
|
||||||
|
suffix = _response_suffix(endpoint)
|
||||||
|
|
||||||
|
for index, (request, output) in enumerate(zip(requests_list, outputs, strict=False)):
|
||||||
|
if not output.success:
|
||||||
|
continue
|
||||||
|
item_path = path / f"{index:05d}_{request.request_id}{suffix}"
|
||||||
|
if isinstance(output.response_body, bytes):
|
||||||
|
item_path.write_bytes(output.response_body)
|
||||||
|
else:
|
||||||
|
item_path.write_text(json.dumps(output.response_body, indent=2, sort_keys=True), encoding="utf-8")
|
||||||
|
saved_paths.append(str(item_path))
|
||||||
|
return saved_paths
|
||||||
|
|
||||||
|
|
||||||
def wait_for_service(base_url: str, timeout: int = 120) -> None:
|
def wait_for_service(base_url: str, timeout: int = 120) -> None:
|
||||||
print(f"Waiting for service at {base_url}...")
|
print(f"Waiting for service at {base_url}...")
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
@@ -998,6 +1037,13 @@ async def benchmark(args):
|
|||||||
metrics["model"] = args.model
|
metrics["model"] = args.model
|
||||||
metrics["dataset"] = args.dataset
|
metrics["dataset"] = args.dataset
|
||||||
metrics["task"] = args.task
|
metrics["task"] = args.task
|
||||||
|
if args.save_response_dir:
|
||||||
|
metrics["saved_responses"] = _save_success_responses(
|
||||||
|
outputs,
|
||||||
|
requests_list,
|
||||||
|
args.save_response_dir,
|
||||||
|
args.endpoint,
|
||||||
|
)
|
||||||
|
|
||||||
print("\n{s:{c}^{n}}".format(s=" Serving Benchmark Result ", n=50, c="="))
|
print("\n{s:{c}^{n}}".format(s=" Serving Benchmark Result ", n=50, c="="))
|
||||||
|
|
||||||
@@ -1154,6 +1200,12 @@ if __name__ == "__main__":
|
|||||||
)
|
)
|
||||||
parser.add_argument("--fps", type=int, default=None, help="FPS (for video).")
|
parser.add_argument("--fps", type=int, default=None, help="FPS (for video).")
|
||||||
parser.add_argument("--output-file", type=str, default=None, help="Output JSON file for metrics.")
|
parser.add_argument("--output-file", type=str, default=None, help="Output JSON file for metrics.")
|
||||||
|
parser.add_argument(
|
||||||
|
"--save-response-dir",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help="Directory to save successful raw responses. Video responses are saved as MP4 files.",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--slo",
|
"--slo",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
@@ -1189,6 +1241,12 @@ if __name__ == "__main__":
|
|||||||
'{"width":768,"height":768,"num_inference_steps":20,"weight":0.85}]'
|
'{"width":768,"height":768,"num_inference_steps":20,"weight":0.85}]'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--random-prompt",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help="Prompt to use for every synthetic random request. Defaults to generated placeholder prompts.",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--num-input-images",
|
"--num-input-images",
|
||||||
type=int,
|
type=int,
|
||||||
|
|||||||
@@ -0,0 +1,222 @@
|
|||||||
|
# Helios-Distilled Serving Performance Dashboard
|
||||||
|
|
||||||
|
This document describes how to deploy and benchmark **BestWishYsh/Helios-Distilled** using vLLM-Omni. It follows the same serving benchmark structure as the Qwen-Image and Wan2.2 dashboards.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. Overview
|
||||||
|
|
||||||
|
Helios-Distilled is a text-to-video diffusion model. Helios uses an autoregressive chunking pattern that generates 33 frames per chunk. For stable comparisons, choose `num_frames` as a multiple of 33, such as 99.
|
||||||
|
|
||||||
|
This document covers:
|
||||||
|
|
||||||
|
* Service launch configuration
|
||||||
|
* Benchmark scripts and usage
|
||||||
|
* Dataset and workload settings
|
||||||
|
* Performance and accuracy comparison methodology
|
||||||
|
* Reproducibility guidelines
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. Test Environment
|
||||||
|
|
||||||
|
| Component | Specification |
|
||||||
|
|------------|----------------|
|
||||||
|
| GPU | NVIDIA H100 for CI accuracy/perf gate; local bring-up also used L20X |
|
||||||
|
| Diffusion Attention Backend | FlashAttention |
|
||||||
|
| Model | `BestWishYsh/Helios-Distilled` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. Service Launch Configuration
|
||||||
|
|
||||||
|
## 3.1 Basic Serving Command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vllm serve BestWishYsh/Helios-Distilled --omni \
|
||||||
|
--port 8091
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3.2 Key Parameters
|
||||||
|
|
||||||
|
| Parameter | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `--port` | Serving port |
|
||||||
|
| `--max-num-seqs` | Maximum number of active sequences |
|
||||||
|
| `--enable-diffusion-pipeline-profiler` | Optional stage-level profiling |
|
||||||
|
| `--profiler-config` | Optional torch profiler configuration |
|
||||||
|
|
||||||
|
Record these parameters when reporting performance results.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. Benchmark Scripts
|
||||||
|
|
||||||
|
## 4.1 Online Serving Benchmark
|
||||||
|
|
||||||
|
Use the shared diffusion serving benchmark, as with Qwen-Image and Wan2.2:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python benchmarks/diffusion/diffusion_benchmark_serving.py \
|
||||||
|
--base-url http://localhost:8091 \
|
||||||
|
--endpoint /v1/videos \
|
||||||
|
--model BestWishYsh/Helios-Distilled \
|
||||||
|
--dataset random \
|
||||||
|
--task t2v \
|
||||||
|
--num-prompts 1 \
|
||||||
|
--max-concurrency 1 \
|
||||||
|
--disable-tqdm \
|
||||||
|
--random-request-config '[
|
||||||
|
{
|
||||||
|
"width": 640,
|
||||||
|
"height": 384,
|
||||||
|
"num_inference_steps": 50,
|
||||||
|
"num_frames": 99,
|
||||||
|
"fps": 16,
|
||||||
|
"guidance_scale": 1.0,
|
||||||
|
"is_enable_stage2": true,
|
||||||
|
"pyramid_num_stages": 3,
|
||||||
|
"pyramid_num_inference_steps_list": [1, 1, 1],
|
||||||
|
"is_amplify_first_chunk": false,
|
||||||
|
"weight": 1
|
||||||
|
}
|
||||||
|
]'
|
||||||
|
```
|
||||||
|
|
||||||
|
`--random-request-config` forwards standard fields such as `width`, `height`, `num_frames`, `fps`, and `num_inference_steps` as first-class request fields. Model-specific fields are forwarded in the request body, which is required for Helios stage-2 settings.
|
||||||
|
|
||||||
|
## 4.2 Warmed Serving Benchmark
|
||||||
|
|
||||||
|
For local bring-up, use the same shared serving benchmark with one warmup request and one measured request:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python benchmarks/diffusion/diffusion_benchmark_serving.py \
|
||||||
|
--base-url http://localhost:8091 \
|
||||||
|
--endpoint /v1/videos \
|
||||||
|
--model BestWishYsh/Helios-Distilled \
|
||||||
|
--dataset random \
|
||||||
|
--task t2v \
|
||||||
|
--num-prompts 1 \
|
||||||
|
--max-concurrency 1 \
|
||||||
|
--warmup-requests 1 \
|
||||||
|
--warmup-num-inference-steps 1 \
|
||||||
|
--random-prompt "A cat wearing sunglasses dances on a beach at sunset, cinematic lighting." \
|
||||||
|
--seed 42 \
|
||||||
|
--disable-tqdm \
|
||||||
|
--output-file metrics.json \
|
||||||
|
--save-response-dir responses \
|
||||||
|
--random-request-config '[
|
||||||
|
{
|
||||||
|
"width": 640,
|
||||||
|
"height": 384,
|
||||||
|
"num_inference_steps": 50,
|
||||||
|
"num_frames": 99,
|
||||||
|
"fps": 16,
|
||||||
|
"guidance_scale": 1.0,
|
||||||
|
"is_enable_stage2": true,
|
||||||
|
"pyramid_num_stages": 3,
|
||||||
|
"pyramid_num_inference_steps_list": [1, 1, 1],
|
||||||
|
"is_amplify_first_chunk": false,
|
||||||
|
"weight": 1
|
||||||
|
}
|
||||||
|
]'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4.3 PR-vs-Main Accuracy and Performance Comparison
|
||||||
|
|
||||||
|
The PR-vs-main accuracy/performance gate lives in the Helios accuracy pytest. It checks out `origin/main`, starts a server for each checkout, and drives both with `diffusion_benchmark_serving.py`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pytest -s -v tests/e2e/accuracy/test_helios_distilled.py::test_helios_distilled_pr_matches_main_acc_perf \
|
||||||
|
-m 'full_model and benchmark' \
|
||||||
|
--run-level 'full_model'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. Dataset & Workload Settings
|
||||||
|
|
||||||
|
## 5.1 Recommended Evaluation Configurations
|
||||||
|
|
||||||
|
### Dataset A (99 frames, 384x640)
|
||||||
|
|
||||||
|
* Dataset: `random`
|
||||||
|
* Task: t2v
|
||||||
|
* Concurrency: 1
|
||||||
|
* Frame count: 99 (`33 x 3`)
|
||||||
|
* FPS: 16
|
||||||
|
* Stage-2 steps: `[1, 1, 1]`
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"width": 640,
|
||||||
|
"height": 384,
|
||||||
|
"num_inference_steps": 50,
|
||||||
|
"num_frames": 99,
|
||||||
|
"fps": 16,
|
||||||
|
"guidance_scale": 1.0,
|
||||||
|
"is_enable_stage2": true,
|
||||||
|
"pyramid_num_stages": 3,
|
||||||
|
"pyramid_num_inference_steps_list": [1, 1, 1],
|
||||||
|
"is_amplify_first_chunk": false,
|
||||||
|
"weight": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6. Performance Metrics
|
||||||
|
|
||||||
|
| Metric | Description | Unit |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Mean Latency | Mean request latency | seconds |
|
||||||
|
| P99 Latency | P99 request latency | seconds |
|
||||||
|
| Generated FPS | `num_frames / measured_time_s` | frames/second |
|
||||||
|
| Candidate/reference latency ratio | PR latency divided by reference latency | ratio |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 7. Accuracy Metrics
|
||||||
|
|
||||||
|
The PR-vs-main gate compares encoded video frames extracted from MP4 outputs:
|
||||||
|
|
||||||
|
| Metric | Threshold | Direction |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| PSNR | `>= 35 dB` | Higher is better |
|
||||||
|
| MAE | `<= 3.0 / 255` | Lower is better |
|
||||||
|
| Candidate/reference latency ratio | `<= 1.03` | Lower is better |
|
||||||
|
|
||||||
|
Local L20X bring-up for this PR produced:
|
||||||
|
|
||||||
|
| Variant | Measured Latency (ms) | Generated FPS |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `origin/main` | 5317.95 | 18.62 |
|
||||||
|
| Candidate PR | 5283.27 | 18.74 |
|
||||||
|
|
||||||
|
Encoded-video similarity:
|
||||||
|
|
||||||
|
| Metric | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| PSNR | 38.66 dB |
|
||||||
|
| MAE | 1.89 / 255 |
|
||||||
|
| Cosine similarity | 0.99978 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 8. Reproducibility Checklist
|
||||||
|
|
||||||
|
To ensure consistent and comparable benchmark results:
|
||||||
|
|
||||||
|
* Record GPU type
|
||||||
|
* Record vLLM and vLLM-Omni versions
|
||||||
|
* Record benchmark parameters: resolution, frame count, FPS, seed, concurrency, and stage-2 steps
|
||||||
|
* Use frame counts that are multiples of 33
|
||||||
|
* Keep prompt, seed, resolution, and sampling parameters identical when comparing PR vs main
|
||||||
|
* Ensure no background workload on GPUs during testing
|
||||||
|
* Use the same MP4 export and frame extraction path for both variants
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This document serves as the Helios-Distilled serving performance reference under vLLM-Omni.
|
||||||
@@ -0,0 +1,368 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import signal
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
from tempfile import TemporaryDirectory
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pytest
|
||||||
|
import torch
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
from tests.e2e.accuracy.helpers import reset_artifact_dir
|
||||||
|
from tests.helpers.mark import hardware_test
|
||||||
|
|
||||||
|
pytestmark = [pytest.mark.diffusion, pytest.mark.full_model]
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parents[3]
|
||||||
|
MODEL_NAME = "BestWishYsh/Helios-Distilled"
|
||||||
|
PROMPT = "A cat wearing sunglasses dances on a beach at sunset, cinematic lighting."
|
||||||
|
HEIGHT = 384
|
||||||
|
WIDTH = 640
|
||||||
|
NUM_FRAMES = 99
|
||||||
|
FPS = 16
|
||||||
|
SEED = 42
|
||||||
|
GUIDANCE_SCALE = 1.0
|
||||||
|
PYRAMID_NUM_INFERENCE_STEPS = (1, 1, 1)
|
||||||
|
MIN_PSNR_DB = 35.0
|
||||||
|
MAX_MAE = 3.0
|
||||||
|
MAX_TIME_RATIO = 1.03
|
||||||
|
WARMUP_REQUESTS = 1
|
||||||
|
WARMUP_NUM_INFERENCE_STEPS = 1
|
||||||
|
|
||||||
|
|
||||||
|
def _run(command: list[str], *, cwd: Path, env: dict[str, str] | None = None) -> subprocess.CompletedProcess[str]:
|
||||||
|
print("+ " + " ".join(command), flush=True)
|
||||||
|
return subprocess.run(
|
||||||
|
command,
|
||||||
|
cwd=str(cwd),
|
||||||
|
env=env,
|
||||||
|
check=True,
|
||||||
|
text=True,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _read_json(path: Path) -> dict[str, Any]:
|
||||||
|
return json.loads(path.read_text(encoding="utf-8"))
|
||||||
|
|
||||||
|
|
||||||
|
def _open_port(host: str = "127.0.0.1") -> int:
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||||
|
sock.bind((host, 0))
|
||||||
|
return int(sock.getsockname()[1])
|
||||||
|
|
||||||
|
|
||||||
|
def _wait_for_service(base_url: str, process: subprocess.Popen, *, timeout_s: float = 1200.0) -> None:
|
||||||
|
import requests
|
||||||
|
|
||||||
|
deadline = time.monotonic() + timeout_s
|
||||||
|
while time.monotonic() < deadline:
|
||||||
|
if process.poll() is not None:
|
||||||
|
raise RuntimeError(f"Server exited with code {process.returncode} before becoming ready.")
|
||||||
|
try:
|
||||||
|
response = requests.get(f"{base_url}/health", timeout=1)
|
||||||
|
if response.status_code == 200:
|
||||||
|
return
|
||||||
|
except requests.RequestException:
|
||||||
|
pass
|
||||||
|
time.sleep(2)
|
||||||
|
raise TimeoutError(f"Server at {base_url} did not become ready within {timeout_s:.0f}s.")
|
||||||
|
|
||||||
|
|
||||||
|
def _stop_process(process: subprocess.Popen) -> None:
|
||||||
|
if process.poll() is not None:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
os.killpg(process.pid, signal.SIGTERM)
|
||||||
|
process.wait(timeout=30)
|
||||||
|
except Exception:
|
||||||
|
os.killpg(process.pid, signal.SIGKILL)
|
||||||
|
process.wait(timeout=30)
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_frames(video_path: Path, output_dir: Path) -> list[Image.Image]:
|
||||||
|
output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
frame_pattern = output_dir / "frame_%05d.png"
|
||||||
|
_run(
|
||||||
|
[
|
||||||
|
"ffmpeg",
|
||||||
|
"-hide_banner",
|
||||||
|
"-loglevel",
|
||||||
|
"error",
|
||||||
|
"-i",
|
||||||
|
str(video_path),
|
||||||
|
"-vsync",
|
||||||
|
"0",
|
||||||
|
str(frame_pattern),
|
||||||
|
],
|
||||||
|
cwd=output_dir,
|
||||||
|
)
|
||||||
|
frame_paths = sorted(output_dir.glob("frame_*.png"))
|
||||||
|
if not frame_paths:
|
||||||
|
raise RuntimeError(f"No frames extracted from {video_path}")
|
||||||
|
return [Image.open(path).convert("RGB").copy() for path in frame_paths]
|
||||||
|
|
||||||
|
|
||||||
|
def _image_metrics(reference: np.ndarray, candidate: np.ndarray) -> dict[str, float]:
|
||||||
|
diff = reference.astype(np.float64) - candidate.astype(np.float64)
|
||||||
|
mse = float(np.mean(diff * diff))
|
||||||
|
mae = float(np.mean(np.abs(diff)))
|
||||||
|
max_abs = float(np.max(np.abs(diff)))
|
||||||
|
psnr_db = float("inf") if mse == 0.0 else 20.0 * math.log10(255.0) - 10.0 * math.log10(mse)
|
||||||
|
ref_flat = reference.astype(np.float64).reshape(-1)
|
||||||
|
cand_flat = candidate.astype(np.float64).reshape(-1)
|
||||||
|
ref_norm = float(np.linalg.norm(ref_flat))
|
||||||
|
cand_norm = float(np.linalg.norm(cand_flat))
|
||||||
|
if ref_norm == 0.0 and cand_norm == 0.0:
|
||||||
|
cosine = 1.0
|
||||||
|
else:
|
||||||
|
cosine = float(np.dot(ref_flat, cand_flat) / (ref_norm * cand_norm))
|
||||||
|
return {
|
||||||
|
"mae": mae,
|
||||||
|
"mse": mse,
|
||||||
|
"max_abs": max_abs,
|
||||||
|
"psnr_db": psnr_db,
|
||||||
|
"cosine_similarity": cosine,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _video_metrics(reference_frames: list[Image.Image], candidate_frames: list[Image.Image]) -> dict[str, Any]:
|
||||||
|
if len(reference_frames) != len(candidate_frames):
|
||||||
|
raise ValueError(f"Frame count mismatch: reference={len(reference_frames)} candidate={len(candidate_frames)}")
|
||||||
|
|
||||||
|
ref_stack = np.stack([np.asarray(frame, dtype=np.uint8) for frame in reference_frames], axis=0)
|
||||||
|
cand_stack = np.stack([np.asarray(frame, dtype=np.uint8) for frame in candidate_frames], axis=0)
|
||||||
|
mid = len(reference_frames) // 2
|
||||||
|
return {
|
||||||
|
"num_frames": len(reference_frames),
|
||||||
|
"frame0_metrics": _image_metrics(ref_stack[0], cand_stack[0]),
|
||||||
|
"mid_frame_index": mid,
|
||||||
|
"mid_frame_metrics": _image_metrics(ref_stack[mid], cand_stack[mid]),
|
||||||
|
"all_frames_metrics": _image_metrics(ref_stack, cand_stack),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _reference_repo(output_root: Path) -> Path:
|
||||||
|
configured = os.environ.get("VLLM_HELIOS_REFERENCE_REPO")
|
||||||
|
if configured:
|
||||||
|
reference = Path(configured).expanduser().resolve()
|
||||||
|
if not reference.exists():
|
||||||
|
raise FileNotFoundError(f"VLLM_HELIOS_REFERENCE_REPO does not exist: {reference}")
|
||||||
|
return reference
|
||||||
|
|
||||||
|
reference = output_root / "origin_main"
|
||||||
|
if reference.exists():
|
||||||
|
subprocess.run(["git", "worktree", "remove", "--force", str(reference)], cwd=str(REPO_ROOT), check=False)
|
||||||
|
if reference.exists():
|
||||||
|
shutil.rmtree(reference)
|
||||||
|
_run(["git", "fetch", "origin", "main"], cwd=REPO_ROOT)
|
||||||
|
_run(["git", "worktree", "add", "--detach", str(reference), "origin/main"], cwd=REPO_ROOT)
|
||||||
|
return reference
|
||||||
|
|
||||||
|
|
||||||
|
def _server_env(repo: Path) -> dict[str, str]:
|
||||||
|
env = dict(os.environ)
|
||||||
|
env["PYTHONPATH"] = str(repo) + (os.pathsep + env["PYTHONPATH"] if env.get("PYTHONPATH") else "")
|
||||||
|
env.setdefault("VLLM_WORKER_MULTIPROC_METHOD", "spawn")
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
def _benchmark_config() -> str:
|
||||||
|
return json.dumps(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"width": WIDTH,
|
||||||
|
"height": HEIGHT,
|
||||||
|
"num_inference_steps": 50,
|
||||||
|
"num_frames": NUM_FRAMES,
|
||||||
|
"fps": FPS,
|
||||||
|
"guidance_scale": GUIDANCE_SCALE,
|
||||||
|
"is_enable_stage2": True,
|
||||||
|
"pyramid_num_stages": 3,
|
||||||
|
"pyramid_num_inference_steps_list": list(PYRAMID_NUM_INFERENCE_STEPS),
|
||||||
|
"is_amplify_first_chunk": False,
|
||||||
|
"weight": 1,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _start_server(*, repo: Path, variant_dir: Path) -> tuple[subprocess.Popen, str]:
|
||||||
|
port = _open_port()
|
||||||
|
base_url = f"http://127.0.0.1:{port}"
|
||||||
|
log_file = (variant_dir / "server.log").open("w", encoding="utf-8")
|
||||||
|
command = [
|
||||||
|
sys.executable,
|
||||||
|
"-m",
|
||||||
|
"vllm_omni.entrypoints.cli.main",
|
||||||
|
"serve",
|
||||||
|
MODEL_NAME,
|
||||||
|
"--host",
|
||||||
|
"127.0.0.1",
|
||||||
|
"--port",
|
||||||
|
str(port),
|
||||||
|
"--omni",
|
||||||
|
]
|
||||||
|
print("+ " + " ".join(command), flush=True)
|
||||||
|
process = subprocess.Popen(
|
||||||
|
command,
|
||||||
|
cwd=str(repo),
|
||||||
|
env=_server_env(repo),
|
||||||
|
stdout=log_file,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
preexec_fn=os.setsid,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
_wait_for_service(base_url, process)
|
||||||
|
except Exception:
|
||||||
|
log_file.close()
|
||||||
|
_stop_process(process)
|
||||||
|
raise
|
||||||
|
log_file.close()
|
||||||
|
return process, base_url
|
||||||
|
|
||||||
|
|
||||||
|
def _run_serving_benchmark(*, repo: Path, variant_dir: Path, base_url: str) -> dict[str, Any]:
|
||||||
|
metrics_path = variant_dir / "metrics.json"
|
||||||
|
response_dir = variant_dir / "responses"
|
||||||
|
command = [
|
||||||
|
sys.executable,
|
||||||
|
str(REPO_ROOT / "benchmarks/diffusion/diffusion_benchmark_serving.py"),
|
||||||
|
"--base-url",
|
||||||
|
base_url,
|
||||||
|
"--endpoint",
|
||||||
|
"/v1/videos",
|
||||||
|
"--model",
|
||||||
|
MODEL_NAME,
|
||||||
|
"--dataset",
|
||||||
|
"random",
|
||||||
|
"--task",
|
||||||
|
"t2v",
|
||||||
|
"--num-prompts",
|
||||||
|
"1",
|
||||||
|
"--max-concurrency",
|
||||||
|
"1",
|
||||||
|
"--warmup-requests",
|
||||||
|
str(WARMUP_REQUESTS),
|
||||||
|
"--warmup-num-inference-steps",
|
||||||
|
str(WARMUP_NUM_INFERENCE_STEPS),
|
||||||
|
"--warmup-concurrency",
|
||||||
|
"1",
|
||||||
|
"--random-prompt",
|
||||||
|
PROMPT,
|
||||||
|
"--seed",
|
||||||
|
str(SEED),
|
||||||
|
"--disable-tqdm",
|
||||||
|
"--random-request-config",
|
||||||
|
_benchmark_config(),
|
||||||
|
"--output-file",
|
||||||
|
str(metrics_path),
|
||||||
|
"--save-response-dir",
|
||||||
|
str(response_dir),
|
||||||
|
]
|
||||||
|
completed = _run(command, cwd=repo, env=_server_env(repo))
|
||||||
|
(variant_dir / "benchmark.log").write_text(completed.stdout, encoding="utf-8")
|
||||||
|
metrics = _read_json(metrics_path)
|
||||||
|
saved_responses = metrics.get("saved_responses") or []
|
||||||
|
if len(saved_responses) != 1:
|
||||||
|
raise RuntimeError(f"Expected one saved video response, got {saved_responses}")
|
||||||
|
return {
|
||||||
|
"repo": str(repo),
|
||||||
|
"metrics": metrics,
|
||||||
|
"video": saved_responses[0],
|
||||||
|
"log": str(variant_dir / "benchmark.log"),
|
||||||
|
"server_log": str(variant_dir / "server.log"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _run_variant(*, label: str, repo: Path, output_root: Path) -> dict[str, Any]:
|
||||||
|
variant_dir = output_root / label
|
||||||
|
variant_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
process, base_url = _start_server(repo=repo, variant_dir=variant_dir)
|
||||||
|
try:
|
||||||
|
return _run_serving_benchmark(repo=repo, variant_dir=variant_dir, base_url=base_url)
|
||||||
|
finally:
|
||||||
|
_stop_process(process)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.benchmark
|
||||||
|
@hardware_test(res={"cuda": "H100"}, num_cards=1)
|
||||||
|
def test_helios_distilled_pr_matches_main_acc_perf(accuracy_artifact_root: Path) -> None:
|
||||||
|
if not torch.cuda.is_available():
|
||||||
|
pytest.skip("Helios-Distilled accuracy/perf comparison requires CUDA.")
|
||||||
|
|
||||||
|
output_root = reset_artifact_dir(accuracy_artifact_root / "helios_distilled_acc_perf")
|
||||||
|
reference = _reference_repo(output_root)
|
||||||
|
output_json = output_root / "result.json"
|
||||||
|
|
||||||
|
reference_result = _run_variant(label="reference", repo=reference, output_root=output_root)
|
||||||
|
candidate_result = _run_variant(label="candidate", repo=REPO_ROOT, output_root=output_root)
|
||||||
|
|
||||||
|
with TemporaryDirectory() as tmp:
|
||||||
|
tmp_dir = Path(tmp)
|
||||||
|
reference_frames = _extract_frames(Path(reference_result["video"]), tmp_dir / "reference_frames")
|
||||||
|
candidate_frames = _extract_frames(Path(candidate_result["video"]), tmp_dir / "candidate_frames")
|
||||||
|
output_metrics = _video_metrics(reference_frames, candidate_frames)
|
||||||
|
|
||||||
|
reference_time = float(reference_result["metrics"]["latency_mean"])
|
||||||
|
candidate_time = float(candidate_result["metrics"]["latency_mean"])
|
||||||
|
time_ratio = candidate_time / reference_time
|
||||||
|
result = {
|
||||||
|
"reference": reference_result,
|
||||||
|
"candidate": candidate_result,
|
||||||
|
"sampling": {
|
||||||
|
"model": MODEL_NAME,
|
||||||
|
"prompt": PROMPT,
|
||||||
|
"height": HEIGHT,
|
||||||
|
"width": WIDTH,
|
||||||
|
"num_frames": NUM_FRAMES,
|
||||||
|
"fps": FPS,
|
||||||
|
"seed": SEED,
|
||||||
|
"guidance_scale": GUIDANCE_SCALE,
|
||||||
|
"pyramid_num_inference_steps_list": list(PYRAMID_NUM_INFERENCE_STEPS),
|
||||||
|
"is_amplify_first_chunk": False,
|
||||||
|
},
|
||||||
|
"performance": {
|
||||||
|
"reference_measured_time_s": reference_time,
|
||||||
|
"candidate_measured_time_s": candidate_time,
|
||||||
|
"candidate_over_reference_time_ratio": time_ratio,
|
||||||
|
},
|
||||||
|
"output_metrics": output_metrics,
|
||||||
|
"thresholds": {
|
||||||
|
"min_psnr_db": MIN_PSNR_DB,
|
||||||
|
"max_mae": MAX_MAE,
|
||||||
|
"max_time_ratio": MAX_TIME_RATIO,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
output_json.write_text(json.dumps(result, indent=2, sort_keys=True, allow_nan=True), encoding="utf-8")
|
||||||
|
all_metrics = output_metrics["all_frames_metrics"]
|
||||||
|
print(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"output_json": str(output_json),
|
||||||
|
"candidate_over_reference_time_ratio": time_ratio,
|
||||||
|
"all_frames_psnr_db": all_metrics["psnr_db"],
|
||||||
|
"all_frames_mae": all_metrics["mae"],
|
||||||
|
"all_frames_cosine_similarity": all_metrics["cosine_similarity"],
|
||||||
|
},
|
||||||
|
indent=2,
|
||||||
|
allow_nan=True,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert all_metrics["psnr_db"] >= MIN_PSNR_DB
|
||||||
|
assert all_metrics["mae"] <= MAX_MAE
|
||||||
|
assert time_ratio <= MAX_TIME_RATIO
|
||||||
|
assert output_json.exists(), f"Expected Helios comparison result at {output_json}"
|
||||||
@@ -29,7 +29,6 @@ from vllm_omni.diffusion.models.helios.scheduling_helios import HeliosScheduler
|
|||||||
from vllm_omni.diffusion.models.progress_bar import ProgressBarMixin
|
from vllm_omni.diffusion.models.progress_bar import ProgressBarMixin
|
||||||
from vllm_omni.diffusion.profiler.diffusion_pipeline_profiler import DiffusionPipelineProfilerMixin
|
from vllm_omni.diffusion.profiler.diffusion_pipeline_profiler import DiffusionPipelineProfilerMixin
|
||||||
from vllm_omni.diffusion.request import OmniDiffusionRequest
|
from vllm_omni.diffusion.request import OmniDiffusionRequest
|
||||||
from vllm_omni.platforms import current_omni_platform
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from vllm.model_executor.layers.quantization.base_config import (
|
from vllm.model_executor.layers.quantization.base_config import (
|
||||||
@@ -504,7 +503,12 @@ class HeliosPipeline(nn.Module, CFGParallelMixin, ProgressBarMixin, DiffusionPip
|
|||||||
|
|
||||||
# Prepare frame indices
|
# Prepare frame indices
|
||||||
if keep_first_frame:
|
if keep_first_frame:
|
||||||
indices = torch.arange(0, sum([1, *history_sizes, num_latent_frames_per_chunk]))
|
indices = torch.arange(
|
||||||
|
0,
|
||||||
|
sum([1, *history_sizes, num_latent_frames_per_chunk]),
|
||||||
|
device=device,
|
||||||
|
dtype=torch.float32,
|
||||||
|
)
|
||||||
(
|
(
|
||||||
indices_prefix,
|
indices_prefix,
|
||||||
indices_latents_history_long,
|
indices_latents_history_long,
|
||||||
@@ -514,7 +518,12 @@ class HeliosPipeline(nn.Module, CFGParallelMixin, ProgressBarMixin, DiffusionPip
|
|||||||
) = indices.split([1, *history_sizes, num_latent_frames_per_chunk], dim=0)
|
) = indices.split([1, *history_sizes, num_latent_frames_per_chunk], dim=0)
|
||||||
indices_latents_history_short = torch.cat([indices_prefix, indices_latents_history_1x], dim=0)
|
indices_latents_history_short = torch.cat([indices_prefix, indices_latents_history_1x], dim=0)
|
||||||
else:
|
else:
|
||||||
indices = torch.arange(0, sum([*history_sizes, num_latent_frames_per_chunk]))
|
indices = torch.arange(
|
||||||
|
0,
|
||||||
|
sum([*history_sizes, num_latent_frames_per_chunk]),
|
||||||
|
device=device,
|
||||||
|
dtype=torch.float32,
|
||||||
|
)
|
||||||
(
|
(
|
||||||
indices_latents_history_long,
|
indices_latents_history_long,
|
||||||
indices_latents_history_mid,
|
indices_latents_history_mid,
|
||||||
@@ -531,6 +540,7 @@ class HeliosPipeline(nn.Module, CFGParallelMixin, ProgressBarMixin, DiffusionPip
|
|||||||
attention_kwargs = {}
|
attention_kwargs = {}
|
||||||
|
|
||||||
vae_dtype = self.vae.dtype
|
vae_dtype = self.vae.dtype
|
||||||
|
latent_chunks_to_decode = []
|
||||||
|
|
||||||
# Chunked denoising loop
|
# Chunked denoising loop
|
||||||
for k in range(num_latent_chunk):
|
for k in range(num_latent_chunk):
|
||||||
@@ -645,16 +655,15 @@ class HeliosPipeline(nn.Module, CFGParallelMixin, ProgressBarMixin, DiffusionPip
|
|||||||
slice(-num_latent_frames_per_chunk, None),
|
slice(-num_latent_frames_per_chunk, None),
|
||||||
)
|
)
|
||||||
|
|
||||||
current_latents = real_history_latents[index_slice].to(vae_dtype) / latents_std + latents_mean
|
if output_type != "latent":
|
||||||
current_video = self.vae.decode(current_latents, return_dict=False)[0]
|
current_latents = real_history_latents[index_slice].to(vae_dtype) / latents_std + latents_mean
|
||||||
|
latent_chunks_to_decode.append(current_latents)
|
||||||
|
|
||||||
if history_video is None:
|
if output_type != "latent" and latent_chunks_to_decode:
|
||||||
history_video = current_video
|
batched_latents = torch.cat(latent_chunks_to_decode, dim=0)
|
||||||
else:
|
batched_video = self.vae.decode(batched_latents, return_dict=False)[0]
|
||||||
history_video = torch.cat([history_video, current_video], dim=2)
|
history_video = torch.cat(list(batched_video.split(batch_size, dim=0)), dim=2)
|
||||||
|
|
||||||
if current_omni_platform.is_available():
|
|
||||||
current_omni_platform.empty_cache()
|
|
||||||
self._current_timestep = None
|
self._current_timestep = None
|
||||||
|
|
||||||
if output_type == "latent":
|
if output_type == "latent":
|
||||||
@@ -688,6 +697,9 @@ class HeliosPipeline(nn.Module, CFGParallelMixin, ProgressBarMixin, DiffusionPip
|
|||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
"""Single-stage denoising loop for one chunk."""
|
"""Single-stage denoising loop for one chunk."""
|
||||||
batch_size = latents.shape[0]
|
batch_size = latents.shape[0]
|
||||||
|
latents_history_short = latents_history_short.to(transformer_dtype)
|
||||||
|
latents_history_mid = latents_history_mid.to(transformer_dtype)
|
||||||
|
latents_history_long = latents_history_long.to(transformer_dtype)
|
||||||
do_true_cfg = guidance_scale > 1.0 and negative_prompt_embeds is not None
|
do_true_cfg = guidance_scale > 1.0 and negative_prompt_embeds is not None
|
||||||
|
|
||||||
with self.progress_bar(total=len(timesteps)) as pbar:
|
with self.progress_bar(total=len(timesteps)) as pbar:
|
||||||
@@ -702,9 +714,9 @@ class HeliosPipeline(nn.Module, CFGParallelMixin, ProgressBarMixin, DiffusionPip
|
|||||||
"indices_latents_history_short": indices_latents_history_short,
|
"indices_latents_history_short": indices_latents_history_short,
|
||||||
"indices_latents_history_mid": indices_latents_history_mid,
|
"indices_latents_history_mid": indices_latents_history_mid,
|
||||||
"indices_latents_history_long": indices_latents_history_long,
|
"indices_latents_history_long": indices_latents_history_long,
|
||||||
"latents_history_short": latents_history_short.to(transformer_dtype),
|
"latents_history_short": latents_history_short,
|
||||||
"latents_history_mid": latents_history_mid.to(transformer_dtype),
|
"latents_history_mid": latents_history_mid,
|
||||||
"latents_history_long": latents_history_long.to(transformer_dtype),
|
"latents_history_long": latents_history_long,
|
||||||
"attention_kwargs": attention_kwargs,
|
"attention_kwargs": attention_kwargs,
|
||||||
"return_dict": False,
|
"return_dict": False,
|
||||||
}
|
}
|
||||||
@@ -783,6 +795,9 @@ class HeliosPipeline(nn.Module, CFGParallelMixin, ProgressBarMixin, DiffusionPip
|
|||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
"""Pyramid multi-stage denoising for one chunk."""
|
"""Pyramid multi-stage denoising for one chunk."""
|
||||||
batch_size, num_channel, num_frames_lat, height, width = latents.shape
|
batch_size, num_channel, num_frames_lat, height, width = latents.shape
|
||||||
|
latents_history_short = latents_history_short.to(transformer_dtype)
|
||||||
|
latents_history_mid = latents_history_mid.to(transformer_dtype)
|
||||||
|
latents_history_long = latents_history_long.to(transformer_dtype)
|
||||||
|
|
||||||
# Downsample latents to the smallest pyramid level
|
# Downsample latents to the smallest pyramid level
|
||||||
latents_flat = latents.permute(0, 2, 1, 3, 4).reshape(batch_size * num_frames_lat, num_channel, height, width)
|
latents_flat = latents.permute(0, 2, 1, 3, 4).reshape(batch_size * num_frames_lat, num_channel, height, width)
|
||||||
@@ -853,9 +868,9 @@ class HeliosPipeline(nn.Module, CFGParallelMixin, ProgressBarMixin, DiffusionPip
|
|||||||
"indices_latents_history_short": indices_latents_history_short,
|
"indices_latents_history_short": indices_latents_history_short,
|
||||||
"indices_latents_history_mid": indices_latents_history_mid,
|
"indices_latents_history_mid": indices_latents_history_mid,
|
||||||
"indices_latents_history_long": indices_latents_history_long,
|
"indices_latents_history_long": indices_latents_history_long,
|
||||||
"latents_history_short": latents_history_short.to(transformer_dtype),
|
"latents_history_short": latents_history_short,
|
||||||
"latents_history_mid": latents_history_mid.to(transformer_dtype),
|
"latents_history_mid": latents_history_mid,
|
||||||
"latents_history_long": latents_history_long.to(transformer_dtype),
|
"latents_history_long": latents_history_long,
|
||||||
"attention_kwargs": attention_kwargs,
|
"attention_kwargs": attention_kwargs,
|
||||||
"return_dict": False,
|
"return_dict": False,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user