Prophet — 회귀 분해로 추세·계절성·이벤트를 한 번에
지금까지 ARIMA/SARIMA와 ETS를 살펴봤습니다. 두 모형군은 “과거 관측값의 함수”로 현재를 설명하는 방식입니다. 2017년 Facebook(현 Meta) Research 팀이 발표한 Prophet은 접근법이 다릅니다. 시간 $t$ 자체를 입력으로 받는 회귀 분해 모형입니다.1
1. Prophet의 포지션
| ARIMA | ETS | Prophet | |
|---|---|---|---|
| 핵심 아이디어 | 잔차 자기상관 모델링 | 지수가중 레벨·추세·계절 | 회귀 분해 (추세+푸리에+휴일) |
| 추세 처리 | 차분 | $\ell_t$, $b_t$ 직접 추적 | Piecewise linear / Logistic |
| 복수 계절성 | 어려움 | 어려움 | 자연스럽게 지원 |
| 휴일·이벤트 | 없음 | 없음 | 기본 지원 |
| 결측치·불규칙 간격 | 보간 필요 | 보간 필요 | 비교적 강함. 단, 관측 패턴이 계절성 식별을 방해하지 않는지 확인 필요 |
| 모형 선택 | AIC/BIC 자동 | AIC/BIC 자동 | CV 기반 수동 |
Prophet이 빛나는 영역은 일별 또는 시간별 데이터에 연간·주간·일간 계절성이 겹치고, 휴일·프로모션 같은 이벤트 효과까지 있는 비즈니스 KPI 예측입니다. 웹 트래픽, 앱 DAU, 소매 매출이 대표 사례입니다.
2. 핵심 모형 구조
Prophet의 전체 모형은 덧셈 분해입니다:
\[y(t) = g(t) + s(t) + h(t) + \epsilon_t\]| 항 | 의미 | 방법 |
|---|---|---|
| $g(t)$ | 추세 | Piecewise linear 또는 Logistic growth |
| $s(t)$ | 계절성 | 푸리에 급수 |
| $h(t)$ | 휴일·이벤트 | 지시 변수 선형 회귀 |
| $\epsilon_t$ | 오차 | $N(0, \sigma^2)$ 가정 |
ARIMA나 ETS가 “이전 시점의 값이나 오차를 입력으로 받는 점화식”인 반면, Prophet은 시간 $t$ 자체를 독립변수로 삼는 회귀 모형입니다. 결측치나 불규칙 간격 데이터에 강한 이유이며, 동시에 잔차 자기상관을 모형화하지 못하는 이유이기도 합니다.
3. 추세 컴포넌트 $g(t)$ — Change Points
$g(t)$는 두 가지 형태 중 하나를 선택합니다. 데이터에 성장의 상한이 있는가 라는 질문이 선택 기준입니다.
| 3.1 Piecewise Linear | 3.2 Logistic Growth | |
|---|---|---|
| 성장 형태 | 선형 (무한 연장) | S-curve (포화 수렴) |
| 상한(cap) 필요 | 불필요 | 필수 (사용자 지정) |
| 기본값 | growth='linear' | growth='logistic' |
| 적합 데이터 | 매출, 트래픽 등 지속 성장 | 가입자 수, 시장점유율 등 포화 가능 |
두 모드 모두 change points를 동일한 방식으로 처리합니다. 기울기 변화량 벡터 $\boldsymbol{\delta}$와 Laplace 사전분포를 그대로 씁니다.
3.1 Piecewise Linear Trend
가장 자주 쓰는 기본 모드입니다.
후보 change points 배치
Prophet은 학습 구간 앞 80%에 기본 25개의 후보 change points를 균등 간격으로 배치합니다. 이 위치들이 실제 추세 꺾임인지는 아무도 모릅니다. 단지 “여기서 기울기가 바뀔 수도 있다”는 후보 목록입니다. 나머지 20%는 change point를 두지 않아 미래 예측이 마지막 학습 구간의 추세를 그대로 연장하게 합니다.
후보 사이에서 실제 변화가 일어나면?
실제 통계적 성질 변화가 15번째와 16번째 후보 사이에서 일어난다면, 모형은 가장 가까운 후보(16번째)에 δ를 집중시켜 근사합니다. 탐지 해상도는 후보 간격에 의해 제한됩니다. 1년치 일별 데이터(365일 × 앞 80%)에 25개를 배치하면 후보 간격은 약 12일이므로, 실제 변화 위치와의 최대 오차는 6일 수준입니다. 해상도를 높이려면 n_changepoints를 늘리거나, 변화 시점을 이미 알고 있다면 changepoints=['2022-03-01', ...]로 직접 지정합니다.
$g(t)$가 change point를 만날 때마다 기울기가 바뀐다
\[g(t) = \bigl(k + \mathbf{a}(t)^T \boldsymbol{\delta}\bigr)\,t + \bigl(m + \mathbf{a}(t)^T \boldsymbol{\gamma}\bigr)\]- $k$: 기준 성장률
- $m$: 기준점 오프셋
- $\mathbf{a}(t)$: $t$가 $j$번째 change point $s_j$를 지났으면 1, 아직 못 지났으면 0인 지시 벡터
- $\boldsymbol{\delta}$: 각 change point에서 추가되는 기울기 변화량 벡터
- $\boldsymbol{\gamma}$: 연속성 유지를 위한 오프셋 보정 벡터
$\mathbf{a}(t)^T \boldsymbol{\delta}$는 $t$ 이전에 통과한 모든 change point의 $\delta_j$를 합산합니다. 즉:
\[\text{현재 기울기} = k + \sum_{j:\, s_j \leq t} \delta_j\]$g(t)$가 $s_j$를 지나는 순간 기울기에 $\delta_j$가 더해집니다. $\delta_j = 0$이면 그 change point는 사실상 비활성화 — 통과해도 아무 변화가 없습니다.
Laplace 사전분포와 LASSO — change point “감지”의 실체
먼저 짚을 점이 있습니다. Prophet은 변화점을 검정이나 CUSUM 같은 알고리즘으로 “탐지”하지 않습니다. 25개 후보 각각에 $\delta_j$ 파라미터를 붙이고, 모든 $\delta_j$를 데이터에 맞게 동시 추정합니다.
- $\delta_j \approx 0$ → 해당 후보는 변화 없음 (소거됨)
- $\delta_j \neq 0$ → 해당 후보에서 추세가 꺾임 (활성화됨)
“감지”의 실체는 25개 δ 파라미터의 사후 추정 결과입니다. 그렇다면 왜 대부분의 $\delta_j$가 0으로 수렴하는가 — 여기서 Laplace 사전분포가 등장합니다.
\[\delta_j \sim \text{Laplace}(0,\, \tau)\]$\tau$ = changepoint_prior_scale (기본값 0.05)
핵심 항만 단순화해서 MAP 추정의 목적함수를 전개하면:
\[\log p(\mathbf{y} \mid \boldsymbol{\delta}) + \log p(\boldsymbol{\delta}) = -\sum(\text{잔차})^2 - \frac{1}{\tau}\sum_j |\delta_j|\]이것이 LASSO(L1 정규화) 목적함수입니다 ($\lambda = 1/\tau$). Ridge(L2)가 계수를 0 근처로 수축시키는 데 그치는 반면, LASSO는 작은 계수를 0으로 강하게 수축시키고 MAP 최적화에서는 sparse한 해를 만들 수 있습니다. 이유는 제약 경계의 모양 차이입니다. L2 제약은 원형이라 잔차 등고선이 경계 위 어느 점과도 만날 수 있지만, L1 제약은 마름모형이라 등고선이 꼭짓점(= 특정 $\delta_j = 0$인 점)에서 만날 가능성이 높습니다.
$\tau$가 작을수록 패널티 $1/\tau$가 커져 더 많은 $\delta_j$가 0으로 수렴하고 추세가 완만해집니다. 클수록 더 많은 change point가 활성화되어 추세가 유연해지지만 과적합 위험이 있습니다.2
L1 vs L2 — 기하학적 비교
Laplace 분포의 확률밀도함수: $f(x) = \frac{1}{2\tau}\exp\!\left(-\frac{\lvert x\rvert}{\tau}\right)$. 정규분포가 $e^{-x^2}$으로 감소하는 반면 Laplace는 $e^{-\lvert x\rvert}$으로 감소해 0 근방에서 훨씬 뾰족합니다.  MAP 해는 잔차 등고선(타원)과 사전분포 제약 경계가 처음 만나는 점입니다. - **L2 (Ridge / 정규분포)**: 원형 경계 → 등고선이 경계 위 어느 점과도 만날 수 있어 $\delta_j = 0$이 나오기 어려움 - **L1 (LASSO / Laplace)**: 마름모 경계 → 꼭짓점이 축 위에 있어($\delta_j = 0$인 점) 등고선이 꼭짓점에서 만날 가능성이 높음 → **스파스 해** 자연스럽게 생성 | 사전분포 | MAP 동치 | 효과 | |---|---|---| | 정규분포 $N(0, \sigma^2)$ | Ridge (L2) | 0 근처로 수축, 완전한 0은 드물다 | | Laplace$(0, \tau)$ | LASSO (L1) | 작은 값을 0으로 강하게 수축시키며 MAP에서는 sparse한 해를 만들 수 있다 |아래는 후보 change points 중 일부만 활성화된 $g(t)$ 예시입니다. 빨간 점선 위치에서 기울기가 실제로 꺾이고, 노란 점선 위치는 $\delta_j = 0$으로 소거되어 아무 변화가 없습니다.
이 change point 메커니즘이 Prophet의 Regime Change 대응입니다. 다만 다루는 것은 추세 기울기의 변화이며, 분산 구조나 계절 패턴 자체의 변화는 대상 밖입니다.
3.2 Logistic Growth (포화 성장)
신규 가입자 수, 시장 점유율, 바이럴 확산처럼 성장이 언젠가 천장에 수렴하는 데이터에 씁니다. 개념적으로는 기본 로지스틱 함수에 change points를 결합한 형태입니다. 실제 Prophet 구현은 change point 이후에도 곡선이 연속적으로 이어지도록 조정항을 포함합니다.
\[g(t) = \frac{L(t)}{1 + \exp\!\bigl(-(k + \mathbf{a}(t)^T \boldsymbol{\delta})\,(t - (m + \mathbf{a}(t)^T \boldsymbol{\gamma}))\bigr)}\]3.1의 식과 비교하면 구조가 그대로 유지됩니다. $k$와 $m$을 분모의 지수 안으로 집어넣었을 뿐이고, $\boldsymbol{\delta}$와 $\boldsymbol{\gamma}$는 완전히 동일한 change point 메커니즘입니다. change point가 활성화되면 S-curve의 기울기(성장 속도)가 꺾입니다.
$L(t)$: 시간에 따라 변하는 포화 상한
$L(t)$는 단순한 상수가 아닙니다. 시간에 따라 달라지는 포화 상한을 지정할 수 있습니다. 예를 들어 글로벌 시장으로 확장하면서 TAM(Total Addressable Market)이 커지는 경우, 해당 시점부터 $L$을 높게 설정할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from prophet import Prophet
import pandas as pd
# growth='logistic' 사용 시 데이터프레임에 'cap' 열이 필수
df['cap'] = 8.5 # 포화 상한 (log 조회수 기준)
# 시점별로 상한이 달라지는 경우
df.loc[df['ds'] > '2014-01-01', 'cap'] = 10.0 # 이후 상한 상향
model = Prophet(growth='logistic')
model.fit(df)
future = model.make_future_dataframe(periods=365)
future['cap'] = 10.0 # 미래 데이터에도 cap 지정 필수
forecast = model.predict(future)
cap을 빠뜨리면 에러가 납니다. 반대로 growth='linear'일 때는 cap이 없어도 됩니다.
3.1 vs 3.2 — 어떤 모양의 g(t)를 얻는가
3.1은 꺾인 직선들의 연결입니다. 추세가 오르내릴 수 있고 상한이 없습니다. 3.2는 S자를 그리면서 결국 $L(t)$에 수렴합니다. change point가 붙으면 S-curve의 기울기가 중간에 달라집니다. 가파르게 성장하다가 갑자기 속도가 줄어드는 모양을 자연스럽게 표현합니다.
언제 Logistic을 써야 하는가. 포화 상한이 도메인 지식으로 명확히 존재할 때 씁니다. $L$을 너무 낮게 잡으면 모형이 이미 포화된 것처럼 행동하고, 너무 높게 잡으면 사실상 선형 모형과 다르지 않게 됩니다. 상한에 대한 확신이 없으면
growth='linear'가 더 안전합니다.
4. 계절성 컴포넌트 $s(t)$ — 푸리에 급수
계절성을 푸리에 급수로 표현합니다:
\[s(t) = \sum_{n=1}^{N} \left( a_n \cos\frac{2\pi n t}{P} + b_n \sin\frac{2\pi n t}{P} \right)\]- $P$: 계절 주기 (연간 365.25일, 주간 7일)
- $N$: 푸리에 차수 — 클수록 계절 패턴이 유연 (기본값: 연간 10, 주간 3)
- $a_n, b_n$: 추정할 회귀 계수
여러 주기를 동시에 stacking할 수 있습니다. 시간별 데이터라면 일간($P=24$) + 주간($P=168$) + 연간($P=8766$)을 동시에 적합할 수 있습니다. ETS와 SARIMA가 단일 계절 주기를 가정하는 것과 대조적입니다.
계절 계수에는 정규 사전분포가 붙습니다:
\[\beta_n \sim N(0,\, \sigma_s^2)\]seasonality_prior_scale($\sigma_s$, 기본값 10)로 유연성을 조절합니다.
5. 휴일·이벤트 컴포넌트 $h(t)$
각 이벤트를 지시 변수로 표현합니다:
\[h(t) = \mathbf{Z}(t)\,\boldsymbol{\kappa}\]- $\mathbf{Z}(t)$: $t$가 특정 이벤트 윈도우 안에 있는지 나타내는 지시 행렬
- $\boldsymbol{\kappa}$: 이벤트 효과 크기 (추정 대상)
1
2
3
4
5
6
7
8
9
10
11
12
from prophet import Prophet
import pandas as pd
# 설날·추석 같은 한국 공휴일 지정 예시
kr_holidays = pd.DataFrame({
'holiday': 'lunar_new_year',
'ds': pd.to_datetime(['2020-01-24', '2021-02-12', '2022-02-01']),
'lower_window': -1, # 하루 전부터 효과 인정
'upper_window': 3, # 사흘 후까지 효과 인정
})
model = Prophet(holidays=kr_holidays)
lower_window, upper_window로 이벤트 효과가 미치는 날짜 범위를 조절합니다. 휴일 계수에도 정규 사전분포(holidays_prior_scale, 기본값 10)가 붙습니다.
6. 가법 vs 승법 계절성
기본은 가법 $y = g + s + h + \epsilon$입니다.
계절 진폭이 추세 수준에 비례해 커지는 경우(Air Passengers가 대표 예)에는 승법을 씁니다:
\[y(t) = g(t) \cdot \bigl(1 + s(t) + h(t)\bigr) + \epsilon_t\]1
2
# 승법 계절성
Prophet(seasonality_mode='multiplicative')
ETS는 (A,A,A) vs (A,A,M) 같은 조합을 AIC로 자동 선택하지만, Prophet은 사용자가 직접 지정해야 합니다. 데이터를 시각화해 계절 진폭이 일정하면 가법, 추세에 비례해 커지면 승법을 선택합니다.
7. 적합 방법 — Stan 백엔드의 베이즈 MAP
Stan이란?
Stan은 베이지안 통계 모형을 위한 확률적 프로그래밍 언어(Probabilistic Programming Language) 이자 추론 엔진입니다.3 2012년 Columbia 대학교의 Andrew Gelman 연구팀이 개발해 오픈소스로 공개했으며, 이름은 몬테카를로 방법의 공동 창안자인 Stanisław Ulam에서 따왔습니다.
Stan이 하는 일을 단계로 나누면:
- 모형 기술: Stan 전용 언어(
.stan파일)로 likelihood와 사전분포를 작성 - 컴파일: Stan 코드를 C++로 변환 및 컴파일
- 추론: HMC-NUTS(Monte Carlo 샘플링) 또는 L-BFGS(최적화)로 모수 추정
Prophet의 핵심 모형($g + s + h$)은 Stan 코드 파일로 구현되어 있습니다. model.fit()을 호출하면 내부적으로 Stan을 실행해 모수를 추정하고 결과를 Python으로 돌려줍니다.
Stan은 Prophet과 완전히 독립적인 범용 도구입니다. 생태학(개체수 동태 모형), 의학(임상 시험 분석), 경제학(거시 모형) 등 베이지안 추론이 필요한 곳이라면 어디서든 쓰입니다. R에서는 rstan, brms, rstanarm이 Stan을 래핑하는 대표적인 패키지이고, Python에서는 cmdstanpy와 pystan이 인터페이스 역할을 합니다. 최근 Python Prophet은 보통 cmdstanpy 백엔드를 사용하며, 환경과 버전에 따라 CmdStan(독립 실행형 Stan) 설치나 설정이 추가로 필요할 수 있습니다.
왜 Prophet이 Stan을 선택했나. Prophet의 모형은 change point 기울기 $\boldsymbol{\delta}$에 Laplace 사전분포를, 푸리에 계수 $\boldsymbol{\beta}$에 정규 사전분포를 주는 구조입니다. 이처럼 사전분포가 명시적으로 지정된 베이지안 모형은 Stan이 가장 자연스럽게 표현하고 효율적으로 추론합니다. MLE 기반의 statsmodels나 sklearn과는 설계 철학이 다릅니다.
라이선스와 상업적 이용. Prophet은 MIT 라이선스, Stan(CmdStan)은 BSD-3-Clause를 적용합니다. 둘 다 상업적 이용이 자유로운 오픈소스 라이선스입니다. 제품이나 서비스에 Prophet을 내장하거나 사내 예측 시스템에 활용해도 라이선스 제약이 없습니다.4
MAP vs MCMC
Stan은 두 가지 추론 모드를 모두 지원하며, Prophet이 이를 그대로 노출합니다.
기본 (MAP): L-BFGS로 사후 최빈값(Maximum A Posteriori)을 최적화합니다. 빠르고 실전에서 충분합니다.
선택 옵션 (MCMC): HMC-NUTS로 사후 분포 전체를 샘플링합니다. 예측 불확실성 구간이 통계적으로 더 정확해지지만 수십 배 느립니다.
1
2
3
4
5
# 기본: MAP (Stan L-BFGS)
model = Prophet()
# MCMC: Stan HMC-NUTS, 1000 샘플
model = Prophet(mcmc_samples=1000)
추정되는 주요 모수:
| 모수 | 사전분포 | 역할 |
|---|---|---|
| $k$, $m$ | 정규 | 기준 성장률·오프셋 |
| $\boldsymbol{\delta}$ | Laplace$(0, \tau)$ | change point 기울기 변화 |
| $\boldsymbol{\beta}$ | $N(0, \sigma_s^2)$ | 푸리에 계수 |
| $\boldsymbol{\kappa}$ | $N(0, \sigma_h^2)$ | 휴일 효과 |
| $\sigma$ | 양수 제약하에서 추정 | 잔차 표준편차 |
8. 모형 선택 — AIC/BIC 없음, CV로 대체
8.1 왜 CV를 쓰는가
ARIMA의 auto.arima나 ETS의 auto_ets는 AIC/BIC를 기준으로 모형 공간을 탐색합니다. AIC/BIC는 “학습 데이터에서 측정한 적합도 — 복잡도 패널티”를 계산하는데, 이를 위해 모형의 자유 모수 수($k$)가 비교적 명확해야 합니다.
Prophet은 ARIMA/ETS처럼 패키지가 AIC/BIC 기반 자동 모형 선택을 제공하지 않습니다. change point prior, seasonality prior 같은 정규화 요소 때문에 단순한 모수 개수 기반 비교가 덜 직관적이고, 실무적으로는 미래 예측 성능을 직접 보는 rolling-origin CV가 권장됩니다. 물론 정규화 모형에서도 effective degrees of freedom, WAIC, LOO 같은 기준을 생각할 수 있지만, Prophet의 표준 튜닝 흐름은 CV에 가깝습니다.
8.2 Rolling-origin CV 작동 원리
Prophet의 교차검증은 Rolling-origin evaluation (또는 walk-forward validation)입니다. 시간축을 따라 학습 윈도우를 앞으로 밀면서 예측을 반복합니다.
세 파라미터가 윈도우 구조를 결정합니다:
1
2
3
4
5
6
전체 데이터: |←────────────── T ──────────────────────────→|
1회차: |←── initial ──→|←── horizon ──→|
2회차: |←── initial + period ──→|←── horizon ──→|
3회차: |←── initial + 2×period ──→|←── horizon ──→|
...
initial: 첫 번째 학습에 쓸 데이터 길이. 계절성 주기를 충분히 포함해야 하므로 연간 데이터라면 최소 1년, 권장 2년 이상.period: 커트오프(cutoff)를 얼마씩 앞으로 이동할지. 작을수록 평가 윈도우가 많아져 더 안정적이지만 느려집니다.horizon: 각 커트오프에서 얼마나 앞을 예측할지. 실제 운영 예측 지평과 맞추는 것이 원칙입니다.
1
2
3
4
5
6
7
8
9
10
from prophet.diagnostics import cross_validation, performance_metrics
df_cv = cross_validation(
model,
initial='730 days', # 첫 학습: 최소 2년치
period='180 days', # 6개월마다 커트오프 이동
horizon='365 days', # 1년 앞까지 예측 평가
parallel='processes', # 병렬 처리 (데이터가 길면 필수)
)
# df_cv 컬럼: ds, yhat, yhat_lower, yhat_upper, y, cutoff
결과 데이터프레임에는 각 (cutoff, ds) 조합마다 예측값과 실제값이 쌓입니다. 같은 ds 시점이라도 여러 커트오프에서 예측됐다면 여러 행이 생깁니다.
8.3 성능 지표 집계
performance_metrics()는 horizon 기준으로 지표를 집계합니다. “커트오프로부터 30일 뒤 예측은 얼마나 정확한가”, “180일 뒤는?” 식으로 예측 지평에 따른 성능 저하 곡선을 볼 수 있습니다.
1
2
3
4
5
6
df_p = performance_metrics(df_cv)
print(df_p[['horizon', 'mape', 'rmse', 'coverage']].head(10))
# horizon: 예측 지평 (1day, 2days, ...)
# mape : Mean Absolute Percentage Error
# rmse : Root Mean Squared Error
# coverage: 실제값이 80% 예측 구간 안에 드는 비율
rolling_window=1을 주면 horizon별 단일 값, 기본값(0.1)을 쓰면 인접 horizon 구간의 이동평균으로 부드럽게 집계합니다.
8.4 하이퍼파라미터 그리드 서치
CV를 평가 도구로 삼아 후보 조합을 전부 돌려보는 것이 Prophet의 표준 튜닝 절차입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from itertools import product
import numpy as np
param_grid = {
'changepoint_prior_scale': [0.001, 0.01, 0.05, 0.1, 0.5],
'seasonality_prior_scale': [0.1, 1.0, 10.0 ],
'seasonality_mode': ['additive', 'multiplicative'],
}
all_params = [dict(zip(param_grid.keys(), v))
for v in product(*param_grid.values())]
# 총 5 × 3 × 2 = 30 조합
mapes = []
for params in all_params:
m = Prophet(**params).fit(df)
df_cv = cross_validation(m, initial='730 days',
period='180 days', horizon='365 days',
parallel='processes')
df_p = performance_metrics(df_cv, rolling_window=1)
mapes.append(df_p['mape'].mean())
best = all_params[np.argmin(mapes)]
print(f"최적 파라미터: {best}")
print(f"최적 MAPE: {min(mapes):.4f}")
조합이 많으면 시간이 걸립니다. 실용적인 순서는:
seasonality_mode먼저 시각적으로 판단 (계절 진폭 확인)changepoint_prior_scale을 로그 스케일로 탐색 (가장 중요)seasonality_prior_scale은 기본값 10에서 크게 벗어나지 않는 경우가 많음holidays_prior_scale은 holiday 데이터가 있을 때만 튜닝
8.5 주요 하이퍼파라미터 정리
| 파라미터 | 기본값 | 범위 | 역할 |
|---|---|---|---|
changepoint_prior_scale | 0.05 | 0.001–0.5 | 추세 유연성. 클수록 꺾임 많음. 가장 먼저 조정 |
seasonality_prior_scale | 10 | 0.01–10 | 계절성 진폭 허용 범위 |
holidays_prior_scale | 10 | 0.01–10 | 휴일 효과 크기 |
seasonality_mode | 'additive' | — | 가법 vs 승법. 시각 판단 우선 |
n_changepoints | 25 | 5–50 | 후보 change point 수. prior_scale로 조절하는 편이 낫다 |
changepoint_prior_scale이 너무 작으면 추세가 거의 직선에 가까워 실제 변화를 못 따라갑니다. 너무 크면 학습 데이터의 잡음까지 추세로 흡수해 테스트 오차가 올라갑니다. CV MAPE 곡선이 특정 값에서 U자 최저점을 그리면 그게 최적입니다.
9. Prophet 강점이 드러나는 예시 — Peyton Manning Wikipedia 조회수
Air Passengers처럼 단순한 월별 데이터보다, Prophet의 강점이 명확하게 드러나는 예시로 시작합니다. Peyton Manning의 Wikipedia 일별 페이지 조회수(2007–2016)입니다.5
이 데이터에는 Prophet이 잘 다루는 요소가 모두 들어 있습니다:
- 주간 계절성: 일요일(경기일) + 월요일(경기 다음날) 조회수 급등
- 연간 계절성: NFL 시즌(9월–2월) 정점, 비시즌(5월–8월) 저점
- 이벤트: Super Bowl 날 조회수 스파이크
- 추세 change points: 2011년 목 부상·수술, 2012년 덴버 브롱코스 이적, 2014년 터치다운 신기록 등 커리어 이벤트마다 기울기가 꺾임
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import pandas as pd
from prophet import Prophet
# 데이터: Prophet 공식 예시 (log-transformed Wikipedia pageviews)
df = pd.read_csv(
'https://raw.githubusercontent.com/facebook/prophet/main/examples/example_wp_log_peyton_manning.csv'
)
df['ds'] = pd.to_datetime(df['ds'])
train = df[df['ds'] < '2015-01-01']
test = df[df['ds'] >= '2015-01-01']
# Super Bowl 날짜를 holiday로 등록
super_bowls = pd.DataFrame({
'holiday': 'super_bowl',
'ds': pd.to_datetime([
'2008-02-03', '2009-02-01', '2010-02-07', '2011-02-06',
'2012-02-05', '2013-02-03', '2014-02-02', '2015-02-01', '2016-02-07'
]),
'lower_window': 0,
'upper_window': 1,
})
model = Prophet(
holidays=super_bowls,
yearly_seasonality=True,
weekly_seasonality=True,
daily_seasonality=False,
changepoint_prior_scale=0.05,
)
model.fit(train)
future = model.make_future_dataframe(periods=len(test), freq='D')
forecast = model.predict(future)
노란 수직선이 자동 탐지된 change points입니다. 2011년 목 부상(조회수 감소 시작), 2012년 덴버 이적(반등), 2013년 말–2014년 초 기록적 시즌(급등) 근방에 change point가 찍힙니다. 이 시점들은 모형에 명시적으로 알려주지 않았습니다.
컴포넌트 분해 결과를 보면 Prophet이 각 패턴을 어떻게 분리해냈는지 볼 수 있습니다:
- 추세: 커리어 이벤트와 맞물려 기울기가 여러 번 꺾임
- 주간 계절성: 일요일(경기일) > 월요일 > 평일 > 토요일 순으로 명확한 패턴
- 연간 계절성: NFL 시즌 시작인 9월에 급등, 비시즌인 여름에 저점
ARIMA나 ETS는 이 세 가지를 동시에 분리해내기 어렵습니다. ARIMA는 주간 자기상관을 (7,d,q) 차분으로 간접 처리할 수 있지만 연간 계절성과 이벤트를 동시에 다루기 어렵고, ETS는 단일 계절 주기만 지원합니다.
10. Air Passengers 적용 예시
이전 포스트들과 같은 데이터로 비교합니다. Air Passengers는 승법 계절성이 명확하므로 seasonality_mode='multiplicative'를 씁니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import pandas as pd
import numpy as np
import statsmodels.api as sm
from prophet import Prophet
import matplotlib.pyplot as plt
# 데이터 준비
ap = sm.datasets.get_rdataset("AirPassengers", "datasets").data
df = pd.DataFrame({
'ds': pd.date_range('1949-01', periods=144, freq='MS'),
'y': ap['value'].values,
})
train = df[df['ds'] < '1959-01-01']
test = df[df['ds'] >= '1959-01-01']
# 모형 적합
model = Prophet(
seasonality_mode='multiplicative',
yearly_seasonality=True,
weekly_seasonality=False,
daily_seasonality=False,
changepoint_prior_scale=0.05,
)
model.fit(train)
# 예측
future = model.make_future_dataframe(periods=24, freq='MS')
forecast = model.predict(future)
# 예측 시각화
fig, ax = plt.subplots(figsize=(12, 4))
ax.plot(df['ds'], df['y'], color='lightgray', label='실제')
ax.plot(forecast['ds'], forecast['yhat'], label='예측', linewidth=1.8)
ax.fill_between(
forecast['ds'],
forecast['yhat_lower'],
forecast['yhat_upper'],
alpha=0.2, label='80% 예측 구간',
)
ax.axvline(pd.Timestamp('1959-01-01'), color='red', linestyle='--', alpha=0.5, label='학습/테스트 분할')
ax.legend()
ax.set_title('Prophet — Air Passengers 예측')
plt.tight_layout()
plt.savefig('assets/img/posts/fig_prophet_forecast.png', dpi=150)
plt.show()
1
2
3
4
5
# 컴포넌트 분해 시각화
fig = model.plot_components(forecast)
plt.tight_layout()
plt.savefig('assets/img/posts/fig_prophet_components.png', dpi=150)
plt.show()
plot_components()는 추세, 연간 계절성을 분리해 보여줍니다. 추세 그래프에서 수직 점선으로 표시되는 자동 탐지 change points도 확인할 수 있습니다.
1
2
3
4
5
# 24개월 예측 MAPE
pred = forecast[forecast['ds'].isin(test['ds'])][['ds', 'yhat']]
merged = test.merge(pred, on='ds')
mape = np.mean(np.abs((merged['y'] - merged['yhat']) / merged['y'])) * 100
print(f"MAPE: {mape:.2f}%")
| 모형 | MAPE (24개월) |
|---|---|
| SARIMA(0,1,1)(0,1,1)₁₂ | 8.52% |
| Holt-Winters (수동) | 6.39% |
| ETS (자동) | ~6% |
| Prophet (multiplicative) | 5.35% |
Air Passengers는 이벤트·휴일 효과가 없는 단순한 교과서 데이터입니다. Prophet의 강점인 복수 계절성과 휴일 처리가 드러나지 않으므로 수치 비교보다 모형 구조 파악에 집중하는 것이 좋습니다.
11. ARIMA·ETS와의 비교
결정적 차이: 잔차 자기상관
ARIMA는 잔차 자기상관을 핵심으로 모델링합니다. ETS는 상태공간 구조에서 간접적으로 처리합니다. Prophet은 기본 관측 오차를 독립 오차로 두며 ARMA식 잔차 자기상관 구조를 포함하지 않습니다. 적합 후 잔차에 ACF를 그려 패턴이 남아 있으면 Prophet 단독으로는 포착하지 못하는 구조입니다.
왜 “$t$를 독립변수로 쓰는 회귀”이면 잔차 자기상관을 못 잡는가
Prophet의 예측 함수는 타임스탬프 $t$만 보고 예측값을 만듭니다:
\[\hat{y}(t) = g(t) + s(t) + h(t)\]이 함수는 어제 잔차가 얼마였는지, 그제 값이 얼마였는지 전혀 참조하지 않습니다. 반면 ARIMA(1,0,0)의 예측 함수는:
\[\hat{y}_t = \phi_1 y_{t-1} + \text{const}\]어제 관측값 $y_{t-1}$ 을 직접 입력으로 받습니다.
잔차 자기상관이 있다는 것은 예컨대 $\epsilon_t = 0.8\,\epsilon_{t-1} + \text{white noise}$ 같은 구조가 남아 있다는 뜻입니다. 이 패턴을 활용하려면 모형이 “어제 잔차가 얼마였는지”를 알아야 합니다. Prophet은 $t$만 입력으로 받으므로 그 경로 자체가 없습니다. 잔차에 패턴이 남아 있어도 다음 예측에 반영할 방법이 없습니다.
| 예측 함수의 입력 | 잔차 패턴 활용 | |
|---|---|---|
| ARIMA | $y_{t-1},\, y_{t-2},\, \epsilon_{t-1}, \ldots$ | 가능 |
| Prophet | $t$ (타임스탬프) | 불가 |
이건 Prophet만의 문제가 아닙니다. 시간을 독립변수로 쓰는 회귀는 모두 같은 한계를 가집니다. 단순 선형 회귀 $y = \beta_0 + \beta_1 t + \epsilon$도 잔차에 자기상관이 있으면 예측이 비효율적(inefficient)입니다. ARIMA는 그 남은 패턴까지 쓰는 모형입니다.
계절성 표현
기본 SARIMA는 단일 계절 차분, 기본 ETS는 단일 계절 컴포넌트를 중심으로 다루는 반면, Prophet은 복수 계절 주기를 동시에 처리합니다. 물론 SARIMAX에 Fourier term을 넣거나 TBATS/MSTL 같은 확장을 쓰면 다른 접근에서도 복수 계절성을 다룰 수 있습니다.
동치 관계
일부 선형 additive ETS와 ARIMA 사이에는 예측식 수준의 연결이 있습니다(예: 일정 조건하에서 SES = ARIMA(0,1,1), Holt’s = ARIMA(0,2,2)). Prophet과 이들 사이에는 공식적인 동치 관계가 없습니다. 같은 데이터를 다루지만 패러다임이 다른 모형입니다.
ARIMA / ETS
- 과거 $y$ 값의 점화식
- 잔차 자기상관 모델링 (ARIMA)
- 단일 계절 주기
- AIC/BIC 자동 선택
- 결측·불규칙 간격에 약함
Prophet
- 시간 $t$를 입력으로 받는 회귀
- 잔차 자기상관 미처리
- 복수 계절 주기 동시 지원
- CV 기반 수동 튜닝
- 결측·불규칙 간격에 강함
12. Prophet이 다루지 않는 영역
| 영역 | 대안 |
|---|---|
| 잔차 자기상관 | ARIMA on residuals |
| 조건부 이분산·변동성 클러스터링 | GARCH |
| 다변량 의존성 | VAR, VARMA |
| 비가우시안 오차 | GLM 계열 시계열 |
| 계절 패턴 자체의 구조적 변화 | STL + 동적 모형 |
실전에서 가장 자주 마주치는 한계는 잔차 자기상관입니다. 이는 Prophet과 ARIMA를 결합해 해결할 수 있습니다.
13. 앙상블 전략
Prophet + ARIMA on residuals
점예측 앙상블로는 간단하지만, Prophet과 ARIMA 잔차 모델의 예측 불확실성을 어떻게 결합할지는 별도로 다뤄야 합니다. 따라서 아래 방식은 우선 점예측 개선 아이디어로 이해하는 편이 안전합니다.
Prophet이 추세·계절·이벤트를 제거한 뒤 남은 잔차를 ARIMA로 추가 모델링합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
from statsmodels.tsa.arima.model import ARIMA
# 1단계: Prophet 학습 구간 적합값
forecast_train = forecast[forecast['ds'].isin(train['ds'])]
residuals = train['y'].values - forecast_train['yhat'].values
# 2단계: 잔차를 ARIMA로 모델링
arima_res = ARIMA(residuals, order=(1, 0, 1)).fit()
arima_fcast = arima_res.forecast(steps=24)
# 3단계: Prophet 예측 + ARIMA 잔차 예측 합산
forecast_test = forecast[forecast['ds'].isin(test['ds'])]['yhat'].values
final = forecast_test + arima_fcast
Prophet + LightGBM
Prophet의 컴포넌트 분해 결과($\hat{g}$, $\hat{s}$, $\hat{h}$)를 피처로 ML 모형에 투입하는 패턴입니다. Kaggle 시계열 대회 상위권 솔루션에 자주 등장합니다.
단순 평균 앙상블
1
final = (prophet_forecast + ets_forecast + arima_forecast) / 3
모형별 가정이 달라 각자 포착하는 패턴이 다르기 때문에, 단순 평균만으로도 개별 모형보다 안정적인 예측이 나오는 경우가 많습니다.
14. 언제 Prophet을 쓸까
잘 맞는 경우:
- 1년 이상의 일별 또는 시간별 데이터
- 연간·주간·일간이 겹치는 복수 계절성
- 연휴·프로모션 등 알려진 이벤트 효과
- 추세 꺾임이 발생한 이력이 있는 데이터
- 결측치나 불규칙 시간 간격
주의가 필요한 경우:
- 금융 수익률처럼 자기상관이 지배적인 데이터
- 학습 데이터가 수십 개 수준으로 짧은 경우 (change point 탐지 불안정)
- 변동성 예측이 목적인 경우 (Prophet은 평균 예측만 제공)
- 다변량 인과 관계가 핵심인 경우
이전 포스트들의 흐름과 연결 지으면: ARIMA는 자기상관 구조를, ETS는 지수가중 분해를, Prophet은 회귀 분해를 핵심 아이디어로 삼습니다. 세 접근법은 서로 경쟁하기도 하지만 각자가 잘 다루는 시계열의 결이 다릅니다. 앙상블을 통해 보완적으로 쓰는 것이 실전에서 더 일반적입니다.
참고문헌
- Taylor, S. J., & Letham, B. (2018). Forecasting at scale. The American Statistician, 72(1), 37–45. DOI: 10.1080/00031305.2017.1380080
- Carpenter, B., Gelman, A., Hoffman, M. D., Lee, D., Goodrich, B., Betancourt, M., Brubaker, M., Guo, J., Li, P., & Riddell, A. (2017). Stan: A probabilistic programming language. Journal of Statistical Software, 76(1), 1–32.
- Hyndman, R. J., & Athanasopoulos, G. (2021). Forecasting: Principles and Practice (3rd ed.). OTexts.
AI의 도움을 받아 작성되었으며 최대한 레퍼런스를 밝히려 노력했으나 오류가 있을 수 있으니 정확한 정보를 다시 한번 확인하시기 바랍니다.
Taylor, S. J., & Letham, B. (2018). Forecasting at scale. The American Statistician, 72(1), 37–45. DOI: 10.1080/00031305.2017.1380080. 원래 2017년 PeerJ Preprints로 먼저 공개됐으며, Facebook 내부의 수백 개 시계열 예측 파이프라인을 자동화한 경험에서 출발한 논문입니다. ↩︎
Change point 탐지의 상세 수식 유도는 Taylor & Letham(2018) Appendix를 참고하세요. ↩︎
Carpenter, B., Gelman, A., Hoffman, M. D., Lee, D., Goodrich, B., Betancourt, M., Brubaker, M., Guo, J., Li, P., & Riddell, A. (2017). Stan: A probabilistic programming language. Journal of Statistical Software, 76(1), 1–32. DOI: 10.18637/jss.v076.i01. Stan 공식 사이트: mc-stan.org. ↩︎
Prophet GitHub: github.com/facebook/prophet (MIT License). Stan(CmdStan) GitHub: github.com/stan-dev/cmdstan (BSD-3-Clause). 두 라이선스 모두 저작권 표시를 유지하는 조건 하에 복사·수정·배포·상업적 이용이 자유롭습니다. ↩︎
Peyton Manning Wikipedia 조회수 데이터는 Prophet 공식 GitHub 예시 데이터셋입니다.
y값은 자연로그 변환된 조회수입니다. ↩︎




