논문 읽는 법

먼저 읽을 장 23.1 문헌 탐색과 지식 관리1.1 AI 연구의 생애주기

논문을 처음부터 끝까지 순서대로 읽으면 한 편에 하루가 걸리고, 다 읽고 나서도 "그래서 무엇이 새롭고, 어디까지 믿어도 되는가"에 답하지 못하는 경우가 많다. 논문은 소설이 아니다. 저자가 가장 설득력 있게 보이도록 배치한 주장과, 그 주장을 뒷받침하도록 고른 근거의 묶음이다. 읽는 사람의 일은 이 둘을 분리해, 근거가 실제로 지지하는 주장의 범위를 다시 정하는 것이다.

이 장은 목적에 따라 읽는 깊이를 정하는 법, 정해진 순서로 필요한 정보를 뽑는 법, 주장과 근거를 분리해 비판적으로 읽는 법, 그리고 논문의 수식을 공개 구현과 대응시키는 법을 다룬다. 23.1장에서 모은 문헌을 이 방식으로 읽고, 그 결과를 논문 노트에 남긴다. 1.1장 실습에서 논문을 생애주기 단계로 분해했다면, 이 장은 그 분해를 각 단계의 근거까지 내려가 확인하는 일이다.

학습 목표

  • 목적에 맞는 깊이로 논문을 읽는다
  • 주장과 근거를 분리한다
  • 수식과 구현을 대응시킨다

읽기의 단계

왜 단계를 나누는가. 23.1장의 지도를 만들려면 30편을 읽어야 하고, 재현하려면 한 편을 아주 깊이 읽어야 한다. 모든 논문을 같은 깊이로 읽으면 30편을 다 깊게 읽느라 한 달을 쓰거나, 재현할 논문을 훑어만 보고 실험을 시작하게 된다. 읽기의 깊이는 읽는 목적이 정한다.

세 단계. 논문을 여러 번에 걸쳐 점점 깊게 읽는 방법은 Keshav(2007)가 세 번의 읽기(three-pass approach)로 정리한 것이 널리 알려져 있다. 이 책은 이를 다음 세 단계로 부른다.

단계 목적 읽는 곳 끝났을 때 답할 수 있는 질문
훑어보기 더 읽을지 정한다, 지도에 넣는다 제목, 초록, 서론, 절 제목, 그림·표, 결론 무엇을 풀었는가, 어떤 종류의 논문인가, 내 연구와 관련 있는가
이해하기 내용을 파악해 남에게 설명한다 본문 전체, 그림·표의 축과 조건. 증명과 부록의 세부는 넘긴다 왜 작동한다고 주장하는가, 무엇을 검증했는가, 무엇과 비교했는가
재구성하기 재현하거나 내 연구의 기반으로 쓴다 수식 유도, 부록, 공개 코드, 하이퍼파라미터 표 같은 가정에서 나라면 어떻게 만들었을까, 어디서 결과가 달라질 수 있는가

훑어보기는 몇 분 안에 끝나야 한다. 23.1장의 30편은 대부분 이 단계에서 멈춘다. 이해하기는 지도에서 핵심으로 고른 몇 편, 재구성하기는 재현하거나 직접 비교할 한두 편에만 한다. 재구성하기는 머릿속으로 논문을 처음부터 다시 쓰는 일이다. 같은 문제와 가정에서 출발해 저자의 각 결정을 스스로 내려 보면, 저자가 왜 그 결정을 했는지, 그리고 논문이 설명하지 않고 넘어간 결정이 어디인지 드러난다. 재현(23.3장)은 이 단계에서 시작한다.

읽는 순서

왜 순서대로 읽지 않는가. 논문은 서론 → 관련 연구 → 방법 → 실험 → 결론 순서로 쓰여 있지만, 이 순서는 저자가 설득하기 좋은 순서다. 읽는 사람에게 필요한 순서는 다르다. 먼저 무엇을 주장하는지 알아야 방법의 어느 부분이 그 주장을 떠받치는지 보며 읽을 수 있다.

권장 순서.

  1. 초록 · 그림 · 결론. 주장과 그 주장의 근거가 되는 대표 결과를 찾는다. 첫 그림(대개 방법의 개요도)과 주요 결과 표를 본다. 이 셋만으로 "이 논문은 X 조건에서 Y가 Z보다 낫다고 주장한다"는 한 문장을 쓴다.
  2. 방법. 그 주장을 만들어 내는 핵심 아이디어가 무엇인지, 무엇이 새롭고 무엇이 기존 방법 그대로인지 구분한다. 처음 보는 기호는 정의된 곳을 찾아 노트에 적는다.
  3. 실험. 1에서 쓴 문장의 각 부분(X 조건, Y, Z, "낫다")이 실험의 어디에서 확인되는지 찾는다. 데이터·분할·metric·baseline·seed 수를 적는다.
  4. 부록. 하이퍼파라미터, 추가 실험, 실패한 설정, 본문에서 빠진 조건이 여기 있다. 재구성하기 단계에서는 부록이 본문만큼 중요하다.

관련 연구 절은 23.1장의 지도를 넓히는 데 쓴다. 저자가 자기 연구를 기존 연구와 어떻게 구별하는지 보면, 저자 스스로 생각하는 기여가 무엇인지 알 수 있다.

무엇을 추출하는가

왜 정해진 항목을 뽑는가. 읽고 나서 "좋은 논문이었다"만 남으면 다시 읽어야 한다. 정해진 항목을 뽑아 두면 논문끼리 비교할 수 있고(23.1장의 지도), 재현할 때 필요한 조건이 이미 정리되어 있다.

다섯 항목.

  • 문제 정의: 입력·출력·일반화 대상(1.3장). "새 분자에 대한 예측"인지 "학습에 쓴 분자 계열 안의 새 형태(conformation)에 대한 예측"인지는 전혀 다른 문제다.
  • 가정: 결과가 성립하려면 참이어야 하는 것. 명시된 가정과 명시되지 않았지만 실험 설정에서 드러나는 가정을 구분해 적는다.
  • 기여: 저자가 주장하는 새로운 점. 1.1장의 연구 종류(알고리즘·응용·시스템·데이터·분석) 중 무엇인지 정한다. 기여의 종류에 따라 필요한 근거가 다르다(23.4장).
  • 비교 대상: baseline이 무엇이고, 어떻게 튜닝했는지. 같은 데이터·같은 분할·같은 예산에서 돌렸는지, 다른 논문의 숫자를 옮겨 왔는지.
  • 평가 조건: 데이터와 그 판(version), 분할 방식과 단위, metric, seed 수, 오차 막대의 의미.

비판적 읽기

왜 필요한가. 동료 심사는 결과가 맞다는 보증이 아니다(1.6장). 심사자는 대개 코드를 돌려 보지 않고, 시간도 제한되어 있다. 그래서 논문에 실린 주장 중 일부는 근거가 지지하는 범위보다 넓다. 의도적인 과장이 아니어도 그렇다. 저자는 자기 방법이 잘 되는 조건을 가장 잘 알고, 그 조건에서 실험하는 것이 자연스럽기 때문이다. ML 연구의 글쓰기에서 자주 보이는 문제로 설명과 추측을 구분하지 않는 것, 성능 향상의 출처를 밝히지 않는 것, 수식을 설명이 아니라 인상을 주는 데 쓰는 것, 용어를 잘못 쓰는 것이 지적된 바 있다(Lipton & Steinhardt, 2018).

주장–근거 표. 비판적 읽기의 기본 도구는 주장과 근거를 한 줄씩 대응시킨 표다. 초록과 서론의 주장을 하나씩 적고, 각 주장을 지지하는 실험을 찾는다.

주장 (위치) 근거 (위치) 근거의 범위 판정
"모든 벤치마크에서 SOTA" (초록) 표 2: 데이터셋 3개, seed 1개 3개 데이터셋, 단일 실행 범위 초과: "3개 데이터셋에서 단일 실행 기준 최고"
"물리 법칙을 학습한다" (서론) 그림 4: 에너지 보존 오차 학습 범위 안의 초기 조건 범위 초과: 범위 밖 외삽은 미시험
"구조 X가 핵심" (4절) 표 5: X 제거 ablation 같은 recipe에서 X만 제거 지지됨
"계산이 10배 빠르다" (초록) 없음 (본문에 시간 측정 없음) — 근거 없음

근거가 없는 주장, 근거보다 넓은 주장이 표에서 바로 드러난다. 판정 열은 결과가 틀렸다는 뜻이 아니라, 인용할 때 어디까지 말할 수 있는가를 정한다.

빠진 baseline. 비교 대상이 약하면 어떤 방법도 좋아 보인다. 다음을 확인한다.

  • 단순한 baseline이 있는가. 선형 모델, 잘 튜닝한 gradient boosting, 물리 모델, "마지막 값을 그대로 예측"(persistence) 같은 기준이 있는지.
  • baseline도 튜닝했는가. 제안 방법만 공들여 튜닝하고 baseline은 기본 설정으로 돌리면, 차이의 일부는 방법이 아니라 튜닝 노력이다. 언어 모델 분야에서는 표준 LSTM을 대규모 하이퍼파라미터 탐색으로 다시 튜닝하자 더 최근의 구조들보다 좋은 결과가 나왔다는 보고가 있다(Melis et al., 2018).
  • 분야의 표준 방법과 비교했는가. AI for Science 논문은 ML baseline과만 비교하고 분야의 표준 수치해석·통계 방법을 빼는 경우가 있다.

숨겨진 조건. 결과가 특정 조건에서만 성립하는데 그 조건이 본문에 드러나지 않는 경우다. 부록이나 코드에서 찾는다. 테스트 세트에서 고른 하이퍼파라미터, 실패한 seed의 제외, 학습 데이터와 겹치는 테스트 데이터(15.4장), 특정 데이터 전처리, 보고된 것보다 훨씬 큰 계산 예산이 대표적이다.

표의 수치를 다시 계산하기. 논문이 신뢰구간이나 검정을 보고하지 않아도, 표에 평균과 퍼짐과 실행 횟수가 있으면 차이의 불확실성을 직접 계산할 수 있다(12.4장, 12.5장). 그런데 표의 "±" 뒤 숫자가 표준편차(SD, 한 번 실행할 때 흔들리는 정도)인지 표준오차(SE, 평균의 정밀도, SD/n\mathrm{SD}/\sqrt{n})인지 캡션에 없는 경우가 많다. 두 해석으로 모두 계산해 본다.

표의 값은 다음과 같다고 하자. "seed 5개의 평균 ± (설명 없음)", baseline 0.842±0.0060.842 \pm 0.006, 제안 방법 0.854±0.0050.854 \pm 0.005. 두 방법이 같은 seed를 썼는지, 즉 짝지은 비교가 가능한지는 표만으로 알 수 없으므로 짝짓지 않은 Welch 구간을 쓴다.

import numpy as np
from scipy import stats

n = 5                                        # 표 캡션: "seed 5개의 평균 ± ..."
m_base, pm_base = 0.842, 0.006               # baseline 정확도
m_ours, pm_ours = 0.854, 0.005               # 제안 방법 정확도

def compare(s_base, s_ours):
    diff = m_ours - m_base
    se = np.sqrt(s_base**2 / n + s_ours**2 / n)            # Welch 표준오차
    v = se**4 / ((s_base**2 / n) ** 2 / (n - 1) + (s_ours**2 / n) ** 2 / (n - 1))
    t = stats.t.ppf(0.975, v)
    d = diff / np.sqrt((s_base**2 + s_ours**2) / 2)        # Cohen's d (pooled SD)
    return diff, diff - t * se, diff + t * se, d

for label, sb, so in [("±가 표준편차(SD)", pm_base, pm_ours),
                      ("±가 표준오차(SE)", pm_base * np.sqrt(n), pm_ours * np.sqrt(n))]:
    diff, lo, hi, d = compare(sb, so)
    print(f"{label}: 차이 {diff:+.3f}, 95% CI [{lo:+.3f}, {hi:+.3f}], d = {d:.2f}")
±가 표준편차(SD): 차이 +0.012, 95% CI [+0.004, +0.020], d = 2.17
±가 표준오차(SE): 차이 +0.012, 95% CI [-0.006, +0.030], d = 0.97

같은 표가 해석에 따라 다른 결론을 낸다. ±가 표준편차라면 차이의 구간이 0을 포함하지 않고, 효과 크기 dd(차이가 실행 간 흔들림의 몇 배인가)도 2를 넘는다. ±가 표준오차라면 실제 흔들림은 5≈2.24\sqrt{5} \approx 2.24배 더 크고, 구간은 0을 포함한다. 이 경우 seed 5개로는 차이의 방향조차 확정할 수 없다. 캡션이 이를 밝히지 않았다면, 논문의 주장은 더 약한 쪽 해석까지만 받아들이고 저자에게 확인하거나 코드로 재현한다(23.3장).

수식과 코드

왜 대응시키는가. 보고된 숫자를 만든 것은 수식이 아니라 코드다. 논문의 수식과 공개 코드가 다르면, 수식대로 재구현한 사람은 다른 숫자를 얻는다. 차이는 대개 사소해 보이는 곳에 있다. 평균인가 합인가, 정규화 상수가 어디에 곱해지는가, 로그의 밑, 분산인가 표준편차인가, 행렬이 전치되어 있는가.

대응표. 재구성하기 단계에서는 수식의 각 항과 코드의 줄을 대응시킨다.

논문 (식 번호) 코드 (파일:줄) 일치 메모
식 (3) 데이터 항 1N∑i(yi−y^i)2\frac{1}{N}\sum_i (y_i - \hat{y}_i)^2 train.py:88 ((y - yhat) ** 2).sum() ✗ 평균이 아니라 합
식 (3) 정규화 항 λ∥w∥2\lambda \lVert \mathbf{w} \rVert^2 train.py:89 lam * (w ** 2).sum() ✓
식 (5) 학습률 감쇠 config.yaml schedule: cosine ? 본문에 감쇠 방식 언급 없음

합과 평균의 차이. 위 표 첫 줄의 차이가 얼마나 큰지 계산한다. 논문은 데이터 항을 평균으로 썼고, 코드는 합으로 썼다. 정규화 계수 λ\lambda는 둘 다 같은 값이다. 능선 회귀(ridge regression)에서 두 손실의 최솟값은 닫힌 형태로 구해진다.

논문: (1NX⊤X+λI)w=1NX⊤y,코드: (X⊤X+λI)w=X⊤y.\text{논문: } \Big(\tfrac{1}{N}\mathbf{X}^\top\mathbf{X} + \lambda\mathbf{I}\Big)\mathbf{w} = \tfrac{1}{N}\mathbf{X}^\top\mathbf{y}, \qquad \text{코드: } \big(\mathbf{X}^\top\mathbf{X} + \lambda\mathbf{I}\big)\mathbf{w} = \mathbf{X}^\top\mathbf{y}.

양변에 NN을 곱해 보면, 논문의 식은 코드에서 λ\lambda를 NλN\lambda로 바꾼 것과 같다. 즉 같은 λ\lambda라도 코드의 실효 정규화 강도는 논문의 1/N1/N이다.

rng = np.random.default_rng(0)
N, D = 200, 10
X = rng.normal(size=(N, D))                  # (N, D)
w_true = rng.normal(size=D)                  # (D,)
y = X @ w_true + rng.normal(size=N)          # (N,)
X_te = rng.normal(size=(2000, D))
y_te = X_te @ w_true + rng.normal(size=2000)

lam = 1.0
# 논문: L = (1/N) sum (y - Xw)^2 + lam |w|^2  ->  (X^T X / N + lam I) w = X^T y / N
w_paper = np.linalg.solve(X.T @ X / N + lam * np.eye(D), X.T @ y / N)
# 공개 코드: loss = ((y - X @ w) ** 2).sum() + lam * (w ** 2).sum()  ->  (X^T X + lam I) w = X^T y
w_code = np.linalg.solve(X.T @ X + lam * np.eye(D), X.T @ y)
w_match = np.linalg.solve(X.T @ X + N * lam * np.eye(D), X.T @ y)   # 코드에서 lam을 N배로

mse = lambda w: np.mean((y_te - X_te @ w) ** 2)
print(f"|w| 참값 {np.linalg.norm(w_true):.2f}  논문 식 {np.linalg.norm(w_paper):.2f}  코드 {np.linalg.norm(w_code):.2f}")
print(f"test MSE  논문 식 {mse(w_paper):.3f}  코드 {mse(w_code):.3f}")
print("코드의 lam을 N배로 하면 논문 식과 같다:", np.allclose(w_match, w_paper))
|w| 참값 3.14  논문 식 1.57  코드 3.12
test MSE  논문 식 3.458  코드 1.050
코드의 lam을 N배로 하면 논문 식과 같다: True

논문의 식을 그대로 구현하면 가중치가 절반으로 줄어들고, 시험 오차는 세 배 넘게 커진다. 논문의 숫자가 공개 코드에서 나왔다면, 수식대로 재구현한 사람은 "재현에 실패했다"고 결론 내리게 된다. 실제로는 방법이 아니라 손실의 스케일이 달랐을 뿐이다. 학습률, weight decay, 온도, 손실 가중치처럼 다른 항과의 상대적 크기가 의미를 갖는 하이퍼파라미터는 모두 이런 스케일 차이에 민감하다. batch마다 평균을 내는지 합을 내는지에 따라 같은 값이 batch 크기에 따라 다른 강도가 되는 경우도 흔하다.

불일치를 발견했을 때. 어느 쪽이 "맞는지" 판단하기 전에 기록부터 한다. 보고된 숫자를 만든 것은 대개 공개 코드이므로, 먼저 코드 그대로 재현하고, 그다음 수식대로 바꿨을 때의 차이를 따로 측정한다. 이 순서를 지켜야 재현 실패의 원인을 분리할 수 있다(23.3장).

실습

23.1장의 지도에서 핵심 논문 하나를 골라, 이해하기 단계까지 읽고 다음 네 질문으로 한 쪽 요약을 쓴다. 가능하면 공개 코드가 있는 논문을 고른다. 이 요약은 23.3장의 재현에서 출발점이 된다.

  1. 무엇을 풀었는가. 문제 정의(입력·출력·일반화 대상), 기여의 종류, 저자가 주장하는 새로운 점.
  2. 왜 작동하는가. 저자의 설명과, 그 설명을 뒷받침하는 실험(ablation 등). 설명이 실험으로 확인되었는지, 추측인지 구분한다.
  3. 무엇을 검증했는가. 데이터·분할·metric·baseline·seed 수. 주장–근거 표를 만든다.
  4. 어디까지 주장 가능한가. 근거가 지지하는 주장의 범위를 한두 문장으로 다시 쓴다. 표에 평균·퍼짐·실행 횟수가 있으면 차이의 구간을 직접 계산해 함께 적는다.

공개 코드가 있으면 핵심 손실 함수와 학습 루프에 대해 수식–코드 대응표를 만든다. 표의 수치로 차이의 구간과 효과 크기를 계산하는 함수를 직접 구현한다.

실패 사례

  • 초록의 주장을 실험이 실제로 지지하는지 확인하지 않는 것. "모든 조건에서", "일관되게", "물리를 학습한다" 같은 말이 데이터셋 몇 개, seed 하나의 실험에 기대고 있는 경우가 많다. 주장–근거 표를 만들어 범위를 다시 정한다.
  • 오차 막대의 의미를 확인하지 않는 것. 표준편차와 표준오차를 구분하지 않으면 정밀도를 n\sqrt{n}배 잘못 읽는다. 캡션에 없으면 두 해석으로 모두 계산해 보고, 약한 쪽까지만 믿는다.
  • 수식만 보고 재구현한 결과를 논문과 비교하는 것. 평균과 합, 정규화 상수, 손실 가중치의 차이가 결과를 크게 바꾼다. 공개 코드가 있으면 대응표를 먼저 만든다.
  • 약한 baseline과의 비교를 그대로 받아들이는 것. baseline이 튜닝되었는지, 단순한 기준과 분야의 표준 방법이 들어 있는지 확인한다.
  • 모든 논문을 같은 깊이로 읽는 것. 목적에 따라 훑어보기·이해하기·재구성하기를 나눈다.

통과 기준

  • 핵심 논문 하나의 한 쪽 요약(무엇을 풀었는가·왜 작동하는가·무엇을 검증했는가·어디까지 주장 가능한가)과 주장–근거 표를 작성한다. 표의 모든 주장에 근거의 위치와 판정이 있다.
  • 논문 세미나에서 그 논문을 주장–근거–한계의 구조로 15분 안에 발표하고, 청중의 질문에 근거의 위치를 짚어 답한다.
  • 공개 코드가 있는 논문에 대해 핵심 손실의 수식–코드 대응표를 만들고, 불일치가 있으면 기록한다.
  • 연습문제 table-effect-size의 테스트를 통과한다.

참고 문헌

  • Keshav (2007). How to read a paper. ACM SIGCOMM Computer Communication Review, 37(3), 83–84. — 세 번에 나눠 읽는 방법
  • Lipton, Steinhardt (2018). Troubling trends in machine learning scholarship. arXiv:1807.03341. — ML 논문에서 자주 보이는 서술상의 문제
  • Melis, Dyer, Blunsom (2018). On the state of the art of evaluation in neural language models. International Conference on Learning Representations (ICLR). — 대규모 하이퍼파라미터 탐색으로 다시 튜닝한 baseline의 비교