bram4627 commited on
Commit
f8fa836
1 Parent(s): 489fb33

Upload 6 files

Browse files
Files changed (7) hide show
  1. .gitattributes +1 -0
  2. app.py +101 -0
  3. lstm_model.h5 +3 -0
  4. requirements.txt +57 -0
  5. scaler.sav +0 -0
  6. static/hujan.jpg +3 -0
  7. templates/index.html +203 -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
+ static/hujan.jpg filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, render_template
2
+ import joblib
3
+ from tensorflow.keras.models import load_model
4
+ import numpy as np
5
+ import os
6
+
7
+ # Load models
8
+ model_load = load_model('lstm_model.h5')
9
+ scaler_load = joblib.load('scaler.sav') # Load the correct scaler
10
+
11
+ # Flask app initialization
12
+ app = Flask(__name__)
13
+ app.config["SECRET_KEY"] = os.urandom(24)
14
+ app.config["DEBUG"] = True
15
+
16
+ def validasi_inputan(form_data):
17
+ errors = {}
18
+
19
+ # Validasi temp_1, temp_2, temp_3
20
+ for field in ["temp_1", "temp_2", "temp_3"]:
21
+ if not form_data.get(field):
22
+ errors[field] = f"{field} tidak boleh kosong."
23
+ else:
24
+ try:
25
+ float(form_data.get(field))
26
+ except ValueError:
27
+ errors[field] = f"{field} harus berupa angka."
28
+
29
+ # Validasi feelslike_1, feelslike_2, feelslike_3
30
+ for field in ["feelslike_1", "feelslike_2", "feelslike_3"]:
31
+ if not form_data.get(field):
32
+ errors[field] = f"{field} tidak boleh kosong."
33
+ else:
34
+ try:
35
+ float(form_data.get(field))
36
+ except ValueError:
37
+ errors[field] = f"{field} harus berupa angka."
38
+
39
+ return errors
40
+
41
+ def validate_data(record):
42
+ errors = {}
43
+
44
+ # Validasi rentang nilai (15 - 50 untuk suhu)
45
+ for key, value in record.items():
46
+ if value < 15 or value > 50:
47
+ errors[key] = f"{key} harus diantara 15 dan 50."
48
+
49
+ return errors
50
+
51
+ @app.route("/", methods=["GET", "POST"])
52
+ def index():
53
+ prediction = None
54
+ errors = {}
55
+ predictions_list = []
56
+
57
+ if request.method == "POST":
58
+ # Validasi input kosong
59
+ errors = validasi_inputan(request.form)
60
+
61
+ if not errors:
62
+ record = {
63
+ "temp_1": float(request.form.get("temp_1")),
64
+ "temp_2": float(request.form.get("temp_2")),
65
+ "temp_3": float(request.form.get("temp_3")),
66
+ "feelslike_1": float(request.form.get("feelslike_1")),
67
+ "feelslike_2": float(request.form.get("feelslike_2")),
68
+ "feelslike_3": float(request.form.get("feelslike_3")),
69
+ }
70
+
71
+ # Validasi rentang nilai
72
+ errors = validate_data(record)
73
+
74
+ if not errors:
75
+ # Input data untuk prediksi
76
+ input_data = np.array([
77
+ [record["temp_1"], record["feelslike_1"]],
78
+ [record["temp_2"], record["feelslike_2"]],
79
+ [record["temp_3"], record["feelslike_3"]]
80
+ ])
81
+
82
+ # Scaling input data
83
+ input_data_scaled = scaler_load.transform(input_data)
84
+
85
+ # Prediksi untuk 5 periode ke depan
86
+ last_input_scaled = input_data_scaled.copy()
87
+
88
+ for _ in range(5): # Prediksi 5 periode
89
+ prediction_normalized = model_load.predict(last_input_scaled.reshape(1, 3, 2))
90
+ prediction_denormalized = scaler_load.inverse_transform(prediction_normalized)
91
+ predictions_list.append(prediction_denormalized.flatten())
92
+
93
+ # Update input dengan prediksi terbaru
94
+ last_input_scaled = np.append(last_input_scaled[1:], prediction_normalized, axis=0)
95
+
96
+
97
+
98
+ return render_template('index.html', prediction=predictions_list, errors=errors, record=request.form)
99
+
100
+ if __name__ == "__main__":
101
+ app.run()
lstm_model.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d4f4ee5f28e2dd797b212ef2bff479100dacf6c541be4defbcf92eae4eac37d7
3
+ size 152504
requirements.txt ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ attrs==23.1.0
2
+ beautifulsoup4==4.12.2
3
+ blinker==1.6.2
4
+ certifi==2023.7.22
5
+ cffi==1.15.1
6
+ charset-normalizer==3.3.2
7
+ click==8.1.7
8
+ colorama==0.4.6
9
+ contourpy==1.2.1
10
+ cycler==0.12.1
11
+ exceptiongroup==1.1.2
12
+ Flask==2.3.3
13
+ fonttools==4.52.1
14
+ gunicorn==22.0.0
15
+ h11==0.14.0
16
+ h5py==3.11.0
17
+ idna==3.4
18
+ inexactsearch==1.0.2
19
+ itsdangerous==2.1.2
20
+ Jinja2==3.1.2
21
+ joblib==1.3.2
22
+ kiwisolver==1.4.5
23
+ MarkupSafe==2.1.3
24
+ matplotlib==3.9.0
25
+ networkx==3.3
26
+ nltk==3.8.1
27
+ numpy==1.26.0
28
+ outcome==1.2.0
29
+ packaging==24.0
30
+ pandas==2.1.1
31
+ pillow==10.3.0
32
+ pycparser==2.21
33
+ pyparsing==3.1.2
34
+ PySocks==1.7.1
35
+ pyspellchecker==0.8.1
36
+ python-dateutil==2.8.2
37
+ pytz==2023.3.post1
38
+ regex==2024.5.15
39
+ requests==2.32.2
40
+ scikit-learn==1.2.2
41
+ scipy==1.11.2
42
+ selenium==4.9.0
43
+ silpa_common==0.3
44
+ six==1.16.0
45
+ sniffio==1.3.0
46
+ sortedcontainers==2.4.0
47
+ soundex==1.1.3
48
+ soupsieve==2.4.1
49
+ spellchecker==0.4
50
+ threadpoolctl==3.2.0
51
+ tqdm==4.66.4
52
+ trio==0.22.2
53
+ trio-websocket==0.10.3
54
+ tzdata==2023.3
55
+ urllib3==1.26.16
56
+ Werkzeug==2.3.7
57
+ wsproto==1.2.0
scaler.sav ADDED
Binary file (743 Bytes). View file
 
static/hujan.jpg ADDED

Git LFS Details

  • SHA256: 4085204caa0d85d3380cf4b39aff73b8be295a4030b4fd3c1c09eb206173ee32
  • Pointer size: 132 Bytes
  • Size of remote file: 2.97 MB
templates/index.html ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.0" />
6
+ <title>Prediksi Jumlah Penumpang Bulan Depan</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link
9
+ href="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.3.0/flowbite.min.css"
10
+ rel="stylesheet"
11
+ />
12
+ </head>
13
+ <body class="bg-[url('{{url_for("static", filename="hujan.jpg")}}')] bg-cover bg-center bg-no-repeat h-screen flex items-center justify-center">
14
+ <div class="container mx-auto max-w-lg">
15
+ <div
16
+ class="px-4 py-6 flex flex-col justify-center items-center w-full"
17
+ >
18
+ <div class="mb-8 w-full text-center">
19
+ <h1 class="text-3xl text-white font-bold">Jumlah Penumpang Kereta Api</h1>
20
+ <h1 class="text-lg text-white font-bold">Prediksi</h1>
21
+ </div>
22
+
23
+ <div class="w-full">
24
+ <div class="bg-white p-6 rounded-lg shadow-md text-justify">
25
+ <h2 class="text-xl font-semibold mb-4 text-center">MASUKKAN DATA</h2>
26
+ <form method="POST" action="">
27
+ <div class="grid grid-cols-2 gap-4 w-full mb-4">
28
+ <!-- Temp 1 -->
29
+ <div class="col-span-2 xl:col-span-1">
30
+ <label class="block text-sm font-medium text-zinc-700">temp_1</label>
31
+ <div class="mt-1 flex rounded-md shadow-sm">
32
+ <input
33
+ name="temp_1"
34
+ type="number"
35
+ class="w-full mt-1 px-4 py-2 border rounded-md bg-gray-100"
36
+ placeholder="Contoh: 30.5"
37
+ step="0.1"
38
+ value="{{ record['temp_1'] }}"
39
+ required
40
+ />
41
+ </div>
42
+ {% if errors.temp_1 %}
43
+ <p class="text-red-500 text-xs italic">{{ errors.temp_1 }}</p>
44
+ {% endif %}
45
+ </div>
46
+
47
+ <!-- Temp 2 -->
48
+ <div class="col-span-2 xl:col-span-1">
49
+ <label class="block text-sm font-medium text-zinc-700">temp_2</label>
50
+ <div class="mt-1 flex rounded-md shadow-sm">
51
+ <input
52
+ name="temp_2"
53
+ type="number"
54
+ class="w-full mt-1 px-4 py-2 border rounded-md bg-gray-100"
55
+ placeholder="Contoh: 30.5"
56
+ step="0.1"
57
+ value="{{ record['temp_2'] }}"
58
+ required
59
+ />
60
+ </div>
61
+ {% if errors.temp_2 %}
62
+ <p class="text-red-500 text-xs italic">{{ errors.temp_2 }}</p>
63
+ {% endif %}
64
+ </div>
65
+
66
+ <!-- Temp 3 -->
67
+ <div class="col-span-2 xl:col-span-1">
68
+ <label class="block text-sm font-medium text-zinc-700">temp_3</label>
69
+ <div class="mt-1 flex rounded-md shadow-sm">
70
+ <input
71
+ name="temp_3"
72
+ type="number"
73
+ class="w-full mt-1 px-4 py-2 border rounded-md bg-gray-100"
74
+ placeholder="Contoh: 30.5"
75
+ step="0.1"
76
+ value="{{ record['temp_3'] }}"
77
+ required
78
+ />
79
+ </div>
80
+ {% if errors.temp_3 %}
81
+ <p class="text-red-500 text-xs italic">{{ errors.temp_3 }}</p>
82
+ {% endif %}
83
+ </div>
84
+
85
+ <!-- FeelsLike 1 -->
86
+ <div class="col-span-2 xl:col-span-1">
87
+ <label class="block text-sm font-medium text-zinc-700">feelslike_1</label>
88
+ <div class="mt-1 flex rounded-md shadow-sm">
89
+ <input
90
+ name="feelslike_1"
91
+ type="number"
92
+ class="w-full mt-1 px-4 py-2 border rounded-md bg-gray-100"
93
+ placeholder="Contoh: 29.8"
94
+ step="0.1"
95
+ value="{{ record['feelslike_1'] }}"
96
+ required
97
+ />
98
+ </div>
99
+ {% if errors.feelslike_1 %}
100
+ <p class="text-red-500 text-xs italic">{{ errors.feelslike_1 }}</p>
101
+ {% endif %}
102
+ </div>
103
+
104
+ <!-- FeelsLike 2 -->
105
+ <div class="col-span-2 xl:col-span-1">
106
+ <label class="block text-sm font-medium text-zinc-700">feelslike_2</label>
107
+ <div class="mt-1 flex rounded-md shadow-sm">
108
+ <input
109
+ name="feelslike_2"
110
+ type="number"
111
+ class="w-full mt-1 px-4 py-2 border rounded-md bg-gray-100"
112
+ placeholder="Contoh: 29.8"
113
+ step="0.1"
114
+ value="{{ record['feelslike_2'] }}"
115
+ required
116
+ />
117
+ </div>
118
+ {% if errors.feelslike_2 %}
119
+ <p class="text-red-500 text-xs italic">{{ errors.feelslike_2 }}</p>
120
+ {% endif %}
121
+ </div>
122
+
123
+ <!-- FeelsLike 3 -->
124
+ <div class="col-span-2 xl:col-span-1">
125
+ <label class="block text-sm font-medium text-zinc-700">feelslike_3</label>
126
+ <div class="mt-1 flex rounded-md shadow-sm">
127
+ <input
128
+ name="feelslike_3"
129
+ type="number"
130
+ class="w-full mt-1 px-4 py-2 border rounded-md bg-gray-100"
131
+ placeholder="Contoh: 29.8"
132
+ step="0.1"
133
+ value="{{ record['feelslike_3'] }}"
134
+ required
135
+ />
136
+ </div>
137
+ {% if errors.feelslike_3 %}
138
+ <p class="text-red-500 text-xs italic">{{ errors.feelslike_3 }}</p>
139
+ {% endif %}
140
+ </div>
141
+ </div>
142
+
143
+ <div class="flex justify-center gap-2">
144
+ <button
145
+ type="button"
146
+ id="clear-button"
147
+ class="text-gray-900 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-100 font-medium rounded-lg text-sm px-5 py-2.5"
148
+ >
149
+ Bersihkan
150
+ </button>
151
+ <button
152
+ type="submit"
153
+ class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 focus:outline-none"
154
+ >
155
+ Kirim
156
+ </button>
157
+ </div>
158
+ </form>
159
+ </div>
160
+
161
+ {% if prediction is not none %}
162
+ <div class="flex flex-col justify-center items-center mt-6 p-6 rounded-lg shadow-md bg-blue-500 text-center">
163
+ <h2 class="text-white text-2xl lg:text-3xl font-semibold">Prediksi Jumlah Penumpang</h2>
164
+ <p class="text-white text-lg mt-2">Hasil prediksi untuk 5 periode ke depan:</p>
165
+
166
+ <table class="mt-4 bg-white shadow-lg rounded-lg w-full max-w-md">
167
+ <thead>
168
+ <tr class="bg-blue-700 text-white">
169
+ <th class="py-2 px-4 border">Periode</th>
170
+ <th class="py-2 px-4 border">Prediksi Fitur 1</th>
171
+ <th class="py-2 px-4 border">Prediksi Fitur 2</th>
172
+ </tr>
173
+ </thead>
174
+ <tbody>
175
+ {% for pred in prediction %}
176
+ <tr class="{{ loop.cycle('bg-gray-100', 'bg-gray-200') }}">
177
+ <td class="py-2 px-4 border">Periode {{ loop.index }}</td>
178
+ <td class="py-2 px-4 border">{{ pred[0] | round(2) }}</td>
179
+ <td class="py-2 px-4 border">{{ pred[1] | round(2) }}</td>
180
+ </tr>
181
+ {% endfor %}
182
+ </tbody>
183
+ </table>
184
+
185
+ <p class="text-white text-sm mt-4 italic">* Prediksi berdasarkan data yang diinputkan.</p>
186
+ </div>
187
+ {% endif %}
188
+
189
+ </div>
190
+ </div>
191
+ </div>
192
+
193
+ <script>
194
+ document
195
+ .getElementById("clear-button")
196
+ .addEventListener("click", function () {
197
+ const inputs = document.querySelectorAll('input[type="number"]');
198
+ inputs.forEach((input) => (input.value = ""));
199
+ });
200
+ </script>
201
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.3.0/flowbite.min.js"></script>
202
+ </body>
203
+ </html>