아프리카의 $700B 부채 위기를 모니터링하기 위해 ML 플랫폼을 구축했습니다 - 배운 점
Source: Dev.to
문제: 7천억 달러 규모의 블라인드 스팟
아프리카 9개국이 현재 부채 위기에 처해 있습니다. 대륙 전체 주권 부채는 7천억 달러를 넘어가며, 몇몇 국가에서는 부채 서비스가 정부 수입의 40 % 이상을 차지합니다.
2022년 붕괴는 많은 사람을 놀라게 했습니다: 가나는 “관리 가능한 부채 수준”에서 18개월도 채 되지 않아 주권 디폴트에 빠졌습니다. 잠비아, 모잠비크, 에티오피아도 유사한 궤적을 보였습니다.
핵심 문제: 전통적인 모니터링은 후행 지표에 의존합니다. IMF가 한 국가를 “고위험”으로 표시할 때는 예방 조치를 취하기엔 이미 늦은 경우가 많습니다.
머릿속에 떠오른 질문: 머신러닝이 더 이른 경고 신호를 제공할 수 있을까?
내가 만든 것
Africa‑Debt‑Intelligence는 실시간 주권 부채 위험 모니터링 플랫폼으로:
- IMF World Economic Outlook와 World Bank International Debt Statistics에서 재정 데이터를 집계
- ML 클러스터링 및 시계열 분석을 활용해 위험 점수(0‑100 척도) 생성
- 신뢰 구간을 포함한 5년 뒤 부채 궤적 예측
- 각 국가의 위험 프로필에 맞춘 정책 권고 제공
- 재정 지표가 임계값을 초과하면 실시간 알림 발송
현재 이 플랫폼은 15개 사하라 이남 아프리카 경제를 모니터링하고 있으며, 이는 지역 GDP의 **85 %**에 해당합니다.
GitHub Repository:
Tech Stack: Python, React, scikit‑learn, pandas, REST APIs
기술 아키텍처
데이터 파이프라인
공공 API에서 자동으로 데이터 수집:
def load_and_clean_data(filepath: str) -> pd.DataFrame:
"""
Load long‑format fiscal data and perform cleaning operations.
"""
df = pd.read_csv(filepath)
# Convert time to year format
df['Year'] = pd.to_datetime(df['Time']).dt.year
# Handle missing values with forward fill + interpolation
df = df.groupby(['Country', 'Indicator']).apply(
lambda x: x.interpolate(method='linear')
).reset_index(drop=True)
# Normalize fiscal indicators to % of GDP
gdp_data = df[df['Indicator'] == 'GDP'][['Country', 'Year', 'Amount']]
gdp_data = gdp_data.rename(columns={'Amount': 'GDP'})
df = df.merge(gdp_data, on=['Country', 'Year'], how='left')
# Create normalized ratios
indicators_to_normalize = ['External_Debt', 'Revenue', 'Expenditure', 'Deficit']
for ind in indicators_to_normalize:
mask = df['Indicator'] == ind
df.loc[mask, 'Normalized_Value'] = (
df.loc[mask, 'Amount'] / df.loc[mask, 'GDP'] * 100
)
return df
추적하는 주요 지표
- 부채‑대‑GDP 비율
- 재정 균형 (% GDP)
- 수입‑대‑GDP 비율
- 부채 서비스 비율
- GDP 성장률
- 인플레이션율
- 외채 노출액
- 외환보유고(수입 개월수)
위험 점수 모델
비지도 학습과 도메인‑특화 가중치를 결합:
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
def generate_risk_scores(df: pd.DataFrame) -> pd.DataFrame:
"""
Generate composite risk scores using K‑means clustering
and weighted fiscal indicators.
"""
features = [
'Debt_to_GDP', 'Fiscal_Balance', 'Revenue_to_GDP',
'Debt_Service_Ratio', 'GDP_Growth', 'Inflation'
]
# Standardize features
scaler = StandardScaler()
X_scaled = scaler.fit_transform(df[features])
# K‑means clustering to identify risk groups
kmeans = KMeans(n_clusters=4, random_state=42)
df['Risk_Cluster'] = kmeans.fit_predict(X_scaled)
# Weighted composite score
weights = {
'Debt_to_GDP': 0.25,
'Debt_Service_Ratio': 0.25,
'Fiscal_Balance': 0.20,
'Revenue_to_GDP': 0.15,
'GDP_Growth': 0.10,
'Inflation': 0.05
}
df['Risk_Score'] = sum(
df[feature] * weight
for feature, weight in weights.items()
)
# Normalize to 0‑1 scale
df['Risk_Score'] = (
(df['Risk_Score'] - df['Risk_Score'].min()) /
(df['Risk_Score'].max() - df['Risk_Score'].min())
)
return df
위험 임계값
- 0.00‑0.40 – 낮은 위험 (녹색)
- 0.41‑0.60 – 중간 위험 (노란색)
- 0.61‑0.75 – 높은 위험 (주황색)
- 0.76‑1.00 – 위기 위험 (빨간색)
시계열 예측
ARIMA 모델을 사용해 5년간 부채‑대‑GDP 예측을 신뢰 구간과 함께 생성:
from statsmodels.tsa.arima.model import ARIMA
def forecast_debt_trajectory(country_data: pd.DataFrame,
periods: int = 20) -> dict:
"""
Generate 5‑year debt‑to‑GDP forecast with confidence intervals.
"""
model = ARIMA(country_data['Debt_to_GDP'], order=(2, 1, 2))
fitted_model = model.fit()
forecast = fitted_model.forecast(steps=periods)
conf_int = fitted_model.get_forecast(steps=periods).conf_int()
return {
'forecast': forecast,
'lower_bound': conf_int.iloc[:, 0],
'upper_bound': conf_int.iloc[:, 1]
}
직면한 도전 과제
도전 과제 1: 데이터 품질 지옥
아프리카 거시경제 데이터는 자주 수정되거나 불규칙적이며 누락되는 경우가 많습니다.
예시: 가나의 부채‑대‑GDP 비율이 2023년에 소급적으로 15 포인트 상승해 재작성되면서 과거 모습이 크게 바뀌었습니다.
해결 방안
- IMF, World Bank, AfDB 등 다중 소스와 교차 검증
- 누락된 분기 데이터를 보간
- 신뢰 수준을 표시하는 데이터‑품질 플래그 추가
- 이상치를 수동으로 점검
도전 과제 2: “위험” 정의하기
위험 점수를 어떻게 해석하고 검증할 것인가?
해결 방안
- 2000‑2023년 사이의 역사적 부채 위기 사례에 대해 백테스트 수행
- 점수 > 0.70이 실제 위기 10건 중 8건을 사전에 포착함을 확인
- 평균 선행 시간: 14개월 (위기 발생 전)
- 예측과 실제 결과를 비교한 혼동 행렬 구축
역사적 검증 결과
- 가나 2022: 18개월 전(점수 0.82) 경고
- 잠비아 2020: 16개월 전(점수 0.79) 경고
- 모잠비크 2016: 12개월 전(점수 0.75) 경고
도전 과제 3: 해석 가능하게 만들기
정책 입안자는 왜 국가가 경고되는지 이해해야 합니다.
해결 방안
- 위험 점수의 주요 원인을 보여주는 특성 중요도 분석
- 각 요인의 기여도 분해 제공
- 특정 취약점에 맞춘 정책 권고서 작성
- 예시 문구: “부채 서비스가 수입의 62 %를 차지해 위험이 상승했습니다.”
도전 과제 4: 데이터 최신성 유지
API가 지연되거나 실패할 수 있으며, 수동 입력은 확장성이 없습니다.
해결 방안
- 월간 자동 ETL 파이프라인 운영
- API 실패 시 캐시된 데이터로 대체
- 대시보드에 데이터 최신성 표시 지표 제공
