
Lucid 개발의 출발점은 단순한 호기심 수준을 넘어, 딥러닝의 내적인 동작 원리를 정말 작동하는 수준에서 이해해보고자 하는 욕구였다. 실제 model training이 가능하고, PyTorch의 미니멀한 클론을 Python + NumPy 환경에서 구현해보고 싶었다.

이전 글에선 Lucid의 출발점이 되었던 autodiff의 추상적 구조인 Tensor를 node로 바라보고 computation graph를 구성한 뒤, 이를 역순으로 순회하며 gradient를 전파한다는 아이디어를 정리했다. 이번 글에선 이를 직접 구현하고자 한다.

Lucid는 연산 과정의 반복적이고 오류가 나기 쉬운 패턴을 제거하기 위해 operation 객체와 func_op 데코레이터를 도입했다. 이 덕분에 연산 구현자는 forward와 gradient 공식만 정의하면 되고, 그래프 연결과 같은 공통 로직은 모두 자동화된다.

정립된 Tensor 연산 시스템을 바탕으로 실제 여러 텐서 연산들을 수학적인 gradient 계산에 기반하여 클래스로 구현하였다.

텐서 유틸리티 연산은 모델 코드에서 자주 등장하지만, 실제로는 shape/axis bookkeeping과 gradient 역전을 정확히 처리하는 작은 규약 모음이다. 이번 글에서는 이러한 유틸리티 연산들을 구현한 과정을 담았다.

PyTorch의 nn.functional을 모티브 삼아 Lucid의 기본적인 Tensor primitives를 바탕으로 이후 신경망 구현 및 학습에 필수적인 functional API를 구축하였다.

이번 글에서는 Lucid의 Functional API 중 특히 합성곱 연산의 구현에 초점을 맞춰서 작성하였다. 합성곱은 다른 연산에 비해 특수하여 이에 최적화된 참신한 기법이 필요하다. 구체적으로 Im2Col+GEMM의 방식을 사용하였다.

Lucid의 nn.Module은 파이토치의 사용성·철학을 최대한 모사하면서 다양한 백엔드에 맞게 단순화된 구현을 제공한다. 자동 등록과 state_dict, 모드/디바이스 전파, hook 지원, 컨테이너까지 포함한 이 뼈대 위에서 이후의 모든 모델 정의가 이루어진다.

Lucid의 파라미터/버퍼 시스템은 PyTorch의 개념을 그대로 가져오되, NumPy/MLX 백엔드에 맞게 단순화했다. Parameter는 항상 학습 대상이며 grad를 보존하고, Buffer는 학습 대상이 아니지만 state_dict에 포함된다.

Lucid의 Optimizer/LRScheduler 베이스는 PyTorch 호환성을 목표로, Parameter만 다루는 optimizer와 param_group 학습률을 갱신하는 스케줄러라는 단순한 원리를 따른다.

MLX 통합은 Lucid가 NumPy 전용 연구용 프레임워크에서 Apple 실리콘을 온전히 활용하는 실전 엔진으로 이동했다는 전환점이다.

Lucid에서 데이터 파이프라인은 사용자가 가장 자주 만나는 영역이다. PyTorch의 Dataset/DataLoader를 거의 습관처럼 쓰던 입장에서, Lucid도 익숙한 사용감을 제공하는 것이 중요했다.

Lucid 고유의 데이터 타입 시스템인 Numeric은 작은 클래스지만, 백엔드 데이터 타입 이중화라는 근본 문제를 사용자에게서 숨겨주는 핵심 부품이다.

Lucid가 제공하는 SGD 계열과 Adam 계열 옵티마이저를 대표로 삼아 왜 이 두 축이 실전 학습에서 기본 선택이 되는지, 그리고 수식이 코드에 어떻게 녹아들어 있는지를 집중적으로 다룬다.

이번 개발 일지는 Lucid의 학습률 스케줄러를 총망라해, 수식과 코드가 어떻게 1:1로 대응되는지 긴 호흡으로 풀어본다.

이번 개발 일지에서는 Lucid의 완성도와 사용성을 현실적인 기준으로 점검하기 위해, 고전적인 CNN인 LeNet-5로 MNIST를 학습시키고 PyTorch와 결과를 비교했다.

이번 ResNet-18/CIFAR-10 환경에서의 PyTorch와의 비교 실험은 Lucid가 초소형 모델 검증을 넘어서 중형급 CNN을 제대로 다룰 수 있는지를 확인하는 리포트의 성격이다.