![CNN-LSTM](https://all4nothing.github.io//assets/images/cnn-lstm.png)
CNN-LSTM
November 18, 2023CNN-LSTM
CNN과 LSTM으로 만든 Neural Network로 image caption 생성하기
다음 내용들을 포함하고 있다.
- Build Vocab
- Preprocessing Image data
- Define image captions data loader
- Define CNN-LSTM model
- Train the models
visit : https://github.com/All4Nothing/pytorch-DL-project/blob/main/Ch02_CNN_LSTM.ipynb
CNN-LSTM
CNN과 LSTM을 인코더-디코더 프레임워크로 연결해, 이미지나 동영상 데이터를 통해 텍스트를 추출하는 하이브리드 모델을 만들 수 있다. 이러한 모델은 image captioning에 사용할 수 있다.
CNN을 incoder로 LSTM을 decoder로 구성하여 다음과 같이 학습에 사용할 수 있다.
Model Define
class CNNModel(nn.Module):
def __init__(self, embedding_size):
""" Load the pretrained ResNet-152 and replace top fc layer. """
super(CNNModel, self).__init__()
resnet = models.resnet152(pretrained=True)
module_list = list(resnet.children())[:-1] # delete the last fc layer
self.resnet_module = nn.Sequential(*module_list)
self.linear_layer = nn.Linear(resnet.fc.in_features, embedding_size)
self.batch_norm = nn.BatchNorm1d(embedding_size, momentum=0.01)
def forward(self, input_images):
""" Extract feature vectors from input images. """
with torch.no_grad():
resnet_features = self.resnet_module(input_images)
resnet_features = resnet_features.reshape(resnet_features.size(0), -1)
final_features = self.batch_norm(self.linear_layer(resnet_features))
return final_features
class LSTMModel(nn.Module):
def __init__(self, embedding_size, hidden_layer_size, vocabulary_size, num_layers, max_seq_len=20):
""" Set the hyper-parameters and build the layers. """
super(LSTMModel, self).__init__()
self.embedding_layer = nn.Embedding(vocabulary_size, embedding_size)
self.lstm_layer = nn.LSTM(embedding_size, hidden_layer_size, num_layers, batch_first = True)
self.linear_layer = nn.Linear(hidden_layer_size, vocabulary_size)
self.max_seq_len = max_seq_len
def forward(self, input_features, capts, lens):
""" Decode image feature vectors and generates captions. """
embeddings = self.embedding_layer(capts)
embeddings = torch.cat((input_features.unsqueeze(1), embeddings), 1)
lstm_input = pack_padded_sequence(embeddings, lens, batch_first = True)
hidden_variables, _ = self.lstm_layer(lstm_input)
model_outputs = self.linear_layer(hidden_variables[0])
return model_outputs
def sample(self, input_features, lstm_states = None):
""" Generate captions for given image features using greedy search. """
""" 확률이 가장 높은 문장을 선택한다 """
sampled_indices = []
lstm_inputs = input_features.unsqueeze(1)
for i in range(self.max_seq_len):
hidden_variables, lstm_states = self.lstm_layer(lstm_inputs, lstm_states) # hiddens: (batch_size, 1, hidden_size)
model_outputs = self.linear_layer(hidden_variables.squeeze(1)) # outputs: (batch_size, vocab_size)
_, predicted_outputs = model_outputs.max(1) # predicted: (batch_size)
sampled_indices.append(predicted_outputs)
lstm_inputs = self.embedding_layer(predicted_outputs) # inputs: (batch_size, embed_size)
lstm_inputs = lstm_inputs.unsqueeze(1) # inputs: (batch_size, 1, embed_size)
sampled_indices = torch.stack(sampled_indices, 1) # sampled_ids: (batch_size, max_seq_length)
return sampled_indices
LSTM layer는 순환 계층으로 LSTM 셀이 시간 차원을 따라 unfold 되어 LSTM 셀의 시간 배열을 구성한다. 여기서 이 셀은 각 시간 단계마다 단어의 예측 확률을 출력하고 가장 확률이 높은 단어가 출력 문장 뒤에 추가된다.
각 시간 단계에서 LSTM 셀은 내부 셀 상태를 생성하고 이 상태는 다음 시간 단계의 LSTM 셀의 입력으로 전달된다. LSTM 셀이 토큰/단어를 출력할 때 까지 이 과정을 반복한다.
nn.Embedding()
PyTorch에서 임베딩 층(embedding layer)을 만들어 훈련 데이터로부터 처음부터 임베딩 벡터를 학습한다.
nn.Embedding()을 사용하여 학습 가능한 임베딩 테이블(룩업 테이블)을 만든다.
nn.Embedding은 크게 두 가지 인자를 받는다.
- num_embeddings : 임베딩을 할 단어들의 개수. 다시 말해 단어 집합의 크기이다.
- embedding_dim : 임베딩 할 벡터의 차원이다. 사용자가 정해주는 hyperparameter이다.
- padding_idx : 선택적으로 사용하는 para,eter이다. 패딩을 위한 토큰의 인덱스를 알려준다.
embedding_layer **=** nn**.**Embedding(num_embeddings**=**len(vocab), embedding_dim**=**3, padding_idx**=**1)
print(embedding_layer**.**weight)
Parameter containing:
tensor([[-0.1778, -1.9974, -1.2478],
[ 0.0000, 0.0000, 0.0000],
[ 1.0921, 0.0416, -0.7896],
[ 0.0960, -0.6029, 0.3721],
[ 0.2780, -0.4300, -1.9770],
[ 0.0727, 0.5782, -3.2617],
[-0.0173, -0.7092, 0.9121],
[-0.4817, -1.1222, 2.2774]], requires_grad=True)
Train loop
# Build the models
encoder_model = CNNModel(256).to(device)
decoder_model = LSTMModel(256, 512, len(vocabulary), 1).to(device)
# Loss and optimizer
loss_criterion = nn.CrossEntropyLoss()
parameters = list(decoder_model.parameters()) + list(encoder_model.linear_layer.parameters()) + list(encoder_model.batch_norm.parameters())
optimizer = torch.optim.Adam(parameters, lr=0.001)
# Train the models
total_num_steps = len(custom_data_loader)
for epoch in range(5):
for i, (imgs, caps, lens) in enumerate(custom_data_loader):
# Set mini-batch dataset
imgs = imgs.to(device)
caps = caps.to(device)
tgts = pack_padded_sequence(caps, lens, batch_first=True)[0]
# Forward, backward and optimize
feats = encoder_model(imgs)
outputs = decoder_model(feats, caps, lens)
loss = loss_criterion(outputs, tgts)
decoder_model.zero_grad()
encoder_model.zero_grad()
loss.backward()
optimizer.step()
# Print log info
if i % 10 == 0:
print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}, Perplexity: {:5.4f}'
.format(epoch, 5, i, total_num_steps, loss.item(),
np.exp(loss.item())))
# Save the model checkpoints
if (i+1) % 1000 == 0:
torch.save(decoder_model.state_dict(), os.path.join(
'models_dir/', 'decoder-{}-{}.ckpt'.format(epoch+1, i+1)))
torch.save(encoder_model.state_dict(), os.path.join(
'models_dir/', 'encoder-{}-{}.ckpt'.format(epoch+1, i+1)))
# concepts해당 포스팅은 ‘실전! 파이토치 딥러닝 프로젝트’의 Ch02을 참고하여 작성되었습니다.