영화 추천 시스템 연구 프로젝트다. MovieLens 32M을 주 상호작용 로그로 사용하고, Genome 2021과 ML-32M 확장 데이터를 보조 신호로 활용해 전처리, 품질 필터링, user-sequence 생성, EDA를 수행한다.
이 프로젝트의 운영 규칙, 디렉터리 정책, LLM 작업 절차는 PROJECT_GUIDE.md가 정본이다. 이 README는 빠른 실행과 사용 안내를 위한 문서다.
- 목적: 학습 가능한 영화 추천용 데이터셋을 일관된 스키마와 파이프라인으로 정리한다.
- 주 데이터:
data/ml-32m/raw/ - 보조 데이터:
data/genome_2021/,data/ml-32m-extension-main/ - 주요 산출물:
data/movies_processed.csvdata/movies_processed_drop.csvdata/ratings_drop.csvdata/ratings_drop_processed.jsonl
- 보조 산출물:
data/ml-32m/genre.csv
degent/
├── data/
│ ├── docs/ # 연구 제안서 등 참고 문서
│ ├── ml-32m/ # MovieLens 32M
│ │ ├── raw/ # 원본 CSV
│ │ ├── genre.csv # 장르 multi-hot 보조 산출물
│ │ └── readme.md # 데이터셋 설명
│ ├── ml-32m-extension-main/ # ML-32M extension
│ ├── genome_2021/ # Genome 2021
│ ├── movies_processed.csv # 영화 통합 전처리 결과
│ ├── movies_processed_drop.csv # drop 규칙 반영 영화 데이터
│ ├── ratings_drop.csv # drop 결과 반영 rating 데이터
│ └── ratings_drop_processed.jsonl # user sequence JSONL
├── schemas/ # 데이터 계약 정본
├── preprocess/ # 전처리 및 후처리 스크립트
├── replay/ # C++ rating replay event generator
├── dashboard/ # compact SQLite 기반 Streamlit replay dashboard
├── api/ # React dashboard용 read-only FastAPI backend
├── frontend/ # React + Vite replay dashboard
├── model/ # SASRec + Contrastive Loss 모델 파이프라인
├── outputs/ # 모델 산출물과 실행 로그
├── experiments/ # 로컬 실험 메타데이터 (git에는 구조 파일만 유지)
├── eda/ # raw / processed EDA
├── docs/ # 데이터 흐름, 산출물, 설계 결정 보조 문서
├── plan/ # 로컬 작업 계획서와 상태별 보관 (git에는 구조 파일과 템플릿만 유지)
├── todo.md # 현재 작업 상태와 협업 메모
├── AGENTS.md # Codex 등 LLM 작업 진입점
├── CLAUDE.md # Claude 작업 진입점
├── .cursorrules # Cursor 작업 진입점
├── .windsurfrules # Windsurf 작업 진입점
├── requirements.txt
├── PROJECT_GUIDE.md
└── README.md
원본 데이터는 git으로 추적하지 않는다. data/**/raw/ 내부 파일은 각자 로컬에 직접 배치해야 한다.
아래 파일이 data/ml-32m/raw/에 있어야 한다.
movies.csvratings.csvtags.csvlinks.csv
raw EDA를 실행하려면 Genome 2021 데이터가 data/genome_2021/ 아래에 배치되어 있어야 한다. 현재 코드 기준으로 다음 경로 중 하나를 인식한다.
data/genome_2021/raw/data/genome_2021/movie_dataset_public_final/
보조 데이터 설명은 data/ml-32m-extension-main/readme.md를 참고한다. 현재 루트 파이프라인의 필수 입력은 아니지만 연구 참고용으로 보관한다.
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt패키지 설치와 제거는 프로젝트 루트의 repo-local .venv에서만 수행한다. 시스템 Python, sudo pip, OS package manager, 전역 CUDA/toolkit 설치는 프로젝트 작업 범위에서 사용하지 않는다.
주요 의존성은 torch==2.5.1+cu121, RAPIDS/cuML 25.10.0, polars, matplotlib, numpy, PyYAML, pandas, umap-learn, hdbscan, streamlit, plotly다. GPU dependency 버저닝 결정은 docs/decisions/0003-pin-rapids-cuml-gpu-dependencies.md를 따른다.
React dashboard를 실행할 때는 Python API 의존성(api/requirements.txt)과 Node/Vite 의존성(frontend/package.json)도 별도로 설치한다.
기본 파이프라인은 아래 순서로 진행한다.
MovieLens 원본 CSV를 조인해 data/movies_processed.csv를 만든다.
python3 preprocess/preprocess_movie/preprocess_movies.py주요 처리:
title에서 연도를 분리해title,releaseYear생성genres를 JSON 배열 문자열로 변환tags.csv를 집계해tag,tagCount생성ratings.csv를 집계해ratingAvg,ratingCount생성links.csv를 조인해imdbId,tmdbId추가
함께 생성되는 검증 산출물:
preprocess/preprocess_movie/validation_report.jsonpreprocess/preprocess_movie/bad_rows.csv
학습용 영화 테이블을 만들기 위해 품질 규칙으로 row를 제거한다.
python3 preprocess/drop_movie/drop_movies.py현재 drop 기준:
ratingCount == 0genres == []releaseYear IS NULL
출력:
data/movies_processed_drop.csvpreprocess/drop_movie/validation_report.jsonpreprocess/drop_movie/bad_rows.csv
유지된 영화 집합만 남기도록 raw rating을 필터링한다.
python3 preprocess/drop_rating/drop_ratings.py출력:
data/ratings_drop.csvpreprocess/drop_rating/validation_report.jsonpreprocess/drop_rating/bad_rows.csv
ratings_drop.csv는 userId, ratedAt, movieId 기준으로 정렬되어 저장된다.
rating row를 user별 chronological history로 재구성한다.
python3 preprocess/process_rating/process_ratings_drop.py출력:
data/ratings_drop_processed.jsonlpreprocess/process_rating/validation_report.jsonpreprocess/process_rating/bad_rows.csv
JSONL 레코드 예시:
{
"userId": 123,
"ratings": [
{
"ratedAt": "2001-01-01T00:00:00Z",
"movieId": 10,
"rating": 4.0
}
],
"ratingCount": 1,
"firstRatedAt": "2001-01-01T00:00:00Z",
"lastRatedAt": "2001-01-01T00:00:00Z"
}MovieLens movies.csv의 pipe-delimited genre를 multi-hot 벡터로 변환해 data/ml-32m/genre.csv를 만든다. 현재 메인 전처리/drop/user-sequence 파이프라인의 필수 단계는 아니다.
(cd preprocess/preprocess_genre && python3 preprocess_genre.py)출력:
data/ml-32m/genre.csv
MovieLens와 Genome 2021을 분리 분석한 뒤 통합 요약까지 생성한다.
python3 -m eda.raw.eda_overview --source all선택 가능한 옵션:
--source ml32m--source genome2021--source combined--source all
출력 위치:
eda/eda_outputs/ml_32m/eda/eda_outputs/genome_2021/eda/eda_outputs/
이전 실행 결과가 eda/raw/outputs/ 아래에 남아 있을 수 있지만, 현재 raw EDA 코드는 eda/eda_outputs/를 쓴다.
전처리 완료 후 산출물을 기준으로 drop 전후 비교와 최종 rating 분포를 분석한다.
python3 eda/processed/eda_processed.py출력 위치:
eda/processed/outputs/eda_report.mdeda/processed/outputs/*.png
현재 공식 POST replay dashboard 계약은 compact-only reader다. 입력은 history run이 만든 compact projection 하나다.
outputs/post/<run_id>_history/dashboard_compact/dashboard_compact.sqlite
Streamlit reader:
streamlit run dashboard/cluster_dashboard.py앱은 outputs/post/**/dashboard_compact/dashboard_compact.sqlite를 자동 탐색하고, 필요하면 sidebar에서 직접 경로를 입력한다. 읽는 table은 event_timeline, visualization_states, cluster_snapshots, recommendations 네 개다. production/production.sqlite, history/history.sqlite, legacy replay.sqlite, JSONL debug artifact는 dashboard 입력 fallback으로 읽지 않는다.
React + FastAPI reader도 같은 compact DB 계약을 사용한다. API는 compact dashboard DB와 별도 movie metadata DB를 read-only로 열고, frontend는 timeline, cluster scatter, K 변화, recommendation panel을 표시한다.
.venv/bin/pip install -r api/requirements.txt
DASHBOARD_DB_PATH=outputs/post/<run_id>_history/dashboard_compact/dashboard_compact.sqlite \
MOVIES_DB_PATH=data/movies.db \
POSTER_DIR=data/MLP-20M \
.venv/bin/uvicorn api.main:app --reloadcd frontend
npm install
VITE_API_BASE=http://localhost:8000 npm run devAPI endpoint 요약:
GET /api/usersGET /api/users/{user_id}/timelineGET /api/users/{user_id}/events/{event_id}GET /api/users/{user_id}/events/{event_id}/visualizationGET /api/users/{user_id}/events/{event_id}/clustersGET /api/users/{user_id}/events/{event_id}/recommendations
data/movies.db와 data/MLP-20M/ poster payload는 로컬 보조 입력이며 git으로 추적하지 않는다. 세부 파일 계약은 docs/streaming-replay-dashboard-contract.md, UI별 실행 안내는 dashboard/README.md, api/README.md, frontend/README.md를 따른다.
python3 -m model.batch.train는 기본적으로 새 run id를 만들고, python3 -m model.batch.extract_canonical, python3 -m model.batch.cluster, python3 -m model.batch.recommend, python3 -m model.stream.recommend_online은 최신 run id를 이어받는다.
python3 -m model.batch.extract_canonical은 event 하나당 embedding 하나를 보장하는 outputs/canonical_embeddings.npz를 저장한다. 현재 python3 -m model.batch.cluster의 기본 입력도 이 canonical embedding이다. 과거 overlap-window 추출 산출물인 outputs/embeddings.npz는 legacy artifact로만 취급한다.
python3 -m model.batch.cluster는 유저별 UMAP+HDBSCAN을 실행하고 outputs/user_interests.npz와 outputs/batch/state.sqlite를 만든다. outputs/user_interests.npz는 dashboard/export용 label/UMAP 배열이고, interest vector 정본은 SQLite state store다. model.common.cluster의 공통 backend를 사용하며 --cluster-backend auto는 가능한 경우 GPU, 불가능하면 CPU fallback을 사용한다. 장르 라벨링은 data/movies_processed_drop.csv가 있으면 자동으로 붙는다.
python3 -m model.batch.export_clusters는 outputs/user_interests.npz를 dashboard용 data/clustering/user_clusters.parquet 등 테이블 포맷으로 변환한다.
python3 -m model.stream.extract_online은 raw rating event를 SQLite user state에 저장하고, 현재까지 관측된 history 기준 positive projection에서 active online embedding을 만든다. Standalone/debug 실행은 outputs/stream/online_embeddings.npz와 outputs/stream/online_embedding_events.jsonl을 남기며, replay runtime 기본 경로에서는 production/production.sqlite의 active embedding cache가 정본이다.
python3 -m model.stream.interest_assign은 active online embedding을 SQLite interest state에 연결한다. interest vector가 없으면 pending buffer와 refit request를 남기고, interest vector가 있으면 cosine similarity로 assign한다. Standalone/debug 로그는 outputs/stream/interest_assignments.jsonl, outputs/stream/refit_requests.jsonl이다.
python3 -m model.stream.cluster_refit은 open refit request를 소비해 user별 active embedding 전체를 다시 clustering하고 interest state를 replace한다. 기본 backend는 auto이며 cuML import와 CUDA runtime probe가 통과하면 GPU를 사용한다. GPU가 불가하거나 auto GPU refit 실행이 실패하면 CPU umap-learn + hdbscan으로 fallback한다. refit 결과는 outputs/stream/refit_events.jsonl에 기록된다.
python3 -m model.stream.recommend_online은 SQLite interest/user state와 SASRec item embedding으로 score(u, i) = max_k(u_k^T v_i)를 계산해 outputs/stream/stream_recommendations.jsonl에 top-K 추천을 append한다. seen positive item은 기본적으로 제외한다.
make -C replay는 replay/bin/rating_replay를 빌드한다. python3 -m model.stream.replay_pipeline은 replay input event를 timestamp trace로 소비해 --speed N 기준 schedule에 맞춰 event를 주입하고, 각 event 처리 후 extract_online -> interest_assign -> cluster_refit을 in-process로 호출한다. --history-mode off 기본 실행은 outputs/post/<run_id>_production/production/production.sqlite에 clean production current state를 남긴다. --history-mode history 실행은 outputs/post/<run_id>_history/production/production.sqlite, history/history.sqlite, dashboard_compact/dashboard_compact.sqlite를 만든다. --recommend를 추가하면 event 처리 후 recommend_online을 실행해 production artifact 영역의 stream_recommendations.jsonl도 남긴다.
로컬 실험 기록:
- 로컬
experiments/model/<run_id>/manifest.json: command, git 상태, 입력/출력 metadata, config - 로컬
experiments/model/<run_id>/metrics.jsonl: 학습 지표와 extract/cluster summary - 로컬
experiments/model/<run_id>/notes.md: 사람이 적는 실험 해석
모델 산출물과 run별 실험 기록은 git으로 추적하지 않는다. 세부 옵션은 model/README.md를 따른다.
이전 모델 코드는 model/prev/ 같은 스냅샷 디렉터리에 복사하지 않는다.
- 현재 공식 구조와 실행 경로:
PROJECT_GUIDE.md,model/README.md - 구조 변경과 모델링 판단 이유:
docs/decisions/ - 실험별 config, metric, 산출물 참조, 이전 run 대비 관찰: 로컬
experiments/model/<run_id>/ - 특정 파일의 과거 코드: git history
비교 대상으로 계속 실행해야 하는 구현은 별도 결정 후 model/baselines/처럼 목적이 명확한 경로로 둔다.
스키마는 schemas/가 정본이다.
- 컬럼 추가, 변경, 삭제는 코드보다 먼저
schemas/에서 정의한다. - 호환되지 않는 변경은 새 버전 파일로 올린다.
- CSV에 배열을 저장할 때는
logical_type과physical_type을 분리한다. - 현재 주요 processed 스키마:
schemas/ml32m/processed/movies_processed.v2.schema.yamlschemas/ml32m/processed/ratings_drop.v1.schema.yamlschemas/ml32m/processed/ratings_drop_processed.v1.schema.yaml
세부 규칙은 schemas/README.md를 참고한다.
PROJECT_GUIDE.md를 단일 진실 공급원으로 사용한다.- 새 작업 전
todo.md와plan/active/의 기존 계획을 확인한다. data/**/raw/는 절대 수정하지 않는다.- 생성물은 스크립트로 재생성하는 것을 원칙으로 한다. 단,
data/ml-32m/genre.csv처럼 명시적으로 포함된 보조 산출물은 예외로 둔다. - 데이터셋이나 파이프라인이 바뀌면
PROJECT_GUIDE.md도 함께 갱신한다.
현재 저장소에 포함된 validation artifact 기준 요약은 다음과 같다.
movies_processed.csv: 87,584 rowsmovies_processed_drop.csv: 77,647 rowsratings_drop.csv: 31,916,363 rowsratings_drop_processed.jsonl: 200,948 users
정확한 세부 수치는 각 validation_report.json을 확인하면 된다.
PROJECT_GUIDE.md: 프로젝트 운영 규칙과 구조docs/README.md: GitHub에 남는 docs 읽는 순서와 local-only 기록 정책AGENTS.md,CLAUDE.md,.cursorrules,.windsurfrules: LLM 도구별 진입점todo.md: 현재 작업 상태와 협업 메모docs/current-pipeline-snapshot.md: 현재 batch / streaming 구현, streaming data flow, 문제 포인트docs/data-flow.md: raw -> processed -> model -> compact dashboard/API/frontend 흐름docs/artifacts.md: 원본 데이터와 생성물의 수정 가능 여부docs/decisions/: 중요한 설계 결정 기록experiments/model/README.md: 로컬 모델 실험 메타데이터 기록 규칙schemas/README.md: 스키마 컨벤션preprocess/README.md: 전처리 실행 순서와 입출력plan/_template.md: 새 로컬 계획서 템플릿plan/active/,plan/done/,plan/expired/: 로컬 작업 계획서 보관 구조experiments/model/: 로컬 모델 실험 메타데이터. git에는 README와.gitkeep구조만 유지eda/processed/outputs/eda_report.md: processed 데이터 분석 결과eda/eda_outputs/eda_report.md: raw 데이터 통합 분석 결과