Look Up: 스마트폰으로 구름을 읽고, 휴대폰을 내려놓으라고 알려주는 오픈 웨이트 모델
요약
이 프로젝트는 오픈 웨이트 비전 모델을 활용하여 스마트폰으로 구름 관측 웹 애플리케이션 'Look Up'을 개발했습니다. 사용자는 휴대폰 카메라로 하늘 사진을 찍으면, 온디바이스에서 클라우드 유형과 의미를 파악하고 다음 관찰 포인트를 안내받습니다. 이후에는 기기를 내려놓고 실제 자연 현상(구름의 변화)에 집중하도록 유도하는 것이 핵심입니다.
핵심 포인트
- 오픈 웨이트 비전 모델을 활용한 온디바이스 구름 관측 앱 구현
- CLIP ViT-B/16 등 브라우저 기반 AI 모델 실행 기술 적용
- 단순 정보 제공을 넘어, 사용자가 자연 현상에 집중하도록 유도하는 경험 설계
이 글은 Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass에 제출하는 내용입니다.
제가 만든 것
Look Up은 구름 관측을 위한 작은 웹 애플리케이션입니다.
사용자가 휴대폰을 하늘로 향하게 하여 사진 한 장을 찍습니다. 휴대폰 자체에서 실행되는 오픈 웨이트 비전 모델이 사용자가 보고 있는 10가지 구름 유형 중 어느 것인지, 그 구름이 보통 다음 몇 시간 동안 무엇을 의미하는지, 그리고 주의 깊게 살펴볼 작은 포인트를 알려줍니다. 그런 다음 휴대폰을 내려놓으라고 요청합니다.
마지막 부분이 핵심입니다. 구름의 가장 좋은 점은 _움직인다_는 것입니다: 적운(cumulus)은 5분 만에 커지거나 증발하고, 비행운(contrail)은 사라지거나 퍼지며, 권운(halo)은 태양 주위에 나타납니다. 이는 화면을 보는 것을 멈춰야만 볼 수 있습니다. 따라서 모든 관측은 하나의 임무로 끝납니다:
적운 (Cumulus): 구름 하나를 골라 5분 동안 지켜보세요. 위로 자라고 있나요, 아니면 가장자리가 해지며 증발하고 있나요?
권운 (Cirrus): 가닥 중 하나를 골라 그 꼬리가 어느 방향으로 빗질되는지 관찰하세요. 그것이 수 킬로미터 상공의 바람 방향입니다.
**휴대폰을 내려놓기(Put the phone away)**를 누릅니다. 화면은 검게 변하고 돌아올 때까지 시간을 카운트하며 (휴대폰 잠금 설정 가능합니다). 사용자가 돌아오면, 구름이 어떤 변화를 주었는지(
👉 https://look-up-sky-smoky.vercel.app
휴대폰으로 열고, Wi-Fi에서 한 번 **'신호 없음 준비(Get ready for no signal)'**를 누르면 (모델 다운로드, 약 90MB), 밖에 나가서 **'하늘 읽기(Read the sky)'**를 누르세요. 계정 가입이나 설치가 필요 없습니다 (원한다면 홈 화면에 추가할 수 있습니다).
코드
GitHub logo Kamelyoul / look-up
Look Up — 휴대폰으로 하늘을 비추면, 구름을 배우고, 휴대폰을 내려놓으세요. 온디바이스 CLIP, 오프라인 작동.
Look Up
휴대폰으로 하늘을 비추세요. 오픈 웨이트 모델이 사용자의 휴대폰에서 구름의 이름을 알려주고, 일반적으로 어떤 의미인지 설명하며, 관찰할 만한 작은 것을 제시합니다. 그리고는 휴대폰을 내려놓습니다.
DEV Hacktoberfest Open-Source AI Challenge, Week 1: Touch Grass (2026년 10월 5일~11일)을 위해 제작되었습니다. 이 프로젝트와 리포지토리는 해당 챌린지 기간 동안 만들어졌습니다.
기능
- 하늘 읽기(Read the sky): 하늘 사진을 찍거나 (또는 이미 찍은 사진을 선택합니다).
- CLIP ViT-B/16 (오픈 웨이트, MIT)이 Transformers.js와 ONNX Runtime Web을 통해 브라우저 내에서 실행됩니다. 이 모델은 사진을 10개의 WMO 구름 장르, 그리고 비행운(contrail), 맑은 하늘(clear sky), _하늘이 아닌 것(not the sky)_으로 제로샷 분류합니다.
- 사용자는 자신의 눈으로 무엇을 확인해야 하는지, 해당 구름이 다음 몇 시간 동안 일반적으로 어떤 의미인지 (헤지드 날씨 전설), 그리고 임무가 담긴 카드를 받게 됩니다...
프레임워크나 백엔드 없이 순수 TypeScript와 Vite로 제작되었습니다. MIT 라이선스입니다. npm test는 34개의 단위 테스트를 실행하며, npm run eval은 아래의 정확도 수치를 재현합니다.
구축 방법
모델: CLIP, 제로샷, 브라우저 내에서
핵심은 오픈 웨이트(open-weight) 이미지-텍스트 모델인 OpenAI의 CLIP ViT-B/16입니다. 저는 이를 ONNX Runtime Web (WebAssembly) 위에서 Transformers.js를 사용하여, 8비트로 Xenova의 ONNX export로 구동합니다.
CLIP은 본래 구름을 분류하도록 학습되지 않았습니다. 그럴 필요가 없습니다. 이 모델은 이미지와 문장을 같은 공간에 매핑하기 때문에, 저는 각 클래스를 일반 영어로 설명하고 가장 가까운 설명을 선택합니다. 13개 클래스(10개의 WMO 구름 속성와 contrail, clear sky, not the sky 포함) 각각은 다음과 같은 3~6개의 프롬프트를 받습니다:
prompts: [
'a photo of cumulus clouds',
'fluffy white cotton-ball cumulus clouds with flat bases in a blue sky',
...
설명적인 프롬프트가 중요합니다. CLIP은
- 최댓값(Max) 사용, 평균값(Mean) 아님. '하늘이 아닌 것(not the sky)' 클래스는 매우 다른 요소들(잔디, 방, 화면, 거리 등)을 혼합합니다. 이들을 평균 내면 아무것도 잘 맞지 않는 흐릿한 '평균 비-하늘'이 나오게 됩니다. 따라서 각 클래스는 가장 잘 맞는 프롬프트로 점수를 얻습니다. 제가 테스트한 세트에서, 이는 평균값을 사용했을 때 18개였던 것과 달리, 24개의 비-하늘 사진 중 22개를 거부했습니다. 이 기능을 발끝에 겨누면 하늘을 올려다보라고 알려줍니다.
- 확신하지 못해도 괜찮습니다. 만약 최고 클래스의 확률이 45% 미만이라면, 카드에는 "Altocumulus일 수도 있고 Cirrocumulus일 수도 있어요 — 고민되네요" 라고 표시됩니다. 실제 하늘은 여러 속(genera)을 한 번에 포함하는 경우가 많으며, 저는 앱이 확신하지 못한다고 말해주는 것이 거짓말하는 것보다 낫다고 생각합니다.
- 폭풍우는 특별 취급을 받습니다. 만약 'cumulonimbus'가 최고 추측이거나, 차순위로 나오거나, 단순히 10%를 초과하면, 카드에 빨간 줄이 추가됩니다: "이것은 천둥번개를 동반한 구름일 수도 있습니다. 높고 어둡거나 천둥소리가 들린다면, 실내로 들어가세요."
실제로 작동했을까요? 제가 측정해봤습니다.
제로샷(Zero-shot) 데모는 쉽게 골라볼 수 있기 때문에, 저는 Wikimedia Commons에서 작고 재현 가능한 테스트 세트를 구축했습니다. 각 구름 카테고리별로 파일 이름이 해당 속만을 명시하는 상위 12개 JPEG 사진 (총 132장의 하늘 사진), 그리고 앱이 거부해야 할 잔디밭과 거실 사진 24장입니다. 평가 스크립트는 브라우저가 실제로 사용하는 정확한 코드 경로를 사용합니다: 동일한 가중치, 동일한 프롬프트, 동일한 점수 산정 방식.
| 지표 (CLIP ViT-B/16, 8비트) | 결과 |
|---|---|
| Top-1 정확도, 13개 클래스 (기대치 ≈ 7.7%) | 43% (57/132) |
| ... | |
| 그것은 특징적인 하늘(비행운, 적운, 고적운 '양떼')에서는 뛰어납니다. 하지만 미묘한 구름에는 솔직히 좋지 않습니다: 권운(cirrostratus) (2/12)과 층적운(stratocumulus) (1/12)은 대부분 다른 것으로 나옵니다. 이것이 앱이 최고 추측을 보여주고 모든 것을 "스스로 눈으로 확인해 보세요"라고 표현하는 이유입니다. 오라클이라기보다는, 보게끔 유도하는 야외 안내서에 가깝습니다. (주의: Commons의 레이블은 크라우드소싱되었으며, 많은 사진들이 한 가지 속 이상을 포함하고 있습니다.) |
모델 선택: 수치로 결정하다. 저는 더 작은 패치의 형제 모델인 CLIP ViT-B/32도 실행해 보았습니다. 이 모델은 두 배 빠르고 자신감이 있을 때(84% 대 69%) 더 정확했습니다. 하지만 B/16이 9개를 잡아낸 12장의 천둥번개 사진 중 5개만 포착했고, 오경보도 더 많이 발생시켰습니다(16대 10). 사람들에게 실내로 가라고 알려주는 앱이라면 이 점으로 결정되었습니다. 저는 B/16을 채택하고 추가되는 약 200ms를 감수했습니다. 전체 이미지별 표는 리포지토리(eval/)에 있습니다.
나머지 기능
- 오프라인(Offline): 작은 서비스 워커가 앱 셸을 캐시합니다. Transformers.js는 가중치와 WASM 런타임을 브라우저의 Cache Storage에 저장하므로, 첫 로드 후에는 비행기 모드에서도 작동합니다.
- '휴대폰 내려놓기(Phone away)' 화면: 검은색이며 큰 카운터만 있고 다른 것은 없습니다. 시간은 타임스탬프를 기준으로 측정되므로, 휴대폰을 잠가도 계속 카운트됩니다.
- 하늘 기록(Sky log):
localStorage만 사용합니다. 아무것도 기기를 벗어나지 않습니다. - 테스트: 34개의 단위 테스트(Vitest)가 점수 산정 및 base64 임베딩 형식을 다룹니다. 누군가 프롬프트를 수정해도 임베딩을 재생성하지 않으면 테스트가 실패합니다. 또 다른 테스트는 천둥번개 카드가 결코
3. 실행하는 데 비용이 들지 않기 때문에 무료로 유지할 수 있습니다. API 키도, 요청당 청구액도, 속도 제한도 없습니다. 전체가 정적 사이트입니다. 만약 천 명이 화창한 토요일에 사용하더라도 제 비용은 변하지 않습니다.
네 번째이자 더 개인적인 이유가 있습니다: 제가 열어보고 수정할 수 있다는 것입니다. 모델이 공개되어 있기 때문에, 제가 그것을 두 개로 분할하여 하나는 빌드 시간에 실행하고 휴대폰이 필요한 절반만 배포할 수 있습니다. 폐쇄된 엔드포인트로는 그렇게 할 수 없습니다. 클래스들은 TypeScript 파일 안의 문장일 뿐입니다. _mammatus_나 _noctilucent clouds_를 추가하고 싶으신가요? 한 줄을 추가하고, npm run embeddings를 실행한 다음 재배포하기만 하면 됩니다. 재학습이나 권한이 필요하지 않습니다.
폐쇄형 모델이 유리할 수 있는 경우: 대규모 호스팅 멀티모달 모델은 어려운 쌍(altocumulus vs. cirrocumulus)에서 더 정확할 가능성이 높습니다. 하지만 신호가 필요하고, 사진을 보고, 조회당 비용이 발생합니다. 화면에서 벗어나 Wi-Fi가 없는 곳으로 나가게 하는 것이 목표인 앱에게는 휴대할 수 있는 작은 모델이 사용할 수 없는 큰 모델보다 낫습니다.
나의 에이전트 세션
솔직히 말씀드리자면: 저는 AI 코딩 에이전트(Claude Code)를 사용하여 이것을 구축했습니다. 이 에이전트가 대부분의 코드, 테스트 및 평가 스크립트를 작성했고, 이 게시글 초안 작성도 도왔습니다. 위에 있는 모든 숫자는 제가 말한 것이 아니라 재실행할 수 있는 리포지토리 내의 스크립트(npm test, npm run eval)에서 나온 것입니다. 저는 세션 녹화본을 가지고 있지 않습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기