-
로컬 VLM으로 가족사진 3만 장 분석하기 — 열흘간의 대장정IT 2026. 4. 3. 21:00
왜 3만 장의 사진에 AI를 붙이려 했나
가족사진은 쌓이기만 한다. 폰에서 NAS로, NAS에서 사진 관리 서버로 옮겨도 결국 "찾을 수 없는 사진"이 되어간다. "작년 여름 바다에서 찍은 사진" 같은 자연어 검색을 하려면 사진마다 설명과 태그가 있어야 하는데, 3만 장을 사람이 수동으로 분류하는 것은 불가능에 가깝다.
해결책은 VLM(Vision Language Model)이었다. 이미지를 보고 자연어로 설명을 생성하는 AI 모델을 로컬 GPU에서 돌려, 사진마다 한국어 설명·인물 수·장소 유형·장면 분류·키워드 태그를 자동으로 붙이기로 했다.
시스템 구성
구성요소 역할 Immich 사진 관리 서버 (API로 에셋 목록 조회, 썸네일 다운로드, 태그 부착) Ollama 로컬 LLM 서빙 엔진 (VLM 모델 호스팅) Qwen2.5VL VLM 모델 (이미지 → 한국어 JSON 설명 생성) GPU 스케줄러 우선순위 기반 GPU 자원 관리 (선점/복구 자동화) Python 스크립트 전체 파이프라인 오케스트레이션 파이프라인의 동작 방식은 간단하다. Immich API에서 아직 분석하지 않은 사진 목록을 가져오고, 썸네일을 다운로드해 base64로 인코딩한 뒤 Ollama의 VLM에 던진다. 모델이 한국어 JSON으로 응답하면 파싱해서 Immich에 설명과 태그를 업데이트한다.
열흘간의 타임라인
Day 1-2: 마이그레이션과 사전 분석 (3/16~17)
먼저 기존 사진 라이브러리에서 Immich로 약 24,000장을 마이그레이션했다. 이 과정에서 700여 개의 중복과 16개의 손상 파일이 걸러졌다. 초기 캐시에는 이미 약 12,000장의 분석 결과가 있었다(이전에 부분적으로 돌린 결과).
Day 6: 본격 시작, 그리고 첫 번째 위기 (3/22)
32B 모델로 본격적인 분석을 시작했다. 한 장당 약 30~40초. 16,000장이 남아 있었으니 예상 소요 시간은 약 130시간(5일 이상)이었다.
오후가 되자 첫 번째 위기가 찾아왔다. Ollama 서비스가 갑자기 응답을 멈췄다. 다른 높은 우선순위 작업이 GPU를 선점하면서 Ollama 프로세스가 종료된 것이다. 이후 약 4시간 동안 자동 재시도가 반복됐지만, Ollama가 살아나지 않아 15,535장이 연달아 실패했다.
성공: 0건, 실패: 15535건 소요 시간: 2.7분이 경험에서 교훈을 얻었다. Ollama 서비스 상태를 먼저 확인하는 헬스체크 로직이 필요했고, 서비스가 죽었을 때 무한 재시도하는 대신 빠르게 실패하고 다음 스케줄링을 기다리는 것이 나았다.
Day 7: 모델 교체 실험 — 32B vs 7B (3/23)
자정 무렵, 속도를 높이기 위해 Qwen2.5VL 7B 모델로 교체해봤다. 결과는 극적이었다.
지표 32B 모델 7B 모델 이미지당 처리 시간 30~40초 약 6초 예상 잔여 시간 130시간+ 26시간 성공률 96~97% 99.7% JSON 파싱 실패 가끔 발생 거의 없음 7B 모델은 5배 이상 빨랐고, 오히려 성공률도 높았다. 간결한 응답을 생성하는 경향이 있어 JSON 파싱 실패가 줄어든 것이다. 약 11시간 동안 쉬지 않고 돌려 5,600장 이상을 한 번에 처리했다. 이후 다시 32B로 전환해 품질과 속도의 균형을 맞췄다.
Day 7~10: GPU 선점과 자동 복구 (3/23~26)
로컬 GPU 서버에서는 VLM 분석만 도는 것이 아니다. 음성 전사 파이프라인, RAG 임베딩 등 더 높은 우선순위 작업이 들어오면 VLM은 자동으로 밀려난다. 열흘 동안 기록된 선점(preemption)은 총 6회였다.
voice-pipeline(우선순위 10) → vlm-analysis(우선순위 90) 선점 프로세스 강제 종료됨 ... Background 작업 자동 시작: vlm-analysis → vlm-analysis 시작됨 (lock 설정 완료)GPU 스케줄러가 이 과정을 자동으로 관리했다. 높은 우선순위 작업이 끝나면 VLM이 자동 재시작된다. 여기서 핵심은 중간 결과가 캐시에 저장되어 있다는 점이다. 50장마다 캐시를 디스크에 기록하기 때문에, 아무리 강제 종료되어도 최대 50장 분의 작업만 유실된다.
Day 10: 완주 (3/26)
마지막 대형 배치는 1,203장이었다. 약 6시간(371분)이 걸렸고, 성공 1,188건·실패 15건으로 마무리됐다.
캐시: 29,974개 에셋 ID 미처리: 8개 (전부 삭제된 파일 — 404 Not Found) 사실상 완료.마지막 8개는 서버에서 이미 삭제된 사진들이라 물리적으로 분석이 불가능한 항목이었다. 약 30,000장의 가족사진에 AI가 한국어 설명과 태그를 붙이는 작업이 마무리된 순간이었다.
캐시 시스템이 열쇠였다
이 프로젝트에서 가장 중요한 설계 결정은 로컬 JSON 캐시였다. 분석이 완료된 에셋 ID를 별도 파일에 저장해두면, 다음 실행 때 이미 처리된 사진을 건너뛸 수 있다.
날짜 캐시 크기 증가량 3/22 오전 11,931 — 3/22 저녁 12,392 +461 3/23 오전 15,732 +3,340 3/23 오후 18,028 +2,296 3/24 23,002 +4,974 3/25 27,656 +4,654 3/26 (최종) 29,974 +2,318 캐시 덕분에 재시작할 때마다 처음부터 다시 돌릴 필요가 없었다. 마지막 실행에서는 4,746개 미처리 에셋 중 4,738개가 캐시 히트로 스킵되어, 실제로 분석할 대상은 8장뿐이었다. 캐시 적중률 99.8%.
VLM이 생성한 결과물
모델은 각 사진에 대해 다음과 같은 구조화된 정보를 생성했다.
{ "description": "여러 가족 구성원이 야외에서 함께 서 있으며, 밝은 표정을 짓고 있다.", "people_count": 5, "location_type": "실외", "scene": "공원 나들이", "tags": ["가족", "야외", "공원", "봄"] }이 정보가 Immich에 반영되면, "공원에서 찍은 가족사진" 같은 자연어 검색이 가능해진다. 태그에는
vlm:접두어를 붙여 수동 태그와 구분했고, 분석 완료된 사진에는vlm:processed태그를 달아 중복 처리를 방지했다.숫자로 보는 열흘
지표 값 총 분석 사진 수 약 29,974장 소요 기간 10일 (3/16~3/26) 전체 성공률 98~99% 사용 모델 Qwen2.5VL 32B + 7B 이미지당 처리 시간 6초 (7B) / 30~40초 (32B) GPU 선점 횟수 6회 Ollama 장애 1회 (약 4시간) 최종 미처리 (삭제 파일) 8장 돌아보며
작은 모델의 힘. 32B 모델이 항상 정답은 아니었다. 7B 모델은 5배 빠르면서도 이 태스크에서는 오히려 안정적이었다. 태스크의 복잡도에 맞는 모델을 고르는 것이 전체 처리 시간에 결정적이었다.
캐시는 생명줄. 장기 실행 작업에서 중간 결과를 저장하지 않으면, 한 번의 장애로 모든 진행이 날아간다. 50건마다 저장하는 단순한 캐시만으로도 6번의 강제 종료를 버텨냈다.
GPU 스케줄러의 가치. 단일 GPU 환경에서 여러 AI 작업을 돌리려면 자원 관리가 필수다. 우선순위 기반의 선점·복구 메커니즘이 없었다면, 매번 수동으로 프로세스를 죽이고 다시 시작해야 했을 것이다.
3만 장의 사진이 이제 AI가 붙여준 한국어 설명과 태그를 갖게 됐다. "아이가 케이크 앞에 앉아 있는 사진", "해변에서 찍은 여름 사진" — 이런 검색이 실제로 동작하는 것을 보면, 열흘간의 대장정이 헛되지 않았음을 느낀다.
이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.
'IT' 카테고리의 다른 글
Claude Code가 플랜을 짜는 방법 - Plan Mode 내부 동작 원리 (0) 2026.04.08 axios에 악성코드가 심어졌다 - 북한 해커의 npm 공급망 공격 분석 (2) 2026.04.07 Claude Code Hooks로 AI 에이전트의 다단계 파이프라인을 결정적으로 만들기 (0) 2026.04.06 Qwen3.5-122B 양자화 비교: Q4_K_M vs Unsloth UD-Q3_K_XL 실측 (1) 2026.04.05 텔레그램 AI 어시스턴트에 기억을 심다 — 단기·에피소드·장기 메모리 설계기 (1) 2026.04.04 Context7 분석 (5) 다층 품질 스코어링 (1) 2026.04.02 Context7 분석 (4) 5단계 품질 파이프라인 (0) 2026.04.01 Context7 분석 (3) 서버 사이드 리랭킹 (0) 2026.04.01 Context7 분석 (2) 코드 스니펫 vs 정보 스니펫 (0) 2026.03.31 Context7 분석 (1) 문서 특화 청킹 전략 (1) 2026.03.31