Deep Learning

[NLP] 멀티 모달 (Multi-Modality)과 이미지 캡셔닝

seoraroong 2024. 10. 29. 14:00

멀티 모달 (Multi-Modality)이란?

텍스트, 이미지, 오디오, 비디오 등 다양한 유형의 데이터(Modality)를 함께 사용하는 것을 말한다.

멀티 모달 모델 (Multi-Modality-Model)은 이러한 여러 유형의 데이터를 인식하고 이해할 수 있는 모델을 말한다.

개별 Modality에서 얻을 수 있는 정보 이상을 추출할 수 있으며, 더 풍부한 표현 학습 및 예측이 가능해진다.


멀티 모달 모델의 구성 요소

현재 사용되고 있는 멀티 모달 모델 (Multi-Modality-Model)의 기본적인 구조는 3가지 단계로 나누어볼 수 있다.

 

각 Modality별로 입력 인코딩 -> 여러개 데이터 type을 결합(fusion) -> 모델의 input으로 넣어 inference 도출

 

각 과정을 간단하게 이해해보자.

 

1. 각 Modality에는 데이터를 처리하는 개별 입력 모듈이 있다. 입력 모듈 (Input Modules)은 Modality별로 적합한 특징 추출기 (feature extractor)를 사용해 데이터를 임베딩 벡터로 변환한다.

입력 모듈의 예
- Text Modality: RNN(LSTM, GRU), Transformer, BERT 등 자연어 처리에 사용되는 신경망구조
- Image Modality: CNN을 사용해 feature map 추출
- Audio Modality: MFCC(Mel-frequency cepstral coeffiecients), RNN, CNN

 

2. Modality를 결합하는 중앙처리부(Fusion Layer)에서 Modality 간의 정보를 어떻게 결합하고 상호작용할지 결정하게 된다. 각 Modality의 임베딩 벡터가 결합된 이후 공유된 표현 (Shared Representation)으로 변환해 상호작용과 통합을 이룬다.

Modality 결합 방법의 예시
- 단순 결합 (Simple Fusion): 각 Modality의 임베딩 벡터를 연결해 하나의 벡터로 결합하는 방법
- 어텐션 기반 결합 (Attention-based Fusion): 어텐션 메커니즘을 사용해 각 Modality의 중요도를 동적으로 평가, 중요한                                                                          Modality에 더 높은 가중치를 부여하는 방식
- 교차 모달리티 결합 (Cross-Modality Fusion): 각 Modality 간의 상호작용을 교차 주의(cross attention) 방식으로 처리                                                                              하는 방법, 예를 들어 Text Modality의 정보를 기반으로 Image Modality                                                                              의  특정 부분에 주목

 

3. 모델이 통합된 정보를 바탕으로 최종 예측을 수행한다. 


이미지 캡셔닝 (Image Captioning)

가장 대표적인 Multi-Modality로는 이미지 캡셔닝이 있다. 이미지 캡셔닝이란 이미지 데이터를 입력 받아 그에 대한 텍스트 설명 (caption)을 자동으로 생성하는 것이다. 이미지-텍스트 멀티 모달 구조이며 아이디어를 간단히 설명하면, 이미지를 잘 이해하는 CV 모델과 텍스트를 생성할 수 있는 NLP 모델을 결합하는 것이다. 

 

입력 모듈: CNN으로 이미지에서 특징 맵 (feature map)을 추출하고, 텍스트는 RNN 또는 BERT를 이용해 텍스트 임베딩을 추출한다.

결합 모듈: 이미지와 텍스트 임베딩을 어텐션 메커니즘으로 결합해 이미지에서 특정 부분이 텍스트와 어떻게 관련이 있는지 학습한다.

출력 모듈: 이미지와 텍스트를 결합한 정보를 이용해 이미지 캡션을 생성하거나 텍스트 분류 작업을 수행한다.


이미지 캡셔닝 모델 구조 파헤치기

Image Captioning Model - https://arxiv.org/abs/1411.4555

위 모델을 Encoder-Decoder 구조로 이미지를 입력받아 텍스트로 번역하는 과정으로 이해할 수 있다.

 

이때, 이미지의 경우 LSTM network의 hidden state로 넘겨지는 것이 아닌 input data로 취급한다.

 

Encoder(CNN)

CNN을 사용해 이미지에서 중요한 시각적 특징을 추출하는 부분이다. CNN이 추출한 특징 벡터는 이후에 LSTM 네트워크의 입력으로 사용된다.

ImageNet의 데이터로 학습된 pretrained model을 사용하되, Feature Extractor 부분은 변형하지 않고 그대로 사용하며 이후 학습 또한 진행하지 않는다.

이때, 출력층의 경우 기존의 classifier가 아니라 이후에 RNN의 첫번째 데이터에 입력되어야 하기 때문에 Embedding Layer와 같은 길이가 되어야 한다. 여기서 RNN은 LSTM Network

 

Decoder(RNN)

CNN으로부터 전달된 이미지 특징 벡터가 LSTM Network의 첫 번째 입력으로 들어가게 되고 이전에 추론된 단어를 입력 받아 다음 단어를 예측하게 된다.

S0​: 첫 번째 단어

S1​,S2​,...,SN−1: 이전에 예측된 단어

 

짚고 넘어가기 - LSTM Decoder의 첫 번째 단계에서의 명확하지 않은 추론 과정

Image Captioning Model 그림을 보면 LSTM에 첫번째 이미지 특징 벡터가 입력된 후 첫 출력이 존재하지 않는 것을 알 수 있다. 그러나 추론이 진행되어 hidden state와 cell state는 다음으로 전달된다. 이것을 직관적으로 이해하면, 이미지가 hidden state 형태로 디코더에 전달되고, 디코더의 두번째 input인 S0부터 입력받아 텍스트를 생성하는 것으로 이해할 수 있다.

 

그렇다면 첫 번째 단어를 예측할 때, 이미지와 첫 번째 입력(S0)은 무엇일까? 

 

두 가지 가정을 고려해볼 수 있다.

 

가정1. 실제로는 추론을 진행해 다음 input data로 사용되지만 이미지가 LSTM의 초기 hidden state를 생성하게 된다는 것을 강조하기 위해 그림에 표현하지 않았다

 

가정2. 이미지는 LSTM의 초기 hidden state를 생성할 뿐, 사실 첫 번째 입력은 [SOS] 토큰이다.

: 이 가정에서 이미지는 LSTM의 hidden state와 cell state를 초기화하는 데만 사용되며, 실제 첫 번째 입력은 [SOS], Start Of Sentence 즉, 문장의 시작을 나타내는 토큰이다. 즉, 이미지는 직접적으로 첫 번째 출력에 영향을 미치지 않는다는 것이다.

 

그렇다면 어떤 가정을 선택해야 할까?

 

이 모델의 핵심 컨셉은 이미지 자체가 첫 번째 hidden state를 생성하고, 이를 바탕으로 첫 번째 단어를 예측하는 것이다. 연구 목적이나 설계에 따라 어떤 가정을 선택해야하는 지는 달라질 수 있지만 현재는 가정1이 적합하다고 판단한다.

 


코드로 파헤치기 - Show and Tell 구현하기

EncoderCNN 클래스

class EncoderCNN(nn.Module):
    def __init__(self, embed_size):
        super().__init__()
        inception = models.inception_v3(pretrained=True)
        self.inception = inception
        # 최종 출력은 Embedding Vector의 길이와 동일해야 한다.
        self.inception.fc = nn.Linear(inception.fc.in_features, embed_size)
        self.relu = nn.ReLU()
        
    def forward(self, images):
        features = self.inception(images)
        features = self.relu(features)
        return features

- pretrained model인 Inception v3을 로드해 사용하며, ImageNet 데이터셋에서 학습된 가중치를 사용한다.

 

self.inception.fc = nn.Linear(inception.fc.in_features, embed_size)

Inception v3 모델의 마지막 fully connected later를 새로운 레이어로 교체해준다.

-> 원래 Inception v3의 최종 출력 레이어는 ImageNet의 분류에 맞게 구성되어 있는 상태이다. 우리는 이 사전 학습된 모델을 이미지 캡셔닝에 사용하고자 하기 때문에 최종 출력 크기를 우리가 원하는 embed_size로 맞춰주어야 한다.

 

DecoderRNN 클래스

class DecoderRNN(nn.Module):
    def __init__(self, embed_size, hidden_size, vocab_size, num_layers=1):
        super().__init__()
        self.embed = nn.Embedding(vocab_size, embed_size)
        self.lstm = nn.LSTM(embed_size, hidden_size, num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_size, vocab_size)

    def forward(self, features, captions=None, max_len=20):
        # 이미지의 feature를 첫 인풋으로 입력한다.
        inputs = features.unsqueeze(1)  # (batch_size, 1, embed_size)
        
	      outputs = []
        
        for t in range(max_len):
            hiddens, cells = self.lstm(inputs, (hiddens, cells))
            output = self.fc(hiddens.squeeze(1))  # (batch_size, vocab_size)
            predicted = output.argmax(1)
            outputs.append(predicted)

            if captions:
                # 실제 정답 단어 사용
                # 첫번째 인풋이 embedding layer를 통과하지 않기 때문에 구현 순서가 다르다.
                inputs = self.embed(captions[:, t]).unsqueeze(1)  # (batch_size, 1, embed_size)
            else:
                # 예측된 단어를 입력으로 사용
                inputs = self.embed(predicted).unsqueeze(1)  # (batch_size, 1, embed_size)
        
        return outputs

위 DecoderRNN 클래스는 이미지 특징 벡터를 입력으로 받아 LSTM을 통해 단어를 순차적으로 생성하여 캡션을 만든다.

 

학습할 때는 정답 단어인 captions가 입력으로 사용되고, 추론할 때는 이전에 예측한 단어를 다음 입력으로 사용해 문장을 완성한다.

LSTM이 매 시점마다 hidden state를 업데이트 해 다음 단어를 예측히고 최종적으로 캡션을 생성하게 된다.