AI 공부

8. print(model) 한 줄로 Transformer 구조 이해해보기 (feat. Llama-3.2-1B)

kdb1248 2026. 3. 29. 17:00

목차 

1. print(model) 한 줄로 Transformer 구조 이해해 보기 (feat. Llama-3.2-1B)

- 0) 전체 흐름 요약

- 1) Embedding Layer

- 2) Decoder Layers (총 16개)

- 3) Final RMSNorm

- 4) LM Head (출력층)

- 5) 전체 흐름 재정리

- 6) 느낀점 

 

1. print(model) 한 줄로 Transformer 구조 이해해 보기 (feat. Llama-3.2-1B)

Llama-3.2-1B 모델을 print(model) 했을 때 나오는 내부 레이어 구조를 출력해본 뒤,

출력 결과물을 통해 Transformer 구조 속 요소를 하나하나 가볍게 뜯어볼 수 있었다. 

이렇게 하나하나 뜯어본 요소들에 대해 적어본다.

Llama-1B 모델을 print(model)한 것

 

0) 전체 흐름 요약 

모델에 입력이 들어와 출력이 나오기까지의 전체 흐름을 요약하면 아래와 같다.

- 입력 토큰 → Embedding → 16개의 Transformer decoder → 최종 Norm →   다음 토큰 확률 계산 및 출력

구성요소 설명
Embedding(128256×2048) 토큰ID를 벡터화
16개 Decoder Layer Llama 1B의 핵심
Self-Attention 문맥 관계 학습
MLP (2048→8192→2048) 의미 패턴 확장·압축
RMSNorm 안정화
Rotary Embedding 위치 정보 처리
LM Head(2048→128256) 다음 토큰 확률 계산

1) Embedding Layer

(embed_tokens): Embedding(128256, 2048)

✔ 역할

토큰 ID를 2048차원의 벡터로 변환.

✔ 의미

  • 128256 → vocab size (토큰 개수)
  • 2048 → embedding size (모델 hidden dimension)

즉,

토큰 12345 → [0.03, -0.12, ..., 0.05]  # 2048개 숫자

이렇게 변환한 후 Transformer 레이어로 전달됨.

(2048은 각 토큰을 표현하는 벡터 길이. 즉, 토큰 하나 = 2048차원 좌표)

 

 

2) Decoder Layers (총 16개)

(layers): ModuleList(
  (0): LlamaDecoderLayer(
  (1): LlamaDecoderLayer(
  ...
  (15): LlamaDecoderLayer(

LLama-1B는 총 16개의 Transformer Decoder Layer로 구성돼 있음

각 Layer 내부엔 아래 설명되는 4가지 블록이 있음.

 

 

각 LlamaDecoderLayer는 아래 구성:

 2)- ① Self-Attention

1. 전체 구조

(self_attn): LlamaAttention(
  (q_proj): Linear(2048 → 2048)
  (k_proj): Linear(2048 → 512)
  (v_proj): Linear(2048 → 512)
  (o_proj): Linear(2048 → 2048)
)

✔ 역할

Attention(Q·K·V)를 계산해

“현재 단어가 문맥의 어떤 단어를 얼마나 참고해야 하는지” 산출.

( 입력 벡터(2048차원)를 → Q, K, V로 바꿔서 Attention 계산 → 다시 2048로 합침)

 

✔ 특징

  • Query는 2048 → 2048
  • Key/Value는 2048 → 512 (압축됨 → multi-head 구조 때문)
  • Output Projection은 다시 2048차원으로 복구

 

2. self-attention의 의미

“이 단어를 이해할 때, 다른 단어들을 얼마나 참고해야 하지?”

예:

"I ate an apple because it was sweet"

여기서 "it"은 누구?

=>  "apple"을 강하게 참고해야 함

 

3. Q,K,V 직관 

요소 역할
Q (Query) “나는 누구를 참고해야 하지?”
K (Key) “나는 어떤 특징을 가지고 있어”
V (Value) “내가 실제로 전달할 정보”

 

4. 실제 계산 흐름

1) Q,K,V 만들기

입력 (각 토큰 벡터 2048차원)

↓

Q = q_proj(x)  → 2048
K = k_proj(x)  → 512
V = v_proj(x)  → 512

2) 유사도 계산

이게 Attention의 본질
- Attention Score = Q · K

의미:
- “이 단어(Q)가 저 단어(K)랑 얼마나 관련 있지?”

3) 가중치 만들기

Softmax(QKᵀ)

=> 확률처럼 변환

합 = 1
어떤 단어를 얼마나 참고할지 결정

4) 정보 가져오기

가중치 × V

=> 중요한 단어 정보는 많이 가져오고
=> 덜 중요한 단어는 거의 무시

 

5. 왜 K,V는 512차원으로 줄였을까?

✔ 이유: Multi-Head Attention 때문

예를 들어:

hidden dim = 2048
head 수 = 4
그러면:

2048 / 4 = 512

=> 각 head는 512차원씩 담당

✔ 직관

=> 하나의 Attention이 아니라

여러 개의 Attention이 동시에 다른 관점으로 문장을 해석

예:

head 1 → 문법 관계
head 2 → 의미 관계
head 3 → 위치 관계

그래서:

✔ Q( “나는 누구를 참고해야 하지?” )는 전체 표현 유지 (2048)
✔ K( “나는 어떤 특징을 가지고 있어” ), V( “내가 실제로 전달할 정보” )는 head 단위로 쪼개서 계산 (512)

 

6. o_proj (출력 projection)

Attention 끝나면:

여러 head 결과를 합침 (concat)
그 결과:

=> 다시 2048로 맞춰야 다음 레이어로 전달 가능

o_proj: 2048 → 2048

역할:

Attention 결과를 모델의 기본 차원으로 정리

7. 전체 흐름 요약

Self-Attention =
“각 단어가 문장 안의 다른 단어들을 가중치 기반으로 참고해서 의미를 업데이트하는 과정”

입력 (2048)
 → Q/K/V 생성
 → Q·K로 중요도 계산
 → V에서 정보 가져옴
 → 여러 head 합침
 → o_proj로 2048 복구

2)-② MLP (Feedforward 네트워크)

1. 전체 구조

(mlp): LlamaMLP(
  (gate_proj): Linear(2048 → 8192)
  (up_proj):   Linear(2048 → 8192)
  (down_proj): Linear(8192 → 2048)
)

✔ 역할

Self-Attention에서 얻은 정보를 “비선형 변환”으로 확장·압축하여

더 복잡한 패턴을 학습함. (차원을 크게 늘렸다가(확장) → 비선형 처리 → 다시 줄이는(압축) 구조)

✔ 특징

  • hidden dimension이 2048 → 8192로 확장됨
  • SiLU 같은 활성함수 사용
  • 모델의 파라미터 대부분이 여기에 집중됨

MLP는 언어모델에서 문장 구조, 의미적 일관성, 패턴 학습 등을 담당.

 

2. 왜 MLP가 필요한가?

Self-Attention만 있으면:

=> “어떤 단어를 참고할지”만 결정함
=> 하지만 그 정보를 어떻게 해석할지는 못함

그래서 MLP가 등장:

✔ 역할
=> Attention으로 모은 정보를 변형해서 의미를 만들어냄

예:

“apple + sweet + ate”
→ 단순 연결이 아니라
→ “먹은 대상이 사과고, 맛은 달다” 같은 구조 이해

 

3. 내부 동작(Llama 스타일 핵심)

LLaMA는 일반 MLP보다 한 단계 더 있음 

Step 1: 두 개의 projection
x → gate_proj → 8192
x → up_proj   → 8192

=> 같은 입력을 두 갈래로 보냄

Step 2: gating (핵심 포인트)
SiLU(gate_proj(x)) * up_proj(x)

✔ 직관
gate_proj → “이 정보 얼마나 쓸지 결정”
up_proj → “실제 정보”

즉:
정보 × 중요도 필터

Step 3: 다시 축소
→ down_proj (8192 → 2048)

다시 원래 차원으로 압축해서 다음 레이어로 전달

 

4. 왜 2048 → 8192로 늘릴까?

✔ 이유: 표현력 증가

2048 공간에서는:
표현할 수 있는 패턴 제한됨

8192로 확장하면:
더 복잡한 조합 가능

예:

단순 단어 관계 → 문장 구조
단어 의미 → 문맥 의미

한 줄 핵심:
차원을 늘리면 “생각할 공간”이 커진다

5. 왜 비선형(SiLU)이 필요할까?

만약 Linear만 쓰면:
전체가 그냥 하나의 선형 변환이 됨

→ 아무리 깊어도 표현력 제한됨

그래서:

SiLU(x) = x * sigmoid(x)
비선형 추가 → 복잡한 패턴 학습 가능

 

6. 정리 

MLP는 Attention으로 모은 정보를 고차원 공간에서 비선형적으로 변환해 복잡한 의미 패턴을 학습하는 부분이다

구분 역할
Self-Attention 단어들끼리 관계 파악
MLP 그 관계를 해석해서 의미 생성

2)-③ LayerNorm (정규화)

1. 전체 구조

(input_layernorm)
(post_attention_layernorm)

LLama 계열은 RMSNorm을 사용.

✔ 역할

  • 학습 안정화
  • gradient 폭주 방지
  • Attention과 MLP 입력을 정규화해 학습 효율 개선

2. 뭐 하는 애냐?

입력 벡터를 “적당한 범위로 맞춰주는 정리 단계”

  • 값이 너무 크거나 작지 않게 조정
  • 분포를 안정적으로 유지

3. 왜 필요하냐?

학습하다 보면:

  • 값이 계속 커짐 → gradient 폭주
  • 값이 너무 작아짐 → gradient 소실

 그래서 학습이 불안정해짐

 

✔ 해결: 정규화로 값의 스케일을 일정하게 유지

 

4. LLaMA는 RMSNorm 사용

일반 LayerNorm 대신: RMSNorm (Root Mean Square Norm)

차이 (직관)

LayerNorm → 평균 + 분산 둘 다 사용
RMSNorm → 평균 없이 크기(스케일)만 맞춤

장점:

계산 더 단순
속도 빠름
성능 거의 동일 (LLM에서 검증됨)

5. 어디에 쓰이냐?

(input_layernorm)
→ Attention 전에

(post_attention_layernorm)
→ MLP 전에

즉:
각 블록 들어가기 전에 항상 정리하고 시작

6. 한줄 요약

Norm은 각 레이어 입력을 안정적인 분포로 맞춰서 학습이 잘 되도록 만드는 장치

 

2)-④ Rotary Embedding (Rotary Position Embedding)

1. 전체 구조

(rotary_emb): LlamaRotaryEmbedding

Rope는 위치 정보를 각 Attention head에 자연스럽게 녹여주는 방식이다.

Transformer는 원래 positional encoding이 필요하기 때문에,

LLama는 sinusoidal 기반 RoPE를 사용해 문장 순서 정보 학습.

 

2. 왜 필요하냐?

Transformer는 기본적으로:
단어 순서를 모름

예:
“I love you”
“you love I”

=> 같은 입력으로 봄 (순서 개념 없음)

그래서 필요:
Positional Encoding (위치 정보)

3. 핵심 아이디어

기존 방식:
위치 벡터를 더하기 (+)

RoPE 방식:
벡터를 “회전(rotation)”시켜서 위치 정보 반영

✔ 직관
단어 벡터를 각 위치마다 조금씩 “회전”시킴
그러면:
위치가 다르면 벡터 방향이 달라짐

✔ 왜 좋은가?
1. 상대적 위치 학습 가능
“앞 단어 vs 뒤 단어” 관계 잘 잡음
2. 길이 일반화 잘됨
긴 문장에서도 성능 유지
3. Attention에 자연스럽게 녹음
Q, K에 직접 적용됨

4. 어디에 적용되나?

Attention 안에서:
Q, K → RoPE 적용 → Attention 계산

 

5. 한줄 요약

RoPE는 토큰 벡터를 회전시켜 위치 정보를 Attention 계산에 직접 반영하는 방식

3) Final RMSNorm

1. 전체 구조

(norm): LlamaRMSNorm(2048)

전체 Transformer Block이 끝난 뒤

최종적으로 한 번 더 레이어 정규화를 수행.

 

2. 뭐 하는 애냐?

=> 모든 Transformer 블록을 지난 후, 마지막으로 한 번 더 정규화

 

3. 왜 마지막에 또 하냐?

여러 레이어를 거치면서:

  • 값 스케일이 다시 흔들릴 수 있음
  • 분포가 약간씩 틀어짐

=> 그래서 마지막에 한 번 더 정리

 

4. 역할

  • 출력 벡터를 안정적인 범위로 맞춤
  • 다음 단계(예: LM Head)에 전달하기 전에 품질 정리

5. 어디에 쓰이냐

Embedding
→ [Attention + MLP] × N
→ Final RMSNorm ← 여기
→ LM Head (출력)
 

6. 한 줄 핵심

Final RMSNorm은 전체 레이어를 통과한 출력 표현을 안정화해 최종 예측 품질을 높이는 역할

 

7. 간단 비유

  • 중간 Norm = 각 단계마다 정리
  • Final Norm = 최종 출고 전에 검수

4) LM Head (출력층)

1. 전체 구조

(lm_head): Linear(in_features=2048, out_features=128256)

✔ 역할

Transformer의 마지막 hidden state(2048차원)를

단어 확률 분포(vocab size=128256)로 변환.

즉 “다음 토큰 예측”을 담당하는 최종 레이어.

예:

입력 hidden vector → { "the": 0.05, "cat": 0.02, "is": 0.13, ... }

 

2. 뭐 하는 애냐?

: 마지막 hidden vector(2048차원)를 → 모든 단어에 대한 점수로 변환

3. 어떻게 동작하냐

Transformer를 다 거치면:

[0.12, -0.98, ..., 0.33] (2048차원)

이 벡터가 나옴

↓

LM Head 통과

↓

[단어1 점수, 단어2 점수, ..., 단어128256 점수]

vocab 크기만큼 점수 생성

---------------------
그다음
이 점수들을 그대로 쓰는 게 아니라:

Softmax → 확률로 변환

예:

"the": 0.05
"cat": 0.02
"is": 0.13
...
=> 다음에 나올 단어 확률 분포

 

 

4. 역할 한 줄 정리

: 모델의 내부 표현을 “다음 단어 확률”로 바꿔주는 최종 변환기

5. 왜 vocab size로 나오냐?

=> 모델의 목표가 이것이기 때문:

P(다음 단어 | 지금까지 문장)
 

그래서:

가능한 모든 토큰(128256개)에 대해 확률 계산

5) 전체 흐름 재정리

구성요소 설명
Embedding(128256×2048) 토큰ID를 벡터화
16개 Decoder Layer Llama 1B의 핵심
Self-Attention 문맥 관계 학습
MLP (2048→8192→2048) 의미 패턴 확장·압축
RMSNorm 안정화
Rotary Embedding 위치 정보 처리
LM Head(2048→128256) 다음 토큰 확률 계산

 

 

위 사진 처럼 나올 수 있는 다음 토큰의 확률들을 게산하는게 최종 결과라면, 이를 위한 중간과정이 지금까지 적은 프로세스 처럼 이뤄진다고 볼 수 있다.

6) 느낀점

대략적으로 알고 있던 트랜스포머 구조를, 실제 모델을 기준으로 각 레이어 단위까지 뜯어보며 이해해 봤다는 점에서 의미가 있었던 시간이었다.

특히 단순히 “다음 토큰을 확률 기반으로 예측한다”라는 결과만 보는 것이 아니라, 이러한 결과가 나오기까지의 중간 과정을 직접 파고들어 봤다는 점이 인상적이었다.

Self-Attention을 통해 문장 내 단어 간 관계를 계산하고, RoPE를 통해 위치 정보를 반영하며, MLP를 통해 이를 비선형적으로 해석하는 과정이 실제 연산 구조로 어떻게 구현되는지를 확인할 수 있었다.

이를 통해 트랜스포머의 각 단계별 역할을 보다 명확히 체감할 수 있었고, 모델 구조를 스스로 읽어낼 수 있는 관점이 생겼다고 느꼈다.

이번에는 Llama-1B와 같은 비교적 작은 모델을 기준으로 살펴봤지만, 대부분의 LLM이 동일한 트랜스포머 구조를 기반으로 한다는 점에서, 이번 경험이 이후 다양한 모델을 이해하고 비교하는 데에도 기반이 될 수 있을 것이라 생각한다.

또한 이번에는 전체적인 프로세스를 중심으로 살펴봤다면, 이후에는 각 단계별 연산이나 내부 로직까지 더 깊게 파고들어 보는 것도 의미가 있을 것이라 생각했다.


 

(꾸준히 공부하고, 적을 테니 많은 관심 부탁드립니다.)

 

Profile:

Linkedin