esp32-ai: 파라미터 대부분을 플래시에 두고 ESP32-S3에서 28.9M 언어 모델을 돌리기

esp32-ai 소개

마이크로컨트롤러에서 언어 모델을 돌릴 때 모델 크기를 결정하는 것은 연산량이 아니라 빠른 메모리의 양입니다. ESP32-S3에 들어 있는 내부 SRAM은 512KB뿐이고, 토큰마다 수없이 읽히는 값들이 그 안에 들어가야 합니다. 임베딩 표는 보통 파라미터의 대부분을 차지하기 때문에, 표를 어디에 둘 것인가가 곧 모델 크기의 상한이 됩니다.

esp32-ai는 그 상한을 메모리 계층으로 우회한 프로젝트입니다. 2,890만 개 파라미터 가운데 2,500만 개를 조회 표(lookup table)로 만들어 느린 플래시에 남겨 두고, 한 토큰을 만들 때 그 표에서 필요한 여섯 줄 정도, 약 450바이트만 꺼내 씁니다. 모델 대부분은 실행을 위해 메모리로 올라오지 않고 플래시에 앉은 채 조금씩 샘플링됩니다.

이 배치를 가능하게 하는 것이 Google이 Gemma 3n에서 제시한 계층별 임베딩(Per-Layer Embeddings, PLE) 입니다. 원래는 휴대폰과 GPU를 염두에 둔 설계인데, esp32-ai는 같은 아이디어를 마이크로컨트롤러의 메모리 배치 위에서 구현했습니다.

esp32-ai가 내놓은 수치

항목
파라미터 2,890만 개 저장 (그중 2,500만 개는 플래시 조회 표)
ESP32-S3, SRAM 512KB, PSRAM 8MB, 플래시 16MB
속도 종단간 초당 9.88토큰, 토큰당 연산 94.9ms
연결성 없음, 전부 기기 위에서 동작
모델 크기 4비트 양자화 기준 14.9MB

ESP32-S3 보드에 연결된 화면에서 모델이 글을 생성하는 esp32-ai 데모

esp32-ai의 세 계층 메모리 배치

각 계층에는 그 계층의 읽기 빈도에 맞는 것만 들어갑니다. 저장소는 이 배치를 다음과 같이 정리하고 있습니다.

  SRAM  (빠르고 작음)   활성값과 정규화 가중치, 토큰마다 여러 번 읽힘
  PSRAM (중간)          코어와 출력 헤드, 위치마다 한 번 읽힘
  FLASH (크고 느림)     2,500만 파라미터 표, 토큰당 약 6줄(450B) 읽음

실제 ESP32-S3 N16R8에서 Xtensa 사이클 카운터로 측정한 대역폭은 이 설계가 성립하는 근거입니다. 조회 표를 플래시에 두는 비용이 얼마나 되는지가 프로젝트 전체가 걸려 있던 숫자였습니다.

측정 항목
PSRAM 순차 읽기 60.7 MB/s
내부 SRAM 순차 읽기 240 MB/s
플래시 무작위 읽기 (512바이트 한 줄) 20.3 마이크로초
토큰당 표 비용 (무작위 6줄) 약 0.12ms
토큰당 출력 헤드 비용 (PSRAM 1.5MB 스캔) 약 17.3ms
대역폭만 고려한 상한 약 58 tok/s

표를 읽는 비용은 토큰당 메모리 시간의 약 0.7%에 그칩니다. 오히려 시간을 잡아먹는 쪽은 기준 모델도 똑같이 치르는 출력 헤드입니다. 저장소는 이 0.7%를 "표의 합성 대역폭 점유율" 로 인용하라고 못 박고 있으며, 이것이 관측된 추론 처리량이 아니라 대역폭 기준 상한이라는 점도 함께 적어 두었습니다.

esp32-ai가 검증한 계층별 임베딩의 효과

배포 구성은 어휘 32,768, d_model 96, 6개 층, ple_dim 128이고 코어는 약 559K 파라미터입니다. 코어 크기를 맞춘 상태에서 두 개의 시드로 비교한 결과가 RESULTS.md 에 정리되어 있습니다.

구성 코어 전체 퍼플렉서티 기준 대비
baseline 559K 3.7M 12.58
ple 558K 28.9M 11.41 0.098 nats 개선
fatembed 559K 28.9M 11.94 0.052 nats 개선

같은 코어를 쓰면서 SRAM에 들어가는 기준 모델보다 퍼플렉서티가 12.58에서 11.41로 내려갔고, 저장소는 이 차이가 시드 잡음의 약 16배라고 밝히고 있습니다. 임베딩을 아래쪽에 한 번에 주입하는 fatembed 와 비교하면 층마다 주입하는 쪽이 0.046 nats 더 좋았습니다.

어휘 크기가 이 효과를 좌우한다는 점도 대조군으로 확인되어 있습니다. 어휘 4,096에서는 PLE의 이득이 0.025 nats에 그치지만 어휘 32,768에서는 0.098 nats로 네 배가 됩니다. 어휘가 커질수록 표는 커지는 동시에 토큰마다 읽는 양은 그대로라서, 표를 느린 저장소에 두는 전략이 유리해지는 구간에 들어갑니다.

저장소는 "2,890만 파라미터" 라는 표현이 무엇을 뜻하는지도 분명히 해 두었습니다. 이것은 저장된 파라미터 수이고, "메모리 계층 분할로 상주시킨 파라미터" 로 인용해야 하며 능력의 배수로 읽어서는 안 된다고 적고 있습니다.

esp32-ai로 직접 돌려보기

내려받기와 배포가 서로 다른 명령으로 분리되어 있습니다. 한쪽은 네트워크에 닿고 다른 쪽은 보드를 건드립니다.

scripts/fetch_model.sh <tinystories|barista>   # 내려받기, 검증, artifacts/ 에 설치
scripts/deploy.sh      <tinystories|barista>   # 헤더 생성, 게이트 실행, 컴파일, 플래시

scripts/fetch_model.sh 는 추론 자산을 스크립트에 고정된 SHA-256과 바이트 크기로 검사하고, 릴리스가 함께 배포하는 metadata.json 도 같은 값과 교차 확인합니다. 모든 검사를 통과해야만 설치하므로 내려받기가 실패해도 기존 파일은 그대로 남습니다. scripts/deploy.sh 는 네트워크에 전혀 접근하지 않고 artifacts/<model>/ 에 있는 것만으로 동작합니다. 보드가 한 번에 하나의 모델만 담기 때문에 두 명령 모두 모델 이름을 반드시 받습니다.

공개된 모델은 두 가지입니다. 이야기 생성용 TinyStories 모델 과 에스프레소 관련 질의응답용 Barista 모델 이 각각 Hugging Face에 올라와 있습니다. 재사용 가능한 아키텍처는 src/ 에, 공개된 수치를 재현하는 학습과 절제 실험, 양자화 코드는 research/tinystories/ 에 있습니다.

esp32-ai는 누구에게 유용한가

임베디드 환경에서 모델을 어떻게 쪼개 배치할지 고민하는 사람에게 참고 자료로 잘 맞습니다. 대역폭 측정과 절제 실험이 함께 공개되어 있어서 "왜 되는가" 를 숫자로 따라갈 수 있고, C 런타임이 내보낸 PyTorch 기준값과 32,768개 로짓 전부에서 최대 절대 오차 0.00001로 일치한다는 검증 절차도 그대로 드러나 있습니다.

반대로 쓸 만한 챗봇이나 비서를 기대하고 접근하면 맞지 않습니다. 학습 데이터가 TinyStories라서 짧고 단순한 이야기를 그럭저럭 일관되게 쓰는 정도이고, 질문에 답하거나 지시를 따르거나 코드를 쓰거나 사실을 아는 일은 하지 못합니다. 이 한계는 추론을 담당하는 작은 코어에서 오는 것이라 메모리 배치 기법으로는 바뀌지 않습니다. 흥미로운 지점은 2,890만 파라미터 모델이 무슨 말을 할 수 있는가가 아니라, 큰 모델을 작은 칩에 앉히는 구조 자체에 있습니다.

esp32-ai가 딛고 선 자료

학습에 쓰인 TinyStories는 작은 모델도 일관된 글쓰기를 배울 수 있을 만큼 단순한 합성 이야기 모음으로, Microsoft Research의 Ronen Eldan과 Yuanzhi Li가 공개했습니다. 다른 한 축인 계층별 임베딩은 Gemma 3n의 설계이고, 작은 언어 모델을 학습해 순수 C로 돌리는 부분은 Andrej Karpathy의 llama2.c 를 참고했습니다.

esp32-ai의 라이선스

esp32-ai는 MIT 라이선스로 공개되어 있어 개인 및 상업적 목적으로 자유롭게 사용할 수 있습니다.

:scroll: TinyStories 논문

:books: Google Gemma 3n 계층별 임베딩 문서

:github: esp32-ai 프로젝트 GitHub 저장소

:hugs: esp32-ai 모델 다운로드

더 읽어보기




이 글은 GPT 모델로 정리한 초안을 바탕으로 한 것으로, 원문의 내용 또는 의도와 다르게 정리된 내용이 있을 수 있습니다. 관심있는 내용이시라면 원문도 함께 참고해주세요! 읽으시면서 어색하거나 잘못된 내용을 발견하시면 댓글로 알려주시기를 부탁드립니다. :hugs:

이 도구를 직접 설치해 사용해보셨다면, :pytorch:파이토치 한국 사용자 모임:south_korea: 회원들을 위해 경험이나 팁을 댓글로 남겨주세요! :folded_hands:

1개의 좋아요