1. RAG
RAG는 “Retrieval Augmented Generation”의 약자로, 사용자의 쿼리에 대한 응답을 생성할 때 모델의 내재적 지식에만 의존하지 않고, 외부의 지식을 검색하여 보충하는 방법이다. 이 방법은 크게 세 가지 중요한 단계로 구성된다:
- Indexing: 외부 지식을 효율적으로 검색할 수 있도록 미리 처리해 두는 단계이다.
- Retrieval: 사용자의 쿼리에 적합한 지식을 검색하여 모델에 전달하는 단계이다.
- Generation: 전달받은 지식을 기반으로 사용자의 쿼리에 대응하는 응답을 생성하는 단계이다.
2. Language Models
언어 모델은 자연어를 확률적으로 모델링하는 시스템이다. 즉, 주어진 단어나 문장 조합이 얼마나 자연스러운지를 학습하고, 이에 따라 확률을 부여할 수 있다. 세상에는 무수히 많은 어휘가 존재하고, 이들로 구성할 수 있는 단어 시퀀스도 무궁무진하다. 그러나 언어 모델은 이러한 시퀀스 중에서 자연스러운 조합에 높은 확률을, 부자연스러운 조합에는 낮은 확률을 부여한다.
3. Natural Language Processing
언어 모델은 자연어 처리(NLP)에서 다양한 문제를 해결하는 데 사용된다. NLP의 주요 과제는 자연어 이해(NLU)와 자연어 생성(NLG)으로 나뉜다.
- 자연어 이해(NLU): 주어진 텍스트를 모델이 이해하고, 이를 바탕으로 문제를 해결하는 것이다. 예를 들어, 텍스트 분류나 정답 스팬 선택이 여기에 해당한다.
- 자연어 생성(NLG): 모델이 자연스러운 문장을 생성하는 작업이다. 대표적인 예로는 ChatGPT가 있다.
이 두가지 분야는 별개의 문제로 취급되기도 하고, 함께 풀어야 할 문제로도 취급되는 굉장히 밀접한 분야다.
4. Transformer Attention
자연어 분야에서 트랜스포머가 등장하기 전에는 순환신경망(Recurrent Neural Network) 모델들이 주류를 차지하고 있었다. 순환신경망 모델들은 입력값을 순차적으로 받아서 처리할 수 있어서 자연어의 순차적 특성을 반영할 수 있었다.
이런 순환신경망을 응용한 seq2seq를 사용해 각 단어에 얼마나 중요도를 부여할지 까지 계산한 방식이 바다나우 어텐션이었다.
seq2seq 구조
seq2seq 모델은 입력 시퀀스를 순차적으로 받아들이고, 이를 통해 출력을 생성하는 구조를 가진다.
바다나우 어텐션 구조
바다나우 어텐션은 이러한 seq2seq 모델에서 각 단어의 중요도를 계산하여 특정 단어에 더 많은 가중치를 부여하는 방식이다.
Transformer Model
트랜스포머 모델은 "Attention is All You Need”라는 논문 이름이 말해주는 것처럼 바다나우 어텐션과 달리 어텐션이 다른 모델에 붙어 부가적으로 가중치를 계산하는 역할을 하지 않고, 그 자체로서 모델에서 중심적인 역할을 하게된다.
트랜스포머의 인코더는 입력 시퀀스를 한 번에 받아들이고, 이를 통해 각 토큰의 중요도를 계산하여 처리한다.
seq2seq와도 비슷한 형태이다.
그런데 인코더 단계를 보면 시간의 변화에 따라서 토큰을 하나하나 받아들이는 게 아니라 한 번에 모두 받아들이고 있다. 순차적으로 입력값을 처리하던 seq2seq와는 조금 대조적인 모습이다. 디코더단에서도 마찬가지로 이전 시점까지의 출력값들을 한 번에 받아서 처리하고 있다.
이렇게 한 덩어리로 보이는 인코더와 디코더는 사실 여러 개의 인코더와 디코더들이 쌓여있는 형태이다.
각 인코더 레이어는 이전의 출력값을 받아서 새로운 출력값을 출력한다.
디코더도 마찬가지로 각 디코더 레이어는 이전 디코더 레이어의 출력값을 받아서 새로운 출력값을 계산한다.
그런데 이때 인코더 레이어와 다르게 마지막 인코더 블록의 출력값도 디코더 레이어에서는 같이 받아서 참고하게 된다. seq2seq에서 인코더에서 만든 컨텍스트 벡터를 디코더에서 활용했던 것과 유사하다.
5. 트랜스포머 어텐션 - 인코더
자연어로 된 인풋값은 우선 인코더로 들어가기 전에 한번 전처리를 거치게 된다. tokenization과 gen embedding 과정을 거치게 되는데 이 과정에서 워드 투 벡터와 같이 이미 학습된 임베딩을 활용할 수도 있고, 러너블한 임베딩 매트릭스를 활용할 수도 있다. 그런데 임베딩 생성 과정 직후에 Positional Encoding이라는 단계가 하나 더 있다.
이때 같은 단어를 가지고도 어떻게 배열하느냐에 따라 그럴듯한 문장이 될 수도 있고, 전혀 말이 안 되는 단어의 나열이 될 수 있다. 즉, 자연어는 위치 정보에 굉장히 민감한 데이터다. 이때 트랜스포머 어텐션은 입력값을 순차적으로 받지 않고 한 번에 받는다고 앞서 말했다.
순환신경망처럼 입력값을 순서대로 받아서 처리한다면 모델의 단어의 순서가 자연스럽게 반영될 수 있는데 그런 방법이 불가능한 어텐션에서는 단어의 구조를 반영하기 위해서 다른 방법을 사용해야 한다.
이때 사용되는 게 Positional Encoding이다.
Positional Encoding
포지셔널 인코딩은 각 위치에 대해서 고유한 인코딩을 생성한다. 일반적으로 아래와 같은 삼각함수를 통해서 생성되게 된다.
기존의 input값에 포지셔널 인코딩을 더하게 되면 같은 토큰이라도 위치에 따라서 서로 다른 값을 가지게 된다. 그리고 이렇게 sine, cosine 함수를 이용해서 포지션을 인코딩해주면 문장 내에서 단어들의 상대적인 위치를 반영할 수 있다는 이점이 있다.
오른쪽 도표는 차원이 100이고 포지션은 5차원까지 있는 가상의 상황을 세팅하고 포지셔널 인코딩 값을 구현해 본 결과다. 이 표의 i, j 번째 엘리먼트는 i번째 포지션과 j번째 포지션 사이의 닷 프로덕트 결과이다. 여기서 밝게 빛나고 있는 대각선 값은 자기 자신과의 닷 프로덕트가 된다.
표를 잘 보면 대체로 가까운 거리에서는 색이 밝아지고 멀어질수록 색이 어두워진다는 것을 관찰할 수 있다. 즉, 가까워질수록 포지셔널 인코딩 값이 서로 비슷해지고, 멀어질수록 달라진다는 의미다. 문장 내에서 상대적인 위치를 반영할 수 있다는 이점은 이런 뜻이다.
트랜스포머는 입력값을 순차적으로 처리하지 않기 때문에, 단어의 순서를 반영하기 위해 Positional Encoding을 사용한다. 이는 각 위치에 고유한 인코딩을 생성하여, 입력값에 더해주는 방식이다. 이렇게 함으로써 모델이 단어들의 상대적 위치를 인식할 수 있게 된다.
이렇게 포지셔널 인코딩까지 더해서 input embedding이 완성된다. 이렇게 만들어진 임베딩은 본격적으로 인코더 레이어 안으로 들어간다.
6. 닷 프로덕트 어텐션(Dot-Product Attention)
Dot-Product Attention에서는 query, key, value 개념이 나온다.
- query: 무엇을 찾고 있는지에 대한 질문
- key: 데이터를 식별할 수 있는 정보로, query와 비교할 수 있는 데이터
- value: 실제 활용하고자 하는 데이터
이 세 가지 개념을 바탕으로, 트랜스포머 모델은 각 단어의 중요도를 계산하고, 이를 바탕으로 적절한 출력을 생성한다.
바다나우 어텐션으로 설명
query는 내가 무엇을 찾고자 하는지에 대한 목적의식이다.
위 도표를 리콜해 보면 'take your time'이라는 input이 들어온 상황이고 디코더단에서 토큰을 생성하는 중에 나올 수 있는 그림이다.
• 인코더 (Encoder): take, your, time 세 단어를 입력으로 받아 각각의 히든 스테이트 h1, h2, h3를 생성한다.
• 디코더 (Decoder): 이미 생성된 단어 '천천히' 다음에 올 단어를 예측하기 위해 현재 히든 스테이트 s2를 사용한다.
'천천히'라는 토큰이 이전에 생성되었을 때 다음 토큰을 생성하기 위해서 세 토큰 중에 어떤 것을 얼마나 참조해야 되는지 알고 싶은 상황이다.
이때 어떤 토큰을 얼마나 참조할지를 결정하기 위해서 바다나우 어텐션에서는 디코더가 가지고 있는 이전스텝에서의 히든 스테이트를 인코더의 각 타임 스텝에서 생성된 히든 스테이트들과 비교해서 결정했었다.
즉, 이 상태에서 보면
- query는 디코더의 히든 스테이트(s2: 이 예시에서는 '천천히' 다음에 올 단거를 예측할 때 사용되는 디코더의 상태)가 되고
- key는 인코더의 각 타임 스텝에서의 히든 스테이트 h1 (“take”), h2 (“your”), h3 (“time”)가 된다.
- value도 인코더의 각 타임 스텝에서의 히든 스테이트 h1, h2, h3이다. 왜냐하면 인코더의 히든 스테이트를 가중합 해서 최종 콘텍스트 벡터를 만들기 때문이다.
즉, Query는 디코더의 히든 스테이트 s2, Key와 Value는 인코더의 히든 스테이트 h1, h2, h3가 된다.
Self Attention
바다나우 어텐션 구조는 디코더단에서 토큰을 만들어낼 때 인코더에서 본 토큰들 중에 어떤 것이 더 관련성이 있는지 찾아내는 과정이다. 그러나 트랜스포머에서는 인코더단에서부터 벌써 어텐션을 적용한다.
이건 무슨 의미가 있을까? take your time 보다 좀 더 긴 예제를 들어보자.
'오늘 길 가다가 지난 회사 사수님을 만났는데 그분 혈색이 좋아보이더라' 라는 문장을 생각해보자. 사람인 우리는 그분이라는 말이 회사와 지난을 가리키는 것이 아니라 사수님을 가리키는 것이라는 사실을 알 수 있다. 그러나 앞서 seq2seq 방식이라면 머신에게 이 사실을 어떻게 알려줘야할까? 혹은 모델이 이 관계를 파악하고 있는지 우리가 어떻게 알 수 있을까?
여기서 self attention의 개념이 사용된다. 만약 디코딩 할때뿐만 아니라 인코딩을 할때도 어텐션을 적용하면 어떻게 될까? 이렇게 되면 어텐션의 개념이 지금까지 이야기했던것과 조금 바뀔것이다.
이전에는 쿼리는 디코더에서 가져오고, 키는 인코더에서 가져오고, 두가지를 대조해서 지금 생성할 때 각 value가 어떤것이 가중치가 높은지를 생각했다면, 이번에는 인코더의 각 토큰을 다른 토큰들과 비교해보는 과정이 될것이다.
'지난', '회사', '사수님'과 같은 단어들의 key와'그분' 이라는 쿼리를 가지고 비교하는 셈이다. 이렇게 비교하면 '그분'이라는 토큰이 '사수님'과 깊은 관계를 맺고 있다는 사실을 알 수 있을 가능성이 좀 더 커질 수 있다.
이런식으로 주어진 텍스트의 토큰들 안에서 어텐션을 적용하는 것을 Self Attention이라고 한다.
Self-Attention은 문장 내에서 각 토큰이 다른 토큰들과 얼마나 연관성이 있는지를 계산하는 메커니즘이다. 이를 통해 모델은 문장 내에서 단어들 간의 관계를 파악할 수 있다. Self-Attention은 인코더와 디코더 모두에서 사용되며, 각 토큰의 중요도를 계산하여 다음 단어를 예측하는 데 활용된다.
그렇다면 트랜스포머에서 사용하는 key, query, value는 무엇이 될까?
트랜스포머 어텐션
'take your time'이라는 input값과 각각 상응하는 임베딩이 있다고 가정해보자.
닷 프로덕트 어텐션에서는 key, value 각각의 메트릭스를 하나씩 할당한다. 각 단어에 대한 query, key, value는 인코더 레이어에 들어온 임베딩을 이 매트릭스들에 곱해서 구할 수 있다.
임베딩이 wq 행렬에 곱해진다면 query, wk 행렬에 곱해진다면 key, wv 행렬에 곱해진다면 value가 된다. 이 매트릭스들은 다른 학습 가능한 파라미터들과 마찬가지로 학습이 진행되면서 값이 업데이트 된다.
값이 구해졌다면 이제 닷 프로덕트를 통해서 쿼리와 각 아이템들 간의 연관선을 구하고 그 가중치를 이용해서 value를 가중합 할 수 있다.
그런데 트랜스포머 논문을 보다 보면 '멀티헤드' 라는 말을 볼 수 있다. 여기서 설명할때는 하나의 어텐션에 대한 작동에 대해서만 말한건데, 사실 트랜스포머는 한 번의 과정에 여러개의 어텐션을 사용한다.
하나의 어텐션만 사용하는 경우는 해당 어텐션이 잘못된 결과를 내게 되면 그대로 그 잘못된 결과를 따라하게 된다. 대신 여러개의 어텐션을 함께 사용하고 결과를 같이 사용한다면 이런 리스크를 조금 줄일 수 있다.
멀티헤드 어텐션에서는 아래 중앙에 있는 그림처럼 각 어텐션 헤드에서 나온 결과물을 concat해서 최종적으로 사용하게 된다.
7. 트랜스포머 어텐션 - 디코더
왼쪽 도표의 디코더 구성을 보면 어텐션이 두 개가 들어가있는걸 볼 수 있다.
- Masked Multi-Head Attention
- Encoder-Decoder Multi-Head Attention
두 가지 어텐션을 살짝 다른데, 디코더 블록의 첫 컴포넌트는 Masked Multi-Head Attention이다.
Masked Multi-Head Attention
Masked Multi-Head Attention는 self attention이라는 점에서는 앞서 봤던 인코더의 멀티헤드 어텐션과 동일하다. 여기에서는 디코더에서 생성하는 토큰들을 가지고 셀프 어텐션을 한번 취하게 된다. 그런데 아직 생성하지도 않은 토큰을 참고하면 안된다! 치팅이 되어서 학습이 제대로 되지 않을것이다. (치팅이란 모델이 학습 과정에서 미래의 정보를 사용하는 것을 의미하며, 이는 모델의 일반화 능력을 저해한다.)
이런 문제를 해결하기 위해 디코더의 셀프 어텐션에서는 현재 시점상 아직 생성되지 않은 시점의 토큰들을 마이너스 인피니티로 마스킹해준다. 마이너스에 무한대 값을 채어 넣어서 토큰들을 참조할 수 없게 하는것이다. 이외의 작동 방식은 인코더의 셀프 어텐션과 동일하다.
Encoder-Decoder Multi-Head Attention
바로 다음 컴포넌트인 인코더 디코더 멀티헤드 어텐션에서 아까 인코더 블록에서의 결과물을 사용한다. 여기서의 사용 방식은 위에서 설명했던 바다나우 어텐션의 query, key, value 관점으로 표현했을때와 비슷하다. query는 이전에 masked multi-head attention에서 나온 결과물을, key, value는 인코더의 output embedding을 출처로 해서 닷 프로덕트 어텐션을 계산하게 된다.
즉, 인코더의 출력과 디코더의 출력을 결합하여 최종적인 출력을 생성한다. 이를 통해 입력 시퀀스와 생성 중인 시퀀스의 맥락을 모두 반영할 수 있다.
8. 결론
이렇게 RAG와 요즘 NLP의 핵심이 되는 트랜스포머 어탠션을 알아봤다. 사실 작성하면서 조금 어려운 내용이라 한번에 이해되진 않는데 그래도 계속 읽으면서 이해하려고 노력하고 있다.
이제 다음글에서는 이 트랜스포머 어텐션을 이용한 모델인 Bert와 GPT를 살펴보자
'RAG' 카테고리의 다른 글
[RAG] RAG와 Fine-Tuning 차이점과 Small Language Models (SLM) (2) | 2024.08.30 |
---|---|
[RAG] RAG 파이프라인 (1) | 2024.08.30 |
Bert와 GPT 차이점 (1) | 2024.08.30 |
페르소나를 이용한 챗봇 (2) - chat memory 추가 (0) | 2024.08.13 |
페르소나를 이용한 챗봇 (1) - 셜록 홈즈 데이터 준비 및 검색 엔진 설정 (0) | 2024.08.13 |