머신러닝&딥러닝/자연어처리

Transformer(3) - Positional Encoding, Position-Wise Feedforward, Residual connection

Like_Me 2019. 12. 26. 22:32
반응형

Attention is all you need

  Transformer의 핵심인 Multi-Head Attention을 모두 알아보았고 남은 Feed forward, Residual Connection, Positional Encoding에 대해 알아볼 것이다.

 

Transformer는 입력값을 줄 때, RNN과 달리 입력을 순차적으로 주지 않는다. 따라서 시퀀스 정보를 넣어줘야 하는 문제가 생긴다. 이 문제를 해결한 것이 Positional Encoding이다. Positional Encoding의 기본적 메커니즘은 Embedding 된 input과 같은 크기의 벡터를 각각에 더해줌으로써 상대적인 위치정보에 대해서 알려주는 것이다.

이 포지셔널 인코딩은 보통 sin,cos을 이용하여 계산하는데 식은 다음과 같다.

 

pos는 전체 시퀀스에서 몇번째 단어(임베딩 벡터)인지를 의미하며, i는 임베딩 벡터 내에서의 인덱스를 의미한다. d_model은 임베딩 벡터의 차원수를 의미하는데 이는 모든 층의 출력 차원으로 사용된다. sin과 cos을 이용하면 모든 값이 -1~1로 통일되며, 각각의 값들이 모두 다르게 되는 구조를 만들기 편하다(sin, cos안의 pos값에 나누기 10000을 해준 이유가 그것이 아닐까). 또한, 임베딩 벡터에서 sin, cos을 번갈아가며(짝수 index = sin, 홀수 index = cos) 사용하므로 상대적 위치 정보를 전달하기도 좋을 것이다.

이를 코드로 구현하면 아래와 같다.

  def positional_encoding(model_dim, sentence_len):
  	encoded_vec = np.array([pos/np.power(10000, 2*i/model_dim)
                            for pos in range(sentence_len) for i in range(model_dim)])
    encoded_vec[::2] = np.sin(encoded_vec[::2])
    encoded_vec[1::2] = np.cos(encoded_vec[1::2])
    # 텐서플로 그래프에 올리기위해 constant로 만들며, [시퀀스 길이*피처크기]의 행렬을 생성한다.
    return tf.constant(encoded_vec.reshape([sentence_len, model_dim]), dtype=tf.float32)

 


포지션-와이즈 피드 포워드 네트워크는 셀프 어텐션 층을 거친 후 통과하는 네트워크다. 구조는 두 개의 선형층을 거치는 형태이다. 활성 함수를 첫 번째 선형층에서 Relu를 사용한다.

def feed_forward(inputs, num_units):
    model_dim = inputs.get_shape()[-1]
    layer = keras.layers.Dense(num_units, activation='relu')(inputs)
    output = keras.layers.Dense(model_dim)(layer)
    return output

 


마지막 Residual Connection은 Residual Network에서 나온 개념으로 identity를 더해주는 방법이다.

x -> F(x) -> H(x) = F(x) + x 가 되는 구조이다. 이런 구조가 왜 잘되는지 정확히 밝혀진 것은 없는 것으로 알지만 가장 흥미로웠던 설명은 일반적인 Ensemble과 같은 효과를 내어서 그렇다는 것이다. 이전의 출력을 다음 계층을 통과한 결과와 더해주면 데이터를 보는 하나의 시선을 추가해주는 효과가 있다는 것이다. (참고: https://github.com/sjchoi86/dl_tutorials_10weeks/blob/master/papers/Residual%20Networks%20are%20Exponential%20Ensembles%20of%20Relatively%20Shallow%20Networks.pdf

또한 여기서는 dropout과 layer-normalization을 이용하여 과적합을 방지해주었다.

 

def residual_connection(inputs, layer_outputs, dropout=0.3):
    # dropout과 정규화로 overfitting을 막아준다.
    residual_connection = inputs + keras.layers.Dropout(dropout)(layer_outputs)
    outputs = layer_normalization(residual_connection)
    return outputs

 

이전의 Multi-Head Attention이나 이번에 배운 모든 계층을 보면 결국, 출력이 [sequence_len, model_dim]이 된다는걸 알면 구현할 때 에러가 나지 않을 것이다.

반응형