DevOps··2분 읽기·6

4. FFN: 지식이 저장된 곳

LLM의 FFN이 어텐션과 어떻게 역할을 나누는지. 확장→압축 구조가 왜 "지식 저장소"인지. 분산 표현의 원리, LayerNorm의 역할까지.

글꼴

3편에서 어텐션이 "killer"와 "OOM"의 관계를 파악하는 과정을 봤다. "killer" 벡터에 "OOM" 정보가 58% 섞여 들어왔고, 이제 이 벡터는 "OOM과 관련된 killer"라는 문맥을 담고 있다.

하지만 이게 "무슨 의미인지"는 아직 모른다. "nginx가 OOM killer의 대상이다"까지는 파악했지만, "그래서 이게 서비스 장애라는 뜻이다"는 아직이다. 그 변환을 하는 게 FFN(Feed-Forward Network)이다.


어텐션만으로는 부족하다

3편에서 어텐션을 "OOM" 하나로 이해했다. FFN도 마찬가지로 "OOM" 벡터 하나가 FFN을 통과하는 과정을 추적한다.

어텐션은 토큰 간의 관계를 파악한다. 하지만 "OOM이 메모리 부족이라서 이게 위험하다"라는 지식을 적용하지는 못한다. 관계 파악과 의미 변환은 다른 일이다. FFN이 관계가 파악된 벡터를 받아서 의미를 변환한다.


FFN의 구조 — 확장했다가 압축한다

"OOM" 벡터가 FFN을 통과하는 과정이다.

"OOM"의 어텐션 출력 벡터 (2048차원)

         ▼  × W1 행렬 (2048 × 8192)
┌─────────────────────────────────────────────────────┐
│             확장된 벡터 (8192차원)                       │
│                                                       │
│  뉴런#0   뉴런#1   뉴런#2   ...  뉴런#1842  ...  뉴런#8191 │
│  [0.02]   [4.05]  [-0.45]       [ 2.08 ]       [0.33]  │
│    ↓        ↓        ↓             ↓               ↓    │
│  활성화 함수 (SiLU) 적용                                  │
│  [0.01]   [3.98]  [-0.15]       [ 1.87 ]       [0.22]  │
│   약반응    강반응    억제          반응             약반응   │
│                                                       │
│  8192개 뉴런의 반응 "조합"이 의미를 만든다:                   │
│  [0.01, 3.98, -0.15, ..., 1.87, ..., 0.22]             │
│  → 이 패턴 전체가 "OOM = 메모리 관련 커널 이벤트"를 인코딩    │
│                                                       │
│  같은 뉴런 #1이라도 다른 입력에서는 다른 의미로 작용.          │
│  뉴런 하나 = 개념 하나가 아님. 조합이 개념.                   │
└──────────────────────┬──────────────────────────────┘

                        ▼  × W2 행렬 (8192 × 2048)
               출력 벡터 (2048차원)
    "메모리 부족 상황"이라는 의미가 인코딩된 벡터

2048차원을 8192차원으로 확장했다가 다시 2048차원으로 압축한다. 왜 이런 구조일까?


왜 확장했다가 압축하는가

확장은 2048차원을 8192차원으로 펼치는 거다. 더 많은 "축"으로 입력을 분해하는 것이라고 생각하면 된다.

활성화 함수(SiLU)가 입력에 반응하는 뉴런만 살리고 나머지를 억제한다. 이 과정이 "이 입력에서 중요한 특징"을 추출하는 효과를 낸다. 그리고 압축 단계에서 활성화된 뉴런들의 조합을 원래 차원으로 되돌린다.

여기서 핵심은 이거다. 의미는 개별 뉴런이 아니라 뉴런들의 활성화 패턴(조합)에 인코딩된다. 이걸 분산 표현(distributed representation)이라고 한다.

"뉴런 #1 = 커널 메모리 이벤트"처럼 1

대응하는 게 아니다. 같은 뉴런 #1이라도 다른 입력에서는 다른 의미로 작용한다. 8192개 뉴런의 조합이므로 표현 가능한 의미의 수는 사실상 무한하다.

7B 모델은 뉴런이 약 14,000개다. 더 고차원으로 펼치니까 더 미세한 패턴을 구분할 수 있다. 3B와 7B의 결과 차이가 여기서도 나온다.


FFN = 지식 저장소

W1과 W2의 파라미터에 학습된 지식이 인코딩되어 있다.

W1에는 "OOM이 들어오면 이런 활성화 패턴을 만들어라"는 정보가, W2에는 "이 활성화 패턴을 이런 의미의 벡터로 바꿔라"는 정보가 담겨 있다.

FFN의 파라미터가 전체의 약 2/3를 차지한다. 모델의 "지식"은 대부분 여기에 있다. 어텐션이 "누가 누구와 관련 있는지"를 파악하는 거라면, FFN은 "그래서 그게 뭔 뜻인지"를 변환하는 거다.


잔차 연결 + LayerNorm

어텐션과 FFN 각각의 출력에 두 가지가 적용된다.

어텐션/FFN 각각의 출력에 두 가지가 적용된다:
 
  ① 잔차 연결: 출력 = 입력 + 변환 결과 (원본 보존)
  ② LayerNorm: 벡터의 값 분포를 정규화
 
LayerNorm이 하는 일:
  벡터 [0.208, -0.845, 1.102, ...]
    → 평균을 0으로, 분산을 1로 맞춤
    → [0.15, -0.92, 1.08, ...]  (값의 스케일이 안정적)
 
왜 필요한가:
  레이어를 36개 쌓으면 잔차 연결이 누적되면서 값이 점점 커질 수 있다.
  LayerNorm이 매 레이어마다 값을 안정적인 범위로 맞춰준다.
  이게 없으면 깊은 네트워크의 학습이 불안정해진다.

잔차 연결은 3편에서 다뤘다. FFN에서도 마찬가지로 "FFN 출력 = FFN 입력 + FFN 결과"가 적용된다.

LayerNorm은 이번에 처음 나왔다. 벡터 값의 평균을 0으로, 분산을 1로 맞추는 정규화다. 레이어를 36개 쌓으면 잔차 연결이 누적되면서 값이 점점 커질 수 있는데, LayerNorm이 매 레이어마다 값을 안정적인 범위로 잡아준다.

하나의 레이어 안에서의 전체 흐름을 정리하면 이렇다. 입력 → LayerNorm → 어텐션 → 잔차 연결 → LayerNorm → FFN → 잔차 연결 → 출력. 이 출력이 다음 레이어의 입력이 된다.

인풋/아웃풋 차원이 항상 동일(2048)이므로 레이어를 몇 개든 쌓을 수 있다. 이게 트랜스포머 구조의 핵심적인 설계다.


여기까지가 LLM의 두 핵심 부품이다. 어텐션은 "누가 누구와 관련 있는지" 관계를 파악하고, FFN은 "그래서 그게 뭔 의미인지" 변환한다. 지금까지는 "OOM" 하나로 부품을 이해했다.

그런데 실제로는 토큰이 5개다. "OOM killer가 nginx를" 5개 토큰이 동시에 36개 레이어를 통과하면 어떤 일이 벌어지는 걸까? 다음 편에서 부품들을 조립한다.

이 글이 어떠셨나요?

이 글이 도움이 되셨나요?
공유:

관련 포스트

뉴스레터 구독

새 글이 올라오면 이메일로 알려드려요.

댓글

댓글을 불러오는 중...