이번 포스팅은 Transformer에 대해 알아보겠습니다.

 

seq2seq 모델은 인코더-디코더 구조로 구성되어져 있었습니다.

 

여기서 인코더는 입력 시퀀스를 하나의 벡터 표현으로 압축하고, 디코더는 이 벡터 표현을 통해서 출력 시퀀스를 만들어냈습니다.

 

하지만 이러한 구조는 인코더가 입력 시퀀스를 하나의 벡터로 압축하는 과정에서 입력 시퀀스의 정보가 일부 손실된다는 단점이 있었고, 이를 보정하기 위해 어텐션이 사용되었습니다.

 

그런데 어텐션을 RNN의 보정을 위한 용도로서 사용하는 것이 아니라 어텐션만으로 인코더와 디코더를 만든게 Transformer 입니다.

 

 

Transformer 하이퍼 파라미터

 

  • \(d_{model}\) = 512 : 
    • 인코더와 디코더에서의 정해진 입력과 출력의 크기
    • 각 인코더와 디코더가 다음 층의 인코더와 디코더로 값을 보낼 때에도 이 차원을 유지
  • num_layers = 6 : 
    • 인코더와 디코더가 총 몇 층으로 구성되었는지를 의미
  • num_haeds = 8 : 
    • 어텐션을 사용할 때, 한 번 하는 것 보다 여러 개로 분할해서 병렬로 어텐션을 수행하고 결과값을 다시 하나로 합치는 방식을 택했습니다. 이때 이 병렬의 개수
  • \(d_{ff}\) = 2048 : 
    • 피드 포워드 신경망이 존재하며 해당 신경망의 은닉층의 크기를 의미

 

 

Transformer

 

이전 seq2seq 구조에서는 인코더와 디코더에서 각각 하나의 RNN이 t개의 시점(time step)을 가지는 구조였다면

 

이번에는 인코더와 디코더라는 단위가 N개로 구성되는 구조입니다.

Transformer

 


 

 

Positional Encoding

 

RNN이 자연어 처리에서 유용했던 이유는 단어의 위치에 따라 단어를 순차적으로 입력받아서 처리하는

 

RNN의 특성으로 인해 각 단어의 위치 정보(position information)를 가질 수 있다는 점에 있었습니다.

 

하지만 트랜스포머는 단어 입력을 순차적으로 받는 방식이 아니므로 단어의 위치 정보를 다른 방식으로 알려줄 필요가 있습니다.

 

트랜스포머는 단어의 위치 정보를 얻기 위해서 각 단어의 임베딩 벡터에 위치 정보들을 더하여 모델의 입력으로 사용하는데,

 

이를 포지셔널 인코딩(positional encoding)이라고 합니다.

 

 

임베딩 벡터가 인코더의 입력으로 사용되기 전 포지셔널 인코딩값이 더해지는 과정입니다.

 

Positional Encoding가 구해지는 식을 보겠습니다.

 

Positional Encoding

 

  • \(pos\) : 입력 문장에서의 임베딩 벡터의 위치
    • \(i\) : 임베딩 벡터 내의 차원의 인덱스를 의미
      • \(2i + 1\) (홀수) 일때는 \(cos\) 함수 사용
      • \(2i\) (짝수) 일때는 \(sin\) 함수 사용
  • \(d_{model}\) : 트랜스포머의 모든 층의 출력 차원을 의미, 위에서 512라고 설정해놓은 하이퍼파라미터 값.

 

위와 같은 포지셔널 인코딩 방법을 사용하면 순서 정보가 보존되는데,

 

예를 들어 각 임베딩 벡터에 포지셔널 인코딩의 값을 더하면 같은 단어라고 하더라도 문장 내의 위치에 따라서 트랜스포머의 입력으로 들어가는 임베딩 벡터의 값이 달라집니다.

 


 

Attention

 

Transformer에서 사용하는 Attention의 종류는 3가지입니다.

 

Encoder와 Decoder의 구성을 보면

 

  1. Encoder
    • Encoder Self-Attention : Multi-head Self-Attention은 Self-Attention을 병렬적으로 사용하였다는 의미
    • Position-wise FFNN
  2. Decoder
    • Masked Decoder Self-Attention
    • Encoder-Decoder Attention

 

그러면 먼저 Encoder Self-Attention 부터 알아보겠습니다.

 

 

Encoder Self-Attention

 

어텐션을 자기 자신에게 수행한다는 의미입니다.

 

Seq2Seq

앞서 배운 seq2seq에서 어텐션을 사용할 경우의 Q, K, V의 정의를 다시 생각해보겠습니다.

 

사실 t 시점이라는 것은 계속 변화하면서 반복적으로 쿼리를 수행하므로 결국 전체 시점에 대해서 일반화를 할 수도 있습니다.

그런데 셀프 어텐션에서는 Q, K, V가 전부 동일합니다. 트랜스포머의 셀프 어텐션에서의 Q, K, V는 아래와 같습니다.

  • Q : 입력 문장의 모든 단어 벡터들
  • K : 입력 문장의 모든 단어 벡터들
  • V : 입력 문장의 모든 단어 벡터들

 

Self-Attention의 장점

사람은 그것(it)이 피곤한 주체가 동물이라는 것을 아주 쉽게 알 수 있지만 기계는 그렇지 않습니다.

 

하지만 셀프 어텐션은 입력 문장 내의 단어들끼리 유사도를 구하므로서 그것(it)이 동물(animal)과 연관되었을 확률이 높다는 것을 찾아냅니다.

 

 

Self-Attention의 Q, K, V를 구하는 방법

위 사진은 Self-Attention의 Q, K, V를 구하는 방법입니다.

 

기존의 벡터로부터 더 작은 벡터는 가중치 행렬을 곱하므로서 완성됩니다.

 

각 가중치 행렬은 \(dmodel\times(dmodel/num_heads)\)의 크기를 가집니다.

 

가중치 행렬은 훈련 과정에서 학습됩니다.

 

즉, 논문과 같이 dmodel=512이고 num_heads=8 이라면,

 

가중치 행렬은 각각 512 x (512 / 8) = 512 x 64 크기를 가지게 됩니다.

 

그리고 각 벡터에 3개의 서로 다른 가중치 행렬을 곱하고 64의 크기를 가지는 Q, K, V 벡터를 얻어냅니다.

 

모든 단어 벡터에 위와 같은 과정을 거치면 I, am, a, student는 각각의 Q, K, V 벡터를 얻습니다.

 

이후, Attention에서 해주었던과 같이 계산해줍니다.

 

여기서 \(d_{k} = d_{model}/\)num_heads 입니다.

 

 

행렬 연산으로 일괄 처리하기

 

 

 

위와 같이 행렬을 이용하여 한번에 처리할 수 있습니다.

 

 


 

Encoder - 멀티 헤드 어텐션 (Multi-head Attention)

 

병렬 어텐션으로 얻을 수 있는 효과는 다른 시각으로 정보들을 수집하겠다는 겁니다.

 

앞서 사용한 예문 '그 동물은 길을 건너지 않았다. 왜냐하면 그것은 너무 피곤하였기 때문이다.'를 상기해봅시다.

 

단어 그것(it)이 쿼리였다고 해봅시다.

 

즉, it에 대한 Q벡터로부터 다른 단어와의 연관도를 구하였을 때 첫번째 어텐션 헤드는 '그것(it)'과 '동물(animal)'의 연관도를 높게 본다면, 두번째 어텐션 헤드는 '그것(it)'과 '피곤하였기 때문이다(tired)'의 연관도를 높게 볼 수 있습니다.

 

각 어텐션 헤드는 전부 다른 시각에서 보고있기 때문입니다.

 

Multi-head Attention

 

위와 같은 방식으로 Multi-head Attention은 진행해 줍니다.

 

이후,

\(a_{0}, a_{1}, a_{2}, ... \) 를 concatenate 해줍니다.

 

 

이렇게 나온 결과 행렬이 멀티-헤드 어텐션의 최종 결과물입니다.

 

위의 그림은 어텐션 헤드를 모두 연결한 행렬이 가중치 행렬 Wo과 곱해지는 과정을 보여줍니다.

 

이때 결과물인 멀티-헤드 어텐션 행렬은 인코더의 입력이었던 문장 행렬의 (seq_len, dmodel) 크기와 동일합니다.

 

 


 

Encoder - 포지션-와이즈 피드 포워드 신경망 (Position-wise FFNN)

 

Position-wise FFNN은 인코더와 디코더에서 공통적으로 가지고 있는 서브층입니다.

 

쉽게 말하면 완전 연결 FFNN(Fully-connected FFNN)이라고 해석할 수 있습니다.

 

앞서 인공 신경망은 결국 벡터와 행렬 연산으로 표현될 수 있음을 배웠습니다.

 

아래는 포지션 와이즈 FFNN의 수식을 보여줍니다.

 

  • \(d_{ff}\)=2048
  • \(x\)= (seq_len, dmodel) 의 크기
  • \(W_{1}\)= (dmodel, dff) 의 크기
  • \(W_{2}\)= (dff, dmodel) 의 크기

W1, b1, W2, b2는 하나의 인코더 층 내에서는 다른 문장, 다른 단어들마다 정확하게 동일하게 사용됩니다.

 

하지만 인코더 층마다는 다른 값을 가집니다.

 

 


 

Encoder - 잔차 연결(Residual connection)과 층 정규화(Layer Normalization)

 

FFNN을 통과한 후 잔차 연결과 정규화를 거치게 됩니다.

 

잔차 연결에 대한 설명은 생략하도록 하고 정규화에 대해 알아보겠습니다.

 

위 사진은 정규화가 되는 과정을 표현한 겁니다.

  • \(\gamma\) 의 초기값은 1
  • \(\beta\) 의 초기값은 0

 

 


 

Decoder - Masked Multi-head Self-Attention

 

디코더는 번역할 문장에 해당되는 문장 행렬을 한 번에 입력받습니다.

 

seq2seq의 디코더에 사용되는 RNN 계열의 신경망은 입력 단어를 매 시점마다 순차적으로 입력받으므로

 

다음 단어 예측에 현재 시점을 포함한 이전 시점에 입력된 단어들만 참고할 수 있습니다.

 

반면, Transformer는 문장 행렬로 입력을 한 번에 받으므로 현재 시점의 단어를 예측하고자 할 때,

 

입력 문장 행렬로부터 미래 시점의 단어까지도 참고할 수 있는 현상이 발생합니다.

 

가령, suis를 예측해야 하는 시점이라고 해봅시다. Input은 <sos> je suis étudiant 입니다.

 

RNN 계열의 seq2seq의 디코더라면 현재까지 디코더에 입력된 단어는 <sos>와 je뿐일 것입니다.

 

반면, Transformer는 이미 문장 행렬로 <sos> je suis étudiant를 입력받았습니다.

 

이를 위해 Transformer의 디코더에서는 현재 시점의 예측에서 현재 시점보다 미래에 있는 단어들을 참고하지 못하도록 룩-어헤드 마스크(look-ahead mask)를 도입습니다.

 

룩-어헤드 마스크(look-ahead mask)는 디코더의 첫번째 서브층인 Masked Multi-head Self-Attention에서 동작됩니다.

 

디코더의 첫번째 서브층인 멀티 헤드 셀프 어텐션 층은 인코더의 첫번째 서브층인 멀티 헤드 셀프 어텐션 층과 동일한 연산을 수행합니다.

 

오직 다른 점은 어텐션 스코어 행렬에서 마스킹을 적용한다는 점만 다릅니다.

 

우선 다음과 같이 셀프 어텐션을 통해 어텐션 스코어 행렬을 얻습니다.

 

 

마스킹 된 후의 어텐션 스코어 행렬의 각 행을 보면 자기 자신과 그 이전 단어들만을 참고할 수 있음을 볼 수 있습니다.

 

그 외에는 인코더의 첫번째 서브층과 같습니다.

 

여기서 마스크는 Attention Score Matrix의 마스킹 할 위치에 매우 작은 음수값을 더해줍니다.

 

매우 작은 값이므로 소프트맥스 함수를 지나면 행렬의 해당 위치의 값은 0이 됩니다.

 

Transformer 에는 Attetion이 총 3번 쓰이는데 각 어텐션 시 전달하는 마스킹은 아래와 같습니다.

  • 인코더의 셀프 어텐션 : 패딩 마스크를 전달
  • 디코더의 첫번째 서브층인 마스크드 셀프 어텐션 : 룩-어헤드 마스크를 전달 <-- 지금 설명하고 있음.
  • 디코더의 두번째 서브층인 인코더-디코더 어텐션 : 패딩 마스크를 전달

 

 


 

Decoder - Encoder-Decoder Attention

 

디코더의 두번째 서브층은 멀티 헤드 어텐션을 수행한다는 점에서는 이전의 어텐션들과는 공통점이 있으나

이번에는 셀프 어텐션이 아닙니다.

 

셀프 어텐션은 Query, Key, Value가 같은 경우를 말하는데, 인코더-디코더 어텐션은 Query가 디코더인 행렬인 반면,

 

Key와 Value는 인코더 행렬이기 때문입니다. 다시 한 번 각 서브층에서의 Q, K, V의 관계를 정리해봅시다.

  • 인코더의 첫번째 서브층 : Query = Key = Value (Self-Attention)
  • 디코더의 첫번째 서브층 : Query = Key = Value (Self-Attention)
  • 디코더의 두번째 서브층 : Query : 디코더 행렬 / Key = Value : 인코더 행렬

 

Encoder-Decoder Attention

위 그림을 보면 Query는 Decoder 행렬, Key = Value는 Encoder 행렬로 볼 수 있습니다.

 

Attention Score Matrix는 위와 같이 계산이 되겠습니다.

 

다음 계산은 Attention 과 같으니 생략하도록 하겠습니다.

 

 

 

 

 

 

출처:

'Deep Learning > NLP' 카테고리의 다른 글

02. Attention  (0) 2022.05.24
01. RNN, LSTM  (0) 2022.05.23

저번 포스팅했던 RNN과 LSTM에 이어서 이번에는 Transformer에 대해 포스팅 해보도록 하겠습니다.

 

Transformer에 들어가기 앞서 Seq2Seq (Sequence to Sequence)Attention에 대해 알아보겠습니다.

 

Seq2Seq (Sequence to Sequence)

Sequence to Sequence는 번역기와 같이 Sequence DataSequence Data로 나오게 되는 것이라고 보면 됩니다.

 

내부를 보면 Input Data를 받는 부분을 인코더 (Encoder)라 하고, Output 값이 나오는 부분을 디코더 (Decoder)라 합니다.

 

Seq2Seq Training 과정

  • 인코더 (Encoder)
    • Context Vector: 모든 단어들을 순차적으로 입력받은 뒤, 모든 단어 정보들을 압축해서 하나의 벡터로 만듭니다.
  • 디코더 (Decoder)
    • Context Vector를 번역된 단어로 한 개씩 순차적으로 출력합니다.
    • 인코더로부터 전달받은 컨텍스트 벡터는 디코더 RNN 셀의 첫번째 은닉 상태에 사용합니다.
    • 디코더는 <sos>가 입력되면, 다음에 등장할 확률이 높은 단어를 예측합니다. 즉, 시작을 알리는 신호!

 

 

Attention

 

어텐션의 기본 아이디어는 디코더에서 출력 단어를 예측하는 매 시점(time step)마다,

 

인코더에서의 전체 입력 문장을 다시 한 번 참고한다는 점입니다.

 

단, 전체 입력 문장을 전부 다 동일한 비율로 참고하는 것이 아니라,

 

해당 시점에서 예측해야할 단어와 연관이 있는 입력 단어 부분을 좀 더 집중(attention)해서 보게 됩니다.

 

 

어텐션을 함수로 표현하면 주로 다음과 같이 표현됩니다.


Attention(Q, K, V) = Attention Value

 

어텐션 함수는

 

1. '쿼리(Query)'에 대해서 모든 '키(Key)'와의 유사도를 각각 구합니다.

 

2. 유사도를 키와 맵핑되어있는 각각의 '값(Value)'에 반영

 

3. '값(Value)'을 모두 더해서 리턴

 

여기서는 이를 어텐션 값(Attention Value)이라고 하겠습니다.

 

  • Q = Query : t 시점의 디코더 셀에서의 은닉 상태
  • K = Keys : 모든 시점의 인코더 셀의 은닉 상태들
  • V = Values : 모든 시점의 인코더 셀의 은닉 상태들

 


Dot Product Attention

 

Dot Product Attention

Dot Product Attention은 Attention의 종류 중 하나로써 Attention의 이해를 돕기 위해 이를 기준으로 설명하겠습니다.

 

※ 사전 정의

  • 위 그림은 디코더의 세번째 LSTM 셀에서 출력 단어를 예측할 때, 어텐션 메커니즘을 사용하는 모습을 보여줍니다. 
  • 디코더의 첫번째, 두번째 LSTM 셀은 이미 어텐션 메커니즘을 통해 je와 suis를 예측하는 과정을 거쳤다고 가정
  • 세번째 LSTM 셀은 출력 단어를 예측하기 위해서 인코더의 모든 입력 단어들의 정보를 다시 한번 참고하고자 합니다.
  • 소프트맥스 함수를 통해 나온 결과값은 I, am, a, student 단어 각각이 출력 단어를 예측할 때 얼마나 도움이 되는지의 정도를 수치화한 값
  • 인코더의 은닉 상태는 각각 \(h_{1}, h_{2}, h_{3}, h_{4}\) 입니다.
  • 디코더의 은닉 상태는 현재 시점 기준 \(s_{t}\) 입니다.

 

 

Attention Score

시점 t에서 출력 단어를 예측하기 위해서 디코더의 셀은 세 개의 입력값이 필요합니다.

  • 이전 시점 t-1의 은닉 상태
  • 이전 시점 t-1에 나온 출력 단어
  • 어텐션 값 (Attention Value) = \(a_{t}\)

 

 

※ Attention 값 구하기 ※

 

Attention 값을 구하기 위해서는 어텐션 스코어 (Attention Score)를 먼저 구해주어야 합니다.

 

어텐션 스코어란 현재 디코더의 시점 t에서 단어를 예측하기 위해,

 

인코더의 모든 은닉 상태 각각이 디코더의 현 시점의 은닉 상태 st와 얼마나 유사한지를 판단하는 스코어값입니다.

 

 

1. \(s_{t}\) 를 전치 (transpose) 하여 \(s_{t}^{T}\) 를 만들어 줍니다.

 


Attention Score​

2. 각각의 \(h_{i}\) 와 내적을 시행해줍니다! - \(score(Q, K) = Q \cdot K\)

  • \(score(s_{t}, h_{i}) = s_{t}^{T}h_{i} \)

 


3. \(s_{t}\) 와 \( h_{1}, h_{2}, ... \) 의 내적 모음을 \(e^{t}\) (어텐션 스코어 모음값) 이라고 정의 해줍니다.

  • \(e^{t} = [s_{t}^{T}h_{1}, s_{t}^{T}h_{2}, ... s_{t}^{T}h_{i}]\)
  • 각각의 값은 어텐션 가중치(Attention Weight)라고 합니다.

 


4. \(e^{t}\) 에 softmax를 적용하여 모든 값을 합하면 1이 되는 확률 분포를 얻어냅니다.

  • \(a^{t} = softmax(e^{t})\)

 


5. 어텐션 값(Attention Value) 을 구해줍니다.

  • \(a_{t}=\sum_{i = 1}^{N}a_{i}^th_{i}\)

 


6. 어텐션 값과 디코더의 t 시점의 은닉 상태를 Concatenate 합니다.

  • \(a_{t}\) 와 \(s_{t}\)의 결합은 \(\upsilon_{t}\) 라고 합니다.
  • \(\upsilon_{t}\) 를 \(\hat{y}\) 를 예측하는데 입력값으로 사용합니다.

 


7. 출력층 연산의 입력이 되는 \(\tilde{s}_{t}\) 를 구해줍니다.

  • 논문에서는 \(\upsilon_{t}\) 를 바로 출력층의 연산에 보내기 전에 신경망 연산을 하나 추가해줬습니다.

 


8. \(\tilde{s}_{t}\) 를 출력층의 입력으로 사용하여 \(\hat{y}_{t}\) 를 구합니다.

 

 

 

 

이렇게 Key, Query, Value 개념에 맞게 Attention을 살펴 봤습니다.

 

이제 다음 게시물은 Transformer를 정리해서 올리겠습니다.

 

 

 

 

 

 

 

 

 

출처:

'Deep Learning > NLP' 카테고리의 다른 글

03. Transformer  (0) 2022.05.25
01. RNN, LSTM  (0) 2022.05.23

Vision Transformer를 보면서 NLP에 대한 지식이 한참 모자른것 같아서 처음부터 다시 공부하려고 작성했습니다.

 

 

RNN (Recurrent Neural Network)

 

RNN(Recurrent Neural Network)은 순차적으로 처리하는 시퀀스(Sequence) 모델입니다.

 

번역기를 생각해보면 입력은 번역하고자 하는 단어로 이루어진 순차적인 문장입니다.

 

출력에 해당되는 번역된 문장 또한 단어의 시퀀스입니다.

 

이와 같이 순차적인 데이터들을 처리하기 위해 고안된 모델들을 시퀀스 모델이라고 합니다.

 

그 중 RNN은 가장 기본적인 인공 신경망 시퀀스 모델입니다.

 

은닉층에서 활성화 함수를 지난 값은 오직 출력층 방향으로만 향하는 신경망들을 피드 포워드 신경망(Feed Forward Neural Network)이라고 합니다.

 

하지만 RNN은 피드 포워드 신경망과는 다르게 출력층의 방향으로도 나아가면서, 다른 은닉층의 input 값으로도 나아갑니다.

 

RNN

위 사진은 RNN을 간단히 나타낸 그림입니다.

 

현 시점을 \(t\) 라고 한다면, \(t\) 는 \(t+1\) 에 영향을 주고, \(t+1\) 는 \(t+2\) 에 영향을 주는 Sequence 형태 입니다.

 

RNN 종류

또한, RNN은 입력과 출력을 다양하게 설계 할 수 있습니다.

 

RNN layer를 2개 쌓는 다면 위와 같은 구조로 됩니다. (깊은 순환 신경망 (Deep Recurrent Neural Network))

 

 

RNN 내부 구조

 

RNN 내부 구조

RNN의 내부 구조는 위 사진과 같습니다.

 

레이어를 수식으로 표현하면 순차적으로 아래와 같습니다.

 

\( y1 = tanh(W_{xh} * x1 + b) \)

\( y2 = tanh(W_{xh} * x2 + W_{hh} * h_{t - 1} + b) \)

\( y3 = tanh(W_{xh} * x3 + W_{hh} * h_{t} + b) \)

\( y4 = tanh(W_{xh} * x4 + W_{hh} * h_{t + 1} + b) \)

 

여기서, \( h \) 는 각각

 

\( h_{t - 1} = tanh(W_{xh} * x1 + b) \)

\( h_{t} = tanh(W_{xh} * x2 + W_{hh} * h_{t - 1} + b) \)

\( h_{t + 1} = tanh(W_{xh} * x1 + W_{hh} * h_{t} + b) \)

 

입니다.

 

각 변수들의 크기는 위와 같습니다.

 

즉, 위와 같이 이전 값들을 더해줌으로써 순차적인 데이터들을 처리할 수 있습니다.

 

여기서 \( tanh \) 는 비선형성을 주기 위해 추가해주었습니다.

 

 

tanh

 

tanh

tanh 는 sigmoid 와 매우 비슷해 보이지만,

 

sigmoid 는 0 ~ 1 사이의 값을 갖고, tanh 는 -1 ~ 1 사이의 값을 가지게 됩니다.

 

또한, sigmoid의 최대 기울기 값이 0.25이지만 tanh는 그보다 큰 값을 가지기 때문에 Gradient Vanishing 현상이 쬐~끔 나아졌다고 생각합니다. - 제 개인적인 생각.

 

tanh 공식

 

 

 

양방향 순환 신경망 ( Bidirectional Recurrent Neural Network )

 

 

양방향 RNN은 하나의 출력값을 예측하기 위해 기본적으로 두 개의 메모리 셀을 사용합니다.

 

첫번째 메모리 셀은 앞에서 배운 것처럼 앞 시점의 은닉 상태(Forward States) 를 전달받아 현재의 은닉 상태를 계산합니다.

 

위의 그림에서는 주황색 메모리 셀에 해당됩니다.

 

두번째 메모리 셀은 앞에서 배운 것과는 다릅니다.

 

앞 시점의 은닉 상태가 아니라 뒤 시점의 은닉 상태(Backward States) 를 전달 받아 현재의 은닉 상태를 계산합니다.

 

입력 시퀀스를 반대 방향으로 읽는 것입니다. 

 

 


LSTM ( Long Short-Term Memory )

 

RNN의 발전한 형태인 LSTM 입니다.

 

RNN은 비교적 짧은 시퀀스(sequence)에 대해서만 효과를 보이는 단점이 있습니다.

 

바닐라 RNN의 시점(time step)이 길어질 수록 앞의 정보가 뒤로 충분히 전달되지 못하는 현상이 발생합니다.

 

뒤로 갈수록 \(x1\) 의 정보량은 손실되고, 시점이 충분히 긴 상황에서는 \(x1\) 의 전체 정보에 대한 영향력은 거의 의미가 없을 수도 있습니다.

 

LSTM

뭔가 굉장히 복잡해 보입니다.

 

LSTM은 은닉층의 메모리 셀에 입력 게이트, 삭제 게이트, 출력 게이트를 추가하여 불필요한 기억을 지우고, 기억해야할 것들을 정합니다.

 

위의 그림에서는 t시점의 셀 상태를 \(C_{t}\)로 표현되었습니다.

 

※ 변수 설명

  • 이하 식에서 \( \sigma \)는 시그모이드 함수를 의미합니다.
  • 이하 식에서 \( tanh \)는 하이퍼볼릭탄젠트 함수를 의미합니다.
  • \( W_{xi}, W_{xg}, W_{xf}, W_{xo} \) 는 \( x_{t} \) 와 함께 각 게이트에서 사용되는 4개의 가중치입니다.
  • \( W_{hi}, W_{hg}, W_{hf}, W_{ho} \) 는 \(h_{t - 1} \) 와 함께 각 게이트에서 사용되는 4개의 가중치입니다.
  • \( b_{i], b_{g}, b_{f}, b_{o} \) 는 각 게이트에서 사용되는 4개의 편향입니다.

 

 


1. 입력 게이트

 

입력 게이트

※ 사용하는 변수

  • \( W_{xi},    W_{hi},    b_{i} \)
  • \( W_{xg},    W_{hg},    b_{g} \)

 

입력 게이트는 현재 정보를 기억하기 위한 게이트입니다. 

 

\( \sigma \) 함수를 지나 0과 1사이의 값을 가지는 it과 \( tanh \) 함수를 지나 -1과 1사이의 값을 가지는 gt.

 

두 개의 값을 가지고 이번에 선택된 기억할 정보의 양을 정하는데,

 

구체적으로 어떻게 결정하는지는 아래에서 배우게 될 셀 상태 수식에서 보겠습니다.

 

 


2. 삭제 게이트

 

삭제 게이트

※ 사용하는 변수

  • \( W_{xf},    W_{hf},    b_{f} \)

 

삭제 게이트는 기억을 삭제하기 위한 게이트입니다.

 

0에 가까울수록 정보가 많이 삭제된 것이고 1에 가까울수록 정보를 온전히 기억한 것입니다.

 

 


3. 셀 상태

 

셀 상태

입력 게이트에서 구한 it, gt 이 두 개의 값에 대해서 원소별 곱(entrywise product)을 진행합니다.

 

여기서는 식으로 ∘ 로 표현합니다. 이것이 이번에 선택된 기억할 값입니다.

 

입력 게이트에서 선택된 기억을 삭제 게이트의 결과값과 더합니다.

 

이 값을 현재 시점 t의 셀 상태라고 하며, 이 값은 다음 t+1 시점의 LSTM 셀로 넘겨집니다.

 

삭제 게이트의 출력값인 ft가 0이 된다면,

 

이전 시점의 셀 상태의 값인 Ct−1은 현재 시점의 셀 상태의 값을 결정하기 위한 영향력이 0이 되면서,

 

오직 입력 게이트의 결과만이 현재 시점의 셀 상태의 값 Ct을 결정할 수 있습니다.

 

입력 게이트의 it값을 0이라고 한다면, 현재 시점의 셀 상태의 값 Ct는 오직 이전 시점의 셀 상태의 값 Ct−1의 값에만 의존합니다.

 

결과적으로 삭제 게이트는 이전 시점의 입력을 얼마나 반영할지를 의미하고, 입력 게이트는 현재 시점의 입력을 얼마나 반영할지를 결정합니다.

 

 

 

 


4. 출력 게이트

 

출력 게이트

 

출력 게이트는 현재 시점 t의 x값과 이전 시점 t-1의 은닉 상태가 시그모이드 함수를 지난 값입니다.

 

해당 값은 현재 시점 t의 은닉 상태를 결정하는 일에 쓰이게 됩니다.

 

셀 상태의 값이 하이퍼볼릭탄젠트 함수를 지나 -1과 1사이의 값이 되고,

 

해당 값은 출력 게이트의 값과 연산되면서, 값이 걸러지는 효과가 발생하여 은닉 상태가 됩니다.

 

은닉 상태의 값은 또한 출력층으로도 향합니다.

 

다음은 Transformer에 대해 포스팅 해보겠습니다.

 

 

 

 

 

 

 

출처 :

'Deep Learning > NLP' 카테고리의 다른 글

03. Transformer  (0) 2022.05.25
02. Attention  (0) 2022.05.24

 

https://programmers.co.kr/learn/courses/30/lessons/42748

 

코딩테스트 연습 - K번째수

[1, 5, 2, 6, 3, 7, 4] [[2, 5, 3], [4, 4, 1], [1, 7, 3]] [5, 6, 3]

programmers.co.kr

 

#include <string>
#include <vector>
#include <algorithm>

using namespace std;

vector<int> solution(vector<int> array, vector<vector<int>> commands) {
	vector<int> answer;
	vector<int> temp;
	int first = 0;
	int end = 0;
	int pick = 0;
	int temp2 = 0;

	for (int i = 0; i < commands.size(); i++)
	{
		first = commands[i][0];
		end = commands[i][1];
		pick = commands[i][2];
		for (int j = first - 1; j < end; j++)
		{
			temp.push_back(array[j]);
		}
		sort(temp.begin(), temp.end());
		answer.push_back(temp[pick - 1]);
		temp.clear();
		vector<int>().swap(temp);
	}

	return answer;
}

int main() {
	vector<int> absolutes = { 1, 5, 2, 6, 3, 7, 4 };
	vector<vector<int>> commands = { {2, 5, 3}, { 4, 4, 1}, { 1, 7, 3 } };

	solution(absolutes, commands);
}

'알고리즘 > Coding Test' 카테고리의 다른 글

추억점수 C++  (0) 2023.04.14
달리기 경주 C++  (0) 2023.04.14
소수 만들기 C++  (0) 2022.05.19
음양 더하기 C++  (0) 2022.05.19
없는 숫자 더하기 C++  (0) 2022.05.19

 

https://programmers.co.kr/learn/courses/30/lessons/12977?language=cpp 

 

코딩테스트 연습 - 소수 만들기

주어진 숫자 중 3개의 수를 더했을 때 소수가 되는 경우의 개수를 구하려고 합니다. 숫자들이 들어있는 배열 nums가 매개변수로 주어질 때, nums에 있는 숫자들 중 서로 다른 3개를 골라 더했을 때

programmers.co.kr

 

 

 

#include <string>
#include <vector>
#include <iostream>

using namespace std;

int solution(vector<int> nums) {
	int answer = 0;
	int temp = 0;
	bool check = true;

	// [실행] 버튼을 누르면 출력 값을 볼 수 있습니다.
	cout << "Hello Cpp" << endl;

	for (int i = 0; i < nums.size() - 2; i++)
	{
		for (int j = i + 1; j < nums.size() - 1; j++)
		{
			for (int k = j + 1; k < nums.size(); k++)
			{
				temp = nums[i] + nums[j] + nums[k];
				for (int l = 2; l < temp / 2; l++)
				{
					if (temp % l == 0) { check = false; break; }
				}

				if (check) { answer += 1; }
				check = true;
			}
		}
	}

	return answer;
}

int main() {
	vector<int> absolutes = { 1,2,3,4 };

	solution(absolutes);
}

'알고리즘 > Coding Test' 카테고리의 다른 글

달리기 경주 C++  (0) 2023.04.14
K번째 수 C++  (0) 2022.05.19
음양 더하기 C++  (0) 2022.05.19
없는 숫자 더하기 C++  (0) 2022.05.19
키패드 누르기 C++  (0) 2022.05.19

 

https://programmers.co.kr/learn/courses/30/lessons/76501

 

코딩테스트 연습 - 음양 더하기

어떤 정수들이 있습니다. 이 정수들의 절댓값을 차례대로 담은 정수 배열 absolutes와 이 정수들의 부호를 차례대로 담은 불리언 배열 signs가 매개변수로 주어집니다. 실제 정수들의 합을 구하여 re

programmers.co.kr

 

 

#include <string>
#include <vector>

using namespace std;

int solution(vector<int> absolutes, vector<bool> signs) {
	int answer = 0;

	for (int i = 0; i < absolutes.size(); i++)
	{
		if (signs[i])
		{
			answer += absolutes[i];
		}
		else
		{
			answer -= absolutes[i];
		}
	}

	return answer;
}

int main() {
	vector<int> absolutes = { 4,7,12 };
	vector<bool> signs = { true,false,true };

	solution(absolutes, signs);
}

'알고리즘 > Coding Test' 카테고리의 다른 글

K번째 수 C++  (0) 2022.05.19
소수 만들기 C++  (0) 2022.05.19
없는 숫자 더하기 C++  (0) 2022.05.19
키패드 누르기 C++  (0) 2022.05.19
숫자 문자열과 영단어 C++  (0) 2022.05.18

 

https://programmers.co.kr/learn/courses/30/lessons/86051

 

코딩테스트 연습 - 없는 숫자 더하기

0부터 9까지의 숫자 중 일부가 들어있는 정수 배열 numbers가 매개변수로 주어집니다. numbers에서 찾을 수 없는 0부터 9까지의 숫자를 모두 찾아 더한 수를 return 하도록 solution 함수를 완성해주세요.

programmers.co.kr

 

 

#include <string>
#include <vector>

using namespace std;

int solution(vector<int> numbers) {
	int answer = -1;
	int zerotonine = 45;
	int vector_plus = 0;

	// 0 ~ 9 까지의 덧셈은 = 45
	for (int i = 0; i < numbers.size(); i++)
	{
		vector_plus += numbers[i];
	}

	answer = zerotonine - vector_plus;

	return answer;
}

'알고리즘 > Coding Test' 카테고리의 다른 글

소수 만들기 C++  (0) 2022.05.19
음양 더하기 C++  (0) 2022.05.19
키패드 누르기 C++  (0) 2022.05.19
숫자 문자열과 영단어 C++  (0) 2022.05.18
신규 아이디 추천 C++  (0) 2022.05.18

 

https://programmers.co.kr/learn/courses/30/lessons/67256

 

코딩테스트 연습 - 키패드 누르기

[1, 3, 4, 5, 8, 2, 1, 4, 5, 9, 5] "right" "LRLLLRLLRRL" [7, 0, 8, 2, 8, 3, 1, 5, 7, 6, 2] "left" "LRLLRRLLLRR" [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] "right" "LLRLLRLLRL"

programmers.co.kr

 

#include <string>
#include <vector>

using namespace std;

string solution(vector<int> numbers, string hand) {
	string answer = "";
	int right_location = 10;
	int left_location = 12;

	for (int i = 0; i < numbers.size(); i++)
	{
		int temp = numbers[i];
		if (temp == 0) { temp = 11; }	// 0일때의 위치

		if (temp % 3 == 1)
		{
			left_location = temp;
			answer.append("L");
		}
		else if (temp % 3 == 0)
		{
			right_location = temp;
			answer.append("R");
		}
		else
		{
			// 행 거리 차이
			int right_dis = abs((right_location - 1) / 3 - temp / 3);
			int left_dis = abs((left_location - 1) / 3 - temp / 3);

			// 열 거리 차이
			right_dis += abs((right_location - 1) % 3 - (temp - 1) % 3);
			left_dis += abs((left_location - 1) % 3 - (temp - 1) % 3);
			
			if (right_dis < left_dis)
			{
				right_location = temp;
				answer.append("R");
			}
			else if (right_dis > left_dis)	// left 거리가 더 짭을때
			{
				left_location = temp;
				answer.append("L");
			}
			else
			{
				if (hand == "right") { right_location = temp; answer.append("R"); }
				else { left_location = temp; answer.append("L"); }
			}
		}
	}

	return answer;
}

int main() {
	vector<int> ex = { 7, 0, 8, 2, 8, 3, 1, 5, 7, 6, 2 };
	solution(ex, "left");
}

'알고리즘 > Coding Test' 카테고리의 다른 글

음양 더하기 C++  (0) 2022.05.19
없는 숫자 더하기 C++  (0) 2022.05.19
숫자 문자열과 영단어 C++  (0) 2022.05.18
신규 아이디 추천 C++  (0) 2022.05.18
신고 결과 받기 C++  (0) 2022.05.17

+ Recent posts