๋จธ์ ๋ฌ๋ ๋ชจ๋ธ ๊ตฌ์ถ ๊ณผ์ ๋ถ์ - ์์ธก ์ ํ๋ ๊ฐ์ ์๋์ ํ๊ณ์ ๋์ถ
# ์๋๋ฆฌ์ค 1: 2023๋
ํ์ต โ 2024๋
์์ธก
- ํ์ต ๋ฐ์ดํฐ: 2023๋
12๊ฐ์
- ์์ธก ๋์: 2024๋
12๊ฐ์
- ๊ฒ์ฆ ๋ฐฉ๋ฒ: 2024๋
์ค์ ๋ฐ์ดํฐ์ ๋น๊ต (ํ์ฌ ๊ฐ๋ฅ)
# ์๋๋ฆฌ์ค 2: 2023-2024๋
ํ์ต โ 2025๋
์์ธก
- ํ์ต ๋ฐ์ดํฐ: 2023-2024๋
24๊ฐ์
- ์์ธก ๋์: 2025๋
12๊ฐ์
- ๊ฒ์ฆ ๋ฐฉ๋ฒ: 2025๋
์ค์ ๋ฐ์ดํฐ์ ๋น๊ต (์งํ ์ค)
# ๊ธฐ๋ณธ ๊ฐ์
- 3๋
๊ฐ์ ๋ฐ์ดํฐ๋ ํจํด ํ์ต์ ์ถฉ๋ถ
- ๊ณผ๊ฑฐ ๋ฐ์ดํฐ ํจํด์ด ๋ฏธ๋ ์์ธก์ ์ ํจ
- ๋จธ์ ๋ฌ๋ ์๊ณ ๋ฆฌ์ฆ์ ํตํ ์ ํ๋ ํฅ์ ๊ฐ๋ฅ
- ๋ค์ค ์๋๋ฆฌ์ค ๊ฒ์ฆ์ ํตํ ๋ชจ๋ธ ์ ๋ขฐ์ฑ ํ๋ณด
๊ตฌํ ํ์ผ: 04_aws_budget_prediction.py
# Athena์์ CSM ๋ฐ์ดํฐ ์กฐํ
query = """
SELECT year, month, usageaccountid, productcode,
SUM(unblendedcost) as monthly_cost,
SUM(amortized_cost) as amortized_monthly_cost
FROM awsbilling.summary_view
WHERE year >= '2023'
GROUP BY year, month, usageaccountid, productcode
ORDER BY year, month
"""
# ์๋ณ ์ด ๋น์ฉ ์ง๊ณ ๋ฐ ์๊ณ์ด ๋ฐ์ดํฐ ์์ฑ
monthly_totals = df.groupby('date')['monthly_cost'].sum().reset_index()
monthly_totals = monthly_totals.sort_values('date')
# ๊ธฐ๋ณธ ์๊ณ์ด ํน์ฑ
monthly_totals['month_index'] = range(len(monthly_totals)) # ์์ฐจ ์ธ๋ฑ์ค
monthly_totals['month_num'] = monthly_totals['date'].dt.month # ์ (1-12)
monthly_totals['quarter'] = monthly_totals['date'].dt.quarter # ๋ถ๊ธฐ (1-4)
# ์ด๋ํ๊ท ํน์ฑ (ํธ๋ ๋ ํ์
)
monthly_totals['ma_3'] = monthly_totals['monthly_cost'].rolling(3).mean() # 3๊ฐ์ ์ด๋ํ๊ท
monthly_totals['ma_6'] = monthly_totals['monthly_cost'].rolling(6).mean() # 6๊ฐ์ ์ด๋ํ๊ท
# ๋ชจ๋ธ ์ ์
models = {
'rf': RandomForestRegressor(n_estimators=100, random_state=42),
'lr': LinearRegression()
}
# ํน์ฑ ๋ณ์ ์ ํ
features = ['month_index', 'month_num', 'quarter', 'ma_3', 'ma_6']
X = train_data[features].fillna(method='bfill')
y = train_data['monthly_cost']
# ๋ชจ๋ธ ํ์ต
for name, model in models.items():
model.fit(X, y)
# 2025๋
์์ธก์ ์ํ ํน์ฑ ์์ฑ
future_months = pd.date_range(start='2025-01-01', periods=12, freq='MS')
future_features = []
for i, date in enumerate(future_months):
# ๊ณผ๊ฑฐ ๋ฐ์ดํฐ ๊ธฐ๋ฐ ์ด๋ํ๊ท ๊ณ์ฐ
recent_costs = train_data['monthly_cost'].tail(6).values
ma_3 = np.mean(recent_costs[-3:]) if len(recent_costs) >= 3 else recent_costs.mean()
ma_6 = np.mean(recent_costs) if len(recent_costs) >= 6 else recent_costs.mean()
future_features.append({
'month_index': len(train_data) + i,
'month_num': date.month,
'quarter': date.quarter,
'ma_3': ma_3,
'ma_6': ma_6
})
# ์์ธก ์คํ
predictions = {}
for name, model in models.items():
pred = model.predict(future_X)
predictions[name] = pred
๊ฒ์ฆ ํ์ผ: 05_real_budget_prediction.py | 07_model_validation.py
| ์ | ์์ธก๊ฐ | ์ค์ ๊ฐ | ์ค์ฐจ์จ |
|---|---|---|---|
| 2025-01 | โโโโโโโโ | โโโโโโโโ | +20.5% |
| 2025-02 | โโโโโโโโ | โโโโโโโโ | +51.9% |
| 2025-03 | โโโโโโโโ | โโโโโโโโ | +38.2% |
| 2025-04 | โโโโโโโโ | โโโโโโโโ | +46.1% |
| 2025-05 | โโโโโโโโ | โโโโโโโโ | +42.7% |
๊ฐ์ ํ์ผ: 09_improved_budget_model.py
# ๋ค์ํ ์๋์ฐ ์ด๋ํ๊ท
df['ma_2'] = df['total_cost'].rolling(2, min_periods=1).mean() # 2๊ฐ์
df['ma_3'] = df['total_cost'].rolling(3, min_periods=1).mean() # 3๊ฐ์
df['ma_6'] = df['total_cost'].rolling(6, min_periods=1).mean() # 6๊ฐ์
# ๋ณํ์จ ํน์ฑ
df['mom_change'] = df['total_cost'].pct_change() # ์๊ฐ ๋ณํ์จ
df['mom_change_ma3'] = df['mom_change'].rolling(3, min_periods=1).mean() # ๋ณํ์จ ํํํ
# ์ผ๊ฐํจ์๋ฅผ ์ด์ฉํ ๊ณ์ ์ฑ ํํ
df['sin_month'] = np.sin(2 * np.pi * df['month_num'] / 12)
df['cos_month'] = np.cos(2 * np.pi * df['month_num'] / 12)
df['sin_quarter'] = np.sin(2 * np.pi * df['quarter'] / 4)
df['cos_quarter'] = np.cos(2 * np.pi * df['quarter'] / 4)
# ์ด์ : 12์๊ณผ 1์์ ์ฐ์์ฑ์ ์ํ์ ์ผ๋ก ํํ
# sin/cos ์กฐํฉ์ผ๋ก ์ํ ์๊ฐ ๊ตฌ์กฐ ๋ชจ๋ธ๋ง
# ๋ณ๋์ฑ ์ธก์
df['volatility_3m'] = df['total_cost'].rolling(3, min_periods=1).std()
df['volatility_6m'] = df['total_cost'].rolling(6, min_periods=1).std()
# ํธ๋ ๋ ๋ถ์
df['linear_trend'] = np.arange(len(df))
df['detrended_cost'] = df['total_cost'] - (df['linear_trend'] * df['total_cost'].diff().mean())
# ๋น์ฆ๋์ค ํน์ฑ (๋๋ฉ์ธ ์ง์ ๋ฐ์)
df['is_q1'] = (df['quarter'] == 1).astype(int) # ์ ๋
์์ฐ ์ฆ๊ฐ ํจ๊ณผ
df['is_q4'] = (df['quarter'] == 4).astype(int) # ์ฐ๋ง ์ง์ถ ์ฆ๊ฐ ํจ๊ณผ
df['is_year_end'] = (df['month_num'] >= 11).astype(int) # ์ฐ๋ง ํจ๊ณผ
class ImprovedBudgetPredictor:
def __init__(self):
self.models = {
'rf': RandomForestRegressor(n_estimators=200, random_state=42),
'gb': GradientBoostingRegressor(n_estimators=100, random_state=42),
'lr': LinearRegression()
}
# ๊ฐ์ค ํ๊ท ์ ์ํ ๊ฐ์ค์น ์ค์
self.weights = {'rf': 0.4, 'gb': 0.4, 'lr': 0.2}
def predict_ensemble(self, X):
"""์์๋ธ ์์ธก"""
predictions = {}
for name, model in self.models.items():
predictions[name] = model.predict(X)
# ๊ฐ์ค ํ๊ท ๊ณ์ฐ
ensemble_pred = (
predictions['rf'] * self.weights['rf'] +
predictions['gb'] * self.weights['gb'] +
predictions['lr'] * self.weights['lr']
)
return ensemble_pred
๊ฒ์ฆ ํ์ผ: 07_model_validation.py
# ์ค์ vs ์์ธก ๋น๊ต (2025๋
1-5์)
actual_2025 = [
{"month": "01", "actual_cost": 731337.61},
{"month": "02", "actual_cost": 576939.76},
{"month": "03", "actual_cost": 616179.57},
{"month": "04", "actual_cost": 607998.79},
{"month": "05", "actual_cost": 592674.50}
]
predictions_2025 = [
{"month": "01", "predicted_cost": 881207},
{"month": "02", "predicted_cost": 876152},
{"month": "03", "predicted_cost": 851643},
{"month": "04", "predicted_cost": 888317},
{"month": "05", "predicted_cost": 845591}
]
# ์ค์ฐจ ๊ณ์ฐ
for i in range(5):
actual = actual_2025[i]['actual_cost']
predicted = predictions_2025[i]['predicted_cost']
error = abs(predicted - actual) / actual * 100
print(f"2025-{i+1:02d}: {error:.1f}% ์ค์ฐจ")
๋์๋ณด๋ ํ์ผ: 13_QuickSight_Dashboard_Guide.md | 14_create_quicksight_dashboard.py
# QuickSight ๋์๋ณด๋ ์๋ ์์ฑ
def create_budget_dashboard():
quicksight = boto3.client('quicksight')
# ๋ฐ์ดํฐ์
์์ฑ
response = quicksight.create_data_set(
AwsAccountId=account_id,
DataSetId='budget-prediction-dataset',
Name='Budget Prediction Analysis',
PhysicalTableMap={
'budget-table': {
'S3Source': {
'DataSourceArn': athena_datasource_arn,
'InputColumns': [
{'Name': 'date', 'Type': 'DATETIME'},
{'Name': 'actual_cost', 'Type': 'DECIMAL'},
{'Name': 'predicted_cost', 'Type': 'DECIMAL'}
]
}
}
}
)
# ์๊ฐํ ์์ฑ (์๊ณ์ด ์ฐจํธ, ์ค์ฐจ ๋ถ์ ๋ฑ)
return create_visualizations()
๋จธ์ ๋ฌ๋์ ๋ง๋ฅ ํด๊ฒฐ์ฑ ์ด ์๋๋ฉฐ, ๋ฐ์ดํฐ ํ์ง, ๋น์ฆ๋์ค ์ปจํ ์คํธ, ํ์ค์ ๊ธฐ๋์น ์ค์ ์ด ํ๋ก์ ํธ ์ฑ๊ณต์ ํต์ฌ ์์์ ๋๋ค. ๊ฒฝ์ฐ์ ๋ฐ๋ผ ๋จ์ํ ๊ท์น ๊ธฐ๋ฐ ์ ๊ทผ๋ฒ์ด ๋ณต์กํ ML ๋ชจ๋ธ๋ณด๋ค ๋ ์ค์ฉ์ ์ผ ์ ์์ต๋๋ค.
# ์ฒ์๋ถํฐ ์ฐ๋ฆฌ ๋ฐ์ดํฐ๋ก ํ์ต
features = ['month_index', 'month_num', 'quarter', 'ma_3', 'ma_6']
model = RandomForestRegressor()
model.fit(X, y) # ์ฒ์๋ถํฐ ํ์ต
# ์ด๋ฏธ ํ๋ จ๋ ๋ชจ๋ธ์ ๊ฐ์ ธ์์ ์ถ๊ฐ ํ์ต
pretrained_model = load_model("gpt-3.5")
fine_tuned_model = pretrained_model.fine_tune(my_data)
| ์๋ชป๋ ์ดํด | ์ค์ ์ํฉ |
|---|---|
| "23๋ โ24๋ ๋ณํ๋ฅผ ํ์ต" | "์๋ณ, ๊ณ์ ๋ณ ํจํด์ ํ์ต" |
| "์ฐ๋๋ณ ์ฐจ์ด๋ฅผ ํ์ต" | "ํน์ฑ๊ณผ ๊ฒฐ๊ณผ์ ๊ด๊ณ๋ฅผ ํ์ต" |
# ์๋ชป๋ ๊ธฐ๋
"ML์ด ๋ฏธ๋๋ฅผ ์ ํํ ์์ธกํด์ค ๊ฒ์ด๋ค"
# ์ฌ๋ฐ๋ฅธ ๊ธฐ๋
"ML์ด ๊ณผ๊ฑฐ ํจํด์ ๋ถ์ํด์ ์ฐธ๊ณ ์๋ฃ๋ฅผ ์ ๊ณตํด์ค ๊ฒ์ด๋ค"
"์ ๋ฌธ๊ฐ๊ฐ ๋ ๋์ ํ๋จ์ ํ ์ ์๋๋ก ๋์์ ์ค ๊ฒ์ด๋ค"
# CLI (ํ์ฌ)
python predict.py --data costs.csv
# ์น ์๋น์ค (๋ชฉํ)
1. ๋ธ๋ผ์ฐ์ ์์ ํ์ผ ์
๋ก๋
2. "์์ธกํ๊ธฐ" ๋ฒํผ ํด๋ฆญ
3. ์ฐจํธ๋ก ๊ฒฐ๊ณผ ํ์ธ
# ์ฌ์ ์ ์๋ ์ด๋ฒคํธ ํ์
EVENTS = {
'service_shutdown': {
'name': '์๋น์ค ์ข
๋ฃ',
'impact': 'negative',
'inputs': ['date', 'monthly_cost']
},
'service_launch': {
'name': '์ ๊ท ์๋น์ค',
'impact': 'positive',
'inputs': ['date', 'expected_cost']
}
}
# ์ฌ์ฉ์ ์
๋ ฅ
event_type = "service_shutdown"
shutdown_date = "2025-10-01"
monthly_savings = 50000
# ์๋ ์ ์ฉ
for month in prediction_timeline:
if month >= shutdown_date:
prediction[month] -= monthly_savings
| ์ ๊ทผ๋ฒ | ์ฅ์ | ํ๊ณ |
|---|---|---|
| ML๋ง ์ฌ์ฉ | ๊ณผ๊ฑฐ ํจํด ํ์ต ๊ฐ๊ด์ ๋ถ์ |
๋ฏธ๋ ๋ณํ ์์ธก ๋ถ๊ฐ ๋น์ฆ๋์ค ์ปจํ ์คํธ ๋ฌด์ |
| ์ ๋ฌธ๊ฐ ์ง๊ฐ๋ง | ๋น์ฆ๋์ค ์ดํด ๋ฏธ๋ ๋ณํ ๊ณ ๋ ค |
์ฃผ๊ด์ ํธํฅ ์ผ๊ด์ฑ ๋ถ์กฑ |
| ํ์ด๋ธ๋ฆฌ๋ ์ ๊ทผ | ํจํด + ๋น์ฆ๋์ค ์ง์ ํฌ๋ช ํ ์กฐ์ ๊ณผ์ |
๋ณต์ก์ฑ ์ฆ๊ฐ ์ ์ง๋ณด์ ํ์ |
# ML ์์ธก: $85,000 (๊ณผ๊ฑฐ ํจํด ๊ธฐ๋ฐ)
# ๋น์ฆ๋์ค ์กฐ์ : -$50,000 (A ์๋น์ค ์ข
๋ฃ)
# ์ต์ข
์์ธก: $35,000
# ์ค๋ช
: "๊ณผ๊ฑฐ ํจํด์ผ๋ก๋ $85K์ด์ง๋ง,
# A ์๋น์ค ์ข
๋ฃ๋ก $50K ์ ์ฝ ์์"
๊ธฐ์กด ML ๋ชจ๋ธ์ ํ๊ณ๋ฅผ ๋๋ผ๊ณ "์ ์๊ณ์ด ์ ์ฉ ๋ชจ๋ธ์ ์ ์ผ์๊น?"๋ผ๋ ์๋ฌธ์์ ์์๋ ์คํ์ ๋๋ค.
| ์์ | ๋ชจ๋ธ | ํ๊ท ์ค์ฐจ์จ | ํน์ง |
|---|---|---|---|
| ๐ 1์ | ์ต์ ํ๋ Prophet | 14.6% | ๋จ์ํจ + ์๊ณ์ด ์ ์ฉ |
| 2์ | ๊ธฐ์กด ML (RF+GB+LR) | 35.2% | ๋ณต์กํ ํน์ฑ ์์ง๋์ด๋ง |
| 3์ | ๊ธฐ๋ณธ Prophet | 57.5% | ๊ณผ์ ํฉ๋ ๋ณต์ก ๋ชจ๋ธ |
# ํต์ฌ: ๋จ์ํจ์ด ์น๋ฆฌ
model = Prophet(
changepoint_prior_scale=0.01, # ํธ๋ ๋ ๋ณํ ๋ฏผ๊ฐ๋ ๋ฎ์ถค
seasonality_prior_scale=1.0, # ๊ณ์ ์ฑ ๊ฐ๋ ์กฐ์
seasonality_mode='additive' # ๋จ์ํ ๋ง์
๋ชจ๋
)
# ์ต์ํ์ ๋น์ฆ๋์ค ํน์ฑ๋ง
model.add_regressor('is_year_end') # ์ฐ๋ง ํจ๊ณผ
model.add_regressor('is_q1') # ์ ๋
ํจ๊ณผ
๊ตฌํ ํ์ผ: prophet_budget_prediction_v2.py
# ์ต์ ํ๋ Prophet ํต์ฌ ์ฝ๋
def build_simple_prophet_model(df):
model = Prophet(
yearly_seasonality=True,
weekly_seasonality=False,
daily_seasonality=False,
changepoint_prior_scale=0.01, # ๊ณผ์ ํฉ ๋ฐฉ์ง
seasonality_prior_scale=1.0, # ๊ณ์ ์ฑ ์กฐ์
seasonality_mode='additive'
)
# ํต์ฌ ํน์ฑ๋ง
model.add_regressor('is_year_end')
model.add_regressor('is_q1')
return model
# ์์ธก ์คํ
model.fit(df)
future = model.make_future_dataframe(periods=12, freq='MS')
forecast = model.predict(future)
# ์๋ชป๋ ์์ (์ฐ๋ฆฌ๊ฐ ํ ๊ฒ)
1. ์ ํต์ ML๋ถํฐ ์์
2. ๋ณต์กํ ํน์ฑ ์์ง๋์ด๋ง (19๊ฐ)
3. ์์๋ธ ๋ชจ๋ธ ๊ตฌ์ถ
4. ์ฑ๋ฅ ๋ถ์กฑ โ ๋ ๋ณต์กํ ๊ธฐ๋ฒ ์๋
# ์ฌ๋ฐ๋ฅธ ์์ (Prophet ์คํ ํ)
1. Prophet ์๋ โ 14.6% ์ค์ฐจ ๋ฌ์ฑ โ
2. ์ค๋ฌด ๊ธฐ์ค ๋ง์กฑ โ ์๋ฃ
3. (ํ์์) ๋น์ฆ๋์ค ํน์ฑ ์ถ๊ฐ
4. (ํ์์) ๊ณ ๊ธ ๊ธฐ๋ฒ ์ ์ฉ
# ์ฌ์ ์ ์๋ ์ด๋ฒคํธ๋ง ์ฒ๋ฆฌ ๊ฐ๋ฅ
EVENTS = ['service_shutdown', 'service_launch', 'migration']
# โ ์๋ก์ด ์ํฉ ์๊ธฐ๋ฉด ์ฝ๋ ์์ ํ์
# ์์ฐ์ด ์
๋ ฅ โ ๋์ ํ๋ผ๋ฏธํฐ ์์ฑ
user_input = """
10์์ A ์๋น์ค ์ข
๋ฃํ๊ณ , 12์์ B ํ๋ก์ ํธ ์์ํ๋๋ฐ
์ด๊ธฐ 3๊ฐ์์ ๋น์ฉ์ด 2๋ฐฐ ๋ค ๊ฒ ๊ฐ์.
์ฌ๋ฆ์ ํด๋ผ์ฐ๋ ๋ง์ด๊ทธ๋ ์ด์
ํ๋ฉด์ 6๊ฐ์๊ฐ 150% ์์.
"""
# LLM์ด ํ๋ผ๋ฏธํฐ ์ถ์ถ โ ๋์ ์ฝ๋ ์์ฑ
parsed_events = llm_parse_business_context(user_input)
adjustment_code = generate_adjustment_code(parsed_events)
exec(adjustment_code) # ์ค์๊ฐ ์ ์ฉ
| ๊ตฌ๋ถ | ์ ํ | ์ด์ |
|---|---|---|
| ๋ฐฑ์๋ | FastAPI | ๋น ๋ฅธ ๊ฐ๋ฐ, ์๋ ๋ฌธ์ํ, Python ์ํ๊ณ |
| ํ๋ก ํธ์๋ | HTML + JavaScript | ๋จ์ํจ, ๋น ๋ฅธ ํ๋กํ ํ์ดํ |
| ML ๋ชจ๋ธ | Prophet | 14.6% ์ค์ฐจ ๋ฌ์ฑ, ์๊ณ์ด ์ ์ฉ |
| LLM | OpenAI API | ์์ ์ฑ, ๊ตฌ์กฐํ๋ ์ถ๋ ฅ ์ง์ |
| ๋ฐฐํฌ | AWS ECS | ๊ธฐ์กด AWS ํ๊ฒฝ๊ณผ ํตํฉ, ํ์ฅ์ฑ |
# ์คํจ ๋๋น ์ฝ๋ ์์
def safe_llm_parse(user_input):
try:
result = llm.parse(user_input)
confidence = validate_result(result)
if confidence < 0.7:
return fallback_to_manual_input()
return result
except Exception:
return fallback_to_manual_input()
def fallback_to_manual_input():
return show_traditional_form() # ๊ธฐ์กด ๋๋กญ๋ค์ด ๋ฐฉ์
# ์ด์์ ์ธ ์ฌ์ฉ ์๋๋ฆฌ์ค
์ฌ์ฉ์: "๋ค์ ๋ฌ์ ์ ๊ท ํ๋ก์ ํธ 3๊ฐ ์์ํ๊ณ ,
์ฌ๋ฆ์ ๋ ๊ฑฐ์ ์์คํ
3๊ฐ ์ข
๋ฃ ์์ ์ด์ผ"
์์คํ
: "ํ์
ํ์ต๋๋ค.
3์๋ถํฐ ์ $30K ์ฆ๊ฐ, 7์๋ถํฐ ์ $45K ์ ์ฝ ์์.
์ ๋ขฐ๋: ์ค๊ฐ. ํ์ธํ์๊ฒ ์ต๋๊น?"
์ฌ์ฉ์: "๋ง์, ์ ์ฉํด์ค"
์์คํ
: "2025๋
์์ฐ ์์ธก์ ์
๋ฐ์ดํธํ์ต๋๋ค.
๋ณด๊ณ ์๋ฅผ ์ด๋ฉ์ผ๋ก ๋ฐ์กํ์ต๋๋ค."