Bintang Fajar Julio commited on
Commit
74b29ba
·
1 Parent(s): 69f1611
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.xlsx filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use Python 3.12.3 base image
2
+ FROM python:3.12.3
3
+
4
+ # Create a non-root user
5
+ RUN useradd -m -u 1000 user
6
+
7
+ # Set working directory
8
+ WORKDIR /app
9
+
10
+ # Copy and install dependencies
11
+ COPY --chown=user ./requirements.txt requirements.txt
12
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
13
+
14
+ # Copy the rest of the application code
15
+ COPY --chown=user . /app
16
+
17
+ # Switch to the non-root user
18
+ USER user
19
+
20
+ # Expose the port 7860
21
+ EXPOSE 7860
22
+
23
+ # Command to run the application with gunicorn
24
+ CMD ["gunicorn", "--bind", "0.0.0.0:7860", "app:app"]
app.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import pandas as pd
3
+ import torch.nn as nn
4
+
5
+ from flask import Flask, render_template, request
6
+ from model.bilstm import BiLSTM
7
+
8
+ app = Flask(__name__)
9
+
10
+ model = BiLSTM.load_from_checkpoint('checkpoints/bilstm_result/epoch=23-step=3456.ckpt', lr=1e-3, num_classes=1, input_size=12)
11
+ model.eval()
12
+ model.freeze()
13
+
14
+ @app.route('/', methods=['POST', 'GET'])
15
+ def index():
16
+ if request.method == 'POST':
17
+ data = {
18
+ 'tau1': [request.form.get('tau1')],
19
+ 'tau2': [request.form.get('tau2')],
20
+ 'tau3': [request.form.get('tau3')],
21
+ 'tau4': [request.form.get('tau4')],
22
+ 'p1': [request.form.get('p1')],
23
+ 'p2': [request.form.get('p2')],
24
+ 'p3': [request.form.get('p3')],
25
+ 'p4': [request.form.get('p4')],
26
+ 'g1': [request.form.get('g1')],
27
+ 'g2': [request.form.get('g2')],
28
+ 'g3': [request.form.get('g3')],
29
+ 'g4': [request.form.get('g4')]
30
+ }
31
+
32
+ df = pd.DataFrame(data).astype('float')
33
+ X = torch.tensor(df.values.tolist())
34
+
35
+ with torch.no_grad():
36
+ preds = model(X)
37
+
38
+ preds = nn.Sigmoid()(preds.squeeze(1))
39
+ preds = preds.numpy()
40
+
41
+ if preds > 0.5:
42
+ result = 'Stable'
43
+
44
+ else:
45
+ result = 'Unstable'
46
+
47
+ return render_template('index.html', result=result)
48
+
49
+ return render_template('index.html')
50
+
51
+ if __name__ == '__main__':
52
+ app.run(debug=True)
checkpoints/bilstm_result/epoch=23-step=3456.ckpt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6e3ad6ba0bafe6c2afea68eb2a1f36e271dfd3fdfbf90a783f20096a0e67f87c
3
+ size 35048372
dataset/simulated_electrical_grid.csv ADDED
The diff for this file is too large to render. See raw diff
 
main.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytorch_lightning as pl
2
+
3
+ from util.preprocessor import Preprocessor
4
+ from model.bilstm import BiLSTM
5
+ from pytorch_lightning.loggers import TensorBoardLogger
6
+ from pytorch_lightning.callbacks import EarlyStopping, ModelCheckpoint
7
+
8
+ if __name__ == "__main__":
9
+ pl.seed_everything(42)
10
+
11
+ module = Preprocessor(batch_size=64)
12
+ num_classes, input_size = module.get_feature_size()
13
+
14
+ model = BiLSTM(lr=1e-3, num_classes=num_classes, input_size=input_size)
15
+
16
+ checkpoint_callback = ModelCheckpoint(dirpath='./checkpoints/bilstm_result', monitor='val_loss')
17
+ logger = TensorBoardLogger('log', name='bilstm_result')
18
+ early_stop_callback = EarlyStopping(monitor='val_loss', min_delta=0.00, check_on_train_epoch_end=1, patience=10)
19
+
20
+ trainer = pl.Trainer(
21
+ accelerator='gpu',
22
+ max_epochs=100,
23
+ default_root_dir='./checkpoints/bilstm_result',
24
+ callbacks = [checkpoint_callback, early_stop_callback],
25
+ deterministic=True,
26
+ logger=logger)
27
+
28
+ trainer.fit(model=model, datamodule=module)
29
+ trainer.test(model=model, datamodule=module, ckpt_path='best')
model/bilstm.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import pytorch_lightning as pl
4
+
5
+ from torchmetrics.classification import BinaryAccuracy
6
+
7
+ class BiLSTM(pl.LightningModule):
8
+ def __init__(self, lr, num_classes, input_size, hidden_size=300, num_layers=2, dropout=0.5):
9
+ super(BiLSTM, self).__init__()
10
+ self.lstm = nn.LSTM(input_size, hidden_size, num_layers, bidirectional=True, batch_first=True)
11
+ self.dropout = nn.Dropout(dropout)
12
+ self.output_layer = nn.Linear(hidden_size * 2, num_classes)
13
+ self.criterion = nn.BCEWithLogitsLoss()
14
+ self.accuracy_metric = BinaryAccuracy()
15
+ self.lr = lr
16
+ self.sigmoid = nn.Sigmoid()
17
+
18
+ def forward(self, X):
19
+ lstm_output, _ = self.lstm(X)
20
+ preds = self.output_layer(self.dropout(lstm_output))
21
+
22
+ return preds
23
+
24
+ def configure_optimizers(self):
25
+ optimizer = torch.optim.Adam(self.parameters(), lr=self.lr)
26
+
27
+ return optimizer
28
+
29
+ def training_step(self, train_batch, batch_idx):
30
+ X, target = train_batch
31
+
32
+ preds = self(X)
33
+
34
+ preds = preds.squeeze(1)
35
+ loss = self.criterion(preds, target.float())
36
+
37
+ preds = self.sigmoid(preds)
38
+ accuracy = self.accuracy_metric(preds, target)
39
+
40
+ self.log_dict({'train_loss': loss, 'train_accuracy': accuracy}, prog_bar=True, on_epoch=True)
41
+
42
+ return loss
43
+
44
+ def validation_step(self, valid_batch, batch_idx):
45
+ X, target = valid_batch
46
+
47
+ preds = self(X)
48
+
49
+ preds = preds.squeeze(1)
50
+ loss = self.criterion(preds, target.float())
51
+
52
+ preds = self.sigmoid(preds)
53
+ accuracy = self.accuracy_metric(preds, target)
54
+
55
+ self.log_dict({'val_loss': loss, 'val_accuracy': accuracy}, prog_bar=True, on_epoch=True)
56
+
57
+ return loss
58
+
59
+ def test_step(self, test_batch, batch_idx):
60
+ X, target = test_batch
61
+
62
+ preds = self(X)
63
+
64
+ preds = preds.squeeze(1)
65
+ loss = self.criterion(preds, target.float())
66
+
67
+ preds = self.sigmoid(preds)
68
+ accuracy = self.accuracy_metric(preds, target)
69
+
70
+ self.log_dict({'test_loss': loss, 'test_accuracy': accuracy}, prog_bar=True, on_epoch=True)
71
+
72
+ return loss
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ pytorch_lightning
2
+ gunicorn
3
+ flask
4
+ pandas
5
+ torch
6
+ imblearn
7
+ sklearn
static/grid.xlsx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5a4317e3dcb092a541dcff914fbc4fb8525be3f23990d1e9234beb903f9f6763
3
+ size 8802
templates/index.html ADDED
@@ -0,0 +1,464 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+
7
+ <link
8
+ href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
9
+ rel="stylesheet"
10
+ integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
11
+ crossorigin="anonymous"
12
+ />
13
+
14
+ <link
15
+ href="https://fonts.googleapis.com/css2?family=Quicksand:wght@500&display=swap"
16
+ rel="stylesheet"
17
+ />
18
+
19
+ <title>Grid Stability Classifier</title>
20
+ <style>
21
+ * {
22
+ font-family: "Quicksand", sans-serif;
23
+ }
24
+
25
+ .product-card {
26
+ padding: 10px;
27
+ border: none;
28
+ }
29
+
30
+ .box {
31
+ transition: 0.5s;
32
+ border-radius: 20px;
33
+ width: 300px;
34
+ }
35
+
36
+ .box:hover {
37
+ transform: translateY(-40px);
38
+ }
39
+
40
+ .card-body {
41
+ text-align: center;
42
+ }
43
+
44
+ .container {
45
+ margin-top: 20px;
46
+ }
47
+
48
+ .form-group {
49
+ margin-bottom: 15px;
50
+ }
51
+
52
+ .btn {
53
+ background-color: rgba(40, 119, 165, 0.8);
54
+ border-radius: 15px;
55
+ }
56
+
57
+ .btn-product {
58
+ color: rgb(255, 255, 255);
59
+ background-color: rgba(40, 119, 165, 0.8);
60
+ border-radius: 0px 28.5px 2px;
61
+ padding: 8px 50px;
62
+ text-decoration: none;
63
+ }
64
+ </style>
65
+ </head>
66
+
67
+ <body>
68
+ <nav
69
+ class="navbar navbar-expand navbar-light shadow-sm sticky-top bg-white"
70
+ >
71
+ <div
72
+ class="collapse navbar-collapse justify-content-center"
73
+ id="navbarSupportedContent"
74
+ >
75
+ <ul class="navbar-nav">
76
+ <li class="nav-item">
77
+ <a
78
+ class="nav-link"
79
+ id="navFeatures"
80
+ href="#features"
81
+ onclick="setActiveNav('navFeatures')"
82
+ >Features</a
83
+ >
84
+ </li>
85
+ <li class="nav-item">
86
+ <a
87
+ class="nav-link"
88
+ id="navClassifier"
89
+ href="#classifier"
90
+ onclick="setActiveNav('navClassifier')"
91
+ >Classifier</a
92
+ >
93
+ </li>
94
+ </ul>
95
+ </div>
96
+ </nav>
97
+
98
+ <div id="features">
99
+ <h4 style="text-align: center; margin-top: 30px">Features</h4>
100
+ </div>
101
+
102
+ <div class="container">
103
+ <div class="product-card shadow rounded p-4">
104
+ <table class="table">
105
+ <thead>
106
+ <tr>
107
+ <th scope="col">Name</th>
108
+ <th scope="col" style="text-align: center">Description</th>
109
+ </tr>
110
+ </thead>
111
+ <tbody>
112
+ <tr>
113
+ <td>tau1</td>
114
+ <td>
115
+ Electricity producer's reaction time in response to price change
116
+ </td>
117
+ </tr>
118
+ <tr>
119
+ <td>tau2</td>
120
+ <td>
121
+ First consumers' reaction time in response to price change
122
+ </td>
123
+ </tr>
124
+ <tr>
125
+ <td>tau3</td>
126
+ <td>
127
+ Second consumer's reaction time in response to price change
128
+ </td>
129
+ </tr>
130
+ <tr>
131
+ <td>tau4</td>
132
+ <td>
133
+ Third consumer's reaction time in response to price change
134
+ </td>
135
+ </tr>
136
+ <tr>
137
+ <td>p1</td>
138
+ <td>
139
+ Nominal power (positive real) produced by the producer (amount
140
+ of electricity produced)
141
+ </td>
142
+ </tr>
143
+ <tr>
144
+ <td>p2</td>
145
+ <td>
146
+ Nominal power (negative real) consumed by the first consumer
147
+ (amount of electricity consumed by the first consumer)
148
+ </td>
149
+ </tr>
150
+ <tr>
151
+ <td>p3</td>
152
+ <td>
153
+ Nominal power (negative real) consumed by the second consumer
154
+ (amount of electricity consumed by the second consumer)
155
+ </td>
156
+ </tr>
157
+ <tr>
158
+ <td>p4</td>
159
+ <td>
160
+ Nominal power (negative real) consumed by the third consumer
161
+ (amount of electricity consumed by the third consumer)
162
+ </td>
163
+ </tr>
164
+ <tr>
165
+ <td>g1</td>
166
+ <td>
167
+ (Gamma coefficient) proportional to price elasticity of producer
168
+ </td>
169
+ </tr>
170
+ <tr>
171
+ <td>g2</td>
172
+ <td>
173
+ (Gamma coefficient) proportional to the price elasticity of the
174
+ first consumer
175
+ </td>
176
+ </tr>
177
+ <tr>
178
+ <td>g3</td>
179
+ <td>
180
+ (Gamma coefficient) proportional to the price elasticity of the
181
+ second consumer
182
+ </td>
183
+ </tr>
184
+ <tr>
185
+ <td>g4</td>
186
+ <td>
187
+ (Gamma coefficient) proportional to the price elasticity of the
188
+ third consumer
189
+ </td>
190
+ </tr>
191
+ </tbody>
192
+ </table>
193
+ </div>
194
+ </div>
195
+
196
+ <div id="classifier">
197
+ <h4 style="text-align: center; margin-top: 60px">Classifier</h4>
198
+ </div>
199
+
200
+ <div class="container mb-5">
201
+ <div class="product-card shadow rounded p-4">
202
+ <form action="/" method="post">
203
+ <div class="row mb-1">
204
+ <div class="form-group">
205
+ <label for="excelFile">Upload Excel File</label>
206
+ <input
207
+ type="file"
208
+ id="excelFile"
209
+ accept=".xlsx, .xls"
210
+ class="form-control"
211
+ />
212
+ <a
213
+ style="
214
+ float: right;
215
+ text-decoration: underline;
216
+ color: rgba(40, 119, 165, 0.8);
217
+ cursor: pointer;
218
+ "
219
+ href="{{ url_for('static', filename='grid.xlsx') }}"
220
+ >Unduh template excel</a
221
+ >
222
+ </div>
223
+ </div>
224
+ <p class="text-center">atau</p>
225
+ <div class="row mt-3">
226
+ <div class="col-md-3">
227
+ <div class="form-group">
228
+ <label>tau1</label>
229
+ <input
230
+ type="text"
231
+ name="tau1"
232
+ placeholder="tau1"
233
+ class="form-control"
234
+ required
235
+ />
236
+ </div>
237
+ </div>
238
+ <div class="col-md-3">
239
+ <div class="form-group">
240
+ <label>tau2</label>
241
+ <input
242
+ type="text"
243
+ name="tau2"
244
+ placeholder="tau2"
245
+ class="form-control"
246
+ required
247
+ />
248
+ </div>
249
+ </div>
250
+ <div class="col-md-3">
251
+ <div class="form-group">
252
+ <label>tau3</label>
253
+ <input
254
+ type="text"
255
+ name="tau3"
256
+ placeholder="tau3"
257
+ class="form-control"
258
+ required
259
+ />
260
+ </div>
261
+ </div>
262
+ <div class="col-md-3">
263
+ <div class="form-group">
264
+ <label>tau4</label>
265
+ <input
266
+ type="text"
267
+ name="tau4"
268
+ placeholder="tau4"
269
+ class="form-control"
270
+ required
271
+ />
272
+ </div>
273
+ </div>
274
+ </div>
275
+
276
+ <div class="row">
277
+ <div class="col-md-3">
278
+ <div class="form-group">
279
+ <label>p1</label>
280
+ <input
281
+ type="text"
282
+ name="p1"
283
+ placeholder="p1"
284
+ class="form-control"
285
+ required
286
+ />
287
+ </div>
288
+ </div>
289
+ <div class="col-md-3">
290
+ <div class="form-group">
291
+ <label>p2</label>
292
+ <input
293
+ type="text"
294
+ name="p2"
295
+ placeholder="p2"
296
+ class="form-control"
297
+ required
298
+ />
299
+ </div>
300
+ </div>
301
+ <div class="col-md-3">
302
+ <div class="form-group">
303
+ <label>p3</label>
304
+ <input
305
+ type="text"
306
+ name="p3"
307
+ placeholder="p3"
308
+ class="form-control"
309
+ required
310
+ />
311
+ </div>
312
+ </div>
313
+ <div class="col-md-3">
314
+ <div class="form-group">
315
+ <label>p4</label>
316
+ <input
317
+ type="text"
318
+ name="p4"
319
+ placeholder="p4"
320
+ class="form-control"
321
+ required
322
+ />
323
+ </div>
324
+ </div>
325
+ </div>
326
+
327
+ <div class="row">
328
+ <div class="col-md-3">
329
+ <div class="form-group">
330
+ <label>g1</label>
331
+ <input
332
+ type="text"
333
+ name="g1"
334
+ placeholder="g1"
335
+ class="form-control"
336
+ required
337
+ />
338
+ </div>
339
+ </div>
340
+ <div class="col-md-3">
341
+ <div class="form-group">
342
+ <label>g2</label>
343
+ <input
344
+ type="text"
345
+ name="g2"
346
+ placeholder="g2"
347
+ class="form-control"
348
+ required
349
+ />
350
+ </div>
351
+ </div>
352
+ <div class="col-md-3">
353
+ <div class="form-group">
354
+ <label>g3</label>
355
+ <input
356
+ type="text"
357
+ name="g3"
358
+ placeholder="g3"
359
+ class="form-control"
360
+ required
361
+ />
362
+ </div>
363
+ </div>
364
+ <div class="col-md-3">
365
+ <div class="form-group">
366
+ <label>g4</label>
367
+ <input
368
+ type="text"
369
+ name="g4"
370
+ placeholder="g4"
371
+ class="form-control"
372
+ required
373
+ />
374
+ </div>
375
+ </div>
376
+ </div>
377
+
378
+ <div class="d-flex justify-content-center mt-4">
379
+ <button type="submit" class="btn btn-primary w-50">Classify</button>
380
+ </div>
381
+ </form>
382
+ </div>
383
+ </div>
384
+ <script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
385
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
386
+ <script
387
+ src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
388
+ integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
389
+ crossorigin="anonymous"
390
+ ></script>
391
+ <script>
392
+ {% if result %}
393
+ Swal.fire({
394
+ title: 'Classification Result',
395
+ text: '{{ result }}',
396
+ icon: 'info',
397
+ confirmButtonColor: '#3085d6',
398
+ confirmButtonText: 'OK'
399
+ });
400
+ {% endif %}
401
+ </script>
402
+ <script>
403
+ document
404
+ .getElementById("excelFile")
405
+ .addEventListener("change", handleFile, false);
406
+
407
+ function handleFile(event) {
408
+ const file = event.target.files[0];
409
+ if (!file) return;
410
+
411
+ const reader = new FileReader();
412
+ reader.onload = function (e) {
413
+ const data = new Uint8Array(e.target.result);
414
+ const workbook = XLSX.read(data, { type: "array" });
415
+
416
+ const sheetName = workbook.SheetNames[0];
417
+ const sheet = workbook.Sheets[sheetName];
418
+ const json = XLSX.utils.sheet_to_json(sheet, { header: 1 });
419
+
420
+ populateForm(json);
421
+ };
422
+ reader.readAsArrayBuffer(file);
423
+ }
424
+
425
+ function populateForm(data) {
426
+ const fields = data[0];
427
+ const values = data[1];
428
+
429
+ fields.forEach((field, index) => {
430
+ const input = document.querySelector(`[name="${field}"]`);
431
+
432
+ if (values[index] == undefined) {
433
+ values[index] = 0;
434
+ }
435
+
436
+ if (input) {
437
+ input.value = values[index];
438
+ }
439
+ });
440
+ }
441
+
442
+ function filterNumericInput(event) {
443
+ const input = event.target;
444
+ let value = input.value;
445
+
446
+ // Remove all characters except digits and decimal point
447
+ value = value.replace(/[^0-9.]/g, "");
448
+
449
+ // Remove multiple decimal points
450
+ const parts = value.split(".");
451
+ if (parts.length > 2) {
452
+ value = parts[0] + "." + parts.slice(1).join("");
453
+ }
454
+
455
+ input.value = value;
456
+ }
457
+
458
+ // Attach the filter function to all number input fields
459
+ document.querySelectorAll('input[type="text"]').forEach((input) => {
460
+ input.addEventListener("input", filterNumericInput);
461
+ });
462
+ </script>
463
+ </body>
464
+ </html>
util/preprocessor.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import torch
3
+ import multiprocessing
4
+ import pandas as pd
5
+ import pytorch_lightning as pl
6
+
7
+ from sklearn.model_selection import train_test_split
8
+ from torch.utils.data import TensorDataset, DataLoader
9
+ from imblearn.over_sampling import SMOTE
10
+
11
+
12
+ class Preprocessor(pl.LightningDataModule):
13
+ def __init__(self, batch_size):
14
+ super(Preprocessor, self).__init__()
15
+ self.dataset = pd.read_csv('dataset/simulated_electrical_grid.csv')
16
+ self.batch_size = batch_size
17
+ self.oversampling = SMOTE(random_state=42)
18
+
19
+ def setup(self, stage=None):
20
+ train_set, valid_set, test_set = self.preprocessor()
21
+ if stage == "fit":
22
+ self.train_set = train_set
23
+ self.valid_set = valid_set
24
+ elif stage == "test":
25
+ self.test_set = test_set
26
+
27
+ def train_dataloader(self):
28
+ return DataLoader(
29
+ dataset=self.train_set,
30
+ batch_size=self.batch_size,
31
+ shuffle=True,
32
+ num_workers=multiprocessing.cpu_count()
33
+ )
34
+
35
+ def val_dataloader(self):
36
+ return DataLoader(
37
+ dataset=self.valid_set,
38
+ batch_size=self.batch_size,
39
+ shuffle=False,
40
+ num_workers=multiprocessing.cpu_count()
41
+ )
42
+
43
+ def test_dataloader(self):
44
+ return DataLoader(
45
+ dataset=self.test_set,
46
+ batch_size=self.batch_size,
47
+ shuffle=False,
48
+ num_workers=multiprocessing.cpu_count()
49
+ )
50
+
51
+ def preprocessor(self):
52
+ if os.path.exists("dataset/train_set.pt") and os.path.exists("dataset/valid_set.pt") and os.path.exists("dataset/test_set.pt"):
53
+ print("\nLoading Data...")
54
+ train_set = torch.load("dataset/train_set.pt")
55
+ valid_set = torch.load("dataset/valid_set.pt")
56
+ test_set = torch.load("dataset/test_set.pt")
57
+ print('[ Loading Completed ]\n')
58
+
59
+ else:
60
+ print("\nPreprocessing Data...")
61
+ train_set, valid_set, test_set = self.preprocessing_data(self.dataset)
62
+ print('[ Preprocessing Completed ]\n')
63
+
64
+ return train_set, valid_set, test_set
65
+
66
+ def preprocessing_data(self, dataset):
67
+ X = dataset[['tau1', 'tau2', 'tau3', 'tau4', 'p1', 'p2', 'p3', 'p4', 'g1', 'g2', 'g3', 'g4']]
68
+ y = dataset['stabf']
69
+
70
+ X_train_res, y_train_res = self.oversampling.fit_resample(X, y)
71
+ y_train_res = self.label_encoding(y_train_res)
72
+
73
+ X_train_valid, X_test, y_train_valid, y_test = train_test_split(X_train_res, y_train_res, test_size=0.2, random_state=42)
74
+ X_train, X_valid, y_train, y_valid = train_test_split(X_train_valid, y_train_valid, test_size=0.1, random_state=42)
75
+
76
+ X_train_tensor = torch.tensor(X_train.values.tolist())
77
+ y_train_tensor = torch.tensor(y_train.values.tolist())
78
+
79
+ X_valid_tensor = torch.tensor(X_valid.values.tolist())
80
+ y_valid_tensor = torch.tensor(y_valid.values.tolist())
81
+
82
+ X_test_tensor = torch.tensor(X_test.values.tolist())
83
+ y_test_tensor = torch.tensor(y_test.values.tolist())
84
+
85
+ train_set = TensorDataset(X_train_tensor, y_train_tensor)
86
+ valid_set = TensorDataset(X_valid_tensor, y_valid_tensor)
87
+ test_set = TensorDataset(X_test_tensor, y_test_tensor)
88
+
89
+ torch.save(train_set, "dataset/train_set.pt")
90
+ torch.save(valid_set, "dataset/valid_set.pt")
91
+ torch.save(test_set, "dataset/test_set.pt")
92
+
93
+ return train_set, valid_set, test_set
94
+
95
+ def label_encoding(self, y_train):
96
+ encoder = {'unstable': 0, 'stable': 1}
97
+ y_train = y_train.astype('str').map(encoder)
98
+
99
+ return y_train
100
+
101
+ def get_feature_size(self):
102
+ X = self.dataset[['tau1','tau2','tau3','tau4','p1', 'p2', 'p3', 'p4','g1','g2','g3','g4']]
103
+
104
+ return 1, len(X.columns.tolist())