
CLIP을 사용하여 '데스크 사진을 AI가 100점 만점으로 채점'하는 Web 툴을 Hugging Face 무료 플랜으로 제작
요약
CLIP 모델을 활용하여 데스크 사진을 8개 항목으로 채점하는 웹 툴 제작 과정을 소개합니다. Hugging Face 무료 플랜과 FastAPI를 사용하여 별도의 학습 없이 Zero-shot 방식으로 구현하는 방법과 기술적 팁을 다룹니다.
핵심 포인트
- CLIP의 Zero-shot 능력을 활용한 상대적 유사도 기반 채점 방식
- pos/neg 텍스트 쌍과 Softmax를 이용한 점수 산출 로직
- Hugging Face Spaces(Docker SDK)와 FastAPI를 활용한 무료 배포
- Numpy 버전 고정 및 모델 프리로드를 통한 실행 안정성 및 속도 최적화
「나의 재택근무 데스크 환경, 객관적으로 보면 몇 점일까?」 —— 이 의문에 답하기 위해, 데스크 사진을 한 장 업로드하면 AI가 의자, 모니터, 조명 등 8개 항목을 100점 만점으로 채점하는 Web 툴을 만들었습니다.
👉 실제로 작동하는 것: https://thedeskovery.com/desk-score/
이 기사에서는 학습 없는 (Zero-shot) CLIP으로 '그럴듯한 채점'을 어떻게 실현했는지, 그리고 Hugging Face의 무료 플랜만으로 완결시킨 구성과 주의할 점을 공유합니다. 「이미지를 대략적으로 분류하고 싶다」거나 「무료로 AI 추론 API를 공개하고 싶다」는 분들에게 참고가 되길 바랍니다.
[브라우저] 사진 업로드
│ fetch (multipart/form-data)
▼
...
- 프론트엔드: WordPress 고정 페이지에 순수 JavaScript (채점 UI + 결과 표시)
- 백엔드:
Hugging Face Spaces의 Docker SDK (무료 · CPU)에 FastAPI를 탑재 - 모델:
CLIP (Zero-shot). 학습 데이터도 파인튜닝 (Fine-tuning)도 없음
CLIP은 이미지와 텍스트를 동일한 공간에 임베딩(Embedding)할 수 있으므로, '쌍이 되는 상태'의 텍스트를 준비하여 사진이 어느 쪽에 더 가까운지를 측정하는 것만으로도 '그럴듯한 채점'이 가능합니다.
예를 들어 '의자' 카테고리라면 다음과 같습니다:
CATS = [
{"key":"chair", "label":"의자", "weight":20,
"pos":"a high-end ergonomic office chair with a high backrest and armrests",
...
각 카테리아에서 pos (좋은 상태)와 neg (나쁜 상태)의 두 문장을 준비하고, 이미지와의 유사도를 비교 → softmax를 통해 'pos에 가까운 정도'를 0~100점으로 변환합니다.
import torch
from transformers import CLIPModel, CLIPProcessor
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32").eval()
...
포인트는 세 가지입니다:
- pos/neg의 2지선다로 구성하여 softmax 적용: 절대적인 유사도는 환경 차이가 크기 때문에, '좋음/나쁨 중 어느 쪽에 더 가까운가'라는 상대적 비교로 설정하면 안정적입니다.
- 스케일링 (Scaling): 그대로 두면 차이가 작아 전부 50점 근처에 몰리므로,
*100으로 스케일링하여 날카롭게 만듭니다. - 5~98 사이로 클리핑 (Clipping): 0점/100점으로 완전히 치우치는 것은 체감상 맞지 않으므로 가볍게 다듬습니다.
'엄격한 스코어링'이 아니라, '대략적인 기준'으로서 납득할 수 있는 점수를 반환하는 것이 목표입니다. 가중 평균을 통해 종합 점수 + S/A/B/C/D 랭크, 그리고 가장 점수가 낮은 카테고리(=개선 여지가 있는 부분)도 함께 반환합니다.
from fastapi import FastAPI, UploadFile, File
from fastapi.middleware.cors import CORSMiddleware
from PIL import Image
...
CORS는 전체 허용(브라우저에서 직접 호출하기 때문)으로 설정했습니다. startup 시점에 모델을 프리로드(Preload)해 두면, 무료 플랜이 슬립(Sleep) 상태에서 복귀한 후의 첫 채점 속도가 체감상 빨라집니다.
이 부분이 가장 시간을 많이 잡아먹었습니다. requirements.txt를 그대로 작성하면 numpy 2.x가 설치되어, 텐서(Tensor) 생성 관련하여 실행 시 에러가 발생합니다. 해결 방법은 **numpy를 1.x 버전으로 고정(Pin)**하는 것입니다:
numpy==1.26.4 ← 이것을 첫 번째 줄에
fastapi==0.110.0
uvicorn[standard]==0.29.0
...
빌드 시점에 모델을 미리 받아두면 기동이 안정적입니다.
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
...
README.md의 프론트매터(Frontmatter)에서 sdk: docker / app_port: 7860을 지정하면 됩니다. torch를 매 빌드마다 다시 설치하므로 빌드에는 5~10분 정도 소요됩니다.
일정 시간 액세스가 없으면 슬립(Sleep) 상태로 전환되며, 첫 요청 시 콜드 스타트(Cold Start, 컨테이너 기동 + 모델 로드)가 발생합니다. 이를 감안하여 프론트엔드에서 "AI 구동 중..." 메시지를 띄우거나, 페이지를 여는 순간 헬스 체크(Health Check)를 호출하여 워밍업(Warm-up)을 수행함으로써 사용자 경험을 보완하고 있습니다.
- 깔끔한 듀얼 모니터 + 좋은 의자가 있는 데스크 → 높은 점수 (S~A 랭크)
- 노트북만 있는 경우 · 물건이 많은 책상 → 낮은 점수, D 랭크에 가까움
- "가장 개선할 여지(伸びしろ)"
도 대체로 타당함 (어두운 사진 → 조명, 케이블이 가득한 경우 → 배선 정리)
조명이나 촬영 각도에 따라 점수는 변동될 수 있지만, "현재 내 데스크의 상태를 대략적으로 파악하는" 용도로는 충분히 잘 맞습니다. 추가 학습 없이 무료로 이 정도 성능을 내는 것은 CLIP의 강력함 덕분입니다.
- CLIP의 제로샷 (Zero-shot) + **pos/neg의 소프트맥스 (Softmax)**를 통해, 학습 없이도 "납득할 수 있는 채점"이 가능
- **Hugging Face 무료 플랜 (Docker Space)**에서 AI 추론 API를 공개할 수 있음 (numpy 버전 고정 주의)
- 엄밀함보다는 "대략적인 기준 + 다음 단계"를 제시하는 설계로 만들면 사용자 경험이 좋아짐
같은 발상으로 "방 정리 상태", "코디의 OO도"와 같은 가벼운 채점 툴은 다양하게 만들 수 있을 것 같습니다. 괜찮으시다면 여러분의 데스크에서 직접 테스트해 보세요 👇
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기