Big Ben
Big Ben's Log
Big Ben
전체 방문자
오늘
어제
  • 전체 글 (80)
    • 파이썬 (23)
      • 파이썬 기초 (5)
      • 클래스 (6)
      • 자료구조 (4)
      • Tensorflow (3)
      • PyTorch (2)
      • konlpy (1)
      • anaconda (1)
    • 머신러닝 (3)
      • 선형회귀 (1)
      • Tree 기반 (1)
    • 딥러닝 (6)
      • NLP (2)
      • VISION (2)
      • TABULAR (0)
      • 딥러닝 서버 구축 (2)
    • 그래프 이론 (1)
      • 그래프마이닝 (1)
      • GNN (0)
    • 강화학습 (3)
      • 강화학습 기본 (3)
    • 인공지능 (5)
    • 추천시스템 (2)
      • 추천시스템 기초 (2)
    • Competitions (1)
    • 빅데이터 (8)
      • 하둡 (3)
      • 스파크 (4)
      • 클라우드 (1)
    • SQL (7)
      • MariaDB (2)
    • 논문 리뷰 (2)
    • 대학원 (0)
      • 데이터 사이언스 (0)
      • 경제학 (0)
    • 선형대수학 (7)
      • 선형대수 ICE BREAKING (1)
      • 벡터 (5)
      • 고윳값 (1)
    • 개인프로젝트 (0)
      • 포트폴리오 대시보드 + AI기반 주식 자동매매 (0)
    • 재테크 (1)
    • 자동차 (0)
    • 알고리즘 (11)

블로그 메뉴

  • 홈
  • 태그
  • 미디어로그
  • 위치로그
  • 방명록

공지사항

인기 글

태그

  • 파이썬기초
  • 알고리즘
  • MariaDB
  • 데이터
  • 백준
  • 프로그래머스
  • 데이터베이스
  • TensorFlow
  • sql
  • class
  • 딥러닝
  • 파이썬
  • 선형대수
  • mysql
  • 인공지능
  • Baekjoon
  • 프로그래밍
  • pytorch
  • 코테
  • 선형대수학
  • 코딩테스트
  • 객체지향
  • 객체
  • 하둡
  • 자료구조
  • 데이터사이언스
  • 빅데이터
  • AI
  • PYTHON
  • 머신러닝

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Big Ben

Big Ben's Log

트랜스포머(Transformer) - 1 (포지셔널 인코딩)
딥러닝/NLP

트랜스포머(Transformer) - 1 (포지셔널 인코딩)

2022. 12. 22. 10:39
반응형

트랜스포머는 Attention 메커니즘을 이해한 뒤 읽는 것을 추천한다. 

 

트랜스포머는 NLP에서의 예제가 풍부하며, 그외 다른 State-of-the-Art (SOTA) 기술의 기반이 되는 알고리즘이다.

 

특히 기계 번역 분야에서 이 모델은 seq2seq의 구조인 인코더-디코더를 따르면서도, RNN을 사용하지 않고도 RNN보다 우수한 성능을 보여주었다.

 

필자는 아직도 트랜스포머에 대해 공부하고 있으며, 트랜스포머 자체가 방대하고 복잡하게 설계된 알고리즘이기에, 여러 차시로 나눠서 트랜스포머에 대해 설명하고자 한다.

 

요약본의 상당 부분은 유원준 & 안상준 저의 <딥러닝을 이용한 자연어처리 입문> 책을 참고하였음을 사전에 밝힌다.

 

1. 기존 seq2seq 모델의 한계와 Transformer의 동작원리

seq2seq 모델의 기본 구조는 인코더-디코더 구조이다. 

이때 인코더는 입력 시퀀스를 차례대로 받아서 모든 정보를 하나의 벡터 표현 압축, 

은닉 상태의 값 (컨텍스트 벡터)를 디코더로 넘기며 벡터표현을 통해 출력 시퀀스를 만들어낸다.

 

이 과정에서 입력 시퀀스가 압축되기에 일부 정보 손실을 피할 수 없고, 이것에 대한 보완책으로 attention 메커니즘이 사용되었다.

 

Transformer는 "RNN의 보정용으로 attention을 활용하지 말고, 아예 attention만으로 인코더와 디코더를 만들어보자"라는 아이디어에서 착안되었다.

 

seq2seq의 인코더-디코더 구조와 동일한 것을 알 수 있다.

위 그림처럼 Transformer는 인코더와 디코더 구조로 이루어져있다. 

하지만 RNN이 t개의 시점을 갖는 구조였다면, Transformer에선 인코더와 디코더라는 단위가 N개로 구성되는 구조이다.

 

예를 들어 인코더와 디코더 각각 6개씩 갖는 모델을 보자면, 아래와 같은 그림으로 표현할 수 있다.

여러 개의 인코더와 디코더를 하나의 그림으로 표현한다면 아래처럼 생각할 수 있을 것이다.

여기서 seq2seq과 다른 점은 Encoder의 정보를 압축한 (seq2seq관점에서) context vector라고 불리는 중간층이 없다는 것이다.

 

하지만 [ I, am, a, student ] 라는 단어의 순차성을 고려할 수 있었던 RNN의 특성을 Transformer에선 어떻게 구현할 수 있을까?

 

바로 단어의 위치 정보를 알려주는 포지셔널 인코딩(Positional Encoding)을 활용하는 것입니다.

 

더 딥하게 공부하기 전에 트랜스포머의 주요 하이퍼파라미터를 알고 넘어가보자.

2. Transformer의 주요 하이퍼파라미터 

  • \(d_{model} = 512 \) : 임베딩 차원 (=인코더와 디코더에 정해진 입/출력 크기, 논문에선 512를 사용 - 언제든 변경 가능)
  • \( \text{num_layers}=6 \): 인코더 & 디코더 층 개수 (논문에선 6개의 층을 사용 - 언제든 변경 가능)
  • \( \text{num_heads}=8 \): 병렬로 어텐션을 사용하는데, 병렬의 개수 (논문에선 8개를 사용 - 언제든 변경 가능)
  • \( d_{ff}=2048 \): 트랜스포머 내부 FFNN 은닉층의 크기 (언제든 변경 가능)

3. 포지셔널 인코딩 (Positional Encoding)

앞서 트랜스포머는 RNN처럼 단어 입력을 순차적으로 받지 않는다고 했다. 따라서 우리는 단어의 위치 정보(position information)를 어떤식으로든 인코더-디코더에 알려줄 필요가 있다. 이때 positional encoding을 활용하게 되는데, 각 단어의 임베딩 벡터에 위치정보를 단순하게 더한 것을 모델의 입력으로 사용하게 된다.

포지셔널 인코딩을 사용한다면 순서 정보가 보존되어 입력 시퀀스를 자연스럽게 받아줄 수 있다.

또한, 같은 단어라고 할지라도, 문장 내 위치에 따라서 transformer의 입력으로 들어가는 임베딩 벡터의 값이 달라져서 조금 더 세련되게 단어 위치 정보를 습득할 수 있는 것이 특징이다.

이때 포지셔널 인코딩 값을 만들기 위해서 sine과 cosine함수를 활용하게 되는데, 이 두 삼각함수의 값을 임베딩 벡터에 더해주면서 단어의 순서 정보를 반영할 수 있다. 

$$ \text{PE}_{(pos,2i)}=sin(\frac{pos}{10000^{\frac{2i}{d_{model}}}}) $$

$$ \text{PE}_{(pos,2i+1)}=cos(\frac{pos}{10000^{\frac{2i}{d_{model}}}}) $$

위 함수를 이해하기 위해서는 위에서 본 임베딩 벡터와 포지셔널 인코딩의 덧셈은 사실 임베딩 벡터가 모여 만들어진 문장 행렬과 포지셔널 인코딩 행렬의 덧셈연산을 통해 이루어진다는 점을 이해해야한다.

\( pos\)는 입력 문장에서의 임베딩 벡터의 위치를 나타내며, \( i \)는 임베딩 벡터 내의 차원의 인덱스를 의미한다. 

따라서 위 수식에 의거하면, 임베딩 벡터 내의 각 차원의 인덱스가 짝수인 경우에는 사인함수의 값을 사용하고, 홀수인 경우에는 코사인 함수의 값을 활용한다.

 

이젠 아래의 코드를 통해 positional encoding을 구현해보자

import numpy as np
import tensorflow as tf

class PositionalEncoding(tf.keras.layers.Layer):
    def __init__(self, position, d_model):
        super(PositionalEncoding, self).__init__()
        self.pos_encoding = self.positional_encoding(position, d_model)

    def get_angles(self, position, i, d_model):
        angles = 1 / tf.pow(10000, (2 * (i // 2)) / tf.cast(d_model, tf.float32))
        return position * angles

    def positional_encoding(self, position, d_model):
        angle_rads = self.get_angles(
            position=tf.range(position, dtype=tf.float32)[:, tf.newaxis], # tf.newaxis를 통해 차원을 하나 추가
            i=tf.range(d_model, dtype=tf.float32)[tf.newaxis, :], # tf.newaxis를 통해 차원을 하나 추가
            d_model=d_model
        )

        # 배열의 짝수 인덱스(2i)에는 사인 함수 적용
        sines = tf.math.sin(angle_rads[:, 0::2])
        # 배열의 홀수 인덱스(2i+1)에는 코사인 함수 적용
        cosines = tf.math.cos(angle_rads[:, 1::2])

        angle_rads = np.zeros(angle_rads.shape)
        angle_rads[:, 0::2] = sines
        angle_rads[:, 1::2] = cosines
        pos_encoding = tf.constant(angle_rads)
        pos_encoding = pos_encoding[tf.newaxis, ...] # ...은 :와 비슷한 용도로 사용 가

        print(pos_encoding.shape)

        return tf.cast(pos_encoding, tf.float32)

    def call(self, inputs):
        return inputs + self.pos_encoding[:, :tf.shape(inputs)[1], :]

 

그리고 50x128의 크기를 갖는 포지셔널 인코딩 행렬을 시각화하여 어떤 형태를 갖는지도 확인해보자

(입력 문장의 단어 개수: 50개 / 단어 임베딩 벡터의 차원: 128)

import matplotlib.pyplot as plt

# 문장의 길이 50, 임베딩 벡터 차원 128

sample_pos_encoding = PositionalEncoding(50, 128)  # 포지션은 총 50개. why? 단어가 50개임

plt.figure(figsize=(10, 10))
plt.pcolormesh(sample_pos_encoding.pos_encoding.numpy()[0], cmap='RdBu')
plt.xlabel('Depth')
plt.xlim((0, 128))
plt.ylabel('Position')
plt.colorbar()
plt.show()

 

여기까지 포지셔널 인코딩에 대해 알아보았다.

다음 포스팅은 Transformer가 활용하는 Attention에 대해 알아보도록 하자.

반응형
저작자표시 (새창열림)

'딥러닝 > NLP' 카테고리의 다른 글

[Attention Mechansim] 바다나우 어텐션  (0) 2022.12.19
    '딥러닝/NLP' 카테고리의 다른 글
    • [Attention Mechansim] 바다나우 어텐션
    Big Ben
    Big Ben
    Data Scientist

    티스토리툴바