Bintang Fajar Julio
commited on
Commit
·
74b29ba
1
Parent(s):
69f1611
init
Browse files- .gitattributes +1 -0
- Dockerfile +24 -0
- app.py +52 -0
- checkpoints/bilstm_result/epoch=23-step=3456.ckpt +3 -0
- dataset/simulated_electrical_grid.csv +0 -0
- main.py +29 -0
- model/bilstm.py +72 -0
- requirements.txt +7 -0
- static/grid.xlsx +3 -0
- templates/index.html +464 -0
- util/preprocessor.py +104 -0
.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())
|