[Lucid] Lucid 개발 동기와 Autodiff 구상

안암동컴맹·2025년 11월 20일

Lucid Development

목록 보기
1/20
post-thumbnail

🚀 Lucid 개발 동기와 Autodiff 구상

📘 개발을 결심하게 된 배경

Lucid 개발의 출발점은 단순한 호기심 수준을 넘어, 딥러닝의 내적인 동작 원리를 정말 작동하는 수준에서 이해해보고자 하는 욕구였다. 특히 밑바닥부터 시작하는 딥러닝 2 를 읽으면서 깊은 자극을 받았다. 이 책은 순수 Python 만으로 Tensor 구조와 간단한 자동미분(Autodiff) 엔진을 구현해 나가는 과정을 담고 있었고, 단순한 코드 표현 너머에 숨겨진 아이디어를 충분히 드러내주었다.

책을 읽으며 "나도 from-scratch 방식으로 딥러닝 프레임워크를 만들어보고 싶다" 는 생각이 자연스럽게 떠올랐다. 단, 나는 책의 수준에서 만족하고 싶지 않았다. 실제 model training이 가능하고, 외관상으로도 PyTorch(이하 torch)에 뒤지지 않는, 오히려 torch의 미니멀한 클론처럼 느껴질 만큼 자연스러운 사용성을 가진 프레임워크를 Python + NumPy 라는 제한적인 환경 안에서 직접 구현해보고 싶었다. 이것이 Lucid 개발의 최초 동기이자 핵심 문제의식이었다.

참고로 Lucid라는 이름은 내가 개인적으로 좋아하는 영단어인 lucid (adj. 명료한, 뚜렷한)Lumerico's Comprehensive Interface for Deep Learning 으로 나름 그럴싸하게(?) 약어화한 이름이다.

🐱 깃헙 리포지토리


🧩 Autodiff 구상: Tensor을 Node로 바라보기

Autodiff는 딥러닝 프레임워크의 심장이라고 해도 과언이 아니다. 모델의 파라미터를 업데이트하기 위해서는 loss(손실 함수)에서부터 gradient를 역방향으로 흘려보내야 하는데, 이는 매우 많은 연산을 체계적으로 계산해야 하기 때문이다. 나는 이 autodiff 과정을 완전히 bottom-up 방식으로 다시 나만의 방식으로, 내 능력 안에서 구현해보고 싶었다.

이때 떠올린 아이디어는 의외로 단순했다. Tensor를 단순한 데이터 배열이 아니라, 계산 그래프(computation graph)의 노드로 바라보는 것이다. Tensor 라는 새로운 커스텀한 자료 구조에 자신의 데이터뿐 아니라, 자신을 만들어낸 연산(operation) 정보부모(parent) Tensor 들을 함께 담아야 한다는 구상이었다. 그러면 forward 연산이 일어날 때마다 자연스럽게 서로 연결된 구조가 만들어지고, 이 연결이 쌓이며 하나의 거대한 그래프가 완성될 것이라고 생각했다.

이 구조는 linked list(연결 리스트)처럼 단방향으로 흘러가는 형태이면서도, 실제로는 Directed Acyclic Graph(DAG) 의 형태로 확장된다. 연산이 중첩될수록 그래프는 복잡해지지만 순환성이 없기 때문에, 역전파(backpropagation) 가 가능하다는 중요한 특성을 유지할 수 있다.

🔗 Computation Graph를 통한 미분

Computation graph는 단순히 연산 기록의 모음이 아니라, 역방향 미분을 가능하게 하는 핵심 구조다. Forward pass 동안 Tensor는 다음과 같은 정보를 유지하며 그래프를 확장한다.

  • 자신을 생성한 Tensor operation
  • 해당 operation의 입력이었던 Tensor들
  • Backward 시 사용될 로컬 derivative(미분) 정보

이렇게 생성된 그래프는, 역방향으로 순회하기만 하면 모든 gradient를 체계적으로 계산 할 수 있는 기반이 된다. Forward는 데이터를 흘려보내는 과정이지만, backward는 그 흐름을 뒤집어가며 각 경로에서 발생하는 미분량을 모두 합산(aggregate)하는 절차이다.

위 아이디어를 바탕으로 Tensor 자료형의 프로토타입을 설계하자면 다음 과 같다.

class Tensor:
	self.data: NumPyArray  # 데이터 값
    self.grad: NumPyArray | None = None  # Local 미분값 (초기에는 None)
    self.operation: Callable  # 자신을 생성한 operation
    self.prev: List[Self]  # 부모 Tensor

Tensor을 node로 바라보는 접근은 autodiff 구현을 극도로 단순화했다.

모든 연산을 기록한다 \rightarrow 역순으로 따라간다 \rightarrow Chain Rule을 적용한다

라는 단일한 아이디어만으로 수많은 연산의 미분을 일반화할 수 있기 때문이다.

📐 Chain Rule 기반의 Autodiff

수학적으로 autodiff는 연쇄 법칙(chain rule) 을 반복 적용하는 과정이다. 모든 연산을 국소적으로(locally) 미분 가능하다 고 가정하다고, 각 연산이 input에 대해 어떤 gradient를 갖는지만 알고 있으면 어떤 복잡한 함수든 미분할 수 있다.

Backward기 특정 Tensor의 gradient는 그 Tensor를 필요로 했던 모든 연산의 gradient contribution(기여도) 를 합산한 결과가 된다. 이를 수식으로 표현하면 다음과 같다.

xi=jchildren(xi)xjxjxi\nabla_{\mathbf{x}_i}=\sum_{j\in\text{children}(\mathbf{x}_i)}\nabla_{\mathbf{x}_j}\cdot\frac{\partial\mathbf{x}_j}{\partial\mathbf{x}_i}

이 공식 하나가 autodiff의 모든 동작을 설명한다. 결국 backpropagation"모든 children으로부터 gradient를 모아 parent에세 전달하는 과정" 에 불과하다.

🔙 Reverse-mode Autodiff와 역전파의 본질

Lucid의 autodiff 설계를 고민하던 당시, Reverse-mode autodiff는 선택이 아니라 사실상 필연적인 방식이었다. 그 이유는 딥러닝 자체의 구조적 특성 때문인데, 대부분의 모델이 scalar loss(종속 변수 하나)와 수많은 파라미터들(수천~수억의 독립 변수)로 구성되어 있기 때문이다.

Forward-mode는 독립 변수 각각에 대해 미분을 따로 계산해야 하므로, 파라미터가 많아질수록 계산량이 폭증한다. 반면 reverse-mode는 종속 변수의 수만큼만 미분을 수행하면 되는데, loss는 보통 하나이므로 매우 효율적이다.

딥러닝에서 말하는 역전파(backpropagation)는 결국 reverse-mode autodiff의 구체적 구현이라고 볼 수 있다.

Forward에서 만들어진 computation graph를 기반으로, loss에서 시작하여 그 값을 역방향으로 전파(propagate)하고, 각 Tensor가 가진 local derivative를 이용해 parent 방향으로 gradient를 계산한다. 이때 graph는 위상 정렬된 순서의 역순 으로 순회되는데, 이는 모든 자식 정보가 부모보다 항상 먼저 계산되기 때문이다.

Reverse-mode의 핵심은 다음과 같은 단일 흐름으로 설명될 수 있다.

1. Forward에서 computation graph를 구성한다.
2. Loss의 초기 gradient를 11로 둔다.
3. Computation graph에 대한 위상 정렬(topological sort) 를 수행한다.
4. 그래프를 역방향으로 순회하며 각 Tensor의 local derivative를 parent에 전달한다.
5. Parent는 여러 경로로부터 전달된 gradient를 모두 축적한다.

이 방식은 딥러닝 모델처럼 계층적이며 비선형적인 구조에서도 안정적이고 효율적으로 작동한다.


🌱 Lucid 개발의 뿌리가 된 설계 원리

Lucid의 모든 구조인 Tensor 클래스, operation 추상화, backward autodiff 시스템 등은 결국 다음과 같은 원리에서 출발하였다. Tensor는 단순한 data container가 아니라 computation graph의 node이며, 연산은 그래프의 확장, backward는 그래프의 역방향 해석 과정이다.

이 단순한 구조적 관점을 실제 구현으로 확장하는 과정이 이후 본격적인 Lucid 개발의 촉매로 작용하게 되었다.

profile
Korea Univ. Computer Science & Engineering

0개의 댓글