본문 바로가기
Data & Research

[Deep Learning] TimeDistributed

by 물박사의 저장공간 2025. 11. 24.

2025.03.15 - [Data & Research] - [ML & DL 기초] Table of Contents


TensorFlow에서 사용하는 TimeDistributed의 사용방법에 대해 알아보고 Pytorch에서는 어떻게 구현하는지 알아보겠습니다. 

TimeDistributed는 시계열 데이터의 각 Time Step동일한 층(layer)을 독립적으로 적용(weight 공유)할 때 사용하는 wrapper입니다. 

 

1. TensorFlow의 TimeDistributed

예시1) 각 프레임(시간 축)마다 CNN으로 영상 특징 추출 → 그 결과를 LSTM에 전달

TimeDistributed(Conv2D(...))(video_input)

 

예시2) 각 시점마다 분류해야 하는 문제 (POS tagging, NER, sequence labeling 등)

TimeDistributed(Dense(num_classes, activation='softmax'))(lstm_output)

 

 

자칫 잘못하면 RNN계열 Layer들의 return_sequences=True 와 뭐가 다른가? 헷갈릴 수도 있는데 RNN layer의 return_sequence는 RNN 수행 후 모든 시점의 hidden state를 반환하는 것이구요. 

TimeDistributed 같은 경우에는 Time Step 간 연결이 없습니다(마치 시간 축 차원을 Batch 축 차원처럼 간주한다고 보면 됩니다). Time Step 에 각각 동일한 Layer를 적용하는 겁니다. 

 

아래 예시를 좀 더 구체적으로 살펴볼까요?

import tensorflow as tf
from tensorflow.keras import layers, models

# 입력 형태: (Batch, Frames, Height, Width, Channels)
# 예: 10개의 프레임, 64x64 RGB 이미지
input_shape = (10, 64, 64, 3)

model = models.Sequential()

# 1. TimeDistributed(Conv2D): 10개의 프레임 각각에 대해 동일한 Conv 연산 수행
model.add(layers.TimeDistributed(
    layers.Conv2D(32, (3, 3), activation='relu'),
    input_shape=input_shape
))
model.add(layers.TimeDistributed(layers.MaxPooling2D((2, 2))))

# 2. TimeDistributed(Flatten): 각 프레임의 3D Feature Map을 1D 벡터로 변환
# (Batch, 10, H', W', 32) -> (Batch, 10, Features)
model.add(layers.TimeDistributed(layers.Flatten()))

# 3. LSTM: 시간적 특징 학습
model.add(layers.LSTM(64, return_sequences=False))

# 4. Final Classification
model.add(layers.Dense(1, activation='sigmoid'))

model.summary()

 

model.add(layers.TimeDistributed(layers.Flatten())) 가 구체적으로 어떻게 작동하는 Step-by-Step으로 살펴봅시다. 

 

1단계: 차원 병합 (Folding) - Wrapper

TimeDistributed 레이어는 입력을 받으면 가장 먼저 Batch 차원과 Time 차원을 하나로 합칩니다. 이를 통해 5차원 텐서를 4차원으로 바꿉니다.

 

2단계: 레이어 적용 (Apply Layer)

이제 변형된 텐서가 Flatten 레이어로 들어갑니다. Flatten은 무조건 첫 번째 차원(index 0)을 유지합니다.

 

3단계: 차원 복원 (Unfolding)

Flatten 작업이 끝나면, TimeDistributed는 아까 합쳤던 차원을 다시 원래대로 분리합니다.

 

2. Pytorch에서의 구현

이걸 pytorch에서 구현한다면 어떻게 될까요?

PyTorch에는 Keras의 TimeDistributed처럼 전용 래퍼(Wrapper) 클래스가 공식적으로 존재하지 않습니다. 대신 PyTorch의 철학인 "Explicit is better than implicit (명시적인 것이 암시적인 것보다 낫다)" 에 따라, forward 함수 내부에서 텐서의 차원(Shape)을 직접 조작(Reshape/View) 하여 동일한 효과를 냅니다.

 

import torch
import torch.nn as nn

class TimeDistributedCRNN(nn.Module):
    def __init__(self):
        super(TimeDistributedCRNN, self).__init__()
        
        # 1. 이미지 특징 추출을 위한 CNN 정의 (각 프레임용)
        self.cnn = nn.Sequential(
            nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)
        )
        
        # 2. 시계열 처리를 위한 LSTM 정의
        # CNN 출력 크기 계산 필요: 64x64 -> 32x32 (Pooling 후), 채널 32
        # Flatten 후 크기: 32 * 32 * 32 = 32768
        self.lstm = nn.LSTM(input_size=32*32*32, hidden_size=64, batch_first=True)
        
        # 3. 분류기
        self.fc = nn.Linear(64, 1)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        # 입력 x의 형태: (Batch, Time, Channels, Height, Width)
        # 예: (B, 10, 3, 64, 64)
        b, t, c, h, w = x.size()

        # --- [Step 1: TimeDistributed(Conv2D) 구현] ---
        
        # 1) Folding: Batch와 Time 축을 합침
        # (B, 10, 3, 64, 64) -> (B*10, 3, 64, 64)
        x = x.view(b * t, c, h, w) 
        
        # 2) Layer Apply: CNN 통과 (마치 B*10장의 이미지를 처리하듯)
        # (B*10, 32, 32, 32)
        x = self.cnn(x) 
        
        # --- [Step 2: TimeDistributed(Flatten) 구현] ---
        
        # 여기서는 별도의 Unfolding 없이 바로 Flatten을 수행할 수 있습니다.
        # (B*10, 32, 32, 32) -> (B*10, 32768)
        x = x.view(b * t, -1)
        
        # --- [Step 3: RNN 입력을 위한 Unfolding] ---
        
        # 3) Unfolding: 다시 Batch와 Time 축 분리
        # LSTM에 넣기 위해 (Batch, Time, Feature) 형태로 복원
        # (B*10, 32768) -> (B, 10, 32768)
        x = x.view(b, t, -1) 
        
        # --- [Step 4: LSTM 및 결과] ---
        
        # LSTM 통과 (Batch, Time, Hidden) -> (Batch, Time, 64)
        # output: 전체 시퀀스 출력, (h_n, c_n): 마지막 hidden state
        lstm_out, (h_n, c_n) = self.lstm(x)
        
        # 마지막 타임스텝의 결과만 사용한다고 가정 (Many-to-One)
        # (Batch, 64)
        last_out = lstm_out[:, -1, :]
        
        out = self.fc(last_out)
        return self.sigmoid(out)

# 모델 테스트
input_tensor = torch.randn(5, 10, 3, 64, 64) # (Batch, Time, C, H, W)
model = TimeDistributedCRNN()
output = model(input_tensor)
print(f"Input shape: {input_tensor.shape}")
print(f"Output shape: {output.shape}") # (5, 1)

 

참고) PyTorch의 nn.Linear (Dense layer)나 nn.RNN, nn.LSTM 등은 기본적으로 다차원 입력을 지원합니다.