cs231n 2025 ver. - Lecture 8

이준학·2026년 6월 10일

cs231n

목록 보기
14/17

    이전에 들었던 cs231n 강의는 약 9~10년 정도 된 강의였다. 얼마 전에 유튜브를 보다가 2025년 버전이 나왔다는 것을 확인하고, 이전 강의와 겹치지 않는 부분만 들어봐야겠다고 생각했다. Lecture 8은 Attention과 Transformer에 대한 내용이다. 이전의 내용인 RNN의 문제와 이를 어느 정도 해결한 RNN + Attention의 케이스에서 Attention을 독립적인 layer로 떼어내는 과정을 따라가게 될 것이다.

1. Recap of RNNs

    RNN은 data sequence를 처리하는 네트워크로, 다양한 분야의 task에 적용될 수 있다. one-to-one problem을 해결하는 CNN과 달리, RNN은 many-to-many problem들을 해결하는데 사용된다. 그러나 sequence가 길어지면 bottleneck 문제가 생기며 context vector가 제대로 정보를 전달할 수 없다는 문제가 있다.

1) Motivating Problem 1: Seq-to-Seq RNNs

    강의에서 살펴보는 예시는 영어를 이탈리아어로 번역하는 machine translation의 예시이다. 이를 위해 RNN을 사용한다. Seq-to-Seq RNN은 아래 사진과 같은 Encoder-Decoder 구조로 구성되어 있다.

위의 사진에서의 문자들은

  • cc: context vector
  • hh, ss : 각각 encoder, decoder의 hidden state.
  • xx: input sequence
  • yy: output sequence

를 의미한다. Encoder와 Decoder에서 각각 어떤 일이 일어나는지 알아보자.

Encoder

    Encoder의 주된 기능은 input sequence를 요약해서 context vector로 만드는 것이다. 이 과정에서 hidden state hh들이 이용되며, 결과적으로 context vector가 만들어진다. 식은 위의 구조도에서와 같다. 이때 WW는 encoder의 weight 또는 parameter를 의미한다.

ht=fW(xt,ht1)h_t= f_W(x_t, h_{t-1})

참고로, 실전에서 c=hTc=h_T로 많이 사용한다고 한다. (hTh_T: 마지막 hidden state)

Decoder

    Decoder는 encoder가 요약한 context vector cc를 이용해 주어진 task (이 예시에서는 machine translation이다.)를 완료한다. 식은 마찬가지로 아래와 같다.

st=gU(yt1,st1,c)s_t = g_U(y_{t-1},s_{t-1},c)

context vector와 이전 decoder hidden state, 그리고 이전 output을 input으로 받아 다음 hidden state를 계산하는 방식이다. 참고로, 여기서 UU는 decoder의 weight 또는 parameter로, encoder와 decoder는 parameter를 공유하지 않고 따로 학습한다.

2) Problem & Solutions for RNN

    위에서도 잠깐 언급했고, 사진에서도 'input sequence 길이가 길어지면(T=1000) 어떻게 될 것인가?' 라는 질문을 하고 있다. 이러한 문제에 대해 알아보고 그 해결책에 대해서도 고민해보자.

왜 T=1000이 되면 문제가 될까?

    먼저, context vector는 크기가 고정되어 있는 fixed size vector이다. 그래서 만약 input sequence가 길어져 요약해야 할 정보도 많아진다면, 같은 크기의 저장 공간에 더 많은 정보를 요약해야 하는 상황이 발생하는 것이다. 때문에 요약의 퀄리티가 떨어질 수밖에 없다.

해결책?

    이렇게 bottleneck 현상이 일어나기 때문에 output의 각 step마다 전체 input sequence를 볼 수 있다면, 이러한 문제를 해결할 수 있을 것이다. 그래서 나온 방법이 Attention이다.

3) Motivating Problem 2: Seq-to-Seq RNN & Attention

    위의 사진이 Seq-to-Seq RNN에 Attention module이 추가된 버전의 네트워크 구조도이다. RNN 부분은 바뀌는 것이 없기 때문에, Attention을 중심으로 살펴보자. encoder decoder 모두 조금씩은 바뀌지만, 더 중요한 변화는 decoder 쪽에 있다. Attention의 주요 아이디어는 context vector를 decoder의 각 time step마다 계산하겠다는 것이다. 이것은 각 time step에서 전체 input sequence를 보자고 했던 위의 해결책과도 동일한 이야기이다. 현재 decoder time step의 상황에 잘 맞는 input sequence가 무엇일지 계산하는 방식이다.

    이제 어떻게 계산이 이루어지는지 알아보자. 일단 이전에 본 것과 같이 input과 hidden state를 계산한다. 이로부터 계산하는 것은 alignment score ee와 attention score aa이다.

et,i=fatt(hi,st1)e_{t,i} = f_{att}(h_i, s_{t-1})
at,i=softmax(et,i)a_{t,i} = softmax(e_{t,i}), 0<at,i<1,iat,i=10<a_{t,i}<1, \sum_i a_{t,i}=1

Alignment Score (ee)

    여기서 fattf_{att}는 linear layer이다. 이전의 decoder hidden state와 encoder의 모든 hidden state간의 score를 계산한다. 그래서 et,ie_{t,i}와 같이 표현되는 것이다. 만약 e1,2e_{1,2}라면, 첫 번째 decoder time step에서 두 번째 input hidden state(h2h_2)와의 score를 의미하는 것이다. 이렇게 모든 decoder time step에 대한 alignment score를 계산한 후, Attention score를 계산한다.

Attention Score (aa)

    Attention score는 ee를 softmax function에 태운 것이다. 이렇게 하는 이유는 특정 decoder hidden state(time step)에서의 각 input hidden state에 대한 probability distribution을 얻고, 이를 weight와 같이 사용하기 위해서이다. (실제로 context vector를 계산할 때 aa를 weight로 사용한다.

Context Vector (ctc_t)

    다음은 특정 decoder time step에서의 context vector의 계산이다.

ct=iat,ihic_t = \sum_i a_{t,i}h_i

현재 decoder time step과 가장 잘 align 되는 input sequence에 더 가중치를 주어 context vector에 반영되도록 식을 설계함으로써 더 좋은 성능을 기대할 수 있다.

이제 이렇게 얻은 context vector로 decoder의 계산인 st=gU(yt1,st1,ct)s_t = g_U(y_{t-1},s_{t-1},c_t)를 진행한다. 이러한 과정을 반복해서 RNN의 output을 얻는 것이다.

약 4번의 time step이 지나면 위와 같이 출력이 나오게 된다. 이런 방식을 사용하면 우리가 이전의 RNN에서 봤던 input sequence의 길이에 따른 문제도 해결되고, 각 time step 별로 다른 context vector를 사용하기 때문에 정확도도 더 올라갈 수 있다.

    계산된 attention score를 시각화 해보면 아래와 같이 영어와 이탈리아어의 같은 의미를 가진 단어들끼리의 연관성이 높다는 것도 확인할 수 있다.

마지막으로 RNN + Attention에 대해 정리하고 넘어가자. 대략적인 순서는 아래와 같다.

  1. Encoder는 기본적으로 vanilla RNN과 비슷하게 작동한다.
  2. Decoder를 계산할 때, Encoder의 hidden state를 참고해 alignment score와 attention score를 계산한다. 이때 attention score는 decoder의 time step에 대한 input token들의 probability distribution으로 볼 수 있다.
  3. Decoder의 각 time step마다 현재 time step에 적합한 context vector ctc_t를 계산한다. 그리고, 그를 이용해 decoder hidden state를 계산하고, 이를 반복한다.

2. Generalizing Attention

    이제 RNN에서 Attention module을 떼어내 보자. Attention module만 떼어낸다면 다양한 곳에 적용할 수 있기 때문에 강력한 도구가 될 것이다.

1) Attention without RNNs

    Attention Layer만 따로 보기 위해서는 몇 가지의 용어를 정의해야 한다.

  • Data Vectors : RNN에서의 encoder hidden state hth_t
  • Query Vectors : RNN에서의 Decoder hidden state sts_t
  • Output Vectors : context vectors ctc_t (yy가 아님에 주의하자.)

이렇게 정의하면, 각 query(= 특정 time step에서의 decoder hidden state)는 모든 data vector(= encoder hidden state)를 참조하여 하나의 output vector(= context vector)를 내게 된다. 왜 이렇게 정의하는거야? 라는 생각이 들 수 있지만, Attention을 RNN으로부터 떼어내 독립적인 layer로 만들기 위한 과정임을 생각한다면 어느 정도 이해가 가는 설정이다. 지금까지 한 내용을 정리하면 아래와 같이 나타낼 수 있다. 대괄호 안에 들어가 있는 것은 vector의 크기 혹은 차원이다.

우리가 위에서 이야기 했던 것과 크게 다르지 않다는 것도 확인할 수 있다.

2) Generalizing Attention

    이제 여기서 RNN을 완전히 없애려면 어떻게 해야 할까?

  1. Similarity score를 계산할 때, 간단함을 위해 linear layer 대신 scaled dot product를 사용한다.
  2. 여러 개의 query vector를 동시에 처리한다.
  3. Key와 Value를 분리한다.

    먼저 1번에서 scale을 하는 이유는 (DQ\sqrt {D_Q}) silmilarity score가 attention score 계산을 위해 이용될 때 문제가 생기기 때문이다. similarity score 값이 너무 커지면, softmax function의 특성 상 saturation problem이 생기기 쉽다. 따라서 scaling을 통해 similarity score가 너무 커지지 않게 방지하는 것이다.

    3번에서 Key와 Value를 분리하는 것은 위의 사진에서도 볼 수 있듯이 data vector가 similarity 계산과 output vector에 모두 사용되기 때문이다. 효율성 측면에서도 좋지 않기 때문에, Data Vector를 조금 가공한 Key와 Value를 얻어 이를 각각 similarity 계산과 output vector에 따로 사용하는 것이다. Key와 Value는 하나의 목적만 가지기 때문에 더 효율적인 운영이 가능하다. 자세한 내용은 뒤에 더 살펴보도록 하자.

    앞서 이야기한 1~3번의 내용을 모두 반영한 것이 아래의 구조도이다.

꽤나 복잡해 보이지만 RNN의 내용과 다를 게 없다. 하나하나 뜯어보자.

Keys & Values

    위에까지의 설명만 들었다면 '그래서 data vector를 어떻게 가공해서 key랑 value를 만들겠다는 건데?' 라는 의문이 생길 것이다. 의외로 간단하게, key와 value는 linear layer로 구성된다. (dot product) data vector의 projection = key & value가 되는 것이다. 식은 아래와 같다.

K=XWK,V=XWVK = XW_K, V= XW_V

'어떻게 좋은 key & value를 만드는 건데?' 라는 질문은 사실 우리가 생각할 필요가 없다. 이것이 딥러닝의 장점이자 단점이기도 한데, 네트워크가 학습되면서 weight matrix인 WK,WVW_K, W_V가 gradient descent과 같은 알고리즘을 통해 좋은 성능을 내는 방향으로 업데이트 되기 때문이다. key와 value는 data vector에 필터를 먹인 것과 같이 생각할 수도 있다. data vector의 일부만 사용하기 때문이다.

nbsp   대략적이니 이해는 되겠지만, 구체적인 예시를 통해서 key, value를 알아보자.

  • Query : 최고의 학교는 어디인가?
  • Keys : query와 비교되는 것들. (background에서 진행됨)
  • Values : 스탠포드 대학교

Query는 우리가 찾으려는 정보에 대한 질문, Key는 많은 정보들 사이에서 query가 필요로 하는 정보와 비교되는 것들, Value는 질문인 query에 대한 답 정도로 생각할 수 있다.

구조도 설명

    이제 구조도를 이해해보자. 제일 먼저 data vector에서 linear projection을 통해 K,VK,V를 얻는다. Query는 Key와 비교되어 similarity score인 ee가 계산되는데, 이는 우리가 이전에 봤던 것과 별반 다르지 않다. 여러 개의 query를 동시에 처리한다는 것만 달라지고, 그에 따라 적합한 연산인 matrix multiplication이 사용되었을 뿐이다. 이후에 simliarity score가 softmax를 타고 Attention score가 계산된다. 여기서 Attention score의 각 열은 query에 대한 distribution을 의미한다. (ex) A1,1,A1,2,A1,3A_{1,1},A_{1,2}, A_{1,3}Q1Q_1에 대한 distribution을 구성함.)

    output도 matmul로 한 번에 계산된다. 그러나 이전에 본 Attention score와 Value의 weighted sum임은 동일하다.

이러한 형태의 Attention Layer를 Cross Attention Layer라고 한다.

3) Self-Attention Layer

Self-Attention VS Cross-Attention
Attention은 두 가지 종류의 layer가 있다. Cross-Attention은 두 가지의 input(Q,data vector)이 있고, 이들은 서로 attend하며 정보를 주고 받는다. 반면에 Self-Attention은 한 가지의 input(data vector)만 존재한다. 두 경우 모두 Query, Key, Value가 있지만, Self- Attention은 Q,K,V를 모두 data vector에서 만들어 내야 한다. 자기 자신을 attend한다는 의미에서 Self-Attention이라고 이름이 붙은 것이다.

    Self-Attention은 지금까지 엄청나게 많이 사용되는 방법 중 하나다. Transformer도 이것을 기반으로 만들어졌기 때문에 매우 중요하다. 사실 Self-Attention이라고 해서 많은 것이 바뀌는 것은 아니다. Cross-Attention에서 Query가 data vector와 다르게 존재했다면, 여기서는 data vector에서 projection을 통해 Q,K,V를 모두 얻는 것이다. 구조도를 봐도 그것을 확인할 수 있다. 그 이후의 내용은 cross-attention과 동일하다.

    그런데 Cross-Attention과 Self-Attention을 사용하기에 적합한 상황이 있는지 의문이 들 수 있다. 그건 그냥 주어진 데이터를 따르면 된다. 만약 image captioning과 같이 이미지와 텍스트, 두 가지 종류의 input이 있다면 cross-attention이 더 좋은 선택일 것이다. 그러나 image classification과 같은 task는 input이 이미지 하나이기 때문에 self-attention을 선택하는 것이다.

Permutation equivariant

    만약 input의 순서를 바꾼다면 어떻게 될까?

위의 사진에서도 볼 수 있듯이, output도 순서가 바뀌어서 나오게 된다. 이때, output의 내용은 바뀌지 않는다. 이렇게 input의 순서가 바뀌면 output의 순서도 바뀌고, 그 내용은 바뀌지 않는 특성을 permutation equivariant이라고 한다. 식으로는 아래와 같이 표현한다.

F(σ(X))=σ(F(X))F(\sigma (X)) = \sigma (F(X))

이러한 특성을 갖는 Self-Attention은 단점이 있다. Self-Attention은 순서에 대한 정보가 들어 있지 않다. 그러나 어떠한 task에서는 순서에 대한 정보가 도움이 될 수 있다. 이를 반영해주는 것이 fixed size vector를 이용한 function에 따라 인덱스를 매기는 positional encoding이다.

4) Masked Self-Attention

    Masked Self-Attention은 벡터들이 현재 time step 이후의 sequence는 못 보게 masking 처리를 하는 것이다.

위의 사진과 같이 Similiarity를 계산하고, 현재 time step 이후의 similarity는 -\infty 로 만들어준다. 이렇게 하면 softmax를 타고 나올 때 0이 되므로 효과적인 masking을 할 수 있다.

5) Multiheaded Self-Attention

    다음은 Multiheaded Self-Attention이다. 위에서 봤던 self-attention layer 하나를 block 혹은 head이라고 표현한다. multiheaded라는 이름에서 알 수 있듯이 이는 여러 head를 사용하는 것을 의미한다. 동일한 block H개를 사용하며, 이들의 weight matrix는 공유하지 않고 다르게 세팅한다. 굳이 이렇게 해야 하나? 라는 의문이 들 수 있는데, 이는 네트워크의 capacity를 늘리기 위해서이다.

위의 사진과 같은 방식으로 parallel한 방식으로 H(여기서는 H=3이다.)개의 head를 돌린다. 각 head는 위에서 본 self-attention layer와 동일하다. output stage에서는 같은 input에 대한 output을 fusing하여 하나의 output을 만들어 낸다. fusing은 linear layer로 진행된다. 동일한 구조의 head가 여러 개라고 해서 그들이 모두 같은 것을 학습하는 것은 아니다. weight initialization도 랜덤하게 하고, 그에 따라 업데이트도 다르게 될 것이기 때문에 각기 다른 것을 학습해서 fusing 했을 때 더 유의미한 결과가 나오도록 도와주는 역할을 할 가능성이 높다.

3. Transformer

    이제 Self-Attention을 중점적으로 사용한 모델, Transformer에 대해 알아보자. Transformer는 아래와 같이 여러 개의 block으로 구성되어 있다.

위의 구조도에서 block 하나씩 살펴보자.

input이 들어오면 self-attention을 지나 layer norm을 거치게 된다. 모델에 안정성을 주기 위함이다. 그리고 나서 MLP를 통과하고, 다시 Layer norm을 거친다. 중간중간에 residual connection도 확인할 수 있다. Transformer에서는 이러한 블럭 12개가 사용된다. Transformer는 encoder-decoder 구조이지만, 현재 많이 사용되는 transformer 기반 모델들은 decoder only transformer인 경우가 많다.
참고로 이전에 내가 정리했던 Transformer 논문 글을 공유한다.
Attention is all you need

profile
AI/ Computer Vision

0개의 댓글