Skip to content

Repository files navigation

Soundlog 모델 서비스

좌표 + 온보딩(여행 상태·무드) → 추천 곡 10 + 배경 사진 1을 돌려주는 FastAPI 서비스. 2026 관광데이터 활용 공모전 출품작의 ML 컴포넌트다.

POST /recommend {x, y, state, mood}
        │
        ▼
  poi_select.pick_poi ──── 자체 카탈로그 512곳에서 대표 POI 1개 (API 0회)
        │                  거리 0.45 · 타입 0.20 · 대표성 0.15 재랭킹
        │                  + 별칭/부모 collapse (근정전 → 경복궁)
        ├─ 없음(5km 밖) → tour_api.nearby_poi (관광공사가 메움)
        │                  └ 그래도 없음 → 곡: 무드 100% · 사진: null
        │  둘 중 어느 쪽이든 contentid 있으면 detailCommon2로 개요 보강
        ▼
  ┌─ 곡 ──────────────────────────┐   ┌─ 사진 ────────────────────────┐
  │ soundlog_place.decide          │   │ photo_recommend.pick_photo    │
  │  └ 지원 축일 때만 POI α=0.3    │   │  ① POI 대표 사진(firstimage)  │
  │ KURE 임베딩 → 출처 매칭        │   │  ② 갤러리 5,043장 임베딩 매칭 │
  │ → 독점 상한 그리디 → 곡 10     │   │     (장소>시군구>시도 게이트) │
  └────────────────────────────────┘   │  ③ null → 프론트 그라데이션   │
                                       └───────────────────────────────┘
        ▼
  {tracks, backgroundImageUrl, poi, meta}     ← 응답 계약은 API.md

POST /photo {x, y}                          ← 리캡 배경 전용
        │
        └─ 위 흐름의 왼쪽(곡)을 건너뛰고 POI→사진만 탄다.
           같은 좌표면 /recommend가 주는 배경과 같은 사진이 나온다 —
           poi_payload()를 공유해서 둘이 갈라지지 않는다.
           내보내는 URL은 항상 https (iOS ATS 차단 회피).

곡과 사진이 같은 POI를 본다 — 배경은 해수욕장인데 곡은 옆 음식점 기준이 되는 어긋남을 없애기 위해서다. 관광공사 TourAPI는 더 이상 부르지 않는다(대표 장소를 구조적으로 누락해서 자체 카탈로그로 피벗 — recommend.py 상단 주석에 근거).

폴더 지도

경로 역할
main.py FastAPI 서버. 응답 조립(곡+사진), 헬스 체크
recommend.py 곡 추천 파이프라인. POI→축 판정→α블렌드→매칭→상한 그리디
tour_api.py 요청 시점 관광공사 — 카탈로그 밖 좌표 폴백 + POI 개요 보강
embedder.py KURE 단일 로드 지점. 곡·사진이 같은 모델 인스턴스를 공유
poi_select.py 좌표→대표 POI. 카탈로그·별칭/부모 맵. 요청당 ~1ms
photo_recommend.py POI→배경 사진. firstimage→갤러리 3단계 게이트→null
soundlog_place.py 장소 축 8종 어휘·지원 판정(decide). 부족 축은 무드만
soundlog_catalog.py 서빙 DB 로더. excluded/canon/🎤접두사 교정 + 임베딩 캐시
soundlog.db 서빙 DB (v3: 출처 695 · 곡 5,095행 · 고유 곡 3,149 · 채널 25)
data/poi_catalog_v3.csv POI 카탈로그 805행(좌표 확보 512곳 로드)
data/photos_ready.csv 관광공사 갤러리 사진 5,043장 메타 (죽은 URL 667장 제거 후)
data/photo_embs.npy 위 사진의 KURE 임베딩 (5043×1024, ~20MB)
tests/ test_swap.py(회귀 86검사) · try_poi.py · smoke_api.py
tools/ build_serve_tables.py · make_deploy_zip.py · make_fixture_db.py
rollback/ 이전 서빙 DB. rollback_originals/엔 통합 전 py 3종

실행

:: 로컬 (Windows)
.venv\Scripts\activate
uvicorn main:app --port 8000
# 서버 (Ubuntu / Naver Cloud)
nohup .venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000 >> server.log 2>&1 &

첫 기동은 KURE 로드 + 임베딩 캐시 확인으로 수십 초 걸린다. 로그에 준비 완료 — 요청 받을 수 있음이 찍히면 준비된 것이다.

기동이 곧 자기 진단이다 — 로그에 카탈로그 교정 내역(0건이어도), 장소 축 지원표(4/8), POI 카탈로그·별칭·부모 수가 전부 찍힌다. 침묵하는 단계가 없으니, 로그에 없는 건 안 돈 것이다.

배포가 제대로 됐는지 30초 확인

curl http://127.0.0.1:8000/
필드 기대값 다르면
catalog.uniqueTracks 3,149 다른 DB를 물고 있음 (.envML_CATALOG_DB)
catalog.channels 25
catalog.corrections.* 0 아님 build_serve_tables.py 안 돌린 DB
poi.count 512 data/poi_catalog_v3.csv 누락/미배치
photos.count 5,043 data/photos_ready.csv 누락
tourApi.enabled true false면 .envTOUR_API_KEY가 없음
photos.embMatched true false면 기동 시 임베딩 재계산됨(수 분) — npy 행 불일치

poiCache 섹션이 보이면 구버전 main.py다.

환경 변수 (.env)

변수 기본
ML_CATALOG_DB soundlog_serve.db(파일 옆) 서빙 DB 경로. 이 폴더는 soundlog.db 사용
ML_ALPHA 0.3 곡 쿼리에서 POI 비중 (지원 축일 때만)
ML_AXIS_MIN_TRACKS / ML_AXIS_MIN_SOURCES 40 / 10 축 지원 문턱
ML_PER_VIDEO_CAP / ML_PER_ARTIST_CAP 3 / 2 곡 독점 상한
ML_POSITION_DECAY 0.01 같은 출처 내 수록순 미세 감쇠
ML_MAX_SEQ_LEN 256 KURE 입력 길이
ML_EMBED_MODEL nlpai-lab/KURE-v1 임베딩 모델
ML_EXCLUDE_CHANNELS (빈값) 채널 제외(부분일치, 쉼표)
TOUR_API_KEY (없음) 관광공사 키. 없으면 폴백·보강이 조용히 꺼진다
ML_TOUR_API 1 TourAPI 전체 스위치
ML_TOUR_FALLBACK / ML_TOUR_DETAIL 1 / 1 폴백·보강 개별 스위치
ML_TOUR_BUDGET 2.0 호출당 예산(초). urllib timeout은 소켓 단위라 상한이 아니다
ML_TOUR_DETAIL_BLOCKING 0 1이면 개요를 응답에서 기다린다(실측 최대 5.9초 — 권장 안 함)
ML_TOUR_OVERVIEW_QUERY 0 1이면 개요를 쿼리 벡터에까지 투입. 실측상 광안리 곡이 나빠지고 응답이 160→1,200ms — 권장 안 함. 축 탐지는 이 값과 무관하게 항상 개요를 쓴다

사라진 변수ML_POI_BUDGET/CACHE_TTL/CACHE_MAX/GRID/TYPE_WEIGHT/ DIST_HALF. POI 선택이 카탈로그로 옮겨가 요청당 호출 예산이 필요 없어졌다 (TourAPI 예산은 ML_TOUR_BUDGET이 따로 관리한다). 사진 쪽 상수(PHOTO_ALPHA=0.7, 게이트 문턱)는 env가 아니라 photo_recommend.py 상단에 근거와 함께 있다.

더 읽을 것

  • TESTING.md — 기능별 테스트 명령·기대 출력·트러블슈팅
  • API.md/recommend 응답 계약. 백엔드에 이 파일만 전달하면 된다
  • 설계 근거는 각 파일 상단 주석에 그 자리에서 남겼다 (왜 TourAPI를 버렸나 → recommend.py, 왜 부족 축은 안 섞나 → soundlog_place.py, 왜 POI 선택에 임베딩을 안 쓰나 → poi_select.py, 사진 게이트가 왜 3단계인가 → photo_recommend.py)

About

SoundLog 위치·무드 기반 음악 및 장소 이미지 추천 서비스

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages