당신의 피처 스토어는 스칼라(Scalars)를 위해 설계되었습니다. 임베딩(Embeddings)이 이를 망가뜨렸습니다.
요약
기존 피처 스토어는 스칼라 값 중심의 설계로 인해 임베딩 벡터의 특성을 제대로 반영하지 못합니다. 임베딩은 단순한 값이 아닌 모델에 종속된 좌표계이므로, 모델 체크포인트 변경 시 발생하는 벡터 공간의 변화를 관리하는 새로운 접근 방식이 필요합니다.
핵심 포인트
- 피처 스토어의 기존 설계는 고정된 스키마를 가진 스칼라 값에 최적화되어 있음
- 임베딩은 모델 체크포인트에 따라 의미가 변하는 좌표계의 성격을 가짐
- 서로 다른 모델에서 생성된 임베딩은 기하학적으로 비교 불가능함
- 단순 스키마 버전 관리를 넘어 좌표계 버전 관리가 필수적임
요약 (TL;DR) — 피처 스토어(Feature stores)는 피처가 고정된 스키마를 가진 안정적이고 비교 가능한 스칼라(scalar) 값이라는 가정하에 설계되었습니다. 임베딩(Embedding) 피처는 이 가정을 위반합니다. 동일한 피처 이름 아래에 있는 두 벡터(vector)라도 서로 다른 모델 체크포인트(model checkpoints)에서 생성되었다면 기하학적으로 비교할 수 없습니다. 이 에세이는 임베딩 버전 관리(versioning)가 스키마 버전 관리가 아니라 좌표계 버전 관리(coordinate-system versioning)라고 주장하며, 대부분의 피처 스토어 도구들이 혼합된 버전의 벡터로 학습된 모델이 조용히 성능 저하를 일으킬 때까지 이러한 차이를 묵인하고 있다고 지적합니다.
피처 스토어는 실제 문제를 해결했습니다. 피처를 한 번 계산하고, 온라인과 오프라인에서 일관되게 제공하며, 학습 데이터가 미래를 엿보지 못하게(data leakage 방지) 합니다. 이러한 계약은 스칼라 값 — 이동 평균(rolling average), 클릭률(click-through rate), 지난 30일간의 구매 횟수 등 — 에 대해서는 완벽하게 작동합니다. 하지만 당신의 "피처"가 신경망(neural network)에 의해 생성된 768차원 벡터(vector)가 되는 순간, 이 계약은 조용히 무너집니다. 그리고 거의 어떤 도구도 이 차이를 실제적인 아키텍처상의 결함(architectural fault line)으로 취급하지 않습니다.
피처 스토어 계약은 스칼라를 위해 구축되었습니다
피처 스토어가 실제로 무엇을 보장하는지 생각해 보십시오. 피처 스토어는 user_avg_order_value가 화요일에 배치 작업(batch job)으로 계산되든 수요일에 스트리밍 작업(streaming job)으로 계산되든 동일한 의미를 가짐을 보장합니다. 또한 시점 정확성(point-in-time correctness)을 보장합니다. 즉, 학습 시에 보는 값이 추론(inference) 시점에 존재했을 값과 일치함을 보장합니다. 그리고 안정적인 스키마(schema)를 보장합니다. 변환 로직(transformation logic)을 명시적으로 버전 관리하지 않는 한, 부동 소수점(float)은 영원히 부동 소수점입니다.
이 모든 추론은 피처가 고정되고 해석 가능한 공간에 존재한다는 것을 전제로 합니다. 지난달의 매출 수치와 이번 달의 매출 수치는 직접 비교가 가능합니다. 이 수치들을 차이 분석(diff)하고, 버킷(bucket)으로 나누고, 대시보드에 드리프트(drift)를 시각화할 수 있으며, 계산 로직이 변경되지 않는 한 계산 시점과 상관없이 수치는 동일한 의미를 갖습니다.
이제 동일한 슬롯에 임베딩 (embedding)을 넣어보십시오. user_embedding은 벡터 (vector)입니다. 이것은 스칼라 (scalar) 피처와 정확히 똑같이 로그가 기록되고, 버전이 관리되며, 서빙됩니다. 동일한 레지스트리 엔트리, 동일한 시점 조인 (point-in-time join) 로직, 그리고 결측률 (null rates)과 분포 드리프트 (distribution drift)를 확인하는 동일한 모니터링 파이프라인을 거칩니다. 툴링 (tooling)은 이를 "약간 변한 값"으로 취급합니다. 그것이 바로 실수입니다.
임베딩은 값이 아니라, 좌표계입니다
스칼라 피처는 크기 (magnitude)가 변합니다. 임베딩은 _의미 (meaning)_가 변하며, 그 의미는 그것을 생성한 모델에 의해 전적으로 정의됩니다. 동일한 인코더 (encoder)의 서로 다른 두 체크포인트 (checkpoint)에서 계산된 두 임베딩은 "서로 다른 시점의 동일한 피처"가 아닙니다. 그것들은 일반적으로 서로 관련이 없는 두 개의 서로 다른 벡터 공간 (vector space) 내의 좌표입니다. 체크포인트 A의 47번 차원은 주제 유사성과 상관관계가 있을 수 있습니다. 하지만 다른 랜덤 시드 (random seed)와 약간 다른 데이터 혼합 (data mix)으로 학습된 체크포인트 B의 47번 차원은 특별한 것과 아무런 상관관계가 없을 수 있습니다. 축 정렬 (axis alignment)에 대한 보장도, 스케일 (scale)에 대한 보장도 없으며, 코사인 유사도 (cosine similarity)가 두 공간 사이에서 심지어 동일한 의미를 갖는지조차 보장할 수 없습니다.
이 부분이 바로 스칼라 피처에 대한 직관이 당신을 오도하는 지점입니다. 스칼라의 경우, "드리프트 (drift)"는 분포가 이동했음을 의미합니다. 즉, 평균이 높아지거나 분산 (variance)이 넓어졌음을 뜻합니다. 히스토그램 비교를 통해 이를 감지하고 넘어갈 수 있습니다. 하지만 임베딩의 경우, 이와 대등한 실패 모드는 분포 드리프트가 전혀 아닙니다. 그것은 바로 _공간 불일치 (space incompatibility)_입니다. 벡터들은 노름 (norm)의 분포가 동일할 수 있음에도 불구하고, 서로 아무런 관련이 없는 방향을 가리킬 수 있습니다. 왜냐하면 그것들을 생성한 변환 (transformation)이 바뀌었기 때문입니다.
스칼라 드리프트를 포착하기 위해 구축된 표준 피처 모니터링 (feature monitoring) 방식은 이를 아무 문제 없이 통과시켜 버릴 것입니다. 노름은 정상적으로 보이고, 결측치 (nulls)는 0이며, 아무런 경고도 울리지 않습니다.
왜 "동일한 피처, 새로운 모델 버전"이 조용한 데이터 오염 버그인가
실제 사례에서의 실패 모드는 다음과 같습니다. 한 팀이 주기적으로 재학습(retrained)되거나 미세 조정(fine-tuned)되는 인코더(encoder)를 기반으로 하는 item_embedding이라는 피처 그룹(feature group)을 관리하고 있습니다. 설계된 대로 피처 스토어(feature store)의 역할은 온라인 서비스되는 벡터(vector)가 학습을 위해 기록된 벡터와 일치하도록 보장하는 것입니다. 피처 스토어는 그 역할을 완벽하게 수행합니다. 하지만 피처 스토어가 하지 못하는 것은, 백필(backfill)이나 부분적 롤아웃(partial rollout) 과정에서 동일한 피처 그룹 내의 동일한 이름과 동일한 스키마(schema) 아래, 인코더 버전 N에 의해 계산된 벡터와 인코더 버전 N+1에 의해 계산된 벡터가 섞이는 것을 막는 일입니다.
하위(downstream) 랭킹(ranking) 또는 검색(retrieval) 모델은 그 차이를 알지 못합니다. 모델은 item_embedding이라고 라벨링된 768개의 부동 소수점(floats) 배치를 입력받아, 학습 세트에 존재하는 기하학적 구조(geometry)에 맞춰 가중치(weights)를 학습합니다. 만약 학습 예시의 절반은 이전 모델로 인코딩되었고 나머지 절반은 새 모델로 인코딩되었다면, 모델은 혼합되고 내부적으로 일관되지 않은 유사성(similarity) 개념을 학습하게 됩니다. 그리고 이는 데이터 버그처럼 보이지 않고 일반적인 성능 저하처럼 보이는 방식으로 성능을 떨어뜨릴 것입니다. 파이프라인의 그 어떤 것도 "어떤 인코더가 이 벡터를 생성했는가"를 조인 키(join key)로 취급하지 않기 때문에 아무도 이에 대해 경고를 울리지 않습니다. 그것은 추적된다 하더라도 메타데이터(metadata)일 뿐입니다. 그것은 파티션 키(partition key)가 되어야 합니다.
이는 전형적인 학습-서빙 왜곡(training-serving skew) 문제보다 더 심각합니다. 왜냐하면 왜곡(skew)은 보통 통계적 테스트로 잡아낼 수 있는 측정 가능한 분포 불일치(distribution mismatch)로 나타나기 때문입니다. 임베딩 버전 간의 좌표계 불일치(coordinate-system mismatch)는 통계적으로는 보이지 않으면서도 의미론적(semantically)으로는 완전히 어긋날 수 있습니다. 두 인코더는 차원별로 일치하는 주변 분포(marginal distributions)를 가진 벡터를 생성하면서도, 어떤 아이템들이 유사한지에 대해서는 완전히 상충하는 결과를 낼 수 있습니다.
실제로 무엇이 바뀌어야 하는가
해결책은 "모니터링을 더 추가하는 것"이 아닙니다. 인코더 버전을 변경 로그(changelog)의 각주가 아니라, 피처 정체성(feature's identity)의 일급 차원(first-class dimension)으로 취급하는 것입니다. 여기서 몇 가지 구체적인 시사점이 도출됩니다:
• 임베딩 피처 그룹은 엄격하게 강제되는 불변성(invariant)이 필요합니다: 학습 세트 또는 온라인 서빙 경로의 모든 벡터는 정확히 하나의 인코더 버전으로 생성되어야 합니다. 혼합된 백필(backfills)이나 전환 기간 동안 하나의 피처 이름 아래에서 두 버전이 공존하도록 허용하는 부드러운 롤아웃(soft rollouts)은 안 됩니다.
• 모델 업데이트 시 재임베딩(Re-embedding)은 값 새로 고침(value refresh)이 아니라 스키마 마이그레이션(schema migration)처럼 다루어져야 합니다. 왜냐하면 그것이 본질적으로 그러하기 때문입니다. 이전 벡터와 새 벡터는 값이 업데이트된 동일한 피처가 아닙니다. 이름만 공유하는 두 개의 별개 피처일 뿐입니다.
• 유사도 기반 다운스트림 소비자(Similarity-based downstream consumers) — 검색(retrieval), 클러스터링(clustering), 최근접 이웃 조회(nearest-neighbor lookups) — 는 쿼리 벡터와 인덱스가 서로 다른 인코더 버전으로 구축되었을 경우, 단순히 오해를 불러일으키는 것이 아니라 명백히 손상된 것임을 알리는 방식으로 실패하는 명시적인 호환성 검사(compatibility checks)가 필요합니다.
• 피처 스토어의 계보(lineage)는 모든 벡터에 첨부되는 쿼리 가능한 메타데이터로서 '어떤 모델, 어떤 체크포인트, 어떤 학습 실행'을 포착해야 하며, 테이블 조인 전에 아무도 읽지 않는 모델 카드에 대한 주석이 되어서는 안 됩니다.
이 모든 것을 위해 피처 스토어 추상화(abstraction)를 포기할 필요는 없습니다. 단지 그 추상화가 '피처는 비교 가능한 값'이라는 가정에 기반하여 구축되었으며, 이 가정이 대규모로 증가하는 프로덕션 피처의 상당 부분에서 더 이상 충족되지 않는다는 것을 인정해야 합니다. 임베딩, 검색 점수(retrieval scores), 기타 모델 파생 피처들은 확률적이고 버전이 지정된 프로세스의 출력물이며, 그 비교 가능성은 스칼라 피처가 그렇지 않은 방식으로 출처(provenance)에 따라 조건부입니다.
더 좁은 관점
더 많은 프로덕션 시스템이 랭킹(ranking), 검색 증강 생성 (RAG, retrieval-augmented generation), 에이전트 메모리 (agent memory)를 위해 피처로서 임베딩 (embeddings)에 의존함에 따라, 이는 더 이상 예외적인 사례가 아닌 기본값이 되어가고 있습니다. 피해를 입는 팀들은 파이프라인이 명백하게 고장 난 팀들이 아닐 것입니다. 그들은 대시보드가 깨끗해 보이고, 결측치 (nulls)가 0이며, 드리프트 지표 (drift metrics)가 녹색을 띠고 있지만, 실제로는 더 이상 존재하지 않는 좌표계로부터 조용히 학습하고 있는 모델을 가진 팀들이 될 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기