input : batch_features(512,49,1280)
1.인코더에 batch_features를 집어넣는다.
features = encoder(batch_features) : (512, 49, 512)
class CNN_Encoder(tf.keras.Model):
def __init__(self, embedding_dim):
super(CNN_Encoder, self).__init__()
self.dropout = tf.keras.layers.Dropout(0.25)
self.fc = tf.keras.layers.Dense(embedding_dim)
def call(self, x):
x = self.dropout(x)
x = self.fc(x)
x = tf.nn.relu(x)
return x
인코더의 구조는 위와 같다
Dense층을 하나 통과하니 1280이 512로 바뀌었다. 왜 49는 그대로 있는지 잘 모르겠다.
2. 예측해야할 시점은 총 72개이다.(smiles의 최대 길이가 < , > 합해서 72이기 때문
predictions, hidden, _ = decoder(dec_input, features, hidden)
dec_input(512,1) : 시작 토큰(Qurey) --> 시작토큰인 '<'를 input으로 주기 위함.
features(512,49,512) : 임베딩(Keys), --> 모든 시점의 인코더 셀의 은닉상태? 즉 인코더에서나온 피쳐들.( features = encdoer(batch_features) )
hidden(512,1024) : 이건 무슨 역할을 하는 것인지 모르겠다. reset_state함수를 호출해서 (batch_size,1024)만큼 만든것 1024는 units이다. --> 디코더의 은닉 상태
디코더의 구조를 보면
class RNN_Decoder(tf.keras.Model):
def __init__(self, embedding_dim, units, vocab_size):
super(RNN_Decoder, self).__init__()
self.units = units
self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
self.gru = tf.keras.layers.GRU(self.units,
return_sequences=True,
return_state=True,
recurrent_initializer='glorot_uniform')
self.dropout =tf.keras.layers.Dropout(0.25)
self.fc1 = tf.keras.layers.Dense(self.units)
self.fc2 = tf.keras.layers.Dense(vocab_size)
self.attention = BahdanauAttention(self.units)
def call(self, x, features, hidden):
context_vector, attention_weights = self.attention(features, hidden)
x = self.embedding(x)
x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1)
output, state = self.gru(x)
x = self.fc1(output)
x = self.dropout(x)
x = tf.reshape(x, (-1, x.shape[2]))
x = self.fc2(x)
return x, state, attention_weights
def reset_state(self, batch_size):
return tf.zeros((batch_size, self.units))
context_vextor와 x를 concat해서 입력으로 넣어준다.
어텐션 모델에 features와 hidden을 매개변수로 해서 넘겨주는데. 다음과 같은 구조로 넘어간다.
class BahdanauAttention(tf.keras.Model):
def __init__(self, units):
super(BahdanauAttention, self).__init__()
self.W1 = tf.keras.layers.Dense(units)
self.W2 = tf.keras.layers.Dense(units)
self.V = tf.keras.layers.Dense(1)
def call(self, features, hidden): #(512, 49, 512), (512, 1024)
hidden_with_time_axis = tf.expand_dims(hidden, 1) # (512, 1, 1024)
score = tf.nn.tanh(self.W1(features) + self.W2(hidden_with_time_axis))
print("score는 : ",score ) #(512, 49, 1024)
attention_weights = tf.nn.softmax(self.V(score), axis=1)
print("attention_weight는 : ", attention_weights) #(512, 49, 1)
context_vector = attention_weights * features # (512, 49, 1)*(512, 49, 512)
context_vector = tf.reduce_sum(context_vector, axis=1)
print("context_vector는 : ", context_vector) #(512, 512)
return context_vector, attention_weights
score(어텐션 스코어)를 어떻게 구했는가?
encoder를 통해 얻은 features와 hidden을 더해서 얻어내었다.
softmax를 통해 attention wieght을 구하고
contect_vector를 구했다.
한줄정리.
더 나은 context vector를 만들기 위해서
어텐션 메커니즘을 적용을 하는데
인코더의 피쳐(은닉상태가 될수있다 여기서는 cnn이므로 그냥 피쳐라고 해도무방)와
디코더의 t-1시점의 은닉상태(hidden)을 이용해서 어텐션 스코어를 구한후 어텐션 가중치를 구한후
context_vector를 만든다. 그리고 context_vector와 x(decoder의 시점 t에서의 입력(단어))
그니까 context_vector를 만들기 위해서 attention을 하는 기준은 디코더의 은닉상태이다.
기존 인코더 투 디코더 모델은 context_vector고정되어있기 때문에 어텐션이 더 낫다? 라고 할수 있겠다.
--> 지금 체크해서 다시 명확하게 정리를 해 보자.
기존의 인코더 투 디코더 모델의 문맥 벡터는 인코더 RNN셀의 마지막 시점의 은닉 상태이다.