<?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 blog: AI 논문, agent system, software infrastructure를 인과 사슬로 읽는 개인 기술 블로그입니다.</description><link>https://justinhuangai.github.io/</link><item><title>《Externalization in LLM Agents》: LLM Agent의 인지적 외부화</title><link>https://justinhuangai.github.io/ko-KR/posts/externalization-in-llm-agents-memory-skills-protocols-and-harness-engineering/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko-KR/posts/externalization-in-llm-agents-memory-skills-protocols-and-harness-engineering/</guid><description>Externalization in LLM Agents를 cognitive artifacts 관점에서 읽는다. Agent의 진전은 점점 더 memory, skills, protocols, harness를 모델 밖의 인프라로 옮기는 일에 가까워지고 있다.</description><pubDate>Wed, 13 May 2026 08:00:00 GMT</pubDate><content:encoded>Agent 이야기는 자주 모델 순위로 돌아간다.

어떤 모델이 더 잘 추론하는지, 어떤 모델의 컨텍스트 창이 더 긴지, 어떤 모델이 코딩 benchmark에서 이겼는지. 모두 중요한 질문이다. 하지만 [“Externalization in LLM Agents: A Unified Review of Memory, Skills, Protocols and Harness Engineering”](/papers/2604.08224v1.pdf)은 조금 다른 중심을 가리킨다.

이 논문이 묻는 질문은 “모델이 무엇을 더 배울 수 있는가”가 아니다.

**모델이 더 이상 혼자 짊어지지 않아도 되는 인지적 부담은 무엇인가?**

그래서 핵심 단어가 `externalization`이다. 단순히 LLM 주변에 컴포넌트를 몇 개 붙인다는 뜻이 아니다. 작업의 표현 방식을 바꾼다는 뜻에 가깝다. 인간이 종이, 지도, 달력, 컴퓨터를 쓰는 방식도 그렇다. 외부 도구는 뇌 자체를 키우지 않는다. 대신 문제의 모양을 바꾼다. 회상은 인식으로 바뀌고, 암산은 기호 조작으로 바뀌며, 즉흥적 판단은 절차 수행으로 바뀐다.

LLM Agent도 비슷한 이동을 겪고 있다.

![논문 Figure 1: LLM Agent 설계 원리로서의 외부화](/images/posts/externalization-in-llm-agents/figure-1-externalization-overview.webp)

*Figure 1, from Zhou et al., [arXiv:2604.08224](https://arxiv.org/abs/2604.08224), [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/), format-converted for web display, content unchanged.*

## 이 survey가 정리해 주는 것

이 논문은 새 모델 논문도 아니고 benchmark 논문도 아니다. 가치는 개념 정리에 있다. 이미 벌어지고 있는 engineering migration에 이름을 붙인다.

초기에는 능력을 주로 weights 안에 있는 것으로 봤다. 더 큰 모델, 더 좋은 pretraining, 더 강한 alignment가 중심이었다. 이후 능력은 점점 context 안에서 조직되기 시작했다. prompt, few-shot 예시, RAG, reasoning trace, tool description이 중요해졌다. 지금의 실전 Agent 시스템은 더 두꺼운 runtime에 의존한다. memory store, file system, skill library, tool protocol, sandbox, approval gate, log, evaluator, sub-agent orchestration이 모두 포함된다.

논문은 이 흐름을 이렇게 압축한다.

**weights → context → harness**

![LLM Agent의 인지적 외부화 지도](/images/posts/externalization-in-llm-agents/cognitive-externalization-map.webp)

이 그림의 핵심은 하나의 공학적 흐름이다. 능력은 모델 weights 안에만 있어서는 안 된다. 초기 시스템은 weights에 더 많이 의존했고, context engineering은 한 번의 실행을 임시로 조직할 수 있게 했다. 실제 Agent는 장기적인 신뢰성을 위해 harness가 필요하다. Memory는 상태를 보존하고, skills는 절차를 담고, protocols는 tool과 Agent 사이의 상호작용을 구조화하며, governance는 권한, 감사, 실패 복구를 맡는다.

따라서 핵심 질문은 모델이 얼마나 강한가에만 있지 않다. 외부 runtime이 task를 안정적으로 표현하고, 제약하고, 복구할 수 있는지가 함께 중요하다.

## Memory는 시간을 외부화한다

가장 이해하기 쉬운 외부화는 memory다.

Agent가 context window에만 의존한다면, 매 실행은 임시 작업 공간에 의존한다. 더 긴 context는 도움이 되지만 선택 문제를 없애지는 못한다. 무엇을 넣을 것인가, 무엇을 잊을 것인가, 무엇이 낡은 정보인가, 무엇이 노이즈인가. 더 중요한 점은 context가 기본적으로 일시적이라는 것이다. 외부 상태가 없으면 작업이 끝난 뒤 많은 경험이 사라진다.

외부 memory는 문제를 “모델이 이것을 기억할 수 있는가”에서 “모델이 검색된 올바른 상태를 인식하고 사용할 수 있는가”로 바꾼다. 이것이 cognitive artifacts의 패턴이다. 쇼핑 목록은 생물학적 기억력을 키우지 않는다. 기억 과제를 바꾼다.

Agent에게 memory는 사용자 선호, 프로젝트 규칙, 과거 결정, 실패 궤적, 도메인 지식, 반복되는 제약을 저장할 수 있다. 어려운 부분은 저장 그 자체가 아니다. 무엇을 쓸지, 언제 검색할지, 얼마나 주입할지, 어떻게 압축할지, 오래된 상태가 새 판단을 오염시키지 않게 할지를 정하는 것이다.

따라서 memory는 단순히 더 긴 context의 대체물이 아니다. 시간적 연속성을 위한 인프라다.

## Skills는 절차를 외부화한다

두 번째 외부화는 skills다.

여기서 skill은 “모델이 어떤 도구를 호출할 수 있다”는 뜻이 아니다. 재사용 가능한 절차적 지식이다. step, heuristic, stopping condition, escalation rule, recovery pattern, safety constraint가 포함될 수 있다. 성숙한 skill은 Agent에게 이 종류의 작업을 보통 어떻게 수행해야 하는지 알려준다.

이는 tool use와 다른 추상화 계층이다. Tool은 action을 제공한다. Protocol은 action이 어떻게 발견되고 호출되는지 정의한다. Skill은 그 action들을 어떻게 조직해 반복 가능한 작업으로 만들지를 담는다.

![논문 Figure 5: 외부화된 절차적 전문성으로서의 skills](/images/posts/externalization-in-llm-agents/figure-5-skills-lifecycle.webp)

*Figure 5, from Zhou et al., [arXiv:2604.08224](https://arxiv.org/abs/2604.08224), [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/), reproduced unchanged.*

Software engineering agent를 보면 이 점이 매우 구체적이다.

모델은 코드를 수정하고, 테스트를 실행하고, 로그를 읽고, PR 설명을 쓰는 방법을 알고 있을 수 있다. 하지만 안정적인 실행에는 로컬 절차가 필요하다. 어떤 파일을 먼저 읽을지, 사용자 변경을 어떻게 보존할지, 언제 `rg`를 쓸지, 언제 전체 검증을 돌릴지, 이미 staged된 변경은 어떻게 다룰지, 어떤 명령은 조심해야 하는지가 모두 중요하다.

이 모든 것을 매번 모델이 즉석에서 다시 추론하게 두면 Agent의 행동에는 쉽게 드리프트가 생긴다. 그것을 skill로 외부화하면 작업은 더 이상 “workflow를 새로 발명하기”가 아니라 “검증된 workflow를 선택하고 따르기”가 된다.

그래서 skills는 과소평가되기 쉽다. 화려하지 않지만 variance를 직접 낮춘다.

## Protocols는 상호작용 질서를 외부화한다

세 번째 외부화는 protocols다.

Protocol이 없으면 Agent의 상호작용은 대부분 자유 텍스트 협상이다. 모델이 tool call을 원한다고 말한다. 도구가 텍스트를 반환한다. 다른 Agent가 그 텍스트의 의미를 추론한다. 데모에서는 작동할 수 있지만 production에서는 취약하다.

Protocol은 모호한 상호작용을 machine-readable contract로 바꾼다. Tool discovery, argument schema, permission, error, delegation, lifecycle, user approval이 prompt 관습만으로 유지되어서는 안 된다.

가치는 interoperability에만 있지 않다. Protocol은 governance surface도 만든다. 상호작용이 구조화되면 시스템은 validate, audit, replay, monitor, restrict를 할 수 있다. 자유 텍스트는 유연하지만 다루기 어렵다.

## Harness는 인지 환경이다

논문의 가장 강한 정리는 memory, skills, protocols를 harness engineering 안에 놓는 것이다.

Harness는 모델 주변의 얇은 wrapper가 아니다. Agent가 실제로 동작하는 인지 환경이다. control loop, context budget, permission model, sandbox, human approval, log, evaluation hook, failure recovery, sub-agent orchestration이 여기서 작동한다.

![논문 Figure 3: harnessed LLM agent의 외부화 아키텍처](/images/posts/externalization-in-llm-agents/figure-3-harnessed-agent-architecture.webp)

*Figure 3, from Zhou et al., [arXiv:2604.08224](https://arxiv.org/abs/2604.08224), [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/), reproduced unchanged.*

중요한 점은 harness가 memory, skills, protocols 옆에 놓인 또 하나의 모듈이 아니라는 것이다. Harness는 이 외부화 구조들을 호스팅하고 조정하는 runtime이다. Memory는 상태를 제공한다. Skills는 절차를 제공한다. Protocols는 상호작용 구조를 제공한다. Harness는 언제 무엇을 로드할지, 어떻게 상호작용시킬지, 어떤 제약을 적용할지를 결정한다.

그래서 성숙한 Agent는 점점 작은 operating environment처럼 보인다. 자원, 권한, lifecycle, log, policy, recovery path를 가진다. 단순히 “모델 + 도구 목록”이 아니다.

## 능력의 경계가 이동하고 있다

이 논문의 핵심 전환은 “능력은 어디에 있는가”라는 질문을 바꾸는 것이다.

능력이 weights에만 있다면 Agent 개선은 더 좋은 모델 사용, fine-tuning, retraining이 된다. 능력이 context에도 있다면 prompt와 retrieval이 중요한 engineering surface가 된다. 능력이 harness에도 있다면 system design 자체가 capability의 일부가 된다.

이것은 모델을 덜 중요하게 보는 관점이 아니다. 오히려 강한 모델일수록 좋은 외부 구조 안에 놓을 가치가 크다. LLM은 주어진 정보를 종합하고 판단하고 일반화하는 데 강하다. 그러나 persistent memory, repeatable procedure, permission management, long-lived state, cross-system coordination을 자연스럽게 안정적으로 수행하는 것은 아니다.

Externalization은 편법이 아니다. 실제 경계를 인정하고 작업을 다시 쓰는 engineering이다.

좋은 Agent 시스템은 모델이 매번 처음부터 방법을 찾게 만들지 않는다. 지속 가능한 상태는 memory로, 재사용 가능한 절차는 skills로, 관리 가능한 교환은 protocols로, runtime reliability는 harness로 옮긴다.

## 외부화의 비용도 있다

이 프레임워크는 설득력 있지만 외부화는 공짜가 아니다.

Memory는 오래된 상태, privacy boundary, retrieval pollution을 만든다. Skills는 낡거나 과적합될 수 있고, 잘못 조합되면 위험해질 수 있다. Protocols는 호환되지 않는 표준으로 조각날 수 있고, 시스템을 경직된 interface에 묶을 수도 있다. Harness는 복잡도를 키운다. approval gate, log, sandbox, policy, subroutine이 많아질수록 engineering discipline이 필요하다.

평가 문제도 있다. Agent capability가 모델과 외부 인프라에 분산되어 있다면 우리는 정확히 무엇을 측정하는가? 같은 모델도 다른 harness 안에서는 전혀 다르게 행동할 수 있다. “model capability”와 “agent capability”는 더 이상 같은 말이 아니다.

그래서 이 논문의 위치가 분명하다. 외부화가 모든 문제를 해결한다고 말하지 않는다. 신뢰할 수 있는 agency는 모델과 환경의 공동 설계에서 나온다고 말한다.

## 실용적 판단

이 survey의 핵심 문장은 이것입니다. **Agent 발전의 한 축은 인지 부담을 모델 가중치에서 검사 가능하고, 재사용 가능하고, 거버넌스 가능한 외부 구조로 옮기는 것이다.**

모델이 중요하지 않다는 뜻은 아닙니다. 모델은 여전히 이해, 계획, 생성의 상한을 정합니다. 하지만 Agent가 장기 과제에 들어가면 신뢰성은 매번 즉석 추론에만 의존할 수 없습니다. 상태는 저장되어야 하고, 절차는 재사용 가능해야 하며, 도구 호출에는 protocol이 필요하고, 권한과 실패는 harness가 받아야 합니다.

Externalization은 과제를 &quot;모델이 매번 알아내게 하라&quot;에서 &quot;시스템이 남길 수 있는 것을 남기게 하라&quot;로 바꿉니다. Memory는 시간을 보존합니다. Skills는 절차를 보존합니다. Protocols는 상호작용 질서를 보존합니다. Governance는 경계를 보존합니다. Harness는 이 조각들을 runtime environment로 묶습니다.

다음에 Agent 시스템을 볼 때는 어떤 모델을 쓰는지만 묻지 않는 편이 좋습니다. 어떤 인지 부담을 모델 밖으로 옮겼는지, 어떤 외부 구조가 검사 가능하고 업데이트 가능하며 rollback 가능한지 물어야 합니다. 이 질문이 모델 이름보다 실제 능력에 더 가깝습니다.</content:encoded><category>Paper Reading</category><category>paper-reading</category><category>agents</category><category>externalization</category><category>harness-engineering</category><category>memory</category><category>skills</category><category>protocols</category><category>LLM</category></item><item><title>《AutoCodeBench》: LLM이 코드 벤치마크를 자동 생성할 때</title><link>https://justinhuangai.github.io/ko-KR/posts/autocodebench-large-language-models-are-automatic-code-benchmark-generators/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko-KR/posts/autocodebench-large-language-models-are-automatic-code-benchmark-generators/</guid><description>AutoCodeBench에서 왜 Elixir 열이 눈여겨볼 만한지, 그리고 그것이 자동 생성 다국어 코드 benchmark의 난도 등가성 논의를 어떻게 이끄는지</description><pubDate>Sun, 19 Apr 2026 08:19:00 GMT</pubDate><content:encoded>코드 평가의 첫 질문은 누가 1등인가가 아닙니다. 문제는 과제가 어디서 왔는가입니다. benchmark가 모델에 의해 생성되고, 번역되고, 필터링된다면 점수는 더 이상 모델이 문제를 푼 결과만이 아닙니다. 문제를 만든 시스템의 산물이기도 합니다.

[《AutoCodeBench》](/papers/2508.09101v1.pdf)를 읽을 가치가 있는 이유는 자동 다국어 코드 benchmark의 어려운 문제를 드러내기 때문입니다. 언어 사이에서 &quot;어려움&quot;이 정말 동등하게 유지되는가입니다. Elixir 열이 중요한 것은 LLM이 Elixir를 가장 잘한다는 증거라서가 아니라, benchmark pipeline의 구조적 변수를 드러내기 때문입니다.

## 먼저 이 순위표가 무엇을 보여주는지

먼저 논문 원표를 직접 보는 편이 이해가 쉽다.

![AutoCodeBench 논문 Table 4: 20개 프로그래밍 언어에서의 Pass@1](/images/posts/autocodebench-table-4.webp)

원문 Table 4에서는 각 행이 하나의 참가 대형 모델이고, 각 열이 하나의 프로그래밍 언어다.

둘이 만나는 칸의 숫자는 그 언어 문제를 그 모델에게 줬을 때, 첫 번째로 낸 답안만 보고 몇 문제가 한 번에 통과했는지를 뜻한다. 논문은 이 지표를 Pass@1이라고 부른다. 여기서 `@1`은 말 그대로 &quot;한 번만 기회가 있다&quot; 정도로 이해하면 된다.

맨 위에는 Current Upper Bound라는 특수한 행이 있다. 이 행은 특정 모델의 결과가 아니다. 참가한 모델들 중 어느 하나라도 맞힌 문제를 모두 합집합으로 모아 계산한 통과율이다. 즉 어떤 문제가 어떤 모델이든 한 번이라도 맞혔다면, 그 문제는 이 상한에 들어간다. 논문 표 주석에도 이 점이 명시돼 있다.

Elixir는 총 198문항이고, Current Upper Bound는 97.5다. 거칠게 계산하면 약 193개의 Elixir 문제가 적어도 한 모델에는 풀렸다는 뜻이다. 같은 행에서 Kotlin은 89.5, C#은 88.4, Racket은 88.3, Python은 63.3이다.

여기서 가장 쉽게 빠지는 함정이 있다. Elixir 97.5와 Python 63.3을 바로 이어서 &quot;Elixir가 Python보다 대형 언어 모델에 더 적합하다&quot;라고 쓰면 안 된다.

그건 엄밀한 결론이 아니다.

언어마다 문제의 출처도 다르고, 생성 방식도 다르며, 난도 필터링이 작동하는 방식도 같지 않을 수 있기 때문이다. 언어 열의 숫자만 보고 언어 자체의 우열을 말해 버리면 benchmark 결과를 과잉 해석하게 된다.

정말 물어야 할 질문은 따로 있다.

**같은 모델들, 같은 평가 파이프라인 안에서 왜 Elixir 열은 이렇게 안정적으로 높은 위치에 머무는가?**

## 이건 특정 모델 하나의 우연이 아니다

먼저 한 가지 설명을 지워 보자. 혹시 이것이 단지 Current Upper Bound라는 통계량의 성격 때문일 수도 있다. 여러 모델의 정답을 합집합으로 모으면 숫자가 올라가는 것은 당연하다.

그렇다면 개별 모델 행을 봐야 한다.

Claude Opus 4 추론 모드에서 Elixir는 80.3이고, C#은 74.9, Kotlin은 72.5다. Claude Sonnet 4 비추론 모드에서는 Elixir가 74.2, C#이 72.9, Kotlin이 72.0이다. DeepSeek-R1-0528 추론 모드에서는 Python이 38.8인데 Elixir는 77.3이다.

여기서 비교하고 있는 것은 &quot;Claude 대 Elixir&quot;가 아니다. Claude는 모델이고 Elixir는 언어다. 둘은 같은 차원 위에 있지 않다.

실제로 비교하는 것은 이것이다.

**같은 모델을 기준으로 가로로 봤을 때, 언어별 성능이 어떻게 달라지는가.**

Table 4를 행 단위로 가로로 훑어 보면, Elixir는 계속 높은 점수 쪽에 나타난다. 어떤 한 모델의 단발성 흔들림이 아니라, 여러 모델 결과에서 반복해서 나타나는 언어 열 현상이다.

그래서 문제는 leaderboard에서 언어별 열로 이동한다.

## 문제는 어떻게 만들어졌는가

이 현상을 이해하려면 AutoCodeBench의 생성 파이프라인으로 돌아가야 한다.

논문은 AutoCodeGen이라는 자동화 파이프라인을 제안한다. Stack-Edu 안의 실제 코드 조각에서 씨앗을 뽑고, 모델이 그것을 독립적으로 실행 가능한 완전한 풀이로 확장하도록 한 다음, 공개 테스트와 비공개 테스트 입력을 생성한다. 그 풀이와 테스트를 다국어 샌드박스에서 실행해 실제 출력을 얻고, 마지막에는 풀이와 테스트 함수로부터 역으로 문제 설명을 생성한다.

이 설계의 핵심은 테스트 출력이 모델이 상상해서 적은 값이 아니라는 점이다. 실제로 코드를 실행해서 나온 결과다.

바로 이 점 때문에 AutoCodeBench는 &quot;모델에게 문제와 테스트를 직접 쓰게 한다&quot;는 방식보다 더 믿을 만하다. 모델은 후보를 만들고, 샌드박스는 사실을 제공한다.

다만 20개 언어가 완전히 같은 방식으로 생성된 것은 아니다.

논문은 이 부분을 분명히 적고 있다. Python, C++, Shell, Java, JavaScript, Go의 6개 언어는 완전한 AutoCodeGen 파이프라인을 직접 사용한다. 나머지 14개 언어도 이론상 같은 흐름을 탈 수 있지만, 자원과 다양성이 부족해서 논문은 근사 언어 번역을 사용했다. Table 3에서 Elixir의 번역 경로는 Python → Elixir다.

여기서 아주 중요한 변수가 생긴다.

**Elixir 문제는 Elixir 생태계의 원생 문제라기보다, Python 문제를 Elixir로 옮긴 문제일 가능성이 크다.**

번역 자체가 문제라는 뜻은 아니다.

문제는 번역 이후에도 난도가 다른 언어와 같은 수준으로 유지되느냐는 점이다.

## 난도 필터에는 언어별 사각지대가 있다

AutoCodeGen에는 난도를 제어하는 장치가 있다.

각 문제에 대해 DeepSeek-Coder-V2-Lite로 10번 샘플링하고, 샌드박스에서 정답을 검증한다. 10번 모두 통과하면 그 문제는 너무 쉽다고 보고 버린다. 논문은 그런 문제는 평가 가치가 낮다고 본다.

이 논리는 인기 언어에서는 더 잘 작동한다.

필터링 모델이 Python, Java, C++에 익숙하다면, 10번 모두 맞히는 문제는 실제로 쉬운 문제일 가능성이 높다. 반대로 자꾸 틀리는 문제는 어느 정도 난도가 있을 가능성이 높다.

하지만 Elixir처럼 저자원 언어로 가면 상황이 복잡해진다.

논문 Section 3.3은 인기 언어와 저자원 언어를 따로 비교한다. 인기 언어 그룹은 Python, C++, Java, C#이고, 저자원 언어 그룹은 Racket, Shell, Elixir, TypeScript다. 결과는 이렇다. 인기 언어 그룹에서는 모델 평균 Pass@1 차이가 50.4에서 53.8로 상대적으로 작고, 저자원 언어 그룹에서는 45.3에서 62.0으로 훨씬 넓다.

논문은 이어서 중요한 단서를 하나 제시한다. 상위권 모델들이 저자원 언어에서 훨씬 더 잘 나오는 이유는, DeepSeek-Coder-V2-Lite가 저자원 언어에서 충분히 강하지 못해 쉬운 문제를 제대로 걸러내지 못했기 때문일 수 있다는 것이다.

이 문장은 꽤 중요하다.

논문이 직접 &quot;Elixir 점수가 높은 건 문제가 쉬워서다&quot;라고 말하는 것은 아니다. 하지만 아주 그럴듯한 기제 설명은 제공한다.

**쉬운 문제를 걸러내는 데 쓰인 모델이 저자원 언어에서는 충분히 강하지 않을 수 있다. 그 결과 강한 모델에게는 그다지 어렵지 않은 문제들이 같은 강도로 걸러지지 않은 채 남았을 수 있다.**

이 설명은 &quot;대형 언어 모델이 Elixir를 특히 잘한다&quot;는 설명보다 훨씬 더 안정적이다.

Elixir의 높은 점수는 단순한 언어 숙련도만이 아니라, 세 가지 요인이 동시에 겹친 결과일 수 있다.

첫째, 문제는 Python → Elixir의 근사 번역에서 왔다.

둘째, 난도 필터는 저자원 언어에서 인기 언어만큼 신뢰롭지 않을 수 있다.

셋째, 상위 모델과 필터링 모델 사이의 능력 차이가 저자원 언어에서 더 크게 드러난다.

이 세 가지를 함께 놓고 보면, 왜 Elixir 열이 유난히 밝게 보이는지 훨씬 자연스럽게 설명된다.

## Lite 버전은 다른 방향의 증거를 준다

AutoCodeBench에는 AutoCodeBench-Lite라는 경량 버전도 있다.

Lite의 구성 방식은 또 다른 단서를 제공한다. 논문은 먼저 모든 모델의 풀이 결과를 모은 뒤, 각 문제가 몇 개 모델에 의해 풀리는지 기준으로 정렬한다. 두 개 미만의 모델만 푼 문제는 먼저 버리고, 남은 문제들 가운데서 통과 모델 수가 적은 것부터 약 1,500개를 고른다. 논문의 의도는 &quot;최소한 어떤 모델은 풀지만, 여전히 모델 간 차이를 보여 주는 문제&quot;를 남기는 데 있다.

전체 AutoCodeBench에서 Elixir 문제는 198개다.

Lite 버전으로 가면 Elixir는 61개만 남는다. 20개 언어 중에서도 가장 적은 쪽에 속한다. 이보다 적은 것은 JavaScript의 57개와 PHP의 60개뿐이다.

이 숫자 하나만으로 &quot;Elixir 문제는 다 쉽다&quot;를 증명할 수는 없다.

Lite에 들어가지 못하는 이유는 두 가지일 수 있기 때문이다. 너무 적은 수의 모델만 풀어서 첫 단계에서 제거됐을 수도 있고, 너무 많은 수의 모델이 풀어서 통과 수가 낮은 문제부터 뽑는 과정에서 뒤로 밀렸을 수도 있다.

하지만 Elixir의 Current Upper Bound가 97.5까지 올라가 있고, 많은 개별 모델 행에서도 Elixir 점수가 높다는 점을 함께 놓고 보면, Lite로 줄어드는 이 변화는 적어도 하나의 간접 신호는 제공한다.

**전체 Elixir 문제 집합의 난도 분포는 다른 언어들과 완전히 같지 않을 가능성이 높다.**

핵심은 바로 이 부분이다.

이 말은 Elixir 문제가 &quot;잘못됐다&quot;는 뜻도 아니고, AutoCodeBench가 &quot;믿을 수 없다&quot;는 뜻도 아니다.

자동 생성, 근사 번역, 모델 기반 필터링으로 benchmark를 만들 때, 생성 파이프라인 자체가 언어별 난도 분포를 바꿔 놓을 수 있다는 뜻이다.

## 더 은밀한 편향이 하나 더 있다

논문 Section 4.2는 모델 편향 문제도 다룬다.

전체 생성 파이프라인은 DeepSeek 계열 모델에 크게 의존한다. DeepSeek-V3-0324는 코드 생성을 담당하고, DeepSeek-R1-0528은 품질 검수를 담당하는 Critic 역할을 한다. 논문도 이 점이 DeepSeek 계열 모델에 유리한 편향을 줄 수 있다고 인정한다. 이를 완화하기 위해 쉬운 문제 필터링 단계에는 DeepSeek-Coder-V2-Lite를 넣어 일종의 &quot;push-and-pull&quot; 균형을 만들려고 했다.

더 중요한 점은, 정량 결과를 보면 이것이 단순히 &quot;출제자가 유리하다&quot;는 한 문장으로는 끝나지 않는다는 것이다.

Table 7의 단계별 결과를 보면 Critic 필터링 단계는 실제로 DeepSeek-R1-0528의 성능을 올려 준다. 하지만 o3와 Gemini 2.5 Pro의 상승 폭은 오히려 DeepSeek-V3-0324보다 더 크다. 논문의 최종 판단도 매우 절제돼 있다. 자동 파이프라인이 DeepSeek 계열에 유리한 편향을 줄 수는 있지만, 영향은 작아 보인다는 것이다.

이 부분은 Elixir와 똑같은 문제는 아니지만, 둘 다 같은 핵심을 가리킨다.

**자동화된 benchmark는 중립적인 기계가 아니다.**

누가 문제를 만들고, 누가 검수하고, 누가 쉬운 문제를 걸러내고, 어떤 언어는 원생 생성이고 어떤 언어는 번역 생성인지. 이런 모든 요소가 데이터 안에 흔적을 남긴다.

그리고 결국 그 흔적은 조용히 리더보드로 들어간다.

## Elixir 97.5는 무엇을 말해 주는가

이 숫자가 뜻하지 않는 것은 &quot;대형 언어 모델은 Elixir를 가장 잘한다&quot;는 말이다.

또 &quot;AutoCodeBench에는 문제가 있다&quot;라고 쓰는 것도 맞지 않다.

더 정확한 문장은 이쪽에 가깝다.

**AutoCodeBench는 자동 생성 다국어 benchmark의 핵심 난점을 드러낸다. 언어 간 난도 등가성은 겉보기보다 훨씬 어렵다.**

표면만 보면 20개 언어가 균형 있게 배치돼 있다. 언어마다 대략 200개 문제다.

하지만 개수 균형이 곧 난도 등가성을 의미하지는 않는다.

Python 문제를 Elixir로 번역했을 때, 그것이 여전히 Elixir 생태계의 실제 문제를 대표하는가?

필터링 모델이 Python에서는 쉬운 문제를 잘 걷어 낸다면, Elixir에서도 같은 날카로움으로 걷어 낼 수 있는가?

샌드박스는 실행 결과를 검증할 수 있지만, 문제 설명, 언어 관습, 인터페이스 설계, 표준 라이브러리 사용 방식까지도 모든 언어에서 똑같이 자연스럽고 공정한가?

이 질문들은 총점 표에는 직접 나타나지 않는다.

하지만 Elixir 열은 그 질문들을 다시 표면으로 끌어올린다.

## 이 논문에서 정말 쓸 만한 부분

AutoCodeBench의 가치는 모델 순위를 매긴 것에만 있지 않다.

더 중요한 기여는 benchmark 생산 방식을 한 단계 앞으로 밀었다는 데 있다. 모델이 문제를 생성하고, 샌드박스가 출력을 검증하고, Critic이 품질을 점검하고, 반복 샘플링이 난도를 걸러낸다. 논문은 AutoCodeBench가 20개 프로그래밍 언어에 걸쳐 3,920개 문제와 37,777개 테스트 케이스를 포함하며, 더 어렵고 더 실용적이며 더 넓은 언어 범위를 갖는 코드 생성 평가셋을 목표로 한다고 분명히 말한다.

이 길은 앞으로도 계속 갈 가능성이 높다.

코드 평가는 본질적인 장점이 있기 때문이다. 코드는 실행할 수 있다. 과제 정의만 충분히 명확하다면 테스트 케이스로 정답을 검증할 수 있다. 글쓰기, 개방형 질의응답, 복잡한 추론과 비교하면, 코드는 &quot;생성 → 실행 → 검증 → 필터링&quot;의 폐쇄 루프를 만들기에 훨씬 적합하다.

하지만 Elixir 열은 자동화가 공짜가 아니라는 사실을 상기시킨다.

문제를 생성하는 모델은 자신이 익숙한 문제 구조를 집어넣는다.

문제를 번역하는 모델은 원천 언어의 과제 모양을 집어넣는다.

난도를 걸러내는 모델은 자기 언어 능력의 한계에 묶인다.

샌드박스는 실행 결과를 검증할 수 있지만, 문제 자체가 각 언어 생태계 안에서 똑같이 자연스럽고 똑같이 공정하다고 보장하지는 못한다.

그래서 앞으로의 코드 benchmark는 어떤 모델이 가장 높은 점수를 냈는가만 물어서는 안 된다. 이 질문도 함께 해야 한다.

- 이 문제들은 어떻게 생성됐는가?
- 어떤 언어는 원생 생성이고, 어떤 언어는 번역 생성인가?
- 난도 필터는 언어마다 같은 수준으로 강한가?
- 인간 검증은 어떤 언어를 덮고 있고, 어떤 언어는 빠져 있는가?

논문의 인간 검증은 Python, C++, Java, JavaScript, Go, Shell의 여섯 언어만 포함하며, 정확도는 87.6%다. Elixir는 이 인간 검증 언어 집합에 들어 있지 않다. 이 디테일 역시 번역 생성 언어의 품질과 난도 분포에 대해 더 촘촘한 후속 검증이 필요하다는 점을 보여 준다.

## 마지막 결론

Elixir가 이 표에서 이렇게 높게 나온다고 해서 곧바로 &quot;대형 언어 모델이 Elixir를 가장 잘한다&quot;라고 말할 수는 없다. 앞에서 본 변수들은 모두 실제로 작동한다. benchmark 생성 과정도 결과에 영향을 주고, 번역 경로도 영향을 주며, 저자원 언어에서 필터가 얼마나 잘 작동하는지도 결과에 영향을 준다.

그런데 그런 요소들을 다 감안하고도 Elixir 열은 따로 떼어 놓고 볼 만하다.

결국 남는 질문은 &quot;이 점수를 어떻게 해석할까&quot;가 아니라 왜 하필 Elixir냐는 것이다.

benchmark 생성 과정, 번역 경로, 난도 필터링 같은 조건을 다 테이블 위에 올려놓고도 Elixir가 여전히 이렇게 눈에 띈다면, Elixir는 적어도 계속 따라가 볼 만하다.

이걸 곧바로 &quot;LLM이 Elixir에 강하다&quot;는 증거로 쓰자는 뜻은 아니다. 다만 그 뒤에 정말로 더 들여다볼 가치가 있는 무언가가 있을 수 있다는 뜻이다.

그게 Elixir의 동시성 모델일 수도 있고, 추상화 방식일 수도 있고, 과제를 표현하는 방식과 언어 특성이 오늘날 대형 모델의 작동 방식과 더 깊게 맞닿아 있을 수도 있다.

OpenAI가 최근 오픈소스로 공개한 [Symphony](https://github.com/openai/symphony) 프로젝트에도 Elixir 구현이 들어 있다.

이 방향은 [왜 Elixir는 AI에 가장 잘 맞는 언어인가](/ko-KR/translations/why-elixir-is-the-best-language-for-ai/)와도 연결된다.</content:encoded><category>Paper Reading</category><category>paper-reading</category><category>autocodebench</category><category>elixir</category><category>code-generation</category><category>benchmark</category><category>LLM</category></item><item><title>《Attention Residuals》: 잔차 연결도 어텐션처럼 만들기</title><link>https://justinhuangai.github.io/ko-KR/posts/attention-residuals/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko-KR/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>Residual connection은 오랫동안 학습 안정성을 위한 통로로 이해됐습니다. 깊은 네트워크에서 gradient가 지나가게 하고, 오래된 표현이 너무 빨리 사라지지 않게 합니다. 하지만 모델을 정보 시스템으로 보면 더 깊은 질문이 생깁니다. 깊이 방향의 정보는 어떻게 라우팅되어야 할까요?

[《Attention Residuals》](/papers/2603.15031v1.pdf)는 residual connection을 안정화 장치가 아니라 layer 사이의 정보 라우팅으로 다시 정의합니다. 질문은 모듈 하나를 더 붙이면 benchmark가 몇 점 오르는가가 아닙니다. sequence dimension에는 attention이 있는데 왜 depth dimension은 아직 fixed addition을 쓰는가입니다.

## 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 showLanguage
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를 제안했고, 그 아이디어를 실제로 학습 가능하고 배포 가능하며 비용까지 계산된 엔지니어링 해법으로 밀어 붙였다.**

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. 실험에서 진짜 봐야 할 것

**주 실험의 개별 점수보다 더 중요한 것은, AttnRes가 스케일링 추세, 학습 동역학, 다운스트림 성능 전반에서 같은 방향의 신호를 보여 준다는 점이다.**

### 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 학습 동역학에서 출력 크기가 더 이상 폭주하지 않는다

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;가 아니라, 깊이 방향에서 입력 의존적인 선택적 집계를 한다는 사실 자체가 효과를 만든다는 점이다.**

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

핵심 ablation 결과를 보면:

- **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 설계가 성능 상한이 아니라는 뜻이다. 이것은 인프라 최적화를 위해 고른 절충안이다. 다시 말해, 저자들은 더 강한 버전을 몰라서 안 쓴 것이 아니라, 확장 가능한 버전을 의식적으로 택했다.

그래서 이 리포트의 실제 trade-off가 드러난다. 본문, 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. 이 보고서가 바꾼 질문

Attention Residuals의 핵심 문장은 이것입니다. **residual connection은 학습 안정성을 위한 통로일 뿐 아니라 layer 사이의 정보 라우팅 규칙이다.**

이 보고서는 기본값처럼 받아들여진 구조를 다시 질문으로 만듭니다. 표준 PreNorm residual은 과거 layer들을 거의 고정 가중치로 더합니다. 학습은 안정적이지만 routing rule은 거칩니다. AttnRes는 묻습니다. sequence dimension은 이미 attention으로 중요한 것을 고르는데, 왜 depth dimension은 아직 fixed addition인가?

이 프레이밍은 &quot;모듈 하나 추가&quot;보다 더 가치 있습니다. Residual connection을 gradient highway 이야기에서 꺼내 정보 시스템 안으로 돌려놓습니다. 각 layer는 어떤 이전 layer에 접근해야 하는가? 어떤 표현은 크게 남기고, 어떤 표현은 낮춰야 하는가? Block AttnRes의 의미도 단순한 memory 절약이 아닙니다. 품질, bandwidth, cache, pipeline 제약 사이에서 학습 가능한 대규모 타협점을 제시합니다.

다음에 architecture change를 볼 때는 benchmark가 몇 점 올랐는지만 보지 않는 편이 좋습니다. 어떤 정보 경로를 다시 정의했는지 물어야 합니다. 중요한 구조 변화는 능력을 직접 추가하기보다 능력이 흐르는 경로를 바꿉니다.

---

**더 읽어보기**

- [《Sequence to Sequence Learning with Neural Networks》](/ko-KR/posts/sequence-to-sequence-learning-with-neural-networks/) (신경망을 이용한 시퀀스-투-시퀀스 학습) — encoder-decoder 패러다임의 출발점
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/ko-KR/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/) (정렬과 번역을 공동으로 학습하는 신경 기계 번역) — attention 메커니즘의 기원
- [《Attention Is All You Need》](/ko-KR/posts/attention-is-all-you-need/) (어텐션만 있으면 충분하다) — attention이 주연이 되고 Transformer가 탄생한 순간
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/ko-KR/posts/bert/) (BERT: 언어 이해를 위한 깊은 양방향 트랜스포머 사전학습) — 사전학습 패러다임의 확립
- [《Scaling Laws for Neural Language Models》](/ko-KR/posts/scaling-laws-for-neural-language-models/) (신경 언어 모델을 위한 스케일링 법칙) — 규모에 관한 수학
- [《Language Models are Few-Shot Learners》](/ko-KR/posts/language-models-are-few-shot-learners/) (언어 모델은 퓨샷 학습자다) — 더 큰 모델은 컨텍스트에서 더 많은 능력을 끌어낸다
- [《Training Compute-Optimal Large Language Models》](/ko-KR/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》: Chinchilla가 바꾼 것</title><link>https://justinhuangai.github.io/ko-KR/posts/training-compute-optimal-large-language-models/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko-KR/posts/training-compute-optimal-large-language-models/</guid><description>Chinchilla 논문 — 왜 대부분의 대형 모델이 과소 학습되었는지, 그리고 컴퓨팅 예산을 현명하게 쓰는 법, 실제 Python 코드 예시 포함</description><pubDate>Wed, 11 Mar 2026 08:58:04 GMT</pubDate><content:encoded>Chinchilla가 반박한 것은 scale이 효과적인가가 아닙니다. 고정된 compute 예산에서 scale을 어떻게 나눠야 하는가였습니다. 2022년의 많은 대형 모델은 비용을 주로 파라미터에 썼고, 데이터는 같은 비율로 늘리지 않았습니다. 문제는 모델이 작아서가 아니라 충분히 학습되지 않았다는 데 있었습니다.

[《Training Compute-Optimal Large Language Models》](/papers/2203.15556v1.pdf)의 핵심은 대형 모델에 반대하는 것이 아닙니다. 파라미터와 데이터가 함께 compute 예산을 채워야 한다는 것입니다. 더 강한 모델은 크기만으로 나오지 않습니다. 파라미터, 데이터, 학습 스텝 사이의 올바른 비율에서 나옵니다.

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

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

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

## 1. 질문

2022년 초, AI 커뮤니티는 [Kaplan et al. (2020)](/ko-KR/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 showLanguage
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 showLanguage
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 showLanguage
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 showLanguage
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 showLanguage
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 showLanguage
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. 이 논문이 바꾼 질문

Chinchilla의 핵심 문장은 이것입니다. **대형 모델에 반대하는 것이 아니라, 파라미터와 데이터가 함께 compute 예산을 채워야 한다는 것이다.**

이 논문은 scaling을 멈추라고 말하지 않았습니다. scaling의 배분을 수정했습니다. Kaplan은 업계에 더 크게 만들 자신감을 줬습니다. Chinchilla는 같은 compute 예산에서 얼마나 크게, 얼마나 오래 학습하는 것이 최적인지 물었습니다. 답은 파라미터를 계속 쌓는 것이 아니라 파라미터 수와 training token을 거의 같은 비율로 키우는 것이었습니다.

이 판단은 model size를 단일 숭배에서 끌어냈습니다. 300B token만 본 280B 파라미터 모델은 1.4T token을 본 70B 파라미터 모델보다 약할 수 있습니다. 능력은 파라미터 수 자체가 아닙니다. compute가 파라미터와 데이터에 함께 흡수된 결과입니다.

다음에 대형 모델 발표를 볼 때는 파라미터 수부터 묻지 않는 편이 좋습니다. 얼마나 많은 token으로 학습했는지, compute가 어떻게 배분됐는지, undertrained인지 물어야 합니다. 진짜 scaling은 모델을 부풀리는 것이 아니라 compute 한 단위가 loss를 낮추는 곳에 놓이게 하는 것입니다.

---

**논문 읽기 시리즈**

- [《Sequence to Sequence Learning with Neural Networks》](/ko-KR/posts/sequence-to-sequence-learning-with-neural-networks/) (신경망을 이용한 시퀀스-투-시퀀스 학습) — 인코더-디코더 패러다임의 확립
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/ko-KR/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/) (정렬과 번역을 공동으로 학습하는 신경 기계 번역) — 어텐션의 기원
- [《Attention Is All You Need》](/ko-KR/posts/attention-is-all-you-need/) (어텐션만 있으면 충분하다) — 어텐션이 주역이 되다: Transformer의 탄생
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/ko-KR/posts/bert/) (BERT: 언어 이해를 위한 깊은 양방향 트랜스포머 사전학습) — 사전 학습 패러다임의 확립
- [《Scaling Laws for Neural Language Models》](/ko-KR/posts/scaling-laws-for-neural-language-models/) (신경 언어 모델을 위한 스케일링 법칙) — 규모의 수학
- [《Language Models are Few-Shot Learners》](/ko-KR/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-KR/posts/scaling-laws-for-neural-language-models/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko-KR/posts/scaling-laws-for-neural-language-models/</guid><description>규모의 수학 — 더 큰 모델이 예측 가능하게 더 나은 이유, 실제 Python 코드 예시 포함</description><pubDate>Sun, 01 Mar 2026 08:45:39 GMT</pubDate><content:encoded>대형 모델 학습은 먼저 예산 배분 문제입니다. 파라미터, 데이터, compute는 모두 비용입니다. 질문은 &quot;클수록 좋다&quot;가 아닙니다. 다음 1달러를 파라미터에 쓸지, 데이터에 쓸지, 더 긴 학습에 쓸지입니다.

[《Scaling Laws for Neural Language Models》](/papers/2001.08361v1.pdf)는 대형 모델 학습을 경험주의에서 예산 함수로 옮겼습니다. 새 아키텍처를 발명한 논문은 아닙니다. 대신 돈을 쓰기 전에 수익 곡선을 추정하는 더 단단한 판단 도구를 줬습니다.

## 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 showLanguage
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 showLanguage
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 showLanguage
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 showLanguage
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 showLanguage
def critical_batch_size(loss: float, b_star: float, l_star: float) -&gt; float:
    return b_star * (l_star / loss) ** 4.8
```

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

## 7. 이 논문이 바꾼 질문

Scaling Laws의 핵심 문장은 이것입니다. **대형 모델 학습은 감각적 시행착오가 아니라 예산 함수다.**

이 논문의 가장 중요한 기여는 특정 지수가 아닙니다. &quot;클수록 좋다&quot;를 경험적 감각에서 추정 가능한 곡선으로 바꾼 것입니다. 파라미터, 데이터, compute는 더 이상 흩어진 엔지니어링 변수가 아니라 같은 장부에서 marginal return을 비교할 수 있는 투자 항목이 됐습니다.

이 논문은 업계에 영원히 더 큰 모델만 만들라고 말하지 않았습니다. 주어진 가정과 관측 범위 안에서 돈을 쓰기 전에 계산하라고 말했습니다. 파라미터를 늘릴지, 데이터를 늘릴지, 학습을 더 할지, 무엇이 loss를 더 낮출 가능성이 큰가? GPT-3의 규모 결정이 덜 무모해 보였던 배경에는 이런 예산 신뢰가 있었습니다.

다음에 모델 scale을 볼 때는 이것이 군비 경쟁인지부터 묻지 않는 편이 좋습니다. 예산 함수가 무엇인지, 병목 변수가 무엇인지, marginal return이 얼마나 남았는지를 물어야 합니다. 성숙한 scaling은 돈을 더 태우는 것이 아니라 돈이 어디에서 능력으로 바뀌는지 아는 것입니다.

---

**논문 읽기 시리즈**

- [《Sequence to Sequence Learning with Neural Networks》](/ko-KR/posts/sequence-to-sequence-learning-with-neural-networks/) (신경망을 이용한 시퀀스-투-시퀀스 학습) — 인코더-디코더 패러다임의 확립
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/ko-KR/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/) (정렬과 번역을 공동으로 학습하는 신경 기계 번역) — 어텐션의 기원
- [《Attention Is All You Need》](/ko-KR/posts/attention-is-all-you-need/) (어텐션만 있으면 충분하다) — 어텐션이 주역이 되다: Transformer의 탄생
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/ko-KR/posts/bert/) (BERT: 언어 이해를 위한 깊은 양방향 트랜스포머 사전학습) — 사전 학습 패러다임의 확립
- [《Language Models are Few-Shot Learners》](/ko-KR/posts/language-models-are-few-shot-learners/) (언어 모델은 퓨샷 학습자다) — 더 큰 모델, 컨텍스트에서 더 잘 능력을 이끌어내다
- [《Training Compute-Optimal Large Language Models》](/ko-KR/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 아키텍처: 셀프호스팅 AI 어시스턴트의 엔지니어링 골격 🦞</title><link>https://justinhuangai.github.io/ko-KR/posts/openclaw-architecture/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko-KR/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-KR/posts/openclaw-ecosystem/)에서 생태계를 다뤘으니, 이번에는 아키텍처를 봅니다.

OpenClaw의 코드베이스는 작지 않습니다. TypeScript 43만 줄입니다. 하지만 볼 지점은 규모가 아니라 복잡성을 어디에 배치했는가입니다. 셀프호스팅 AI 어시스턴트는 20개 이상의 채팅 입구, 여러 Agent, 도구 실행, 권한 경계를 동시에 다뤄야 합니다. 아키텍처의 질문은 &quot;기능을 어떻게 다 넣을까&quot;가 아니라 &quot;어떤 복잡성은 런타임에 남기고, 어떤 복잡성은 모델, CLI, 정책 계층으로 넘길까&quot;입니다.

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

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

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

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

먼저 전체 그림을 봅시다:

![OpenClaw 아키텍처](/images/openclaw-architecture-ko-KR.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; 워크스페이스

여기서 핵심 설계는 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. 도구 시스템: 복잡성을 CLI로 넘기기

OpenClaw의 도구 표면은 의도적으로 작습니다.

많은 AI Agent 프레임워크는 많은 전용 도구를 미리 제공합니다. OpenClaw은 핵심 도구 4개만 남깁니다:

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

이 노선의 전제는 커맨드 라인이 이미 현실 세계의 많은 기능을 감싸고 있다는 것입니다. 날씨는 `curl`로 가져오고, 이메일은 CLI 도구를 호출하고, 데이터베이스는 `psql`로 다룹니다. 시스템은 모든 상황에 전용 도구를 미리 만들지 않고, 범용 실행 인터페이스를 모델에게 줍니다.

대가는 분명합니다. 모델이 어떤 명령을 호출할지, 명령을 어떻게 조합할지, 어디서 멈출지를 알아야 합니다. 복잡성은 사라지지 않았습니다. 도구 목록에서 모델 능력, CLI 생태계, 권한 정책으로 이동했을 뿐입니다.

이 네 가지 핵심 도구 위에 55개의 내장 Skills과 ClawHub 스킬 마켓플레이스가 있습니다. Skills은 반복되는 작업 흐름을 패키징해 Agent가 매번 Bash 단계에서 다시 계획하지 않게 합니다.

**MCP는 주 경로가 아니라 우회 경로에 놓입니다.** MCP는 Anthropic의 도구 프로토콜 표준이고 많은 AI 프레임워크가 도입하고 있습니다. OpenClaw의 주 경로는 CLI/Unix이며, 내장 `mcporter` 브리지를 함께 둡니다. 핵심은 단순히 표준을 거부하는 것이 아니라 도구 생태계의 중심을 커맨드 라인의 조합성에 두는 것입니다.

**자기 확장은 임시 해결을 skill로 남깁니다.** OpenClaw Agent가 못 하는 일을 만나면 skill을 작성하고 설치한 뒤, 문제가 생기면 수정하고 다시 로드할 수 있습니다. 중요한 점은 추상적으로 &quot;더 강해진다&quot;가 아닙니다. 한 번의 해결책이 재사용 가능한 작업 흐름으로 남을 수 있는가입니다.

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

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

```json showLanguage
{
  &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;도구가 적을수록 좋다&quot;가 아닙니다. 모델 능력, CLI 생태계, 권한 정책을 합치면 거대한 전용 도구 목록보다 더 잘 확장될 수 있다는 베팅입니다. 모델이 약하면 Bash는 위험한 표면이 됩니다. 권한을 제대로 묶을 수 있다면 도구 시스템은 매우 얇아집니다.

**궁극적인 시험은 아키텍처가 얼마나 예쁜지가 아니라, 얼마나 안정적으로 돌아가느냐입니다.** 이 분석은 정적 소스코드 리딩에 기반합니다. 실제 성능 -- 고동시성에서 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》: GPT-3와 인컨텍스트 학습</title><link>https://justinhuangai.github.io/ko-KR/posts/language-models-are-few-shot-learners/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko-KR/posts/language-models-are-few-shot-learners/</guid><description>더 큰 모델, 컨텍스트에서 더 잘 능력을 이끌어내다, 실제 Python 코드 예시 포함</description><pubDate>Wed, 11 Feb 2026 08:22:54 GMT</pubDate><content:encoded>GPT-3의 질문은 단순히 모델을 더 키울 수 있는가가 아니었습니다. 과제 적응이 반드시 파라미터 업데이트를 필요로 하는가였습니다. BERT 패러다임에서는 새 과제마다 fine-tuning이 필요했고, 데이터, 학습 파이프라인, 배포 버전이 함께 늘어났습니다.

[《Language Models are Few-Shot Learners》](/papers/2005.14165v4.pdf)의 핵심은 task adaptation을 파라미터 업데이트에서 context로 옮긴 것입니다. 가중치는 고정됩니다. 지시문과 몇 개의 예제가 prompt에 들어갑니다. 과제는 추론 시점에 임시로 조직됩니다.

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

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

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

## 1. 문제 정의

[BERT](/ko-KR/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 showLanguage
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-KR/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 showLanguage
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 showLanguage
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의 핵심 문장은 이것입니다. **task adaptation은 파라미터 업데이트에서 context로 이동할 수 있다.**

이 논문은 단순히 큰 모델이 더 강하다고 말한 것이 아닙니다. 사람과 모델 사이의 인터페이스를 바꿨습니다. 이전에는 모델이 새 과제를 하게 하려면 데이터를 모으고, 파라미터를 fine-tune하고, 새 버전을 배포해야 했습니다. GPT-3는 다른 경로를 보였습니다. 가중치는 고정하고, 지시문, 예제, 입력을 context에 넣어 추론 시점에 적응하게 합니다.

이것이 fine-tuning의 끝은 아닙니다. 적응 비용의 일부를 학습 단계에서 context 설계 단계로 옮긴 것입니다. Prompt는 더 이상 단순한 입력 텍스트가 아닙니다. 가벼운 task program이 됩니다. Context window도 단순한 용량 숫자가 아니라 임시 작업 공간이 됩니다.

다음에 대형 모델을 볼 때는 파라미터 수만 묻지 않는 편이 좋습니다. task adaptation이 어디에 있는지 물어야 합니다. 가중치 안인가, context 안인가, 외부 도구와 workflow 안인가? 이 질문이 모델 크기보다 제품 형태에 더 가깝습니다.

---

**논문 읽기 시리즈**

- [《Sequence to Sequence Learning with Neural Networks》](/ko-KR/posts/sequence-to-sequence-learning-with-neural-networks/) (신경망을 이용한 시퀀스-투-시퀀스 학습) — 인코더-디코더 패러다임의 확립
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/ko-KR/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/) (정렬과 번역을 공동으로 학습하는 신경 기계 번역) — 어텐션의 기원
- [《Attention Is All You Need》](/ko-KR/posts/attention-is-all-you-need/) (어텐션만 있으면 충분하다) — 어텐션이 주역이 되다: Transformer의 탄생
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/ko-KR/posts/bert/) (BERT: 언어 이해를 위한 깊은 양방향 트랜스포머 사전학습) — 사전 학습 패러다임의 확립
- [《Scaling Laws for Neural Language Models》](/ko-KR/posts/scaling-laws-for-neural-language-models/) (신경 언어 모델을 위한 스케일링 법칙) — 스케일의 수학: 왜 더 큰 모델이 예측 가능하게 더 좋은가
- [《Training Compute-Optimal Large Language Models》](/ko-KR/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 생태계: 오픈소스 프로젝트에서 AI assistant platform으로 🦞</title><link>https://justinhuangai.github.io/ko-KR/posts/openclaw-ecosystem/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko-KR/posts/openclaw-ecosystem/</guid><description>OpenClaw를 다시 본다. star는 ecosystem이 아니다. supply friction과 usage friction을 계속 낮추는 structure가 ecosystem이다.</description><pubDate>Tue, 03 Feb 2026 08:09:32 GMT</pubDate><content:encoded>![OpenClaw](/images/openclaw-logo-text-dark.webp)

star는 ecosystem이 아니다.

ecosystem의 첫 번째 질문은 friction이다. developer가 capability를 공급하기 얼마나 어려운가. user가 그것을 발견하고 쓰기 얼마나 어려운가. system이 두 쪽을 얼마나 안정적으로 연결하는가. OpenClaw가 여기서 중요한 이유는 한때 뜨거웠기 때문이 아니라, 하나의 agent runtime을 중심으로 분업 구조가 자라기 시작했기 때문이다.

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

- `ecosystem`: 하나의 repository가 아니라, 같은 core capability 주변에서 역할을 나누는 product와 tool의 묶음
- `runtime`: agent가 계속 실행되며 tool을 조정하고, context를 관리하고, task를 실행하는 핵심 process
- `skill marketplace`: agent가 새 capability를 찾고 설치하는 입구
- `workflow engine`: 반복되는 multi-step task를 재사용 가능한 flow로 포장하는 장치
- `flywheel`: supply, usage, feedback이 서로를 강화하는 growth loop

## 1. Heat보다 Constraint를 먼저 본다

Peter Steinberger는 2011년에 PSPDFKit을 창업해 PDF low-level technology를 오래 만들었다. Apple, Dropbox 같은 고객에게 서비스했고, 이후 일선 개발에서 물러났다가 2025년에 AI product prototype으로 돌아왔다.

Clawdbot은 이 맥락에서 나왔다. 약 한 시간의 prompting으로 project skeleton을 만들고, 11월에 공개했으며, 이름은 Anthropic의 Claude에 대한 nod였다. 2026년 1월 말 Anthropic이 trademark warning을 보냈고, project는 3일 안에 Clawdbot에서 Moltbot, 다시 OpenClaw로 바뀌었다. 이 사건은 48시간 만에 34,000 stars를 만들었다.

하지만 heat는 traffic을 설명할 뿐 ecosystem을 설명하지 못한다. ecosystem은 structure로 판단해야 한다.

## 2. Repository 수가 Ecosystem은 아니다

OpenClaw는 이제 runtime repository 하나가 아니다. 여러 layer로 갈라지기 시작했다.

| Component | Role | Signal |
|------|------|------|
| **OpenClaw** | Core agent runtime | 140k+ stars |
| **ClawHub** | Skill marketplace | 5.4k stars |
| **Lobster** | Workflow engine | ~800 stars |
| **acpx** | Headless command-line tool | ~780 stars |
| **openclaw-ansible** | Automated deployment | ~490 stars |
| **nix-openclaw** | Declarative Nix setup | ~530 stars |

이 표에서 볼 것은 숫자가 아니라 boundary다. runtime, marketplace, workflow, deployment가 하나의 거대한 repository로 다시 합쳐지지 않고 agent lifecycle 주변에서 나뉜다.

platform이 되려면 두 friction을 동시에 낮춰야 한다. supply side에서 capability를 만드는 friction, usage side에서 capability를 발견하는 friction. OpenClaw도 여기서 판단해야 한다.

## 3. 세 가지 구조적 신호

### Channel Coverage: Usage Friction을 낮춘다

WhatsApp, Telegram, Slack, Discord, Signal, iMessage, Feishu, LINE, Matrix 등 20개 이상의 platform이 연결되어 있다. 겉으로는 channel coverage지만, 실제로는 entry point의 위치가 바뀐다.

사용자는 AI assistant를 쓰기 위해 새 app으로 이동할 필요가 없다. agent가 이미 쓰는 chat channel 안으로 들어오고, task는 기존 대화 안에서 생긴다. adoption의 핵심은 기능 수가 아니라 첫 사용의 저항이 충분히 낮은가다.

### ClawHub: Discovery Friction을 낮춘다

skill marketplace는 vector search로 semantic matching을 한다. 사용자는 directory를 뒤질 필요 없이 &quot;email을 보내는 skill&quot;이라고 말하면 후보를 찾을 수 있다.

이것은 discovery 문제를 푼다. supply 문제를 푼 것은 아니다. skill marketplace가 성립하려면 고품질 third-party skill이 계속 나오고, 유지되고, review되고, 신뢰되어야 한다. discovery entrance는 생겼다. supply flywheel은 아직 증명되지 않았다.

### Lobster: Replanning Friction을 낮춘다

multi-step task에서 숨은 비용은 종종 개별 tool call이 아니다. 매번 model이 workflow를 다시 계획하게 만드는 비용이다.

Lobster는 자주 쓰는 작업을 reusable workflow로 포장한다. flow가 검증되면 model이 매번 즉석에서 다시 구성하지 않아도 된다. approval gate도 있어 영향이 큰 step이 blind automation이 되는 것을 줄인다.

가치는 &quot;더 많은 자동화&quot;가 아니다. 반복 작업을 live reasoning에서 inspectable procedure로 옮기는 것이다.

## 4. Risk도 Structure에서 나온다

**Security는 ecosystem의 전제다.** Kaspersky는 512개 vulnerability를 발견했고, 그중 8개는 critical이었다. high-privilege agent, third-party skills, 20개 이상의 entry points가 합쳐지면 attack surface는 자연스럽게 커진다. security debt는 평범한 technical debt가 아니라 trust debt다.

**Business model은 아직 닫히지 않았다.** MIT license, subscription 없음, user-provided API key는 adoption friction을 낮춘다. 동시에 운영 비용 회수를 어렵게 만든다. open-source heat는 자동으로 maintenance budget이 되지 않는다.

**Contribution structure도 봐야 한다.** 방향, product judgment, core implementation이 계속 소수에게 집중되면 ecosystem depth는 single-point dependence에 막힌다. platform과 star project의 차이는 non-founder contribution이 예측 가능하게 늘어나는가에 있다.

## 5. 앞으로 볼 것

OpenClaw의 질문은 더 이상 attention을 모을 수 있느냐가 아니다. 이미 모았다.

이제 볼 것은 네 가지다.

1. security default가 조여지는가: pairing, allowlist, sandbox, approval이 option이 아니라 default가 되는가.
2. skill ecosystem이 supply를 만드는가: quality skill이 계속 publish, install, update, review되는가.
3. non-founder contribution이 늘어나는가: ecosystem이 single-person drive를 벗어나는가.
4. governance가 읽히는가: maintainership, review authority, roadmap decision이 community에 의해 이해되고 계승될 수 있는가.

star는 ecosystem을 증명하지 못한다. supply friction과 usage friction을 계속 낮추는 structure가 ecosystem의 시작을 증명한다.</content:encoded><category>OpenClaw</category><category>AI</category><category>open-source</category><category>openclaw</category></item><item><title>《BERT》: 언어 이해 사전학습 패러다임의 확립</title><link>https://justinhuangai.github.io/ko-KR/posts/bert/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko-KR/posts/bert/</guid><description>사전학습 패러다임의 확립, 실제 Python 코드 예시 포함</description><pubDate>Sat, 31 Jan 2026 08:52:21 GMT</pubDate><content:encoded>BERT의 목표는 더 깊은 Transformer를 만드는 것이 아니었습니다. NLP 과제가 오랫동안 조각나 있던 문제를 겨냥했습니다. 질의응답, 분류, sequence labeling마다 모델 형태와 데이터 인터페이스가 달랐고, 모델이 배운 언어 지식을 깔끔하게 재사용하기 어려웠습니다.

[《BERT》](/papers/1810.04805v2.pdf)의 핵심은 bidirectional Transformer만이 아닙니다. 언어 이해 과제를 pretrained representation의 재사용 문제로 통일했다는 데 있습니다. 먼저 범용 표현을 학습하고, 얇은 task layer로 각 문제에 맞춥니다. 이 전환이 구조 자체보다 더 중요했습니다.

## 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 showLanguage
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 showLanguage
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-KR/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 showLanguage
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 encoder만도 아니고 bidirectional만도 아닙니다. MLM과 NSP로 unlabeled text를 pretraining signal로 만들고, classification, question answering, sequence labeling이 같은 입력 형식과 representation base를 공유하게 한 것입니다.

이것은 NLP의 엔지니어링 단위를 바꿨습니다. BERT 이전에는 각 과제가 별도 프로젝트처럼 보였습니다. BERT 이후 많은 과제는 같은 pretrained representation 위의 얇은 adaptation layer가 됐습니다. 모델은 각 task dataset에서 언어를 다시 배우지 않습니다. 먼저 일반 언어 기반을 갖고, 그 위에서 task boundary를 배웁니다.

다음에 understanding model을 볼 때는 bidirectional인지에서 멈추지 않는 편이 좋습니다. 그 representation이 재사용 가능한가? task 차이는 모델 본체 안에 들어가는가, 아니면 얇은 인터페이스로 압축되는가?

---

**논문 읽기 시리즈**

- [《Sequence to Sequence Learning with Neural Networks》](/ko-KR/posts/sequence-to-sequence-learning-with-neural-networks/) (신경망을 이용한 시퀀스-투-시퀀스 학습) — 인코더-디코더 패러다임의 확립
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/ko-KR/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/) (정렬과 번역을 공동으로 학습하는 신경 기계 번역) — Attention의 기원
- [《Attention Is All You Need》](/ko-KR/posts/attention-is-all-you-need/) (어텐션만 있으면 충분하다) — Attention이 주역이 되다: Transformer의 탄생
- [《Scaling Laws for Neural Language Models》](/ko-KR/posts/scaling-laws-for-neural-language-models/) (신경 언어 모델을 위한 스케일링 법칙) — 스케일의 수학: 왜 더 큰 모델이 예측 가능하게 더 좋은가
- [《Language Models are Few-Shot Learners》](/ko-KR/posts/language-models-are-few-shot-learners/) (언어 모델은 퓨샷 학습자다) — 더 큰 모델, 문맥에서 더 잘 이끌어내는 능력
- [《Training Compute-Optimal Large Language Models》](/ko-KR/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-KR/posts/sequence-to-sequence-learning-with-neural-networks/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko-KR/posts/sequence-to-sequence-learning-with-neural-networks/</guid><description>인코더-디코더 패러다임의 확립, 실제 Python 코드 예시 포함</description><pubDate>Sat, 24 Jan 2026 08:41:08 GMT</pubDate><content:encoded>Seq2Seq의 출발점은 단순한 제약입니다. 입력과 출력의 길이가 모두 고정되어 있지 않습니다. 전통적인 번역 파이프라인은 이를 처리할 수 있었지만, 끝에서 끝까지 함께 최적화하기는 어려웠습니다. [《Sequence to Sequence Learning with Neural Networks》](/papers/1409.3215v3.pdf)는 이 문제를 두 개의 학습 가능한 인터페이스로 바꿨습니다. 하나의 네트워크가 입력 전체를 읽고, 다른 네트워크가 출력을 한 단계씩 생성합니다.

이 논문의 가치는 기계 번역을 한 번에 해결했다는 데 있지 않습니다. end-to-end sequence mapping이 가능하다는 것을 보였다는 데 있습니다. 동시에 약점도 분명했습니다. 모든 정보가 하나의 고정 길이 벡터를 지나야 했습니다. Attention과 Transformer는 이 병목에서 자라났습니다.

## 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 showLanguage
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 showLanguage
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. 이 논문이 바꾼 질문

Seq2Seq의 핵심 문장은 이것입니다. **end-to-end mapping은 가능하지만, 고정 벡터는 병목이 된다.**

이 논문은 기계 번역을 사람이 조립한 파이프라인에서 하나의 시스템으로 학습할 수 있는 mapping 문제로 바꿨습니다. 신경망이 입력 전체를 읽고, 출력을 한 단계씩 생성할 수 있음을 보였습니다. 입력과 출력의 길이가 같을 필요도, 사람이 정렬을 붙일 필요도 없었습니다.

동시에 병목도 분명히 드러났습니다. 원문 정보 전체가 하나의 벡터를 지나야 했습니다. 문장이 길수록 압축 손실은 커집니다. source sentence를 뒤집는 요령은 거리 문제를 완화했지만, 정보가 좁은 문을 지나야 한다는 사실은 바꾸지 못했습니다.

다음에 Seq2Seq를 볼 때는 &quot;encoder-decoder&quot;에서 멈추지 않는 편이 좋습니다. 이 시스템은 정보를 어디에 압축하는가? 그 압축 지점이 다음 아키텍처가 우회해야 할 병목이 되는가?

---

**논문 읽기 시리즈**

- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/ko-KR/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/) (정렬과 번역을 공동으로 학습하는 신경 기계 번역) — Attention의 기원
- [《Attention Is All You Need》](/ko-KR/posts/attention-is-all-you-need/) (어텐션만 있으면 충분하다) — Attention이 주인공이 되다: Transformer의 탄생
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/ko-KR/posts/bert/) (BERT: 언어 이해를 위한 깊은 양방향 트랜스포머 사전학습) — 사전학습 패러다임의 확립
- [《Scaling Laws for Neural Language Models》](/ko-KR/posts/scaling-laws-for-neural-language-models/) (신경 언어 모델을 위한 스케일링 법칙) — 스케일의 수학: 왜 더 큰 모델이 예측 가능하게 더 좋은가
- [《Language Models are Few-Shot Learners》](/ko-KR/posts/language-models-are-few-shot-learners/) (언어 모델은 퓨샷 학습자다) — 더 큰 모델, 맥락에서 능력을 이끌어내는 데 더 뛰어나다
- [《Training Compute-Optimal Large Language Models》](/ko-KR/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-KR/posts/clawdbot/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko-KR/posts/clawdbot/</guid><description>Clawdbot은 self-hosted AI agent의 핵심 문제가 모델 지능이 아니라 control, context, execution rights의 소유권임을 보여준다.</description><pubDate>Fri, 16 Jan 2026 08:34:57 GMT</pubDate><content:encoded>AI assistant의 첫 번째 질문은 채팅을 잘하느냐가 아니다. 누가 그것을 소유하느냐다.

memory, context, tool execution이 모두 플랫폼 서버에 있다면 사용자가 가진 것은 계정이지 자기 소유의 지능 시스템이 아니다. [Clawdbot](https://github.com/clawdbot/clawdbot)의 초기 가치는 여기에 있다. agent를 cloud product에서 끌어내려 사용자가 배포하고, 검사하고, 권한을 철회할 수 있는 환경으로 옮긴다.

코드베이스도 단순한 데모가 아니다. 20만 줄 이상의 TypeScript, macOS, iOS, Android 네이티브 앱, 그리고 50개 이상의 skill module이 있다.

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

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

- `centralized AI assistant`: 대화 기록, memory, control이 주로 플랫폼 쪽에 남는 방식
- `self-hosting`: 소프트웨어를 자신의 machine이나 server에서 실행해 deployment와 data 권한을 되찾는 방식
- `AI agent`: context를 기억하고 tool을 호출하며 task를 실행하는 runtime system
- `chat channel`: WhatsApp, Telegram, Slack, iMessage처럼 이미 쓰고 있는 대화 입구
- `skill`: agent에 설치하는 새 능력으로, 보통 절차, tool call, boundary condition을 포함한다

## 1. Interface보다 Control이 중요하다

대부분의 AI 제품은 문제를 interface 경쟁으로 만든다. 어느 chat window가 더 매끄러운가, 어느 model이 더 잘 답하는가, 어느 subscription이 더 싼가.

Clawdbot은 다른 질문을 던진다. AI가 사용자 대신 오래 일해야 한다면, 왜 그 memory와 execution rights가 기본적으로 third-party platform에 있어야 하는가?

이것은 privacy 구호가 아니다. agent가 file을 읽고, tool을 호출하고, account에 접근하고, long-term preference를 기억하기 시작하면 더 이상 편리한 웹페이지가 아니다. 개인 infrastructure에 가까워진다. 개인 infrastructure의 핵심은 능력만이 아니라 이동 가능성, 감사 가능성, 중지 가능성이다.

## 2. Chat Channel은 장식이 아니다

Clawdbot은 agent를 WhatsApp, Telegram, Slack, iMessage 같은 기존 chat surface에 연결한다.

중요한 것은 platform 수가 아니다. task entry point가 새 앱 하나에 갇히지 않는다는 점이다. 사용자는 AI 제품을 떠올리고, 열고, context를 붙여넣을 필요가 없다. agent가 일이 이미 시작되는 대화 안에 들어온다.

이것은 사용 빈도를 바꾼다. 별도 창 안에만 있는 agent는 attention을 두고 경쟁한다. 기존 channel 안에 있는 agent는 task 자체를 두고 경쟁한다.

## 3. Self-Hosting은 자동으로 안전하지 않다

self-hosting은 power를 되돌려준다. 하지만 그 power가 잘 다뤄진다는 보장은 없다.

command를 실행하고, memory를 저장하고, skill을 설치할 수 있는 agent는 공격면도 넓다. prompt injection, 잘못된 tool call, skill supply chain, 넓은 permission은 &quot;model이 틀렸다&quot;를 &quot;system이 잘못 실행했다&quot;로 바꾼다.

Clawdbot의 유용한 교훈은 단순히 local에서 돈다는 점이 아니다. agent를 permission boundary가 있는 system으로 보게 만든다는 점이다. model은 한 구성요소일 뿐이다. memory, tools, identity, approval, logs가 agent가 실제 workflow에 오래 들어갈 수 있는지를 결정한다.

## 4. 바뀐 질문

Clawdbot은 최종 형태의 제품이 아니다. 초기 샘플에 가깝다.

그것은 질문을 &quot;어느 AI assistant가 더 똑똑한가&quot;에서 &quot;personal agent를 누가 control하는가&quot;로 바꾼다. 이 질문이 더 낮은 층에 있고, 피하기도 어렵다.

언젠가 각자가 장기 실행되는 AI system을 갖게 된다면 분기점은 chat interface가 아닐 것이다. 세 가지 ownership이다. context는 누구의 것인가, execution rights는 누구의 것인가, failure cost는 누가 부담하는가.</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》: Transformer 이전의 어텐션</title><link>https://justinhuangai.github.io/ko-KR/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko-KR/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>Seq2Seq는 end-to-end 번역이 가능하다는 것을 보였지만, 하나의 단단한 병목을 남겼습니다. 원문 전체가 하나의 고정 벡터에 들어가야 했습니다. [《Neural Machine Translation by Jointly Learning to Align and Translate》](/papers/1409.0473v7.pdf)의 첫 번째 의미는 &quot;문장 전체를 기억하라&quot;를 &quot;매 단계 관련 정보를 다시 찾아라&quot;로 바꾼 데 있습니다.

여기서 attention은 아직 Transformer의 주인공이 아닙니다. RNN 옆에 붙은 검색 경로입니다. 진짜 변화는 공식이 아니라 과제의 형태입니다. Decoder는 더 이상 하나의 요약 벡터만 믿지 않습니다. 단어를 생성할 때마다 입력과 다시 정렬할 수 있습니다.

## 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 showLanguage
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 showLanguage
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 showLanguage
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. 이 논문이 바꾼 질문

Attention의 첫 번째 의미는 이것입니다. **문장 전체를 기억하라를 매 단계 관련 정보를 다시 찾아라로 바꾼다.**

이 논문은 Transformer를 발명한 것도, RNN을 버린 것도 아닙니다. 인터페이스를 바꿨습니다. Encoder는 더 이상 하나의 요약만 넘기지 않습니다. 입력의 각 위치에 대한 표현을 보관합니다. Decoder는 단어를 생성할 때마다 원문의 어디를 볼지 다시 결정합니다.

이 변화가 긴 문장의 운명을 바꿨습니다. 모델은 더 이상 하나의 고정 벡터에 모든 것을 기억하도록 강요받지 않습니다. 번역은 반복되는 검색과 생성이 됩니다. Alignment는 외부 주석이 아니라 학습 목표 안에서 생겨납니다.

다음에 attention을 볼 때는 공식이 얼마나 예쁜지부터 묻지 않는 편이 좋습니다. 어떤 과제를 기억 문제에서 검색 문제로 바꿨는지 물어야 합니다. 그것이 나중에 Transformer로 확대될 수 있었던 이유입니다.

---

**논문 읽기 시리즈**

- [《Sequence to Sequence Learning with Neural Networks》](/ko-KR/posts/sequence-to-sequence-learning-with-neural-networks/) (신경망을 이용한 시퀀스-투-시퀀스 학습) — Encoder-decoder 패러다임의 확립
- [《Attention Is All You Need》](/ko-KR/posts/attention-is-all-you-need/) (어텐션만 있으면 충분하다) — Attention이 주역이 되다: Transformer의 탄생
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/ko-KR/posts/bert/) (BERT: 언어 이해를 위한 깊은 양방향 트랜스포머 사전학습) — 사전 학습 패러다임의 확립
- [《Scaling Laws for Neural Language Models》](/ko-KR/posts/scaling-laws-for-neural-language-models/) (신경 언어 모델을 위한 스케일링 법칙) — 스케일의 수학: 왜 더 큰 모델이 예측 가능하게 더 좋은가
- [《Language Models are Few-Shot Learners》](/ko-KR/posts/language-models-are-few-shot-learners/) (언어 모델은 퓨샷 학습자다) — 더 큰 모델, 문맥에서 능력을 더 잘 이끌어내다
- [《Training Compute-Optimal Large Language Models》](/ko-KR/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》: Transformer의 설계 원점</title><link>https://justinhuangai.github.io/ko-KR/posts/attention-is-all-you-need/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko-KR/posts/attention-is-all-you-need/</guid><description>Transformer 논문 연구 노트, 실제 Python 코드 예시 포함</description><pubDate>Tue, 06 Jan 2026 08:18:46 GMT</pubDate><content:encoded>Transformer의 첫 질문은 attention이 유용한가가 아닙니다. 왜 sequence modeling이 시간 순서에 묶여 있어야 하는가입니다. RNN은 텍스트 순서와 계산 순서를 함께 묶었고, long-range dependency, 병렬 학습, 정보 검색이 모두 그 좁은 경로에 묶였습니다.

[《Attention Is All You Need》](/papers/1706.03762v7.pdf)의 혁명은 attention 자체가 아니라 sequence modeling을 시간 순서 문제가 아니라 전역 주소 지정 문제로 바꾼 데 있습니다. 각 위치가 지금 가장 봐야 할 위치를 직접 찾습니다.

## 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 showLanguage
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 showLanguage
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 showLanguage
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 showLanguage
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. 이 논문이 바꾼 질문

Transformer의 핵심 문장은 이것입니다. **sequence modeling은 시간 순서대로 계산할 필요가 없고, 전역 주소 지정 문제가 될 수 있다.**

이 논문의 핵심은 &quot;attention이 강하다&quot;가 아닙니다. Attention은 이미 Bahdanau의 작업에 있었습니다. 진짜 전환은 recurrence와 convolution을 뼈대에서 빼고, 각 token이 다른 token을 직접 접근하게 한 것입니다. 계산 경로는 더 이상 텍스트 순서에 묶이지 않았고, long-range dependency는 긴 중간 상태의 사슬을 통과할 필요가 없어졌습니다.

Self-attention, residual connection, LayerNorm, feed-forward network는 각각의 영웅이 아닙니다. 함께 병렬 학습과 scale에 맞는 sequence machine을 만듭니다. Transformer의 힘은 정보 흐름과 하드웨어 효율을 동시에 다시 쓴 데 있습니다.

다음에 새 아키텍처를 볼 때는 어떤 모듈을 썼는지만 묻지 않는 편이 좋습니다. 문제의 좌표계를 바꿨는지 물어야 합니다. 오래된 계산 경로를 최적화하는가, 아니면 정보 주소 지정 방식을 바꾸는가?

---

**논문 읽기 시리즈**

- [《Sequence to Sequence Learning with Neural Networks》](/ko-KR/posts/sequence-to-sequence-learning-with-neural-networks/) (신경망을 이용한 시퀀스-투-시퀀스 학습) — Encoder-decoder 패러다임의 확립
- [《Neural Machine Translation by Jointly Learning to Align and Translate》](/ko-KR/posts/neural-machine-translation-by-jointly-learning-to-align-and-translate/) (정렬과 번역을 공동으로 학습하는 신경 기계 번역) — Attention의 기원
- [《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》](/ko-KR/posts/bert/) (BERT: 언어 이해를 위한 깊은 양방향 트랜스포머 사전학습) — 사전 학습 패러다임의 확립
- [《Scaling Laws for Neural Language Models》](/ko-KR/posts/scaling-laws-for-neural-language-models/) (신경 언어 모델을 위한 스케일링 법칙) — 스케일의 수학: 왜 더 큰 모델이 예측 가능하게 더 좋은가
- [《Language Models are Few-Shot Learners》](/ko-KR/posts/language-models-are-few-shot-learners/) (언어 모델은 퓨샷 학습자다) — 더 큰 모델, 맥락에서 능력을 이끌어내는 데 더 뛰어남
- [《Training Compute-Optimal Large Language Models》](/ko-KR/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>Justin Huang Blog에 오신 것을 환영합니다</title><link>https://justinhuangai.github.io/ko-KR/posts/hello-world/</link><guid isPermaLink="true">https://justinhuangai.github.io/ko-KR/posts/hello-world/</guid><description>AI 논문, agent 시스템, 소프트웨어 인프라, 그리고 그 뒤의 인과 사슬을 읽는 개인 기술 블로그.</description><pubDate>Thu, 01 Jan 2026 08:07:13 GMT</pubDate><content:encoded>이 블로그는 정보 피드가 아니다.

피드는 무엇이 새로 나왔는지 알려주는 데 능하다. 하지만 어떤 문제가 시간이 지나도 중요한 이유를 보존하는 데는 약하다. 이곳에서 하려는 일은 반대다. 기술 문제를 다시 이해 가능한 인과 사슬 안에 놓는 것이다.

## 무엇을 쓰는가

대부분의 글은 세 가지 대상에서 출발한다. 논문, 시스템, 엔지니어링 행동이다.

논문은 질문을 바꿀 때 중요해진다. Transformer는 단지 attention이 강하다는 이야기가 아니다. sequence modeling을 시간 순서 문제가 아니라 전역 주소 지정 문제로 바꾸었다. GPT-3는 단순히 더 큰 모델이 아니다. 일부 task adaptation을 parameter update에서 context로 옮겼다. Chinchilla는 큰 모델을 반대한 것이 아니라, parameter와 data가 compute budget을 함께 써야 한다고 말했다.

시스템은 제약이 보일 때 분석할 가치가 있다. agent 제품은 &quot;model plus tools&quot;가 아니다. memory가 어디에 있는지, permission을 어떻게 줄이는지, runtime state를 어떻게 복구하는지, protocol이 상호작용을 어떻게 제한하는지, 실패 비용을 누가 부담하는지가 함께 들어 있다.

엔지니어링 행동은 incentive 안에 놓아야 선명해진다. benchmark, open-source ecosystem, model ranking, agent platform은 밖에서 보면 깔끔해 보인다. 실제 문제는 대개 generation pipeline, cost structure, governance boundary 안에 있다.

## 어떻게 쓰는가

나는 단순히 다시 말하는 글을 원하지 않는다. 요약은 텍스트를 압축한다. 좋은 독해는 프레임을 바꾼다.

각 글은 적어도 하나의 더 날카로운 질문을 남겨야 한다. 모델이 더 강하다면, 어떤 예산 조건에서 강한가? agent가 더 유능하다면, 그 능력은 weight에 있는가, context에 있는가, runtime structure에 있는가? benchmark가 더 어렵다면, 어려운 것은 task 자체인가, 아니면 생성과 필터링 과정이 만든 난이도인가?

이곳의 기준은 단순하다. 구호를 줄이고, 메커니즘을 더 본다.

## 사람과 에이전트를 위해

사람은 최신 글에서 시작해도 되고, 태그를 따라 한 주제를 읽어도 된다.

AI agent는 `/llms.txt`, `/llms-full.txt`, 또는 각 글 URL 뒤에 `.md`를 붙인 Markdown endpoint를 읽을 수 있다. 의도적인 설계다. 글이 검색되고, 인용되고, 재사용되고, 반박될 것이라면 machine-readable version도 일급 입구여야 한다.

이 블로그가 남기고 싶은 것은 의견의 양이 아니다. 다시 집어 들었을 때 여전히 작동하는 문제 구조다.</content:encoded><category>General</category><category>hello</category><category>blog</category></item></channel></rss>