온디바이스 인텔리전스의 비밀: 모바일 NPU를 위한 가중치 프루닝(Weight Pruning) 및 희소성(Sparsity) 마스터하기
요약
모바일 기기에서 LLM을 효율적으로 실행하기 위한 가중치 프루닝과 희소성(Sparsity) 기술을 다룹니다. 로또 티켓 가설을 바탕으로 모델의 불필요한 매개변수를 제거하여 NPU 최적화를 달성하는 원리를 설명합니다.
핵심 포인트
- 가중치 프루닝을 통해 모델의 크기를 줄이고 전력 소모를 최적화함
- 로또 티켓 가설(LTH)은 거대 모델 내 최적의 서브 네트워크 존재를 설명함
- 비구조적 희소성과 구조적 희소성의 차이를 이해하는 것이 중요함
- 모바일 NPU 환경에서 온디바이스 AI 구현을 위한 핵심 기술임
진정으로 개인적이고, 프라이빗하며, 즉각적인 AI라는 꿈은 항상 단 하나의 거대한 문제, 즉 **하드웨어 제약(hardware constraints)**에 의해 병목 현상을 겪어왔습니다.
우리는 Gemini Nano와 같은 대규모 언어 모델(LLMs)이 멀리 떨어진 데이터 센터가 아니라 우리 휴대폰에서 로컬로 실행되기를 원합니다. 하지만 여기에는 근본적인 긴장 관계가 존재합니다. 심층 신경망(Deep neural networks)은 거대하고 전력을 많이 소모하는 괴물과 같습니다. 전형적인 밀집 모델(dense model)은 모든 뉴런이 다른 모든 뉴런과 연결된 부동 소수점(floating-point) 숫자의 밀집된 숲입니다. 수백 와트의 전력을 사용하는 데스크톱 GPU에서는 이것이 괜찮습니다. 하지만 배터리로 구동되는 모바일 기기에서는 이는 스로틀링(thermal throttling)과 즉각적인 애플리케이션 충돌을 야기하는 레시피가 됩니다.
우리는 어떻게 이 간극을 메울 수 있을까요? 그 답은 고빈도 매매(high-frequency trading) 알고리즘에나 어울릴 법한 개념이지만, 실제로는 현대 모바일 AI의 근간이 되는 개념인 **희소성 (Sparsity)**에 있습니다.
이번 심층 분석에서는 가중치 프루닝 (weight pruning)이 어떻게 거대하고 비효율적인 모델을 날렵하고 강력한 NPU 최적화 기계로 변모시키는지, 그리고 Android 개발자로서 여러분이 Kotlin 2.x와 최신 Android 시스템 아키텍처를 사용하여 이러한 최적화를 어떻게 활용할 수 있는지 탐구할 것입니다.
이론적 토대: "당첨된 티켓" 찾기
우리가 왜 프루닝을 하는지 이해하려면, 우선 모델들이 왜 애초에 그렇게 "뚱뚱한"지 이해해야 합니다. 대부분의 현대 신경망은 과잉 매개변수화 (over-parameterization) 문제를 겪습니다. 훈련 과정에서 우리는 모델이 복잡한 패턴을 학습할 수 있는 충분한 "용량 (capacity)"을 확보하기 위해 거대한 아키텍처를 구축합니다. 하지만 훈련이 완료되면, 그 가중치(weights) 중 놀라울 정도로 많은 수가 거의 아무런 역할도 하지 않는다는 것을 깨닫게 됩니다. 그것들은 사실상 노이즈(noise)에 불과합니다.
로또 티켓 가설 (Lottery Ticket Hypothesis, LTH)
만약 대부분의 가중치가 쓸모없다면, 왜 처음부터 작은 모델을 훈련시키지 않을까요? 여기서 **로또 티켓 가설 (Lottery Ticket Hypothesis)**이 등장합니다.
LTH는 거대하고 무작위로 초기화된 신경망(Neural Network) 내에 더 작은 서브 네트워크, 즉 '당첨 티켓 (Winning Ticket)'이 포함되어 있으며, 이를 단독으로 훈련시킬 경우 원래의 밀집 네트워크 (Dense Network)와 동일한 정확도에 도달할 수 있다고 가정합니다. 우리가 거대한 모델을 훈련시키는 것은 본질적으로 수백만 장의 로또 티켓을 사는 것과 같습니다. 대부분은 낙첨됩니다. 가중치 프루닝 (Weight Pruning)은 이러한 당첨 티켓을 식별하고 낙첨된 티켓들을 버리는 정교한 과정입니다.
비구조적 희소성 (Unstructured Sparsity) vs. 구조적 희소성 (Structured Sparsity): 개발자의 딜레마
모든 프루닝이 동일하게 만들어지는 것은 아닙니다. 엔지니어로서 잘못된 유형의 희소성 (Sparsity)을 선택하면 모델이 원래의 밀집 버전보다 오히려 더 느려질 수 있습니다.
-
비구조적 프루닝 (Unstructured Pruning, 미세 입자형): 이는 중요도가 낮은 개별 가중치들을 0으로 설정하는 것을 포함합니다.
- 장점: 믿을 수 없을 정도로 정밀합니다. 정확도를 크게 해치지 않으면서 '가장 약한 연결 고리'들을 제거할 수 있습니다.
- 단점: '구멍이 뚫린' 행렬을 생성합니다. 표준 하드웨어 (CPU 및 GPU)는 연속적인 메모리 (Contiguous Memory)를 선호합니다. 프로세서가 0이 아닌 값을 찾기 위해 행렬 여기저기를 '점프'하며 돌아다녀야 할 때, 이러한 점프를 관리하는 오버헤드가 연산을 건너뛰어 얻는 이득보다 커지는 경우가 많습니다. 이것이 비구조적 희소성이 표준 모바일 하드웨어에서 실제적인 속도 향상을 제공하지 못하는 경우가 많은 이유입니다.
-
구조적 프루닝 (Structured Pruning, 조립 입자형): 개별 가중치 대신, 전체 뉴런 (Neurons), 채널 (Channels), 또는 필터 (Filters)와 같은 아키텍처 구성 요소 전체를 제거합니다.
- 장점: 결과물인 텐서 (Tensor)는 여전히 밀집된(Dense) 형태의 더 작은 데이터 블록입니다. 이는 기존의 BLAS (Basic Linear Algebra Subprograms) 라이브러리 및 NPU 시스톨릭 어레이 (Systolic Arrays)와 즉시 호환됩니다.
- 단점: 매우 공격적입니다. 채널 전체를 제거하는 것은 손톱 하나를 제거하는 것이 아니라 팔 전체를 제거하는 것과 같습니다. 중요한 정보를 잃을 가능성이 훨씬 높으며, 이는 정확도의 급격한 하락으로 이어질 수 있습니다.
하드웨어 가속: NPU가 0을 활용하는 방법
표준 CPU에게 0은 그저 또 다른 숫자일 뿐입니다. 하지만 현대적인 **신경망 처리 장치 (NPU, Neural Processing Unit)**에게 0은 에너지를 절약할 수 있는 기회입니다.
신경망 연산의 핵심은 곱셈-누산 (MAC, Multiply-Accumulate) 연산인 $y = \sum (w_i \cdot x_i)$입니다. 만약 가중치($w_i$)가 0이라면, 결과값은 0임이 보장됩니다. "희소성 인식 (sparsity-aware)" NPU는 제로 스키핑 (Zero-Skipping) 로직을 사용합니다. MAC 유닛을 실행하기 전에 하드웨어가 가중치 메타데이터를 확인합니다. 만약 그것이 0이라면, NPU는 곱셈뿐만 아니라 그에 대응하는 활성화 값 (activation)에 대한 메모리 읽기 과정까지 건너뜁니다.
에너지 배당금: 메모리 vs 연산
이는 모바일 개발자에게 가장 중요한 시사점입니다: 모바일 AI에서는 데이터를 이동시키는 비용이 계산하는 비용보다 더 많은 에너지를 소모합니다.
LPDDR5 RAM에서 NPU 캐시로 값을 읽어오는 것은 실제 부동 소수점 곱셈보다 수십 배(orders of magnitude) 더 많은 전력을 소비합니다. 압축 희소 행 (CSR, Compressed Sparse Row) 또는 **압축 희소 열 (CSC, Compressed Sparse Column)**과 같은 희소 저장 형식을 사용하면, 단순히 공간을 절약하는 것뿐만 아니라 버스를 통해 데이터를 이동시키는 데 필요한 대역폭을 줄일 수 있습니다. 이는 칩의 열 발자국 (thermal footprint)을 줄여주며, 긴 AI 세션 동안 성능을 저하시키는 치명적인 열 스로틀링 (thermal throttling) 현상을 방지합니다.
안드로이드의 혁명: AICore와 Gemini Nano
Google은 이러한 복잡하고 희소한 모델들을 관리하는 것이 개별 앱 개발자 모두의 책임이 되어서는 안 된다는 점을 인식했습니다. 이것이 AICore의 도입으로 이어졌습니다.
AICore가 패러다임 전환인 이유
기존 방식에서는 만약 세 개의 서로 다른 앱이 유사한 LLM을 사용한다면, 각 앱은 2GB 이상의 모델 복사본을 기기의 RAM에 개별적으로 로드해야 했습니다. 이는 거의 즉시 저메모리 킬러 (LMK, Low Memory Killer)를 트리거하게 됩니다.
AICore는 CameraX가 카메라 하드웨어를 추상화하는 방식과 유사하게, AI 모델을 시스템 수준의 리소스로 취급합니다. 모델은 권한이 부여된 시스템 프로세스에 상주합니다. 앱들은 프로세스 간 통신 (IPC, Inter-Process Communication)을 통해 모델과 통신합니다. 이를 통해 다음과 같은 사항을 보장합니다:
- 중복 로딩 없음 (No Redundant Loading): 가중치(Weights)는 RAM에 한 번만 로드되어 공유됩니다.
- 효율적인 웜업 (Efficient Warm-up): NPU 캐시(Cache)가 시스템 전체를 위해 한 번만 웜업됩니다.
- 원활한 업데이트 (Seamless Updates): Google이 시스템 업데이트를 통해 Gemini Nano를 개선하면, 앱은 Play Store 업데이트 없이도 자동으로 그 혜택을 받습니다.
이론에서 현대적인 Kotlin 2.x로의 연결
이러한 희소(Sparse) 시스템 수준 모델과 상호 작용하는 시스템을 구현하려면 동시성(Concurrency)과 하드웨어 추상화(Hardware Abstraction)에 대한 정교한 접근 방식이 필요합니다. 바로 이 지점에서 Kotlin 2.x가 빛을 발합니다.
AICore와 함께 작업할 때, 모델 로딩은 비동기적이고 다단계인 라이프사이클(Lifecycle)을 가집니다. 우리는 단순히 "함수를 호출"하는 것이 아니라, 상태(State)를 관찰해야 합니다. StateFlow와 코루틴(Coroutines)을 사용함으로써, Loading에서 Warming Up을 거쳐 Ready로 이어지는 전환을 라이프사이클을 인식하는(Lifecycle-aware) 방식으로 관리할 수 있습니다.
나아가, Kotlin에 도입된 **컨텍스트 수신자 (Context Receivers)**를 사용하면 하드웨어 구현으로부터 분리된 매우 최적화된 추론(Inference) 코드를 작성할 수 있습니다. 우리는 AIExecutionEnvironment를 정의하고, 유효한 NPU 핸들(Handle)이 스코프(Scope) 내에 있을 때만 추론 로직이 실행되도록 보장할 수 있습니다.
견고한 모델 매니저(Model Manager) 구현하기
다음은 희소 모델 메타데이터와 NPU 상태의 복잡성을 처리하기 위해 Kotlin 2.x를 사용하여 고수준의 ModelManager를 구조화하는 방법입니다.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.serialization.*
...
실전 구현: 희소성 성능 분석기 (Sparsity Performance Analyzer)
"희소성 배당 (Sparsity Dividend)"을 진정으로 이해하려면 실제로 작동하는 모습을 확인해야 합니다. 우리는 **희소성 성능 분석기 (Sparsity Performance Analyzer)**를 구축할 수 있습니다. 이는 CameraX 입력을 사용하여 밀집(Dense) 모델과 프루닝(Pruned)된 모델을 나란히 실행하고, 실시간 지연 시간(Latency) 차이를 측정하는 Android 애플리케이션입니다.
1. 추론 엔진 (TFLite + NNAPI)
Android에서 희소성 (Sparsity)을 활용하는 핵심은 **NNAPI (Neural Networks API)**입니다. NNAPI는 NPU로 향하는 관문 역할을 합니다. 프루닝 (Pruning)된 모델을 NNAPI를 통해 전달하면, 드라이버가 하드웨어의 제로 스킵 (Zero-skipping) 기능을 활용할 수 있습니다.
package com.edgeai.performance.sparsity
import android.content.Context
...
2. ViewModel: 속도 향상 측정
우리는 동일한 비디오 프레임에서 밀집 모델 (Dense model)과 프루닝된 모델 (Pruned model)의 실행 시간을 비교하기 위해 measureNanoTime을 사용합니다.
package com.edgeai.performance.sparsity
import androidx.lifecycle.ViewModel
...
요약: 엣지 AI (Edge AI)로 가는 길
우리가 살펴본 바와 같이, 밀집 (Dense) 모델에서 희소 (Sparse) 모델로의 전환은 단순한 수학적 최적화가 아닙니다. 이는 LLM (Large Language Models)과 복잡한 신경망을 엣지 (Edge)로 가져오기 위한 근본적인 요구 사항입니다.
복권 가설 (Lottery Ticket Hypothesis), NPU 하드웨어 아키텍처 (Hardware architecture), 그리고 AICore의 시스템 수준 설계 사이의 교차점을 이해함으로써, 개발자들은 단순히 "API를 호출하는 것"을 넘어 진정으로 성능이 뛰어나고 열 효율적인 AI 애플리케이션을 구축하기 시작할 수 있습니다.
| 개념 | 밀집 모델 (Dense Model) | 프루닝/희소 모델 (Pruned/Sparse Model) | Android/NPU 영향 |
|---|---|---|---|
| 가중치 분포 (Weight Distribution) | 대부분의 가중치 $\neq 0$ | 대부분의 가중치 $= 0$ | 메모리 사용량 (Memory footprint) 감소 |
| ... |
함께 논의해 봅시다
- 구조적 vs 비구조적 (Structured vs Unstructured): 현재 모바일 NPU 하드웨어의 상태를 고려할 때, 결국 순수하게 구조적 프루닝 (Structured pruning)으로의 전환이 일어날 것이라고 보십니까, 아니면 하드웨어가 발전하여 비구조적 희소성 (Unstructured sparsity)도 그만큼 효율적으로 처리하게 될 것이라고 보십니까?
- AICore 모델: AI에 대한 "시스템 서비스 (System Service)" 접근 방식(AICore와 같은)이 Android를 위한 올바른 방향이라고 생각하십니까, 아니면 개발자가 고도로 맞춤화된 실험적인 모델 아키텍처를 구현하는 능력을 제한한다고 생각하십니까?
여러분의 생각을 아래 댓글로 남겨주세요!
여기서 시연된 개념과 코드는 전자책인 Edge AI Performance. Optimizing hardware acceleration via NPU (Neural Processing Unit), GPU, and DSP에 명시된 포괄적인 로드맵에서 직접 가져온 것입니다. 해당 전자책은 여기에서 확인하실 수 있습니다.
Python, TypeScript, C#, Swift, Kotlin을 활용한 다른 모든 프로그래밍 및 AI 전자책도 Leanpub.com에서 확인해 보세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기