왜 LLM은 내 지시를 따르지 않았을까 - 위치의 중요성
지시가 잘 반영되지 않고 위치가 영향을 준 이유 (feat. Attention Dilution과 Position Bias)
개요
LLM을 활용하여 면접 질문을 생성하는 과정에서 프롬프트에 명확하게 지시했는데도 모델이 다른 기준으로 답을 생성하는 문제를 겪었다.
문제 상황
프롬프트는 두 가지 입력을 함께 사용하여 질문을 생성하도록 설계하였다.
- 핵심 키워드 (질문의 기준이 되는 정보)
- 자기소개 (참조용 컨텍스트)
자기소개는 참고만 하고, 키워드를 기반으로 질문을 생성해라.
하지만 실제 결과는 달랐다. 모델은 키워드보다 자기소개 내용을 중심으로 질문을 생성하였다.
더 강하게 지시해보자
처음에는 단순히 프롬프트를 잘못 작성했다고 생각했다. 그래서 지시를 더 명확하게 쓰고, 표현도 여러 번 수정했다.
하지만 결과는 크게 달라지지 않았다. 여전히 키워드보다 자기소개를 중심으로 질문이 생성되었다.
분명히 키워드 기반으로 생성하라고 했는데, 모델은 자기소개를 기준으로 삼고 있었다.
그래서 지시를 더 추가하기보다는, 다시 처음부터 작성해보았다. 그 과정에서 입력 순서를 바꾸게 되었고, 동일한 정보임에도 결과가 달라지는 것을 확인하였다.
이후 관련 내용을 찾아보면서, LLM은 입력의 내용뿐 아니라 위치에도 영향을 받는다는 것을 알게 되었다.
내 마음대로 LLM 파헤치기
처음에는 프롬프트를 잘못 작성한 내 잘못이라고 생각했다. 하지만 아무리 규칙을 추가해도 해결되지 않았던 것이 위치 하나 바꾸었다고 해결되는 것을 보고 모델의 동작 방식을 이해해야겠다고 생각했다.
LLM이란
위키피디아와 구글에서는 LLM을 다음과 같이 정의하고 있다.
A language model is a computational model that predicts sequences in natural language.
[위키피디아]
A language model estimates the probability of a token or sequence of tokens occuring within a sequence.
[Google Machine Learning]
여기서 한 가지 의문이 생겼다.
Google 정의에 의하면 LLM은 주어진 문맥에서 다음에 올 토큰의 확률을 예측하는 모델이라고 한다.
다음에 오는 토큰을 결정하는 것이 아니었나?
LLM의 전체 흐름
알아보니 둘은 서로 다른 단계를 설명하는 표현이다.
LLM이 동작하는 흐름을 살펴보자.
1
2
3
4
5
6
7
Text(Input)
-> Tokenization
-> Embedding
-> Transformer Layer: 다음 토큰 예측 🔮
-> Softmax: 다음 토큰에 대한 확률 분포로 변환
-> Decoding: 다음 토큰 선택 ✅
-> Detokenizer: 선택된 토큰을 텍스트로 변환
Text- ex) 돼지는 케이크를 먹었다.
Tokenization: 모델이 알아듣기 쉬운 토큰 단위로 쪼개고, 각 토큰에 고유한 ID를 부여하는 과정- ex) 돼지는 / 케이크를 / 먹었다. → (각각 고유한 ID 부여)
Embedding: 토큰 ID를 고차원 벡터로 변환하는 과정- ex)
돼지_UNIQUE_ID→ [0.13, -0.72, …]
- ex)
Transformer: 여러 겹의 Layer로 이루어져 있으며, Attention 메커니즘을 통해 단어 간의 관계를 반영한 문맥 표현을 만든다. 이 표현을 바탕으로 다음 토큰에 대한 점수(Logits)가 계산된다.Softmax: 모델의 결과(Logits)를 확률 분포로 변환Decoding: Greedy, Sampling, Top-K 등의 알고리즘으로 다음에 올 토큰을 선택Detokenizer: 사람이 이해할 수 있는 텍스트로 변환
LLM은 주어진 문맥을 바탕으로 다음에 올 수 있는 여러 토큰들의 확률 분포를 계산하고, 생성 단계에서는 그 확률 분포를 바탕으로 하나의 토큰을 선택한다.
이 과정은 새롭게 생성된 토큰을 포함한 문맥을 다시 입력으로 사용하면서 반복된다.
Autoregressive Generation
이처럼 이전까지 생성된 토큰들을 다시 입력으로 사용하여 다음 토큰을 예측하는 방식을 Autoregressive Generation이라고 한다. 여기서 autoregressive는 과거로 되돌아간다는 의미가 아니라, 자기 자신(auto)의 이전 출력(regressive)에 기반하여 다음 값을 생성한다는 뜻이다.
즉 모델은 전체 문장을 한 번에 생성하는 것이 아니라, 현재까지의 문맥을 기반으로 다음 토큰을 하나씩 예측하며 문장을 만들어나간다.
예를 들어, 문장은 다음과 같은 방식으로 생성된다.
1
2
(입력) 돼지는 → 다음 토큰: 케이크를
(입력) 돼지는 케이크를 → 다음 토큰: 먹었다
API 요금제를 확인했을 때 출력 토큰 비용이 더 비싼 이유
입력 토큰은 한 번에 모델에 전달되어 한 번의 연산으로 처리된다.
반면 출력 토큰은 Autoregressive 방식으로 하나씩 순차적으로 생성된다. 이 과정에서 매번 전체 문맥을 다시 참고하여 확률을 계산해야 하기 때문에, 출력 토큰은 입력 토큰보다 더 많은 연산 비용을 요구하게 된다.
이러한 구조적인 특성 때문에 API에서는 일반적으로 출력 토큰 비용이 더 비싸게 책정된다.
Attention is All You Need
Transformer의 핵심 메커니즘인 Attention.
단어 뜻처럼 주의, 주목, 집중하는 것이다.
우리가 글을 읽을 때 문장 내 모든 단어들을 주의 깊게 보지 않고 중요한 몇몇 단어들 위주로 읽어내려가듯 모델도 중요한 단어들에 더 주목하겠다는 것이 핵심 아이디어다.
이전에는 RNN 기반의 Seq2Seq 모델이 널리 사용되었는데, 해당 구조는 입력을 순차적으로 처리해야 했기 때문에 계산 시간이 오래 걸렸다. 또한 긴 문장에서는 앞쪽 정보가 점차 희미해지거나 입력 전체를 하나의 벡터로 압축하는 과정에서 정보 손실이 발생하는 문제가 있었다.
Transformer는 이러한 문제를 해결하기 위해 입력을 하나로 압축하는 대신, 각 토큰이 다른 모든 토큰을 참조할 수 있는 구조로 바꾸었다.
Attention의 동작 방식
그럼 Transformer는 중요한 것을 어떻게 판별할 수 있을까?
자세히 설명하기 전에 알아두어야 할 개념이 있다.
- Query: 지금 이해하려는 대상
- Key: 비교 대상
- Value: Key에 해당하는 정보
Attention은 이 3가지 개념을 통해 설명될 수 있다.
Query를 기준으로 Key들과 비교하여 연관도를 파악하고,
이에 따라 Value를 반영하여 문맥에 맞는 새로운 표현을 만드는 과정
위 문장만 보면 무슨 이야기인지 모르겠다. 하나씩 뜯어보자.
(1) Query를 기준으로 Key들과 비교하여 연관도 파악하기
위에서 Embedding이라는 과정을 통해 토큰들을 벡터화한다고 하였다. 이렇게 구해진 벡터들은 내적을 통해 토큰 간의 연관도를 비교할 수 있고, 내적 값이 클수록 두 토큰이 더 밀접하게 관련되어 있다고 판단한다.
“돼지는 케이크를 먹었다.” 라는 문장에서 “먹었다”라는 단어를 이해한다고 해보자. 이 때 모델은 “먹었다”라는 Query를 기준으로 “돼지”, “케이크” 같은 다른 토큰 Key와 비교하여, 각 토큰이 얼마나 관련 있는지 계산한다.
1
2
(먹었다-돼지 내적 결과값) = 12
(먹었다-케이크 내적 결과값) = 28
(2) 연관도를 기반으로 Value를 반영하여 문맥에 맞는 새로운 표현 만들기
그래서 이 단어 간의 연관도를 계산해서 무엇을 하는걸까?
이 값은 그대로 사용되지 않고, 먼저 스케일을 조정한 후, softmax를 적용하여 합이 1이 되는 가중치 분포로 변환한다.
Softmax란 여러 값들을 합이 1이 되도록 정규화하여,
각 값이 얼마나 반영될지를 나타내는 비율로 바꾸어주는 함수다.
1
2
(먹었다와 돼지의 연관도) = 0.3
(먹었다와 케이크의 연관도) = 0.7
이제 0.3만큼의 돼지의 Value와 0.7만큼의 케이크의 Value를 반영하면 “먹었다”라는 단어를 문맥에 맞게 다시 표현한 벡터를 얻을 수 있다.
Attention은 단순히 중요한 토큰을 찾는 것이 아니라, 각 토큰이 다른 토큰의 정보를 얼마나 반영할지를 계산해서 현재 단어의 의미를 문맥에 맞게 다시 만드는 과정이다.
Softmax는 두 번 등장한다.
- Attention 가중치 계산할 때: 어떤 토큰 정보를 얼마나 반영할지 결정하는 가중치를 만들기 위해 사용
- Logits 변환할 때: 다음에 선택할 토큰의 확률 분포를 만들기 위해 사용
왜 중요한 정보가 반영되지 않은걸까
Attention을 활용한다면 중요한 정보를 충분히 반영할 수 있어야 한다. 하지만 실제로는 그렇지 않았다.
분명 더 중요한 키워드가 있었음에도 모델은 다른 정보를 더 강하게 반영했다. 왜 그런 결과가 나온 것일까?
(1) 왜 지시 사항 추가가 안먹혔을까?
Attention Dilution
Self Attention은 모든 토큰이 서로를 참고하며 중요도를 계산하고, 그 결과를 softmax로 하나의 확률 분포로 만든다. 즉 Attention은 본질적으로 전체 토큰 사이에서 합이 1인 가중치를 나누어 갖는 구조다.
문제는 입력이 길어질수록 발생한다.
토큰이 3-4개일 때는 특정 토큰에 높은 가중치를 줄 수 있다. 하지만 토큰이 수천개 이상으로 늘어나면 가중치를 분배해야 할 토큰들이 많아져, 개별 토큰의 영향력이 상대적으로 약해지게 된다.
이러한 현상을 Attention Dilution(희석)이라고 한다.
즉 지시를 더 추가할수록 모델이 더 잘 따를 것 같지만, 실제로는 각 지시가 차지하는 비중이 줄어들면서, 결과적으로 특정 지시가 잘 반영되지 않는 상황이 발생할 수 있다.
토큰이 많더라도 중요한 것에만 높은 weight를 줄 수 없을까?
- Self Attention은 특정 토큰만 선택하는 구조가 아니라, 모든 토큰 간의 관계를 전부 비교하는 구조다.
- Transformer는 여러 Layer를 통해 입력을 반복적으로 재해석하고, 이전 Layer에서 재해석된 표현을 다시 attention한다.
이 과정에서 중요하지 않은 정보도 계속 포함되어 계산되기 때문에, Layer를 거칠수록 해당 작업이 반복되어 특정 정보만 극단적으로 강조되기보다 전체적으로 평균화되는 경향이 나타날 수 있다.
(2) 왜 위치를 바꾸니까 동작했을까?
Position Bias
여러 연구에 따르면 Transformer에서는 토큰의 위치에 따라 가중치가 고르게 분배되지 않는 position bias가 존재한다. 모델은 모든 토큰을 참조하지만, 실제로는 특정 위치의 토큰에 높은 가중치를 부여하는 경향이 있다고 한다. 또한 뒤쪽에 위치한 토큰일수록 더 높은 가중치가 부여되는 recency bias가 관찰된다고 한다.
이는 동일한 정보라도 위치에 따라 모델이 반영하는 정도가 달라질 수 있음을 의미한다.
모델은 어떻게 위치를 아는거지?
다음 두 문장은 단어는 같지만 의미는 다르다.
- 돼지가 케이크를 먹었다.
- 케이크를 돼지가 먹었다.
따라서 분리된 토큰들은 각자 자신의 순서를 기억할 필요가 있다.
Transformer는 RNN처럼 순차적으로 입력을 처리하지 않기 때문에 위치에 대한 정보를 따로 기록해야 한다. 이를 위해 등장한 것이 Positional Encoding이다.여기서 중요한 포인트는 벡터에는 의미뿐 아니라, 위치 정보도 함께 들어간다는 것이다. 그래서 같은 단어라도 위치가 다르면 완전히 다른 벡터로 취급된다.
즉 모델은 내용뿐만 아니라, 정보의 위치 또한 함께 인식한다.
Lost in the Middle
이러한 현상은 LLM에서 알려진 Lost in the Middle 문제와도 연결된다.
입력이 길어질수록 모델은 모든 정보를 동일하게 활용하지 못하고, 특히 중간에 위치한 정보가 앞이나 뒤에 있는 정보보다 상대적으로 덜 반영되는 경향을 보인다고 한다.
즉 모델은 입력 전체를 참고하지만, 위치에 따라 정보의 영향력이 달라지는 구조적인 특성을 가진다고 볼 수 있다.
결론
- Transformer의 핵심 엔진인 Attention은 중요한 정보를 더 잘 반영할 수 있도록 설계되어 있다.
- 하지만 실제로는 입력의 길이가 길어질수록 정보의 영향력이 희석되거나, 위치에 따라 특정 정보가 더 강하게 반영되는 현상이 나타난다.
- 즉 모델은 모든 정보를 동일하게 처리하는 것이 아니라, 입력의 양과 위치에 따라 서로 다른 방식으로 반영한다.
- 이러한 특성으로 인해 단순히 지시를 더 추가하는 것보다 어떤 정보를 어디에 배치하느냐가 더 큰 영향을 줄 수도 있다.
나에게 LLM은 여전히 명확하게 설명하기 어려운 영역이다. 원하는 결과를 도출하기 위해 프롬프트를 고치는 과정에서 계속해서 ‘왜?’라는 생각이 머릿속에 남았다. 정확한 이유를 알 수 없다는 점이 너무 답답해서, 결국 LLM에 대해 조금 학습해보게 되었다.
그래서 이번 글에서는 생략된 내용들도 많다. Self Attention, Multi Head Attention, Mask 같은 개념들도 제대로 다루지 못했다. 이후에 기회가 된다면, 부족했던 부분들을 더 깊게 학습해보고 싶다.