2021년 MacBook에서 Gemma4-31B로 1년치 영상을 로컬 색인하기(50GB 스왑)

6 days ago 8
  • 영상 아카이브의 병목은 편집 도구보다 검색 불가능성이었고, 라벨 없는 클립을 영어로 질의 가능한 인덱스로 바꾸는 데 초점을 맞춤
  • 로컬 우선 설계로 각 클립 옆에 .description.md 사이드카 파일을 만들고, rating·조명·위치·전사·키워드·산문 설명을 한 번의 비전 호출에서 추출함
  • 파이프라인은 ffprobe, exiftool, Nominatim, ffmpeg, WhisperX, insightface, 비전 모델을 묶어 메타데이터·GPS·프레임·전사·얼굴 임베딩을 생성함
  • 2021년 16인치 MacBook Pro M1 Max 64GB가 LM Studio에서 Gemma 4 31B Q4를 실행했고, 대량 처리 중 스왑은 최고 50.89GB까지 올라감
  • 구조화 스키마와 enum 제약은 환각을 줄였고, 대량 색인은 로컬 31B로 처리한 뒤 어려운 10~20%만 클라우드 모델로 재평가하는 구성이 가능했음

문제의 출발점: 편집보다 검색

  • Maasai Mara에서 반년 가까이 머무는 동안 iPhone, DJI Pocket, 드론, Nikon Z8, Ray-Ban Meta로 촬영한 영상이 계속 쌓였지만, 대부분 다시 열어보지 못한 채 남아 있었음
  • Mara Hilltop의 소셜 채널은 콘텐츠 부족이 아니라 편집 시간 부족 때문에 3개월 동안 멈춰 있었음
  • Claude Code와 Opus 4.5/4.6으로 개발 작업은 장시간 에이전트 실행과 병렬 작업이 가능해졌고, KaribuKit의 첫 유료 숙소 출시와 겹치면서 영상 편집 시간은 더 줄어듦
  • 처음 떠올린 해법은 Eddie AI, Higgsfield MCP, Submagic, Buffer를 조합한 월 $140 SaaS 스택이었지만, 실제 병목과 맞지 않았음
  • 생성형 AI 영상은 실제 여행 브랜드와 맞지 않았고, 투숙객이 실제 장소를 기대하는 상황에서 잘못 표시된 AI 장면은 신뢰를 해칠 수 있었음
  • 현실적인 게시 빈도는 주 3~5개가 아니라 주 2~3개에 가까워, 초기 계획은 두 번째 주부터 실패할 가능성이 컸음
  • 이미 보유한 DaVinci Resolve Studio와 Resolve 21의 IntelliSearch, Smart Bins, Voice to Subtitle은 Eddie가 제공하는 기능의 약 70%를 커버함
  • 남은 구성은 Claude Code가 오픈소스 DaVinci Resolve MCP로 Resolve를 제어하고, 정보성 클립에 한해 ElevenLabs로 보이스오버를 처리하는 구조였으며, 비용은 월 $22로 줄어듦

진짜 병목: AI 편집기보다 먼저 필요한 인덱스

  • 시중 AI 영상 편집기는 영상이 이미 라벨링되어 있다고 가정하지만, 실제 아카이브는 IMG_*.mov, DJI_*.mp4, Mara june 2024 backup final FINAL 같은 이름으로 흩어져 있었음
  • Eddie는 전사 검색은 가능하지만, 라벨 없는 아카이브에서 “황금빛 시간대 언덕 위 코끼리” 같은 장면을 찾을 수 없었음
  • 파일명, 부모 폴더, GPS 좌표, 전사 텍스트만으로는 “프레임 안에 기린이 있는 일출 와이드샷” 같은 시각적 내용을 알 수 없음
  • 실제 지렛대는 편집기 위가 아니라 그 앞단에 있었고, 먼저 아카이브를 영어로 질의 가능한 형태로 만드는 인덱스가 필요했음

로컬 우선 인덱서 설계

  • 전체 구조는 SimbaStack에서 클라이언트용으로 만드는 AI 네이티브 빌드와 비슷했지만, 직접 클라이언트이자 엔지니어였기 때문에 의사결정이 빨랐음
  • 네 가지 제약

    • 로컬 우선이어야 했음
      • Mara Hilltop 아카이브는 물리 SSD에 있고 개인 영상은 노트북에 있었으며, 수천 개의 멀티 GB 클립을 클라우드에 올리는 것은 비용과 프라이버시 양쪽에서 맞지 않았음
    • 중앙 DB보다 사이드카 파일을 원했음
      • 각 클립 옆에 .description.md를 두고 일반 텍스트로 grep 가능하게 만듦
      • 인덱서가 나중에 망가져도 파일은 남고, 드라이브 사이를 이동해도 데이터와 함께 이동함
    • 비전 호출 한 번에 필요한 정보를 모두 뽑아야 했음
      • 추출 프레임에 대한 비전 패스가 비싼 작업이므로, 나중에 알고 싶은 정보까지 첫 호출에서 얻도록 스키마를 처음부터 넓게 잡았음
      • 포함 항목은 rating, technical quality, lighting, time of day, color palette, audio quality, people count, keywords, faces, location, transcript, prose description 등이었음
    • 세 가지 비전 백엔드를 선택할 수 있게 했음
      • 기본값은 Claude Max 구독의 CLI로, 한계 비용이 없었음
      • 속도가 필요할 때는 Anthropic API를 사용함
      • 대량 처리에는 LM Studio를 향한 로컬 백엔드를 사용했고, 이 로컬 백엔드가 핵심이었음

클립별 처리 파이프라인

  • ffprobe로 메타데이터를 읽음
  • exiftool로 GPS 위도, 경도, 고도를 읽었고, iPhone, DJI Pocket, 드론 영상에서 동일하게 동작함
  • Nominatim으로 역지오코딩을 수행했으며, 무료이고 속도 제한이 있고 API 키가 필요 없었음
  • ffmpeg로 1920px 크기의 프레임 5장을 균등 간격으로 추출함
  • WhisperX로 단어 단위 정렬과 pyannote 화자 분리를 포함한 전사를 수행했으며, Hindi, English, Swahili 등 97개 언어를 지원함
  • insightface로 얼굴을 감지하고, 나중에 아카이브 전체에서 인물 검색을 할 수 있도록 512차원 ArcFace 임베딩을 중앙 SQLite 얼굴 DB에 저장함
  • 비전 모델은 프레임, 전사 일부, 폴더 맥락을 읽고 YAML frontmatter와 산문 설명을 반환함
  • 최종 결과는 클립 옆에 .description.md 사이드카로 기록됨
  • Mara Hilltop의 실제 클립 IMG_1103.MOV는 파일명만으로 맥락을 알 수 없었지만, Gemma가 생성한 사이드카에는 사파리 텐트 설정, 실내에서 사바나로 이어지는 카메라 팬, 샷 타입, 마케팅 릴과 여행 브이로그 B-roll 같은 사용처가 포함됐음
  • 폴더 단위로는 각 클립 옆 사이드카뿐 아니라 상단의 _INDEX.json과 _INDEX.md가 생성되어 빠른 grep과 LLM 전달에 쓰였음
  • 전체 구현은 약 1,400줄 Python으로 된 Claude Code skill이며, Claude Code가 대부분 작성했고 사람의 역할은 아키텍처, 프롬프트, 스키마 설계, 버그 triage였음

오래된 MacBook에서 돌린 로컬 31B 모델

  • 2021년에 구입한 16인치 MacBook Pro M1 Max 64GB RAM은 원래 LLM용이 아니라 Chrome 탭, DaVinci Resolve, Slack, Discord, Drive를 동시에 돌리기 위한 선택이었음
  • 5년 뒤 같은 노트북이 LM Studio에서 Gemma 4 31B Q4를 띄우고 1년치 영상 아카이브를 처리함
  • LM Studio에는 28.40GB 모델이 메모리에 올라갔고, REST API는 127.0.0.1:1234에서 동작함
  • 대량 처리 중에는 64GB RAM만으로는 부족해졌고, Activity Monitor 기준 스왑 사용량이 최고 50.89GB까지 올라감
  • 이 상태는 평범한 작업일에 계속 유지할 만한 상태는 아니었지만, 주말 동안 강하게 밀어붙이는 수준에서는 허용 범위로 봤음
  • 노트북은 뜨거워지고 팬은 크게 돌았지만, 다른 작업을 하는 동안 계속 사이드카를 생성함
  • M1 Max 16인치는 5년 된 하드웨어에서도 31B 파라미터 모델을 사용 가능한 속도로 돌릴 여유를 보여줬고, 로컬 LLM이 더 효율적으로 바뀌면 앞으로 3~5년은 더 쓸 수 있을 것으로 봤음

네 가지 버그와 교훈

  • WhisperX 3.8의 화자 분리 API 변경

    • WhisperX 3.8에서 whisperx.DiarizationPipeline이 whisperx.diarize 서브모듈로 이동함
    • 생성자 인자 use_auth_token은 pyannote 3.x를 따라 token으로 바뀜
    • 해결책은 시그니처 introspection이었음
    • 스크립트가 먼저 token=을 시도하고, 생성자가 TypeError를 내면 use_auth_token=으로 폴백함
    • 빠르게 바뀌는 AI 라이브러리를 호출할 때는 방어적 생성자 호출이 저렴한 보험이 됨
  • Claude CLI가 권한 오류를 성공 응답처럼 반환

    • CLI 백엔드 첫 테스트에서 4개 사이드카가 모두 “I need permission to read the image frames...”라는 동일한 텍스트로 돌아옴
    • exit code가 0이고 출력이 비어 있지 않아 스크립트의 성공 체크를 통과함
    • 비대화형 모드에서 --permission-mode bypassPermissions가 없으면 Claude CLI는 프롬프트 대신 권한 거부 텍스트를 응답 본문으로 반환함
    • 해결책은 해당 플래그를 추가하고, “I need permission”이 들어간 짧은 응답을 설명이 아니라 오류로 처리하는 방어 체크였음
    • AI 도구를 스크립트로 다룰 때 비대화형 권한 흐름에 조용한 실패가 숨어 있음
  • Gemma가 people_count: "many"를 반환

    • 비전 프롬프트가 integer or the string "many" if >10이라고 지시했기 때문에 Gemma는 지시를 제대로 따른 셈이었음
    • 버그는 모델이 아니라 스키마 설계였음
    • 수정 후에는 0~99 정수로 추정하라고 명시하고, 기존 "many" 응답은 파서에서 강제로 변환함
    • 스키마 필드는 int 또는 특정 string처럼 union으로 만들지 말고, 항상 정수 또는 항상 문자열 중 하나로 고정해야 downstream 소비자가 단순해짐
  • 손떨림 있는 오토바이 클립이 잘못 폐기됨

    • 초기 cull 프롬프트는 사진 포트폴리오 기준에 가까워서 심한 모션 블러, 소프트 포커스, 흔들림을 cull로 평가함
    • 스페인 여행 중 찍은 야간 핸드헬드 오토바이 클립도 폐기 대상이 됐지만, 그 흐림 자체가 기억의 분위기였음
    • cull 기준을 “완벽하지 않은 촬영”이 아니라 “실제 기록이 아닌 것”으로 바꿈
    • 폐기 대상은 렌즈 캡, 주머니 속 영상, 2초짜리 테스트 클립, 완전히 날아간 노출 같은 클립으로 좁힘
    • 사진 아카이브는 공격적으로 cull하고, 영상 추억은 관대하게 cull해야 하며, 같은 스키마라도 모드를 명확히 해야 함

구조화 스키마와 로컬 모델에서 얻은 결론

  • 열거형 제약은 환각을 줄임

    • Gemma 4 E4B는 밤에 찍은 coworking-space 사진을 “brightly lit, abundant natural light, floor-to-ceiling windows”라고 묘사했지만, 창밖은 완전히 어두운 밤이었음
    • 31B에 구조화된 스키마를 주고 golden_hour | bright_daylight | overcast | dim_interior | nighttime | mixed | unclear 중 하나를 고르게 하자, thinking-off와 thinking-on 모두 nighttime을 회복함
    • 모델은 열린 산문에서는 거짓 설명을 만들 수 있지만, enum에서는 새 값을 발명할 수 없고 잘못 고를 수 있을 뿐임
    • 지시보다 스키마가 더 안전했음
  • 로컬 31B와 구조화 프롬프트는 클라우드와의 격차를 줄임

    • Gemma 4 31B Q4 thinking-off는 구조화된 스키마를 사용할 때 많은 테스트 클립에서 Sonnet 4.6과 구분하기 어려운 출력을 냄
    • 클라우드 모델의 프리미엄은 어려운 10~20% 클립에서 가치가 있었음
    • 수천 개 클립을 밤새 색인하는 대량 작업은 로컬에서 돌리고, 로컬이 review로 표시한 클립만 클라우드로 재평가하는 2단 구조가 확장 가능했음
  • AI 영상 편집기는 너무 위쪽 레이어에서 경쟁함

    • 가치 있는 레이어는 편집기가 아니라 검색 가능한 인덱스였음
    • “Mara의 핸드헬드 실내 클립, 황금빛 시간대, 사람이 있고, 8초보다 긴 것”처럼 자연어로 질의할 수 있으면 그 위의 편집기는 단순해짐
    • AI 편집기 시장은 존재하지 않는 인덱스 위의 표면 레이어를 두고 경쟁하고 있으며, 전제 조건인 인덱스를 건너뛰고 있음

다음 단계와 한계

  • 다음 작업은 Claude Code를 오케스트레이터로 쓰고, DaVinci Resolve MCP로 컷을 만들며, 정보성 클립에는 ElevenLabs 보이스오버를 붙이는 편집기를 만드는 것임
  • 보이스 클론에는 명확한 제한이 있음
    • 방향 안내, 객실 설명, 다국어 버전, 직접 말할 만한 사실 정보 같은 유틸리티 콘텐츠에만 사용함
    • 후기나 창업자 메시지에는 사용하지 않음
    • 2026년에는 공개 의무 관련 법이 현실이고, hospitality 브랜드의 신뢰는 쉽게 잃을 수 있음
  • 인덱스가 있으면 47GB DJI Pocket 영상에서 일출 와이드샷을 직접 스크러빙하는 일을 피할 수 있음
  • 현재 5년 된 노트북에서 Mara Hilltop 1년치 영상은 영어로 질의 가능해졌고, 비용은 주말 시간과 50GB 스왑이었음
  • 오래된 SSD들에 남아 있는 나머지 연도들이 다음 처리 대상임
  • Mara Hilltop의 소셜 채널은 아직 다시 살아나지 않았음
    • 인덱서는 적절한 클립을 찾는 문제만 해결함
    • 완성된 릴로 만드는 편집기가 나머지 절반이며, 성공하면 후속 글을 쓰고 실패하면 실패 이유를 다룰 예정임
  • 올바른 해답은 사람을 고용하는 것일 수도 있음
    • Mara Hilltop에 맞는 따뜻하고 관찰적인 감각을 가진 편집자를 찾는 일이 또 다른 skill을 작성하는 것보다 어려울 수 있음
    • 과도하게 잘라낸 MTV식 릴은 원하는 방향이 아님
  • 코드는 github.com/Simbastack-hq/framedex에 공개됐고, PR과 이슈를 받고 있음
Read Entire Article