모델 운영

MLOPS / 88번째 글

엣지 NPU 런타임 — 모델을 기기 안 전용 칩에 올릴 때

휴대폰과 산업용 기기의 NPU에 모델을 올리는 일이 GPU 배포와 어떻게 다른지 정리합니다. 미리 컴파일해야 하는 이유, 연산자 지원에서 막히는 지점, 그리고 NPU가 실제로 이기는 자리를 다룹니다.

PALDYN Team15 MIN READ

휴대폰, 노트북, 공장의 검사 장비, 매장의 카메라 박스에는 이제 NPU(Neural Processing Unit)가 들어 있다. 신경망의 행렬 곱과 합성곱만 골라 아주 적은 전력으로 처리하는 전용 회로다. 같은 작업을 CPU로 할 때보다 열 배 넘게 적은 전력을 쓴다는 숫자가 스펙 시트에 적혀 있다.

지난 글에서 브라우저의 WebGPU를 다뤘는데, 그쪽은 표준 API 하나로 여러 장치를 덮는 세계였다. NPU는 정반대다. 벤더마다 도구가 다르고, 되는 모델과 안 되는 모델이 갈리고, 기기에 올린 뒤에는 아무것도 못 바꾼다. 이 차이를 모르고 GPU 배포하듯 접근하면 프로젝트 절반쯤에서 막힌다.

실행할 것을 미리 만들어 가야 한다

GPU 배포는 유연하다. 모델 파일을 서버에 두고 런타임이 읽어서 커널을 만들고 돌린다. 모델을 바꾸고 싶으면 파일을 갈아 끼우면 된다.

NPU에는 그 경로가 없다. NPU는 범용 프로세서가 아니라 정해진 연산만 하는 회로라서, 모델을 그 회로가 이해하는 명령 묶음으로 미리 번역해 두어야 한다. 그 번역을 하는 것이 벤더가 제공하는 컴파일러이고, 결과물은 그 칩 계열에서만 도는 전용 바이너리다.

NPU 배포의 빌드 시점과 실행 시점

이 구조에서 나오는 성질이 셋이다.

빌드가 개발 기계에서 끝난다. 기기에서는 이미 만들어진 바이너리를 불러 실행만 한다. 그래서 배포가 앱 업데이트와 같은 절차를 탄다 — 모델만 살짝 고쳐 내보내는 일이 안 된다.

칩 계열마다 따로 만든다. 퀄컴용으로 컴파일한 것은 미디어텍 NPU에서 안 돈다. 안드로이드 기기를 여럿 지원한다면 빌드 산출물이 그만큼 늘어난다.

컴파일 단계에서 거절당한다. 그리고 이것이 실무에서 가장 자주 부딪히는 벽이다.

연산자 지원이 실제 병목이다

NPU 컴파일러는 모델 그래프를 훑으면서 각 연산자를 자기 회로에 대응시킨다. 대응이 없는 연산자를 만나면 두 가지 중 하나가 일어난다 — 컴파일이 실패하거나, 그 부분만 CPU에서 돌리도록 그래프를 쪼갠다.

후자가 더 위험하다. 컴파일은 성공했는데 실행해 보니 기대의 3분의 1도 안 나오는 상황이 여기서 나온다. 그래프가 NPU와 CPU를 오가면 그 경계마다 메모리를 복사하고 동기화하느라, NPU를 쓰는 이득이 통째로 사라진다. 경계가 다섯 군데만 생겨도 그냥 CPU로 다 돌리는 것보다 느려지는 일이 흔하다.

막히는 자리는 대개 정해져 있다.

잘 도는 것 자주 막히는 것
합성곱, 완전연결, 표준 활성화 함수 커스텀 연산자, 프레임워크 밖에서 짠 층
고정된 입력 크기 동적 shape — 길이가 매번 달라지는 입력
배치 정규화, 풀링 조건 분기, 반복문이 그래프에 들어간 구조
INT8 정수 연산 부동소수점만으로 되는 연산, 높은 정밀도 누적

동적 shape가 특히 아프다. 언어 모델은 시퀀스 길이가 매번 다르고, NPU는 그것을 잘 못 다룬다. 그래서 실제 배포에서는 길이를 몇 개의 고정값으로 나눠 각각 컴파일해 두고 입력에 맞는 것을 고르는 식으로 우회한다. 컴파일 산출물이 다시 몇 배로 늘어난다.

그래서 모델을 고르는 시점부터 이 제약을 반영해야 한다. 학습을 다 마치고 배포 단계에서 컴파일러를 처음 돌려 보면, 그때 나오는 답이 "이 구조는 안 됩니다"일 수 있다. 순서를 뒤집어서, 목표 칩의 컴파일러로 빈 모델 껍데기부터 먼저 통과시켜 보고 학습에 들어가는 것이 맞다.

양자화가 선택이 아니다

GPU에서 양자화는 최적화다. 안 해도 돌아가고, 하면 빨라진다.

NPU에서는 대부분 전제조건이다. 상당수의 NPU가 INT8 정수 연산 위주로 설계돼 있어서, 부동소수점 모델은 아예 안 올라가거나 성능이 안 나온다. 그래서 파이프라인이 이렇게 된다.

  1. 부동소수점으로 학습을 마친다
  2. 실제 입력 분포를 대표하는 보정 데이터(calibration data) 수백 건을 모델에 통과시켜, 층마다 값이 어느 범위에 있는지를 잰다
  3. 그 범위로 스케일을 정해 INT8로 변환한다
  4. 정확도가 떨어졌으면 문제 층을 찾아 정밀도를 되돌리거나, 양자화를 고려한 재학습을 한다

2번의 보정 데이터가 조용한 함정이다. 개발자가 편의상 학습 데이터 앞에서 몇백 건을 잘라 쓰는 경우가 많은데, 그것이 실제 현장 입력과 분포가 다르면 스케일이 어긋난다. 공장 조명 아래에서 찍힌 이미지로 돌아갈 모델을 밝은 스튜디오 샘플로 보정하면, 실측 정확도가 검증셋보다 눈에 띄게 낮게 나오고 원인이 잘 안 보인다. 보정 데이터는 배포될 현장에서 뽑는다.

그리고 NPU마다 양자화 방식의 세부가 다르다. 층 단위로 스케일 하나를 쓰는 칩과 채널 단위로 나눠 쓰는 칩이 있고, 대칭 양자화만 지원하는 칩도 있다. 채널 단위를 못 쓰면 채널마다 값 범위가 크게 다른 층에서 정확도가 무너진다. 이런 것은 스펙 시트에 잘 안 적혀 있어서 작은 모델로 먼저 재 보는 수밖에 없다.

NPU가 실제로 이기는 자리

벤치마크를 짧게 돌리면 NPU가 모바일 GPU보다 느리게 나오는 일이 많다. 그래서 "NPU 별거 없네"라는 결론으로 가기 쉬운데, 재는 구간이 잘못됐다.

연속 부하에서 NPU와 모바일 GPU의 처리량 변화

NPU의 장점은 최고 속도가 아니라 전력당 성능이고, 그것이 드러나는 곳은 지속 부하다. 모바일 GPU는 처음엔 빠르지만 발열이 쌓이면 클럭을 내리고, 10분쯤 지나면 처음의 절반 아래로 떨어진다. NPU는 시작이 낮은 대신 발열이 적어 거의 안 깎인다.

그래서 판단 기준이 이렇게 갈린다.

  • 간헐적으로 한 번씩 도는 추론 — 사진 한 장 분류, 가끔 부르는 요약. GPU나 CPU로 충분하고 NPU 도입 비용이 안 맞는다
  • 계속 도는 추론 — 카메라 프리뷰의 실시간 검출, 상시 켜진 음성 인식, 24시간 도는 검사 장비. 여기가 NPU 자리다
  • 배터리로 도는 기기 — 전력이 곧 사용 시간이면 다른 선택지가 없다
  • 팬이 없는 밀폐 기기 — 열을 뺄 방법이 없으므로 발열이 적은 경로가 유일한 답이다

두 번째와 네 번째가 겹치는 산업용 카메라 박스 같은 자리가 NPU의 전형적인 제자리다.

도구가 벤더마다 갈린다

여기가 GPU 세계와 가장 다른 부분이다. CUDA 하나로 대부분을 덮는 GPU와 달리, NPU는 통합된 표준이 사실상 없다.

계열 주로 쓰는 도구 어디에 있는가
퀄컴 QNN / SNPE 안드로이드 휴대폰, 스냅드래곤 기기
애플 Core ML 아이폰, 맥
인텔 OpenVINO AI PC, 산업용 x86
엔비디아 TensorRT Jetson 계열 엣지 보드
안드로이드 공통 NNAPI 계열 위임 여러 칩을 한 코드로 — 대신 최적화가 얕다

마지막 줄이 절충안이다. 안드로이드의 공통 경로를 쓰면 코드 하나로 여러 칩을 덮을 수 있지만, 벤더 SDK를 직접 쓸 때보다 성능이 덜 나오고 최신 기능이 늦게 들어온다. 기기 종류가 많으면 공통 경로로 시작하고, 주력 기기 한두 종만 벤더 SDK로 따로 최적화하는 것이 현실적인 형태다.

그리고 어느 쪽이든 CPU 폴백 경로를 함께 둔다. NPU 초기화가 실패하는 기기가 반드시 나온다 — 드라이버 버전이 낮거나, 칩은 있는데 제조사가 접근을 막아 뒀거나, 다른 앱이 이미 점유하고 있는 경우다. 이때 앱이 기능을 통째로 잃으면 안 된다.

프로젝트를 시작하는 순서

이 분야는 순서를 틀리면 되돌리는 비용이 크다. 실제로 통하는 순서는 이렇다.

  1. 목표 기기를 먼저 확정한다. 칩이 정해져야 도구가 정해지고, 도구가 정해져야 제약이 보인다
  2. 그 칩의 컴파일러로 모델 구조를 먼저 통과시킨다. 학습 전에, 무작위 가중치로도 된다. 여기서 막히면 구조를 바꾼다
  3. INT8 전제로 학습을 설계한다. 양자화를 고려한 학습을 처음부터 넣으면 나중에 정확도를 되찾느라 도는 일이 없다
  4. 보정 데이터를 현장에서 모은다. 학습 데이터를 재활용하지 않는다
  5. 실기기에서 지속 부하로 잰다. 짧은 벤치마크는 발열 이후를 못 보여 준다
  6. NPU·CPU 어느 쪽으로 돌았는지를 기록에 남긴다. 이 값이 없으면 현장에서 느리다는 제보가 왔을 때 원인을 못 좁힌다

정리

  • NPU는 미리 컴파일한 바이너리만 실행한다. 기기에서 모델을 갈아 끼우는 경로가 없고, 배포가 앱 업데이트 절차를 탄다
  • 실제 병목은 연산자 지원이다. 커스텀 층과 동적 shape에서 막히고, CPU로 쪼개져 돌면 NPU를 쓰는 이득이 사라진다
  • 양자화는 선택이 아니라 전제다. 보정 데이터는 반드시 배포될 현장의 분포에서 뽑는다
  • NPU가 이기는 것은 최고 속도가 아니라 지속 성능과 전력당 성능이다. 짧은 벤치마크만 재면 판단이 뒤집힌다
  • 도구가 벤더마다 갈린다. 기기 종류가 많으면 공통 경로로 시작하고 주력 기기만 벤더 SDK로 따로 다듬는다
  • 어느 경로로 돌았는지를 남기고, CPU 폴백을 반드시 함께 둔다

읽어주셔서 감사합니다. 😊

LATEST

모델 운영의 최신 글

모델 운영2026.09.04

KV 캐시 양자화 — 가중치보다 이쪽이 먼저 넘친다

긴 문맥에서 GPU 메모리를 실제로 잡아먹는 것은 가중치가 아니라 KV 캐시입니다. 캐시 크기를 계산하는 법, K와 V를 다르게 다뤄야 하는 이유, 어디까지 줄여도 되는지를 정리합니다.

16 MIN
모델 운영2026.09.04

양자화 보정 — 데이터 128개가 모델 품질을 정한다

양자화에서 스케일을 정하는 절차가 보정입니다. 무엇을 재는지, 데이터를 어디서 몇 개 뽑아야 하는지, 자르는 지점을 어떻게 고르는지, 그리고 잘못된 보정이 어떤 모양으로 드러나는지를 정리합니다.

21 MIN
모델 운영2026.09.03

과제 특화 증류 — 큰 모델의 답을 작은 모델에 옮긴다

범용 성능이 아니라 우리 과제 하나만 잘하는 작은 모델을 만드는 방법입니다. 교사에게 무엇을 받아야 하는지, 데이터를 어떻게 모으고 거르는지, 손익 분기가 어디인지를 정리합니다.

15 MIN