Attention
1. Attention 그게 뭐임?
- Attention은 네트워크 아키텍처의 하나의 컴포넌트임. 그리고 이건 이키텍처에서 관계성, 유사도를 가중치를 부여하여 관리하는 역할을 함.
- 옛날엔 번역기 같은 NLP 모델에서 RNN을 썼음. 근데 RNN은 문장이 길어질수록 기울기 소실 + 장기 의존성 문제같은 기억력이 안좋은 문제점이 생김. 그래서 "중요한 단어에 집중해서 학습하면 어떨까?" 라는 아이디어가 나옴 → 이게 Attention의 시작임.
- 그래서 Attention이 뭐냐? 문장 안에서 어떤 단어가 중요한지를 판단해서, 그 단어에 가중치(weight)를 줌. 그 결과, 긴 문장도 정보 손실 없이 학습 가능하고, 병렬처리까지 가능해짐.
2. 기존 RNN의 문제점: 왜 Attention이 필요했음?
(1) 기울기 소실 (Gradient Vanishing)
- RNN은 문장을 순차적으로 처리하는데, 역전파(backpropagation)할 때 기울기가 점점 작아짐.
- 특히, 문장이 길어질수록 초반 단어의 정보가 다 날아감.
- 예를 들어, "나는 어릴 때부터 프로그래머가 되고 싶었다." 를 학습할 때,
- 마지막 단어 "되고 싶었다" 를 생성할 때, "나는"이랑 연관이 있을 수도 있잖아?
- 근데 기존 RNN은 초반 단어를 다 까먹음 → 장기 의존성 문제 발생.
(2) 문장이 길어질수록 정보 손실
- RNN은 입력 문장을 고정된 크기의 Context Vector 하나로 압축해서 기억함.
- 즉, 모든 단어 정보를 하나의 벡터로 퉁쳐버림.
- 문장이 길어질수록 정보 손실이 커지고, 번역 품질이 안좋음.
(3) 병렬처리 불가능 (Sequential processing)
- RNN은 이전 단어를 먼저 계산해야 다음 단어를 예측할 수 있음.
- 즉, 문장을 한 번에 처리 못 하고 순차적으로 계산해야 함.
- 학습 속도가 엄청 느림.
그래서 나온게 Attention
→ "출력할 단어를 만들 때, 입력 문장의 중요한 단어에 가중치를 줘서 정보 손실을 줄이자!"
🔥 2. General Attention (기존 RNN + Attention)
"출력 단어를 만들 때, 입력 문장에서 어떤 단어를 집중해서 봐야 하는지 결정하는 기법"
- 우리가 번역기를 만든다고 가정해보자.
한-영 번역 "나는 한국 사람이다." → "I am Korean." 이라고 번역해야 함. - 기존 RNN 방식이라면? → Decoder는 Encoder가 만든 Context Vector 하나만 보고 번역함.
근데 문장이 길어지면? → 처음 단어들이 다 뭉개져서 기억 못 함.
그래서 나온 게 General Attention. - General Attention은 Decoder가 특정 단어를 예측할 때, 입력 문장에서 어떤 단어를 중요하게 봐야 하는지 점수(가중치)를 매김.
- 기존 Seq2Seq 모델에서 Decoder가 입력 단어 중 어디를 참고해야 하는지 가중치를 줌. 즉, "어떤 단어가 더 중요한가?" 를 학습하는 방식.
General Attention의 동작 방식
한-영 번역기에서 "나는 한국 사람이다." → "I am Korean." 번역한다고 생각해보자.
(1) Encoder: 입력 문장을 hidden state로 변환
- Encoder는 나는 한국 사람이다. 문장을 순차적으로 처리하면서, 각 단어를 hidden state 벡터로 변환함.
나는 → h1
한국 → h2
사람 → h3
이다. → h4
각 단어마다 벡터(h1, h2, h3, h4)가 생김.
(2) Decoder: 단어 하나씩 번역하면서, 어떤 단어를 집중할지 결정
- Decoder는 문장을 생성할 때, 현재 단어를 예측해야 함.
- 예를 들어 "I am Korean."을 생성할 때, "Korean"을 만들려면 "한국" 단어를 집중해서 봐야 함.
그래서 Decoder는 Encoder의 모든 hidden state를 참고해서 중요도를 계산함. 이걸 "Attention Score" 라고 부름.
(3) Attention Score 계산 (어떤 단어를 중요하게 볼지 결정)
Decoder는 입력 문장의 각 단어에 "이 단어가 중요해?" 라고 점수를 매김.
현재 Decoder 상태 → Query(Q) (지금 예측할 단어)
Encoder 단어 벡터들 → Key(K) (입력 문장의 모든 단어)
각 Q랑 K의 내적을 계산해서 점수를 정함:
Score = Q * K^T
이제 이 점수를 softmax로 변환해서 가중치(확률 값)로 만듦.
Attention Weight = softmax(Score)
결과:
나는 → 0.1
한국 → 0.8 (Korean 만들 때 중요!)
사람 → 0.05
이다. → 0.05
즉, Decoder는 "Korean"을 생성할 때 "한국" 단어를 80% 집중해서 봄.
(4) 최종 출력 벡터 계산 (중요한 단어만 강조)
이제 이 가중치를 활용해서 최종 벡터를 계산해야 함.
Context Vector = sum(Attention Weight * Value(V))
- Value(V) = Encoder의 hidden state (입력 단어 정보)
- 가중치를 곱해서 합치면, "중요한 정보만 남은 벡터" 가 됨.
- 이걸 기반으로 Decoder가 단어를 예측함.
결국 "Korean"을 생성할 때 "한국" 단어가 가장 중요한 정보로 남게 됨.
General Attention 요약
- 기존 RNN은 Encoder의 마지막 hidden state만 참고 → 문장이 길어지면 정보 손실 발생.
- General Attention은 Decoder가 "이 단어를 만들 때, 입력 문장의 어떤 단어를 집중해야 하는지" 가중치를 계산함.
- 입력 문장의 모든 hidden state를 참고해서 가중치를 매기고, 중요한 정보만 남겨서 출력 단어를 생성함.
🔥 3. Self-Attention (Transformer의 핵심)
Self-Attention이 뭐임?
- "각 단어가 문장 내에서 다른 단어들과 얼마나 연관 있는지 가중치를 계산하는 기법"
- "문장에서 중요한 단어를 자동으로 찾아 집중할 수 있도록 하는 기법"
- 기존 RNN은 "순차적"으로 계산해서 문장 전체를 한 번에 못 봄.
- 하지만 Self-Attention은 모든 단어를 한꺼번에 분석해서, 더 중요한 단어를 강조할 수 있음.
Self-Attention이 해결한 것?
- "문장 내 모든 단어를 동시에 분석해서, 어떤 단어가 중요한지 가중치를 부여"
- "모든 단어가 서로 연결될 수 있도록 학습"
- "한 문장을 병렬처리 가능하게 만들어, 학습 속도를 빠르게 개선"
Self-Attention의 작동 방식 (Q, K, V)
Self-Attention은 Query(Q), Key(K), Value(V) 개념을 사용해서 단어 간의 연관성을 계산함.
요소 | 역할 | 설명 |
Q (Query) | "이 단어가 중요할까?", 질문 | 내가 지금 찾고 싶은 정보 (검색어 느낌) |
K (Key) | "각 단어들의 특징이 뭘까?" | 문장의 각 단어가 가진 정보, 특징 (데이터베이스 키 같은 개념) |
V (Value) | "실제 단어 정보", Q, K를 이용해 알아낸 것 | 최종적으로 사용되는 단어 벡터 (중요도 적용됨) 문장의 실제 정보 (단어 의미 벡터) |
Query와 Key 내적의 의미는?
- Self-Attention에서 Query(Q)와 Key(K)의 내적(dot product)은 "유사도(중요도) 점수"를 계산하는 핵심 연산임.
- 이 값이 높을수록, 해당 단어들이 서로 강하게 연결되어 있다는 의미고, 값이 낮으면 연관성이 적거나 중요하지 않다는 의미임.
내적(Inner product, Dot product)?
- 내적(Inner Product, Dot Product)은 두 벡터가 얼마나 같은 방향을 향하고 있는지 측정하는 연산.
- 즉, 두 벡터가 유사하면 내적 값이 크고, 유사하지 않으면 내적 값이 작거나 0이 됨.
- 두 벡터가 직각을 이루면 → 내적 값이 0 → 두 벡터는 완전히 독립적 (연관성이 없음)
- 두 벡터가 반대 반향이면 → 내적 값이 음수 → 두 단어가 완전히 반대 의미라는 뜻
- Q와 K의 내적 값이 크면, 두 단어가 의미적으로 유사하다는 뜻
Value는?
- Self-Attention이 하는 일은 결국 "어떤 단어가 중요한지 점수를 매기는 것"
- 하지만 점수만 있으면 의미가 없음.
- 점수를 활용해서 "어떤 단어 정보를 최종적으로 남길지" 계산해야 함.
- 이때 "점수를 곱하는 대상"이 V (Value)임.
예제: "The animal didn’t cross the street because it was too tired."
💡 "it"이 "animal"을 의미하는지 판단해야 함.
1️⃣ Query, Key, Value 벡터 만들기
단어별 벡터 (임의의 값 예시)
The Q: [0.2, 0.4] K: [0.3, 0.7] V: [0.5, 0.1]
animal Q: [0.9, 0.1] K: [0.8, 0.2] V: [0.6, 0.8]
didn’t Q: [0.5, 0.3] K: [0.4, 0.6] V: [0.7, 0.5]
cross Q: [0.3, 0.7] K: [0.2, 0.9] V: [0.2, 0.9]
street Q: [0.1, 0.9] K: [0.5, 0.5] V: [0.1, 0.7]
because Q: [0.7, 0.2] K: [0.6, 0.3] V: [0.4, 0.3]
it Q: [1.0, 0.2] K: [0.9, 0.1] V: [0.5, 0.5] ← 🔥 핵심 단어
was Q: [0.4, 0.8] K: [0.3, 0.9] V: [0.2, 0.6]
too Q: [0.2, 0.6] K: [0.7, 0.2] V: [0.3, 0.4]
tired Q: [0.5, 0.5] K: [0.6, 0.4] V: [0.1, 0.9]
- 각 단어마다 Q, K, V 벡터가 존재함.
- it의 의미를 찾을 때, Q(it)와 문장의 모든 K를 비교함.
2️⃣ Query와 Key의 내적을 계산하여 "유사도(중요도)"를 구함
Score(it, animal) = Q(it) • K(animal) = (1.0 * 0.8) + (0.2 * 0.2) = 0.84
Score(it, street) = Q(it) • K(street) = (1.0 * 0.5) + (0.2 * 0.5) = 0.6
Score(it, tired) = Q(it) • K(tired) = (1.0 * 0.6) + (0.2 * 0.4) = 0.68
- "it"이 "animal"과 제일 높은 점수를 가짐 (0.84).
3️⃣ Softmax 적용하여 확률값(가중치) 계산
Attention Weight = softmax(Score)
animal → 0.7
street → 0.15
tired → 0.15
- "it"은 "animal"을 70% 정도 중요하게 생각함.
4️⃣ 가중치를 곱해서 최종 Value 벡터 계산
Context Vector = sum(Attention Weight * Value)
- "it"을 해석할 때 "animal"이 70% 정도 반영되도록 조정됨.
참고
https://velog.io/@alstjsdlr0321/Chapter-1.-Introduction-to-LLM-LM-and-ChatGPT-Task-Examples
https://recipesds.tistory.com/entry/Attention-유사도-응용의-끝판왕-그리고-언젠간-알아야-한다