AI/Technical

AI Sales Forecasting 판매 예측 데이터 모델링 템플릿 (2)

Royzero 2026. 2. 9. 00:36
반응형

TL;DR

  • AI Sales Forecasting 프로젝트는 모델보다 데이터 설계(스키마/시간 의미/누수 방지/품질 규칙)에서 승부가 납니다.
  • 판매 예측 데이터는 최소한 sales(타깃) + calendar(캘린더/이벤트) + price + promo + inventory/stockout 축으로 나눠 설계하는 게 안전합니다.
  • 시계열 피처 조인은 포인트-인-타임(과거 시점 기준) 정합성을 보장해야 누수(leakage)를 막을 수 있습니다.
  • 품절(stockout)은 관측 판매를 검열(censored)로 만들고 예측 편향을 유발할 수 있으니, 최소한 stockout_flag는 데이터 계약에 포함하세요.
  • 품질 관리는 "문서"가 아니라 검증 가능한 규칙(Expectation Suite)로 자동화하는 게 실무적으로 맞습니다.

본문

TOC

    1. 데이터 모델링 목표와 원칙(데이터 계약)
    1. “정답 테이블(Training/Inference View)” 스키마
    1. 원천 테이블 설계(판매/가격/프로모션/재고/캘린더)
    1. 시간 의미(이벤트 시간 vs 기준 시점)와 누수 방지
    1. 결측·이상치·품절(검열 수요) 처리
    1. 품질 검증 규칙(자동화 템플릿)
    1. 트러블슈팅 3개
    1. 실무 체크리스트: 배포 전 / 운영 중
    1. FAQ
    1. 다음 글 예고(Part 3)

1) 데이터 모델링 목표와 원칙(데이터 계약)

AI Sales Forecasting에서 데이터 모델링의 목표는 “많이 모으기”가 아니라, 학습/추론에서 동일한 의미로 재현되는 데이터를 만드는 것입니다.
이번 회차는 그 목적을 위해 데이터 계약(Data Contract)을 다음 4가지를 중심으로 고정합니다.

  1. 타깃(y)의 의미를 고정: 판매량(수량)인지, 매출인지, 순매출(반품 차감)인지
  2. 시간 기준을 고정: 일/주 단위, 타임존, 집계 컷오프(예: D+1 새벽 확정)
  3. 피처의 "미래 가용성"을 고정: 예측 시점에 알 수 있는 값만 사용(누수 방지)
  4. 검열(품절)·결측을 구분: "0 판매"와 "판매 불가(품절/미운영)"를 섞지 않기

Why it matters: 모델은 바꿔도 되지만, 데이터 의미가 흔들리면 재현·운영이 무너집니다.
특히 누수는 오프라인 점수를 망치지 않고 "운영에서만" 터지기 때문에 더 위험합니다.


2) “정답 테이블(Training/Inference View)” 스키마

현업에서 가장 많이 쓰이는 형태는 롱 포맷 + 시리즈 키 + 날짜(ds) 입니다. (M5 리테일 데이터도 판매/캘린더/가격을 분리해 제공하는 구조로 유명합니다.)

2-1. 최소 스키마(권장)

컬럼 비고
ds 2026-02-08 기준 날짜(일/주)
series_id SKU123_STORE01 SKU×매장 등
y 17 타깃(판매)
is_open 1/0 영업일 여부
is_listed 1/0 취급 여부(입점/단종)
stockout_flag 1/0 품절(검열) 여부
price 10,900 실판매가/기준가
promo_flag 1/0 프로모션 여부
event_name 설날/블프 캘린더 이벤트

핵심 규칙:

  • y=0은 “팔 수 있었는데 안 팔림”을 의미해야 합니다. is_open=0 또는 is_listed=0이면 그날은 0이 아니라 결측 취급이 더 안전합니다(훈련셋 생성 단계에서 제외/마스킹).
  • stockout_flag=1이면 y는 관측 판매일 뿐, "진짜 수요"가 아닐 수 있습니다(검열).

Why it matters: 이 뷰가 고정되면, 모델/플랫폼(Azure AutoML, Vertex, 오픈소스)이 바뀌어도 파이프라인은 유지됩니다.


3) 원천 테이블 설계(판매/가격/프로모션/재고/캘린더)

여기서는 "정답 뷰"를 만들기 위한 원천 테이블 묶음을 제안합니다. (정확히 M5가 sales + calendar + sell_prices 형태로 분리해 배포한 구조가 참고가 됩니다.)

3-1. 권장 테이블 맵

구분 테이블 키(예) 설명
타깃 fact_sales ds, sku_id, store_id 판매(수요의 관측치)
캘린더 dim_calendar ds 요일/월/공휴일/이벤트
가격 fact_price sku_id, store_id, effective_from~to 가격(유효기간형)
프로모션 fact_promo_plan sku_id, store_id, start~end 계획/집행 프로모션
재고/품절 fact_inventory_snapshot as_of_ts, sku_id, store_id 재고 스냅샷
마스터(정적) dim_product, dim_store sku_id / store_id 카테고리/지역 등

Why it matters: 판매 예측에서 "설명 변수"는 대부분 캘린더/가격/프로모션/재고에서 나오고, 이 축이 분리돼야 시간 의미를 제대로 관리할 수 있습니다.


4) 시간 의미(이벤트 시간 vs 기준 시점)와 누수 방지

4-1. 필수 시간 컬럼 2개: event_timeas_of_time

  • event_time: “그 일이 실제로 발생한 시간”(판매 발생 시점, 가격 적용 시작 시점)
  • as_of_time: “우리 시스템이 그 값을 언제 알았는지(수집/확정 시점)”

시계열 학습에서 가장 흔한 사고는 event_time만 보고 조인해서, 실제로는 나중에 확정된 값을 과거에 섞어 넣는 것입니다. 그래서 피처 조인은 포인트-인-타임 정합성(label 시점에 존재하던 피처 값만 조인)을 강제하는 방식이 권장됩니다.

4-2. “피처 가용성 분류”를 컬럼 메타로 관리

분류 학습/추론
Static 상품 카테고리, 매장 지역 항상 가능
Past-only 지난 7일 판매 합, lag 피처 학습 가능 / 추론은 cutoff 이전까지만
Future-known 캘린더, 계획된 프로모션 학습/추론 모두 가능
Future-unknown 실시간 재고, 실시간 경쟁사 가격 추론 시점에 없으면 누수

Azure AutoML도 시계열에서 lag/rolling 피처를 만들고 활용하는 개념을 문서로 설명합니다(결국 "과거로부터 만든 피처"입니다).

Why it matters: 누수는 모델이 아니라 "조인"에서 생깁니다. 포인트-인-타임 조인을 강제하면 이 계열의 사고를 크게 줄일 수 있습니다.


5) 결측·이상치·품절(검열 수요) 처리

5-1. 품절(stockout)은 반드시 라벨링

품절이 있으면 관측 판매는 "진짜 수요"보다 작을 수 있고, 이 편향이 누적되면 시스템적으로 과소 예측을 만들 수 있습니다. 신선 리테일에서 검열 수요(censored demand) 문제를 정면으로 다룬 데이터셋/연구도 나와 있습니다.

최소 실무 대응(필수):

  • stockout_flag 생성(재고=0 + 판매 제한 신호)
  • stockout_flag=1 구간을 학습에서 제외하거나, 별도 “수요 복원(imputation)” 단계를 둡니다(Part 8에서 런북으로 다룹니다).

5-2. “0 판매”와 “관측 불가”를 분리

  • 휴점/미취급/시스템 장애는 y=0으로 넣지 말고 is_open, is_listed, data_missing_flag로 분리하세요.
  • 리테일 공개 데이터도 판매 외에 캘린더/가격/이벤트 같은 컨텍스트를 함께 제공하는 이유가 여기 있습니다.

Why it matters: 품절과 결측을 섞으면, 모델은 "수요가 없는 상품"으로 학습합니다. 그 다음부터는 모델을 바꿔도 답이 안 나옵니다.


6) 품질 검증 규칙(자동화 템플릿)

품질 규칙은 문서가 아니라 검증 가능한 형태로 남겨야 합니다. Great Expectations는 이런 "검증 가능한 주장(Expectation Suite)"을 모아 관리하고, 결과를 문서(Data Docs)로 만들 수 있다고 설명합니다.

6-1. 핵심 규칙(바로 복사해서 쓰는 체크)

(A) 키/중복

  • (ds, series_id)는 유일해야 함
  • 날짜는 연속(캘린더 기준)이어야 함

(B) 값 범위

  • y >= 0
  • price > 0 (0 또는 음수는 오류)
  • promo_start <= promo_end

(C) 의미 규칙

  • is_open=0이면 y는 NULL(또는 학습 제외)
  • is_listed=0이면 y는 NULL(또는 학습 제외)
  • stockout_flag=1이면 "검열 구간"으로 태깅되어야 함

6-2. 예시: DDL(스켈레톤)

-- 판매(타깃)
create table fact_sales (
  ds date not null,
  sku_id string not null,
  store_id string not null,
  units_sold double,              -- 원천이 정수여도 환산/반품 처리로 double 가능
  event_time timestamp,           -- 실제 판매 발생 시간(있으면)
  as_of_time timestamp not null,  -- 확정/적재 시점
  primary key (ds, sku_id, store_id)
);

-- 캘린더(미래에도 알 수 있는 값)
create table dim_calendar (
  ds date primary key,
  dow int,
  week_of_year int,
  is_holiday int,
  event_name string
);

-- 가격(유효기간형)
create table fact_price (
  sku_id string,
  store_id string,
  effective_from date,
  effective_to date,
  price double,
  as_of_time timestamp not null
);

-- 프로모션(계획/집행)
create table fact_promo_plan (
  sku_id string,
  store_id string,
  promo_id string,
  start_date date,
  end_date date,
  promo_type string,
  discount_rate double,
  as_of_time timestamp not null
);

-- 재고 스냅샷(품절 파생)
create table fact_inventory_snapshot (
  as_of_ts timestamp,
  sku_id string,
  store_id string,
  on_hand double,
  on_order double
);

Why it matters: 품질 규칙이 자동화되면, 데이터가 깨질 때 "모델 성능"이 아니라 "입력 데이터"에서 바로 원인을 찾을 수 있습니다.


7) 트러블슈팅(증상→원인→해결) 3개

  1. 증상: 오프라인 점수는 좋은데 운영에서만 성능 붕괴
  • 원인 후보: 포인트-인-타임 정합성 없는 조인(누수)
  • 해결: label 시점 기준으로 피처를 조인하는 규칙(시간 조건)을 강제
  1. 증상: 특정 상품군만 지속 과소 예측

    • 원인 후보: 품절 구간이 y=0처럼 학습됨(검열 수요)
    • 해결: stockout_flag 생성 후 제외/복원 파이프라인 도입
  2. 증상: 프로모션 기간만 예측이 심하게 빗나감

  • 원인 후보: 계획 프로모션 테이블과 실제 집행 프로모션이 섞임(시간/정의 불일치)
  • 해결: fact_promo_plan(계획)과 fact_promo_actual(집행)을 분리하고, 추론에서는 “계획”만 사용

Why it matters: 장애의 80%는 모델이 아니라 데이터 정의/조인/품절 처리에서 나옵니다.


8) 실무 체크리스트 2종

배포 전(데이터) 체크리스트

  • y 정의(순판매/총판매/매출)가 문서+스키마로 고정
  • (ds, series_id) 유일성 보장
  • 포인트-인-타임 조인 규칙이 테스트로 검증됨
  • stockout_flag, is_open, is_listed가 존재
  • Expectation Suite로 품질 규칙이 자동화됨

운영 중(데이터) 체크리스트

  • 캘린더 갱신 누락(미래 날짜 없음) 알람
  • 가격 유효기간 겹침/공백 탐지
  • 프로모션 기간 이상(역전/중복) 탐지
  • 품절 비율 급증(재고 파이프라인 장애) 알람

Why it matters: 예측 시스템은 “데이터 제품”입니다. 데이터 운영이 무너지면, 어떤 모델도 복구책이 아닙니다.


9) FAQ

  1. Q. 판매 데이터만으로는 안 되나요?
    A. 할 수는 있지만, 실무 리테일에서는 캘린더/가격/프로모션이 성능과 해석에 크게 영향을 줍니다. 공개 리테일 벤치마크도 이 컨텍스트(캘린더/가격)를 함께 제공합니다.

  2. Q. 재고 데이터가 없으면 품절 처리를 못 하나요?
    A. 못 하는 건 아니지만, 품절은 관측 판매를 검열로 만들어 편향을 유발할 수 있으니, 최소한 "판매 제한 신호"를 별도로 기록하는 쪽이 안전합니다.

  3. Q. 누수 방지는 결국 뭐 하나만 지키면 되나요?
    A. 핵심은 "label 시점에 존재하던 피처만 조인"입니다. 이걸 포인트-인-타임 정합성으로 강제하면 대부분 정리됩니다.

  4. Q. lag/rolling 피처는 언제 만들면 되나요?
    A. 원천 테이블에서 미리 만들기보다, 학습셋 빌드 단계에서 컷오프 기준으로 생성하는 편이 안전합니다(누수 방지). Azure 문서도 lag/rolling 피처 개념을 별도로 설명합니다.

  5. Q. Great Expectations 같은 도구는 왜 쓰나요?
    A. 품질 규칙을 "검증 가능한 형태(Expectation Suite)"로 남기고 결과를 문서(Data Docs)로 만들 수 있기 때문입니다.


10) 다음 글 예고(Part 3)

Part 3에서는 베이스라인 2개 + rolling-origin 백테스트 + 리포트 템플릿을 코드 중심으로 정리합니다. (rolling forecasting origin/시계열 CV 개념은 FPP에서도 표준으로 다룹니다.)


결론 (요약 정리)

  • 판매 예측 데이터 모델링은 스키마(축 분리) + 시간 의미(as_of) + 포인트-인-타임 조인이 핵심입니다.
  • 품절은 "0 판매"가 아니라 검열 수요일 수 있으니, 최소한 stockout_flag를 계약에 넣어야 합니다.
  • 품질 관리는 문서가 아니라 자동 검증 규칙으로 운영해야 합니다.

References

반응형