Karpathy의 autoresearch 해부 — 630줄로 AI 연구실 만들기
Andrej Karpathy의 autoresearch를 코드 레벨까지 분석합니다. 630줄 train.py, BPE 토크나이저, MuonAdamW 옵티마이저, 에이전트 프로토콜의 설계 원리를 파헤칩니다.

Karpathy의 autoresearch 해부 -- 630줄로 AI 연구실 만들기
Andrej Karpathy가 2026년 3월 공개한 autoresearch를 코드 레벨까지 분석합니다. 630줄짜리 train.py 하나로 AI 에이전트가 밤새 100개 ML 실험을 자동으로 돌리는 원리를 파헤칩니다.
이 글은 autoresearch 시리즈 3부작 중 Part 1입니다.
- Part 1 (이 글): 프로젝트 구조와 코드 심층 분석
- Part 2: 직접 실행하고 실험 결과 분석하기
- Part 3: 나만의 도메인에 autoresearch 적용하기
1. "잠자는 동안 연구가 된다"
Karpathy는 README 서두에 이런 미래 비전을 적어뒀습니다.
*One day, frontier AI research used to be done by meat computers in between eating, sleeping, having other fun, and synchronizing once in a while using sound wave interconnect in the ritual of "group meeting". That era is long gone. Research is now entirely the domain of autonomous swarms of AI agents running across compute cluster megastructures in the skies. The agents claim that we are now in the 10,205th generation of the code base, in any case no one could tell if that's right or wrong as the "code" is now a self-modifying binary that has grown beyond human comprehension. This repo is the story of how it all began.*
"예전에 프론티어 AI 연구는 고기 컴퓨터(meat computers, 즉 인간)가 밥 먹고, 자고, 놀다가, 가끔 음파 인터커넥트(회의)로 동기화하면서 했다. 그 시대는 끝났다. 연구는 이제 하늘 위 컴퓨트 클러스터를 가로지르는 자율 AI 에이전트 군집의 영역이다." -- 농담 반 진담 반이지만, autoresearch가 지향하는 방향은 명확합니다.
autoresearch를 한 줄로 요약하면 이렇습니다. AI 에이전트에게 작지만 진짜 LLM 훈련 셋업을 주고, 밤새 자율적으로 실험을 반복하게 하는 시스템.
에이전트가 코드를 수정하고, 5분간 훈련하고, 결과가 좋으면 유지하고, 나쁘면 되돌리고, 다시 반복합니다. 인간이 자는 8시간 동안 약 100개의 실험이 돌아갑니다.
2. 프로젝트 구조 -- 3개 파일이 전부다
prepare.py -- 데이터 + 토크나이저 + 데이터로더 + 평가 (수정 불가)
train.py -- GPT 모델 + 옵티마이저 + 학습 루프 (에이전트가 수정)
program.md -- 에이전트 지시서 (인간이 편집)
pyproject.toml -- 의존성 관리각 파일의 역할이 극도로 명확하게 분리되어 있습니다.
| 파일 | 줄 수 | 수정 주체 | 역할 |
|---|---|---|---|
prepare.py | 389줄 | 없음 (고정) | 데이터 다운로드, BPE 토크나이저, 데이터로더, evaluate_bpb |
train.py | 631줄 | AI 에이전트 | GPT 아키텍처, MuonAdamW 옵티마이저, 학습 루프 |
program.md | 114줄 | 인간 | 에이전트 행동 프로토콜, 실험 루프 규칙 |
pyproject.toml | 27줄 | 없음 (고정) | PyTorch 2.9.1, kernels, rustbpe 등 의존성 |
핵심 설계 원칙: 에이전트는 `train.py`만 건드린다. 평가 기준(`prepare.py`)은 절대 변하지 않는다. 인간은 `program.md`만 편집한다.
이 분리가 중요한 이유는 뒤에서 다시 다룹니다.
3. prepare.py 심층 분석 -- 실험의 토대
prepare.py는 에이전트가 절대 수정할 수 없는 파일입니다. 실험의 규칙과 평가 기준이 여기에 고정되어 있습니다.
3.1 상수 -- 게임의 규칙
MAX_SEQ_LEN = 2048 # 컨텍스트 길이
TIME_BUDGET = 300 # 학습 시간 예산: 5분 (초)
EVAL_TOKENS = 40 * 524288 # 검증 토큰 수: 약 2,097만 토큰5분이라는 시간 제한이 autoresearch의 핵심 설계입니다. 모델 크기를 키우든, 배치 사이즈를 바꾸든, 아키텍처를 뒤집든 -- 동일한 시간 안에서 어떤 결과가 나오는지만 비교합니다. step 수가 아니라 wall clock 기준이므로, 에이전트가 선택한 모든 변수가 공정하게 비교됩니다.
3.2 데이터 -- climbmix-400b
BASE_URL = "https://huggingface.co/datasets/karpathy/climbmix-400b-shuffle/resolve/main"
MAX_SHARD = 6542 # 전체 6542개 샤드
VAL_SHARD = MAX_SHARD # 마지막 샤드를 검증용으로 고정HuggingFace에 올라간 climbmix-400b-shuffle 데이터셋에서 Parquet 파일을 다운로드합니다. 기본 설정으로는 10개 샤드만 받고, 마지막 샤드(shard_06542)는 항상 검증용으로 고정됩니다.
3.3 토크나이저 -- rustbpe
VOCAB_SIZE = 8192
SPLIT_PATTERN = r"""'(?i:[sdmt]|ll|ve|re)|[^\r\n\p{L}\p{N}]?+\p{L}+|\p{N}{1,2}| ?[^\s\p{L}\p{N}]++[\r\n]*|\s*[\r\n]|\s+(?!\S)|\s+"""GPT-4 스타일의 split pattern을 사용하되, 숫자 토큰을 {1,3} 대신 {1,2}로 제한한 변형입니다. rustbpe(Rust 기반 BPE 구현)로 빠르게 훈련하고, tiktoken 인코딩으로 변환해서 저장합니다. vocab size를 8192로 작게 잡은 건 소규모 모델에서의 실험 효율성 때문입니다.
3.4 데이터로더 -- BOS-aligned packing
이 프로젝트의 데이터로더는 단순해 보이지만 정교합니다.
def make_dataloader(tokenizer, B, T, split, buffer_size=1000):
"""
BOS-aligned dataloader with best-fit packing.
Every row starts with BOS. Documents packed using best-fit to minimize cropping.
When no document fits remaining space, crops shortest doc to fill exactly.
100% utilization (no padding).
"""핵심 특징:
- BOS-aligned: 모든 행이 BOS(Begin of Sequence) 토큰으로 시작
- Best-fit packing: 남은 공간에 가장 큰 문서를 넣어서 낭비 최소화
- 100% utilization: 패딩 토큰 없음. 남은 공간이 있으면 가장 짧은 문서를 잘라서 정확히 채움
- GPU 최적화: pin_memory + non_blocking copy로 CPU-GPU 전송 효율화
이 데이터로더가 prepare.py에 고정되어 있으므로, 에이전트가 데이터 처리 방식을 바꿔서 부정행위를 할 수 없습니다.
3.5 evaluate_bpb -- 유일한 평가 지표
@torch.no_grad()
def evaluate_bpb(model, tokenizer, batch_size):
"""
Bits per byte (BPB): vocab size-independent evaluation metric.
"""
token_bytes = get_token_bytes(device="cuda")
val_loader = make_dataloader(tokenizer, batch_size, MAX_SEQ_LEN, "val")
steps = EVAL_TOKENS // (batch_size * MAX_SEQ_LEN)
total_nats = 0.0
total_bytes = 0
for _ in range(steps):
x, y, _ = next(val_loader)
loss_flat = model(x, y, reduction='none').view(-1)
y_flat = y.view(-1)
nbytes = token_bytes[y_flat]
mask = nbytes > 0
total_nats += (loss_flat * mask).sum().item()
total_bytes += nbytes.sum().item()
return total_nats / (math.log(2) * total_bytes)왜 일반적인 cross-entropy loss 대신 bits per byte(BPB)를 쓸까요?
일반 loss는 vocab size에 의존합니다. vocab이 크면 토큰당 loss가 높아지고, vocab이 작으면 낮아집니다. 에이전트가 vocab size를 바꿀 수는 없지만(prepare.py가 고정이므로), BPB는 원칙적으로 더 공정한 비교를 가능하게 합니다.
BPB 계산 과정:
- 각 토큰의 cross-entropy loss를 nats 단위로 합산
- 각 타겟 토큰이 원문에서 차지하는 UTF-8 바이트 수를 합산
- special 토큰(바이트 수 = 0)은 양쪽 합에서 제외
총 nats / (ln(2) * 총 바이트)= bits per byte
값이 낮을수록 좋습니다. 이 함수가 prepare.py에 고정되어 있으므로, 에이전트의 모든 실험이 동일한 기준으로 비교됩니다.
4. train.py 핵심 해부 -- GPT + Muon 631줄
train.py는 에이전트가 자유롭게 수정하는 유일한 파일입니다. 모델 정의, 옵티마이저, 학습 루프가 모두 하나의 파일에 들어 있습니다.
4.1 GPT 아키텍처
기본 설정
@dataclass
class GPTConfig:
sequence_len: int = 2048
vocab_size: int = 32768
n_layer: int = 12
n_head: int = 6
n_kv_head: int = 6
n_embd: int = 768
window_pattern: str = "SSSL"이건 기본값이고, 실제로는 하이퍼파라미터 섹션에서 재정의됩니다.
ASPECT_RATIO = 64 # model_dim = depth * ASPECT_RATIO
HEAD_DIM = 128
DEPTH = 8 # 트랜스포머 레이어 수build_model_config 함수가 이 값들로 실제 설정을 만듭니다. DEPTH=8이면 base_dim = 8 * 64 = 512, 이걸 HEAD_DIM=128에 맞춰 정렬하면 n_embd=512, n_head=4가 됩니다.
CausalSelfAttention -- Flash Attention 3 + 슬라이딩 윈도우
class CausalSelfAttention(nn.Module):
def __init__(self, config, layer_idx):
super().__init__()
# ... (Q, K, V 프로젝션)
self.ve_gate = nn.Linear(self.ve_gate_channels, self.n_kv_head, bias=False) \
if has_ve(layer_idx, config.n_layer) else None
def forward(self, x, ve, cos_sin, window_size):
# ... Q, K, V 계산
# Value residual (ResFormer): 입력 의존적 게이트로 value embedding 혼합
if ve is not None:
ve = ve.view(B, T, self.n_kv_head, self.head_dim)
gate = 2 * torch.sigmoid(self.ve_gate(x[..., :self.ve_gate_channels]))
v = v + gate.unsqueeze(-1) * ve
cos, sin = cos_sin
q, k = apply_rotary_emb(q, cos, sin), apply_rotary_emb(k, cos, sin)
q, k = norm(q), norm(k) # QK-Norm
y = fa3.flash_attn_func(q, k, v, causal=True, window_size=window_size)주목할 포인트가 세 가지 있습니다.
첫째, Value Embedding (ResFormer). 교대 레이어(has_ve)에서 입력 토큰의 임베딩을 value에 직접 더해줍니다. 게이트는 2 * sigmoid(...)이므로 범위가 [0, 2]이고, 초기화 시 가중치가 0이므로 sigmoid(0) = 0.5, 2 * 0.5 = 1.0으로 시작합니다. 학습을 통해 각 레이어가 value embedding을 얼마나 활용할지 스스로 결정합니다.
둘째, QK-Norm. RoPE 적용 후 Q와 K에 RMS normalization을 합니다. 이는 attention logit의 스케일을 안정화시킵니다.
셋째, 슬라이딩 윈도우. SSSL 패턴은 short-short-short-long의 반복입니다. short 윈도우는 sequence_len // 2 = 1024, long은 2048(전체 컨텍스트). 마지막 레이어는 항상 long입니다. 이렇게 하면 대부분의 레이어가 절반 컨텍스트만 보므로 연산량이 줄고, 마지막 레이어에서 전체 맥락을 종합합니다.
MLP -- ReLU Squared
class MLP(nn.Module):
def forward(self, x):
x = self.c_fc(x)
x = F.relu(x).square() # ReLU^2
x = self.c_proj(x)
return x활성화 함수가 GeLU나 SwiGLU가 아닌 입니다. F.relu(x).square()는 ReLU의 출력을 제곱한 것으로, sparsity 특성을 유지하면서도 더 날카로운 활성화 패턴을 만듭니다. 구현이 단순하고 연산도 가볍습니다.
Block -- x0 스킵 연결
class Block(nn.Module):
def forward(self, x, ve, cos_sin, window_size):
x = x + self.attn(norm(x), ve, cos_sin, window_size)
x = x + self.mlp(norm(x))
return xBlock 자체는 표준적인 Pre-Norm(RMS Norm) + 잔차 연결입니다. 하지만 GPT의 forward에서 특별한 처리가 추가됩니다.
def forward(self, idx, targets=None, reduction='mean'):
x = self.transformer.wte(idx)
x = norm(x)
x0 = x # 초기 임베딩 저장
for i, block in enumerate(self.transformer.h):
x = self.resid_lambdas[i] * x + self.x0_lambdas[i] * x0 # 핵심
ve = self.value_embeds[str(i)](idx) if str(i) in self.value_embeds else None
x = block(x, ve, cos_sin, self.window_sizes[i])
x = norm(x)각 블록 진입 전에 resid_lambdas[i] * x + x0_lambdas[i] * x0를 계산합니다. resid_lambdas는 1.0으로, x0_lambdas는 0.1로 초기화됩니다. 즉, 매 레이어에서 초기 임베딩을 10%씩 섞어줍니다. 이는 deep network에서 gradient flow를 개선하는 기법입니다.
로그릿 소프트캡
softcap = 15
logits = self.lm_head(x)
logits = logits.float()
logits = softcap * torch.tanh(logits / softcap)로그릿을 범위로 부드럽게 제한합니다. tanh(z/15) * 15는 가 작을 때는 거의 항등함수이고, 커질수록 15에 수렴합니다. Gemma 2에서 도입된 기법으로, 학습 안정성을 크게 개선합니다.
4.2 MuonAdamW 옵티마이저
train.py의 옵티마이저는 단순한 AdamW가 아닙니다. 파라미터 종류에 따라 두 가지 옵티마이저를 사용합니다.
| 파라미터 종류 | 옵티마이저 | 학습률 |
|---|---|---|
lm_head (unembedding) | AdamW | 0.004 |
wte (token embedding) | AdamW | 0.6 |
value_embeds | AdamW | 0.6 |
resid_lambdas | AdamW | 0.005 (0.5 * 0.01) |
x0_lambdas | AdamW | 0.5 |
| 트랜스포머 행렬 (2D) | Muon | 0.04 |
모든 AdamW 학습률에는 스케일링이 적용됩니다.
Muon의 핵심: Polar Express 직교화
Muon(MomentUm Orthogonalized update) 옵티마이저의 핵심 아이디어는 gradient 행렬을 직교 행렬에 가깝게 만드는 것입니다. Newton-Schulz iteration을 사용합니다.
polar_express_coeffs = [
(8.156554524902461, -22.48329292557795, 15.878769915207462),
(4.042929935166739, -2.808917465908714, 0.5000178451051316),
# ... 5 세트
]
# Polar express orthogonalization
X = g.bfloat16()
X = X / (X.norm(dim=(-2, -1), keepdim=True) * 1.02 + 1e-6)
if g.size(-2) > g.size(-1):
for a, b, c in polar_express_coeffs[:ns_steps]:
A = X.mT @ X
B = b * A + c * (A @ A)
X = a * X + X @ B
else:
for a, b, c in polar_express_coeffs[:ns_steps]:
A = X @ X.mT
B = b * A + c * (A @ A)
X = a * X + B @ X5번의 반복으로 gradient 행렬 의 polar decomposition 에서 직교 성분 를 근사합니다. 왜 이게 좋을까요? 직교 업데이트는 파라미터 행렬의 condition number를 개선하고, 학습을 안정화시킵니다.
행렬의 aspect ratio에 따라 또는 중 작은 쪽을 선택합니다. 이는 연산 효율성을 위한 것입니다.
NorMuon -- 분산 정규화
Polar Express 직교화 후에 NorMuon이라는 분산 감소(variance reduction) 기법이 적용됩니다.
# NorMuon variance reduction
v_mean = g.float().square().mean(dim=red_dim, keepdim=True)
second_momentum_buffer.lerp_(v_mean.to(dtype=second_momentum_buffer.dtype), 1 - beta2)
step_size = second_momentum_buffer.clamp_min(1e-10).rsqrt()gradient의 2차 모멘트를 dimension별로 추적하고, 이를 이용해 step size를 정규화합니다. 결과적으로 각 dimension의 gradient 분산이 비슷해지도록 스케일링됩니다.
Cautious Weight Decay
mask = (g * stacked_params) >= 0
stacked_params.sub_(lr * g + lr * wd * stacked_params * mask)일반적인 weight decay는 모든 파라미터에 동일하게 적용됩니다. 여기서는 gradient와 파라미터의 부호가 같은 경우에만 decay를 적용합니다. gradient가 파라미터를 줄이는 방향이면 decay가 이미 그 역할을 하고 있으니 추가 decay가 불필요하다는 직관입니다.
4.3 학습 루프 설계
시간 기반 학습
while True:
# ... gradient accumulation ...
progress = min(total_training_time / TIME_BUDGET, 1.0)
# ... optimizer step ...
if step > 10 and total_training_time >= TIME_BUDGET:
breakstep 수가 아니라 wall clock을 기준으로 학습이 진행됩니다. 처음 10 스텝은 warmup(torch.compile 컴파일 포함)으로 간주하고 시간 계측에서 제외합니다. 그 후부터 total_training_time을 누적해서 5분이 차면 종료합니다.
그래디언트 축적
TOTAL_BATCH_SIZE = 2**19 # ~524K 토큰
DEVICE_BATCH_SIZE = 128
# tokens_per_fwdbwd = 128 * 2048 = 262,144
# grad_accum_steps = 524,288 / 262,144 = 2한 번의 optimizer step에 약 52만 토큰을 사용합니다. GPU 메모리 제한 때문에 이를 2번의 forward-backward pass로 나눠서 처리합니다.
LR 스케줄
WARMUP_RATIO = 0.0 # warmup 없음
WARMDOWN_RATIO = 0.5 # 후반 50%에서 cosine 감소
FINAL_LR_FRAC = 0.0 # 최종 LR = 0
def get_lr_multiplier(progress):
if progress < WARMUP_RATIO:
return progress / WARMUP_RATIO if WARMUP_RATIO > 0 else 1.0
elif progress < 1.0 - WARMDOWN_RATIO:
return 1.0
else:
cooldown = (1.0 - progress) / WARMDOWN_RATIO
return cooldown * 1.0 + (1 - cooldown) * FINAL_LR_FRACwarmup 없이 시작해서 전반 50%는 전체 학습률을 유지하고, 후반 50%에서 0까지 선형으로 감소합니다. 5분밖에 안 되는 학습에서 warmup에 시간을 쓰지 않겠다는 판단입니다.
Muon의 momentum도 별도로 스케줄링됩니다.
def get_muon_momentum(step):
frac = min(step / 300, 1)
return (1 - frac) * 0.85 + frac * 0.950.85에서 시작해 300 스텝에 걸쳐 0.95까지 올라갑니다.
GC 관리 -- 500ms stall 방지
if step == 0:
gc.collect()
gc.freeze()
gc.disable()
elif (step + 1) % 5000 == 0:
gc.collect()Python의 가비지 컬렉터가 예고 없이 돌면 최대 500ms의 지연이 발생합니다. 5분 학습에서 이건 치명적입니다. 그래서 첫 스텝 후에 GC를 전부 정리하고 gc.freeze()로 기존 객체를 영구 세대로 옮긴 뒤 gc.disable()로 자동 GC를 끕니다.
빠른 실패
train_loss_f = train_loss.item()
if math.isnan(train_loss_f) or train_loss_f > 100:
print("FAIL")
exit(1)loss가 NaN이거나 100을 넘으면 즉시 종료합니다. 에이전트가 무모한 실험을 했을 때 5분을 낭비하지 않기 위한 안전장치입니다.
5. program.md -- 에이전트 연구 프로토콜
program.md는 114줄의 Markdown 파일이지만, autoresearch의 두뇌에 해당합니다. Karpathy는 이걸 "연구 조직 코드(research org code)"라고 부릅니다.
Setup 단계
에이전트가 처음 시작할 때의 절차:
- run tag 합의: 날짜 기반 태그 제안 (예:
mar5) - 브랜치 생성:
git checkout -b autoresearch/<tag> - 파일 읽기: README.md, prepare.py, train.py 전체 읽기
- 데이터 확인:
~/.cache/autoresearch/존재 여부 확인 - results.tsv 초기화: 헤더만 있는 TSV 파일 생성
실험 루프 -- LOOP FOREVER
LOOP FOREVER:
1. git 상태 확인
2. train.py 수정 (실험적 아이디어)
3. git commit
4. uv run train.py > run.log 2>&1
5. 결과 추출: grep "^val_bpb:\|^peak_vram_mb:" run.log
6. 크래시면 tail -n 50으로 스택 트레이스 확인
7. results.tsv에 기록
8. 개선됐으면 keep, 아니면 git reset에이전트의 출력은 모두 run.log로 리다이렉트됩니다. tee를 쓰지 않는 이유는 학습 로그가 에이전트의 컨텍스트 윈도우를 낭비하지 않게 하기 위해서입니다.
결과 기록은 TSV(탭 구분) 형식입니다.
commit val_bpb memory_gb status description
a1b2c3d 0.997900 44.0 keep baseline
b2c3d4e 0.993200 44.2 keep increase LR to 0.04
c3d4e5f 1.005000 44.0 discard switch to GeLU activation
d4e5f6g 0.000000 0.0 crash double model width (OOM)핵심 규칙: "절대 멈추지 마라" (NEVER STOP)
program.md에서 가장 중요한 문장입니다.
NEVER STOP: Once the experiment loop has begun, do NOT pause to ask the human if you should continue. Do NOT ask "should I keep going?" or "is this a good stopping point?". The human might be asleep, or gone from a computer and expects you to continue working *indefinitely* until you are manually stopped.
인간이 자는 동안 에이전트가 "계속할까요?"라고 물으면 8시간이 낭비됩니다. 아이디어가 고갈되면 코드를 다시 읽고, 논문을 참조하고, 이전 실패를 조합하라는 지시입니다.
단순성 기준
A 0.001 val_bpb improvement that adds 20 lines of hacky code? Probably not worth it.
A 0.001 val_bpb improvement from deleting code? Definitely keep.
An improvement of ~0 but much simpler code? Keep.Karpathy 스타일이 그대로 반영되어 있습니다. 코드 복잡도가 증가하는 개선은 그만한 가치가 있어야 하고, 코드를 삭제하면서 동일한 성능이 나오면 무조건 채택합니다.
VRAM은 소프트 제약
VRAM is a soft constraint. Some increase is acceptable for meaningful val_bpb gains, but it should not blow up dramatically.
메모리 사용량은 기록하지만 엄격한 제한은 아닙니다. OOM으로 크래시가 나면 자연스럽게 discard됩니다.
6. 실제 결과 -- 밤새 100개 실험
Karpathy 공식 결과
Karpathy는 이 프로젝트를 공개하면서 직접 실험한 결과도 공유했습니다. H100 단일 GPU에서의 결과는 인상적이었습니다. 에이전트(Claude)가 밤새 실험을 반복하면서 GPT-2 학습 시간을 2.02시간에서 1.80시간으로 약 11% 단축시켰습니다.
에이전트가 발견한 것 중 흥미로운 사례들:
- attention scaling 관련 오류 수정
- regularization 부재 문제 발견
- 20년 경력 연구자도 놓칠 수 있는 미묘한 최적화
Shopify CEO의 실험
Shopify CEO Tobi Lutke도 autoresearch를 포크해서 실험했습니다. 그의 실험에서는 val_bpb가 19% 개선되었고, 흥미롭게도 에이전트가 찾은 작은 모델이 기존 큰 모델보다 더 나은 성능을 보였습니다. 5분이라는 제한된 시간 안에서는 모델 크기를 무작정 키우는 것보다 효율적인 아키텍처를 찾는 게 더 중요하다는 점을 에이전트가 스스로 발견한 셈입니다.
커뮤니티 확산
공개 직후 다양한 플랫폼으로 포크가 확산되었습니다.
| 포크 | 플랫폼 |
|---|---|
| miolini/autoresearch-macos | macOS |
| trevin-creator/autoresearch-mlx | macOS (MLX) |
| jsegov/autoresearch-win-rtx | Windows RTX |
Karpathy는 README에서 소규모 컴퓨팅 환경을 위한 튜닝 가이드도 제공합니다. TinyStories 데이터셋 사용, vocab size 축소, MAX_SEQ_LEN 감소, DEPTH 축소 등의 권장사항이 포함되어 있습니다.
7. 왜 이게 중요한가 -- AI 연구의 패러다임 전환
연구의 자동화
전통적인 ML 연구 루프는 이렇습니다.
인간: 가설 수립 -> 코드 작성 -> 실험 실행 -> 결과 분석 -> 반복autoresearch는 이걸 이렇게 바꿉니다.
에이전트: 아이디어 생성 -> train.py 수정 -> 5분 학습 -> val_bpb 확인 -> keep/discard -> 반복가설 수립부터 실험 평가까지의 전체 루프가 에이전트에게 넘어갔습니다.
핵심 인사이트: 두 가지 편집 레이어
autoresearch의 가장 우아한 설계는 편집 대상의 분리입니다.
인간 -> program.md 편집 (연구 전략, 에이전트 행동 규칙)
에이전트 -> train.py 편집 (모델 아키텍처, 하이퍼파라미터)인간은 "어떤 방향으로 연구할 것인가"를 코딩하고, 에이전트는 "구체적으로 어떤 코드를 쓸 것인가"를 실행합니다. program.md는 에이전트의 행동을 결정하는 "메타 코드"입니다.
Meta-optimization 가능성
여기서 한 단계 더 나아가면, program.md 자체를 최적화하는 상위 루프를 상상할 수 있습니다.
외부 에이전트: program.md 수정 -> 내부 에이전트가 100개 실험 -> 최종 val_bpb로 program.md 평가연구 조직의 구조 자체를 자동으로 최적화하는 것입니다. Karpathy는 README에서 이 가능성을 직접 언급합니다: "how you'd iterate on it over time to find the 'research org code' that achieves the fastest research progress."
한계
현재의 autoresearch는 명확한 한계가 있습니다.
- 단일 GPU: 분산 학습 없음
- 단일 파일:
train.py하나만 수정 가능 - 5분 실험: 장시간 학습이 필요한 실험은 불가
- 수치 메트릭만: val_bpb 하나로만 판단 (생성 품질, 추론 능력 등은 미반영)
- 탐색 범위: 에이전트의 창의성에 의존 (구조적 탐색 전략 없음)
이런 제약 때문에 Karpathy 스스로도 "this is the story of how it all began"이라고 부르는 겁니다. 시작에 불과하지만, 방향성은 충분히 흥미롭습니다.
8. 다음 편 예고
이번 글에서는 autoresearch의 구조와 코드를 분석했습니다.
- Part 2에서는 직접 autoresearch를 실행하고, 에이전트가 밤새 만들어낸 실험 결과를 분석하는 과정을 다룹니다. 어떤 아이디어가 성공하고, 어떤 아이디어가 실패하는지, 에이전트의 연구 패턴을 살펴봅니다.
- Part 3에서는 나만의 도메인에 autoresearch를 적용하는 방법을 다룹니다. program.md를 어떻게 커스터마이징하는지, prepare.py를 다른 태스크에 맞게 어떻게 수정하는지, 그리고 실제 프로덕션 환경에서의 활용 방안을 정리합니다.
*이 글에서 분석한 코드는 karpathy/autoresearch 저장소의 2026년 3월 기준 코드입니다.*
이메일로 받아보기
관련 포스트

스스로 진화하는 AI 에이전트 — 2026년의 새로운 패러다임
GenericAgent, Evolver, Open Agents — 스스로 스킬을 만들고, 실행 경로를 기억하고, 실패에서 배우는 자가 진화 에이전트 3종 비교.

나만의 LLM Knowledge Base 구축하기 — Karpathy 스타일 지식 시스템
Obsidian + Claude Code로 영구적인 개인 지식 체계를 만드는 완전 가이드. 위키 + 메모리 두 축의 지식 시스템.

Karpathy의 CLAUDE.md가 48K 스타를 받은 이유 — 그리고 나만의 CLAUDE.md 작성법
마크다운 파일 하나로 AI 코딩 정확도를 65%에서 94%로. Karpathy의 4가지 규칙과 실전 작성법을 분석합니다.