현재 pytorch를 활용해서 Attention이 적용된 Sequence to Sequence 모델을 만들었습니다. 예시 코드는 아래와 같습니다.
import torch
import torch.nn as nn
def verbose_shape(*tensors):
for tensor in tensors:
print(tensor.shape, end=' ')
class Encoder(nn.Module):
def __init__(self,
notes_vocab_size,
durations_vocab_size,
embedding_size,
hidden_size):
super(Encoder, self).__init__()
self.notes_vocab_size = notes_vocab_size
self.durations_vocab_size = durations_vocab_size
self.embedding_size = embedding_size
self.hidden_size = hidden_size
self.notes_embed = nn.Embedding(num_embeddings=self.notes_vocab_size, embedding_dim=embedding_size)
self.durations_embed = nn.Embedding(num_embeddings=self.durations_vocab_size, embedding_dim=embedding_size)
self.lstm = nn.LSTM(input_size=self.embedding_size*2, hidden_size=self.hidden_size, num_layers=1, batch_first=True)
self.notes_fc = nn.Linear(in_features=self.hidden_size, out_features=self.notes_vocab_size)
self.durations_fc = nn.Linear(in_features=self.hidden_size, out_features=self.durations_vocab_size)
def forward(self, x_notes, x_durations):
x_notes_embed = self.notes_embed(x_notes)
x_durations_embed = self.durations_embed(x_durations)
x = torch.cat((x_notes_embed, x_durations_embed), dim=-1)
hs, (h, c) = self.lstm(x)
a_notes = F.softmax(self.notes_fc(hs), dim=-1) # (batch-size, sequence_length, vocab_size)
a_durations = F.softmax(self.durations_fc(hs), dim=-1) # one-hot encoding
notes_pred = torch.argmax(a_notes, dim=-1)
durations_pred = torch.argmax(a_durations, dim=-1)
return hs, h, c, notes_pred, durations_pred
class Decoder(nn.Module):
def __init__(self,
notes_vocab_size,
durations_vocab_size,
embedding_size,
hidden_size):
super(Decoder, self).__init__()
self.notes_vocab_size = notes_vocab_size
self.durations_vocab_size = durations_vocab_size
self.embedding_size = embedding_size
self.hidden_size = hidden_size
self.notes_embed = nn.Embedding(num_embeddings=self.notes_vocab_size, embedding_dim=self.embedding_size)
self.durations_embed = nn.Embedding(num_embeddings=self.durations_vocab_size, embedding_dim=self.embedding_size)
self.lstm = nn.LSTM(input_size=self.embedding_size*2, hidden_size=self.hidden_size, num_layers=1, batch_first=True)
self.notes_affine = nn.Linear(in_features=self.hidden_size, out_features=self.notes_vocab_size)
self.durations_affine = nn.Linear(in_features=self.hidden_size, out_features=self.durations_vocab_size)
def forward(self, enc_hs, enc_h, enc_c, notes_pred, durations_pred):
x_notes_embed = self.notes_embed(notes_pred)
x_durations_embed = self.durations_embed(durations_pred)
x = torch.cat((x_notes_embed, x_durations_embed), dim=-1)
dec_hs, _ = self.lstm(x, (enc_h, enc_c))
# decoder의 hidden_state 벡터를 하나씩 loop 수행
cs = []
for t in range(dec_hs.size()[1]):
dec_h = dec_hs[:,t,:]
s = torch.bmm(enc_hs, dec_h.unsqueeze(dim=-1))
a = F.softmax(s, dim=1)
ar = a.expand(a.size()[0], a.size()[1], self.hidden_size)
t = enc_hs * ar
c = t.sum(dim=1).unsqueeze(dim=1)
cs.append(c)
cs = torch.cat(cs, dim=1)
y_notes = F.softmax(self.notes_affine(cs), dim=-1)
y_durations = F.softmax(self.durations_affine(cs), dim=-1)
return y_notes, y_durations
# params
notes_vocab_size = 32
durations_vocab_size = 5
embedding_size = 128
hidden_size = 256
# dataset
x_notes = torch.rand(28 ,32).to(torch.int32)
x_durations = torch.rand(28, 32).to(torch.int32)
# model
encoder = Encoder(notes_vocab_size, durations_vocab_size, embedding_size, hidden_size)
decoder = Decoder(notes_vocab_size, durations_vocab_size, embedding_size, hidden_size)
# forward
hs, h, c, notes_pred, durations_pred = encoder(x_notes, x_durations)
y_notes, y_durations = decoder(hs, h, c, notes_pred, durations_pred)
그런데 위 코드에 대해서 아래와 같은 2가지 사항을 검증받고 싶은데요! 검증받고 싶은 사항은 다음과 같습니다.
- 위 코드가 Attention이 적용된 Sequence to Sequence 모델 구현에 적합한 코드인지, 또 이론적으로 맞지 않는 부분은 없는지 확인하고 싶습니다.
- 코드 중 주석으로 "decoder의 hidden_state 벡터를 하나씩 loop 수행" 한 부분을 보면 native한 for ~ loop 구문으로 구현을 했는데요. 이 부분을 벡터화 연산 즉, pytorch에서 지원하는 브로드캐스팅 및 벡터화 연산을 적용해서 효율적으로 변경하고 싶습니다. 어떻게 개선해야 할지 알려주시면 감사하겠습니다.
답변 주시면 감사하겠습니다!