개요

Open-Sora는 세 가지 핵심 컴포넌트로 구성된 복잡한 아키텍처를 가지고 있습니다. 이번 포스트에서는 각 컴포넌트의 구조와 역할, 그리고 서로 어떻게 상호작용하는지 상세히 분석해보겠습니다.

전체 아키텍처 개요

graph TB subgraph "Open-Sora Complete Architecture" A[Text Input] --> B[T5 Text Encoder] C[Video/Image Input] --> D[VAE Encoder] B --> E[Text Embeddings] D --> F[Latent Representation] E --> G[STDiT3 Transformer] F --> G H[Noise] --> G G --> I[Denoised Latent] I --> J[VAE Decoder] J --> K[Generated Video/Image] subgraph "T5 Text Processing" B1[Tokenization] B2[Self-Attention] B3[Feed Forward] B4[Layer Norm] B --> B1 --> B2 --> B3 --> B4 --> E end subgraph "VAE Processing" D1[Spatial Convolution] D2[Temporal Convolution] D3[Latent Distribution] D --> D1 --> D2 --> D3 --> F J1[Latent Sampling] J2[Spatial Deconvolution] J3[Temporal Deconvolution] I --> J1 --> J2 --> J3 --> K end subgraph "STDiT3 Diffusion" G1[Space-Time Attention] G2[Cross Attention] G3[MLP Blocks] G4[Adaptive Layer Norm] F --> G1 --> G2 --> G3 --> G4 --> I E -.-> G2 end end style A fill:#e1f5fe style K fill:#c8e6c9 style G fill:#ffcdd2

Open-Sora는 다음 세 가지 주요 모델로 구성됩니다:

Text Input → T5 Text Encoder → Text Embeddings
                                      ↓
Video/Image → VAE Encoder → Latent Space → STDiT3 Diffusion → Denoised Latent
                                                                      ↓
                                              VAE Decoder ← Generated Video/Image

핵심 구성 요소

  1. T5 Text Encoder: 텍스트 프롬프트를 임베딩으로 변환
  2. VAE (Variational AutoEncoder): 비디오/이미지를 잠재 공간으로 압축/복원
  3. STDiT3 (Space-Time Diffusion Transformer): 잠재 공간에서 확산 과정 수행

1. T5 Text Encoder 분석

graph TB subgraph "T5 Text Encoder Architecture" A[Text Input] --> B[T5 Tokenizer] B --> C[Token Embeddings] C --> D[Positional Encoding] D --> E[Encoder Stack] subgraph "T5 Encoder Layers" E --> F[Self-Attention] F --> G[Add & Norm] G --> H[Feed Forward] H --> I[Add & Norm] I --> J[Next Layer] end J --> K[Final Layer Norm] K --> L[Text Embeddings Output] subgraph "Attention Mechanism" M[Query] N[Key] O[Value] F --> M F --> N F --> O M --> P[Scaled Dot-Product] N --> P O --> P end subgraph "Configuration Options" Q[T5-Base: 12 layers] R[T5-Large: 24 layers] S[Max Length: 512 tokens] T[Multi-language Support] end end style A fill:#e1f5fe style L fill:#c8e6c9 style F fill:#ffcdd2

구조 및 특징

@MODELS.register_module("text_embedder")
class HFEmbedder(nn.Module):
    def __init__(self, from_pretrained: str, max_length: int, shardformer: bool = False, **hf_kwargs):
        super().__init__()
        self.is_clip = "openai" in from_pretrained
        self.max_length = max_length
        self.output_key = "pooler_output" if self.is_clip else "last_hidden_state"

        if self.is_clip:
            self.tokenizer: CLIPTokenizer = CLIPTokenizer.from_pretrained(from_pretrained, max_length=max_length)
            self.hf_module: CLIPTextModel = CLIPTextModel.from_pretrained(from_pretrained, **hf_kwargs)
        else:
            self.tokenizer: T5Tokenizer = T5Tokenizer.from_pretrained(
                from_pretrained, max_length=max_length, legacy=True
            )
            self.hf_module: T5EncoderModel = T5EncoderModel.from_pretrained(from_pretrained, **hf_kwargs)

T5 vs CLIP 선택

T5 선택 이유:

  • 더 긴 시퀀스 처리: 복잡한 비디오 설명 가능
  • 다국어 지원: 한국어 포함 다양한 언어
  • 강력한 텍스트 이해: 문맥적 관계 파악

Shardformer 최적화

def shardformer_t5(t5: T5EncoderModel) -> T5EncoderModel:
    """T5 모델을 샤딩하여 최적화"""
    dtype = t5.shared.weight.dtype
    shard_config = ShardConfig(
        enable_tensor_parallelism=False,  # 텐서 병렬화 비활성화
        enable_jit_fused=True,           # JIT 융합 최적화 활성화
    )
    shard_former = ShardFormer(shard_config=shard_config)
    optim_model, _ = shard_former.optimize(t5, policy=T5EncoderPolicy())
    return optim_model.to(dtype).eval().requires_grad_(False)

최적화 효과:

  • JIT 융합: 연산 그래프 최적화로 속도 향상
  • 메모리 효율성: 그래디언트 계산 비활성화
  • 정밀도 보존: 원본 데이터 타입 유지

텍스트 처리 파이프라인

def forward(self, text: list[str], added_tokens: int = 0, seq_align: int = 1) -> Tensor:
    # 1. 토크나이징
    batch_encoding = self.tokenizer(
        text,
        truncation=True,
        max_length=self.max_length,
        return_length=False,
        return_overflowing_tokens=False,
        padding="max_length",
        return_tensors="pt",
    )
    
    # 2. 시퀀스 정렬 (배치 처리 최적화)
    seq_len = batch_encoding["input_ids"].shape[1]
    if (added_tokens + seq_len) % seq_align != 0:
        num_pad_tokens = seq_align - (added_tokens + seq_len) % seq_align
        batch_encoding["input_ids"] = nn.functional.pad(
            batch_encoding["input_ids"], (0, num_pad_tokens), value=self.tokenizer.pad_token_id
        )

    # 3. 임베딩 생성
    outputs = self.hf_module(
        input_ids=batch_encoding["input_ids"].to(self.hf_module.device),
        attention_mask=None,
        output_hidden_states=False,
    )
    return outputs[self.output_key]

2. VAE (Variational AutoEncoder) 분석

AutoEncoder 구성

@dataclass
class AutoEncoderConfig:
    from_pretrained: str | None
    cache_dir: str | None
    resolution: int          # 해상도
    in_channels: int         # 입력 채널 수
    ch: int                  # 기본 채널 수
    out_ch: int             # 출력 채널 수
    ch_mult: list[int]      # 채널 배수 (다운샘플링용)
    num_res_blocks: int     # 잔차 블록 수
    z_channels: int         # 잠재 공간 채널 수
    scale_factor: float     # 스케일 팩터
    shift_factor: float     # 시프트 팩터
    sample: bool = True     # 샘플링 여부

Attention Block 구조

class AttnBlock(nn.Module):
    def __init__(self, in_channels: int):
        super().__init__()
        self.in_channels = in_channels
        
        # Group Normalization
        self.norm = nn.GroupNorm(
            num_groups=32, 
            num_channels=in_channels, 
            eps=1e-6, 
            affine=True
        )
        
        # Query, Key, Value 프로젝션
        self.q = nn.Conv2d(in_channels, in_channels, 1, 1, 0)
        self.k = nn.Conv2d(in_channels, in_channels, 1, 1, 0)
        self.v = nn.Conv2d(in_channels, in_channels, 1, 1, 0)
        
        # 출력 프로젝션
        self.proj_out = nn.Conv2d(in_channels, in_channels, 1, 1, 0)

핵심 특징:

  • Self-Attention: 공간적 관계 학습
  • Group Normalization: 배치 크기에 무관한 정규화
  • 1x1 Convolution: 효율적인 특징 변환

가우시안 분포 처리

class DiagonalGaussianDistribution:
    def __init__(self, parameters: Tensor, deterministic: bool = False):
        self.parameters = parameters
        self.mean, self.logvar = torch.chunk(parameters, 2, dim=1)
        self.logvar = torch.clamp(self.logvar, -30.0, 20.0)
        self.deterministic = deterministic
        self.std = torch.exp(0.5 * self.logvar)
        self.var = torch.exp(self.logvar)

    def sample(self) -> Tensor:
        if self.deterministic:
            return self.mean
        else:
            return self.mean + self.std * torch.randn_like(self.mean)

역할:

  • 잠재 공간 샘플링: 확률적 인코딩
  • 정규화: 잠재 벡터의 분포 제어
  • 안정성: logvar 클램핑으로 수치 안정성

3. STDiT3 (Space-Time Diffusion Transformer)

아키텍처 개요

STDiT3는 Open-Sora의 핵심 생성 모델로, 다음과 같은 특징을 가집니다:

# Gradio 앱에서의 STDiT3 로딩
from opensora.models.stdit.stdit3 import STDiT3

model_kwargs = {k: v for k, v in config.model.items() 
                if k not in ("type", "from_pretrained", "force_huggingface")}

print("Load STDIT3 from ", weight_path)
stdit = STDiT3.from_pretrained(weight_path, **model_kwargs).cuda()

핵심 혁신 사항

1. Space-Time Attention

  • 공간적 어텐션: 프레임 내 픽셀 간 관계
  • 시간적 어텐션: 프레임 간 시간적 연속성
  • 통합 처리: 3D 어텐션으로 효율적 처리

2. 다중 해상도 지원

# 해상도별 모델 가중치
HF_STDIT_MAP = {
    "t2v": {
        "360p": "hpcaitech/OpenSora-STDiT-v4-360p",
        "720p": "hpcaitech/OpenSora-STDiT-v4",
    },
    "i2v": "hpcaitech/OpenSora-STDiT-v4-i2v",
}

3. 조건부 생성

  • 텍스트 조건: T5 임베딩 활용
  • 이미지 조건: 첫 프레임 고정
  • 모션 점수: 동작 강도 제어

확산 과정 (Diffusion Process)

# 샘플링 과정
samples = scheduler.sample(
    stdit,                    # STDiT3 모델
    text_encoder,            # T5 텍스트 인코더
    z=z,                     # 초기 노이즈
    z_cond=ref,              # 조건부 잠재 벡터
    z_cond_mask=x_cond_mask, # 조건부 마스크
    prompts=batch_prompts_loop,  # 배치 프롬프트
    device=device,
    additional_args=model_args,
    progress=True,
    mask=masks,
    mask_index=mask_index,
    image_cfg_scale=image_cfg_scale,
    use_sdedit=use_sdedit,
    use_oscillation_guidance_for_text=use_oscillation_guidance_for_text,
    use_oscillation_guidance_for_image=use_oscillation_guidance_for_image,
)

모델 간 상호작용

1. 텍스트 → 비디오 생성 파이프라인

def build_models(mode, resolution, enable_optimization=False):
    """모델 구축 및 초기화"""
    
    # 1. VAE 구축
    vae = build_module(config.vae, MODELS).cuda()
    
    # 2. 텍스트 인코더 구축
    text_encoder = build_module(config.text_encoder, MODELS)
    text_encoder.t5.model = text_encoder.t5.model.cuda()
    
    # 3. STDiT3 구축
    stdit = STDiT3.from_pretrained(weight_path, **model_kwargs).cuda()
    
    # 4. 스케줄러 구축
    scheduler = build_module(config.scheduler, SCHEDULERS)
    
    # 5. CFG를 위한 임베더 연결
    text_encoder.y_embedder = stdit.y_embedder
    
    # 6. 최적화 및 평가 모드
    vae = vae.to(torch.bfloat16).eval()
    text_encoder.t5.model = text_encoder.t5.model.eval()
    stdit = stdit.to(torch.bfloat16).eval()
    
    return vae, text_encoder, stdit, scheduler, config

2. 이미지 → 비디오 생성 파이프라인

# 참조 이미지 처리
refs = collect_references_batch(refs, vae, image_size)

# 조건부 참조 준비
if mode == "i2v":
    image_cfg_scale = config.get("image_cfg_scale", 7.5)
    ref, mask_index = prep_ref_and_mask(
        cond_type, condition_frame_length, refs, target_shape, 
        num_loop, device, dtype
    )

성능 최적화 기법

1. 메모리 최적화

# 모델을 bfloat16으로 변환
vae = vae.to(torch.bfloat16).eval()
stdit = stdit.to(torch.bfloat16).eval()

# T5는 fp32 유지 (정확도 보장)
text_encoder.t5.model = text_encoder.t5.model.eval()

2. 분산 처리 지원

# 멀티 GPU 추론
torchrun --nproc_per_node 8 --standalone scripts/diffusion/inference.py \
    configs/diffusion/inference/t2i2v_768px.py \
    --save-dir samples --prompt "raining, sea"

3. 동적 메모리 관리

# CUDA 메모리 정리
torch.cuda.empty_cache()

# 그래디언트 비활성화
with torch.inference_mode():
    # 추론 코드
    pass

모델 크기 및 성능

파라미터 수

  • STDiT3: 11B 파라미터 (메인 생성 모델)
  • T5: 약 3B 파라미터 (텍스트 인코더)
  • VAE: 약 83M 파라미터 (인코더/디코더)

메모리 요구사항

  • 256x256 해상도: 최소 32GB GPU 메모리
  • 768x768 해상도: 최소 80GB GPU 메모리 (A100 권장)

추론 시간

  • 256x256, 5초 비디오: 약 60초 (H100 1GPU)
  • 768x768, 5초 비디오: 약 276초 (H100 8GPU)

훈련 전략

1. 단계별 훈련

  1. VAE 사전 훈련: 이미지/비디오 압축 학습
  2. 텍스트 인코더 고정: T5 가중치 동결
  3. STDiT3 훈련: 확산 과정 학습

2. 다중 해상도 훈련

# 해상도별 점진적 학습
resolutions = ["144p", "360p", "720p"]
for resolution in resolutions:
    train_model(resolution)

3. 데이터 효율성

  • 캡션 품질: 고품질 텍스트-비디오 쌍
  • 다양성: 다양한 장르와 스타일
  • 길이 변화: 2초~15초 비디오

실제 활용 시나리오

1. 콘텐츠 제작

# 소셜 미디어 숏폼 생성
prompt = "A cat playing with a ball in a sunny garden, cute and playful"
video = generate_video(prompt, aspect_ratio="9:16", length="5s")

2. 영상 편집

# 기존 이미지에서 비디오 생성
reference_image = load_image("portrait.jpg")
prompt = "The person smiles and nods gently"
video = generate_video(prompt, reference_image=reference_image)

3. 프로토타이핑

# 광고 컨셉 시각화
prompt = "Modern minimalist product showcase, elegant lighting"
video = generate_video(prompt, resolution="720p", motion_score=3)

결론

Open-Sora의 아키텍처는 모듈화최적화의 완벽한 조합입니다.

핵심 장점:

  • 분업화: 각 컴포넌트의 명확한 역할 분담
  • 확장성: 해상도와 길이에 따른 유연한 스케일링
  • 효율성: bfloat16, Flash Attention 등 최신 최적화 기법
  • 범용성: 텍스트-투-비디오, 이미지-투-비디오 모두 지원

이러한 설계 철학은 다른 멀티모달 생성 모델 개발에도 중요한 참고 자료가 될 것입니다. 다음 포스트에서는 Gradio를 활용한 웹 인터페이스 구현을 살펴보겠습니다.


이 글이 도움이 되셨다면 공유해주세요! 궁금한 점이 있으시면 댓글로 남겨주시기 바랍니다.