kimtaeyeong1229 commited on
Commit
fdef6a0
·
verified ·
1 Parent(s): 138d3c8

Upload utils/models.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. utils/models.py +215 -0
utils/models.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ML models and PyTorch MLP class (reused from notebook)."""
2
+ import numpy as np
3
+ import torch
4
+ import torch.nn as nn
5
+ import torch.optim as optim
6
+ from torch.utils.data import DataLoader, TensorDataset
7
+ from sklearn.linear_model import LogisticRegression
8
+ from sklearn.tree import DecisionTreeClassifier
9
+ from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
10
+ from sklearn.svm import SVC
11
+ from sklearn.neighbors import KNeighborsClassifier
12
+ from sklearn.naive_bayes import GaussianNB
13
+ from sklearn.metrics import accuracy_score, confusion_matrix
14
+ from sklearn.model_selection import cross_val_score
15
+
16
+ try:
17
+ from xgboost import XGBClassifier
18
+ XGBOOST_AVAILABLE = True
19
+ except ImportError:
20
+ XGBOOST_AVAILABLE = False
21
+
22
+ SEED = 42
23
+ DEVICE = torch.device('cpu') # HF Spaces CPU tier
24
+
25
+
26
+ # ── TitanicMLP (노트북 코드 그대로) ──
27
+ class TitanicMLP(nn.Module):
28
+ def __init__(self, input_dim, hidden_dims=None, dropout=0.3):
29
+ super(TitanicMLP, self).__init__()
30
+ if hidden_dims is None:
31
+ hidden_dims = [64, 32]
32
+
33
+ layers = []
34
+ prev_dim = input_dim
35
+
36
+ for hidden_dim in hidden_dims:
37
+ layers.extend([
38
+ nn.Linear(prev_dim, hidden_dim),
39
+ nn.BatchNorm1d(hidden_dim),
40
+ nn.ReLU(),
41
+ nn.Dropout(dropout),
42
+ ])
43
+ prev_dim = hidden_dim
44
+
45
+ layers.append(nn.Linear(prev_dim, 1))
46
+ layers.append(nn.Sigmoid())
47
+
48
+ self.network = nn.Sequential(*layers)
49
+
50
+ def forward(self, x):
51
+ return self.network(x).squeeze()
52
+
53
+
54
+ def make_dataloader(X_arr, y_arr, batch_size=32, shuffle=True):
55
+ X_tensor = torch.FloatTensor(np.array(X_arr)).to(DEVICE)
56
+ y_tensor = torch.FloatTensor(np.array(y_arr)).to(DEVICE)
57
+ dataset = TensorDataset(X_tensor, y_tensor)
58
+ return DataLoader(dataset, batch_size=batch_size, shuffle=shuffle)
59
+
60
+
61
+ def build_sklearn_model(algo: str, params: dict):
62
+ """Build a sklearn model by algorithm name and hyperparameter dict."""
63
+ if algo == 'Logistic Regression':
64
+ return LogisticRegression(
65
+ C=params.get('C', 1.0),
66
+ max_iter=1000,
67
+ random_state=SEED,
68
+ )
69
+ elif algo == 'Decision Tree':
70
+ return DecisionTreeClassifier(
71
+ max_depth=params.get('max_depth', 4),
72
+ min_samples_leaf=params.get('min_samples_leaf', 1),
73
+ random_state=SEED,
74
+ )
75
+ elif algo == 'Random Forest':
76
+ return RandomForestClassifier(
77
+ n_estimators=params.get('n_estimators', 100),
78
+ max_depth=params.get('max_depth', 5),
79
+ random_state=SEED,
80
+ )
81
+ elif algo == 'SVM (RBF)':
82
+ return SVC(
83
+ C=params.get('C', 1.0),
84
+ gamma=params.get('gamma', 'scale'),
85
+ kernel='rbf',
86
+ probability=True,
87
+ random_state=SEED,
88
+ )
89
+ elif algo == 'KNN':
90
+ return KNeighborsClassifier(
91
+ n_neighbors=params.get('n_neighbors', 7),
92
+ weights=params.get('weights', 'uniform'),
93
+ )
94
+ elif algo == 'Gradient Boosting':
95
+ return GradientBoostingClassifier(
96
+ n_estimators=params.get('n_estimators', 100),
97
+ learning_rate=params.get('learning_rate', 0.1),
98
+ max_depth=params.get('max_depth', 3),
99
+ random_state=SEED,
100
+ )
101
+ elif algo == 'XGBoost':
102
+ return XGBClassifier(
103
+ n_estimators=params.get('n_estimators', 100),
104
+ learning_rate=params.get('learning_rate', 0.1),
105
+ max_depth=params.get('max_depth', 3),
106
+ random_state=SEED,
107
+ eval_metric='logloss',
108
+ verbosity=0,
109
+ )
110
+ elif algo == 'Naive Bayes':
111
+ return GaussianNB()
112
+ else:
113
+ raise ValueError(f"Unknown algorithm: {algo}")
114
+
115
+
116
+ def train_sklearn_model(model, X_train, X_test, y_train, y_test, cv_folds=5):
117
+ """Train and evaluate a sklearn model. Returns metrics dict."""
118
+ model.fit(X_train, y_train)
119
+ y_pred = model.predict(X_test)
120
+
121
+ acc = accuracy_score(y_test, y_pred)
122
+ cm = confusion_matrix(y_test, y_pred)
123
+ cv_scores = cross_val_score(model, X_train, y_train, cv=cv_folds, scoring='accuracy')
124
+
125
+ feature_importances = None
126
+ if hasattr(model, 'feature_importances_'):
127
+ feature_importances = model.feature_importances_
128
+ elif hasattr(model, 'coef_'):
129
+ feature_importances = model.coef_[0]
130
+
131
+ return {
132
+ 'model': model,
133
+ 'accuracy': acc,
134
+ 'y_pred': y_pred,
135
+ 'confusion_matrix': cm,
136
+ 'cv_mean': cv_scores.mean(),
137
+ 'cv_std': cv_scores.std(),
138
+ 'feature_importances': feature_importances,
139
+ }
140
+
141
+
142
+ def train_mlp(X_train_scaled, X_test_scaled, y_train, y_test,
143
+ hidden_dims, epochs, lr, batch_size, dropout,
144
+ progress_callback=None):
145
+ """Train TitanicMLP and return training history + metrics."""
146
+ input_dim = X_train_scaled.shape[1]
147
+ mlp = TitanicMLP(input_dim=input_dim, hidden_dims=hidden_dims, dropout=dropout).to(DEVICE)
148
+
149
+ train_loader = make_dataloader(X_train_scaled, y_train.values, batch_size, shuffle=True)
150
+ test_loader = make_dataloader(X_test_scaled, y_test.values, batch_size, shuffle=False)
151
+
152
+ criterion = nn.BCELoss()
153
+ optimizer = optim.Adam(mlp.parameters(), lr=lr, weight_decay=1e-4)
154
+ scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=max(1, epochs // 3), gamma=0.5)
155
+
156
+ train_losses, test_losses = [], []
157
+ train_accs, test_accs = [], []
158
+
159
+ for epoch in range(1, epochs + 1):
160
+ # Train
161
+ mlp.train()
162
+ epoch_loss, correct, total = 0.0, 0, 0
163
+ for X_batch, y_batch in train_loader:
164
+ optimizer.zero_grad()
165
+ output = mlp(X_batch)
166
+ loss = criterion(output, y_batch)
167
+ loss.backward()
168
+ optimizer.step()
169
+ epoch_loss += loss.item()
170
+ pred = (output >= 0.5).float()
171
+ correct += (pred == y_batch).sum().item()
172
+ total += len(y_batch)
173
+
174
+ train_losses.append(epoch_loss / len(train_loader))
175
+ train_accs.append(correct / total)
176
+
177
+ # Eval
178
+ mlp.eval()
179
+ with torch.no_grad():
180
+ t_loss, t_correct, t_total = 0.0, 0, 0
181
+ for X_batch, y_batch in test_loader:
182
+ output = mlp(X_batch)
183
+ t_loss += criterion(output, y_batch).item()
184
+ pred = (output >= 0.5).float()
185
+ t_correct += (pred == y_batch).sum().item()
186
+ t_total += len(y_batch)
187
+
188
+ test_losses.append(t_loss / len(test_loader))
189
+ test_accs.append(t_correct / t_total)
190
+
191
+ scheduler.step()
192
+
193
+ if progress_callback:
194
+ progress_callback(epoch, epochs, train_losses[-1], train_accs[-1],
195
+ test_losses[-1], test_accs[-1])
196
+
197
+ # Final predictions for confusion matrix
198
+ mlp.eval()
199
+ y_pred_list = []
200
+ with torch.no_grad():
201
+ for X_batch, _ in test_loader:
202
+ output = mlp(X_batch)
203
+ y_pred_list.extend((output >= 0.5).cpu().numpy().astype(int))
204
+
205
+ cm = confusion_matrix(y_test, y_pred_list)
206
+
207
+ return {
208
+ 'train_losses': train_losses,
209
+ 'test_losses': test_losses,
210
+ 'train_accs': train_accs,
211
+ 'test_accs': test_accs,
212
+ 'final_acc': test_accs[-1],
213
+ 'confusion_matrix': cm,
214
+ 'y_pred': y_pred_list,
215
+ }