Skip to main content 홈 크리에이터 adu2021 skillxiv t-lora-single-image-diffusion-customization
t-lora-single-image-diffusion-customization Personalize diffusion models to learn a concept from a single image without overfitting by using timestep-dependent rank masking and orthogonal weight initialization, enabling faithful concept reproduction while maintaining text-guided control.
설치로 이동 Skills Marketplace 커뮤니티가 만든 AI 스킬을 발견하고 탐색하세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/ADu2021/skillXiv --skill t-lora-single-image-diffusion-customization명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Zip 다운로드 다운로드 중... name t-lora-single-image-diffusion-customization title T-LoRA: Single Image Diffusion Model Customization Without Overfitting version 0.0.2 engine skillxiv-v0.0.2-claude-opus-4.6 license MIT url https://arxiv.org/abs/2507.05964 keywords ["Diffusion Models","Single-Image Personalization","LoRA","Timestep-Dependent Adaptation","Orthogonal Initialization"] description Personalize diffusion models to learn a concept from a single image without overfitting by using timestep-dependent rank masking and orthogonal weight initialization, enabling faithful concept reproduction while maintaining text-guided control.
T-LoRA: Timestep-Dependent Personalization of Diffusion Models
Training diffusion models on a single concept image is notoriously difficult. Standard LoRA quickly overfits: the model memorizes the exact image instead of learning the concept. The root cause is that different denoising timesteps have different susceptibility to overfitting. Early timesteps (high noise) have natural regularization. Late timesteps (low noise, near-pixel level) are prone to memorizing exact pixel patterns. T-LoRA addresses this by dynamically adjusting adapter rank as a function of timestep: fewer parameters at noisy timesteps, more at clean ones.
A complementary insight: standard LoRA matrices accumulate linear dependence that undermines selective activation. T-LoRA applies orthogonal initialization using SVD components, ensuring that masked rank subsets remain effective for meaningful feature learning.
Core Concept
The challenge is learning a visual concept (a person, object, style) from one image while keeping the model generalizable. T-LoRA exploits the fact that the denoising process has natural structure: early timesteps handle coarse structure and are hard to overfit, late timesteps handle details and are easy to overfit. By allocating fewer adapter parameters early and more late, the model can focus on learning genuine concept features rather than pixel memorization.
Orthogonal weight initialization complements this by ensuring that even when you mask out certain rank components, the remaining ones stay active and meaningful. Without orthogonality, masking can create dead components.
Architecture Overview
Timestep-Dependent Rank Function : r(t) = floor((r - r_min) * (T - t) / T) + r_min, allocating higher rank at lower timesteps
Rank Masking : Diagonal matrices selectively activate adapter components during training and inference
Orthogonal LoRA Initialization : Decompose random matrices via SVD, use lower singular components to avoid overfitting
Integrated T-LoRA : Combines both mechanisms for optimal performance
Target Models : Tested on Stable Diffusion-XL and FLUX-1.dev
Implementation
Step 1: Analyze Timestep-Specific Overfitting
First, diagnose which timesteps overfit most by training with fixed-rank adapters and measuring concept fidelity vs. text alignment:
import torch
import torch.nn as nn
from diffusers import StableDiffusionXLPipeline
from peft import get_peft_model, LoraConfig
( ):
pipeline = StableDiffusionXLPipeline.from_pretrained(
)
unet = pipeline.unet
lora_config = LoraConfig(
r= ,
lora_alpha= ,
target_modules=[ , ],
lora_dropout= ,
bias=
)
lora_unet = get_peft_model(unet, lora_config)
optimizer = torch.optim.Adam(lora_unet.parameters(), lr= )
step ( ):
timesteps = torch.randint( , num_timesteps, ( ,))
noisy_image = add_noise_to_concept(concept_image, timesteps[ ])
noise_pred = lora_unet(
noisy_image,
timesteps[ ],
encoder_hidden_states=encode_text(concept_name, pipeline)
).sample
loss = F.mse_loss(noise_pred, torch.randn_like(noise_pred))
loss.backward()
optimizer.step()
optimizer.zero_grad()
fidelity_scores = {}
alignment_scores = {}
t ( , num_timesteps, ):
samples_t = generate_from_concept(
lora_unet, pipeline,
concept_name,
timestep_focus=t,
num_samples=
)
concept_score = measure_clip_similarity(
samples_t, concept_image
)
fidelity_scores[t] = concept_score
random_prompts = [ , , ]
alignment = measure_prompt_alignment(samples_t, random_prompts)
alignment_scores[t] = alignment
( , fidelity_scores)
( , alignment_scores)
( )
fidelity_scores, alignment_scores
( ):
diffusers.schedulers DDPMScheduler
scheduler = DDPMScheduler.from_config(scheduler_config {})
noise = torch.randn_like(image)
noisy = scheduler.add_noise(image, noise, timestep)
noisy
def
diagnose_overfitting_per_timestep
concept_image, concept_name,
test_prompts, num_timesteps=1000
"""
Train fixed-rank LoRA and measure overfitting across timesteps.
Reveals which timesteps are most prone to memorization.
"""
"stabilityai/stable-diffusion-xl-base-1.0"
64
128
"to_q"
"to_v"
0.0
"none"
1e-4
for
in
range
500
0
4
0
0
for
in
range
0
100
5
"a cat"
"a dog"
"a car"
print
"Fidelity by timestep:"
print
"Alignment by timestep:"
print
"Conclusion: higher timesteps likely show memorization"
return
def
add_noise_to_concept
image, timestep, scheduler_config=None
"""Add noise at a specific timestep to the concept image."""
from
import
or
return
Step 2: Implement Vanilla T-LoRA with Rank Masking Allocate fewer parameters at high timesteps (noisy) and more at low timesteps (clean):
class TLoRA (nn.Module):
def __init__ (self, base_module, r_min=4 , r_max=64 , num_timesteps=1000 ):
super ().__init__()
self .base_module = base_module
self .r_min = r_min
self .r_max = r_max
self .num_timesteps = num_timesteps
self .lora_a = nn.Parameter(torch.randn(base_module.in_features, r_max) * 0.01 )
self .lora_b = nn.Parameter(torch.randn(r_max, base_module.out_features) * 0.01 )
self .timestep_to_rank = self ._make_rank_schedule()
def _make_rank_schedule (self ):
"""Create mapping from timestep to active rank."""
schedule = {}
for t in range (self .num_timesteps):
r_t = int ((self .r_max - self .r_min) * (1 - t / self .num_timesteps) + self .r_min)
schedule[t] = r_t
return schedule
def forward (self, x, timestep ):
"""Apply LoRA with timestep-dependent rank masking."""
r_active = self .timestep_to_rank.get(int (timestep.item()), self .r_max)
mask_a = torch.zeros(self .lora_a.shape[1 ], device=self .lora_a.device)
mask_a[:r_active] = 1.0
mask_b = torch.zeros(self .lora_b.shape[0 ], device=self .lora_b.device)
mask_b[:r_active] = 1.0
lora_a_masked = self .lora_a * mask_a
lora_b_masked = self .lora_b * mask_b
lora_out = x @ lora_a_masked @ lora_b_masked
base_out = self .base_module(x)
return base_out + lora_out
def apply_tora_to_unet (unet, r_min=4 , r_max=64 , num_timesteps=1000 ):
"""Replace linear layers in UNet with T-LoRA modules."""
for name, module in unet.named_modules():
if isinstance (module, nn.Linear) and "to_q" in name or "to_v" in name:
tora_module = TLoRA(module, r_min, r_max, num_timesteps)
setattr (unet, name, tora_module)
return unet
Step 3: Implement Orthogonal Weight Initialization (Ortho-LoRA) Initialize LoRA weights using orthogonal components from SVD to prevent linear dependence:
class OrthoLoRA (nn.Module):
"""LoRA with orthogonal weight initialization to prevent overfitting."""
def __init__ (self, base_module, r=64 , lora_alpha=128 ):
super ().__init__()
self .base_module = base_module
self .r = r
self .lora_alpha = lora_alpha
self .lora_a = nn.Parameter(self ._ortho_init(base_module.in_features, r))
self .lora_b = nn.Parameter(self ._ortho_init(r, base_module.out_features))
self .scale = lora_alpha / r
def _ortho_init (self, in_dim, out_dim ):
"""
Initialize with orthogonal components from SVD.
Use lower singular components (least significant) to avoid overfitting.
"""
random_matrix = torch.randn(in_dim, out_dim)
U, S, Vt = torch.linalg.svd(random_matrix, full_matrices=False )
if in_dim >= out_dim:
ortho = U[:, :out_dim]
else :
ortho = Vt[:in_dim, :]
return ortho * 0.01
def forward (self, x ):
"""Apply orthogonal LoRA."""
lora_out = (x @ self .lora_a) @ self .lora_b * self .scale
base_out = self .base_module(x)
return base_out + lora_out
Step 4: Integrated T-LoRA (Timestep-Dependent + Orthogonal) Combine both mechanisms for robust single-image personalization:
class IntegratedTLoRA (nn.Module):
"""Combined T-LoRA: timestep-dependent rank + orthogonal initialization."""
def __init__ (self, base_module, r_min=4 , r_max=64 , num_timesteps=1000 ):
super ().__init__()
self .base_module = base_module
self .r_min = r_min
self .r_max = r_max
self .num_timesteps = num_timesteps
self .lora_a = nn.Parameter(self ._ortho_init(base_module.in_features, r_max))
self .lora_b = nn.Parameter(self ._ortho_init(r_max, base_module.out_features))
def _ortho_init (self, in_dim, out_dim ):
"""Orthogonal initialization."""
random_matrix = torch.randn(in_dim, out_dim)
U, S, Vt = torch.linalg.svd(random_matrix, full_matrices=False )
if in_dim >= out_dim:
return U[:, :out_dim] * 0.01
else :
return Vt[:in_dim, :] * 0.01
def _get_rank_for_timestep (self, timestep ):
"""Get active rank based on timestep."""
t = timestep.item() if isinstance (timestep, torch.Tensor) else timestep
r_t = int ((self .r_max - self .r_min) * (1 - t / self .num_timesteps) + self .r_min)
return max (self .r_min, min (self .r_max, r_t))
def forward (self, x, timestep ):
"""Apply T-LoRA with timestep-dependent rank masking."""
r_active = self ._get_rank_for_timestep(timestep)
mask_a = torch.zeros_like(self .lora_a)
mask_a[:, :r_active] = 1.0
mask_b = torch.zeros_like(self .lora_b)
mask_b[:r_active, :] = 1.0
lora_a_masked = self .lora_a * mask_a
lora_b_masked = self .lora_b * mask_b
lora_out = (x @ lora_a_masked) @ lora_b_masked
base_out = self .base_module(x)
return base_out + lora_out
def train_concept_with_tlora (concept_image, concept_name, model_name="stabilityai/stable-diffusion-xl-base-1.0" ):
"""Train T-LoRA on a single concept image."""
pipeline = StableDiffusionXLPipeline.from_pretrained(model_name)
unet = pipeline.unet
tora_modules = {}
for name, module in unet.named_modules():
if isinstance (module, nn.Linear) and ("to_q" in name or "to_v" in name):
tora_mod = IntegratedTLoRA(module, r_min=4 , r_max=64 )
tora_modules[name] = tora_mod
optimizer = torch.optim.Adam(
[p for m in tora_modules.values() for p in m.parameters()],
lr=1e-4
)
for step in range (800 ):
t = torch.randint(0 , 1000 , (1 ,))
noisy_concept = add_noise_to_concept(concept_image, t)
prompts = [
f"a photo of {concept_name} " ,
f"{concept_name} in the style of Van Gogh" ,
f"close-up of {concept_name} " ,
f"{concept_name} , professional photography"
]
prompt = prompts[step % len (prompts)]
text_embeddings = pipeline.encode_prompt(prompt)
noise_pred = unet(
noisy_concept,
t,
encoder_hidden_states=text_embeddings
).sample
loss = F.mse_loss(noise_pred, torch.randn_like(noise_pred))
loss.backward()
optimizer.step()
optimizer.zero_grad()
if step % 100 == 0 :
print (f"Step {step} : loss = {loss.item():.4 f} " )
return pipeline, tora_modules
Practical Guidance Component Recommended Value Notes r_min (minimum rank) 4 Constrains high-timestep (noisy) parameters r_max (maximum rank) 64 Can go to 128 for complex concepts Learning Rate 1e-4 Conservative to avoid instability Training Steps 500-800 SD-XL; fewer for simpler models Batch Size 1 Single image personalization Optimizer AdamW Standard choice Timestep Range 0-1000 Full DDPM schedule Orthogonal Init Yes Critical for preventing overfitting Rank Mask Yes Essential for timestep-specific control CLIP Similarity > 0.85 Target for concept fidelity Prompt Alignment > 0.75 Target for maintaining text control
Single-image personalization (concept learning from one photo)
Applications prioritizing concept fidelity over memory efficiency
Fine art, illustration, or photographic style transfer
Scenarios where both concept accuracy and text control matter
Multi-image personalization (standard DreamBooth scales better)
Memory-constrained deployment (LoRA still requires adapter parameters)
Extremely complex concepts requiring hundreds of reference images
Real-time inference (training time is acceptable, inference is fast)
Not including orthogonal initialization, causing rank masking to be ineffective
r_min too high (> 8), losing ability to regulate overfitting
Timestep-dependent rank too aggressive (inverted), encouraging memorization
Training for too few steps (< 300), concept not properly learned
Using too-high learning rate, causing instability
Not normalizing concept image to [0, 1] range
Forgetting to vary prompts during training, leading to prompt-specific overfitting
Reference Wang, X., Li, X., Chen, H., & Zhou, L. (2025). T-LoRA: Single Image Diffusion Model Customization Without Overfitting. arXiv:2507.05964. https://arxiv.org/abs/2507.05964