File size: 7,426 Bytes
bbf5602
 
 
0ab1c9b
 
bbf5602
0ab1c9b
bbf5602
 
 
 
0ab1c9b
 
 
 
 
bbf5602
 
 
fa0e01d
bbf5602
fa0e01d
 
 
bbf5602
 
0ab1c9b
bbf5602
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fa0e01d
bbf5602
fa0e01d
bbf5602
fa0e01d
 
 
 
bbf5602
fa0e01d
 
 
 
 
 
bbf5602
fa0e01d
 
bbf5602
 
 
 
fa0e01d
bbf5602
fa0e01d
 
 
 
 
bbf5602
 
 
 
0ab1c9b
 
 
bbf5602
 
 
 
 
0ab1c9b
 
 
 
 
 
 
 
bbf5602
 
0ab1c9b
bbf5602
0ab1c9b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bbf5602
0ab1c9b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bbf5602
 
0ab1c9b
 
bbf5602
 
 
0ab1c9b
 
 
 
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import pandas as pd
import numpy as np
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score, mean_absolute_percentage_error
from sklearn.model_selection import GridSearchCV
import joblib
import sys
import io
import warnings
warnings.filterwarnings('ignore')

# Set standard output and standard error to UTF-8 to prevent Unicode encoding errors on Windows
if sys.platform == 'win32':
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
    sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')

print("πŸš€ MEMULAI PROSES TRAINING AI LEVEL ADVANCED (ECO-TWIN PRO)...\n")

# ==========================================
# 1. LOAD LOCALIZED DATA
# ==========================================
print("1. Menarik & Memproses Data Historis Lokal...")
df = pd.read_csv('dataset_local_2026.csv')
df['Tanggal'] = pd.to_datetime(df['Tanggal'])

# Baseline Sampah (Diambil dari SIPSN DKI 2025)
base_sampah = 8020.0 
mrt_harian_avg = 85000 
hujan_mean = 10.5

# Data Event 
data_event_csv = """Tanggal,Nama_Event,Ada_Event
2023-01-01,Tahun Baru 2023,1
2023-03-11,Konser BLACKPINK,1
2023-03-12,Konser BLACKPINK,1
2023-05-26,Java Jazz,1
2023-06-19,Timnas Argentina,1
2023-11-15,Coldplay,1
2023-12-31,Tahun Baru 2024,1
2024-01-01,Tahun Baru 2024,1
2024-03-02,Ed Sheeran,1
2024-05-24,Java Jazz 2024,1
2024-12-31,Malam Tahun Baru 2025,1"""
df_event = pd.read_csv(io.StringIO(data_event_csv))
df_event['Tanggal'] = pd.to_datetime(df_event['Tanggal'])

# Bikin Master Kalender 2 Tahun (Lebih banyak data, AI makin pintar)
df = pd.DataFrame({'Tanggal': pd.date_range(start="2023-01-01", end="2024-12-31")})
df = pd.merge(df, df_event[['Tanggal', 'Ada_Event']], on='Tanggal', how='left').fillna({'Ada_Event': 0})

# Simulasi Pola Realistis
df['Penumpang_MRT'] = np.random.normal(loc=mrt_harian_avg, scale=mrt_harian_avg*0.15, size=len(df)).astype(int)
df['Curah_Hujan_mm'] = np.random.exponential(scale=hujan_mean, size=len(df))
df.loc[df['Curah_Hujan_mm'] < 2, 'Curah_Hujan_mm'] = 0

# ==========================================
# 2. FEATURE ENGINEERING (LOCAL BINDING)
# ==========================================
print("2. Melakukan One-Hot Encoding Lokasi & Verifikasi Fitur...")

# Defensive manual one-hot encoding to guarantee column names and order
locations = ['JIS', 'GBK', 'Pasar Senen', 'Gang Sempit Tambora']
for loc in locations:
    df[f'Loc_{loc}'] = (df['Location'] == loc).astype(int)

# Fitur yang dipakai AI buat berpikir
fitur = [
    'Loc_JIS', 'Loc_GBK', 'Loc_Pasar Senen', 'Loc_Gang Sempit Tambora',
    'RR', 'Rain_Lag_1', 'Rain_Lag_2', 'Is_Holiday', 'Ada_Event', 'Crowd_Scale',
    'Hari_Ke', 'Is_Weekend', 'Hari_Dalam_Minggu', 'Bulan'
]

X = df[fitur]
y = df['Volume_Ton']

# ==========================================
# 3. CHRONOLOGICAL SPLIT & TRAINING
# ==========================================
print("3. Membagi Data secara Kronologis (75/25) & Melatih Model...")

# 75% days for training, 25% for test. 
# Since we have 4 locations per day, we split at index: (len(df) // 4 * 0.75) * 4
num_days = len(df) // 4
train_days = int(num_days * 0.75)
train_size = train_days * 4

X_train, X_test = X.iloc[:train_size], X.iloc[train_size:]
y_train, y_test = y.iloc[:train_size], y.iloc[train_size:]

# Menggunakan Gradient Boosting Regressor (Baseline)
print("βš™οΈ Melatih model Baseline...")
base_model = GradientBoostingRegressor(
    n_estimators=200, 
    learning_rate=0.1, 
    max_depth=4, 
    random_state=42
)
base_model.fit(X_train, y_train)
pred_base = base_model.predict(X_test)

# Hitung Metrics Baseline
mae_base = mean_absolute_error(y_test, pred_base)
rmse_base = mean_squared_error(y_test, pred_base) ** 0.5
r2_base = r2_score(y_test, pred_base)
mape_base = mean_absolute_percentage_error(y_test, pred_base) * 100

# ==========================================
# 4. HYPERPARAMETER TUNING (UPGRADE MODEL)
# ==========================================
print("\nβš™οΈ Melakukan Hyperparameter Tuning menggunakan GridSearchCV...")
param_grid = {
    'n_estimators': [100, 200, 300],
    'learning_rate': [0.03, 0.05, 0.1, 0.15],
    'max_depth': [3, 4, 5],
    'subsample': [0.8, 0.9, 1.0]
}

grid_search = GridSearchCV(
    estimator=GradientBoostingRegressor(random_state=42),
    param_grid=param_grid,
    cv=3,
    scoring='neg_mean_absolute_error',
    n_jobs=-1,
    verbose=1
)
grid_search.fit(X_train, y_train)

best_model = grid_search.best_estimator_
pred_best = best_model.predict(X_test)

# Hitung Metrics Upgraded Model
mae_best = mean_absolute_error(y_test, pred_best)
rmse_best = mean_squared_error(y_test, pred_best) ** 0.5
r2_best = r2_score(y_test, pred_best)
mape_best = mean_absolute_percentage_error(y_test, pred_best) * 100

# ==========================================
# 5. PERBANDINGAN METRICS (BUAT DIPAMERIN KE JURI)
# ==========================================
print("\nπŸ“Š HASIL EVALUASI & PERBANDINGAN METRICS:")
print(f"β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”")
print(f"β”‚ Metric                  β”‚ Baseline Model       β”‚ Upgraded Model       β”‚ Status               β”‚")
print(f"β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€")
print(f"β”‚ Mean Absolute Error     β”‚ {mae_base:16.2f} Ton β”‚ {mae_best:16.2f} Ton β”‚ {'Semakin Baik (⬇️)' if mae_best < mae_base else 'Sama/Stabil'}  β”‚")
print(f"β”‚ Root Mean Squared Error β”‚ {rmse_base:16.2f} Ton β”‚ {rmse_best:16.2f} Ton β”‚ {'Semakin Baik (⬇️)' if rmse_best < rmse_base else 'Sama/Stabil'}  β”‚")
print(f"β”‚ R-Squared (RΒ² Score)    β”‚ {r2_base*100:15.2f}% β”‚ {r2_best*100:15.2f}% β”‚ {'Semakin Baik (⬆️)' if r2_best > r2_base else 'Sama/Stabil'}  β”‚")
print(f"β”‚ MAPE (Error Persentase) β”‚ {mape_base:15.2f}% β”‚ {mape_best:15.2f}% β”‚ {'Semakin Baik (⬇️)' if mape_best < mape_base else 'Sama/Stabil'}  β”‚")
print(f"β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜")

print(f"\nβš™οΈ Hyperparameter Terbaik hasil tuning:")
print(f"   - n_estimators : {grid_search.best_params_['n_estimators']}")
print(f"   - learning_rate: {grid_search.best_params_['learning_rate']}")
print(f"   - max_depth    : {grid_search.best_params_['max_depth']}")
print(f"   - subsample    : {grid_search.best_params_['subsample']}")

# Cek Fitur Paling Berpengaruh
importances = best_model.feature_importances_
print("\n🌟 FITUR PALING BERPENGARUH PADA TIMBULAN SAMPAH (UPGRADED):")
for name, importance in zip(fitur, importances):
    print(f"   - {name}: {importance*100:.1f}%")

# Simpan Model Terbaik
joblib.dump(best_model, 'model_sampah_advanced.pkl')
print("\nπŸ’Ύ SUCCESS! 'model_sampah_advanced.pkl' berhasil di-generate menggunakan model hasil upgrade!")