본문 바로가기

카테고리 없음

[I2S] encoder(CNN) to decoder(RNN)어텐션 메커니즘 분석

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셀의 마지막 시점의 은닉 상태이다.