Skip to main content

피트 워든 — TinyML의 실용적 응용 사례

피트는 임베디드 디바이스용 머신러닝 전반을 다룹니다. 라즈베리 파이에서 신경망을 실행하는 것부터 웨이크 워드와 산업용 모니터링까지 폭넓게 이야기합니다. 본 문서는 AI 번역본입니다. 오역이 의심되는 부분은 댓글로 알려주세요.
Created on September 15|Last edited on September 15


에피소드 소개

Pete는 모바일 및 임베디드 디바이스용 딥러닝을 담당하는 TensorFlow Micro 팀의 테크니컬 리드입니다.
루카스와 피트는 라즈베리 파이를 해킹해 AlexNet을 실행했던 경험, 임베디드 디바이스의 전력·크기 제약, 그리고 모델 크기를 줄이는 기법들에 대해 이야기합니다. 피트는 또한 TensorFlow Lite Micro의 실제 적용 사례를 설명하고, TensorFlow 초기부터 참여하며 겪은 경험을 공유합니다.

피트와 연결하기

듣기



타임스탬프

0:00 소개
1:23 신경망을 실행하기 위해 Raspberry Pi 해킹하기
13:50 모델과 하드웨어 아키텍처
18:56 매직 완드 학습하기
21:47 Raspberry Pi vs Arduino
27:51 모델 크기 줄이기
33:29 엣지에서의 학습
39:47 TensorFlow에서 일하는 것은 어떤가요?
47:45 데이터셋 개선과 모델 배포 개선
53:05 마무리

링크

YouTube에서 시청하기



대화록

참고: 대화록은 서드파티 서비스에서 제공되어 일부 부정확한 내용이 포함될 수 있습니다. 수정 사항이 있다면 angelica@wandb.com 으로 보내 주세요. 감사합니다!

소개

피트:
제가 봤을 때 ML 제품 배포에 정말 성공한 팀들은, 형식적이든 비형식적이든 전체를 책임지는 역할을 맡은 사람이 있고, 어셈블리의 내부 루프를 작성하는 엔지니어가 모델을 만드는 사람들과 나란히 협업하도록 구성되어 있습니다.
루카스:
당신은 현실 세계의 머신러닝을 다루는 프로그램 Gradient Dissent를 듣고 계십니다. 진행은 제가 맡은 루카스 비발드입니다. 오늘은 해커이자 블로거로 잘 알려진 피트 워든과의 대화입니다. 그는 여러 업적 중에서도 초기 모바일 머신러닝 앱 회사인 Jetpac을 창업했고, 그 회사는 2014년에 Google에 인수되었습니다. 이후로 TensorFlow 팀에서 테크 리드이자 스태프 엔지니어로 일해 왔고, TensorFlow의 시작부터 함께해 왔습니다. 또한 ML 모델을 임베디드 디바이스, 즉 Arduino부터 Raspberry Pi까지 올려 동작하게 만드는 방법에 관한 책도 썼습니다. 저도 그 주제에 큰 열정을 가지고 있어서, 오늘은 이 부분을 깊이 파고들며 기술적인 내용까지 자세히 다뤘습니다. 분명 흥미롭게 들으실 거라 생각합니다. 간단한 안내 말씀을 드리면, 이번 대화에서는 오디오에 몇 가지 글리치가 있었는데 전적으로 제 책임입니다. 가족과 함께 Big Sur로 여행 중이라 즐겁긴 했지만, 보시다시피 팟캐스트 장비를 모두 챙기지 못했습니다. 혹시 잘 들리지 않는 부분이 있다면, 노트에 제공된 대화록을 확인해 주세요.

신경망을 실행하도록 Raspberry Pi를 해킹하기

루카스:
좋아요, 피트. 묻고 싶은 게 정말 많지만, 제 프로그램이니까 제가 청취자라면 가장 먼저 물을 법한 질문부터 시작할게요. 예전에 Raspberry Pi의 GPU를 해킹해서 신경망을 학습시켰던 그때 이야기를 다시 들려주세요.
피트:
세상에. 네, 정말 재미있었죠. 초기 Raspberry Pi가 처음 나왔을 때 GPU가 있긴 했지만, 기본적으로는 그림을 그리는 것 말고는 쓸모 있게 쓸 수 없는 GPU였어요. 그런데 GPU로 그냥 그림만 그리고 싶진 않잖아요? 그 무렵 리버스 엔지니어링이 조금씩 진행되고 있었고, 하드웨어 쪽의 괴짜 엔지니어들이 저수준에서 Raspberry Pi의 GPU를 어떻게 프로그래밍하는지 설명한 매뉴얼을 손에 넣는 데 성공했더라고요. 이건 제가 예전에 Apple에 있을 때부터 줄곧 미치도록 갈망하던 거였어요. 그동안은 GLSL 같은 비교적 고수준 언어로 GPU를 프로그래밍할 수 있었지만, 저는 늘 설계자가 의도하지 않은 일을 시키고 싶었거든요. 예를 들어 Apple에 있을 때는 정통 그래픽스가 아니라 이미지 처리 작업을 하도록 만들고 싶었어요. 그리고—지금 배경에서 강아지 소리가 들릴 수도 있습니다. 새로 데려온 강아지, 너트메그예요—저는 항상 그걸 마음대로 프로그래밍할 수 있길 바랐습니다. 접근만 가능하다면 어셈블리 수준에서 프로그래밍할 수 있다는 걸 알고 있었거든요. Apple에서 5년을 보내며 ATI와 NVIDIA를 설득해 그 접근 권한을 달라고 했고, 실제로 그들을 설득하는 데까지는 성공했어요. 하지만 Apple의 드라이버 팀이 “안 됩니다. 접근 권한을 주면 그가 하는 별난 작업까지 우리가 지원해야 하니까요”라며 막아버렸죠. 그래서 Raspberry Pi가 등장했을 때—
루카스:
이게 Raspberry Pi 1이었나요, 아니면 2나 3이었나요?
피트:
그때는 Raspberry Pi 1 시절이었어요. 출시된 지 얼마 안 됐을 때였고, GPU에 대한 데이터시트를 실제로 제공했는데, 거기에는 GPU 내부의 기묘한 작은 하드웨어 블록들을 프로그래밍하기 위한 명령어 포맷이 설명되어 있었죠. 어셈블러 같은 건 사실상 전혀 없었습니다. 일반적으로 쓸 수 있을 거라 기대하는 도구가 기본적으로 아무것도 없었어요. 있는 것이라곤 “이게 바로 기계어 명령들이다” 같은 원시 정보뿐이었죠. 특히 그 시절, 그러니까 Raspberry Pi 1 시절에는 ARMv6를 쓰고 있었기 때문에 Raspberry Pi에 SIMD 명령어조차 사실상 없었습니다.
루카스:
SIMD 명령어가 무엇인가요?
피트:
아, 죄송해요. Single Instruction Multiple Data요. x86에 익숙하시다면 SSE나 AVX 같은 걸 떠올리시면 됩니다. 본질적으로 “32개의 숫자 배열이 있으니 전부 곱해라”를 루프 없이 한 번의 명령으로 지정하는 방식이에요. 32번의 명령을 하나씩 도는 대신요. 그래픽이든, 우리 사례처럼 머신러닝이든 숫자 계산이 많은 작업을 가속하는 데 아주 유용하죠. 저는 멋진 이미지 인식 데모를 꼭 만들고 싶었어요. AlexNet이 한창 유행이었을 때부터, Raspberry Pi에서 프레임당 30초보다 짧게 AlexNet을 돌리고 싶었거든요. ARMv6는 정말… 마치 브로드컴에 너무 오래돼서 팔리지 않는 칩이 잔뜩 쌓여 있던 것 같았어요. 공식적인 얘기는 아니고, 사실인지 모르지만 그렇게 느껴졌죠. 그래서 “그래, 우리가 생각 중인 이 Raspberry Pi라는 것에 그냥 써버리자” 같은 분위기였던 것 같아요. 워낙 오래돼서 컴파일러 지원을 찾는 것조차 힘들었고, 기대하는 현대적 최적화도 없었죠. 하지만 GPU라면 제가 원하는 걸 할 수 있겠다는 확신이 있었어요. 그래서 데이터시트를 파고들었고, 몇몇 분들이 이 부분을 오픈 소스로 해킹해둔 게 있어서 프로젝트를 포크해 참고할 수 있었습니다. 재미있게도, Raspberry Pi의 일부 창립자들도 이 주제에 꽤 관심이 있었어요. 결국 저는 꾸역꾸역 해킹을 이어가며 행렬 곱셈을 구현하는 방법을 알아냈습니다. 그리고 이걸 아주 좋아하던 사람 중 한 명이 바로 Raspberry Pi의 창립자 에반 업턴이었어요. 제가 막히면 포럼에 도움 요청을 올렸는데, 실제로 답을 해 준 몇 안 되는 사람 중 한 명이 그였죠. 어쨌든, 그렇게 GPU로 행렬 곱셈을 돌릴 수 있게 되면서 AlexNet을 실제로 실행해 고양이인지 개인지를 30초가 아니라 2초 만에 인식할 수 있었습니다. 수년에 걸쳐 가장 즐거웠던 작업 중 하나였어요. 정말로 끈끈이테이프와 철망으로 이것저것 얽어매며 꾸려나가는 느낌이었거든요. 정말 신났습니다.
루카스:
그게 대체 어떻게 동작하죠? 어셈블리를 직접 써서 GPU에서 돌린다니요. 어떤 환경에서 그걸 작성한 건가요?
피트:
그래서 거의 텍스트 에디터만 쓰고 있었어요. 어셈블리 프로젝트를 해둔 사람들이 몇 있었는데, 어느 것도 제대로 동작하지 않거나 제가 필요한 용도에는 맞지 않았죠. 그래서 결국 이것저것 붙여서 해킹하듯이 엮었습니다. 그다음에 텍스트를 어셈블러에 넣어 원시 형태의 커맨드 스트림을 뽑아냈어요. 그리고 이 프로그램을 업로드하려고 Raspberry Pi의 CPU에서 어느 메모리 주소에 써야 하는지 찾아내야 했죠. 그러면 그 프로그램이, 아마도 실행할 수 있는 명령어 수가 말도 안 되게 작았던 걸로 기억하는데, 한 64개나 128개 정도였던 것 같아요, 그 안에 올라가 있게 됩니다. 프로그램은 아마 4개나 8개 코어 전부에 올라가 있었고요. 그런 다음 그 코어들을 시작시키고, 메모리도 거기서부터 흘려 넣어야 했죠. 솔직히 말해 소프트웨어 공학 관점에서는 완전 재앙이었어요. 그래도 동작하긴 했습니다.
루카스:
음, 어떤 디버깅 메시지를 받나요? 그러니까, 대학 시절에 이런 걸 작성하던 때를 떠올려 보면, 잘못된 게 있으면 컴퓨터가 그냥 크래시 나곤 했던 것 같거든요…
피트:
음, 저는 실제로 픽셀에 값을 써서, 픽셀 색을 보고 프로그램이 어디까지 진행됐는지 판단했어요. 그런데… 저는 색약이라 그게 별 도움이 안 됐죠. 어쨌든 정말 바닥까지 파고들어가는 작업이었어요. 며칠, 아니 몇 주씩 아주 희귀한 기술적 문제에 파묻혀 시간을 잊어버릴 수 있는 그런 종류의 일이었거든요.
루카스:
그러니까, 저도 비슷한 프로젝트를 해본 입장에서 묻자면, 어떻게 하면 끝까지 가서 정말 동작하는 결과를 낼 수 있다는 희망을 유지할 수 있었나요? 그런 종류의 일을 하는 데 있어서 그게 저에겐 가장 어려운 부분인 것 같거든요.
피트:
음, 그때 저는 스타트업에서 일하고 있었는데, 이게 스타트업에서 겪고 있던 다른 문제들보다 훨씬 다루기 쉬운 문제처럼 보였어요. 그래서 여러모로 말하자면, 더 안 좋은 문제들을 미루기 위한 일종의 미루기였죠.
루카스:
정말 훌륭한 답변이네요.
피트:
맞아요.
루카스:
라즈베리 파이에 직접 접근할 수 없게 해 둔 GPU를 포함한 이유가 무엇이었나요? 스트리밍 비디오 같은 용도를 위한 것이었나요?
피트:
네, 그건 정말로 2000년대 초반의 셋톱박스 같은 용도를 염두에 두고 설계된 거였어요. 몇 개 삼각형을 그릴 수는 있었지만, 어떤 것도… 셰이더를 돌리도록 설계된 게 아니었죠. 그래서 그 당시에는 GLSL 같은 것들은 애초에 고려 대상이 아니었어요. 이후에는 더 최신 버전과 GPU에서 그런 작업이 조금 진행되었을 거라고 생각하지만, Raspberry Pi 1 시절로 돌아가 보면, 그냥 삼각형 몇 개 그릴 수 있는 정도였고, 그게 전부였죠.
루카스:
그 뒤로도 계속 Raspberry Pi 동향을 지켜보고 계셨나요? Raspberry Pi 4에 대해서는 어떻게 생각하시나요? 거기에 무엇을 넣을지에 관해 혹시 당신과 논의한 적이 있었나요?
피트:
아니요, 아니요. 저는 평범한 사용자가 아니라는 걸 그들도 더 잘 알고 있었다고 생각해요. 일반적인 개발자 입장에서는 정말 훌륭하죠. Raspberry Pi 4는 멀티스레딩에 제가 언급했던 SIMD 명령까지 갖춘 괴물 같은 머신이니까요. GPU에는 GLSL과 최신 OpenGL 기능들도 지원되는 것으로 알아요. 그런데 해커 입장에서는 “아…” 하는 느낌이 들어요.
루카스:
그게 재미있는 게, 제가 당신을 처음 만난 게 아마 Raspberry Pi 3에서 TensorFlow를 돌리려고 하던 때였던 것 같아요. 말 그대로 컴파일하고 필요한 라이브러리를 제대로 링크하려고 한 거였죠. 그런데 완전히 막혔던 기억이 나요. 이런 얘기 드리기 좀 부끄럽지만, 포럼에 도움을 요청했더니 “와, TensorFlow 기술 지원이 믿을 수 없을 만큼 좋다. 내 질문에 다 답해 주네.”라고 감탄했었죠.
피트:
음, 제 생각에 결국… 제 이메일 주소도 찾으셨죠. 저한테 이메일도 보내셨고, 또 제가 해야 할 일을 미루고 있던 딱 그 타이밍에 연락을 주신 것 같아요. 그래서 “와, 이게 훨씬 더 재밌네. 이거에 시간을 좀 써야겠다.” 했던 기억이 나요. 그런데요, TensorFlow의 의존성이 정말 많다는 점을 과소평가하면 안 됩니다. 파이썬 기반의 클라우드 서버 프로젝트라면 꽤 정상적인 일이에요. 그 환경에서는 사실상 공짜나 다름없으니까요. 그냥 “pip install” 같은 걸로 설치하면 그냥 동작하죠. 하지만 기대하는 바닐라 x86 리눅스 환경이 아닌 곳으로 옮겨가는 순간, 갑자기 그 대가를 치러야 합니다. “이건 어디서 온 거지?” 같은 것들을 일일이 파악해야 하거든요.

모델과 하드웨어 아키텍처

루카스:
맞아요, 맞아요. 그래서 제 머릿속에 떠오르는 질문이 하나 있어요. 이게 당신이 답하기에 적절한 질문인지 잘 모르겠지만, 의견을 듣고 싶어요. 구글을 제외하면 거의 모든 사람들이 NVIDIA GPU에서 모델을 학습시키잖아요. 그 이유가 본질적으로 컴파일을 담당하는 CUDA 라이브러리와, ML 컴포넌트를 저수준 언어로 작성해서 NVIDIA 칩에 컴파일하도록 해주는 cuDNN 때문이라고 들었어요. 그런데 피트 워든이 문서도 거의 공개하지 않는 칩에서조차 직접 행렬 곱 코드를 써서 돌릴 수 있다면, 이 간극은 어디서 생기는 걸까요? 왜 컴파일 대상이 되는 칩이 더 다양하게 쓰이지 않을까요? 왜 TensorFlow는 더 다양한 아키텍처 위에서 더 잘 작동하지 않을까요? 그게… 제 생각엔 그게 TensorFlow의 초기 설계 목표 중 하나였던 걸로 아는데, 2016년, 2017년에 기대했던 만큼 다양한 GPU 아키텍처의 폭발적인 등장은 보지 못한 것 같거든요.
피트:
그렇죠. TensorFlow 자체 경험을 아주 구체적으로 말하긴 어렵지만, 일반적으로 제가 개인적으로 봐 온 걸 말씀드리면, 문제는 연구자들이에요. 계속해서 새로운 기법과 더 나은 학습 방법을 내놓거든요. 보통 벌어지는 일의 패턴은 알렉스 크리제브스키와 동료들이 AlexNet에서 했던 모델을 그대로 따라갑니다. 제가 딥러닝에 처음 빠졌을 때 정말 놀랐던 건, 알렉스가 코드를 공개했을 뿐만 아니라, 고수준 모델을 만드는 일만 한 게 아니라 GPU에서 충분히 빠르게 돌리려고 CUDA 커널을 엄청 해킹해가며 최적화했다는 점이었어요. 이게 매우 흥미로운 지점인데, 최첨단 머신러닝의 고수준 개념들을 이해하는 동시에, 루프 안에서 사실상 어셈블리에 가깝게, 정확히 그 수준까지는 아니더라도 인트린식 단위로 매 사이클을 곱씹는 사고를 해야 했다는 거죠. 대체로 일어나는 일은, 새로운 기법이 등장하면 연구자들이 자기 실험을 돌리기 위해 가능한 한 빠르게 실행돼야 하니까 직접 구현을 합니다. 기본값이 CUDA죠. 그래서 새로운 기법은 결국 CUDA 구현으로 먼저 나오게 돼요. 보통 C++로 된 CPU 구현도 있는데, 최적화가 잘됐을 수도 있고 아닐 수도 있습니다. 하지만 CUDA 구현은 거의 반드시 있어요. 그러면 그 기법이 주목을 받기 시작하면, 나머지 세계는 연구 목적에 맞게 연구자가 쓴 훌륭한 코드를 다양한 정밀도와 서로 다른 시스템으로 포팅하는 방법을 찾아야 하죠. 이렇게 새로 나오는 기법들을 모든 아키텍처에서 돌아가게 만들기 위해 사람들이 해야 하는 숨은 작업이 엄청나게 많습니다. 이건 생태계 전반에 해당하는 얘기예요. 그래서 실험을 할 때, 만약 라즈베리 파이 같은 폼팩터를 쓰되 전력 10W 정도는 감당할 수 있다면, Jetson이나 Jetson Nano 같은 걸 잡는 걸 정말 좋아합니다. 그러면 사실상 데스크톱 머신에서 쓰는 것과 같은 GPU를 훨씬 작은 폼팩터로 쓸 수 있으니까요.
루카스:
정말요. 네. 라즈베리 파이에 NVIDIA 칩이 없다는 게 조금 아쉬워요.
피트:
방열판만으로도...
루카스:
한 가지 느낀 점은… 당신의 책이 정말 훌륭하더군요, 임베디드 ML에 관한. 사실 제가 다른 인터뷰 중이었는데 — 피트 스코모로크와의 인터뷰 클립을 가져와야겠네요 — 우리 둘 다 책상 위에 당신 책을 올려놓고 있었어요. 둘 다 읽고 있었던 거죠. 그분을 아시는지는 모르겠지만—
피트:
네, 저도 잘… 맞아요, 피트는 정말 대단하죠. 놀라운 일을 많이 하고 있어요. 가끔 제가 미루고 있을 때 저를 붙잡아 주는 사람이기도 하고, 저도 조언을 해 주고 서로 주고받곤 합니다.
루카스:
우리도 동네에서 하나 만들어야겠어요…
피트:
그렇죠. 미루기의 기술, 미루기 목록까지 해킹하기.

매직 완드 훈련하기

루카스:
집에서나 개인적인 용도로 흥미로운 프로젝트를 많이 하시는 것 같아요. 개인적으로 해 보신 ML 해킹 프로젝트가 있다면 몇 가지 소개해 주실 수 있을까요?
피트:
오, 그건 정말… 저는 매직 완드를 제대로 작동하게 만드는 데 완전히 집착하고 있어요.
루카스:
자세히 알려 주세요.
피트:
제가 종종 보게 되는 것 중 하나는 업계 전문가들이 안드로이드 폰, 일반적인 스마트폰용으로 만든 애플리케이션들입니다. 가속도계를 이용한 제스처 인식은 이런 폰에서 정말 잘 동작해요. 상용 환경에서 사람들이 아주 잘 구현해냈거든요. 그런데 오픈 소스 쪽에서 실제로 잘 동작하는 예시는 많이 보지 못했습니다. TensorFlow Lite Micro에 포함해 배포하는 예제조차 충분하지 않습니다. 개념 증명 수준일 뿐이고, 제가 원하는 만큼 잘 작동하지 않아요. 그래서 제가 계속 돌아오는 주된 사이드 프로젝트 중 하나가 이런 겁니다. “좋아, 그러면 내가 실제로 제트 문자(Zoro의 Z) 같은 걸 그리거나 — 여기 책상에 작은 Arduino가 있는데 — 그걸 들고 휘두르면 인식하게 만들려면 어떻게 하지…” TV 화면을 향해 그 동작을 해서 채널을 바꾸거나 그런 걸 해 보고 싶어요. 제가 정말로 하고 싶었던 건 — 이 중 일부는 Google I/O에서 공개했어서 링크를 공유할게요. 나중에 설명에 넣어도 좋고요 — 최종 목표가 뭐냐 하면, 이 장치들이 실제로 Bluetooth를 갖고 있으니 키보드나 마우스, 게임패드 컨트롤러를 에뮬레이션하고, 심지어 MIDI 키보드까지도 가능하게 해서 사용자가 마음대로 커스터마이즈할 수 있게 만드는 겁니다. 그래서 어떤 제스처를 하면… 예를 들어 “Z”를 그리면 가상 키보드에서 Z 키를 누르는 걸로 인식되게 하고, 그러면 그 장치가 연결된 대상에서 재미있는 동작이 일어나도록 하는 거죠. 아직은 완벽히 동작하지 않습니다. 하지만… 바라건대 본업에서 충분히 어려운 문제를 만나 피하고 싶어질 때, 그 핑계로 이 프로젝트에 더 시간을 쓰게 되길 기대하고 있어요.

Raspberry Pi 대 Arduino

루카스:
저도 정말 그러길 바랍니다. 임베디드 컴퓨팅에 익숙하지 않은 분들을 위해, Raspberry Pi와 Arduino의 차이를 설명해 주실 수 있을까요? 그리고 Raspberry Pi에서 ML을 실행하는 것과 Arduino에서 실행하는 것 사이에는 어떤 다른 과제가 있는지도 알려 주세요.
피트:
가장 큰 차이는 메모리 용량이에요. 이 Arduino Nano BLE Sense 33는… 제 기억으로 RAM이 256KB이고, 플래시는 512KB 정도의 읽기 전용 메모리를 갖고 있습니다. 실제로 실행해야 하는 환경이 정말, 정말 작죠. 그래서 운영체제에서 기대할 법한 파일 시스템이나 printf 같은 것들이 거의 없다는 뜻이기도 합니다. 실제로는 바이트 하나하나까지 신경 써야 해요. printf 함수 자체도… 많은 구현에서 단지 printf를 포함했다는 이유만으로 코드 크기가 약 25KB나 늘어납니다. 왜냐하면 printf는 본질적으로 거대한 switch 문이라서 “퍼센트 F가 있나요? 그럼 float을 이렇게 출력합니다” 같은 식으로 수백 가지의 변형을 처리하거든요. 우리가 평소에는 생각도 하지 않는, 상상 가능한 모든 출력 형식을 위한 코드가 들어가야 하고, 시스템에 printf가 있으면 그 모든 코드가 포함됩니다. 우리가 목표로 하는 이런 장치들은 대개 프로그램을 쓸 수 있는 공간이 수백 KB에 불과합니다. 눈치채셨겠지만, 저는 최신 것을 예전 환경—예를 들면 Commodore 64 같은—에 쏙 들어가게 만드는 걸 정말 좋아합니다.
루카스:
피트 워든은 꼭 실용적인 이유가 있어야만 무언가를 하는 사람은 아닌 듯한데, 그렇다면 Arduino와 Raspberry Pi 사이에서 각각을 선택할 실용적인 이유는 무엇일까요?
피트:
다행히 제 취미를 그럴듯하게 정당화해서 전업 프로젝트로 바꿀 수 있었습니다. 이걸 어디에 쓰는지 좋은 예를 들어보면… 제 휴대폰을 한번 볼게요. 휴대폰이 어떻게 생겼는지는 다 아시죠. 예를 들어 — 단어 전체를 말하면 사람들 휴대폰이 반응하니 그대로 말하진 않겠습니다 — OK-G 같은 웨이크 워드나, 애플이나 아마존의 웨이크 워드를 떠올려 보세요. 음성 인터페이스를 쓸 때는, 그 단어를 말하면 휴대폰이 깨어나길 바라죠. 그런데 알고 보면, 그 단어를 항상 듣고 있게 하려고 메인 ARM 애플리케이션 프로세서를 24시간 켜둘 여유가 없습니다. 배터리가 그냥 바닥나 버리거든요. 메인 CPU는 웹을 보거나 기기를 조작할 때 켜져 있으면 대략 1W 정도의 전력을 씁니다. 그래서 다들 대신 하는 방법이 있습니다. 흔히 “항상 켜짐” 허브나 칩, 센서 허브 같은 걸 두고, 메인 CPU는 전원을 내려서 전력을 쓰지 않게 합니다. 대신 훨씬 제한적이지만 매우 저전력인 칩이 실제로 동작하면서 마이크를 듣고, 대략 30KB 규모의 아주 작은 ML 모델을 돌려 “지금 내가 들어야 할 그 웨이크 워드를 누가 말했나?”를 판별합니다. 여기에도 똑같은 제약이 있습니다. 많아야 수백 KB의 ���모리뿐이고, 꽤 저사양 프로세서에서 돌아가며, 운영체제도 없어서 바이트 하나가 아쉬운 환경이죠. 그래서 라이브러리를 가능한 한 작게 쥐어짜야 합니다. 이게 바로 TensorFlow Lite Micro를 실제로 활용하는 대표적인 사례 중 하나입니다. 조금 더 일반적으로 보면, Raspberry Pi는… 아마 25달러 정도일 겁니다. 그에 상응하는 — Raspberry Pi Foundation이 작년이나 올초에 내놓은 — Arduino에 가까운 보드가 Pico입니다. 이건 소매가가 3달러 정도였던 걸로 기억해요. Raspberry Pi는 다시 말해 전력을 1~2W 정도 쓰기 때문에 하루 내내 돌리려면 사실상 휴대폰 배터리 하나를 써서 하루에 다 소모하게 됩니다. 반면 Pico는 100mW, 즉 0.1W만 씁니다. 같은 배터리로 10배 더 오래 돌릴 수 있고, 훨씬 작은 배터리로도 운용할 수 있죠. 이런 임베디드 기기는 대개 전력 제약이나 비용 제약, 심지어 폼팩터 제약이 있는 곳에서 쓰입니다. 이 보드는 Raspberry Pi Zero보다도 더 작아서 어디에든 붙일 수 있고, 차에 밟혀도 버틸 만큼 튼튼한 식의 용도에도 잘 맞습니다.

모델 크기 줄이기

루카스:
예를 들어 음성 인식 시스템을 놓고 보면, 클라우드나 대형 데스크톱 서버에 배포하는 경우와 Raspberry Pi에 배포하는 경우, 그리고 Arduino에 배포하는 경우를 비교했을 때, 학습과 배포를 각각 어떻게 다르게 생각해야 하는지 설명해 주실 수 있나요?
피트:
맞아요. 다시 핵심은 크기와, 이런 시스템에서 실제로 쓸 수 있는 공간이 얼마나 되느냐입니다. 항상 “모델을 얼마나 더 작게 만들 수 있을까?”를 고민하게 되죠. 예를 들어 음성 인식 같은 작업을 하려면 모델을 수십 KB 수준으로 줄여야 하고, 실제로 저희 예제 중에는 20KB짜리 모델을 쓰는 것도 있습니다. 이렇게 극저전력 기기에 모델을 얹기 위해서는 정확도와 여러 요소를 상당 부분 희생해야 합니다. 그래도 바람직하게는, 여전히 쓸모 있을 만큼 충분한 정확도를 유지하는 것이 목표입니다.
루카스:
맞아요. 그걸 어떻게 하죠? 정확도를 크게 떨어뜨리지 않으면서 크기를 줄이는 방법이 뭔가요? 몇 가지 기법을 설명해 주실 수 있나요?
피트:
사실 제가 방금 블로그에 쓴 내용이기도 한데, 문헌에서는 생각보다 많이 못 본 트릭이 하나 있어요. 고전적인 AlexNet 방식으로 돌아가 보면, 이미지 인식 네트워크에서 컨볼루션을 수행한 뒤에 종종 풀링 단계를 둡니다. 그 풀링 단계는 보통 average pooling이나 max pooling을 하죠. 이 단계는 컨볼루션의 출력을 받아서 처리하는데, 컨볼루션 출력은 보통 입력과 공간 해상도는 같지만 채널 수가 훨씬 많습니다. 그리고 2×2 블록을 잡아서 그중 최대값만 취하겠다고 하는 식이죠. 즉, 값 4개를 받아 값 1개를 출력하거나, 같은 방식으로 평균을 내서 1개로 줄입니다. 이건 정확도에 도움이 됩니다. 하지만 컨볼루션에서 매우 큰 출력을 만들어내기 때문에, 컨볼루션의 입력을 보관해야 할 뿐 아니라 출력도 보관해야 해서 RAM이 많이 필요합니다. 출력은 공간 크기가 입력과 같고 채널은 더 많기 때문에 메모리 요구량이 더 커지죠. 이 대신 업계에서 흔히 쓰는 기법은 컨볼루션에 stride 2를 사용하는 것입니다. 컨볼루션을 할 때 슬라이딩 윈도가 매번 1픽셀씩 움직이는 대신, 가로와 세로로 2픽셀씩 건너뛰게 하는 거죠. 이렇게 하면 2×2 풀링을 컨볼루션 뒤에 둔 것과 동일한 크기, 동일한 원소 수의 출력을 얻을 수 있습니다. 그런데 연산량은 더 적게 들고, 활성 메모리도 훨씬 덜 필요하다는 장점이 있습니다.
루카스:
흥미롭네요. 저는 모델 크기라면 매개변수 크기만 생각했는데, 말씀을 들어보니 당연히 활성 메모리도 필요하군요. 그래도 그게 모델 크기와 같은 규모일 거라고는 상상하기 어려운데요. 실제로 이미지의 픽셀 데이터랑 중간 결과가 모델 자체보다 더 커질 수 있나요?
피트:
맞아요. 컨볼루션의 좋은 점이 바로 그거죠. 완전연결층에서는 거의 불가능한 방식으로 가중치를 재사용할 수 있습니다. 그래서 컨볼루션 모델에서는 활성 메모리가 꽤 큰 공간을 차지하는 경우도 생깁니다. 조금 기술적인 얘기이긴 한데, 질문에 대한 가장 명확한 답은 양자화이기도 합니다. 부동소수점 모델을 8비트로 바꾸면 메모리 사용량을 즉시 75% 줄일 수 있으니까요.
루카스:
사람들이 4비트, 심지어 1비트까지 낮추는 것도 봤어요. 그에 대해 어떻게 생각하시나요?
피트:
맞아요. 정말 흥미로운 연구들이 많이 나왔습니다. 제 동료가 — 논문 링크는 다시 보내드릴게요 — ResNet에 대해 파레토 최적 비트 깊이가 4비트 정도라는 내용을 다룬 작업을 했습니다. 4비트나 2비트까지 내리는 연구, 심지어 1비트의 이진 네트워크까지 가는 훌륭한 연구들이 여럿 있습니다. 저희 입장에서 가장 큰 과제는 CPU가 일반적으로 8비트 산술 연산 외에는 최적화되어 있지 않다는 점입니다. 이렇게 더 낮은 비트 깊이로 내려가려면 실제로 사용하는 하드웨어 측면에서의 진전이 필요합니다.

엣지에서의 학습

루카스:
엣지에서 실제로 학습을 돌리는 것에 대해 어떻게 생각하시나요? 사람들이 이 얘기를 오래전부터 해왔지만, 일부 학습을 디바이스에서 수행하고 그 결과를 상위로 올리는 실제 사례는 아직 거의 보지 못했습니다. 그런 게 가능한가요, 아니면…
피트:
제가 보기에는, 특히 임베디드 엣지 환경에서는 라벨이 달린 데이터를 확보하기가 매우 어렵습니다. 최근 자가학습에서도 훌씬 진전이 있었지만, 이미지·오디오·가속도계 인식 문제를 푸는 주력 방식은 여전히 대규모 라벨링 데이터셋을 모아 학습시키는 것입니다. 엣지에서 수집하는 데이터에 묵시적 라벨이 붙어 있지 않은 경우가 대부분이기 때문에, 학습을 정당화하기가 매우 어렵습니다. 제가 실제로 꽤 유망하다고 본 한 가지 사례는 산업 모니터링입니다. 예를 들어 기계 장비가 있고, 기계적 문제로 곧 심하게 흔들려 망가질지 알고 싶다고 합시다. 이때 장치에 부착된 가속도계나 마이크 센서를 사용합니다. 어려운 점은 정말로 곧 망가질 정도로 흔들리는 것인지, 아니면 원래 그런 진동 패턴인지 판별하는 것입니다. 이러한 예지 정비에서 유망한 접근은 처음 24시간을 “모든 것이 정상”이라고 가정하고 “이게 정상 상태다”를 학습하는 것입니다. 그리고 그 이후부터 그 범위를 벗어나는 징후만 찾는 것이죠. 즉 처음 24시간의 데이터를 “정상 데이터”로 묵시적으로 라벨링하고, 그 밖으로 벗어나는 모든 변위를 탐지하는 방식입니다. 이는 일정한 학습 방식으로도 말이 됩니다. 하지만 그 경우에도 저는 여전히 임베딩 같은 기법이나, 역전파 전체를 필요로 하지 않는 다른 접근을 검토하라고 권합니다. 예를 들어 특정 사람이 어떤 단어를 말하는지를 인식해야 하는 오디오 모델이 있다면, 그 모델이 N차원 임베딩 벡터를 출력하게 하고, 사용자가 그 단어를 3번 말하게 한 뒤, 그 임베딩 공간에서 이후 발화가 가까운지를 k-최근접 이웃으로 판별하게 하세요. 사용자 입장에서는 “학습이 일어난” 것처럼 보이지만, 변수 조정이나 신경망 가중치 변경 같은 거대한 장치를 돌릴 필요가 없고, 후처리 단계로 깔끔하게 해결할 수 있습니다.
루카스:
실제로 마이크로컨트롤러에 모델을 넣어 제품을 출하하는 회사들처럼, 현실 세계에서의 활용 사례를 많이 보고 계신가요?
피트:
맞아요. 이건 이야기하기가 쉽지 않은데, 이유는 이게 안드로이드 앱처럼 사람들이 비교적 공개적이거나 오픈소스인 영역이 아니기 때문입니다. 상당수는 꽤 정통의, 잘 자리 잡은 산업용 기업이나 자동차 기업 같은 곳들이죠. 그래도 우리는… 이미 내부적으로 ML을 쓰는 제품들이 여럿 시장에 나와 있는 걸 봅니다. 제가 자주 드는 예가 있는데, 2014년에 제가 구글에 합류했을 때 라지엘 알바레스라는 분을 만났습니다. 지금은 페이스북에서 비슷한 일을 하고 있는 걸로 알고 있어요. 그는 OK-G 작업의 상당 부분을 책임졌습니다. 그들은 수십억 대의 휴대폰에 이 기능을 탑재해, ML, 특히 딥러닝을 사용해 이런 종류의 인식을 수행해 왔습니다. 그런데 제가 전혀 몰랐던 건, 그들이 ML을 위해 30킬로바이트짜리 모델을 배포해 왔고, 그걸 몇 년 동안이나 계속했다는 점이었습니다. 제가 알기로는, 애플이나 다른 회사들도 음성 분야에서 오랫동안 매우 유사한 접근을 써 왔습니다. 다만 이런 영역들에서는 현대 ML 세계에서 우리가 기대하는 것처럼 결과를 널리 공개한다는 문화가 많지 않습니다. 레이더 아래로 비행하는 셈이죠. 이런 것들은… 아마 지금 이 순간에도, 여러분 집 안에서 임베디드 하드웨어 위에서 돌아가고 있는 ML 모델들이 이미 있습니다.
루카스:
오디오 인식 말고, 제 집에 있는 그런 ML 모델들은 뭐를 하고 있을까요? 대략 어떤 것들이 있는지 예를 좀 들어 주실 수 있나요?
피트:
맞아요. 가속도계로 제스처를 하는지, 혹은 기계가 기대한 대로 동작하는지 인식하는 일입니다. 세탁기나 식기세척기 같은 가전에서도 마찬가지로, 잡음이 많은 센서 신호를 받아 실제로 무슨 일이 일어나고 있는지 판단하려고 하는 것이죠.
루카스:
제 세탁기에도 ML 모델이 들어 있다고 보시나요?
피트:
전혀 놀랍지 않을 거예요.
루카스:
와.
피트:
맞아요.

TensorFlow에서 일한다는 것

루카스:
TensorFlow처럼 잘 알려진 라이브러리에서 오랫동안 일해 오신 걸 생각하면, 그동안 어떻게 진화해 왔다고 보시나요? 예상치 못했던 방향으로 나아간 점들도 있었나요? 또 그런 프로젝트에서는 앞으로 무엇을 우선순위에 둘지 어떻게 판단하시나요?
피트:
솔직히 말해 TensorFlow가 그렇게 크고 빠르게 성장한 건 정말 놀라웠어요. 지켜보는 것 자체가 대단했죠. 저는 보통 제가 흥미를 느끼는 기이한 기술 문제들을 파고들며 호기심을 따라가는 데 익숙하거든요. 실 하나를 쭉 잡아당기다 보니 결국 TensorFlow로 이어졌고요. 그런데 보기 좋았던 건… TensorFlow만이 아니라 PyTorch, MXNet 같은 다른 프레임워크들까지 포함해, 여기에 관심을 가진 사람들의 수가 폭발적으로 늘어났다는 점이었어요. 특히 실제로 출시되는 제품의 수가 폭발적으로 늘어난 게 인상적이었습니다. 사람들이 찾아낸 활용 사례의 규모와 다양성은 정말 놀라웠어요. 저는 별 10개쯤 받으면 행복한 오픈 소스 프로젝트에 익숙한데, TensorFlow와 다른 프레임워크들이 이렇게 대중적으로 채택되는 걸 보니… 네, 확실히 놀랐고 정말 기분 좋았습니다.
루카스:
그럼 기능 측면에서는 어떤가요? 그동안 어떻게 발전해 왔나요? 그런 라이브러리에는 어떤 새로운 기능이 추가되나요? 왜 그렇게 많은 하위 호환성 파괴 변경을 하시나요?
피트:
네. 그냥 사과드리고 싶습니다 [웃음정말 흥미로운 문제예요. 거의 알렉스 크리제브스키와 이야기했던 주제로 되돌아가는 느낌이거든요. 지금 우리가 있는 ML 패러다임의 전형적인 사례는, 실험하고 모델을 만들고 새로운 접근을 반복하려면 엄청난 유연성이 필요하지만, 한 모델을 시도해 보려는 것만으로도 각 실행마다 수백만 번의 반복과 수백만 개의 데이터 포인트를 흘려보내야 하니 모든 접근이 정말, 정말, 정말, 정말 빠르게 돌아가야 한다는 점이에요. 즉, 거대한 유연성이 필요하면서도 최첨단의 성능이 동시에 필요한 매우 까다로운 조합이고, 주어진 하드웨어에서 낼 수 있는 최대 처리량을 끝까지 짜내야 하죠. 그래서 결국 파이썬이 연산자나 레이어의 블록들을 호출하는 세계가 됩니다. 실제 연산 레이어 자체는 극도로 최적화되어 있지만, 그것들을 매우 임의적인 방식으로 서로 연결해도 그 높은 성능을 유지하길 기대하죠. 특히 TensorFlow에서는 여러 가속 대상 전반에 걸쳐 그걸 해내길 기대합니다. TPU, CPU, AMD, 그리고 NVIDIA GPU 같은 것들이죠. 솔직히 말하면, 정말 어려운 엔지니어링 문제예요. 제가 메인라인 TensorFlow 팀에서 물러난 지도 몇 년이 되었는데, 이걸 그냥 돌아가게 만들고 사용자에게 잘 작동하도록 하기 위해 고려해야 할 차원, 조합, 순열, 갖가지 변수의 양이 얼마나 어마어마한지 보고 깜짝 놀랐습니다. 사용자 입장에서는 우주왕복선 조종 패널처럼 복잡한 인터페이스를 마주하게 되는데, 실제로는 그중 일부만 쓰고 싶어 하죠. 그런데 모두가 원하는 ‘일부’가 서로 다르니까요—
루카스:
그렇죠, 그렇죠. 어쩌면 좀 순진한 질문일 수도 있는데, 제가 보기엔 cuDNN 라이브러리가 TensorFlow의 래퍼와 꽤 비슷해 보이거든요. 맞나요? TensorFlow가 가진 빌딩 블록을 cuDNN도 비슷하게 구현하려는 것처럼 보입니다. 그렇다면 NVIDIA 환경에서는 대부분의 정보가 그냥 cuDNN으로 내려가기만 하면 되는 거 아닌가요?
피트:
그렇죠. 제가 복잡함을 많이 느꼈던 부분은 네트워킹과 분산 처리, 그리고 정말 빠른 데이터 처리와 관련된 부분이었어요. 데이터를 이리저리 옮기는 동안 전송 병목이 생기지 않도록 하는 게 핵심이었죠. JPEG 인코딩을 손봐야 했고, 어떤 라이브러리가 더 빠른지 알아내려고 여러 가지를 시도했습니다. GPU로 충분히 빠르게 밀어 넣기 시작하면, 어느 순간 그게 병목이 되거든요. 다만 인정하자면, 그 부분은 제 전문 영역을 벗어나 있어요. 그 코드를 보면서 감탄만 했지, 거기서 문제를 직접 고쳐보려 하진 않았습니다.
루카스:
대단하네요. 같은 주제에서 한 가지만 더 여쭤볼게요. 이렇게 다양한 하드웨어 환경은 어떻게 테스트하시나요? 라이브러리를 배포하기 전에 모든 것을 실행해 볼 수 있도록 하드웨어를 어디엔가 직접 구축해야 하나요?
피트:
맞아요, 그게 또 하나의 꽤 큰 과제죠. 이렇게 다양한 하드웨어와 수많은 조합 전반에서 지속적 통합과 테스트를 수행하는 일 말이에요. “머신에 GPU 카드가 2장인가요? 4장인가요? 리눅스는 어떤 버전인가요? 윈도우에서 돌리나요? 드라이버는 어느 버전인가요? cuDNN의 가속기 버전은 무엇인가요?” 같은 모든 경우를 다 따져야 하거든요. 실제로는 이런 머신들로 이뤄진 팜을 운영하면서 가능한 한 많은 조합과 순열을 테스트해, 정말로 제대로 동작하는지 확인하려고 합니다. 상상하실 수 있듯이, 전혀 간단한 일이 아닙니다.

데이터셋 개선과 모델 배포 개선

루카스:
좋습니다. 시간이 거의 다 되어 가는데, 늘 마지막에 두 가지 질문을 드리거든요. 그 시간을 남겨 두고 싶습니다. 첫 번째 질문은, 시간이 좀 더 있다면 더 탐구해 보고 싶은, 머신러닝에서 과소평가된 주제가 무엇인지입니다.
피트:
데이터셋이요. 제가 그동안 일해 오며 경험한 공통된 교훈이 있습니다. 머신러닝으로 제품을 만드는 수백 개 팀과 함께 일해 왔는데, 거의 언제나 아키텍처나 하이퍼파라미터를 미세 조정하는 것보다 데이터셋을 개선하는 데 시간을 투자하는 편이 훨씬 더 높은 투자 대비 효과를 가져왔습니다. 그런데 실제로 데이터셋을 다루고, 개선하고, 이해하고, 수집하고, 개별 데이터 포인트를 모으고, 라벨을 정제하는 데 도움이 되는 제대로 된 도구는 거의 없습니다. 정말로 이렇게 생각합니다. 앤드류 응 같은 분들을 포함해 데이터 중심 접근법을 이야기하는 흐름이 보이기 시작했고, 그쪽에 대한 관심이 점점 커지고 있습니다. 이 흐름은 계속될 거라고 봅니다. ML 분야가 성숙해지고 더 많은 사람들이 제품을 실제로 출시해 보려는 과정을 겪으면서 “이런, 더 나은 데이터 도구가 필요하네”라는 사실을 깨닫고 있습니다. 그 결과 해당 영역에 대한 수요와 집중은 훨씬 더 커질 겁니다. 저에게도 그건 대단히 흥미로운 분야입니다.
루카스:
음, 방금 제 마지막 질문에 답을 주신 것 같기도 한데요. 그래도 여러 ML 스타트업을 하셨고 TensorFlow 작업까지 하셨으니 충분히 답해 주실 자격이 있다고 봅니다. 실제 환경에 ML 모델을 배포해 유용한 목적에 맞게 제대로 작동하게 만들 때, 가장 큰 병목은 무엇이라고 보시나요? 데이터셋이 하나라는 건 저도 동의하고, 아마 가장 큰 문제일 텐데, 그 밖에도 보시는 요인이 있나요?
피트:
그렇죠. 또 하나 큰 문제는, 연구 배경을 가진 경우가 많은 모델 개발자와 그것을 배포해야 하는 사람들 사이에 인위적인 분절이 존재한다는 점입니다. 흔히 일어나는 일은, 모델 개발자가 파이썬 환경에서 일정 수준의 정확도를 달성했다는 평가 결과까지 만들어 놓고 “좋아요, 다 됐습니다. 이 모델의 체크포인트는 여기 있어요.”라고 말한 뒤, 그걸 안드로이드 애플리케이션에 배포할 팀에 그대로 넘기는 것입니다. 문제는 애플리케이션 안의 실제 데이터가 학습 데이터와 상당히 다를 수 있다는 겁니다. 실제로 제품이 올라갈 디바이스에 최적화하려면 양자화 같은 작업이나 재학습을 수반하는 무언가를 거의 확실히 해야 합니다. 그리고 손에 쥐고 쓸 수 있는 실제 디바이스에서 시도해 보면, 단순한 평가(use case)에서는 절대 얻을 수 없는 유용한 피드백을 굉장히 많이 얻을 수 있습니다. 피트 스코모로크 얘기로 돌아가면, 제가 그를 처음 만난 건 DJ 파틸과 링크드인 팀이 아주 초기의 데이터 사이언스 작업을 하던 때였습니다. 그들은 이런 아이디어가 있었어요. 아마 데이터 사이언스라는 명칭을 만든 건 DJ였던 것 같은데, 데이터 과학자를 데이터 분석부터 모델 개발, 웹사이트에 실제로 배포하고 그 전 과정을 책임지는 풀스택의 소유자로 정의한 겁니다. 제가 봤을 때 ML 제품을 성공적으로 배포한 팀들은, 공식적이든 비공식적이든 그 전체를 책임지는 ‘뜨거운 감자’ 역할을 맡은 사람이 있었고, 어셈블리의 이너 루프를 작성하는 사람과 모델을 만드는 사람이 서로 붙어 앉아 함께 일했습니다. 앤드루 하워드와 브누아 자콥이 이끈 MobileNet, Mobile Vision 팀이 그런 훌륭한 사례였습니다. 그들은 새로운 모델 기법을 고안하는 일부터, 그것이 아주 낮은 수준의 실제 하드웨어에서 어떻게 돌아갈지를 파악하는 일까지, 모두를 정말 긴밀히 협업하며 진행했습니다. 그래서 앞으로 몇 년 동안 더 많은 사람들이 이런 모델을 채택하면서, 이 부분이 크게 달라지기를 가장 기대하고 있습니다.

아웃트로

루카스:
잘 말씀해 주셨어요. 피트, 정말 감사합니다. 정말 재미있었습니다.
피트:
네, 고마워요, 루카스.
루카스:
이 인터뷰를 재미있게 보고 더 알고 싶다면, 설명란에 있는 쇼 노트 링크를 클릭해 보세요. 언급된 모든 논문 링크, 추가 자료, 그리고 저희가 공들여 만든 전사본까지 확인하실 수 있습니다.

이 글은 AI로 번역되었습니다. 오역이 있을 수 있으니 댓글로 알려 주세요. 원문 보고서는 아래 링크에서 확인하실 수 있습니다: 원문 보고서 보기