Instructions to use CornLogic/10EROS_1.4_Int8_ConvRot with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use CornLogic/10EROS_1.4_Int8_ConvRot with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline from diffusers.utils import load_image, export_to_video # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("CornLogic/10EROS_1.4_Int8_ConvRot", torch_dtype=torch.bfloat16, device_map="cuda") pipe.to("cuda") prompt = "A man with short gray hair plays a red electric guitar." image = load_image( "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/guitar-man.png" ) output = pipe(image=image, prompt=prompt).frames[0] export_to_video(output, "output.mp4") - Notebooks
- Google Colab
- Kaggle
Script for lora-merge
Hi is it possible to share your script you used to merge the DMD lora onto the diff model. I am trying to write one but it's not working out.
this is what i have so far, and it's breaking silently, want to pull my hair out π
import torch
from safetensors import safe_open
from safetensors.torch import load_file, save_file
from tqdm import tqdm
import time
BASE_MODEL = r"C:\ComfyUI\ComfyUI_windows_portable\ComfyUI\models\checkpoints\sulphur_dev_bf16.safetensors"
LORA = r"C:\ComfyUI\ComfyUI_windows_portable\ComfyUI\models\loras\LTX2.3_DMD_reshaped_r256.safetensors"
OUTPUT = r"C:\ComfyUI\ComfyUI_windows_portable\ComfyUI\models\checkpoints\sulphur_dev_bf16_DMD.safetensors"
STRENGTH = 1.0
# Preserve base metadata (LTX-2.3 carries audio VAE config here β dropping it
# breaks audio on the merged checkpoint).
print("Reading base metadata...")
with safe_open(BASE_MODEL, framework="pt", device="cpu") as f:
meta = f.metadata() or {}
print("Loading base model...")
base = load_file(BASE_MODEL)
print("Loading LoRA...")
lora = load_file(LORA)
merged = dict(base)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")
# Find every lora_down tensor
down_keys = [
k
for k in lora.keys()
if (k.endswith(".lora_A.weight") or k.endswith(".lora_down.weight"))
]
applied = 0
skipped_missing = 0
skipped_shape = 0
skipped_nd = 0
with torch.no_grad():
print("Building lookup ...")
lookup = {}
for k in merged:
lookup[k.split("model.")[-1]] = k
for down_key in tqdm(down_keys):
try:
up_key = down_key.replace(".lora_A.weight", ".lora_B.weight")
alpha_key = down_key.replace(".lora_A.weight", ".alpha")
suffix = down_key.replace(".lora_A.weight", ".weight")
suffix = suffix.split("diffusion_model.")[-1]
base_key = lookup.get(suffix)
if base_key not in merged:
skipped_missing += 1
continue
if up_key not in lora:
print(f"Missing lora_up for {down_key}")
skipped_missing += 1
continue
W = merged[base_key]
# Only 2D Linear weights are LoRA-targetable. Anything else (modulation
# tables, norms) must be left alone.
if W.dim() != 2:
print(f"Non-2D, skipping {base_key} {tuple(W.shape)}")
skipped_nd += 1
continue
A = lora[down_key]
B = lora[up_key]
rank = A.shape[0]
if alpha_key in lora:
alpha = float(lora[alpha_key].item())
else:
alpha = rank
scale = (alpha / rank) * STRENGTH
# Verify matrix multiplication dimensions BEFORE allocating delta
if B.shape[1] != A.shape[0]:
print(
f"Rank mismatch: {base_key} " f"A{tuple(A.shape)} B{tuple(B.shape)}"
)
skipped_shape += 1
continue
# Verify resulting matrix will match the checkpoint weight
expected = (B.shape[0], A.shape[1])
if expected != W.shape:
print(
f"Shape mismatch: {base_key} "
f"W{tuple(W.shape)} Expected{expected}"
)
skipped_shape += 1
continue
# Safe to compute
t = time.perf_counter()
delta = torch.matmul(B.float(), A.float())
elapsed = time.perf_counter() - t
delta.mul_(scale)
Wf = W.float()
Wf.add_(delta)
merged[base_key] = Wf.to(W.dtype)
del Wf
applied += 1
del A, B, delta
if applied % 50 == 0:
print(
f"{applied}/{len(down_keys)} "
f"{base_key} "
f"{time.perf_counter()-t:.3f}s",
flush=True,
)
if applied % 100 == 0:
print(
f"{applied}/{len(down_keys)} " f"elapsed={elapsed:.3f}s", flush=True
)
except Exception as e:
print("\nFAILED")
print("Layer:", down_key)
print("Base :", base_key if "base_key" in locals() else "<unknown>")
print("Error:", repr(e))
raise
print(f"\nApplied: {applied}")
print(f"Skipped (no base):{skipped_missing}")
print(f"Skipped (shape): {skipped_shape}")
print(f"Skipped (non-2D): {skipped_nd}")
if applied == 0:
print(
"\nWARNING: 0 keys applied β nothing was merged. Check that the LoRA "
"key names match the base (naming convention mismatch?)."
)
print("\nSaving...")
save_file(merged, OUTPUT, metadata=meta)
print("Done!")
Edit: Actually I shouldn't comment on code while I'm too sleepy to read it properly haha
I'm away from home working remotely for a while, I'll try and find what I used tomorrow if that helps?
Hey man, get to it whenever you have time, I'm not in a rush for this one :)
Couldn't find my original script but I saw the convo with 10Strip on discord and his snippet looked pretty close to what I used. Praise be to Claude for helping me with my terrible python.
import torch
from safetensors import safe_open
from safetensors.torch import load_file, save_file
from tqdm import tqdm
BASE_MODEL = "base.safetensors"
LORA = "lora.safetensors"
OUTPUT = "output.safetensors"
STRENGTH = 1.0
# Preserve base metadata (LTX-2.3 carries audio VAE config here β dropping it
# breaks audio on the merged checkpoint).
print("Reading base metadata...")
with safe_open(BASE_MODEL, framework="pt", device="cpu") as f:
meta = f.metadata() or {}
print("Loading base model...")
base = load_file(BASE_MODEL)
print("Loading LoRA...")
lora = load_file(LORA)
# NOTE: operate on `base` directly rather than merged = dict(base).
# A shallow dict copy keeps every original tensor alive via `base`'s
# own references even after we overwrite the entry in `merged`, roughly
# doubling peak RAM across every touched layer. Aliasing instead lets
# each replaced tensor's old bf16 copy get garbage-collected immediately
# once merged[base_key] is reassigned.
merged = base
# Find every lora_A tensor (this LoRA uses PEFT/diffusers-style naming:
# lora_A/lora_B, not lora_down/lora_up)
down_keys = [k for k in lora.keys() if "lora_A.weight" in k]
applied = 0
skipped_missing = 0
skipped_shape = 0
skipped_nd = 0
for down_key in tqdm(down_keys):
up_key = down_key.replace("lora_A", "lora_B")
alpha_key = down_key.replace(".lora_A.weight", ".alpha")
# Base model keys carry an extra "model." prefix that the LoRA doesn't
# have (LoRA: "diffusion_model...." / base: "model.diffusion_model....")
base_key = "model." + down_key.replace(".lora_A.weight", ".weight")
if base_key not in merged:
skipped_missing += 1
continue
if up_key not in lora:
print(f"Missing lora_up for {down_key}")
skipped_missing += 1
continue
W = merged[base_key]
# Only 2D Linear weights are LoRA-targetable. Anything else (modulation
# tables, norms) must be left alone.
if W.dim() != 2:
print(f"Non-2D, skipping {base_key} {tuple(W.shape)}")
skipped_nd += 1
continue
A = lora[down_key]
B = lora[up_key]
rank = A.shape[0]
if alpha_key in lora:
alpha = float(lora[alpha_key].item())
else:
alpha = rank
scale = alpha / rank
delta = torch.matmul(B.float(), A.float())
delta *= scale
delta *= STRENGTH
# CRITICAL: without this, a mismatched delta silently BROADCASTS and
# corrupts the weight with no error. LTX-2.3 has architectural tensors
# (e.g. scale_shift_table 9-vs-6 slot differences) that hit this.
if delta.shape != W.shape:
print(f"Shape mismatch, skipping {base_key}: "
f"W{tuple(W.shape)} vs delta{tuple(delta.shape)}")
skipped_shape += 1
continue
merged[base_key] = W + delta.to(W.dtype)
applied += 1
del A, B, delta
print(f"\nApplied: {applied}")
print(f"Skipped (no base):{skipped_missing}")
print(f"Skipped (shape): {skipped_shape}")
print(f"Skipped (non-2D): {skipped_nd}")
if applied == 0:
print("\nWARNING: 0 keys applied β nothing was merged. Check that the LoRA "
"key names match the base (naming convention mismatch?).")
print("\nSaving...")
save_file(merged, OUTPUT, metadata=meta)
print("Done!")
aah yes, that was me asking TenStrip for help on a script i wrote, i gave up because it couldn't find the weights, unfortunately i don't have any vibe coding skills :( . I'm too old school for that lol, Thank you for the above brother, will test it and make modifications where needed <3