<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Justin Huang (한국어)</title><description>Justin Huang의 개인 블로그로, AI, 기술, 글쓰기, 삶에 대해 씁니다.</description><link>https://justinhuangai.github.io/</link><item><title>기술 리포트 읽기: 《Attention Residuals》 (어텐션 잔차)</title><link>https://justinhuangai.github.io/ko/posts/attention-residuals/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko/posts/attention-residuals/</guid><description>Kimi Team의 Attention Residuals 기술 리포트 읽기: 왜 residual connection도 attention처럼 바뀌어야 하는지, 그리고 Full AttnRes / Block AttnRes가 그 아이디어를 어떻게 학습 가능하고 배포 가능한 시스템으로 만드는지</description><pubDate>Thu, 19 Mar 2026 08:49:27 GMT</pubDate><content:encoded>2026년 3월 16일, Kimi Team은 arXiv에 기술 리포트 한 편을 올렸다: [《Attention Residuals》](/papers/2603.15031v1.pdf) (어텐션 잔차).

이 리포트에서 저자들이 진짜로 힘을 준 부분은 구조만 봐도 드러난다. 단순히 &quot;새 모듈 하나를 제안했다&quot;가 아니다. `motivation -&gt; AttnRes -&gt; Block AttnRes -&gt; infrastructure -&gt; experiments -&gt; discussion`의 순서로, residual connection이 실제로 무엇을 하고 있는지를 처음부터 다시 설명한다.

## 0. 먼저 몇 가지 용어부터

머신러닝 배경이 전혀 없어도, 이 리포트가 실제로 파고드는 문제만 따라가면 충분하다. 아래 순서대로 감을 잡으면 된다:

- `Transformer`: 오늘날 대부분의 대형 언어 모델이 사용하는 기본 아키텍처다. 층층이 정보를 처리하는 기계라고 생각하면 된다.
- `hidden state`: 특정 층에서 모델이 들고 있는 내부 중간 표현이다. 대략 &quot;모델이 지금 머릿속에 적어 둔 임시 메모&quot;에 가깝다.
- `residual connection / 잔차 연결`: 이전 내용을 보존한 채, 현재 층에서 새로 계산한 것을 그 위에 더하는 경로다.
- `residual / 잔차`: 그 잔차 연결 안에서 새로 더해지는 증가분에 더 가깝다.
- `attention`: 많은 정보 중에서 지금 가장 중요하게 봐야 할 부분을 골라내는 메커니즘이다.
- `PreNorm`: 층에 들어가기 전에 값의 스케일을 먼저 정리하고, 그다음 계산을 수행하는 방식이다.

## 1. 한 문장으로 요약하면

이 기술 리포트가 던지는 질문은 아주 날카롭다:

**Transformer가 이미 시퀀스 차원에서는 recurrence를 attention으로 대체했는데, 왜 깊이 차원에서의 정보 집계는 아직도 고정된 덧셈에 머물러 있는가?**

현대 LLM은 거의 모두 비슷한 층 구조를 쓴다. 먼저 PreNorm을 하고, 그다음 residual 경로를 지난다. 쉽게 말하면 값의 스케일을 정리한 뒤 새로운 계산 결과를 원래 입력에 더해 준다. 우리는 보통 이 구조를 &quot;학습을 안정화하는 장치&quot; 정도로 기억한다. 깊은 네트워크가 훈련 도중 무너지지 않게 해 주는 장치 말이다. 그런데 저자들은 residual connection이 그보다 더 중요한 역할도 하고 있다고 말한다:

**깊이를 따라 정보가 어떻게 모이느냐를 residual connection이 결정한다는 것이다.**

아래 수식이 낯설다면 거기서 멈출 필요는 없다. 바로 뒤의 풀어서 쓴 설명이 핵심이다.

표준 residual 규칙은 아주 단순하다:

$$
h_l = h_{l-1} + f_{l-1}(h_{l-1})
$$

이를 두 부분으로 나누어 보면:

- $h_{l-1}$: 이전 층이 이미 만들어 둔 기존 내용
- $f_{l-1}(h_{l-1})$: 현재 층이 새로 계산한 증가분, 즉 &quot;잔차&quot;라는 말에 더 가까운 부분

그리고 이 둘을 다시 합치는 전체 동작이 더 정확하게는 residual connection이다.

이 점화식을 펼치면 다음과 같다:

$$
h_l = h_1 + \sum_{i=1}^{l-1} f_i(h_i)
$$

사람 말로 옮기면, $l$번째 층이 보는 입력은 사실상 &quot;embedding + 이전 모든 층 출력의 균등 합&quot;이다. 모든 층의 가중치가 1이다. 선택도 없고, 억제도 없고, &quot;이번 단계에서는 17층보다 3층을 더 참고하자&quot; 같은 메커니즘도 없다.

AttnRes의 핵심 아이디어는 한 문장으로 끝난다:

**residual을 고정 덧셈이 아니라, 깊이 방향의 softmax attention으로 바꾸자는 것이다.**

## 2. 기존 residual은 뭐가 문제인가

이 기술 리포트에서 가장 중요한 것은 새 수식을 제안했다는 사실이 아니다. 사람들이 너무 익숙해져서 더 이상 문제라고 여기지 않던 것을 다시 문제로 만들었다는 점이다.

표준 residual은 오랫동안 &quot;최적화 안정화 장치&quot;로만 취급되어 왔다. 그래디언트만 잘 지나가면 역할을 다한 셈이었다. 하지만 정보 흐름 관점에서 보면, 이 경로는 놀랄 만큼 거칠다.

계속 수정되는 문서를 상상해 보자. 매번 이전 버전에서 정말 relevant 한 부분만 골라 정리하는 대신, 모든 이전 초안을 통째로 문서 뒤에 붙여 버린다고 해 보자. 20번째 수정본쯤 되면 3번째 버전의 중요한 통찰은 여전히 &quot;존재&quot;하긴 하지만, 두꺼워진 더미 속에 묻혀 버린다.

저자들이 지적하는 PreNorm 문제는 바로 이것이다. 리포트는 SiameseNorm의 관찰을 인용하면서, PreNorm 아래에서는 `hidden state`의 크기가 깊이에 따라 대략 $O(L)$로 커진다고 강조한다. hidden state는 결국 각 층이 들고 있는 내부 메모다. 그 결과:

- 뒤쪽 층으로 갈수록 점점 더 부푼 &quot;역사적 총합&quot;을 보게 되고
- 초기 층의 정보는 사라지지는 않지만 계속 희석되며
- 뒤쪽 층이 존재감을 가지려면 더 큰 크기의 출력을 내야 한다

리포트는 이 현상을 `PreNorm dilution`이라고 부른다. 정말 정확한 이름이다. 문제는 그래디언트가 끊기는 것도 아니고, 학습이 폭발하는 것도 아니다. 각 층의 상대적 기여가 점점 묽어지는 것이다.

나는 이 리포트의 밑바닥에 흐르는 한 가지 사고가 특히 마음에 들었다. 시퀀스 차원에서는 우리는 이미 &quot;과거 토큰을 모두 똑같이 취급하는 방식&quot;에 만족하지 않았고, 그래서 attention이 등장했다. 그렇다면 왜 깊이 차원에서는 여전히 &quot;모든 이전 층을 같은 가중치로 더하는 방식&quot;을 받아들이고 있는가?

## 3. AttnRes는 실제로 무엇을 하는가

AttnRes의 형태는 꽤 깔끔하다. $l$번째 층은 더 이상 이전 모든 출력의 합을 기계적으로 받지 않는다. 대신 과거 표현들에 대해 가중 선택을 수행한다:

$$
h_l = \sum_{i=0}^{l-1} \alpha_{i \to l} \cdot v_i
$$

가중치 $\alpha_{i \to l}$는 softmax에서 나온다. softmax가 익숙하지 않다면 이렇게 생각해도 된다. 여러 점수를 받아서 합이 1인 가중치로 바꾸는 과정이다. 그래서 모델은 &quot;여기를 더 보고, 저기는 덜 보자&quot;를 분명하게 표현할 수 있다:

$$
\alpha_{i \to l} = \operatorname{softmax}\left(w_l^T \operatorname{RMSNorm}(k_i)\right)
$$

attention을 처음 접한다면 가장 쉬운 직관은 이렇다:

- `query`: 지금 현재 층이 찾고 있는 것
- `key`: 각 과거 층이 달고 있는 일종의 색인 표식
- `value`: 실제로 가져와서 집계하는 내용

여기서 중요한 설계 포인트는 세 가지다.

첫째, **query는 현재 hidden state에서 즉석 계산하지 않는다. 층마다 학습되는 pseudo-query 벡터 $w_l$를 둔다.**  
이건 약간 반직관적이다. 우리는 보통 attention을 보면 query가 현재 입력에서 나와야 한다고 생각한다. 그런데 저자들은 query를 토큰별 동적 벡터가 아니라 층별 파라미터로 잡았다. 그 장점은 같은 block 안의 여러 query를 미리 배치로 계산할 수 있다는 점이고, 바로 그 때문에 뒤에서 설명할 인프라 최적화가 가능해진다.

둘째, **key와 value는 이전 층의 출력에서 직접 온다.**  
즉 입력 의존성이 사라지는 것은 아니다. query가 아니라 각 층의 표현 그 자체에 입력 의존성이 남아 있다. 서로 다른 샘플은 서로 다른 이전 층 출력을 만들기 때문에, 깊이 attention은 결국 여전히 input-dependent 하다.

셋째, **key 앞에 RMSNorm을 붙인다.**  
이건 작은 설계 같지만 꽤 중요하다. 정규화를 하지 않으면 크기가 큰 층이 점곱에서 자동으로 유리해진다. 그러면 attention 가중치는 &quot;누가 더 relevant 한가&quot;보다 &quot;누가 더 크게 말하는가&quot;를 반영하게 된다.

Python(PyTorch 기반)으로 쓰면 대략 이렇게 된다:

```python
import torch
from torch import nn


def attention_residual(
    sources: list[torch.Tensor],
    pseudo_query: torch.Tensor,
    norm: nn.RMSNorm,
) -&gt; torch.Tensor:
    keys = torch.stack([norm(source) for source in sources], dim=0)
    values = torch.stack(sources, dim=0)

    logits = keys @ pseudo_query
    weights = torch.softmax(logits, dim=0)
    return (weights.unsqueeze(-1) * values).sum(dim=0)
```

겉으로 보면 이것은 &quot;attention을 residual 위에 올린 것&quot;처럼 보인다. 하지만 나는 더 정확한 표현이 따로 있다고 본다:

**residual connection을 고정된 누산기에서, 선택적으로 깊이를 검색하는 장치로 바꾼 것이다.**

## 4. 이 리포트의 가장 좋은 점: 아이디어만이 아니라 엔지니어링도 준다

만약 이 리포트가 Full AttnRes까지만 이야기했다면, 여전히 아름다운 research idea에 머물렀을 것이다.

Full AttnRes는 모든 층이 자신보다 앞선 모든 층을 보게 한다. 이론적으로는 이해하기 쉽고, 순수 계산량만 보면 아주 무섭지도 않다. 네트워크 깊이 $L$은 보통 시퀀스 길이 $T$보다 훨씬 작기 때문에, 저자들도 $O(L^2 d)$라는 계산 자체가 가장 큰 공포는 아니라고 말한다.

문제는 대규모 학습에서 터진다:

- activation recomputation 때문에 원래 버려도 되던 중간 출력이 다시 꼭 저장해야 하는 대상이 되고
- pipeline parallelism 때문에 그 층 간 표현들이 stage 사이를 오가야 하며
- 모든 층이 모든 이전 층을 봐야 하면 통신과 캐시 압력이 급격히 커진다

그래서 저자들은 **Block AttnRes**를 제안한다.

방법은 $L$개의 층을 $N$개의 block으로 나누는 것이다. block 내부에서는 먼저 일반적인 합으로 block representation을 만들고, block 사이에서는 attention을 적용한다. 즉:

- Full AttnRes는 모든 과거 층을 본다
- Block AttnRes는 과거 block들의 요약과 현재 block 안의 부분합을 본다

요약하면, 더 세밀한 층 단위 attention을 요약 수준의 block attention으로 바꿔 확장성을 얻는 셈이다.

그리고 저자들은 &quot;block으로 나눴으니 메모리가 줄었다&quot; 수준에서 멈추지 않는다. 시스템 쪽 장부도 실제로 계산한다:

- 학습 단계에서는 **cross-stage caching**을 써서 pipeline 안에서 과거 block을 반복 전송하지 않게 하고
- 추론 단계에서는 **two-phase computation**을 사용하며
- 1단계에서는 inter-block attention을 병렬 계산하고
- 2단계에서는 intra-block lookback을 순차 계산한 다음 online softmax merge로 결과를 합친다

부록과 `table/memory_access.tex`에 이 리포트에서 가장 하드코어한 숫자들이 나온다. 리포트의 대표 설정에서는:

- 표준 residual: 층당 residual 메커니즘 I/O가 `3d`
- naive Full AttnRes: `130d`
- 최적화된 Full AttnRes: `24d`
- Block AttnRes: `5.5d`
- mHC: `34d`

이 숫자들은 많은 것을 말해 준다. Block AttnRes가 &quot;표준 residual만큼 싸다&quot;는 뜻은 아니다. 하지만 이미 &quot;현실적으로 불가능하다&quot;에서 &quot;현실 시스템에서 시도해 볼 만하다&quot;로 내려왔다. 그리고 실제 측정된 오버헤드도 작다:

- 학습 wall-clock overhead는 4% 미만
- 추론 latency overhead는 2% 미만

그래서 나는 이것이 진짜 시스템 지향 기술 리포트처럼 읽힌다고 느꼈다. 아이디어는 새롭지만 비용 계산은 흐릿한 논문이 많다. 이 리포트는 비용 계산까지 신경 쓴다.

## 5. 실험에서 진짜 봐야 할 것

### 5.1 스케일링 법칙 결과: 한 번 운 좋게 이긴 것이 아니다

저자들은 먼저 다섯 개의 모델 규모에서 scaling-law 실험을 수행하며 Baseline, Full AttnRes, Block AttnRes를 비교한다.

피팅된 곡선은 다음과 같다:

- Baseline: $1.891 \times C^{-0.057}$
- Block AttnRes: $1.870 \times C^{-0.058}$
- Full AttnRes: $1.865 \times C^{-0.057}$

여기서 가장 중요한 것은 기울기 차이가 얼마나 되느냐가 아니다. 핵심은 이것이다:

**AttnRes는 compute 범위 전반에서 일관되게 더 낮다.**

리포트는 전달하기 쉬운 요약 문장도 준다. `5.6 PFLOP/s-days` 예산 지점에서 Block AttnRes의 loss는 baseline이 약 `1.25x` 더 많은 compute를 써야 겨우 도달할 수준이라는 것이다.

즉 이것은 &quot;특정 모델 크기에서 우연히 튜닝이 잘 맞은 결과&quot;로 보이지 않는다. 꽤 안정적인 규모 이득으로 읽힌다.

### 5.2 메인 모델 실험은 장난감이 아니다

주 실험은 작은 toy benchmark가 아니다. Kimi Linear 기반의 큰 설정을 사용한다:

- `48B total / 3B activated parameters`
- 27개의 Transformer blocks, 즉 54개 층
- 8-of-256 routed experts + 1개의 shared expert
- `1.4T tokens` 사전학습

이것이 중요한 이유는, 저자들이 단지 작은 모델에서 예쁜 곡선을 그린 것이 아니라 이 residual 재설계를 실제 대규모 학습 레시피 안에 넣어 보았다는 뜻이기 때문이다.

### 5.3 가장 말이 많은 그림: 출력 크기가 더 이상 폭주하지 않는다

나를 가장 설득한 것은 benchmark 표보다 학습 동역학 그림이었다.

Baseline에서는 output magnitude가 깊이에 따라 계속 상승한다. 그림 속 값은 극적이다. 앞 block의 `0.04`, `0.06`, `0.10`이 뒤 block에서 `10.47`, `12.15`까지 올라간다. 이것이야말로 PreNorm dilution이 눈에 보이는 형태다.

반면 Block AttnRes는 전혀 다른 모양을 보인다. block 경계에서 일종의 주기적 리셋이 생기고, 크기는 대략 `0.21`에서 `1.91` 사이를 오갈 뿐이다. 같은 식의 폭주 상승이 없다.

이게 중요한 이유는 AttnRes가 마지막 benchmark에서 &quot;몇 점 더 얻었다&quot;는 수준이 아니라, 학습 중 표현이 깊이를 따라 쌓이는 방식 자체를 바꾸고 있다는 신호이기 때문이다.

### 5.4 다운스트림 태스크: 특히 추론과 코드에서 이득이 크다

사전학습 이후 AttnRes는 리포트에 나온 모든 평가에서 baseline보다 나쁘지 않으며, 특히 다음 지표들이 눈에 띈다:

- MMLU: `73.5 -&gt; 74.6`
- GPQA-Diamond: `36.9 -&gt; 44.4`
- Math: `53.5 -&gt; 57.1`
- HumanEval: `59.1 -&gt; 62.2`
- C-Eval: `79.6 -&gt; 82.5`

특히 GPQA, Math, HumanEval처럼 다단계 추론이나 코드 생성이 중요한 태스크에서 상승폭이 더 크다. 저자들의 설명은 이렇다. 뒤쪽 층이 앞쪽 층의 표현을 더 선택적으로 회수할 수 있다면 compositional tasks가 더 큰 이득을 본다. 나는 이 설명이 설득력 있다고 본다.

복잡한 추론에서 진짜 문제는 정보가 아예 없는 것이 아니라, 중요한 정보가 네트워크 깊은 곳에 묻혀 버리는 것이다.

## 6. Ablation은 무엇을 말해 주는가

이 리포트의 ablation이 좋은 이유는 단지 &quot;효과가 있다&quot;를 보여 주는 데서 멈추지 않고, 왜 효과가 있는지까지 건드리기 때문이다.

가장 흥미로운 몇 가지 결과를 보면:

- **DenseFormer는 1.767로, baseline 1.766과 거의 같다.**  
  단지 모든 이전 층에 접근할 수 있다는 사실만으로는 부족하다는 뜻이다. 중요한 것은 가중치가 input-dependent 하냐는 점이다.

- **mHC는 1.747까지 내려간다.**  
  깊이 차원에서의 동적 혼합이 실제로 유효하다는 뜻이다.

- **Full AttnRes는 1.737까지 내려간다.**  
  baseline, DenseFormer, mHC보다 더 낮다. 명시적인 softmax depth attention이 더 강한 방향이라는 뜻이다.

- **SWA는 최근 창만 보는 방식인데 1.764에 그친다.**  
  이건 중요하다. AttnRes의 이득이 단순히 &quot;최근 몇 층을 더 본다&quot;에 있지 않고, 필요할 때 더 먼 층까지 선택적으로 볼 수 있다는 데 있음을 보여 준다.

- **block size를 2, 4, 8로 바꿔도 loss는 모두 1.746 안팎이다.**  
  그래서 저자들이 최종적으로 약 8개 block을 택한 것이다. 감이 아니라 성능과 엔지니어링 사이의 sweet spot이다.

- **input-dependent query 버전은 1.731로 Full AttnRes보다도 더 좋다.**  
  이건 특히 흥미롭다. 현재 리포트의 pseudo-query 설계가 성능 상한이 아니라는 뜻이기 때문이다. 이것은 인프라 최적화를 위해 고른 절충안이다. 다시 말해, 저자들은 더 강한 버전을 몰라서 안 쓴 것이 아니라, 확장 가능한 버전을 의식적으로 택했다.

그래서 이 리포트가 재미있다. 본문, ablation, 시스템 설계를 함께 보면 진짜 선택 기준이 드러난다. 무작정 최저 loss만 쫓는 것이 아니라, 충분히 강하면서 실제로 훈련 가능한 설계를 찾고 있는 것이다.

## 7. 나는 이 리포트를 어떻게 읽었는가

첫째, 이 리포트의 가장 중요한 점은 새 모듈을 발명했다는 사실이 아니다. residual connection을 다시 &quot;최적화 안정화 도구&quot;가 아니라 &quot;정보 라우팅 메커니즘&quot;으로 끌어올렸다는 점이다.

이 관점을 받아들이는 순간 많은 것이 새로 보인다. residual은 단지 gradient highway가 아니다. depth aggregation rule이기도 하다. 그러면 자연스럽게 이런 질문들이 따라온다:

- 각 층은 정말로 앞선 층들에 선택적으로 접근할 수 있는가?
- 깊이 차원에도 attention sink 같은 현상이 있는가?
- 기존 residual 변형들은 사실상 depth-wise linear attention에 가까운 것 아닌가?

바로 그 지점에서 discussion 섹션이 특히 흥미롭다. 저자들은 여러 residual 변형을 `depth mixing matrix`라는 관점에서 다시 묶어 내고, 한 걸음 더 나아가 이렇게 말한다:

**기존 방법들 상당수는 본질적으로 깊이 차원에서 linear attention을 하고 있고, AttnRes는 깊이 차원의 softmax attention을 한다.**

대담한 주장이다. 하지만 굉장히 생산적인 주장이다. Transformer가 한때 시퀀스 차원을 recurrence에서 softmax attention으로 밀어냈다면, AttnRes는 깊이 차원도 한 걸음 더 밀어 보려 한다는 뜻이기 때문이다.

둘째, 이 리포트의 기질은 &quot;문제를 먼저 제대로 묻고, 그다음 시스템이 돌아가게 만든다&quot;에 가깝다. 각 부품을 무조건 가장 화려하게 만들려 하지 않는다. 예를 들어 query를 token-dependent 동적 벡터가 아니라 layer-specific 파라미터로 둔 것은, 순수 성능만 보면 최강일 필요는 없다. 하지만 그 덕분에 batching, two-phase computation, pipeline cache가 가능해진다. 실제로 굴러가는 기술 리포트는 가장 과감한 국소 설계가 아니라, 전체 제약 아래에서 살아남는 선택으로 만들어지는 경우가 많다.

셋째, 이 리포트에서 가장 오래 남을 문장은 사실 하나의 질문이다:

**Why is depth-wise aggregation still fixed while everything else has become adaptive?**

정말 정확한 질문이다.

## 8. 이 리포트의 경계

너무 많이 칭찬하기 전에, 한계도 분명히 해야 한다.

첫째, 이것은 여전히 **technical report / arXiv preprint**이지, 동료 심사를 거친 학회 논문이 아니다. 가장 안전한 태도는 &quot;미래를 증명했다&quot;가 아니라, &quot;강력한 관점을 제시했고, 공학적으로 구현 가능한 해법을 함께 내놓았다&quot;이다.

둘째, 대규모 결과는 Kimi Linear 계열 아키텍처에 묶여 있다. MoE, KDA/MLA 혼합 attention, Moonlight / DeepSeek-V3 스타일 학습 레시피가 함께 들어간다. 결과 자체의 가치는 줄지 않지만, 모든 dense decoder-only Transformer에 자동으로 일반화할 수는 없다는 뜻이기도 하다.

셋째, 리포트 스스로도 Full AttnRes가 더 강하다고 인정한다. Block AttnRes는 오늘날 하드웨어 제약 아래에서의 현실적 답이다. 앞으로 메모리, 대역폭, interconnect가 더 좋아지거나, 더 효율적인 depth attention 변형이 나온다면 지금의 block 설계가 최종형일 가능성은 낮다.

그래서 내 판단은 이렇다:

- 이미 충분히 강해서 진지하게 읽을 가치가 있고
- 이미 충분히 완성되어 있어서 진지하게 재현해 볼 가치가 있으며
- 아직 최종 결론을 내려도 될 만큼 닫힌 문제는 아니다

## 9. 마지막 인상

지난 10년간 대형 모델 아키텍처의 흐름을 아주 거칠게 요약하면:

- Seq2Seq는 &quot;한 시퀀스를 어떻게 다른 시퀀스로 압축할 것인가?&quot;를 물었고
- Bahdanau는 &quot;왜 디코딩할 때 입력의 다른 위치를 다시 볼 수 없지?&quot;를 물었고
- Transformer는 &quot;왜 시퀀스 모델링이 꼭 recurrence에 의존해야 하지?&quot;를 물었고
- Chinchilla는 &quot;왜 늘어난 compute는 주로 파라미터 수에만 써야 하지?&quot;를 물었다

그렇다면 *Attention Residuals*는 이렇게 묻는다:

**왜 깊이 방향의 정보 집계는 아직도 &quot;모든 과거 층을 똑같이 더하는 시대&quot;에 머물러 있는가?**

이 질문만으로도 이미 충분히 가치가 있다.

몇 년 뒤 AttnRes가 PreNorm처럼 기본 설정이 될지는 나도 모른다. 하지만 이 기술 리포트가 residual connection을 다시 생각하고, 설계하고, 최적화할 가치가 있는 대상으로 되돌려 놓았다는 점만은 꽤 확신한다.

사람들은 attention이 시퀀스 모델링을 다시 썼다고 말했다.

이 리포트는 residual을 다시 쓰려 하고 있다.

2026년 봄, Kimi 팀의 작업은 이미 한 가지를 보여준다: Scaling Laws가 병목에 가까워지는 징후를 드러내기 시작할 때, LLM의 구조 혁신은 계속해서 등장할 것이다.

---

**더 읽어보기**

- [《Sequence to Sequence Learning with Neural Networks》](/ko/posts/sequence-to-sequence-learning-with-neural-networks/) (신경망을 이용한 시퀀스-투-시퀀스 학습) — encoder-decoder 패러다임의 출발점
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/ko/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/) (정렬과 번역을 공동으로 학습하는 신경 기계 번역) — attention 메커니즘의 기원
- [《Attention Is All You Need》](/ko/posts/attention-is-all-you-need/) (어텐션만 있으면 충분하다) — attention이 주연이 되고 Transformer가 탄생한 순간
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/ko/posts/bert/) (BERT: 언어 이해를 위한 깊은 양방향 트랜스포머 사전학습) — 사전학습 패러다임의 확립
- [《Scaling Laws for Neural Language Models》](/ko/posts/scaling-laws-for-neural-language-models/) (신경 언어 모델을 위한 스케일링 법칙) — 규모에 관한 수학
- [《Language Models are Few-Shot Learners》](/ko/posts/language-models-are-few-shot-learners/) (언어 모델은 퓨샷 학습자다) — 더 큰 모델은 컨텍스트에서 더 많은 능력을 끌어낸다
- [《Training Compute-Optimal Large Language Models》](/ko/posts/training-compute-optimal-large-language-models/) (연산량 최적의 대규모 언어 모델 학습) — compute를 가장 잘 쓰는 방법</content:encoded><category>Technical Report Reading</category><category>technical-report-reading</category><category>residual-connections</category><category>transformer</category><category>AI</category><category>LLM</category><category>python</category></item><item><title>논문 읽기: 《Training Compute-Optimal Large Language Models》 (연산량 최적의 대규모 언어 모델 학습)</title><link>https://justinhuangai.github.io/ko/posts/training-compute-optimal-large-language-models/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko/posts/training-compute-optimal-large-language-models/</guid><description>Chinchilla 논문 — 왜 대부분의 대형 모델이 과소 학습되었는지, 그리고 컴퓨팅 예산을 현명하게 쓰는 법, 실제 Python 코드 예시 포함</description><pubDate>Wed, 11 Mar 2026 08:58:04 GMT</pubDate><content:encoded>2022년 3월 29일, DeepMind의 연구팀이 arXiv(연구자들이 학술지 심사를 거치지 않고 논문을 공개할 수 있는 프리프린트 서버)에 논문을 업로드했다: [《Training Compute-Optimal Large Language Models》](/papers/2203.15556v1.pdf) (연산량 최적의 대규모 언어 모델 학습).

제1저자는 Jordan Hoffmann이고, 공저자로 Sebastian Borgeaud, Arthur Mensch, Elena Buchatskaya, Trevor Cai, Eliza Rutherford 등 다수가 참여했다 — 당시 전원 DeepMind 소속이었다. Arthur Mensch는 이후 유럽에서 가장 주목받는 AI 기업 중 하나인 Mistral AI를 공동 창립하게 된다.

이 논문은 흔히 &quot;Chinchilla 논문&quot;이라 불린다 — 팀이 자신들의 발견을 검증하기 위해 학습시킨 700억 파라미터 모델의 이름에서 따온 것이다. 논문 제목이 아니라 동물 이름이 붙었다. AI 업계에서 &quot;Chinchilla 스케일링&quot;은 이 논문의 핵심 주장을 가리키는 관용어가 되었다.

그 주장은 단순하고, 대담하며, 업계 대부분에게 불편한 것이었다: **2022년의 가장 큰 언어 모델들 중 다수는 &quot;모델이 충분히 크지 않은&quot; 것이 아니라, 각자의 컴퓨팅 예산 대비 현저하게 과소 학습된 것이었다.**

## 0. 먼저 몇 가지 용어부터

이 논문은 결국 &quot;예산을 어디에 써야 하는가&quot;를 따지는 글이라서, 아래 용어를 먼저 잡아두면 핵심이 훨씬 빨리 들어온다:

- `컴퓨팅 예산`: 이번 학습 전체에 얼마만큼의 계산 자원을 쓸 수 있는지다.
- `파라미터 수`: 모델이 얼마나 큰지를 나타낸다.
- `token / 토큰`: 모델이 실제로 읽는 최소 텍스트 단위다. 글자나 단어 조각 정도로 생각하면 된다.
- `loss / 손실`: 모델이 전체적으로 얼마나 많이 틀리는지를 나타내는 값이다. 낮을수록 좋다.
- `scaling law / 스케일링 법칙`: 파라미터 수, 데이터 양, 컴퓨팅이 바뀔 때 모델 성능이 어떻게 함께 바뀌는지를 설명하는 법칙이다.
- `undertrained / 과소 학습`: 모델이 너무 작은 것이 아니라, 데이터나 학습량이 부족해서 잠재력을 다 끌어내지 못한 상태다.

## 1. 질문

2022년 초, AI 커뮤니티는 [Kaplan et al. (2020)](/ko/posts/scaling-laws-for-neural-language-models/)에서 분명한 교훈을 내면화한 상태였다: 더 큰 모델은 예측 가능하게 더 낫다. 스케일링 법칙 논문은 성능이 멱법칙을 따르며, 주어진 컴퓨팅 예산에서 모델을 가능한 한 크게 만들어야 한다고 보여주었다.

업계는 그 조언을 충실히 따랐다. 2022년 봄까지 GPT-3는 1,750억 파라미터를 3,000억 토큰으로 학습시켰다. DeepMind 자체의 Gopher는 2,800억 파라미터를 3,000억 토큰으로 학습시켰다. 곧이어 Google도 5,400억 파라미터의 PaLM을 발표했다. 추세는 명확했다: 파라미터 수를 올려라.

하지만 눈앞에 문제가 숨어 있었다. Kaplan et al.은 컴퓨팅을 스케일링할 때 대부분의 예산이 모델 크기에 가야 하고(N ∝ C^0.73), 학습 데이터에는 상대적으로 적게 가야 한다고(D ∝ C^0.27) 결론지었다. 이는 곧: 모델을 거대하게 만들되, 적당한 양의 데이터만 학습시켜라는 뜻이었다.

Hoffmann 팀은 단순한 질문을 던졌다: 그것이 정말 맞는가?

## 2. 세 가지 독립적 접근법, 하나의 답

이 논문이 유난히 설득력 있는 이유는 방법론에 있다. 팀은 단일 실험에 의존하지 않았다. 같은 질문에 세 가지 완전히 독립적인 각도에서 접근했고, 세 가지 모두 같은 답으로 수렴했다.

**접근법 1: 컴퓨팅을 고정하고 배분을 변화시킨다.** 7,000만에서 160억 파라미터에 이르는 400개 이상의 모델을 학습시켰다. 각 모델은 모델 크기와 학습 데이터 사이에 서로 다른 배분을 가졌지만, 총 컴퓨팅은 동일했다. 각 컴퓨팅 수준에서 어떤 모델 크기가 손실을 최소화하는지 찾았다.

**접근법 2: IsoFLOP 프로필.** 9가지 크기(7,000만에서 100억 파라미터)의 모델을 다양한 양의 데이터로 학습시켰는데, 각 그룹의 실행이 대략 같은 총 컴퓨팅을 사용하도록 설계했다. 그런 다음 곡선을 피팅하여 각 컴퓨팅 수준에 대한 최적 모델 크기를 찾았다.

**접근법 3: 파라메트릭 손실 함수 피팅.** 모든 학습 실행에 다음 방정식을 피팅했다:

$$
\hat{L}(N, D) = E + \frac{A}{N^\alpha} + \frac{B}{D^\beta}
$$

여기서 E는 비가역적 손실(자연 언어의 엔트로피 — 어떤 모델도 이보다 더 나을 수 없다), A/N^α는 모델 크기 병목, B/D^β는 데이터 병목을 포착한다. 피팅된 파라미터에서 최적의 N과 D를 컴퓨팅의 함수로 도출했다.

세 가지 접근법 모두 일치했다:

$$
N_{\mathrm{opt}} \propto C^a, \quad D_{\mathrm{opt}} \propto C^b, \quad a \approx 0.50, \quad b \approx 0.50
$$

```python
def optimal_scaling(compute: float) -&gt; tuple[float, float]:
    a = 0.50
    b = 0.50
    n_opt = compute ** a
    d_opt = compute ** b
    return n_opt, d_opt
```

지수 a ≈ b ≈ 0.5의 진정한 의미는: 컴퓨팅이 증가함에 따라 모델 크기와 학습 데이터가 거의 같은 비율로 함께 확장되어야 한다는 것이다. 컴퓨팅이 10배 증가하면 둘 다 약 3.2배, 2배 증가하면 둘 다 약 1.4배 늘려야 한다. 다시 말해, 모델 크기가 두 배가 될 때마다 학습 토큰 수도 두 배가 되어야 한다. 이는 컴퓨팅을 주로 모델 크기에 써야 한다던 Kaplan et al.의 결론과 정면으로 모순된다.

## 3. Kaplan이 틀린 이유

이것은 한쪽이 &quot;틀렸다&quot;는 문제가 아니다. 실험 설정이 달랐고, 그것이 다른 최적 배분 결론으로 이어진 것이다. 두 팀 모두 엄밀한 작업을 했다.

Kaplan et al.은 학습 기간에 맞게 조정되지 않는 고정 학습률 스케줄을 사용했다. 학습률 스케줄을 조정하지 않고 더 많은 스텝을 학습시키면 성능이 떨어진다 — 모델이 본질적으로 더 나빠서가 아니라 최적화가 최적이 아니기 때문이다. 이로 인해 긴 학습 실행이 실제보다 덜 효과적으로 보여, 결과가 더 적은 스텝으로 학습된 더 큰 모델 쪽으로 편향되었다.

Hoffmann 팀은 각 학습 실행에 대해 학습률 스케줄을 조정하여, 각 구성이 공정한 기회를 얻도록 했다. 이렇게 하면, 더 많은 데이터로 더 오래 학습시키는 것이 Kaplan의 수치가 시사하는 것보다 훨씬 더 가치 있다는 것이 드러난다.

```python
from dataclasses import dataclass
from typing import Literal


@dataclass(frozen=True)
class TrainingConfig:
    n_params: float
    n_tokens: float
    schedule: Literal[&quot;fixed&quot;, &quot;cosine_with_warmup&quot;]
    warmup_steps: int
    total_steps: int
```

## 4. 파라메트릭 손실 함수

논문의 접근법 3은 성능에 대한 완전한 수학적 모델을 제공하기 때문에 더 자세히 살펴볼 가치가 있다:

$$
\hat{L}(N, D) = E + \frac{A}{N^\alpha} + \frac{B}{D^\beta}
$$

피팅된 상수는 다음과 같다:

- E = 1.69 — 비가역적 손실 (자연 언어의 엔트로피)
- A = 406.4, α = 0.34 — 모델 크기 항
- B = 410.7, β = 0.28 — 데이터 항

이 방정식의 구조는 연구할 가치가 있다. 손실은 세 가지 성분으로 이루어진다: 절대로 밑으로 내려갈 수 없는 하한(E), 파라미터가 너무 적을 때의 페널티(A/N^α), 데이터가 너무 적을 때의 페널티(B/D^β). 모델 크기 페널티와 데이터 페널티는 가산적이다 — 컴퓨팅 예산을 놓고 경쟁한다.

```python
def estimated_loss(n_params: float, n_tokens: float) -&gt; float:
    e = 1.69
    a = 406.4
    alpha = 0.34
    b = 410.7
    beta = 0.28
    return e + a / (n_params ** alpha) + b / (n_tokens ** beta)


def optimal_params_and_tokens(compute_flops: float) -&gt; tuple[float, float]:
    alpha = 0.34
    beta = 0.28
    a = beta / (alpha + beta)
    b = alpha / (alpha + beta)
    g = 2.0

    base = compute_flops / 6.0
    n_opt = g * (base ** a)
    d_opt = (1.0 / g) * (base ** b)
    return n_opt, d_opt
```

## 5. 충격적인 표

논문의 Table 1은 당시 여러 대형 모델의 실제 파라미터 수와 학습 토큰 수를 나열하고, Table 3은 다양한 모델 크기에 대한 compute-optimal 토큰 추정치를 제시한다. 두 표를 나란히 놓으면, 업계 전체에 대한 감사 보고서처럼 읽힌다:

| 모델 | 파라미터 | 사용된 토큰 | Chinchilla 최적 토큰 |
|-------|-----------|-------------|---------------------------|
| GPT-3 | 175B | 300B | 3.7T |
| Gopher | 280B | 300B | 5.9T |
| Jurassic-1 | 178B | 300B | 3.7T |
| MT-NLG | 530B | 270B | 11.0T |

모든 모델이 대략 3,000억 토큰으로 학습되었다. 그러나 Chinchilla의 분석에 따르면, GPT-3는 3.7조 토큰으로 학습되었어야 했다 — 실제 본 것의 12배 이상이다. Gopher는 거의 6조를 봤어야 했다. 가장 큰 모델인 MT-NLG(5,300억 파라미터)는 11조 토큰으로 학습되었어야 했다 — 실제 학습 데이터의 40배다.

```python
from dataclasses import dataclass


@dataclass(frozen=True)
class ModelComparison:
    name: str
    params_billions: float
    tokens_used_billions: float
    optimal_tokens_billions: float


def industry_models() -&gt; list[ModelComparison]:
    return [
        ModelComparison(&quot;GPT-3&quot;, 175.0, 300.0, 3_700.0),
        ModelComparison(&quot;Gopher&quot;, 280.0, 300.0, 5_900.0),
        ModelComparison(&quot;Jurassic-1&quot;, 178.0, 300.0, 3_700.0),
        ModelComparison(&quot;MT-NLG&quot;, 530.0, 270.0, 11_000.0),
    ]
```

패턴이 뚜렷하다. 업계 전체가 모델 크기에 관계없이 대략 같은 양의 학습 데이터 — 약 3,000억 토큰 — 에 수렴해 있었다. 마치 모두가 3,000억 토큰이면 &quot;충분하다&quot;고 결정하고 추가 컴퓨팅을 전부 모델을 키우는 데 쏟아부은 것 같았다. Chinchilla는 이것이 정확히 거꾸로였다고 말한다.

## 6. 증명: Chinchilla vs. Gopher

이론을 검증하기 위해, 팀은 Chinchilla를 학습시켰다: 700억 파라미터 모델을 1.4조 토큰으로. Chinchilla는 Gopher(2,800억 파라미터, 3,000억 토큰)와 같은 컴퓨팅 예산을 사용했다 — 같은 총 학습 비용이되, 배분만 다르게 했다.

결과는 결정적이었다. Chinchilla는 4배 작음에도 거의 모든 벤치마크에서 Gopher를 능가했다:

- **MMLU** (Massive Multitask Language Understanding): Chinchilla 67.6% vs. Gopher 60.0% vs. GPT-3 43.9%
- **독해력** (RACE-h): Chinchilla 73.3% vs. Gopher 71.6%
- **상식 추론** (HellaSwag): Chinchilla 80.8% vs. Gopher 79.2%
- **BIG-bench**: Chinchilla가 대다수 태스크에서 Gopher를 능가

```python
from dataclasses import dataclass


@dataclass(frozen=True)
class ModelConfig:
    name: str
    params_billions: float
    tokens_billions: float
    mmlu_accuracy: float


def chinchilla_vs_gopher() -&gt; tuple[float, float]:
    gopher = ModelConfig(&quot;Gopher&quot;, 280.0, 300.0, 60.0)
    chinchilla = ModelConfig(&quot;Chinchilla&quot;, 70.0, 1_400.0, 67.6)

    gopher_flops = 6.0 * gopher.params_billions * 1e9 * gopher.tokens_billions * 1e9
    chinchilla_flops = 6.0 * chinchilla.params_billions * 1e9 * chinchilla.tokens_billions * 1e9
    return gopher_flops, chinchilla_flops
```

4배 작은 모델이 같은 컴퓨팅으로 거의 모든 벤치마크에서 더 큰 모델을 이기는 것 — 이것은 강력한 증명이다. 컴퓨팅이 낭비된 게 아니라, 파라미터에서 데이터로 방향만 바꾼 것이다.

## 7. 실질적 결과

Chinchilla 논문은 업계에 즉각적이고 구체적인 결과를 가져왔다.

**더 작은 모델은 운영 비용이 낮다.** 학습 비용은 일회성이지만, 추론 비용 — 실제로 모델을 실행하여 텍스트를 생성하는 비용 — 은 모델 크기에 비례하며, 사용자가 쿼리를 보낼 때마다 발생한다. 700억 모델은 2,800억 모델보다 서빙 비용이 4배 저렴하다. 더 작은 모델이 성능까지 더 좋다면, 이중 승리다: 더 나은 품질에 더 낮은 비용.

**데이터가 병목이 되었다.** Chinchilla 이전에는 제한 요인이 컴퓨팅이었다: GPU를 얼마나 확보할 수 있는가? Chinchilla 이후에는 제한 요인이 데이터로 바뀌었다: 수조 개의 고품질 토큰을 어디서 찾을 것인가? 이것이 업계 전체의 학습 데이터 쟁탈전을 촉발했다 — 대규모 웹 스크래핑, 데이터셋 큐레이션 노력, 그리고 결국 합성 데이터 운동으로 이어졌다.

**LLaMA의 순간.** Meta의 LLaMA(2023년 2월)는 아마도 Chinchilla 스케일링의 가장 직접적인 적용이었을 것이다. LLaMA-13B는 1조 토큰으로 학습되어 대부분의 벤치마크에서 GPT-3(175B)를 능가했다. LLaMA-65B는 1.4조 토큰으로 학습되어 Chinchilla 및 PaLM-540B와 대등한 성능을 보였다. Meta는 Chinchilla 논문을 명시적으로 인용하며, 이전 관례가 제안하는 것보다 훨씬 더 많은 데이터로 의도적으로 더 작은 모델을 학습시켰다.

```python
def inference_cost_comparison() -&gt; tuple[float, float]:
    gopher_cost_per_token = 280.0
    chinchilla_cost_per_token = 70.0

    queries_per_day = 1_000_000.0
    tokens_per_query = 500.0

    daily_cost_gopher = queries_per_day * tokens_per_query * gopher_cost_per_token
    daily_cost_chinchilla = queries_per_day * tokens_per_query * chinchilla_cost_per_token
    return daily_cost_gopher, daily_cost_chinchilla
```

## 8. 내 생각

첫째, 이 논문은 교정이다 — 그것도 우아한 교정. Kaplan et al.의 프레임워크를 가져와서, 방법론적 결함(고정 학습률 스케줄)을 식별하고, 고치고, 다른 답에 도달한다. 선행 연구를 부정하지 않는다 — 그 위에 쌓는다. 파라메트릭 손실 함수 L̂(N, D) = E + A/N^α + B/D^β는 Kaplan의 정식화를 대체한 것이 아니라 정제한 것이다. 최고의 과학은 정확히 이렇다: 누군가 신중한 연구를 하고, 다른 누군가가 더 신중한 연구를 하고, 분야가 앞으로 나아간다.

둘째, 이 논문의 가장 놀라운 발견은 수학이 아니라 이론과 실제 사이의 간극이다. 업계의 모든 사람이 3,000억 토큰이 기본값이 되어가고 있다는 것을 볼 수 있었다. 이 팀이 계산을 돌릴 때까지 아무도 그것을 진지하게 의문시하지 않았다. 모델이 작은 게 아니었다. 굶주리고 있었다. 해결책은 더 크게 만드는 것이 아니었다 — 더 먹이는 것이었다.

셋째, 균등 스케일링 결과(a ≈ b ≈ 0.5)는 단순함 속에 아름다움이 있다. 모델 크기와 데이터 사이에 비대칭이 없다. 컴퓨팅이 더 있으면, 둘 다 균등하게 키워라. 복잡한 배분 전략이 필요 없다. &quot;다음 1달러의 컴퓨팅을 어디에 써야 하는가?&quot; Chinchilla의 답은 파라미터 수에만 계속 베팅하는 것이 아니라, 모델 크기와 학습 데이터를 거의 같은 비율로 함께 키우는 것이다.

넷째, 실질적 유산은 거대하다. Chinchilla 이전에는 더 나은 AI로 가는 길이 &quot;더 크게 만들어라&quot;였다. Chinchilla 이후에는 &quot;더 잘 학습시켜라&quot;가 되었다. 이 하나의 전환이, 가장 큰 파라미터 수를 감당할 수 없지만 대규모 데이터셋을 큐레이션할 수 있는 조직들에게 강력한 모델을 접근 가능하게 만들었다. LLaMA, Mistral, 그리고 오픈소스 LLM 생태계 전체가 이 통찰에 직접적인 빚을 지고 있다.

Kaplan 논문은 말했다: 더 큰 모델이 예측 가능하게 더 낫다. Chinchilla 논문은 말했다: 맞다, 하지만 당신들은 잘못된 방식으로 크게 만들고 있었다. 파라미터를 쌓아두지 마라. 데이터를 먹여라.

한 논문이 업계에 스케일링할 허가를 주었다. 다른 논문이 그 방법을 가르쳤다.

---

**논문 읽기 시리즈**

- [《Sequence to Sequence Learning with Neural Networks》](/ko/posts/sequence-to-sequence-learning-with-neural-networks/) (신경망을 이용한 시퀀스-투-시퀀스 학습) — 인코더-디코더 패러다임의 확립
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/ko/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/) (정렬과 번역을 공동으로 학습하는 신경 기계 번역) — 어텐션의 기원
- [《Attention Is All You Need》](/ko/posts/attention-is-all-you-need/) (어텐션만 있으면 충분하다) — 어텐션이 주역이 되다: Transformer의 탄생
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/ko/posts/bert/) (BERT: 언어 이해를 위한 깊은 양방향 트랜스포머 사전학습) — 사전 학습 패러다임의 확립
- [《Scaling Laws for Neural Language Models》](/ko/posts/scaling-laws-for-neural-language-models/) (신경 언어 모델을 위한 스케일링 법칙) — 규모의 수학
- [《Language Models are Few-Shot Learners》](/ko/posts/language-models-are-few-shot-learners/) (언어 모델은 퓨샷 학습자다) — 더 큰 모델, 컨텍스트에서 더 잘 능력을 이끌어내다</content:encoded><category>Paper Reading</category><category>paper-reading</category><category>chinchilla</category><category>scaling-laws</category><category>AI</category><category>LLM</category><category>python</category></item><item><title>논문 읽기: 《Scaling Laws for Neural Language Models》 (신경 언어 모델을 위한 스케일링 법칙)</title><link>https://justinhuangai.github.io/ko/posts/scaling-laws-for-neural-language-models/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko/posts/scaling-laws-for-neural-language-models/</guid><description>규모의 수학 — 더 큰 모델이 예측 가능하게 더 나은 이유, 실제 Python 코드 예시 포함</description><pubDate>Sun, 01 Mar 2026 08:45:39 GMT</pubDate><content:encoded>2020년 1월 23일, OpenAI의 연구자 10명이 arXiv(연구자들이 학술지 심사를 거치지 않고 논문을 공개할 수 있는 프리프린트 서버)에 논문을 업로드했다: [《Scaling Laws for Neural Language Models》](/papers/2001.08361v1.pdf) (신경 언어 모델을 위한 스케일링 법칙).

10명은 Jared Kaplan, Sam McCandlish, Tom Henighan, Tom B. Brown, Benjamin Chess, Rewon Child, Scott Gray, Alec Radford, Jeffrey Wu, Dario Amodei였다. 당시 전원 OpenAI 소속이었다.

그 저자 목록은 돌이켜 보면 인상적이다. Jared Kaplan과 Sam McCandlish는 이론 물리학자 출신이다 — Kaplan은 OpenAI에 합류하기 전 Johns Hopkins에서 끈 이론(string theory) 교수였다. Dario Amodei는 연구 부사장이었다. Tom B. Brown은 이후 GPT-3 논문의 제1저자가 된다. Alec Radford는 GPT-1과 GPT-2를 설계했다. 2년 안에 Kaplan, McCandlish, Amodei는 OpenAI를 떠나 Anthropic(Claude를 만든 회사)을 공동 창립하게 된다.

끈 이론가들에게는 습관이 있다: 복잡한 현상 속에서 간결한 보편 법칙을 찾는 것이다.

그 습관이 이 논문 전체에 배어 있다.

## 0. 먼저 몇 가지 용어부터

수식이 나오는 논문이 익숙하지 않다면, 아래 용어부터 잡아두면 뒤가 훨씬 편해진다:

- `파라미터 수`: 모델 안에 들어 있는 학습 가능한 숫자의 총량, 즉 모델 크기다.
- `데이터 양`: 학습할 때 모델에게 얼마나 많은 텍스트를 먹였는지를 뜻한다.
- `컴퓨팅 / compute`: 학습 전체에 투입한 계산량이다. 일단은 &quot;전기요금 청구서&quot;처럼 생각해도 된다.
- `loss / 손실`: 모델이 얼마나 틀리고 있는지를 요약한 값이다. 보통 낮을수록 좋다.
- `power law / 멱법칙`: 어떤 양이 일정한 지수 관계로 변하는 패턴이다. 로그 좌표에 그리면 직선에 가깝게 보이는 경우가 많다.
- `로그-로그 좌표`: `1, 10, 100, 1000`처럼 배수로 커지는 눈금을 쓰는 좌표계다.

## 1. 질문

2020년 초, 딥러닝 커뮤니티는 이미 더 큰 모델이 더 나은 성능을 &quot;내는 경향이 있다&quot;는 것을 알고 있었다. 하지만 &quot;경향이 있다&quot;는 과학이 아니다. 기본적인 실용적 질문에 답할 수 없었다: 컴퓨팅 예산을 두 배로 늘리면 성능이 얼마나 향상될까? 그 예산을 더 큰 모델, 더 많은 데이터, 더 긴 학습 중 어디에 써야 할까? 공식이 있을까?

이 논문은 그 질문들에 답했다. 직감도 아니고, 경험 법칙도 아니고 — 방정식으로.

## 2. Power Laws: 핵심 발견

이 논문의 핵심 발견은 언어 모델의 성능이 **power law(멱법칙)**를 따른다는 것이다. 논문이 측정한 범위 내에서, 성능이 주로 하나의 요인에 의해 제한되고 나머지 두 요인에 의해 병목되지 않을 때, 테스트 손실(모델이 다음 단어를 얼마나 잘 예측하는지를 나타내는 지표 — 낮을수록 좋다)을 파라미터 수, 데이터셋 크기, 컴퓨팅에 대해 log-log 그래프에 그리면 대략 직선 관계를 보인다.

세 개의 방정식이 논문 전체를 요약한다:

$$
L(N) \approx \left(\frac{N_c}{N}\right)^{\alpha_N}, \quad \alpha_N \approx 0.076
$$

$$
L(D) \approx \left(\frac{D_c}{D}\right)^{\alpha_D}, \quad \alpha_D \approx 0.095
$$

$$
L(C) \approx \left(\frac{C_c}{C}\right)^{\alpha_C}, \quad \alpha_C \approx 0.050
$$

표기법에 겁먹을 필요 없다. 하나씩 풀어보자:

- **L**은 테스트 손실이다 — 모델 성능을 하나의 숫자로 요약한 것이다. 낮을수록 좋다
- **N**은 파라미터 수(모델 크기)이다. 파라미터가 많을수록 모델이 더 많은 패턴을 저장할 수 있다
- **D**는 모델이 학습한 데이터 토큰 수이다. 데이터가 많을수록 학습할 패턴이 많다
- **C**는 학습에 사용된 총 컴퓨팅이다. PetaFLOP-day 단위로 측정된다(1 PetaFLOP-day = 10^15 부동소수점 연산을 하루 종일 수행한 것)
- **N_c, D_c, C_c**는 상수(곡선 위의 기준점)이다
- **α**(알파)는 지수이다 — log-log 그래프에서 직선의 기울기를 알려준다. 지수가 클수록 규모를 키울 때 성능이 더 빠르게 향상된다

핵심 통찰: 이것들은 멱법칙이지, 대수(logarithmic) 곡선이 아니다. 대수 곡선은 빠르게 평탄해진다 — 입력을 두 배로 늘려도 출력은 거의 움직이지 않는다. 멱법칙은 훨씬 관대하다: 적어도 논문이 측정한 범위 내에서, 성능은 뚜렷한 벽에 부딪히는 징후 없이 멱법칙 추세를 따라 꾸준히 개선되었다. 논문도 이 추세가 영원히 0까지 내려갈 수는 없으며 결국은 평탄해질 것이라고 명시했지만, 관측된 범위 내에서는 추세가 깔끔하게 유지되었다.

```python
def power_law_loss(x: float, x_c: float, alpha: float) -&gt; float:
    return (x_c / x) ** alpha


def scaling_law_examples() -&gt; dict[str, float]:
    alpha_n = 0.076
    alpha_d = 0.095
    alpha_c = 0.050

    return {
        &quot;10x_params&quot;: 10.0 ** alpha_n,
        &quot;10x_data&quot;: 10.0 ** alpha_d,
        &quot;10x_compute&quot;: 10.0 ** alpha_c,
    }
```

지수들이 이야기를 들려준다. 데이터셋 크기(α = 0.095)가 스케일링 배수당 가장 큰 향상을 가져온다. 모델 크기(α = 0.076)가 그 다음이다. 컴퓨팅(α = 0.050)은 가장 적은 향상을 보인다 — 모델 크기와 학습 시간 사이에 적절히 배분하지 않고 컴퓨팅만 늘리는 것은 낭비이기 때문이다. 진짜 레버리지는 올바른 것을 스케일링하는 데서 나온다.

## 3. 논문이 테스트한 범위 내에서, 아키텍처 형태보다 전체 규모가 더 중요하다

여기서 논문은 모두를 놀라게 했다.

팀은 서로 다른 깊이(레이어 수), 너비(히든 차원), 어텐션 헤드 수, 피드포워드 차원을 가진 Transformer들을 테스트했다. 논문이 테스트한 Transformer 형태 범위 내에서, 비임베딩 파라미터 수가 비슷한 한, 깊이와 너비의 구체적인 배분은 손실에 미치는 영향이 작았다.

레이어 2개에 거대한 히든 차원을 가진 Transformer? 비슷한 비임베딩 파라미터 예산 하에서 레이어 40개에 작은 히든 차원을 가진 것과 대략 같은 손실을 보인다.

```python
from dataclasses import dataclass


@dataclass(frozen=True)
class ArchitectureExperiment:
    n_layers: int
    d_model: int
    n_heads: int
    d_ff: int


def non_embedding_params(config: ArchitectureExperiment) -&gt; int:
    n = config.n_layers
    d = config.d_model
    d_ff = config.d_ff
    return n * (4 * d * d + 2 * d * d_ff + 4 * d)
```

이것은 심오한 함의를 갖는다: &quot;최적의&quot; 아키텍처를 찾는 데 몇 주를 소비할 필요가 없다. 합리적인 Transformer 구조를 하나 골라서, 그것을 키우는 데 에너지를 집중하면 된다. 논문은 임베딩 파라미터가 비임베딩 파라미터보다 성능에 기여하는 바가 훨씬 적다는 것을 발견했기 때문에 N에서 임베딩 파라미터를 명시적으로 제외했다 — 모델의 &quot;사고&quot; 능력은 어휘 테이블이 아니라 Transformer 레이어에 있다.

## 4. 모델이 과적합할 때: 데이터 병목

더 크다고 항상 더 나은 건 아니다 — 데이터셋이 너무 작으면 그렇다. 논문에서 진짜 아름다운 부분은, 모델 규모와 데이터 규모가 어떻게 함께 성능을 결정하는지를 하나의 통합된 이변수 공식으로 포착한 것이다:

$$
L(N, D) = \left[\left(\frac{N_c}{N}\right)^{\alpha_N / \alpha_D} + \frac{D_c}{D}\right]^{\alpha_D}
$$

이 공식이 말하는 것은: 손실은 모델 크기나 데이터 크기 하나만의 함수가 아니라, 둘 다의 함수라는 것이다. N이 충분히 커서 첫 번째 항이 사라지면, 남은 항은 손실이 데이터에 의해 병목되었음을 보여준다. D가 충분히 커서 두 번째 항이 사라지면, 남은 것은 모델 규모 병목이다. 공식은 두 체제 사이를 부드럽게 보간하며, 과적합은 두 항이 경쟁하는 자연스러운 결과로 포착된다.

이 관계에서 논문은 과적합이 본격적으로 영향을 미치기 시작하는 대략적인 경험적 임계점을 도출했다:

$$
D \gtrsim 5 \times 10^3 \times N^{0.74}
$$

쉽게 말하면: 모델을 크게 만들수록 필요한 데이터의 양이 늘어난다 — 하지만 선형 이하로. 10배 큰 모델은 10^0.74 ≈ 5.5배의 데이터만 더 필요하다. 더 큰 모델은 더 샘플 효율적이다: 학습 데이터의 각 토큰에서 더 많은 정보를 추출한다.

```python
def loss_nd(n_params: float, n_tokens: float) -&gt; float:
    n_c = 8.8e13
    d_c = 5.4e13
    alpha_n = 0.076
    alpha_d = 0.095
    ratio = alpha_n / alpha_d
    return ((n_c / n_params) ** ratio + d_c / n_tokens) ** alpha_d


def min_dataset_tokens(n_params: float) -&gt; float:
    return 5_000.0 * n_params ** 0.74
```

이 관계를 대략적으로 추산하면, 175B 규모 모델이 과적합을 논문이 논의한 임계값 근처로 억제하려면 거의 1조 토큰에 가까운 데이터가 필요하다. 반대로 보면, GPT-3의 3,000억 토큰은 사실 넉넉하지 않았다. 이것은 「모델을 얼마나 크게 만들고, 데이터를 얼마나 먹일 것인가」가 감이 아니라 분석 가능한 트레이드오프라는 것을 보여준다 — 이후 업계가 이 비율을 재검토한 것(가장 대표적으로 Chinchilla 논문, Hoffmann et al., 2022)도 많은 대형 모델의 데이터가 실제로 부족했다는 인식 때문이었다.

## 5. 컴퓨팅 효율적 학습: 진짜 핵심

고정된 컴퓨팅 예산이 있다면, 어떻게 써야 할까? 이것이 논문에서 가장 실용적으로 중요한 질문이고, 답은 직관에 반한다.

논문은 최적 배분이 다음을 따른다는 것을 발견했다:

$$
N_{\mathrm{opt}} \propto C^{0.73}
$$

$$
B_{\mathrm{opt}} \propto C^{0.24}
$$

$$
S_{\mathrm{opt}} \propto C^{0.03}
$$

해석: 컴퓨팅 예산이 10배 늘면, 모델을 ~5.4배 키우고, 배치 크기를 ~1.7배 늘리고, 학습 시간은 거의 늘리지 않아야 한다(~1.07배 더 많은 스텝).

직관에 반하는 부분: **아주 큰 모델을 학습시키고 수렴 훨씬 전에 멈춰야 한다.** 대부분의 사람들의 직감은 작은 모델을 완전히 학습시키는 것이다. 스케일링 법칙은 그 반대를 말한다 — 같은 컴퓨팅 예산에서, 부분적으로 학습된 큰 모델이 완전히 학습된 작은 모델을 이긴다.

```python
from dataclasses import dataclass


@dataclass(frozen=True)
class ComputeAllocation:
    n_params: float
    batch_size: float
    training_steps: float


def optimal_allocation(compute: float) -&gt; ComputeAllocation:
    return ComputeAllocation(
        n_params=compute ** 0.73,
        batch_size=compute ** 0.24,
        training_steps=compute ** 0.03,
    )


def is_compute_efficient(n_params: float, compute: float) -&gt; bool:
    optimal_n = compute ** 0.73
    return abs(n_params / optimal_n - 1.0) &lt; 0.5
```

이 결과는 업계 전체를 형성했다. 이 논문 5개월 후에 나온 GPT-3는 이 논리를 직접적으로 따랐다: 작은 모델을 완전히 학습시키는 대신, 당시 기준으로 거대한 1,750억 파라미터 모델을 학습시켰다. 이후의 &quot;Chinchilla&quot; 논문(Hoffmann et al., 2022)은 이 지수들을 업데이트하고 대부분의 대형 모델이 최적 데이터 배분 대비 실제로 과소 학습되었다고 주장했지만 — 계산 가능한 최적 트레이드오프가 존재한다는 핵심 통찰은 여기서 시작되었다.

## 6. 임계 배치 크기: 병렬화 시점 알기

논문은 배치 크기에 &quot;최적 지점&quot;이 있다는 것도 발견했으며, 그것은 현재 손실에 의존한다:

$$
B_{\mathrm{crit}} \propto L^{-4.8}
$$

학습이 진행되고 손실이 줄어들수록, 임계 배치 크기는 커진다. 학습 초기에는 손실이 높아서 작은 배치도 충분하다 — 각 배치가 충분히 강한 그래디언트 신호를 제공한다. 나중에는 모델이 이미 쉬운 패턴을 학습했기 때문에, 노이즈를 평균화하고 진전을 이루려면 더 큰 배치가 필요하다.

임계 배치 크기 이하에서는, 배치를 두 배로 늘리면 학습 시간이 대략 절반으로 줄어든다(완벽한 병렬화). 그 이상에서는, 배치를 두 배로 늘려도 거의 도움이 되지 않는다 — 컴퓨팅을 낭비할 뿐이다.

```python
def critical_batch_size(loss: float, b_star: float, l_star: float) -&gt; float:
    return b_star * (l_star / loss) ** 4.8
```

이것은 실용적인 엔지니어링 지혜이다. 많은 팀이 학습 내내 고정된 배치 크기를 사용한다. 스케일링 법칙은 학습이 진행됨에 따라 배치 크기를 늘려야 한다고 말한다 — 작게 시작해서, 모델이 나아짐에 따라 키워라.

## 7. 내 생각

이 논문을 읽고 나서, 몇 가지가 눈에 띈다.

첫째, 이 논문의 가장 깊은 기여는 특정 숫자가 아니다. 신경망 성능이 단순하고 예측 가능한 법칙에 의해 지배된다는 것을 보여준 것이다. 이 논문 이전에는 대형 모델 학습이 대체로 경험적이었다 — 이것저것 시도하고, 하이퍼파라미터를 조정하고, 최선을 바랐다. 이 논문 이후에는 수학을 할 수 있었다. 학습시키기 전에 모델이 얼마나 잘 수행할지 예측할 수 있었다. 적어도 대형 모델 학습에서 가장 비싸고 가장 중요한 부분 — 자원 배분 — 을 경험적 시행착오에서 추정 가능하고 계획 가능한 엔지니어링 문제로 끌어올렸다.

둘째, 저자들의 배경이 중요하다. Kaplan과 McCandlish는 이론 물리학의 사고방식을 가져왔다: 정밀하게 측정하고, 멱법칙을 피팅하고, 보편성을 찾는다. 이것은 대부분의 머신러닝 논문이 쓰여지는 방식이 아니다. 대부분의 ML 논문은 새로운 아키텍처를 제안하고 벤치마크에서 베이스라인을 이겼다는 것을 보여준다. 이 논문은 새로운 아키텍처를 제안하지 않았다. 사고방식을 제안했다. 도구가 새로운 것이 아니라 — 통찰이 새로운 것이다.

셋째, 「모델을 최대한 크게 만들되, 끝까지 학습시키지 않아도 된다」는 결론은 진정으로 직관에 반하며, 업계의 자원 배분 방식을 바꿔놓았다. 이 논문 이전에는 모델 크기를 정하고 완전히 수렴할 때까지 학습시키는 것이 기본이었다 — 컴퓨팅 예산을 남김없이 쓰는 것이다. 이 논문 이후에는 질문이 뒤집혔다: 같은 컴퓨팅 예산이라면, 작은 모델을 극한까지 학습시키는 것보다 모델을 감당할 수 있는 한 최대한 크게 만들고 「충분히 좋아지면」 멈추는 것이 낫다 — 학습을 끝내지 못한 큰 모델이 학습을 완료한 작은 모델보다 더 나은 성능을 보이기 때문이다. 이 사고방식이 직접적으로 GPT-3(1,750억 파라미터, 3,000억 토큰)로 이어졌고, 이후의 모든 대형 모델에 영향을 미쳤다.

넷째, 역사적 관점에서, 이 논문은 [GPT-3 논문](/ko/posts/language-models-are-few-shot-learners/)의 이론적 기반으로 볼 수 있다. GPT-3는 이 논문을 직접 인용했고, GPT-3 논문은 few-shot 능력이 모델 용량에 따라 부드럽게 향상됨을 명확히 보여줬다. GPT-3가 1,750억 파라미터를 선택한 것이 스케일링 법칙에서 영감을 받았다고 보는 것은 합리적인 추론이다 — 비록 GPT-3 논문 자체가 &quot;Kaplan 공식에 대입해서 파라미터 수를 정했다&quot;고 한 줄 한 줄 명시하지는 않았지만. 그래도 스케일링 법칙이 제공한 확신 없이는, 그 규모에서의 의사결정이 훨씬 더 불확실했을 것이다.

「더 큰 모델이 더 강하다」는 말은 2020년 이전까지는 그저 느낌이었다. 이 논문은 그것을 방정식으로 바꿨다 — 얼마나 더 강한지, 얼마나 드는지, 어떻게 쓰는 게 가장 효율적인지를 알려준다.

AI 업계는 이후 컴퓨팅 경쟁이 되었다. 이 논문을 읽고 나면 이해하게 된다: 그것은 맹목적인 군비 경쟁이 아니었다. 누군가가 먼저 계산을 끝낸 것이다.

---

**논문 읽기 시리즈**

- [《Sequence to Sequence Learning with Neural Networks》](/ko/posts/sequence-to-sequence-learning-with-neural-networks/) (신경망을 이용한 시퀀스-투-시퀀스 학습) — 인코더-디코더 패러다임의 확립
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/ko/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/) (정렬과 번역을 공동으로 학습하는 신경 기계 번역) — 어텐션의 기원
- [《Attention Is All You Need》](/ko/posts/attention-is-all-you-need/) (어텐션만 있으면 충분하다) — 어텐션이 주역이 되다: Transformer의 탄생
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/ko/posts/bert/) (BERT: 언어 이해를 위한 깊은 양방향 트랜스포머 사전학습) — 사전 학습 패러다임의 확립
- [《Language Models are Few-Shot Learners》](/ko/posts/language-models-are-few-shot-learners/) (언어 모델은 퓨샷 학습자다) — 더 큰 모델, 컨텍스트에서 더 잘 능력을 이끌어내다
- [《Training Compute-Optimal Large Language Models》](/ko/posts/training-compute-optimal-large-language-models/) (연산량 최적의 대규모 언어 모델 학습) — 컴퓨팅 예산을 현명하게 쓰는 법</content:encoded><category>Paper Reading</category><category>paper-reading</category><category>scaling-laws</category><category>AI</category><category>LLM</category><category>python</category></item><item><title>OpenClaw 특집: 아키텍처 분석 🦞</title><link>https://justinhuangai.github.io/ko/posts/openclaw-architecture/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko/posts/openclaw-architecture/</guid><description>v2026.3.8 소스코드 기반, 셀프호스팅 AI 어시스턴트의 엔지니어링 골격 해부</description><pubDate>Tue, 24 Feb 2026 08:37:11 GMT</pubDate><content:encoded>![OpenClaw](/images/openclaw-logo-text-dark.webp)

[지난 글](/ko/posts/openclaw-ecosystem/)에서 생태계를 다뤘으니, 이번에는 아키텍처를 해부해 보겠습니다.

OpenClaw의 코드베이스는 작지 않습니다 -- TypeScript 43만 줄입니다. 하지만 흥미로운 건 코드 줄 수가 아니라 아키텍처 설계입니다. 20개 이상의 채팅 플랫폼을 동시에 다루고, 여러 Agent를 관리하면서, 도구까지 실시간으로 호출해야 하는 AI 어시스턴트를 어떻게 하나의 시스템에 우겨넣되 무너지지 않게 만들 수 있을까요?

## 0. 먼저 몇 가지 용어부터

시스템 아키텍처에 익숙하지 않아도 괜찮다. 아래 6개 용어만 먼저 잡으면 뒤가 훨씬 수월해진다:

- `아키텍처`: 시스템이 어떻게 계층을 나누고 책임을 배치하는지
- `Channel / 채널 계층`: 외부 채팅 플랫폼마다 다른 프로토콜을 맞춰 주는 적응 계층
- `Gateway`: 메시지 라우팅, 세션, Agent를 관리하는 중앙 조정자
- `Node`: 실제로 명령을 실행하고 디바이스를 움직이는 실행 끝단
- `workspace / 워크스페이스`: Agent의 설정, 메모리, 스킬, 세션 파일이 모여 있는 로컬 폴더
- `sandbox / 샌드박스`: 위험한 작업을 격리해서 실행하는 제한된 환경

## 1. 3계층 아키텍처: Channel -&gt; Gateway -&gt; Node

먼저 전체 그림을 봅시다:

![OpenClaw 아키텍처](/images/openclaw-architecture-ko.svg)

OpenClaw을 3개 부서를 가진 회사라고 생각해 보세요:

| 구성 요소 | 역할 | 비유 |
|-----------|------|------|
| **Channel (어댑터 계층)** | WhatsApp, Telegram, Discord, Feishu 등 20개 이상의 채팅 플랫폼에 연결 | 프론트 데스크 -- 전화 받고, 우편 수령하고, 방문객 맞이 |
| **Gateway** | 중앙 디스패치; 모든 세션과 Agent를 관리 | 두뇌 -- 누구의 메시지를 어디로 보내고 어떻게 답할지 결정 |
| **Node (실행 노드)** | 디바이스에서 실제 작업 수행 -- 사진 촬영, 화면 캡처, 명령 실행 | 손과 발 -- 두뇌가 &quot;사진 찍어&quot;라고 하면 Node가 가서 실행 |

전체 시스템의 핵심은 Gateway입니다 -- 기본적으로 localhost(`127.0.0.1:18789`)에서만 수신 대기하는 장기 실행 프로세스로, 절대 공개 인터넷에 노출되지 않습니다. 원격 접속이 필요하면 Tailscale 터널을 사용하세요. 포트를 직접 열지 마세요. 이런 설계를 &quot;Loopback-First&quot;라고 하는데, 기발한 보안 전략입니다: 열린 포트가 0개면 공격 면적도 0입니다.

왜 분산 구조 대신 단일 프로세스일까요? 이유는 현실적입니다: WhatsApp 프로토콜은 한 번에 하나의 디바이스만 온라인일 수 있습니다. 두 개의 프로세스를 띄우면 서로 싸웁니다. &quot;아키텍처적 정석&quot;을 위해 조정 로직을 잔뜩 쌓느니, 단일 프로세스로 처음부터 끝까지 처리하는 게 낫습니다. 대부분의 개인 사용자에게는 이것만으로도 충분합니다.

## 2. 메시지 하나의 여정

Telegram에서 OpenClaw에게 &quot;내일 베이징 날씨 어때?&quot;라고 보내봅시다. 이런 일이 벌어집니다:

**1단계: 접수.** Telegram 어댑터가 메시지를 수신하고, Telegram 형식의 데이터를 OpenClaw 내부의 통합 형식으로 변환합니다. 어느 플랫폼에서 온 메시지든, 변환 결과는 동일한 형태입니다.

**2단계: 신원 확인 및 라우팅.** 당신은 누구인가요? 이 Agent와 대화할 권한이 있나요? 모르는 사람이라면 먼저 페어링 코드가 전송되고, 소유자가 승인해야 합니다. 확인이 끝나면 라우팅 엔진이 어떤 Agent에게 메시지를 보낼지 결정합니다.

**3단계: 컨텍스트 조립.** 이전 대화 기록이 디스크에서 로드되고, Agent의 성격 정의(SOUL.md), 행동 규칙(AGENTS.md), 메모리 저장소에서 의미적으로 관련된 항목들이 함께 불러와집니다. 이 모든 것이 완전한 &quot;두뇌 브리핑&quot;으로 조립됩니다.

**4단계: LLM 질의.** 그 브리핑이 설정된 LLM 제공자에게 전송됩니다 -- Anthropic, OpenAI, DeepSeek, MiniMax, GLM, Qwen, Gemini 등. 모델이 토큰 단위로 스트리밍 응답을 보내옵니다.

**5단계: 도구 실행.** 모델이 &quot;날씨를 확인하려면 명령어를 실행해야 해&quot;라고 하면, 런타임이 요청을 가로채서 보안 정책에 따라 어디서 실행할지 결정합니다(관리자 명령은 직접 실행; 낯선 사람의 명령은 Docker 샌드박스에서 실행). 결과는 모델에 다시 전달되어 생성을 이어갑니다.

**6단계: 응답.** 최종 답변이 Telegram의 요구사항(글자 수 제한, Markdown 규칙 등)에 맞게 포맷되고, 필요하면 분할된 후 당신에게 전송됩니다. 대화 내용은 디스크에 기록됩니다.

이 전체 과정에서 유일하게 느린 부분은 4단계 -- 모델이 첫 토큰을 생성할 때까지 기다리는 것입니다. 나머지는 전부 밀리초 수준입니다.

## 3. Channel 어댑터 계층: 한 번 만들고 어디서든 대화

OpenClaw은 20개 이상의 채팅 플랫폼과 연동되지만, 각 플랫폼마다 처음부터 끝까지 새로 구현하지 않습니다. &quot;어댑터&quot; 계층을 추상화했고, 각 플랫폼의 어댑터는 딱 네 가지만 책임집니다:

- **로그인**: WhatsApp은 QR 코드 스캔, Telegram은 Bot Token, iMessage는 macOS 네이티브 기능 사용 -- 플랫폼마다 각자의 방식
- **수신 메시지 변환**: 텍스트, 이미지, 답장, 리액션 -- 전부 내부 통합 형식으로 변환
- **접근 제어**: 누가 DM할 수 있는지, 그룹에서 @멘션할 때만 답하는지, 어떤 그룹이 허용되는지
- **발신 응답 변환**: Agent의 응답을 각 플랫폼이 표시할 수 있는 형식으로 변환

6개 주요 플랫폼은 기본 내장(WhatsApp, Telegram, Discord, Slack, Signal, iMessage)이고, 30개 이상(Feishu, LINE, Matrix, Mattermost 등)은 플러그인으로 연결됩니다.

이 추상화의 최대 이점: Agent는 메시지가 어느 플랫폼에서 왔는지 알 필요가 없습니다. WhatsApp에서 대화하는 Agent와 Telegram에서 대화하는 Agent는 같은 Agent입니다 -- 동일한 로직, 완전한 재사용. Channel은 그저 확성기일 뿐, 확성기를 바꿔도 말하는 사람은 같습니다.

## 4. Agent 런타임과 워크스페이스

Agent 런타임의 핵심은 Pi-mono(오픈소스 코딩 Agent)에서 왔으며, Gateway 프로세스에 직접 내장되어 있습니다.

### &quot;모든 것이 텍스트 파일&quot; 워크스페이스

OpenClaw에서 가장 매력적인 설계 중 하나입니다 -- Agent의 모든 설정이 직접 열어서 편집할 수 있는 일반 텍스트 파일입니다:

```
workspace/
├── AGENTS.md          # Agent가 누구이고 어떻게 동작하는지 정의 (이력서 + 사원 핸드북)
├── SOUL.md            # 영혼 정의 -- 성격, 가치관 (설정하고 내버려두세요)
├── USER.md            # 사용자 프로필 -- 이름, 선호도
├── MEMORY.md          # 장기 기억 -- Agent가 능동적으로 메모한 중요한 것들
├── HEARTBEAT.md       # 예약 작업 -- 예: 매일 아침 날씨 알림
├── memory/            # 일기장
│   └── YYYY-MM-DD.md  # 하루에 한 페이지, 추가만 가능
├── skills/            # 스킬 팩
└── sessions.json      # 세션 기록
```

데이터베이스도 없고, 전용 관리 패널도 없습니다 -- 그냥 일반 텍스트 파일입니다. Agent의 성격을 바꾸고 싶으면? SOUL.md를 열고 몇 줄 수정하세요. 뭘 기억하는지 보고 싶으면? MEMORY.md를 열어서 읽으세요. 이런 &quot;Agent의 뇌를 통째로 들여다볼 수 있는&quot; 투명성은 대부분의 폐쇄형 AI 제품이 제공하지 못하는 것입니다.

### 대화 한 턴의 실행 과정

1. **발신자 식별**: 관리자와의 DM인지, 친구의 DM인지, 그룹에서 @멘션한 것인지? 출처에 따라 보안 수준이 다름
2. **메모리 조립**: 대화 기록 로드, 성격과 규칙 로드, 관련 장기 기억 검색, 전부 하나의 완전한 컨텍스트로 조합
3. **LLM 질의**: 설정된 모델에 전송, 폴백 지원 -- 주 모델이 다운되면 자동으로 백업 모델로 전환
4. **실행 및 기억**: 도구 호출 실행, 대화 로그 업데이트

컨텍스트 조립은 모든 것을 무차별적으로 쏟아붓는 게 아닙니다. 관련 없는 스킬은 로드하지 않고, 불필요한 도구는 주입하지 않습니다 -- 토큰을 아끼는 것이 곧 비용을 아끼는 것입니다.

## 5. 4단계 메모리: AI가 정말로 당신을 기억하게 만들기

대부분의 챗봇은 창을 닫으면 당신을 잊어버립니다. OpenClaw은 그렇지 않습니다. 가장 깊은 &quot;나는 누구인가&quot;부터 가장 얕은 &quot;방금 뭘 이야기했지?&quot;까지, 4단계 메모리가 있습니다:

| 계층 | 내용 | 비유 |
|------|------|------|
| **SOUL** | 성격 정의, 절대 변하지 않음 | 당신의 성격 -- 태어날 때부터 결정된 것 |
| **TOOLS** | 현재 설치된 스킬 | 오늘 가져온 도구들 |
| **USER** | 당신에 대한 장기 기억 | 좋아하는 음식을 기억하는 오랜 친구 |
| **Session** | 현재 대화 | 지금 이야기하고 있는 주제 |

몇 가지 기발한 메커니즘이 있습니다:

**데일리 일기.** 매일의 대화가 자동으로 `memory/2026-03-12.md`에 기록됩니다(추가만 가능, 덮어쓰기 불가). 다음 대화가 시작되면, Agent가 자동으로 오늘과 어제의 일기를 훑어보며 연속성을 유지합니다 -- &quot;어제 말한 그 책, 결국 샀어?&quot;

**자동 메모리 구출.** 대화가 길어져서 컨텍스트 윈도우가 거의 찰 때는 어떻게 할까요? OpenClaw이 백그라운드에서 눈에 보이지 않는 턴을 실행해 핵심 정보를 MEMORY.md에 저장한 뒤, 오래된 내용을 압축합니다. 당신은 이 과정을 눈치채지 못하지만, 핵심 사실은 보존됩니다. 이 메커니즘을 Pre-Compaction이라고 부릅니다.

**시맨틱 검색.** &quot;전에 이야기한 배포 문제 있잖아&quot;라고 말하면, 실제로 기억에서 찾아낼 수 있습니다 -- 정확한 키워드 매칭이 아니라 의미를 이해해서 찾습니다. 내부적으로는 벡터 검색(SQLite-vec)과 전통적인 키워드 검색(BM25)의 이중 접근 방식을 사용합니다.

**크로스 플랫폼 아이덴티티.** Telegram에서 30분 대화한 다음 WhatsApp으로 옮겨서 계속해도 -- 당신을 알아봅니다. 같은 사람의 여러 플랫폼 ID가 하나의 아이덴티티로 연결되어 같은 메모리를 공유합니다. 다만 그룹 채팅 메모리는 격리됩니다 -- 그룹에서 한 말이 개인 대화로 흘러가지 않습니다.

## 6. 도구 시스템: 칼 딱 4자루

OpenClaw에서 가장 &quot;반항적인&quot; 설계 선택입니다.

다른 AI Agent 프레임워크들은 수백 개의 내장 도구를 넣으려 합니다. OpenClaw은 딱 네 개만 제공합니다:

| 도구 | 한 줄 설명 |
|------|-----------|
| **Read** | 파일 읽기 |
| **Write** | 파일 쓰기 |
| **Edit** | 파일 수정 |
| **Bash** | 명령어 실행 |

끝입니다. 진짜로요.

창립자의 논리: 커맨드 라인(Bash)이 있으면 뭐든 할 수 있습니다. 날씨 확인? `curl`로 가져오면 됩니다. 이메일 보내기? CLI 도구를 호출하면 됩니다. 데이터베이스 쿼리? `psql`이면 충분합니다. 모든 시나리오마다 전용 도구를 미리 만들어 둘 필요가 없습니다.

이것이 Unix 철학입니다 -- 작은 도구, 조합 가능, 텍스트 스트림. 트레이드오프는? 어떤 명령을 실행할지 스스로 파악할 수 있을 만큼 똑똑한 모델이 필요합니다. 그래서 OpenClaw은 Claude Opus급 모델을 권장합니다. 약한 모델로는 어려울 수 있습니다.

이 네 가지 핵심 도구 위에 55개의 내장 Skills과 ClawHub 스킬 마켓플레이스가 있습니다. Skills은 설치하고 제거할 수 있습니다 -- Agent용 앱이라고 생각하면 됩니다.

**여기서 자극적인 부분: OpenClaw은 의도적으로 MCP를 지원하지 않습니다.** MCP는 Anthropic의 도구 프로토콜 표준으로, 전 세계의 AI 프레임워크가 앞다투어 도입하고 있습니다. OpenClaw은 거부합니다. Peter의 정확한 발언: &quot;MCP는 쓰레기야, 확장이 안 돼. 뭐가 확장되는지 알아? CLI. Unix.&quot; 대안은 내장된 `mcporter` 브리지입니다.

**더 재미있는 건 자기 확장입니다.** OpenClaw Agent가 못 하는 일을 만나면, 스킬을 직접 작성한 다음 자동으로 설치합니다. 스킬에서 버그를 발견하면? 고치고 다시 로드합니다. 이는 Agent가 사용할수록 점점 강해진다는 뜻입니다 -- 본질적으로 스스로를 키우고 있는 셈입니다.

## 7. Multi-Agent 라우팅: 하나의 두뇌, 여러 인격

하나의 Gateway에서 여러 Agent를 동시에 실행할 수 있으며, 각각 독립적으로 작동합니다. 라우팅 규칙은 이렇게 생겼습니다:

```json
{
  &quot;bindings&quot;: [
    { &quot;agentId&quot;: &quot;home&quot;, &quot;match&quot;: { &quot;channel&quot;: &quot;whatsapp&quot;, &quot;accountId&quot;: &quot;personal&quot; } },
    { &quot;agentId&quot;: &quot;work&quot;, &quot;match&quot;: { &quot;channel&quot;: &quot;slack&quot; } },
    { &quot;agentId&quot;: &quot;bot&quot;, &quot;match&quot;: { &quot;channel&quot;: &quot;discord&quot;, &quot;guildId&quot;: &quot;123456&quot; } }
  ]
}
```

쉽게 말하면: 개인 WhatsApp에서 온 메시지는 &quot;홈 어시스턴트&quot;로, Slack 메시지는 &quot;업무 어시스턴트&quot;로, 특정 Discord 서버는 &quot;커뮤니티 봇&quot;으로 갑니다. 세 Agent는 완전히 격리되어 있습니다 -- 각각 고유한 성격, 기억, 스킬, 보안 정책을 가집니다.

## 8. 보안: 3개의 문

당신의 Agent는 당신의 서버에서 돌아갑니다. 명령어를 실행하고 파일을 읽고 쓸 수 있으니 -- 보안이 당연히 중요합니다. OpenClaw의 보안 모델에는 세 개의 문이 있습니다:

**첫 번째 문: 당신은 누구? (DM 페어링)** 모르는 사람이 당신의 Agent에게 메시지를 보내면, Agent가 그냥 답하지 않습니다. 6자리 페어링 코드를 보내고, 당신이 이미 인증된 채널을 통해 확인한 후에야 그 사람이 상호작용할 수 있습니다. 이것이 기본 동작입니다 -- 끄면 당신의 번호를 아는 아무나 API 크레딧을 공짜로 태울 수 있습니다.

**두 번째 문: VIP 통로 (허용 목록).** 신뢰할 수 있는 사람은 허용 목록(`allowFrom`)에 직접 추가하여 페어링 없이 바로 대화할 수 있습니다.

**세 번째 문: 끼어들지 마 (그룹 규칙).** 그룹 채팅에서 Agent는 기본적으로 @멘션이 있을 때만 답합니다 -- 모든 메시지에 불쑥 나타나지 않습니다. 토큰을 아끼고 모두를 귀찮게 하지 않기 위해서입니다.

이 아래에는 심층 방어 계층이 있습니다: 5단계 도구 권한 필터링, Docker 샌드박스 격리(모르는 사람의 명령은 샌드박스에서 실행), 보안 감사 명령(`openclaw security audit`). 이 계층들은 독립적입니다 -- 페어링 코드가 뚫렸다고요? 샌드박스가 여전히 있습니다. 샌드박스가 우회됐다고요? 도구 정책이 호출 가능한 것을 여전히 제한합니다.

## 9. 정리

**단일 프로세스는 게으름이 아니라 실용주의입니다.** 개인 사용자에게는 하나의 프로세스가 모든 것을 처리하는 게 분산 구조보다 훨씬 안정적입니다. 한계점은? 아마 동시 메시지 처리량이 단일 머신의 능력을 초과할 때일 텐데 -- 대다수의 사람에게 그 날은 절대 오지 않을 겁니다.

**Channel 추상화 계층이 가장 가치 있는 계층입니다.** 20개 이상 플랫폼의 특이사항이 어댑터에 완전히 캡슐화되어 있어, Agent는 메시지가 어디서 왔는지 신경 쓰지 않습니다. 새 플랫폼을 추가하고 싶으면? 어댑터를 작성하면 됩니다 -- Agent 로직 변경은 제로입니다. 디커플링이 매우 깔끔합니다.

**보안 설계는 진지하지만, 구현에는 아직 빈틈이 있습니다.** 아키텍처적으로 신원 확인, 샌드박싱, 도구 정책이 심층 방어를 구성합니다. 하지만 Kaspersky 감사에서 512개의 취약점(치명적 8개)이 발견되었으며, 이는 훌륭한 청사진과 실제 보안 사이의 거리가 지속적인 엔지니어링 노력으로 측정된다는 것을 보여줍니다.

**4개 핵심 도구 미니멀리즘은 하나의 베팅입니다.** 모델 능력이 계속 올라갈 것이라는 베팅입니다 -- 충분히 강력해서 미리 만든 도구가 필요 없고 &quot;모든 것은 Bash로 가능&quot;하다는 것입니다. LLM 능력이 정체되면 이 경로는 힘들어집니다. 하지만 모델 지능이 계속 상승하면, 이것이 가장 우아한 접근법일 수도 있습니다.

**궁극적인 시험은 아키텍처가 얼마나 예쁜지가 아니라, 얼마나 안정적으로 돌아가느냐입니다.** 이 분석은 정적 소스코드 리딩에 기반합니다. 실제 성능 -- 고동시성에서 Gateway의 안정성, 샌드박스가 실제로 공격을 견디는지, 크로스 채널 아이덴티티 연결의 엣지 케이스 -- 은 더 많은 프로덕션 데이터로 검증이 필요합니다.

아키텍처는 골격일 뿐입니다. 프로덕션이 진짜 시험입니다.</content:encoded><category>OpenClaw</category><category>AI</category><category>open-source</category><category>openclaw</category></item><item><title>논문 읽기: 《Language Models are Few-Shot Learners》 (언어 모델은 퓨샷 학습자다)</title><link>https://justinhuangai.github.io/ko/posts/language-models-are-few-shot-learners/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko/posts/language-models-are-few-shot-learners/</guid><description>더 큰 모델, 컨텍스트에서 더 잘 능력을 이끌어내다, 실제 Python 코드 예시 포함</description><pubDate>Wed, 11 Feb 2026 08:22:54 GMT</pubDate><content:encoded>2020년 5월 28일, OpenAI는 75페이지짜리 논문을 arXiv(연구자들이 학술지 심사를 거치지 않고 논문을 공개할 수 있는 프리프린트 서버)에 업로드했다: [《Language Models are Few-Shot Learners》](/papers/2005.14165v4.pdf) (언어 모델은 퓨샷 학습자다).

저자는 총 31명이고, 전원 OpenAI 소속이다. 제1저자는 Tom B. Brown이며, 주요 공저자로는 Jared Kaplan(scaling laws의 핵심 연구자), Alec Radford(GPT-1과 GPT-2의 주요 설계자), Ilya Sutskever(OpenAI 공동 창립자 겸 수석 과학자), Dario Amodei(OpenAI 연구 부사장)가 있다.

그 저자 목록은 이후 세계에서 가장 중요한 AI 기업들로 갈라지게 된다. Dario Amodei와 Jared Kaplan은 OpenAI를 떠나 Anthropic을 설립했고, Ilya Sutskever는 나중에 Safe Superintelligence Inc.(SSI)를 공동 창립했다.

논문의 핵심 주장은 간단하다: 언어 모델을 1,750억 개의 파라미터까지 확장하면, 가중치를 전혀 업데이트하지 않고도 — 단지 소수의 예시만으로 — 광범위한 작업을 수행할 수 있으며, 때로는 특별히 fine-tuning된 모델의 성능에 근접한다.

이것은 태스크 수준의 fine-tuning이 아니다. 고정된 파라미터로 순전히 컨텍스트만을 통해 추론 시점에 태스크에 적응하는 능력이다. 논문은 이를 **in-context learning**이라고 부른다.

## 0. 먼저 몇 가지 용어부터

GPT-3 같은 모델이 어떻게 작동하는지 아직 감이 없다면, 아래 용어만 먼저 잡아두면 충분하다:

- `언어 모델`: 앞에 주어진 문맥을 보고 다음 단어를 예측하는 모델이다.
- `파라미터 수`: 모델 안에서 학습되는 숫자의 총량이다. 대략 모델의 &quot;두뇌 용량&quot;이라고 생각해도 된다.
- `prompt / 프롬프트`: 모델에게 보여 주는 작업 설명, 예시, 입력 전체를 말한다.
- `context window / 컨텍스트 윈도우`: 모델이 한 번에 읽을 수 있는 텍스트의 최대 길이다.
- `few-shot / one-shot / zero-shot`: 예시를 여러 개 주는 경우, 하나만 주는 경우, 아예 주지 않는 경우를 각각 뜻한다.
- `in-context learning`: 파라미터를 바꾸지 않고, 프롬프트 안의 설명과 예시만으로 모델이 임시로 작업 방식을 익히는 현상이다.

## 1. 문제 정의

[BERT](/ko/posts/bert/)가 확립한 &quot;사전 학습 + fine-tuning&quot; 패러다임은 2020년에 이미 주류였다. 잘 작동했지만, 논문은 세 가지 근본적인 문제를 지적했다.

첫째, 모든 새로운 태스크에는 여전히 레이블이 붙은 데이터셋이 필요하다. 레이블 데이터는 수집 비용이 비싸고, 많은 실제 태스크에는 대응하는 레이블 데이터셋이 아예 존재하지 않는다.

둘째, fine-tuning된 모델이 테스트 벤치마크에서 보이는 성능이 반드시 진정한 일반화를 반영하는 것은 아니다. 모델이 단순히 학습 데이터의 허위 상관관계(spurious correlation)를 학습했을 수 있다 — 벤치마크에서는 높은 점수를 얻지만 분포 변화(distribution shift) 앞에서는 무너지는 것이다.

셋째, 인간은 이런 식으로 학습하지 않는다. 인간은 한두 가지 예시를 보고, 자연어 지시를 듣고, 새로운 태스크를 처리할 수 있다. 그 시절의 NLP 시스템은 새로운 태스크마다 수천 개의 레이블 샘플로 fine-tuning해야 했다.

논문의 출발점: 모델이 충분히 크다면, 사전 학습 동안 축적한 지식으로 태스크 설명과 몇 가지 예시를 직접 &quot;읽고&quot; 답을 생성할 수 있을까?

## 2. 핵심 아이디어: 파라미터 업데이트 없이, 프롬프트만으로

GPT-3의 평가 방법론은 이전의 모든 대형 모델과 달랐다. 그래디언트 업데이트가 전혀 없는 세 가지 설정을 정의했다:

**Few-Shot**: 모델에 태스크 설명과 10~100개의 예시(정확한 수는 컨텍스트 윈도우에 맞는 양에 따라 다름)를 제공한 뒤, 새로운 입력을 완성하게 한다. 가중치 업데이트도 없고, 역전파도 없다.

**One-Shot**: 예시를 딱 하나만 제공한다. 인간이 새로운 태스크를 학습하는 방식과 가장 비슷하다 — 누군가 한 번 시범을 보이면, 그다음부터는 스스로 해내는 것이다.

**Zero-Shot**: 예시가 전혀 없고, 자연어 지시만 있다. 가장 어려운 설정이지만, 가장 실용적이기도 하다 — 모델이 태스크 자체를 진정으로 &quot;이해&quot;한다면 예시가 필요 없어야 한다.

```python
from dataclasses import dataclass
from typing import Union


@dataclass
class ZeroShot:
    instruction: str
    prompt: str


@dataclass
class OneShot:
    instruction: str
    example: tuple[str, str]
    prompt: str


@dataclass
class FewShot:
    instruction: str
    examples: list[tuple[str, str]]
    prompt: str


EvalSetting = Union[ZeroShot, OneShot, FewShot]


def build_prompt(setting: EvalSetting) -&gt; str:
    if isinstance(setting, ZeroShot):
        return f&quot;{setting.instruction}\n{setting.prompt}&quot;

    if isinstance(setting, OneShot):
        example_input, example_output = setting.example
        return f&quot;{setting.instruction}\n{example_input} {example_output}\n{setting.prompt}&quot;

    lines = [setting.instruction]
    lines.extend(f&quot;{example_input} {example_output}&quot; for example_input, example_output in setting.examples)
    lines.append(setting.prompt)
    return &quot;\n&quot;.join(lines)
```

논문은 이 능력을 **in-context learning**이라고 부른다: 사전 학습 과정에서 모델이 방대한 텍스트로부터 다양한 태스크 패턴을 암묵적으로 학습하고, 추론 시점에 예시들이 컨텍스트에 이어 붙여지면 모델이 순방향 패스 중에 현재 태스크를 &quot;인식&quot;하고 수행한다. 논문은 이 과정을 &quot;메타 학습&quot;의 언어로 설명한다 — 사전 학습이 외부 루프이고, in-context learning이 내부 루프이다.

Fine-tuning과의 차이는 근본적이다. Fine-tuning은 태스크에 맞추기 위해 모델 파라미터를 수정한다. In-context learning은 아무것도 수정하지 않는다 — 같은 모델, 같은 가중치, 순전히 입력 텍스트만 바꿔서 태스크를 전환한다.

## 3. 모델 아키텍처와 규모

GPT-3의 아키텍처는 새로운 발명이 아니다. GPT-2와 마찬가지로, [Transformer](/ko/posts/attention-is-all-you-need/)의 디코더 부분만을 층층이 쌓은 것이다. 유일한 변경점은 Transformer 레이어 내에서 밀집 어텐션과 로컬 밴드 희소 어텐션(Sparse Transformer에서 차용)을 번갈아 사용한 것이다.

진정한 차이는 규모에 있다. 논문은 파라미터 수가 세 자릿수에 걸쳐 분포된 8개 모델을 학습시켰다:

| 모델 | 파라미터 | 레이어 | 히든 크기 | 어텐션 헤드 |
|-------|-----------|--------|-------------|-----------------|
| GPT-3 Small | 125M | 12 | 768 | 12 |
| GPT-3 Medium | 350M | 24 | 1024 | 16 |
| GPT-3 Large | 760M | 24 | 1536 | 16 |
| GPT-3 XL | 1.3B | 24 | 2048 | 24 |
| GPT-3 2.7B | 2.7B | 32 | 2560 | 32 |
| GPT-3 6.7B | 6.7B | 32 | 4096 | 32 |
| GPT-3 13B | 13B | 40 | 5140 | 40 |
| **GPT-3 175B** | **175B** | **96** | **12288** | **96** |

1,750억 개의 파라미터, 96개의 레이어, 96개의 어텐션 헤드, 히든 차원 12288. 컨텍스트 윈도우 2048 토큰. 이 규모는 당시 전례가 없었다 — GPT-2의 15억 파라미터보다 100배 이상 크다.

```python
from dataclasses import dataclass


@dataclass(frozen=True)
class GPT3Config:
    n_params: int
    n_layers: int
    d_model: int
    n_heads: int
    d_head: int
    d_ff: int
    n_ctx: int


def gpt3_175b() -&gt; GPT3Config:
    return GPT3Config(
        n_params=175_000_000_000,
        n_layers=96,
        d_model=12_288,
        n_heads=96,
        d_head=128,
        d_ff=49_152,
        n_ctx=2_048,
    )
```

이 모델들을 학습시킨 목적은 명확했다: scaling laws를 검증하는 것이다. Kaplan 등(이 논문의 공저자 중 한 명)의 이전 연구에서 언어 모델의 손실과 파라미터 수 사이에 매끄러운 멱법칙(power-law) 관계가 있음을 이미 보여줬다. GPT-3는 그 가설을 1,750억 파라미터까지 밀어붙여 in-context learning 능력도 같은 패턴을 따르는지 확인했다.

답은 &quot;그렇다&quot;였다: 모델이 클수록 few-shot 학습의 개선 폭이 더 가팔랐다. Zero-shot 성능은 규모에 따라 꾸준히 상승하고, few-shot 성능은 더 빠르게 상승한다. 이는 더 큰 모델이 단순히 &quot;더 정확한&quot; 것이 아니라, 컨텍스트 정보를 활용하는 데도 더 효율적이라는 뜻이다.

## 4. 학습 데이터

GPT-3는 5개 출처에서 수집한 약 3,000억 개의 토큰으로 학습되었다:

| 데이터셋 | 토큰 수 | 학습 비중 |
|---------|--------|--------------|
| Common Crawl (필터링 후) | 410B | ~60% |
| WebText2 | 19B | ~22% |
| Books1 | 12B | ~8% |
| Books2 | 55B | ~8% |
| English Wikipedia | 3B | ~3% |

주목할 점이 있다: 샘플링 비율은 데이터셋 크기에 비례하지 않는다. 고품질 데이터셋(WebText2, Books, Wikipedia)이 오버샘플링되었다 — WebText2는 학습 중 2.9회, Wikipedia는 3.4회 반복 사용된 반면, Common Crawl은 전체를 한 번도 다 보지 못했다(0.44 에폭). 논문은 의도적으로 약간의 오버피팅을 감수하고 더 높은 품질의 학습 신호를 택했다.

원시 Common Crawl 데이터는 45TB였다. 세 단계의 처리를 거쳤다: (1) 고품질 참조 말뭉치와의 유사도를 기반으로 필터링; (2) 문서 수준의 퍼지 중복 제거; (3) 다양성을 위해 알려진 고품질 데이터셋을 혼합. 필터링 후 570GB가 남았다 — 대략 4,100억 토큰이다.

모든 모델은 Microsoft가 제공한 고대역폭 클러스터의 V100 GPU에서 학습되었다.

## 5. 실험 결과

논문은 20개 이상의 데이터셋에 걸쳐 9가지 주요 태스크 범주를 평가했다. 몇 가지 핵심 결과를 정리하면 다음과 같다.

**언어 모델링**: Penn Tree Bank에서 GPT-3 few-shot 퍼플렉서티(모델이 텍스트에 얼마나 &quot;놀라는지&quot;를 측정한 것 — 낮을수록 좋다)는 20.50으로 새로운 기록을 세웠다. LAMBADA(장거리 컨텍스트를 기반으로 마지막 단어를 예측하는 태스크)에서 zero-shot 정확도는 76.2%, few-shot은 86.4%로 이전 최고 기록을 크게 앞섰다.

**번역**: GPT-3는 번역을 위해 특별히 학습된 적이 없지만, 프랑스어-영어 few-shot BLEU 점수가 32.6으로 최고의 비지도 신경 기계 번역 결과를 넘어섰다. 반면 영어-프랑스어(25.2 BLEU)는 fine-tuning된 모델에 비해 여전히 상당히 뒤처졌다. 흥미로운 발견이 있었다: GPT-3는 영어로 번역하는 것이 영어에서 다른 언어로 번역하는 것보다 눈에 띄게 잘했는데, 이는 학습 데이터의 영어 편중을 직접적으로 반영한다.

**Closed-Book QA**: TriviaQA에서 few-shot 정확도(exact match)는 71.2%로, 같은 closed-book 설정 하에서 fine-tuning된 모델을 넘어섰다. 모델은 어떤 문서도 참조하지 않고 — 파라미터에 저장된 지식만으로 답한다.

**SuperGLUE**: 이 종합 벤치마크에서 GPT-3의 few-shot 성능은 일부 강력한 fine-tuning 베이스라인에 근접했지만, 당시 가장 강력한 전용 fine-tuning 시스템에는 여전히 미치지 못했다.

**합성 태스크**: 논문은 in-context learning을 테스트하기 위해 새로운 태스크도 설계했다. 예를 들어, &quot;만들어낸 단어&quot;의 예시 몇 개를 제공하면(존재하지 않는 단어를 정의하고 문장에서 사용하는 것), GPT-3는 새 단어를 올바르게 학습하고 사용할 수 있었다. 세 자릿수 덧셈은 few-shot에서 거의 100% 정확했고(두 자릿수도 거의 완벽했다), 네 자릿수와 다섯 자릿수에서는 정확도가 급격히 떨어졌다.

```python
from typing import Callable, Protocol


class AutoregressiveModel(Protocol):
    def forward(self, tokens: list[int]) -&gt; list[list[float]]:
        ...


def in_context_learning(
    model: AutoregressiveModel,
    examples: list[tuple[str, str]],
    query: str,
    tokenize: Callable[[str], list[int]],
    decode: Callable[[list[int]], str],
    sample_from: Callable[[list[float]], int],
    eos_token: int,
) -&gt; str:
    prompt_lines = [f&quot;{example_input} {example_output}&quot; for example_input, example_output in examples]
    prompt_lines.append(query)
    prompt = &quot;\n&quot;.join(prompt_lines)

    context = tokenize(prompt)
    output_tokens: list[int] = []

    while True:
        logits = model.forward(context)
        next_token = sample_from(logits[-1])
        if next_token == eos_token:
            break
        output_tokens.append(next_token)
        context.append(next_token)

    return decode(output_tokens)
```

## 6. 데이터 오염

논문은 섹션 4에서 까다로운 문제에 상당한 지면을 할애한다: 학습 데이터와 테스트 데이터의 중복.

GPT-3의 학습 데이터에는 방대한 양의 인터넷 텍스트가 포함되어 있고, 많은 테스트 벤치마크가 인터넷에 공개되어 있다. 이는 모델이 학습 중에 테스트 문제를 &quot;본&quot; 적이 있을 수 있다는 뜻이다. 팀은 학습 전에 이러한 중복을 제거하려 했지만, 처리 파이프라인의 버그로 인해 일부 중복이 완전히 정리되지 않았다. 처음부터 다시 학습시키는 것은 비용상 현실적이지 않았다.

그들의 접근 방식: 각 벤치마크에 대해 &quot;클린 서브셋&quot;(학습 데이터와의 13-gram 중복이 있는 모든 샘플을 제거)을 구성한 뒤, 전체 세트와 클린 서브셋에서의 모델 성능을 비교했다. 결론: 대부분의 벤치마크에서 오염이 결과에 미친 영향은 미미했다. 다만 PIQA와 Winograd는 의심스러운 성능 하락을 보였고, 논문은 해당 결과에 별표를 표시했다.

이 정도의 정직함은 당시로서는 상당히 드물었다. 대부분의 논문은 데이터 오염에 대한 논의를 아예 회피한다. GPT-3는 이 문제를 능동적으로 조사했을 뿐만 아니라 체계적인 탐지 도구까지 개발했다. 그 자체가 후속 연구에 대한 기여이다.

## 7. 한계

논문 섹션 5의 한계에 대한 논의는 상당히 솔직하다.

**텍스트 일관성**: GPT-3는 문서 수준에서 여전히 의미 반복, 자기 모순, 심지어 말이 안 되는 문장을 보인다. 생성 품질은 GPT-2보다 훨씬 나아졌지만, 긴 글에서의 일관성은 여전히 부족하다.

**상식 물리**: GPT-3는 &quot;치즈를 냉장고에 넣으면 녹을까?&quot;와 같은 상식 물리 질문에서 성능이 떨어진다. 언어적 추론은 가능하지만, 물리 세계에 대한 이해는 여전히 피상적이다.

**단방향성의 비용**: 자기회귀 모델인 GPT-3는 왼쪽에서 오른쪽으로만 볼 수 있다. 논문은 양방향 컨텍스트가 필요한 태스크(예: 두 문장에서 같은 단어가 같은 의미를 갖는지 판별하는 것)에서 GPT-3의 few-shot 성능이 fine-tuning된 양방향 모델에 미치지 못한다고 인정한다. 이는 이러한 태스크가 자기회귀 설정에서 GPT-3의 강점이 아님을 보여준다; 단방향 모델링 목표가 구조적 편향을 도입한다.

**샘플 효율성**: GPT-3는 사전 학습 중 약 3,000억 개의 토큰을 봤는데, 이는 인간이 평생 접하는 텍스트 양을 훨씬 넘는다. 논문은 few-shot 학습이 추론 시점에서는 효율적이지만, 사전 학습을 위한 데이터 요구량은 여전히 막대하다고 명시적으로 언급한다.

**추론 비용**: 1,750억 파라미터 모델은 실행 비용이 높고 배포가 어렵다. 논문은 디스틸레이션(대형 모델의 출력을 사용해 소형 모델을 학습시키는 것)을 가능한 방향으로 언급하지만, 천억 파라미터 규모에서는 아직 시도되지 않았다고 밝힌다.

## 8. 사회적 영향

논문은 섹션 6 전체를 사회적 영향에 할애하며, 세 가지 영역을 다룬다.

**오용 위험**: 인간 평가자는 GPT-3가 생성한 뉴스 기사를 우연 수준(~52% 정확도)으로밖에 식별하지 못했다. 모델이 강력할수록 생성된 텍스트를 탐지하기가 더 어렵다. 팀은 악의적 사용의 동향을 추적하기 위해 포럼과 채팅 그룹을 모니터링하고 있다고 보고했다.

**편향**: 논문은 GPT-3의 성별, 인종, 종교에 걸친 편향을 테스트하는 광범위한 실험을 수행했다. 예를 들어, 직업-성별 연관 테스트에서 GPT-3는 &quot;간호사&quot;를 여성과, &quot;은행가&quot;를 남성과 연관시키는 경향이 더 강했다. 종교-감정 연관에서는 &quot;이슬람&quot;이 폭력 관련 단어와 더 자주 동시 출현했다. 논문은 이러한 편향이 학습 데이터에서 비롯됨을 인정하지만 해결책은 제시하지 않는다.

**에너지 소비**: GPT-3 학습에는 막대한 컴퓨팅이 필요하며, 논문은 추정치를 인용하되 구체적인 에너지 수치는 공개하지 않는다. 다만 한 번 학습된 모델은 여러 태스크에 적용할 수 있어, 각 태스크마다 별도의 모델을 학습시키는 것보다 에너지 효율이 높다고 지적한다.

## 9. 내 생각

이 논문을 읽고 나서, 몇 가지가 눈에 띈다.

첫째, GPT-3는 중요한 것을 증명했다: 규모가 in-context learning을 실용성의 문턱 너머로 밀어붙일 수 있다는 것이다. 1,750억 파라미터 모델은 단순히 &quot;더 큰 GPT-2&quot;가 아니다 — in-context learning 성능이 더 작은 모델들을 한 자릿수 이상 넘어선다. 모델은 파라미터 업데이트 없이, 오로지 컨텍스트 속 소수의 예시에 의존해 새로운 태스크를 수행한다. 이 능력은 명시적으로 설계된 것이 아니라, 규모가 커지면서 점차 나타났고, GPT-3의 규모에서야 비로소 충분히 명확하고 실용적이 되었다. BERT가 사전 학습의 가치를 증명했다면, GPT-3는 규모의 가치를 증명했다.

둘째, 논문의 서술 방식이 주목할 만하다. 31명의 저자, 75페이지, 방대한 수의 실험을 투입해 하나의 단순한 질문에 답한다: 더 큰 모델이 소수의 예시를 더 잘 활용하는가? 한계를 회피하지 않았다 — 텍스트 일관성, 상식 추론, 데이터 오염, 편향 — 모두 정면으로 논의했다. 그 수준의 엄밀함은 아이러니하게도 이후의 대형 모델 논문들에서 점점 보기 드물어졌다.

셋째, 이 논문의 저자 목록은 AI 업계 분열의 역사를 읽는 것과 같다. Dario Amodei와 Jared Kaplan은 이후 Anthropic(Claude를 만든 회사)을 설립했고, Ilya Sutskever는 OpenAI를 떠나 SSI를 공동 창립했다. 2020년에 이 사람들은 아직 같은 팀에서 논문을 같이 쓰고 있었는데, 2년 안에 각기 다른 방향으로 갈라졌다. 논문의 사회적 영향과 안전 위험에 대한 논의는 이후의 그 불일치를 예고하는 것이었을지도 모른다.

넷째, 기술 진화의 관점에서, GPT-3는 &quot;사전 학습 + fine-tuning&quot;에서 &quot;사전 학습 + 프롬프트&quot;로의 전환점을 표시한다. BERT의 접근법은: 먼저 범용 지식을 배우고, 각 태스크에 맞게 파라미터를 fine-tuning한다. GPT-3는 말했다: 모델이 충분히 크면 fine-tuning 단계를 건너뛸 수 있다 — 자연어로 원하는 것을 모델에게 말하면 된다. 이 아이디어는 이후 ChatGPT나 Claude 같은 제품의 핵심 상호작용 패러다임으로 발전했다: 사용자가 자연어로 질문하면, 모델이 직접 답한다.

Seq2Seq의 인코드-디코드에서, [Bahdanau 어텐션](/ko/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/)의 &quot;어디를 볼 것인가&quot;로, [Transformer](/ko/posts/attention-is-all-you-need/)의 &quot;모든 곳을 한꺼번에 보기&quot;로, [BERT](/ko/posts/bert/)의 &quot;먼저 배우고, 그다음 fine-tuning&quot;으로, GPT-3의 &quot;fine-tuning이 필요 없을 때까지 키우기&quot;로 — 각 단계마다 인간의 개입 필요성은 줄어들고 모델이 스스로 태스크를 처리하는 능력은 늘어났다.

GPT-3는 종착점이 아니다. 하지만 사람들이 처음으로 진지하게 질문을 던진 순간이었다: 모델을 계속 키우면, 또 무엇이 나타날까?

그 질문에 대한 답이 바로 이후에 벌어진 모든 것이다.

---

**논문 읽기 시리즈**

- [《Sequence to Sequence Learning with Neural Networks》](/ko/posts/sequence-to-sequence-learning-with-neural-networks/) (신경망을 이용한 시퀀스-투-시퀀스 학습) — 인코더-디코더 패러다임의 확립
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/ko/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/) (정렬과 번역을 공동으로 학습하는 신경 기계 번역) — 어텐션의 기원
- [《Attention Is All You Need》](/ko/posts/attention-is-all-you-need/) (어텐션만 있으면 충분하다) — 어텐션이 주역이 되다: Transformer의 탄생
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/ko/posts/bert/) (BERT: 언어 이해를 위한 깊은 양방향 트랜스포머 사전학습) — 사전 학습 패러다임의 확립
- [《Scaling Laws for Neural Language Models》](/ko/posts/scaling-laws-for-neural-language-models/) (신경 언어 모델을 위한 스케일링 법칙) — 스케일의 수학: 왜 더 큰 모델이 예측 가능하게 더 좋은가
- [《Training Compute-Optimal Large Language Models》](/ko/posts/training-compute-optimal-large-language-models/) (연산량 최적의 대규모 언어 모델 학습) — 컴퓨팅 예산을 현명하게 쓰는 법</content:encoded><category>Paper Reading</category><category>paper-reading</category><category>gpt-3</category><category>AI</category><category>LLM</category><category>python</category></item><item><title>OpenClaw 특집: 생태계 분석 🦞</title><link>https://justinhuangai.github.io/ko/posts/openclaw-ecosystem/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko/posts/openclaw-ecosystem/</guid><description>하나의 오픈소스 프로젝트에서 완전한 AI 어시스턴트 생태계로</description><pubDate>Tue, 03 Feb 2026 08:09:32 GMT</pubDate><content:encoded>![OpenClaw](/images/openclaw-logo-text-dark.webp)

## 0. 먼저 몇 가지 용어부터

이런 생태계 분석 글이 처음이라면, 아래 5개 용어를 먼저 잡고 가면 좋다:

- `생태계`: 하나의 레포가 아니라, 같은 핵심 능력을 중심으로 여러 제품과 도구가 연결된 상태
- `런타임`: Agent가 실제로 돌아가며 작업을 조율하는 핵심 프로세스
- `스킬 마켓플레이스`: Agent 능력을 발견하고 설치하는 장소
- `워크플로우 엔진`: 반복적인 다단계 작업을 재사용 가능한 흐름으로 묶는 시스템
- `플라이휠`: 공급과 사용이 서로를 키우는 성장 루프

## 1. 사람부터 시작하자

프로젝트를 이해하려면 그 뒤에 있는 사람부터 봐야 한다.

Peter Steinberger, 오스트리아인. 2011년에 PSPDFKit을 창업해서 로우레벨 PDF 기술을 만들었다. Apple과 Dropbox이 고객이었고, 10억 대 이상의 기기에 서비스를 제공했다. 결과? 공개된 보도에 따르면 &quot;9자릿수 영역&quot;이었다. 그리고 그는 일선에서 물러났다.

그 뒤 번아웃이 왔다. 본인의 말을 빌리면: &quot;화면만 멍하니 쳐다보고, 코드를 한 줄도 못 썼다.&quot; 마드리드행 편도 티켓을 사서 한동안 완전히 세상과 단절했다.

2025년 중반, 블로그에 이렇게 썼다: 불꽃이 돌아왔다고. AI가 데모 단계를 넘어서 진짜 제품 프로토타입을 만들어낼 수 있게 됐다고. 약 한 시간 정도 프롬프팅을 해서 프로젝트의 뼈대를 만들고, 11월에 공개했다. 이름은 Clawdbot -- Anthropic의 Claude에서 따온 말장난(Claw = 집게발)이고, 마스코트는 랍스터다.

2026년 1월 말, Anthropic에서 상표권 경고가 날아왔다 -- 이름이 Claude랑 너무 비슷하다는 거였다. 3일 만에 Clawdbot에서 Moltbot(Molt = 탈피)으로, 다시 OpenClaw로 바뀌었다. 이 이름 변경 자체가 화제가 되면서 48시간 만에 스타가 3만 4천 개 늘었다.

9자릿수 실적을 낸 사람이 MIT 라이선스 오픈소스 프로젝트에 에너지를 쏟기로 결정했다. 동기가 뭐든 간에, 그 선택 자체만으로 진지하게 볼 가치가 있다.

## 2. 단순한 프로젝트가 아니다 -- 생태계를 키우고 있다

바이럴을 탄 오픈소스 프로젝트 대부분에서 &quot;생태계&quot;란 결국: 문서에 있는 로드맵 하나랑 껍데기뿐인 레포 몇 개다.

OpenClaw는 다르다. 실질적인 제품 레이어링이 이루어지고 있다:

| 컴포넌트 | 하는 일 | 스타 수 |
|-----------|---------|---------|
| **OpenClaw** | 코어 Agent 런타임 -- 두뇌 | 140k+ |
| **ClawHub** | 스킬 마켓플레이스 -- Agent용 App Store | 5.4k |
| **Lobster** | 워크플로우 엔진 -- 반복 작업을 원클릭 파이프라인으로 패키징 | ~800 |
| **acpx** | Headless CLI 도구 | ~780 |
| **openclaw-ansible** | 자동 배포 -- 명령어 하나로 전부 세팅 | ~490 |
| **nix-openclaw** | Nix 선언적 설정 | ~530 |

런타임, 스킬 마켓플레이스, 워크플로우 엔진, 배포 도구 -- 각각이 자기 역할을 하고, 책임 경계가 깔끔하다. 하나의 레포에 전부 쑤셔넣는 식의 조잡한 확장이 아니다. 설계된 레이어링이다.

## 3. 핵심 판단 몇 가지

### 채널 커버리지: 과시가 아니다 -- &quot;아무것도 안 해도 된다&quot;는 뜻이다

WhatsApp, Telegram, Slack, Discord, Signal, iMessage, Feishu, LINE, Matrix... 20개 이상의 플랫폼과 직접 연동된다.

이게 무슨 뜻이냐? 새 앱을 깔 필요 없다. 새 인터페이스를 배울 필요 없다. 어떤 습관도 바꿀 필요 없다. 이미 쓰고 있는 채팅에 그냥 나타난다. WhatsApp에서 메시지 보내면 끝 -- 친구한테 카톡하는 것처럼.

채널이 많다는 건 실제 사용 시나리오에 더 가깝다는 뜻이고, 첫 사용의 문턱이 낮다는 뜻이다. 이런 &quot;저마찰 접근&quot;은 단순한 편의가 아니라 압도적인 채택 우위다. 물론 내부적으로는 프로토콜 적응과 유지보수 비용이 있지만, 그건 대가일 뿐 핵심이 아니다. 핵심은 AI 어시스턴트를 &quot;따로 열어야 하는 새 도구&quot;에서 &quot;이미 채팅에 있는 능력&quot;으로 바꾼다는 것이다.

### ClawHub: 방향은 맞다, 아직 이르다

스킬 마켓플레이스는 벡터 검색으로 시맨틱 매칭을 한다 -- 카테고리 디렉토리를 뒤질 필요 없이 &quot;이메일 보내는 스킬 찾아줘&quot;라고 하면 알아서 찾아준다. 설계 의도는 훌륭하다.

하지만 스킬 마켓플레이스의 플라이휠이 정말 돌아가느냐는 두 가지에 달렸다: 충분한 양질의 스킬, 그리고 충분히 좋은 디스커버리. 후자는 이미 갖춰져 있다. 전자는? 서드파티 스킬 퍼블리싱 빈도, 설치량, 업데이트 활동, 리뷰 처리량 -- 이 중 어느 것도 공개 대시보드가 없다. 플라이휠은 돌기 시작했지만, 자생력까지는 멀었다.

### Lobster: 숨겨진 페인 포인트를 해결한다

AI Agent의 가장 큰 숨은 비용이 뭘까? 중복 계획 수립이다.

Agent한테 다단계 작업을 시킬 때마다 매번 처음부터 다시 생각한다: 뭘 먼저 할지, 다음엔 뭘 할지, 어떻게 할지. 이렇게 토큰이 타들어간다. Lobster의 접근법: 고빈도 작업을 원클릭 파이프라인으로 패키징 -- 한 번 만들고, 반복 호출하고, 재계획 불필요.

승인 게이트도 있다: 중요한 단계에서 일시 정지하고 사용자의 허가를 기다린다. Agent가 폭주하는 걸 방지하는 거다. 이건 팀이 Agent에게 얼마나 자율성을 줘야 하는지에 대해 적어도 진지하게 고민했다는 증거다.

### 보안: 피할 수 없는 관문

Kaspersky가 512개의 취약점을 감사했고, 그중 8개가 크리티컬이었다(감사는 아직 Clawdbot이던 시절에 이루어졌다). Cisco의 보안 팀은 대놓고 &quot;보안 악몽&quot;이라고 불렀다. Gary Marcus는 공개적으로 &quot;터지기를 기다리는 재앙&quot;이라고 했다.

문제는 구조적이다: Agent에게 권한을 많이 줄수록 할 수 있는 일이 많아지지만, 공격 표면도 그만큼 넓어진다. Prompt injection으로 나쁜 짓을 시킬 수 있다. 이미 외부 서버로 데이터를 빼돌리는 스킬이 적발된 적도 있다.

OpenClaw도 모르는 건 아니다 -- 페어링 코드, 허용 목록, 샌드박싱, 명령어 승인까지, 층층이 조여가고 있다. 하지만 &quot;고권한 Agent + 서드파티 스킬 + 20개 이상의 진입점&quot;이라는 구조적 긴장은 버그 패치 몇 개로 해결되지 않는다. 이건 장기전이다.

## 4. 리스크 -- 안 다룰 수가 없다

좋은 얘기는 여기까지. 이제 나쁜 얘기.

**보안 부채는 기술 부채가 아니다 -- 신뢰 부채다.** 대형 보안 사고 하나가 터지면 OpenClaw만 다치는 게 아니라, 셀프호스팅 AI Agent이라는 경로 위의 모든 프로젝트가 다친다. 내러티브 전체가 물 밑으로 끌려간다.

**비즈니스 모델이 물음표다.** MIT 라이선스, 구독 없음, 사용자가 자기 API 키를 가져온다. Peter는 프로젝트의 월간 서버 비용이 1~2만 달러 수준이라고 공개적으로 언급했다. 스폰서십으로 돌아가는 오픈소스 프로젝트 -- 지속 가능성은 화제가 돈으로 전환될 수 있느냐에 달렸다. 아직 명확한 경로는 없다.

**성장의 질이 의심스럽다.** 1월 30일의 폭발은 Moltbook(AI Agent 소셜 네트워크)의 바이럴 확산과 긴밀하게 연결되어 있었다. 바이럴로 유입된 스타 중 얼마나 많은 수가 실질적인 생태계 기여자가 될까? 스타 ≠ 코드 기여 ≠ 생태계 깊이.

**단일 지점 의존.** 18,000건 이상의 커밋이 있지만, 핵심 로드맵과 제품 판단은 여전히 한 사람 -- 창업자에게 크게 의존하고 있다. 커뮤니티에서 메인테이너가 합류했지만, &quot;한 사람의 프로젝트&quot;에서 &quot;커뮤니티의 플랫폼&quot;으로 갈 수 있느냐가 진짜 분수령이다.

## 5. 결론

OpenClaw에서 정말 희소한 건 화제성이 아니다 -- 화제성은 누구나 잠깐은 가질 수 있다. 희소한 건 이미 단일 프로젝트에서 생태계의 맹아로 자라났다는 것이다: 레이어링이 있고, 분업이 있고, 실질적인 제품 형태가 있다.

하지만 화제성은 결국 사그라든다.

물이 빠진 뒤에 운명을 결정하는 건 네 가지다:

1. **보안이 단단해졌는가?** 페어링, 허용 목록, 샌드박싱, 승인 게이트가 &quot;옵션&quot;에서 &quot;기본값&quot;으로 바뀌었는가?
2. **스킬 생태계가 돌아가고 있는가?** 스타 수는 잊어라 -- 양질의 스킬이 퍼블리싱되고, 설치되고, 리뷰되는 선순환이 생겼는가?
3. **창업자 외 기여 비율이 늘고 있는가?** 이것이 &quot;스타 프로젝트&quot;인지 &quot;지속 가능한 플랫폼&quot;인지를 결정한다.
4. **거버넌스 구조가 명확한가?** 이것이 화제성 주도 프로젝트에서 장기 인프라로 진화할 수 있는지를 결정한다.

계속 추적할 예정이다.</content:encoded><category>OpenClaw</category><category>AI</category><category>open-source</category><category>openclaw</category></item><item><title>논문 읽기: 《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》 (BERT: 언어 이해를 위한 깊은 양방향 트랜스포머 사전학습)</title><link>https://justinhuangai.github.io/ko/posts/bert/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko/posts/bert/</guid><description>사전학습 패러다임의 확립, 실제 Python 코드 예시 포함</description><pubDate>Sat, 31 Jan 2026 08:52:21 GMT</pubDate><content:encoded>2018년 10월 11일, Google AI Language 팀은 arXiv(연구자들이 학술지 동료 심사를 거치지 않고 논문을 게시할 수 있는 프리프린트 서버)에 한 편의 논문을 업로드했다: [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/papers/1810.04805v2.pdf) (BERT: 언어 이해를 위한 깊은 양방향 트랜스포머 사전학습).

저자는 Jacob Devlin, Ming-Wei Chang, Kenton Lee, Kristina Toutanova로, 모두 Google 소속이다. Devlin은 Google에 합류하기 전 Microsoft Research에서 근무했으며, Google에서 BERT의 설계와 구현을 주도했다.

BERT는 Bidirectional Encoder Representations from Transformers의 약자다. 당시로서는 상당히 대담한 시도를 했다: 먼저 대량의 레이블이 없는 텍스트로 범용 pre-training을 수행한 뒤, 출력 레이어 하나만 추가하고 특정 태스크에 fine-tuning하여 최첨단 성능을 달성한 것이다.

이 &quot;pre-train 후 fine-tune&quot; 패러다임은 이후 NLP 전체의 표준 접근 방식이 되었다. GPT 시리즈도 비슷한 아이디어를 따랐지만 다른 길을 택했다 — 단방향 생성이다. BERT는 양방향 이해를 선택했다. 두 갈래의 길은 각각 방대한 모델 계보를 탄생시켰다.

## 0. 먼저 몇 가지 용어부터

대형 언어 모델의 학습 흐름이 낯설다면, 이 논문에서 가장 중요한 아래 용어만 먼저 잡아두면 된다:

- `Transformer`: BERT가 사용하는 기본 아키텍처다. 문장의 좌우 문맥을 함께 보며 처리하는 기계라고 생각하면 된다.
- `pre-training / 사전 학습`: 특정 작업부터 배우는 것이 아니라, 방대한 일반 텍스트에서 언어 자체를 먼저 배우는 단계다.
- `fine-tuning / 파인튜닝`: 사전 학습으로 얻은 능력을 특정 작업에 맞게 조금 더 조정하는 단계다.
- `bidirectional / 양방향`: 어떤 위치를 이해할 때 왼쪽만 보는 것이 아니라 오른쪽도 함께 본다는 뜻이다.
- `MLM / Masked Language Model`: 일부 단어를 가리고, 주변 문맥만 보고 그 단어를 맞히게 하는 학습 방식이다.
- `NSP / Next Sentence Prediction`: 두 문장이 실제로 이어지는 문장인지 판단하게 하는 학습 방식이다.

## 1. 문제

2018년, NLP는 어색한 현실에 놓여 있었다: 모든 태스크마다 전용 모델 구조를 따로 설계해야 했다. 질의응답에는 하나의 모델이 필요하고, 감성 분석에는 또 다른 모델이, 개체명 인식에는 또 다른 모델이 필요했다. 각 태스크의 레이블 데이터는 부족했고, 하나의 태스크에서 학습한 모델을 다른 태스크로 전이하기도 어려웠다.

이미 pre-training 시도는 있었다. ELMo는 양방향 LSTM을 사용해 문맥 표현을 학습했지만, pre-training된 피처를 태스크별 아키텍처에 &quot;덧붙이는&quot; 방식이었을 뿐 — 아키텍처 자체는 여전히 태스크 전용이었다. OpenAI GPT는 Transformer를 사용해 pre-training과 fine-tuning을 수행했지만, 왼쪽에서 오른쪽으로만 볼 수 있었다(단방향) — 각 토큰은 자신보다 앞에 있는 토큰만 참조할 수 있었고, 뒤에 있는 토큰은 볼 수 없었다.

논문은 단방향 언어 모델이 깊은 양방향 문맥을 필요로 하는 언어 이해 태스크에서 상당한 한계가 있다고 주장했다. 예를 들어:

&gt; &quot;He picked up the _____ and started playing.&quot;

왼쪽 문맥(&quot;He picked up the&quot;)만 보면 답은 무엇이든 될 수 있다. 하지만 오른쪽 문맥(&quot;and started playing&quot;)을 보면, 어떤 악기라는 것을 바로 알 수 있다. 많은 언어 이해 태스크에서 양방향 문맥은 본질적으로 더 유리하다.

## 2. 핵심 아이디어: 단어 일부를 가리고 모델이 맞추게 하기

BERT의 해법은 직관적이다: 양방향 언어 모델을 전통적인 방식으로 학습시킬 수 없으므로(각 단어가 간접적으로 &quot;자기 자신을 볼&quot; 수 있게 되므로), 학습 목표를 바꾼다.

**Masked Language Model (MLM)**: 입력 토큰의 15%를 무작위로 마스킹한다 — 구체적으로는 특수 \[MASK\] 토큰으로 대체한다 — 그런 다음 모델이 문맥으로부터 마스킹된 단어를 예측하게 한다. 이 아이디어는 심리학의 Cloze 테스트(1953년 Taylor가 제안)에서 유래한 것으로, 위의 빈칸 채우기 문제와 같은 원리다.

마스킹 후 모델은 왼쪽과 오른쪽 문맥을 모두 사용해야 예측할 수 있으므로, 양방향 이해가 자연스럽게 나타난다.

그러나 선택된 모든 토큰을 \[MASK\]로 대체하면 문제가 생긴다: \[MASK\]는 fine-tuning 중에는 나타나지 않으므로, pre-training과 fine-tuning 사이에 불일치가 발생한다. 논문의 해법: 선택된 15%의 토큰 중, 80%는 \[MASK\]로 대체하고, 10%는 임의의 토큰으로 대체하며, 10%는 그대로 둔다. 이렇게 하면 모델은 단순히 &quot;\[MASK\]가 보이니 예측해야 한다&quot;에 의존할 수 없고, 모든 위치에서 이해를 유지해야 한다.

```python
import random
from typing import Optional, Sequence


def mask_tokens(
    tokens: Sequence[str],
    mask_prob: float = 0.15,
    vocab: Optional[Sequence[str]] = None,
) -&gt; tuple[list[str], list[int], list[str]]:
    if vocab is None:
        vocab = tokens

    masked = list(tokens)
    positions: list[int] = []
    labels: list[str] = []

    for i, token in enumerate(tokens):
        if random.random() &lt; mask_prob:
            positions.append(i)
            labels.append(token)

            r = random.random()
            if r &lt; 0.8:
                masked[i] = &quot;[MASK]&quot;
            elif r &lt; 0.9:
                masked[i] = random.choice(vocab)

    return masked, positions, labels
```

## 3. 두 번째 Pre-training 태스크: Next Sentence Prediction

많은 NLP 태스크(질의응답, 자연어 추론 등)는 두 문장 간의 관계를 이해해야 하지만, 언어 모델은 그런 관계를 직접 모델링하지 않는다.

논문은 두 번째 pre-training 태스크를 추가했다: **Next Sentence Prediction (NSP)**. 모델에 두 문장 A와 B가 주어지고 — 50%의 확률로 B는 A 다음에 실제로 오는 문장이며, 50%의 확률로 B는 코퍼스에서 무작위로 추출된 문장이다. 모델은 B가 실제로 A 다음에 오는 문장인지 판단해야 한다.

태스크 설계 자체는 단순하지만, 논문의 ablation 실험(구성 요소를 하나씩 제거하며 영향을 관찰하는 방법)에서 NSP를 제거하면 질의응답과 자연어 추론 태스크의 성능이 눈에 띄게 하락하는 것으로 나타났다. 다만, 이후 연구(RoBERTa 등)에서는 NSP의 필요성에 대해 다른 결론에 도달했다.

```python
from dataclasses import dataclass


@dataclass
class PretrainingExample:
    tokens: list[str]
    segment_ids: list[int]
    masked_positions: list[int]
    masked_labels: list[str]
    is_next: bool
```

## 4. 모델 아키텍처

BERT의 아키텍처는 새로운 발명이 아니다. 단순히 [Transformer](/ko/posts/attention-is-all-you-need/)의 인코더 부분을 레이어별로 쌓은 것이다.

논문은 두 가지 크기를 명시한다:

- **BERT_BASE**: 12 레이어, hidden size 768, 12 attention head, 110M 파라미터
- **BERT_LARGE**: 24 레이어, hidden size 1024, 16 attention head, 340M 파라미터

BERT_BASE는 OpenAI GPT와 대략 같은 파라미터 수를 가지므로 직접 비교가 가능하다. 둘 사이의 가장 핵심적인 차이는 딱 하나다: GPT는 단방향 attention을 사용하고(각 토큰은 왼쪽 토큰만 볼 수 있음), BERT는 양방향 attention을 사용한다(각 토큰이 모든 위치를 볼 수 있음).

입력 표현은 세 가지 구성 요소의 합이다:

- **Token Embedding**: WordPiece 토크나이제이션, 30,000 어휘
- **Segment Embedding**: 토큰이 문장 A에 속하는지 문장 B에 속하는지 표시
- **Position Embedding**: 모델에 각 토큰의 위치를 알려줌 (BERT는 사인파가 아닌 학습된 position embedding을 사용)

모든 입력 시퀀스는 특수 \[CLS\] 토큰으로 시작하며, 이 토큰의 최종 레이어 hidden state가 문장 수준 분류(예: NSP, 감성 분석)에 사용된다. 두 문장은 \[SEP\]로 구분한다.

```python
import torch
from torch import nn


class BertEmbeddings(nn.Module):
    def __init__(
        self,
        vocab_size: int,
        hidden_size: int,
        max_positions: int,
        type_vocab_size: int = 2,
        dropout: float = 0.1,
    ) -&gt; None:
        super().__init__()
        self.token_embedding = nn.Embedding(vocab_size, hidden_size)
        self.segment_embedding = nn.Embedding(type_vocab_size, hidden_size)
        self.position_embedding = nn.Embedding(max_positions, hidden_size)
        self.layer_norm = nn.LayerNorm(hidden_size, eps=1e-12)
        self.dropout = nn.Dropout(dropout)

    def forward(
        self,
        token_ids: torch.Tensor,
        segment_ids: torch.Tensor,
    ) -&gt; torch.Tensor:
        position_ids = torch.arange(token_ids.size(1), device=token_ids.device).unsqueeze(0)
        position_ids = position_ids.expand_as(token_ids)
        embeddings = (
            self.token_embedding(token_ids)
            + self.segment_embedding(segment_ids)
            + self.position_embedding(position_ids)
        )
        embeddings = self.layer_norm(embeddings)
        return self.dropout(embeddings)


class BertModel(nn.Module):
    def __init__(
        self,
        vocab_size: int,
        hidden_size: int = 768,
        max_positions: int = 512,
        num_layers: int = 12,
        num_heads: int = 12,
        dropout: float = 0.1,
    ) -&gt; None:
        super().__init__()
        self.embeddings = BertEmbeddings(vocab_size, hidden_size, max_positions, dropout=dropout)
        encoder_layer = nn.TransformerEncoderLayer(
            d_model=hidden_size,
            nhead=num_heads,
            dim_feedforward=4 * hidden_size,
            dropout=dropout,
            activation=&quot;gelu&quot;,
            batch_first=True,
        )
        self.encoder = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)

    def forward(
        self,
        token_ids: torch.Tensor,
        segment_ids: torch.Tensor,
    ) -&gt; torch.Tensor:
        hidden = self.embeddings(token_ids, segment_ids)
        return self.encoder(hidden)
```

## 5. Fine-tuning: 하나의 모델로 모든 태스크를

BERT의 가장 우아한 점은 fine-tuning의 단순함이다. Pre-training이 완료되면, 다운스트림 태스크가 무엇이든 절차는 거의 동일하다: BERT 위에 태스크별 출력 레이어 하나를 추가한 뒤, 소량의 레이블 데이터로 전체 파라미터를 fine-tuning한다.

- **텍스트 분류** (감성 분석, 자연어 추론): \[CLS\] 위치의 출력 벡터를 선형 분류기에 입력
- **질의응답** (주어진 문단에서 답의 범위 찾기): 각 토큰의 출력 벡터에 두 개의 선형 변환을 적용하여 답의 시작과 끝 위치를 예측
- **시퀀스 레이블링** (개체명 인식): 각 토큰의 출력 벡터에 분류기를 부착하여 토큰별로 레이블을 예측

Pre-training에는 며칠이 걸릴 수 있지만, fine-tuning은 보통 수 분에서 수 시간이면 된다(대부분의 태스크가 TPU 한 대에서 1시간 이내). 이 효율성 차이가 &quot;pre-train + fine-tune&quot; 패러다임의 핵심 매력이다.

## 6. 실험 결과

논문은 11개의 NLP 태스크에서 실험을 수행했으며, 모든 태스크에서 새로운 기록을 세웠다.

**GLUE 벤치마크** (General Language Understanding Evaluation, 8개 하위 태스크):
- BERT_LARGE 평균 80.5%, 이전 최고(OpenAI GPT) 대비 7.7 퍼센트 포인트 향상
- 가장 큰 하위 태스크인 MNLI에서 4.6% 향상

**SQuAD v1.1** (독해 질의응답, Test F1):
- BERT_LARGE 단일 모델 + TriviaQA 데이터: F1 91.8, 인간 성능(91.2) 초과
- BERT_LARGE 앙상블 + TriviaQA 데이터: F1 93.2

**SQuAD v2.0** (답할 수 없는 질문 포함):
- F1 83.1, 이전 최고 시스템 대비 5.1 포인트 향상

**SWAG** (상식 추론):
- 정확도 86.3%, OpenAI GPT 대비 8.3 포인트 향상

논문은 또한 모델 크기에 대한 ablation 실험을 수행했으며, 중요한 결론을 도출했다: 더 큰 모델이 모든 태스크에서 더 좋은 성능을 보였으며, 레이블 데이터가 매우 적은 태스크(최소 3,600개)에서도 마찬가지였다. 이는 작은 데이터셋에서 큰 모델이 과적합(학습 데이터를 암기하여 새로운 데이터에서 성능이 저하되는 현상)할 것이라는 당시의 직관에 반하는 결과로, pre-training된 지식이 이 위험을 효과적으로 완화한다는 것을 시사했다.

## 7. 학습 상세

**Pre-training 데이터**: BooksCorpus (8억 단어) + 영어 Wikipedia (25억 단어), 텍스트 본문만 사용하고 목록, 표, 헤더는 제외. 논문은 장거리 문맥 관계를 포착하기 위해 문장 수준으로 셔플된 코퍼스가 아닌 문서 수준 코퍼스를 사용하는 것의 중요성을 강조했다.

**토크나이제이션**: WordPiece, 어휘 크기 30,000. WordPiece는 흔하지 않은 단어를 더 작은 서브워드 단위로 분할한다 — 예를 들어, &quot;playing&quot;은 &quot;play&quot; + &quot;##ing&quot;으로 분할될 수 있다.

**옵티마이저**: Adam, 학습률 1e-4, 처음 10,000 스텝에 걸쳐 선형 warmup 후 선형 감쇠. 배치 크기 256 시퀀스, 최대 시퀀스 길이 512.

**하드웨어**: BERT_BASE는 Cloud TPU 4대(TPU 칩 16개)에서 4일간 학습. BERT_LARGE는 Cloud TPU 16대(TPU 칩 64개)에서 4일간 학습.

**Dropout**: 모든 레이어에서 0.1. 활성화 함수는 원래 Transformer의 ReLU 대신 GELU (Gaussian Error Linear Unit)를 사용.

## 8. 나의 소감

이 논문을 읽고 나서 몇 가지 인상적인 점이 있다.

첫째, BERT의 진정한 기여는 모델 아키텍처(단순히 Transformer 인코더에 불과하다)가 아니라 학습 방법에 있다. Masked language model 아이디어는 단순해 보이지만, 모델이 &quot;치팅&quot;하지 않으면서 양방향 문맥을 활용하는 근본적인 모순을 우아하게 해결한다. 80/10/10 마스킹 전략은 더욱 정교하게 설계되어 pre-training과 fine-tuning 사이의 불일치를 해소한다.

둘째, BERT와 GPT의 갈림길은 이 논문에서 이미 명확하다. GPT의 autoregressive 목표는 생성에 더 자연스럽게 적합하고, BERT의 양방향 인코딩은 판별적 언어 이해 태스크에 더 적합하다. GPT는 이후 더 강력한 생성 능력 쪽으로 스케일업했고, BERT는 RoBERTa, ALBERT, DeBERTa를 포함한 이해 중심 모델 계보를 탄생시켰다. 두 계열 모두 각자의 영역에서 계속 활약하고 있다.

셋째, &quot;pre-train + fine-tune&quot; 패러다임의 영향은 NLP를 훨씬 넘어선다. 컴퓨터 비전도 이후 같은 접근 방식으로 전면 전환했고(ViT, MAE), 멀티모달 모델(CLIP, GPT-4V)까지도 대규모 pre-training에 fine-tuning이나 프롬프팅을 결합하는 방식을 기반으로 한다. BERT가 pre-training을 최초로 시도한 것은 아니지만, 이렇게 간결한 방식으로 pre-training을 유용한 기법에서 NLP의 주류 작업 패러다임으로 끌어올린 것은 BERT가 처음이었다.

넷째, BERT의 입력 처리를 실제 Python으로 다시 쓰면 설계가 얼마나 깔끔한지 체감할 수 있다. \[CLS\] + 문장 A + \[SEP\] + 문장 B + \[SEP\], 세 가지 임베딩을 합산 — 이 하나의 파이프라인으로 분류, 질의응답, 시퀀스 레이블링을 통합된 코드베이스 하나로 처리할 수 있다. 이 &quot;하나의 모델로 모든 태스크를&quot; 이라는 단순함이야말로 진정한 강점이다.

이 논문의 제목에서 가장 중요한 단어가 하나 있다: Pre-training. BERT 이전에는 모든 NLP 태스크가 처음부터 학습하고 있었다. BERT는 한 가지를 증명했다: 언어에 대한 범용 지식을 먼저 학습한 뒤, 거의 모든 태스크로 전이할 수 있다는 것이다.

그 아이디어가 한 분야 전체의 작동 방식을 바꿨다.

---

**논문 읽기 시리즈**

- [《Sequence to Sequence Learning with Neural Networks》](/ko/posts/sequence-to-sequence-learning-with-neural-networks/) (신경망을 이용한 시퀀스-투-시퀀스 학습) — 인코더-디코더 패러다임의 확립
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/ko/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/) (정렬과 번역을 공동으로 학습하는 신경 기계 번역) — Attention의 기원
- [《Attention Is All You Need》](/ko/posts/attention-is-all-you-need/) (어텐션만 있으면 충분하다) — Attention이 주역이 되다: Transformer의 탄생
- [《Scaling Laws for Neural Language Models》](/ko/posts/scaling-laws-for-neural-language-models/) (신경 언어 모델을 위한 스케일링 법칙) — 스케일의 수학: 왜 더 큰 모델이 예측 가능하게 더 좋은가
- [《Language Models are Few-Shot Learners》](/ko/posts/language-models-are-few-shot-learners/) (언어 모델은 퓨샷 학습자다) — 더 큰 모델, 문맥에서 더 잘 이끌어내는 능력
- [《Training Compute-Optimal Large Language Models》](/ko/posts/training-compute-optimal-large-language-models/) (연산량 최적의 대규모 언어 모델 학습) — 컴퓨팅 예산을 현명하게 쓰는 법</content:encoded><category>Paper Reading</category><category>paper-reading</category><category>bert</category><category>AI</category><category>LLM</category><category>python</category></item><item><title>논문 읽기: 《Sequence to Sequence Learning with Neural Networks》 (신경망을 이용한 시퀀스-투-시퀀스 학습)</title><link>https://justinhuangai.github.io/ko/posts/sequence-to-sequence-learning-with-neural-networks/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko/posts/sequence-to-sequence-learning-with-neural-networks/</guid><description>인코더-디코더 패러다임의 확립, 실제 Python 코드 예시 포함</description><pubDate>Sat, 24 Jan 2026 08:41:08 GMT</pubDate><content:encoded>2014년 9월 10일, 세 명의 Google 연구자가 arXiv(연구자들이 저널 심사를 거치지 않고 논문을 공개할 수 있는 프리프린트 서버)에 한 편의 논문을 올렸다: [《Sequence to Sequence Learning with Neural Networks》](/papers/1409.3215v3.pdf) (신경망을 이용한 시퀀스-투-시퀀스 학습).

저자는 Ilya Sutskever, Oriol Vinyals, Quoc V. Le로, 모두 Google 소속이었다. Sutskever는 AlexNet의 공동 저자로, Alex Krizhevsky, Geoffrey Hinton과 함께 딥러닝 혁명의 불을 지핀 논문을 작성했으며, 이후 OpenAI의 공동 창립자가 되었다. Vinyals는 이후 DeepMind에서 AlphaStar(DeepMind의 StarCraft AI)를 이끌었고, Quoc V. Le는 Google에서 AutoML 등의 연구를 주도했다.

이 논문이 한 일은 겉보기에 놀라울 정도로 단순하다: 하나의 신경망으로 문장을 읽어 벡터로 압축하고, 또 다른 신경망으로 그 벡터에서 번역을 생성한다. 입력과 출력은 길이도, 언어도, 구조도 다를 수 있다. 이 프레임워크에는 이름이 있다: &quot;Sequence to Sequence&quot; (Seq2Seq).

이것이 encoder-decoder 패러다임을 확립했다. 이후 [Bahdanau가 여기에 attention을 추가했고](/ko/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/), [Vaswani 등이 Transformer로 아키텍처 전체를 새로 썼다](/ko/posts/attention-is-all-you-need/). 하지만 출발점은 이 논문이었다.

## 0. 먼저 몇 가지 용어부터

머신러닝 배경이 없다면, 이 논문의 작업 흐름에 맞춰 아래 용어부터 익혀 두면 된다:

- `Seq2Seq / 시퀀스-투-시퀀스`: 하나의 입력 시퀀스를 다른 출력 시퀀스로 직접 바꾸는 방식이다. 예를 들어 영어 문장을 프랑스어 문장으로 바꾸는 식이다.
- `encoder`: 입력을 처음부터 끝까지 읽는 부분이다.
- `decoder`: 출력을 한 단어씩 써 내려가는 부분이다.
- `RNN / 순환 신경망`: 텍스트를 순서대로만 처리할 수 있는 오래된 구조다.
- `LSTM`: RNN의 개선형으로, 긴 문장에서 앞쪽 정보를 더 오래 기억하도록 만든 구조다.
- `vector / 벡터`: 일단은 &quot;숫자들로 압축해 둔 요약본&quot; 정도로 이해하면 충분하다.

## 1. 문제

2014년까지 심층 신경망은 이미지 인식 같은 과제에서 이미 돌파구를 마련했지만, 기계 번역처럼 가변 길이의 시퀀스를 또 다른 가변 길이의 시퀀스로 직접 매핑하는 과제에서는 여전히 고전하고 있었다.

영어 문장이 5단어일 때 프랑스어 번역은 7단어가 될 수 있다. 입력과 출력의 길이가 다르고, 단순한 일대일 대응이 존재하지 않는다.

기존의 해결책은 대량의 수작업 규칙과 통계적 특징을 조합해 복잡한 번역 파이프라인(Statistical Machine Translation, SMT)을 구성하는 것이었다. 작동은 했지만 각 구성 요소를 따로 튜닝해야 했고, end-to-end 최적화가 어려웠다.

이 논문은 더 단순한 아이디어를 제안했다: 하나의 end-to-end 신경망으로 소스 언어 시퀀스에서 타깃 언어 시퀀스로 직접 매핑할 수 있지 않을까?

## 2. 핵심 아키텍처: Encoder-Decoder

이 논문의 접근법은 한 문장으로 요약된다: **하나의 LSTM이 읽고, 또 하나의 LSTM이 쓴다.**

LSTM(Long Short-Term Memory)은 장기 의존성을 처리하기 위해 설계된 특수한 RNN이다. 표준 RNN은 시퀀스가 길어질수록 앞부분의 내용을 &quot;잊어버리는&quot; 경향이 있는데, LSTM은 게이팅 메커니즘을 통해 어떤 정보를 유지하고 어떤 정보를 버릴지 결정함으로써 이를 완화한다.

구체적인 워크플로우:

1. **Encoder**(4층 deep LSTM)가 소스 문장을 처음부터 끝까지 읽고, 문장 전체를 고정 길이의 최종 상태 세트로 압축하여 decoder의 시작점으로 넘긴다
2. **Decoder**(또 다른 4층 deep LSTM)가 이 상태에서 출발하여 타깃 언어 번역을 한 단어씩 생성하고, 문장 끝 기호 \&lt;EOS\&gt;를 출력하면 멈춘다

논문에 나오는 확률 공식:

$$
p(y_1, \ldots, y_{T&apos;} \mid x_1, \ldots, x_T) = \prod_t p(y_t \mid v, y_1, \ldots, y_{t-1})
$$

쉽게 말하면: 소스 문장 x가 주어졌을 때, 타깃 문장 y를 생성할 확률은 매 단계에서 다음 단어를 생성할 확률의 곱이다. 각 단계의 예측은 두 가지에 의존한다: encoder가 압축한 벡터 v, 그리고 이전까지 생성된 모든 단어.

```python
import torch
from torch import nn


class Seq2Seq(nn.Module):
    def __init__(self, vocab_size: int, hidden_size: int) -&gt; None:
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, hidden_size)
        self.encoder = nn.LSTM(hidden_size, hidden_size, num_layers=4, batch_first=True)
        self.decoder = nn.LSTM(hidden_size, hidden_size, num_layers=4, batch_first=True)
        self.output_proj = nn.Linear(hidden_size, vocab_size)

    def encode(
        self,
        source_tokens: torch.Tensor,
    ) -&gt; tuple[torch.Tensor, tuple[torch.Tensor, torch.Tensor]]:
        embedded = self.embedding(source_tokens)
        outputs, state = self.encoder(embedded)
        return outputs, state

    def decode(
        self,
        encoder_state: tuple[torch.Tensor, torch.Tensor],
        max_steps: int,
        bos_token_id: int,
        eos_token_id: int,
    ) -&gt; list[int]:
        prev_token = torch.tensor([[bos_token_id]], dtype=torch.long, device=encoder_state[0].device)
        state = encoder_state
        generated: list[int] = []

        for _ in range(max_steps):
            embedded = self.embedding(prev_token)
            output, state = self.decoder(embedded, state)
            logits = self.output_proj(output[:, -1, :])
            next_token_id = int(logits.argmax(dim=-1).item())
            if next_token_id == eos_token_id:
                break
            generated.append(next_token_id)
            prev_token = torch.tensor([[next_token_id]], dtype=torch.long, device=logits.device)

        return generated
```

아키텍처 자체는 복잡하지 않다. 이 논문의 기여는 새로운 구성 요소를 발명한 것이 아니라, 이 단순한 프레임워크가 실제로 작동한다는 것을 -- 그것도 정교하게 튜닝된 전통적 시스템과 경쟁할 만큼 잘 작동한다는 것을 증명한 데 있다.

## 3. 세 가지 핵심 설계 결정

논문은 성능에 큰 영향을 미치는 세 가지 설계 선택을 밝혔다:

**첫째, 두 개의 별도 LSTM을 사용한다.** Encoder와 decoder가 파라미터를 공유하지 않는다. 이로 인해 파라미터 수가 약간 늘어나지만, 모델이 소스 언어와 타깃 언어의 서로 다른 특성을 더 잘 처리할 수 있게 된다. 논문은 또한 이를 통해 여러 언어 쌍을 동시에 학습시키는 것도 가능하다고 언급했다.

**둘째, deep LSTM을 사용한다.** 논문은 4층 LSTM을 사용했으며, 층을 하나 추가할 때마다 perplexity가 거의 10%씩 감소했다. 얕은 LSTM(1~2층)은 성능이 현저히 떨어졌다. 깊이는 모델에 더 큰 표현 공간을 제공한다.

**셋째, 소스 문장을 뒤집는다.** 이것이 논문에서 가장 놀라운 발견이었다. 소스 문장 &quot;a, b, c&quot;를 &quot;c, b, a&quot;로 뒤집어 encoder에 입력하자 BLEU 점수가 25.9에서 30.6으로 -- 약 5포인트나 올랐다.

왜 뒤집기가 도움이 될까? 논문의 설명: 정순서에서는 소스 문장의 첫 번째 단어가 타깃 문장의 첫 번째 단어와 멀리 떨어져 있다(소스 문장 전체가 그 사이에 놓인다). 뒤집으면 소스와 타깃 문장의 앞부분 단어들이 시간적으로 가까워져 gradient(모델이 파라미터를 조정하는 데 사용하는 신호)에 더 많은 &quot;단거리 의존성&quot;이 만들어지고, 이것이 최적화를 쉽게 만든다.

```python
import torch


def reverse_source(source_tokens: list[int]) -&gt; list[int]:
    return list(reversed(source_tokens))


source_sentence = [11, 23, 37, 42]
reversed_source = reverse_source(source_sentence)
source_tensor = torch.tensor([reversed_source], dtype=torch.long)
```

이 트릭은 너무 단순해서 정당한 연구 기여처럼 보이지 않을 정도지만, 실제로 효과가 있었고, 더 깊은 문제를 드러냈다: RNN은 시퀀스 내 요소 간 거리에 민감하다 -- 가까울수록 배우기 쉽다. 이 문제는 이후 attention 메커니즘에 의해 근본적으로 해결되었다.

## 4. 실험 결과

논문은 WMT &apos;14 영어-프랑스어 번역 과제에서 실험을 수행했다.

주요 수치:
- **단일 reversed LSTM**, beam size 12: 30.59 BLEU
- **5개 reversed LSTM 앙상블**, beam size 2: 34.50 BLEU
- **5개 reversed LSTM 앙상블**, beam size 12: **34.81 BLEU**
- **기존 구(句) 기반 번역 시스템** (Moses baseline): 33.30 BLEU

논문이 보고한 실험 설정에서, 5개 LSTM 앙상블이 기존 구 기반 시스템을 34.81 대 33.30으로 앞질렀다. LSTM의 어휘가 80,000단어에 불과하고(어휘 밖의 단어는 UNK로 출력) 기존 시스템의 어휘는 사실상 무제한이었다는 점을 고려하면, 이 결과는 상당히 인상적이다.

논문은 또한 LSTM을 사용해 기존 시스템의 1000-best 후보 목록을 재순위화하여 BLEU 점수를 36.5까지 끌어올렸으며, 이는 당시 발표된 최고 결과(37.0)에 근접한 수치였다.

또 하나 주목할 만한 발견: 당시의 다른 신경망 기법과 비교했을 때, LSTM은 긴 문장에서의 성능 저하가 덜 심했다. 이는 다른 연구자들이 보고한 가파른 긴 문장 성능 하락과 대조적이었으며, 논문은 이를 소스 뒤집기 전략 덕분이라고 설명했다.

## 5. 모델이 &quot;이해하는&quot; 것

논문은 흥미로운 시각화 실험도 수행했다. 다양한 문장을 encoder에 입력하고 최종 hidden state 벡터를 추출한 뒤, PCA로 2차원 평면에 투영했다.

결과:
- 의미가 유사한 문장들이 벡터 공간에서 가까이 모였다
- 능동태와 수동태 문장(&quot;I gave her a card&quot; vs &quot;I was given a card by her&quot;)이 인접한 위치에 나타났다
- 어순이 다르지만 의미가 같은 문장들도 올바르게 군집화되었다

이는 최소한 encoder가 학습한 표현이 단순한 bag-of-words 통계(단어들을 순서에 상관없이 뒤섞는 것)를 넘어서 상당한 양의 구문 및 의미 정보를 담고 있음을 시사한다.

## 6. 학습 상세

**모델 사양**: 4층 LSTM, 층당 1000 유닛, 단어 임베딩 차원 1000, 총 파라미터 수 3억 8400만 개. 이 중 6400만 개는 순수 순환 연결 파라미터이다.

**하드웨어**: GPU 8개. LSTM 층당 GPU 1개, 나머지 4개는 softmax 병렬화에 사용(어휘 80,000단어로 인해 softmax 연산이 무거움). 학습에 약 10일이 소요되었다.

**Optimizer**: 모멘텀 없는 SGD, 초기 학습률 0.7. 5 에포크 이후 반 에포크마다 학습률을 절반으로 줄이며, 총 7.5 에포크 학습.

**Gradient clipping**: gradient의 L2 norm이 임계값 5를 초과하면 비례적으로 축소. 이는 gradient explosion(gradient 값이 갑자기 극단적으로 커져 파라미터 업데이트가 엉망이 되는 현상)을 방지한다.

**배치 최적화**: 비슷한 길이의 문장을 같은 배치에 묶어, 짧은 문장이 긴 문장을 &quot;기다리며&quot; 연산 자원을 낭비하는 것을 방지. 이로 인해 학습 속도가 2배 향상되었다.

## 7. 나의 소감

이 논문을 읽고 몇 가지가 눈에 띈다.

첫째, 이 논문은 야심은 컸지만 방법은 단순했다. 하나의 LSTM이 읽고, 또 하나의 LSTM이 쓰고, 모든 정보는 그 사이의 단일 벡터를 통과한다. Attention도 없고, 복잡한 정렬 메커니즘도 없으며, 언어 구조에 대한 사전 가정조차 없다. 그런데 실제로 작동했고, 정교하게 튜닝된 전통 시스템과 경쟁할 만한 결과를 냈다. 교훈: 충분한 데이터와 연산 자원이 주어지면, 단순한 end-to-end 방법이 놀라울 만큼 강력할 수 있다.

둘째, 소스 뒤집기 발견은 상당히 시사적이다. 우아한 해결책은 아니다 -- 오히려 hack에 가깝다. 하지만 RNN의 근본적 한계를 드러냈다: 시퀀스 내 요소 간 거리에 대한 민감성. Bahdanau의 attention 메커니즘은 모델이 &quot;건너뛰며 볼 수&quot; 있게 해서 더 이상 거리에 구속되지 않게 만들었다. Transformer는 여기서 더 나아가 순차 처리를 완전히 포기하여, 어떤 두 위치 간의 거리든 항상 1로 만들었다. 뒤집기에서 attention으로, attention에서 Transformer로 -- 같은 문제에 대한 세 세대의 해결책이다.

셋째, 이 논문과 Bahdanau의 논문은 거의 동시에 발표되었다(둘 다 2014년 9월). Sutskever가 encoder-decoder 패러다임을 확립했고, Bahdanau가 고정 길이 벡터 병목을 발견하고 attention 메커니즘으로 해결했다. 두 논문은 동전의 양면과 같다: 하나는 프레임워크이고, 다른 하나는 그 프레임워크의 가장 큰 결함에 대한 수정이다.

넷째, 이것을 실제 Python으로 다시 써 보면 아키텍처가 얼마나 최소한인지 체감할 수 있다. Encoder는 그저 입력을 순회하고, decoder는 그저 출력을 순회한다. 하지만 바로 이 단순함 때문에 한계도 분명하다: 모든 정보가 고정 길이 벡터 하나를 통과해야 한다. 이 병목은 코드를 직접 작성할 때 특히 실감난다.

하나의 벡터가 얼마나 많은 정보를 담을 수 있을까? 이것이 이 논문의 암묵적 질문이다.

더 길고 복잡한 문장에 대해서는 -- 충분하지 않다.

그래서 이후에 attention이 나왔고, 그 뒤에 Transformer가 나왔다.

---

**논문 읽기 시리즈**

- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/ko/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/) (정렬과 번역을 공동으로 학습하는 신경 기계 번역) — Attention의 기원
- [《Attention Is All You Need》](/ko/posts/attention-is-all-you-need/) (어텐션만 있으면 충분하다) — Attention이 주인공이 되다: Transformer의 탄생
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/ko/posts/bert/) (BERT: 언어 이해를 위한 깊은 양방향 트랜스포머 사전학습) — 사전학습 패러다임의 확립
- [《Scaling Laws for Neural Language Models》](/ko/posts/scaling-laws-for-neural-language-models/) (신경 언어 모델을 위한 스케일링 법칙) — 스케일의 수학: 왜 더 큰 모델이 예측 가능하게 더 좋은가
- [《Language Models are Few-Shot Learners》](/ko/posts/language-models-are-few-shot-learners/) (언어 모델은 퓨샷 학습자다) — 더 큰 모델, 맥락에서 능력을 이끌어내는 데 더 뛰어나다
- [《Training Compute-Optimal Large Language Models》](/ko/posts/training-compute-optimal-large-language-models/) (연산량 최적의 대규모 언어 모델 학습) — 컴퓨팅 예산을 현명하게 쓰는 법</content:encoded><category>Paper Reading</category><category>paper-reading</category><category>seq2seq</category><category>AI</category><category>LLM</category><category>python</category></item><item><title>Clawdbot: 주목할 만한 탈중앙화 오픈소스 AI 프로젝트</title><link>https://justinhuangai.github.io/ko/posts/clawdbot/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko/posts/clawdbot/</guid><description>모든 채팅 채널을 AI 에이전트에 연결해주는 셀프 호스팅 플랫폼</description><pubDate>Fri, 16 Jan 2026 08:34:57 GMT</pubDate><content:encoded>오늘날 주류 AI 어시스턴트는 대부분 중앙화되어 있다.

대화, 데이터, 컨텍스트 -- 전부 다른 누군가의 서버에 저장된다. AI를 사용하긴 하지만, 진정한 의미에서 소유하고 있는 건 아니다.

[Clawdbot](https://github.com/clawdbot/clawdbot)이라는 오픈소스 프로젝트는 정반대의 방향을 택했다.

이 프로젝트가 하려는 건 또 하나의 더 똑똑한 챗봇을 만드는 게 아니라, AI 능력을 사용자의 손에 돌려주는 것이다. 자신의 기기에서 실행하고, 이미 쓰고 있는 채팅 도구에 연결하고, 데이터와 컨텍스트와 통제권을 온전히 자기 것으로 유지하는 것.

코드베이스를 직접 살펴봤다. TypeScript로 작성된 20만 줄 이상의 코드, macOS, iOS, Android 네이티브 앱, 그리고 50개가 넘는 스킬 모듈을 포함하고 있다.

![WhatsApp에서 Clawd와 대화하기](/images/whatsapp-clawd.webp)

## 0. 먼저 몇 가지 용어부터

이런 종류의 프로젝트가 처음이라면, 아래 5개 용어만 먼저 잡아 두면 글이 훨씬 잘 읽힌다:

- `중앙화된 AI 어시스턴트`: 대화 기록, 메모리, 통제권이 주로 서비스 제공자 서버에 머무는 형태
- `셀프 호스팅`: 소프트웨어를 자기 기기나 서버에서 직접 실행하는 방식
- `AI Agent`: 단순한 챗봇이 아니라, 컨텍스트를 기억하고 도구를 호출하고 일을 수행하는 시스템
- `채팅 채널`: WhatsApp, Telegram, Slack, iMessage처럼 이미 쓰고 있는 대화 창구
- `오픈소스`: 소스 코드가 공개되어 있어 직접 검토, 수정, 배포할 수 있는 형태

주말 해커톤에서 만든 사이드 프로젝트가 아니다 -- 장기적인 프로덕트 관점으로 설계된 시스템이다. 세세한 부분에서 만든 사람의 프로덕트 감각이 느껴진다.

더 인상적인 건 절제력이다.

있어야 할 기능은 있고, 억지로 끼워 넣을 필요 없는 건 없다. &quot;커 보이려고&quot; 기능을 잔뜩 쌓아 올리는 일도 없고, 많은 AI 프로젝트에서 보이는 과시욕이나 통제력 상실도 없다.

이런 절제는 &quot;이것저것 다 하는 것&quot;보다 훨씬 어렵고, 그 자체로 많은 것을 말해준다.

그래서 Clawdbot은 주목할 가치가 있다고 생각한다. 단지 잘 만들어진 오픈소스 프로젝트이기 때문만이 아니라, 드물지만 점점 중요해지는 방향을 대표하기 때문이다:

모든 사람을 같은 AI 플랫폼에 연결하는 게 아니라,
모든 사람이 자신만의 AI 시스템을 소유하게 하는 것.

Clawdbot 같은 프로덕트의 등장은 대항해시대가 시작되기 전, 해안에 도달하는 첫 번째 파도 같은 느낌이다.

파도 자체는 아무 선언도 하지 않는다.
하지만 이미 말해주고 있다: 물결이 오고 있다고.

모두가 자신만의 개인 AI를 갖게 되는 시대, 우리가 생각하는 것보다 가까이 와 있을지도 모른다.</content:encoded><category>OpenClaw</category><category>AI</category><category>open-source</category></item><item><title>논문 읽기: 《Neural Machine Translation by Jointly Learning to Align and Translate》 (정렬과 번역을 공동으로 학습하는 신경 기계 번역)</title><link>https://justinhuangai.github.io/ko/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/</guid><description>어텐션 메커니즘의 기원, 실제 Python 코드 예시 포함</description><pubDate>Sun, 11 Jan 2026 08:26:19 GMT</pubDate><content:encoded>2014년 9월 1일, 세 명의 연구자가 arXiv(연구자들이 학술지 피어 리뷰를 거치지 않고 논문을 공개할 수 있는 프리프린트 서버)에 한 편의 논문을 업로드했다: [《Neural Machine Translation by Jointly Learning to Align and Translate》](/papers/1409.0473v7.pdf) (정렬과 번역을 공동으로 학습하는 신경 기계 번역).

저자는 몬트리올 대학교의 Dzmitry Bahdanau, KyungHyun Cho, Yoshua Bengio 세 사람이다. Yoshua Bengio는 Geoffrey Hinton, Yann LeCun과 함께 딥러닝의 &quot;세 거장&quot;으로 불리며, 세 사람은 2018년 튜링상을 공동 수상했다. Bahdanau는 당시 아직 박사과정 학생이었다.

이 논문의 핵심 기여는 한 가지로 요약할 수 있다: 번역 모델이 각 단어를 생성할 때 원문의 서로 다른 부분을 되돌아볼 수 있게 한 것이다. 지금 생각하면 당연해 보이지만, 당시 신경 기계 번역 연구에서 이것은 진정으로 새로운 아이디어였다. 이 아이디어에는 이름이 있다: &quot;attention 메커니즘.&quot;

3년 후, Google의 여덟 명의 연구자가 이 아이디어를 논리적 극한까지 밀어붙여 [《Attention Is All You Need》](/ko/posts/attention-is-all-you-need/) (어텐션만 있으면 충분하다)를 썼다. Transformer를 이해하고 싶다면, 이 논문은 가장 중요한 선행 연구 중 하나다.

## 0. 먼저 몇 가지 용어부터

머신러닝 배경이 전혀 없어도, 이 논문이 고치려 한 병목만 먼저 잡으면 읽기가 훨씬 쉬워진다:

- `encoder-decoder`: 한쪽이 원문을 끝까지 읽고, 다른 한쪽이 번역문을 한 단어씩 써 내려가는 구조다.
- `RNN / 순환 신경망`: 당시 주류였던 시퀀스 모델이다. 텍스트를 반드시 순서대로 처리해야 한다.
- `hidden state / 은닉 상태`: 모델이 어떤 위치까지 읽은 뒤 손에 쥐고 있는 임시 메모라고 생각하면 된다.
- `alignment / 정렬`: 원문 어느 부분이 지금 생성하려는 번역 단어와 대응되는지를 뜻한다.
- `attention`: 하나의 압축 벡터만 바라보는 대신, 필요할 때 원문의 더 relevant 한 위치를 다시 보게 만드는 방식이다.

## 1. 문제

2014년 신경 기계 번역의 표준 아키텍처는 encoder-decoder였다. Encoder인 RNN(Recurrent Neural Network)이 원문을 처음부터 끝까지 읽고 전체 문장을 하나의 고정 길이 벡터(정해진 개수의 숫자 목록이라고 생각하면 된다)로 압축한다. Decoder인 또 다른 RNN이 이 벡터로부터 시작하여 한 번에 한 단어씩 번역을 생성한다.

문제는 명확하다: 원문이 5단어든 50단어든, encoder는 동일한 길이의 벡터에 모든 것을 욱여넣어야 한다. 짧은 문장은 괜찮지만, 긴 문장은 정보가 손실된다. 누군가에게 한 페이지를 통째로 읽고 한 문장으로 요약하라고 하는 것과 같다 -- 페이지가 길어질수록 빠지는 내용이 많아진다.

논문은 이를 실험으로 입증했다: 문장 길이가 30단어를 넘으면 기존 encoder-decoder의 번역 품질이 급격히 하락했다.

이것이 바로 &quot;고정 길이 병목(fixed-length bottleneck)&quot;이다.

## 2. 핵심 아이디어: 압축을 멈추고, decoder가 스스로 찾게 하라

논문의 해결책은 직관적이다: 전체 문장을 하나의 벡터로 압축하면 정보가 손실되니, 압축을 멈추면 된다. Encoder가 모든 위치의 annotation 벡터를 유지하고(bidirectional RNN의 순방향과 역방향 hidden state를 연결한 것 -- 각 단어를 처리한 후 생성되는 중간 결과물이라고 생각하면 된다), decoder가 각 목표 단어를 생성할 때 스스로 원문의 어느 부분에 집중할지 결정한다.

이것이 attention 메커니즘의 핵심이다: **모든 정보를 하나의 병목으로 강제로 통과시키는 대신, 모델이 필요할 때 필요한 것을 스스로 되돌아가서 찾도록 학습시키는 것이다.**

구체적으로 세 단계로 이루어진다:

**1단계: 점수 계산.** i번째 목표 단어를 생성하기 전에, decoder는 현재 상태 s_{i-1}을 encoder 각 위치의 hidden state h_j와 비교하여 &quot;정렬 점수&quot; e_{ij}를 산출한다. 점수가 높을수록, 현재 목표 단어를 생성하는 데 원문의 j번째 위치가 더 중요하다는 뜻이다.

논문에서 사용한 점수 함수:

$$
e_{ij} = a(s_{i-1}, h_j) = v_a^T \tanh(W_a s_{i-1} + U_a h_j)
$$

이것을 &quot;additive attention&quot;이라 부른다. Decoder 상태와 encoder 상태에 각각 선형 변환(행렬 곱)을 적용하고, 그 결과를 더한 뒤, tanh(값을 -1에서 1 사이로 압축하는 함수)를 통과시키고, 벡터 v_a와 내적하여 스칼라 점수를 만든다.

**2단계: 정규화.** Softmax가 모든 위치의 점수를 합이 1이 되는 확률로 변환한다:

$$
\alpha_{ij} = \operatorname{softmax}(e_{ij}) = \frac{\exp(e_{ij})}{\sum_k \exp(e_{ik})}
$$

**3단계: 가중합.** 이 확률을 사용하여 encoder의 hidden state에 대한 가중합을 계산하고, &quot;context 벡터&quot; c_i를 만든다:

$$
c_i = \sum_j \alpha_{ij} h_j
$$

이 context 벡터가 decoder가 i번째 단어를 생성할 때 원문에서 추출한 핵심 정보다. 모델이 매번 원문의 서로 다른 위치에 집중하기 때문에, context 벡터는 생성되는 단어마다 다르다.

Python(PyTorch 기반)으로 구현하면:

```python
import torch
from torch import nn


def bahdanau_attention(
    decoder_state: torch.Tensor,
    encoder_outputs: torch.Tensor,
    w_a: nn.Linear,
    u_a: nn.Linear,
    v_a: nn.Linear,
) -&gt; tuple[torch.Tensor, torch.Tensor]:
    decoder_features = w_a(decoder_state).unsqueeze(1)
    encoder_features = u_a(encoder_outputs)
    scores = v_a(torch.tanh(decoder_features + encoder_features)).squeeze(-1)
    weights = torch.softmax(scores, dim=-1)
    context = torch.sum(weights.unsqueeze(-1) * encoder_outputs, dim=1)
    return context, weights
```

나중에 Transformer에서 사용한 &quot;dot-product attention&quot;(Q와 K를 직접 내적하는 방식)과 달리, 이 논문은 &quot;additive attention&quot;(각각을 먼저 선형 변환한 뒤 더하는 방식)을 사용한다. 두 접근 방식은 서로 다른 특성을 가지지만, dot-product attention이 효율적인 행렬 곱 연산에 더 적합하다. Transformer가 RNN의 순차적 의존성을 제거한 것과 결합되어, attention은 마침내 대규모 병렬화가 가능한 핵심 연산자가 되었다.

## 3. Encoder: Bidirectional RNN

단방향 RNN은 문장을 왼쪽에서 오른쪽으로 읽으며, 마지막 단어 이후에만 요약 벡터를 출력한다. 문제는: 각 위치의 hidden state가 주로 왼쪽 문맥만 담고 있어 오른쪽을 볼 수 없다는 점이다.

논문은 bidirectional RNN(BiRNN)으로 이를 해결한다. 하나의 RNN이 왼쪽에서 오른쪽으로, 다른 하나가 오른쪽에서 왼쪽으로 읽고, 양 방향의 hidden state를 연결한다. 이렇게 하면 각 위치의 hidden state가 왼쪽과 오른쪽 양쪽의 문맥을 모두 포함하게 된다.

```python
import torch
from torch import nn


class BidirectionalRNN(nn.Module):
    def __init__(self, input_size: int, hidden_size: int) -&gt; None:
        super().__init__()
        self.rnn = nn.GRU(
            input_size=input_size,
            hidden_size=hidden_size,
            bidirectional=True,
            batch_first=True,
        )

    def forward(self, inputs: torch.Tensor) -&gt; torch.Tensor:
        outputs, _ = self.rnn(inputs)
        return outputs
```

논문에서 각 방향은 1000개의 hidden unit을 가지며, 연결하면 2000차원이 된다. 단방향 RNN에 비해 파라미터가 두 배가 되지만, 그 대가로 모든 위치에서 전체 문맥을 볼 수 있다.

## 4. Decoder: 매 단계마다 재정렬

Encoder와 attention 메커니즘을 합치면, decoder의 작업 흐름이 명확해진다:

1. Encoder가 bidirectional RNN으로 원문을 읽고, 모든 위치의 hidden state(annotation 벡터)를 유지한다
2. Decoder가 번역을 생성하기 시작하며, 각 단어를 생성하기 전에:
   - 현재 상태와 모든 annotation 벡터를 사용하여 attention 가중치를 계산한다
   - 가중합으로 context 벡터를 만든다
   - Context 벡터, 이전에 생성한 단어, 현재 상태를 결합하여 다음 단어를 예측한다

```python
import torch
from torch import nn


class AttentionDecoder(nn.Module):
    def __init__(self, embedding_dim: int, hidden_size: int, vocab_size: int) -&gt; None:
        super().__init__()
        self.rnn = nn.GRU(
            input_size=embedding_dim + 2 * hidden_size,
            hidden_size=hidden_size,
            batch_first=True,
        )
        self.w_a = nn.Linear(hidden_size, hidden_size, bias=False)
        self.u_a = nn.Linear(2 * hidden_size, hidden_size, bias=False)
        self.v_a = nn.Linear(hidden_size, 1, bias=False)
        self.output_proj = nn.Linear(hidden_size, vocab_size)

    def decode_step(
        self,
        prev_word: torch.Tensor,
        prev_state: torch.Tensor,
        encoder_outputs: torch.Tensor,
    ) -&gt; tuple[torch.Tensor, torch.Tensor]:
        context, _ = bahdanau_attention(
            prev_state.squeeze(0),
            encoder_outputs,
            self.w_a,
            self.u_a,
            self.v_a,
        )
        rnn_input = torch.cat([prev_word, context.unsqueeze(1)], dim=-1)
        output, new_state = self.rnn(rnn_input, prev_state)
        logits = self.output_proj(output[:, -1, :])
        return logits, new_state
```

핵심 포인트: decoder가 목표 단어를 생성할 때마다 attention 분포를 다시 계산한다. 첫 번째 단어를 번역할 때는 원문의 시작 부분에 집중하고, 마지막 단어를 번역할 때는 끝 부분에 집중할 수 있다. 이런 동적 정렬 능력은 이전의 고정 벡터 아키텍처로는 절대 할 수 없는 것이다.

## 5. 실험 결과

논문은 영어-프랑스어 번역 과제(WMT &apos;14 데이터셋 사용)에서 실험을 수행하고, BLEU 점수(기계 번역의 표준 평가 지표로, 기계 출력이 사람 번역에 얼마나 가까운지를 측정하며 최대 100점)로 성능을 측정했다.

주요 비교:
- **RNNencdec-50** (기존 encoder-decoder, 50단어 이하 문장으로 학습): 26.71 BLEU
- **RNNsearch-50** (attention 적용 모델, 50단어 이하 문장으로 학습): **34.16 BLEU**
- **Moses** (당시 가장 강력한 기존 구문 기반 번역 시스템): 33.30 BLEU

7.45점 향상. 논문의 실험 설정에서, attention 기반 신경 모델은 당시 지배적이었던 기존 구문 기반 번역 시스템과 대등하거나 심지어 능가하는 성능을 보였다.

더 중요한 발견은 논문의 Figure 2에 있다: 문장 길이가 증가하면 기존 encoder-decoder의 BLEU 점수가 급격히 하락한 반면, attention 기반 모델은 거의 영향을 받지 않았다. 이는 논문의 핵심 가설을 직접 검증한다: 고정 길이 벡터가 병목이며, attention 메커니즘이 이를 우회할 수 있다는 것이다.

논문은 또한 attention 가중치를 시각화했다. 영어-프랑스어 번역에서 attention 가중치는 거의 대각선을 이루며, 모델이 &quot;영어 단어 1은 프랑스어 단어 1에 대응, 영어 단어 2는 프랑스어 단어 2에 대응&quot;하는 것을 자동으로 학습했음을 보여준다. 어순이 다를 때(예를 들어 프랑스어에서 형용사가 명사 뒤에 오는 경우) attention 가중치가 그에 맞게 이동했다. 모델은 이 모든 것을 수동 정렬 주석 없이 학습했다.

## 6. 읽고 나서

이 논문을 읽고 몇 가지가 눈에 띈다.

첫째, 이 논문이 해결하는 문제가 극도로 명확하다: encoder가 전체 문장을 하나의 벡터로 압축하면 긴 문장에서 정보가 손실된다. 해결책도 마찬가지로 직관적이다: 압축을 멈추고 decoder가 스스로 찾게 하면 된다. 좋은 연구는 흔히 이런 식이다 -- 문제가 명확하고, 해결책이 자연스럽게 따라온다.

둘째, 이 논문에서 attention은 여전히 RNN의 보조 역할이다. Encoder는 여전히 순환 구조(bidirectional RNN)이고, decoder도 여전히 순환 구조이며, attention은 단지 둘을 연결하는 다리일 뿐이다. 3년 후, Vaswani 등은 훨씬 더 급진적인 질문을 던졌다: attention이 이렇게 잘 작동한다면, RNN을 완전히 버리고 attention만 남기면 어떨까? 그 답이 Transformer였다.

셋째, 이 논문의 attention 메커니즘을 실제 Python으로 다시 써 보면, Transformer의 Scaled Dot-Product Attention에 비해 계산이 상당히 복잡하다는 것을 알 수 있다. Additive attention은 추가 가중치 행렬 W_a, U_a, v_a가 필요한 반면, dot-product attention은 Q와 K를 직접 곱하고 스케일링하기만 하면 된다. &quot;덧셈&quot;에서 &quot;곱셈&quot;으로의 전환은 작은 한 걸음처럼 보이지만, 실제로는 계산을 극적으로 단순화하고 효율적인 행렬 연산에 훨씬 더 적합하게 만들었다.

넷째, Bahdanau는 당시 박사과정 학생이었고, Bengio가 그의 지도교수였다. 한 박사과정 학생의 논문이 이후 10년간 AI 연구의 핵심 구성 요소를 정의하게 된 것이다. Attention 메커니즘은 여기서 시작되어 Transformer에 의해 증폭되었고, 궁극적으로 GPT, BERT, LLaMA의 기반이 되었다.

이 논문은 복잡한 수학을 발명하지 않았다. 단지 직관적인 질문 하나를 던졌을 뿐이다: 왜 decoder가 되돌아볼 수 없는가?

그리고 decoder가 되돌아보게 했다.

그 한 번의 되돌아봄이 시대 전체를 바꿨다.

---

**논문 읽기 시리즈**

- [《Sequence to Sequence Learning with Neural Networks》](/ko/posts/sequence-to-sequence-learning-with-neural-networks/) (신경망을 이용한 시퀀스-투-시퀀스 학습) — Encoder-decoder 패러다임의 확립
- [《Attention Is All You Need》](/ko/posts/attention-is-all-you-need/) (어텐션만 있으면 충분하다) — Attention이 주역이 되다: Transformer의 탄생
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/ko/posts/bert/) (BERT: 언어 이해를 위한 깊은 양방향 트랜스포머 사전학습) — 사전 학습 패러다임의 확립
- [《Scaling Laws for Neural Language Models》](/ko/posts/scaling-laws-for-neural-language-models/) (신경 언어 모델을 위한 스케일링 법칙) — 스케일의 수학: 왜 더 큰 모델이 예측 가능하게 더 좋은가
- [《Language Models are Few-Shot Learners》](/ko/posts/language-models-are-few-shot-learners/) (언어 모델은 퓨샷 학습자다) — 더 큰 모델, 문맥에서 능력을 더 잘 이끌어내다
- [《Training Compute-Optimal Large Language Models》](/ko/posts/training-compute-optimal-large-language-models/) (연산량 최적의 대규모 언어 모델 학습) — 컴퓨팅 예산을 현명하게 쓰는 법</content:encoded><category>Paper Reading</category><category>paper-reading</category><category>attention</category><category>AI</category><category>LLM</category><category>python</category></item><item><title>논문 읽기: 《Attention Is All You Need》 (어텐션만 있으면 충분하다)</title><link>https://justinhuangai.github.io/ko/posts/attention-is-all-you-need/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko/posts/attention-is-all-you-need/</guid><description>Transformer 논문에 대한 이해, 실제 Python 코드 예시 포함</description><pubDate>Tue, 06 Jan 2026 08:18:46 GMT</pubDate><content:encoded>2017년 6월 12일, 여덟 명이 arXiv(연구자들이 학술지 심사를 기다리지 않고 논문을 발표할 수 있는 프리프린트 서버)에 논문 한 편을 올렸다. 제목은 단 다섯 단어: [《Attention Is All You Need》](/papers/1706.03762v7.pdf) (어텐션만 있으면 충분하다).

여덟 명은 Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N. Gomez, Łukasz Kaiser, 그리고 Illia Polosukhin이었으며, 대부분 당시 Google Brain과 Google Research에서 일하고 있었다.

논문이 발표된 후 이들은 뿔뿔이 흩어졌다. Noam Shazeer는 Google을 떠나 Character.AI를 창업했다가, 나중에 프리미엄을 받고 다시 Google에 인수되었다. Aidan Gomez는 토론토 대학교에서 박사 과정을 마치기도 전에 Cohere를 설립하여 기업용 대규모 언어 모델을 구축했다. Llion Jones는 일본으로 건너가 Sakana AI를 창립했다. Illia Polosukhin은 아무도 예상하지 못한 길을 걸었다 -- 블록체인 프로젝트 NEAR Protocol을 시작한 것이다. Ashish Vaswani와 Niki Parmar는 함께 Adept AI를 공동 창립한 뒤, 나중에 Essential AI를 설립했다. Jakob Uszkoreit는 AI를 활용해 RNA 기반 의약품을 설계하는 Inceptive를 창립했다. Łukasz Kaiser는 OpenAI에 합류해 GPT 시리즈 개발에 기여했다.

여덟 명의 저자, 일곱 개의 회사, AI, 블록체인, 바이오테크에 걸친 행보.

약 9년이 지난 지금, ChatGPT, Claude, DeepSeek, Qwen -- 이 AI 제품들의 기본 아키텍처는 거의 모두 그 15페이지짜리 논문으로 거슬러 올라갈 수 있다.

이 글은 내가 논문을 읽고 이해한 내용을 정리한 것이며, 실제 Python 코드 예시 포함했다. 번역도, 요약도 아니다. 기술적 배경 지식 없이도 따라갈 수 있다.

## 0. 먼저 몇 가지 용어부터

머신러닝 배경이 전혀 없어도, 이 논문이 실제로 무엇을 바꾸려 했는지만 따라가면 된다. 아래 용어만 먼저 잡아두자:

- `RNN / 순환 신경망`: 더 오래된 시퀀스 모델이다. 문장을 처리할 때 한 단어씩 순서대로 읽어야 한다.
- `attention`: 많은 정보 중에서 지금 가장 봐야 할 부분을 골라내는 메커니즘이다. 일단은 &quot;중요한 부분을 골라 다시 보는 방식&quot; 정도로 이해하면 충분하다.
- `Query / Key / Value`: attention 안의 세 역할이다. Query는 &quot;지금 무엇을 찾고 있는가&quot;, Key는 &quot;각 정보가 어떤 표식을 달고 있는가&quot;, Value는 &quot;실제로 가져오는 내용&quot;에 가깝다.
- `Transformer`: attention을 중심으로 세운 전체 아키텍처다. 순환에 기대지 않고, 각 위치가 다른 위치를 직접 볼 수 있다.
- `병렬 처리`: 더 똑똑하다는 뜻이 아니라, RNN처럼 줄 서서 처리하지 않고 많은 위치를 동시에 처리할 수 있다는 뜻이다.

## 1. 한 문장 요약

Transformer 이전에는 AI가 언어를 처리하는 방식이 마치 손가락으로 한 단어씩 짚어가며 책을 읽는 것과 같았다. 100번째 단어에 도달하면 1번째 단어가 무슨 내용이었는지 이미 흐려져 있다. 문장이 길수록 망각은 심해진다. 이것이 Recurrent Neural Networks(RNN, 이전 세대의 AI 아키텍처)의 근본적인 병목이었다.

저자들은 간단한 질문을 던졌다: **왜 꼭 순서대로 읽어야 하는가?**

RNN이 토큰을 단계별로 처리해야 하는 것과 달리, Transformer는 입력 전체를 병렬로 처리하며 임의의 두 위치 사이의 관계를 직접 모델링한다. 줄 설 필요도 없고, 이전 단어가 끝날 때까지 기다릴 필요도 없다.

논문은 이 핵심 능력을 &quot;attention&quot;이라 불렀다. 제목이 말하는 것은 &quot;모델에 문자 그대로 attention 외에 아무것도 없다&quot;가 아니다. 시퀀스 모델링에서 attention이 처음으로 주연으로 승격되어, 더 이상 순환(recurrence)이나 합성곱(sliding window로 지역적 특징을 추출하는 방법)을 뼈대로 필요로 하지 않게 되었다는 뜻이다.

## 2. Attention이 실제로 하는 일

시끄러운 바에 들어갔다고 상상해 보자. 스무 명이 동시에 이야기하고 있다. 우리의 뇌는 모든 목소리에 균등하게 주의를 기울이지 않는다. 누군가 이름을 부르면, 귀가 즉시 그 방향으로 고정된다. 다른 모든 소리는 배경 소음으로 사라진다.

Transformer는 모든 단어에 대해 같은 일을 한다. 논문은 세 가지 역할을 정의한다:

- **Query**: 이 단어가 찾고 있는 것. 마치 &quot;방금 내 이름을 부른 사람이 누구지?&quot;라고 찾는 귀와 같다
- **Key**: 이 단어가 제공할 수 있는 것. 바에 있는 각 사람의 음성적 특징과 같다
- **Value**: 이 단어가 담고 있는 실제 내용. 그 사람이 실제로 하고 있는 말과 같다

각 단어의 Query는 다른 모든 단어의 Key와 대조된다. 매칭 점수가 높으면 해당 단어의 Value에서 더 많은 정보를 가져온다. 매칭 점수가 낮으면 사실상 무시된다.

논문이 제시하는 공식은 Scaled Dot-Product Attention이라 불린다:

$$
\operatorname{Attention}(Q, K, V) = \operatorname{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V
$$

공식을 보고 당황하지 않아도 된다. 하나씩 풀어보자:

- **QK^T**: Q와 K의 내적. 내적이란? 두 숫자 리스트를 원소별로 곱한 다음 합산하는 것이다. 예를 들어 [1, 2]와 [3, 4]의 내적은 1x3 + 2x4 = 11이다. 결과가 클수록 두 단어의 관련성이 높다. 이 단계에서는 모든 단어 쌍에 대해 &quot;매칭 점수&quot;를 계산한다
- **/ sqrt(d_k)**: 스케일링 인수로 나눈다. d_k는 벡터의 길이다(벡터는 &quot;무언가를 설명하는 숫자 리스트&quot;로 생각하면 된다 -- 예를 들어 한 단어의 의미를 설명하는 64개의 숫자). 왜 나눌까? 숫자 리스트가 길어질수록 내적 값이 커지는 경향이 있기 때문이다. 스케일링 없이는 차원이 높을수록 내적의 분산이 커져, softmax가 포화 상태에 빠지고(거의 모든 확률이 한 단어에 집중), gradient(모델이 자체 파라미터를 조정하는 데 사용하는 신호)가 줄어들어 학습이 불안정해진다
- **softmax**: 점수 집합을 합이 1인 확률로 변환한다. 예를 들어 세 단어의 점수가 [10, 2, 1]이면, softmax는 이를 대략 [0.99, 0.007, 0.003]으로 변환한다. 가장 높은 점수의 단어가 거의 모든 attention을 차지하고, 나머지는 0에 가깝게 밀린다
- **x V**: 이 확률을 사용해 각 단어의 실제 내용을 가중 결합한다. 높은 확률의 단어는 더 많이 기여하고, 낮은 확률의 단어는 적게 기여한다. 최종 출력은 핵심 정보를 융합한 새로운 벡터다

Python(PyTorch 기반)으로 구현하면:

```python
import math
import torch


def scaled_dot_product_attention(
    query: torch.Tensor,
    key: torch.Tensor,
    value: torch.Tensor,
) -&gt; torch.Tensor:
    d_k = key.size(-1)
    scores = query @ key.transpose(-2, -1) / math.sqrt(d_k)
    weights = torch.softmax(scores, dim=-1)
    return weights @ value
```

불과 몇 줄의 코드다. 이후 산업을 뒤바꾼 수많은 역량이 이 몇 가지 연산 위에 구축되었다.

## 3. Multi-Head Attention: 여러 각도에서 동시에 바라보기

하나의 attention head는 한 가지 유형의 관계 패턴에 고정되기 쉽다. 하지만 언어는 하나의 문장에 여러 층위의 의미를 담고 있다.

&quot;어제 고양이가 매트 위에 앉았다&quot;를 예로 들어보자:
- &quot;고양이&quot;와 &quot;앉았다&quot;는 주어-동사 관계 (누가 무엇을 했는지)
- &quot;어제&quot;와 &quot;앉았다&quot;는 시간 관계 (언제 일어났는지)
- &quot;위에&quot;와 &quot;매트&quot;는 공간 관계 (어디서 일어났는지)

하나의 head에게 이 모든 층위를 동시에 처리하라고 하는 것은 무리한 요구다. 논문의 해법은 multi-head 메커니즘이다: 8개의 head를 파견해 병렬로 실행하여, 모델이 서로 다른 부분공간에서 문장을 동시에 관찰할 기회를 주고, 마지막에 결과를 연결(concatenate)한다.

논문의 공식:

$$
\operatorname{MultiHead}(Q, K, V) = \operatorname{Concat}(\text{head}_1, \ldots, \text{head}_h)\, W^O
$$

풀어보면:
- **head_1, ..., head_h**: 8개의 head가 각각 독립적으로 하나의 attention 계산을 수행하여 8개의 별도 결과를 생성한다
- **Concat**: 8개의 결과를 모두 끝에서 끝으로 이어 붙여 하나의 긴 벡터를 만든다
- **W^O**: 이어 붙인 긴 벡터를 원래 차원으로 다시 투영하는 선형 변환(&quot;행렬을 곱한다&quot;고 생각하면 된다). 마치 관리자가 8명의 조사관으로부터 보고를 듣고 하나의 종합 결론을 내리는 것과 같다

```python
import math
import torch
from torch import nn


class MultiHeadAttention(nn.Module):
    def __init__(self, d_model: int, num_heads: int) -&gt; None:
        super().__init__()
        if d_model % num_heads != 0:
            raise ValueError(&quot;d_model must be divisible by num_heads&quot;)
        self.num_heads = num_heads
        self.d_head = d_model // num_heads
        self.q_proj = nn.Linear(d_model, d_model)
        self.k_proj = nn.Linear(d_model, d_model)
        self.v_proj = nn.Linear(d_model, d_model)
        self.out_proj = nn.Linear(d_model, d_model)

    def _split_heads(self, x: torch.Tensor) -&gt; torch.Tensor:
        batch_size, seq_len, _ = x.shape
        x = x.view(batch_size, seq_len, self.num_heads, self.d_head)
        return x.transpose(1, 2)

    def forward(
        self,
        query: torch.Tensor,
        key: torch.Tensor,
        value: torch.Tensor,
    ) -&gt; torch.Tensor:
        q = self._split_heads(self.q_proj(query))
        k = self._split_heads(self.k_proj(key))
        v = self._split_heads(self.v_proj(value))

        scores = q @ k.transpose(-2, -1) / math.sqrt(self.d_head)
        weights = torch.softmax(scores, dim=-1)
        heads = weights @ v

        batch_size, _, target_len, _ = heads.shape
        merged = heads.transpose(1, 2).contiguous()
        merged = merged.view(batch_size, target_len, self.num_heads * self.d_head)
        return self.out_proj(merged)
```

논문의 파라미터: 모델은 각 단어를 설명하는 데 512개의 숫자를 사용하고(d_model = 512), 8개의 head가 각각 64개의 숫자를 받는다(512 / 8 = 64). 8개 head의 총 계산량은 단일 512차원 head와 대략 같지만, 표현력은 훨씬 크다. 같은 비용으로 다중 관점의 이해를 얻는다. 매우 좋은 거래다.

## 4. Positional Encoding: 모델에게 단어 순서 알려주기

Transformer는 전체 문장을 병렬로 처리하므로 빠르지만, 그 대가로 단어 순서를 잃는다. 추가적인 위치 정보가 없으면, attention 메커니즘만으로는 &quot;고양이가 물고기를 먹었다&quot;와 &quot;물고기가 고양이를 먹었다&quot;의 차이를 구분할 수 없다. 이래서는 안 된다.

해결책: 각 위치에 대해 고유한 &quot;주소 코드&quot;를 생성하여 단어의 벡터에 더한다. 모델은 더 이상 단순히 &quot;고양이&quot;와 &quot;물고기&quot;를 보는 것이 아니라 &quot;위치 1의 고양이&quot;와 &quot;위치 3의 물고기&quot;를 본다.

논문은 사인 함수와 코사인 함수를 사용하여 이 인코딩을 생성한다:

$$
\operatorname{PE}(pos, 2i) = \sin\left(\frac{pos}{10000^{2i / d_{\text{model}}}}\right)
$$

$$
\operatorname{PE}(pos, 2i + 1) = \cos\left(\frac{pos}{10000^{2i / d_{\text{model}}}}\right)
$$

공식이 겁나 보이지만, 핵심 아이디어는 직관적이다:
- **pos**: 문장에서의 단어 위치 (1번째, 2번째, 3번째, ...)
- **i**: 벡터의 몇 번째 차원인지. 짝수 위치는 sin, 홀수 위치는 cos을 사용한다
- **10000^(2i/d_model)**: 차원에 따라 변하는 스케일링 인수. 저차원은 빠르게 진동하고, 고차원은 느리게 진동한다. 시계에 비유하면: 초침은 1분에 한 바퀴를 돌고, 시침은 12시간이 걸린다. 서로 다른 &quot;바늘&quot;이 서로 다른 시간 스케일을 커버하며, 함께 사용하면 어떤 순간이든 정확히 짚어낼 수 있다

최종 결과: 각 위치가 고유한 숫자 지문을 받고, 모델은 이 지문을 사용해 단어 순서를 구분한다.

```python
import math
import torch


def positional_encoding(seq_len: int, d_model: int) -&gt; torch.Tensor:
    positions = torch.arange(seq_len, dtype=torch.float32).unsqueeze(1)
    div_term = torch.exp(
        torch.arange(0, d_model, 2, dtype=torch.float32)
        * (-math.log(10000.0) / d_model)
    )

    encoding = torch.zeros(seq_len, d_model)
    encoding[:, 0::2] = torch.sin(positions * div_term)
    encoding[:, 1::2] = torch.cos(positions * div_term)
    return encoding
```

왜 하필 사인과 코사인일까? 이 함수들에는 우아한 수학적 성질이 있기 때문이다: 고정된 거리만큼 떨어진 두 위치의 인코딩 관계는, 그 위치가 문장의 처음에 있든 끝에 있든 동일하다. 모델은 &quot;위치 3과 위치 8의 관계&quot;를 외울 필요가 없다 -- &quot;5개 위치 떨어짐&quot;이 무엇을 의미하는지만 학습하면 된다. 논문 팀은 모델이 positional encoding을 스스로 학습하게 하는 방법도 시도했는데, 결과는 비슷했다. 하지만 사인파 버전에는 장점이 하나 더 있다: 학습 중에 본 적 없는 길이의 문장도 처리할 수 있다는 것이다.

## 5. Encoder와 Decoder

Transformer의 전체 아키텍처는 두 부분으로 나뉜다.

**Encoder**(6개의 층을 쌓은 구조)는 입력을 이해하는 역할을 한다. 각 층에는 두 개의 하위 층이 있다: multi-head self-attention 하나, feed-forward 네트워크 하나. 각 하위 층에는 두 가지 보호 메커니즘이 있다:

- **잔차 연결(Residual connection)**: 하위 층의 입력을 출력에 직접 더한다. 즉, x + Sublayer(x)이다. 왜? 사진에 필터를 적용한다고 생각해 보자. 필터 결과가 나쁘더라도, 잔차 연결이 있으면 원본 이미지를 여전히 볼 수 있다. 깊은 네트워크에서는 정보가 매 층에서 변환되어 여섯 번째 층에 이르면 알아볼 수 없게 될 수 있다. 잔차 연결은 원래 신호가 &quot;지름길&quot;을 통해 깊은 층까지 직접 도달하게 하여, 정보가 전달 과정에서 소실되는 것을 방지한다
- **Layer normalization** (LayerNorm): 값을 균일한 범위로 재조정하여, 일부 숫자가 무한대로 폭발하고 다른 숫자가 0으로 사라지는 것을 방지한다. 시험 점수를 표준화하는 것과 비슷하다 -- 원래 점수가 아무리 다르더라도, 표준화하면 비교 가능한 척도가 된다

**Decoder**(6개의 층을 쌓은 구조)는 출력을 생성하는 역할을 한다. 구조는 encoder와 유사하지만, 두 가지 중요한 추가 요소가 있다:

첫째, **cross-attention**: decoder가 각 단어를 생성할 때 encoder의 출력을 되돌아본다. 번역 시나리오에서, 이는 영어를 쓰면서 중국어 원문을 다시 확인하는 것과 같다.

둘째, **masking**: 3번째 단어를 생성할 때, 모델은 처음 2개 단어만 볼 수 있다. 4번째 위치 이후는 차단된다(attention 점수를 음의 무한대로 설정하면 softmax 이후 0이 된다). 논리는 간단하다: 글을 쓸 때 다음 단어는 아직 쓰지 않았으므로 미리 볼 수 없다.

```python
from typing import Optional

import torch
from torch import nn


class Transformer(nn.Module):
    def __init__(
        self,
        vocab_size: int,
        d_model: int = 512,
        num_heads: int = 8,
        num_layers: int = 6,
        d_ff: int = 2048,
        dropout: float = 0.1,
    ) -&gt; None:
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, d_model)

        encoder_layer = nn.TransformerEncoderLayer(
            d_model=d_model,
            nhead=num_heads,
            dim_feedforward=d_ff,
            dropout=dropout,
            batch_first=True,
        )
        decoder_layer = nn.TransformerDecoderLayer(
            d_model=d_model,
            nhead=num_heads,
            dim_feedforward=d_ff,
            dropout=dropout,
            batch_first=True,
        )

        self.encoder = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)
        self.decoder = nn.TransformerDecoder(decoder_layer, num_layers=num_layers)
        self.output_proj = nn.Linear(d_model, vocab_size)

    def forward(
        self,
        src_token_ids: torch.Tensor,
        tgt_token_ids: torch.Tensor,
        tgt_mask: Optional[torch.Tensor] = None,
    ) -&gt; torch.Tensor:
        memory = self.encoder(self.embedding(src_token_ids))
        hidden = self.decoder(self.embedding(tgt_token_ids), memory, tgt_mask=tgt_mask)
        return self.output_proj(hidden)
```

놓치기 쉬운 구성 요소가 하나 더 있다: feed-forward 네트워크. 공식은 FFN(x) = max(0, xW1 + b1)W2 + b2이다. 쉽게 말하면: 각 단어의 512차원 벡터를 2048차원으로 확장하고(행렬을 곱하고 바이어스를 더한 다음), ReLU를 통과시키고(모든 음수는 0이 되고 양수는 그대로 유지), 다시 512차원으로 압축한다. ReLU 단계가 핵심이다: &quot;비선형성&quot;을 도입하여, 직선으로는 절대 포착할 수 없는 복잡한 패턴을 모델이 학습할 수 있게 해준다. 모든 연산이 선형이라면, 여러 층을 쌓아도 수학적으로는 단일 층과 동일하다. 비선형성은 복잡성을 모델링하기 위한 전제 조건이다.

## 6. 학습 세부 사항

아키텍처를 설계했으면, 어떻게 학습시킬까? 논문은 이 부분에도 심혈을 기울였다.

**하드웨어**: NVIDIA P100 GPU 8개. 기본 모델은 12시간(100,000 스텝), 대형 모델은 3.5일(300,000 스텝) 학습했다. 오늘날 기준으로 보면 놀라울 정도로 낮은 비용이다.

**Optimizer**: Adam(모델 파라미터를 자동으로 조정하는 알고리즘)을 사용하되, 영리한 learning rate 스케줄을 적용했다. Learning rate는 모델이 매번 업데이트할 때 얼마나 큰 보폭을 내딛는지를 결정한다. 보폭이 너무 크면 최적점을 지나칠 위험이 있고, 너무 작으면 시간이 낭비된다. 논문의 전략: 처음 4,000 스텝 동안 점진적으로 올리고(warmup), 초반에 과도하게 공격적인 업데이트를 피한다. 4,000 스텝 이후에는 스케줄에 따라 점차 감소시켜 학습 후반을 안정화한다. 올랐다가 내리기 -- 전반부에는 대담한 탐색, 후반부에는 세밀한 조정.

**정규화**: 두 가지 기법을 사용한다. 첫째는 Dropout: 학습 중에 뉴런(네트워크 내의 계산 노드)의 10%를 무작위로 비활성화하여, 모델이 특정 경로에 의존하지 않고 더 견고한 특징을 학습하도록 강제한다. 둘째는 label smoothing(epsilon = 0.1): 모델에게 &quot;정답의 확률이 100%&quot;라고 알려주는 대신, &quot;정답에 90%, 나머지 선택지에 10%를 분배&quot;한다고 알려준다. 이렇게 하면 실제로 perplexity(모델이 얼마나 &quot;불확실한지&quot; 측정하는 지표) 자체는 나빠지지만, 번역 품질은 향상된다. 직관적으로, 자신이 100% 확신하지 않는다고 인정하는 모델이 과신하는 모델보다 더 신뢰할 수 있다.

**결과**: 논문은 BLEU 점수(기계 번역의 표준 지표로, 기계 출력이 인간 번역에 얼마나 가까운지 측정하며 최대 100점)로 성능을 평가한다. 영어-독일어: 28.4점. 영어-프랑스어: 41.8점. 둘 다 당시 최고 기록을 갱신했다. 학습 비용은 이전 접근 방식 대비 1~2자릿수 낮았다. 더 빠르고, 더 강하고, 더 저렴하다.

## 7. 나의 소감

이 논문을 읽고 나서 몇 가지가 인상적이었다.

첫째, 이 논문의 핵심 통찰은 놀라울 정도로 간결하다: 순차 처리의 짐을 버리고, attention 메커니즘이 임의의 두 위치 사이의 관계를 직접 모델링하게 하는 것이다. Self-Attention, 잔차 연결, Layer Normalization -- 이 중 어느 것도 새로운 발명이 아니었다. 진정한 돌파구는 새로운 도구를 발명한 것이 아니라, &quot;이 간단한 빌딩 블록들을 조합하면 충분하다&quot;는 것에 저자들이 기꺼이 승부를 건 것 -- 그리고 실험으로 스스로를 증명한 것이다.

둘째, 이것을 실제 Python 코드로 옮겨 적으면서 모든 설계 결정에 대한 이해가 깊어졌다. Scaled Dot-Product Attention을 직접 작성하면, 그 sqrt(d_k) 스케일링이 왜 중요한지 피부로 느끼게 된다. masking을 구현하면, 자기회귀 생성 제약이 정확히 어디서 오는지 이해하게 된다. 논문을 열 번 읽는 것보다 직접 한 번 구현하는 것이 낫다.

셋째, 나를 진정으로 감탄하게 한 것은 이후 얼마나 많은 모델을 탄생시켰는가가 아니라, 2017년에 문제를 재정의한 것이었다: &quot;어떻게 문장을 순서대로 기억할 것인가&quot;에서 &quot;어떻게 모든 위치가 가장 필요한 정보를 직접 찾게 할 것인가&quot;로. GPT, BERT, T5, LLaMA -- 이 모두가 그 재정의의 산물이다.

충분히 좋은 아키텍처가 얼마나 멀리 갈 수 있는지는, 얼마나 많은 사람이 그 위에 계속 쌓아 올릴 의향이 있는지에 달려 있다.

이 논문이 우리에게 그 아키텍처를 주었다.

《Attention Is All You Need》 (어텐션만 있으면 충분하다).

---

**논문 읽기 시리즈**

- [《Sequence to Sequence Learning with Neural Networks》](/ko/posts/sequence-to-sequence-learning-with-neural-networks/) (신경망을 이용한 시퀀스-투-시퀀스 학습) — Encoder-decoder 패러다임의 확립
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/ko/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/) (정렬과 번역을 공동으로 학습하는 신경 기계 번역) — Attention의 기원
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/ko/posts/bert/) (BERT: 언어 이해를 위한 깊은 양방향 트랜스포머 사전학습) — 사전 학습 패러다임의 확립
- [《Scaling Laws for Neural Language Models》](/ko/posts/scaling-laws-for-neural-language-models/) (신경 언어 모델을 위한 스케일링 법칙) — 스케일의 수학: 왜 더 큰 모델이 예측 가능하게 더 좋은가
- [《Language Models are Few-Shot Learners》](/ko/posts/language-models-are-few-shot-learners/) (언어 모델은 퓨샷 학습자다) — 더 큰 모델, 맥락에서 능력을 이끌어내는 데 더 뛰어남
- [《Training Compute-Optimal Large Language Models》](/ko/posts/training-compute-optimal-large-language-models/) (연산량 최적의 대규모 언어 모델 학습) — 컴퓨팅 예산을 현명하게 쓰는 법</content:encoded><category>Paper Reading</category><category>paper-reading</category><category>transformer</category><category>AI</category><category>LLM</category><category>python</category></item><item><title>👋 Hello World</title><link>https://justinhuangai.github.io/ko/posts/hello-world/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko/posts/hello-world/</guid><description>Astro-Theme-Aither에 오신 것을 환영합니다 — 타이포그래피가 디자인을 이끄는 블로그 테마</description><pubDate>Thu, 01 Jan 2026 08:07:13 GMT</pubDate><content:encoded>Astro-Theme-Aither에 오신 것을 환영합니다.

이것은 하나의 믿음 위에 만들어진 블로그 테마입니다: 좋은 글은 좋은 타이포그래피를 누릴 자격이 있다. 세리프 제목, 깔끔한 읽기 리듬, 그리고 방해하지 않는 레이아웃. 여기 있는 모든 것은 단 하나의 목표를 위해 존재합니다 — 당신의 글이 아름답게 보이고 느껴지도록 하는 것.

## 또 하나의 블로그 테마를 만든 이유

웹에는 블로그 테마가 넘쳐나니, 왜 또 하나를 만들었는지 묻는 것은 당연합니다. 답은 우선순위에 있습니다. 대부분의 테마는 시각적 임팩트에 최적화합니다 — 큰 히어로 이미지, 복잡한 레이아웃, 애니메이션 전환. 데모에서는 멋지게 보이지만, 누군가가 2,000단어 글을 읽으려 앉으면 오히려 방해가 됩니다.

Astro-Theme-Aither는 다른 전제에서 출발합니다. 콘텐츠가 곧 제품입니다. 테마의 역할은 그 콘텐츠를 정성껏 제공하는 것입니다: 세심한 폰트 조합, 넉넉한 여백, 그리고 장문의 읽기를 편안하게 만드는 수직 리듬.

이 철학은 기술적 결정에도 확장됩니다. 이 테마는 약 0.5 KB의 클라이언트 사이드 JavaScript만 전송합니다 — 테마 토글에 필요한 최소한만. 나머지는 모두 정적 HTML과 CSS입니다. 레이아웃 시프트도, 로딩 스피너도, 백그라운드에서 하이드레이팅하는 JavaScript 프레임워크도 없습니다. 페이지가 로드되면, 읽기만 하면 됩니다.

## 시작하기

설정하고 실행하는 데 몇 분이면 충분합니다. 전체 과정은 다음과 같습니다:

1. **저장소 복제** — GitHub 템플릿 버튼을 사용하거나 `git clone`으로 직접 복제
2. **의존성 설치** — `pnpm install`을 실행하여 모든 패키지 설치
3. **사이트 설정** — `src/config/site.ts`를 편집하여 사이트 제목, 설명, 네비게이션 링크 설정
4. **샘플 콘텐츠 교체** — `src/content/posts/`의 글을 자신의 Markdown 파일로 교체
5. **개발 시작** — `pnpm dev`를 실행하여 핫 리로딩이 되는 로컬 개발 서버 실행
6. **배포** — `gh-pages`에 푸시하면 내장된 GitHub Pages 워크플로가 사이트를 발행

### 프로젝트 구조

코드베이스는 즉시 이해할 수 있도록 구성되어 있습니다:

```
src/
├── components/     # 재사용 가능한 Astro 컴포넌트
├── config/         # 사이트 설정
├── content/        # Markdown 글과 콘텐츠
├── layouts/        # 페이지 레이아웃 (Layout.astro)
├── pages/          # 라우트 페이지
└── styles/         # Tailwind v4 토큰을 사용한 글로벌 CSS
```

각 디렉토리에는 명확한 역할이 있습니다. 컴포넌트는 작고 조합 가능합니다. 레이아웃은 문서 셸을 처리합니다. 페이지는 라우트를 정의합니다. 콘텐츠는 글을 담고 있습니다. 마법 같은 것은 없습니다 — 잘 정리된 파일들뿐입니다.

### 첫 번째 글 작성

`src/content/posts/`에 다음 프런트매터를 포함하는 새 `.md` 파일을 만드세요:

```markdown
---
title: 글 제목
date: &quot;2026-01-15T16:27:43+08:00&quot;
category: General
description: SEO와 소셜 미리보기를 위한 간단한 요약
tags: [topic, another]
---

여기에 내용을 작성하세요.
```

`title`, `date`, `category` 필드는 필수입니다. `date`는 초와 시간대까지 포함한 ISO 8601 형식을 사용하는 것이 좋습니다. 예: `2026-01-15T16:27:43+08:00`. `description` 필드는 메타 설명 태그와 Open Graph 미리보기를 채우기 때문에 작성을 강력히 권장합니다. 태그는 선택 사항이지만 독자들이 관련 콘텐츠를 발견하는 데 도움이 됩니다.

## 무엇이 포함되어 있나

즉시 사용 가능한 프로덕션 준비 블로깅 플랫폼으로, 필요한 모든 기능을 갖추고 불필요한 것은 없습니다.

### 콘텐츠 기능

- **RSS 피드** — `/rss.xml`에 자동 생성, 모든 피드 리더와 호환
- **사이트맵** — 검색 엔진 인덱싱을 위해 `@astrojs/sitemap`으로 자동 생성
- **SEO 메타 태그** — 모든 페이지에 Open Graph, Twitter 카드, 정규 URL
- **다크 모드** — `localStorage` 지속성을 가진 3단 토글 (라이트 / 다크 / 시스템)
- **카테고리 및 태그 페이지** — 독자들이 주제별로 탐색할 수 있는 정리된 아카이브

### 개발자 기능

- **전체 TypeScript** — strict 모드, 완전히 타입이 지정된 컴포넌트와 유틸리티
- **Content Collections** — 빌드 타임 프런트매터 유효성 검사를 포함한 Astro 내장 타입 안전 Markdown 시스템
- **Tailwind CSS v4** — 색상, 폰트, 간격의 손쉬운 커스터마이징을 위한 `@theme` 디자인 토큰 사용
- **검증 체인** — `pnpm validate`를 통해 콘텐츠 커버리지 검사와 agent 프로토콜 smoke test 실행
- **GitHub Pages** — 기본 배포 워크플로우 포함
- **Google Analytics** — 선택 사항, 메인 스레드를 차단하지 않도록 Partytown Web Worker에서 격리 실행

### 성능

이 테마는 최소한의 JavaScript로 정적 HTML을 출력하기 때문에 기본적으로 뛰어난 성능을 제공합니다. Lighthouse 점수 전 항목 100점을 기대할 수 있습니다 — Performance, Accessibility, Best Practices, SEO. 불필요한 것이 없기 때문에 최적화할 것도 없습니다.

## 커스터마이징

이 테마는 여러분의 것이 되도록 설계되었습니다. 가장 일반적인 커스터마이징은 간단합니다:

- **색상** — `src/styles/global.css`의 CSS 커스텀 프로퍼티를 편집하여 전체 팔레트 변경
- **폰트** — Tailwind 테마 설정에서 font-family 값 교체
- **네비게이션** — `src/config/site.ts`의 네비게이션 링크 배열 업데이트
- **Analytics** — 사이트 설정에서 Google Analytics 측정 ID 추가

더 깊은 변경을 위해 컴포넌트 아키텍처는 의도적으로 단순합니다. 깊게 중첩된 추상화나 복잡한 상태 관리 패턴이 없습니다. 각 컴포넌트는 하나의 일을 하고, props를 읽고, HTML을 렌더링합니다.

## 디자인 철학에 대한 참고

이 테마의 시각적 단순함은 의도적이지만, 엔지니어링적 단순함과는 다릅니다. 내부적으로 이 테마는 놀라울 정도로 많은 것을 처리합니다: 반응형 타이포그래피 스케일, 라이트 및 다크 모드 모두에서의 접근 가능한 색상 대비, 올바른 시맨틱 HTML 구조, 올바른 제목 계층 구조, 그리고 휴대폰부터 울트라와이드 모니터까지 다양한 화면에서의 읽기 경험에 대한 세심한 주의.

좋은 디자인은 보이지 않습니다. 이 테마에서 글을 읽을 때 테마를 전혀 의식하지 않고 글 자체를 즐기게 된다면 — 바로 그것이 의도한 대로 디자인이 작동하는 것입니다.

즐거운 글쓰기 되세요.</content:encoded><category>Tutorial</category><category>hello</category><category>astro</category></item></channel></rss>