- GPT-2와 비슷한 디코더 전용 트랜스포머를 학습 없이 직접 가중치만 설계해
(aab)*패턴을 예측하게 만든 실험임 "aabaabaabaab..."예측은 직전 두 토큰을 봐야 하므로, 단순 교대 패턴보다 어텐션 동작을 드러내기 좋음- 모델은
N_CTX=5,N_VOCAB=2,N_EMBED=8로 작게 구성하고,a=0,b=1토큰화와 원핫 임베딩을 사용함 - 단일 어텐션 헤드는 최근 두 토큰에 0.5씩 주의를 배분하고,
a=1,b=-1인코딩의 상쇄를 이용해 다음 토큰을 계산함 - 모호하지 않은 문맥에서는 100.0% (27/27) 정확도를 보였지만, 5토큰 문맥 한 번 예측에 약 4,000 FLOPs가 필요해 직접 구현한 규칙보다 훨씬 비효율적임
학습 없이 가중치를 직접 지정한 미니 GPT-2
- 목표는 트랜스포머와 어텐션의 각 구성요소가 실제로 무엇을 하는지 직관적으로 이해하는 것임
- 모델은 학습하지 않고 사전학습 가중치도 쓰지 않으며, 저녁 한 번에 각 가중치를 직접 지정하는 방식으로 만들어짐
- 구조는 GPT-2와 비슷한 디코더 전용 트랜스포머이며, 구현은 jaymody의 picoGPT 구현을 바탕으로 단순화됨
- layer norm 제거
- multi-head attention 대신 단일 헤드 사용
- transformer block의
mlpfeed-forward layer 제거
(aab)* 시퀀스를 고른 이유
- 처음에는
"ababababab"같은 시퀀스를 예측하려 했지만, transformer가 shifted sequence를 예측하기 때문에 너무 쉬운 작업이 됨a이면b, 아니면a를 예측하면 되어 위치 임베딩을 쓸 필요가 없음
- 최종 작업은
"aabaabaabaab...", 즉(aab)*시퀀스 예측으로 정해짐- 이전 두 토큰이
ab또는ba이면 다음 토큰은a - 이전 두 토큰이
aa이면 다음 토큰은b bb는 작업 범위 밖의 경우로 취급됨
- 이전 두 토큰이
- 토큰화는 두 기호만 다루는 단순한 방식임
a는0b는1
모델 차원과 계산 흐름
- 선택한 모델 파라미터는 세 가지임
N_CTX = 5: 모델이 한 번에 보는 최대 문맥 길이N_VOCAB = 2:a,b두 토큰N_EMBED = 8: 토큰·위치·계산용 공간을 담는 임베딩 크기
- 실제 작업은 이전 두 토큰만 필요하지만,
N_CTX=5로 잡아 관련 없는 토큰을 무시해야 하는 상황을 포함함 gpt함수는 다음 순서로 동작함wte[inputs] + wpe[range(len(inputs))]로 토큰 임베딩과 위치 임베딩을 더함- 하나의 transformer block을 통과함
- 마지막에
x @ wte.T로 어휘 공간의 logits를 만듦
위치와 토큰을 원핫으로 담는 임베딩
wpe는 위치 임베딩이며, 앞의 5개 임베딩 차원을 위치 원핫으로 사용함- position 0은
[1, 0, 0, 0, 0] - position 4는
[0, 0, 0, 0, 1]
- position 0은
wte는 토큰 임베딩이며, 다음 2개 차원을 토큰 원핫으로 사용함- token
a는 해당 토큰 차원에서[1, 0] - token
b는[0, 1]
- token
- 8번째 임베딩 위치는 처음에는 쓰지 않고, transformer block 안에서 scratch space로 사용함
- 예를 들어
"aabaa"는5 x 8임베딩 행렬로 표현되며, 각 행은 위치 원핫과 토큰 원핫을 함께 포함함
어텐션 헤드가 최근 두 토큰을 고르는 방식
- transformer block은 하나의 attention head와 attention 결과를 다시 임베딩 공간으로 돌리는
c_proj선형층으로 구성됨 c_attn은embed_size x (embed_size * 3)크기의 선형층이며, 입력 임베딩을qkv행렬로 바꾼 뒤q,k,v로 나눔k는 위치 임베딩을 분리해 각 토큰이 가진 위치 정보를 나타냄q는 각 위치가 찾고 싶은 위치 범위를 나타내며,q @ k.T를 통해 attention score 행렬을 만듦- softmax와 causal mask 이후 attention 행렬은 다음 성질을 가짐
- 첫 행은 첫 토큰에만 100% attention
- 이후 행들은 접근 가능한 최신 두 토큰에 각각
0.5씩 attention
- causal mask는 미래 토큰 위치에 매우 작은 값, 실제 코드에서는
-1e10같은 값을 더해 미래 토큰을 보지 못하게 함- 이 손으로 만든 모델은 미래를 훔쳐보도록 설계되지 않았지만, GPT-2 구조에 가깝게 유지하기 위해 mask를 남김
np.sqrt(q.shape[-1])로 나누는 scaling은 실제 학습에서 기울기 개선에 도움이 되지만, 이 수제 모델에는 영향을 주지 않음
v 인코딩과 더하기 상쇄로 만든 예측
v는 토큰 원핫을a=1,b=-1인코딩으로 바꿈- attention 결과가 최근 두 토큰을 0.5씩 평균내기 때문에, 이 인코딩은 다음 규칙을 계산함
a, b→0.5 * 1 + 0.5 * (-1) = 0b, a→0.5 * (-1) + 0.5 * 1 = 0a, a→0.5 * 1 + 0.5 * 1 = 1
- 결과적으로 행의 7번째 위치에는 다음 값이 생김
0이면a를 예측해야 하는 경우1이면b를 예측해야 하는 경우
- 입력
"aabaa"에서 첫 행은 정보가 부족해b예측이 생길 수 있지만, 이후 예측은(aab)*규칙과 맞음
예측값을 다시 어휘 공간으로 보내기
c_proj는 attention 결과의 7번째 위치 값을 토큰 원핫 형식으로 되돌림- 단순히
[..., 1, 0, ...]또는[..., 0, 1, ...]를 만들지 않고,1024로 스케일된 원핫을 만듦embedding[row, 5] = 1024 + (-1024) * predictionembedding[row, 6] = 0 + 1024 * prediction
- transformer block에는 residual connection이 있어
x = x + causal_self_attention(...)로 원래 임베딩이 더해짐 - residual signal이 불필요하게 남기 때문에,
1024스케일을 사용해 이 신호를 압도함 - 마지막으로
x @ wte.T를 계산해 logits를 만들고 softmax를 적용함"aabaa"문맥에서는 최종 예측 행이b를 가리킴- 학습 때는 모든 행의 예측이 유용하지만, 추론에서는 마지막 행만 필요함
생성 결과와 정확도
complete함수는 마지막 최대 5토큰을 모델에 넣고, softmax 결과의 마지막 행에서argmax로 다음 토큰을 선택함- 예시 생성 결과는 다음과 같음
complete("a")→a :: baabaabaabcomplete("ba")→ba :: abaabaabaacomplete("abaab")→abaab :: aabaabaaba
- 범위 밖 입력에서도 반복 패턴으로 회복하는 경우가 있음
complete("ababa")→ababa :: abaabaabaacomplete("bbbbb")→bbbbb :: aabaabaaba
"aab" * 10테스트에서 모호하지 않은 문맥만 평가하면 정확도는 100.0% (27/27) 임
4,000 FLOPs와 8개 명령의 차이
- 5토큰 문맥 전체를 사용할 때, 이 모델은 단일 토큰 예측에 약 4,000 floating point operations가 필요함
- 대부분은 attention 계산에서 사용됨
- context window 축소, fused multiply-add, kv caching 등으로 줄일 수 있음
- 그래도 단일 토큰 예측에는 수백 개의 머신 명령이 필요함
- 같은
(aab)*규칙을 직접 작성한 x64 어셈블리는 8개 명령으로 다음 토큰을 계산함 - 자연어 생성에서 현재 모델보다 1000배 더 효율적인 언어 모델을 학습할 수 있는지에 대한 질문이 남음