d-e-e-k-11 commited on
Commit
5575a8a
·
verified ·
1 Parent(s): 7d37ae9

Upload folder using huggingface_hub

Browse files
Student_Marks.csv ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ number_courses,time_study,Marks
2
+ 3,4.508,19.202
3
+ 4,0.096,7.734
4
+ 4,3.133,13.811
5
+ 6,7.909,53.018
6
+ 8,7.811,55.299
7
+ 6,3.211,17.822
8
+ 3,6.063,29.889
9
+ 5,3.413,17.264
10
+ 4,4.410,20.348
11
+ 3,6.173,30.862
12
+ 3,7.353,42.036
13
+ 7,0.423,12.132
14
+ 7,4.218,24.318
15
+ 3,4.274,17.672
16
+ 3,2.908,11.397
17
+ 4,4.260,19.466
18
+ 5,5.719,30.548
19
+ 8,6.080,38.490
20
+ 6,7.711,50.986
21
+ 8,3.977,25.133
22
+ 4,4.733,22.073
23
+ 6,6.126,35.939
24
+ 5,2.051,12.209
25
+ 7,4.875,28.043
26
+ 4,3.635,16.517
27
+ 3,1.407,6.623
28
+ 7,0.508,12.647
29
+ 8,4.378,26.532
30
+ 5,0.156,9.333
31
+ 4,1.299,8.837
32
+ 8,3.864,24.172
33
+ 3,1.923,8.100
34
+ 8,0.932,15.038
35
+ 6,6.594,39.965
36
+ 3,4.083,17.171
37
+ 3,7.543,43.978
38
+ 4,2.966,13.119
39
+ 6,7.283,46.453
40
+ 7,6.533,41.358
41
+ 6,7.775,51.142
42
+ 4,0.140,7.336
43
+ 6,2.754,15.725
44
+ 6,3.591,19.771
45
+ 5,1.557,10.429
46
+ 4,1.954,9.742
47
+ 3,2.061,8.924
48
+ 4,3.797,16.703
49
+ 4,4.779,22.701
50
+ 3,5.635,26.882
51
+ 5,3.913,19.106
52
+ 6,6.703,40.602
53
+ 6,4.130,22.184
54
+ 4,0.771,7.892
55
+ 7,6.049,36.653
56
+ 8,7.591,53.158
57
+ 7,2.913,18.238
58
+ 8,7.641,53.359
59
+ 7,7.649,51.583
60
+ 3,6.198,31.236
61
+ 8,7.468,51.343
62
+ 6,0.376,10.522
63
+ 4,2.438,10.844
64
+ 6,3.606,19.590
65
+ 3,4.869,21.379
66
+ 7,0.130,12.591
67
+ 6,2.142,13.562
68
+ 4,5.473,27.569
69
+ 3,0.550,6.185
70
+ 4,1.395,8.920
71
+ 6,3.948,21.400
72
+ 4,3.736,16.606
73
+ 5,2.518,13.416
74
+ 3,4.633,20.398
75
+ 3,1.629,7.014
76
+ 4,6.954,39.952
77
+ 3,0.803,6.217
78
+ 5,6.379,36.746
79
+ 8,5.985,38.278
80
+ 7,7.451,49.544
81
+ 3,0.805,6.349
82
+ 7,7.957,54.321
83
+ 8,2.262,17.705
84
+ 4,7.410,44.099
85
+ 5,3.197,16.106
86
+ 8,1.982,16.461
87
+ 8,6.201,39.957
88
+ 7,4.067,23.149
89
+ 3,1.033,6.053
90
+ 5,1.803,11.253
91
+ 7,6.376,40.024
92
+ 7,4.182,24.394
93
+ 8,2.730,19.564
94
+ 4,5.027,23.916
95
+ 8,6.471,42.426
96
+ 8,3.919,24.451
97
+ 6,3.561,19.128
98
+ 3,0.301,5.609
99
+ 4,7.163,41.444
100
+ 7,0.309,12.027
101
+ 3,6.335,32.357
app.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify, render_template
2
+ import tensorflow as tf
3
+ import numpy as np
4
+ import joblib
5
+ import os
6
+
7
+ app = Flask(__name__)
8
+
9
+ # Load model and scalers globally for efficiency
10
+ MODEL_PATH = 'student_marks_rnn_model.h5'
11
+ SCALER_X_PATH = 'scaler_X.pkl'
12
+ SCALER_Y_PATH = 'scaler_y.pkl'
13
+
14
+ model = None
15
+ scaler_X = None
16
+ scaler_y = None
17
+
18
+ def load_resources():
19
+ global model, scaler_X, scaler_y
20
+ if os.path.exists(MODEL_PATH) and os.path.exists(SCALER_X_PATH) and os.path.exists(SCALER_Y_PATH):
21
+ model = tf.keras.models.load_model(MODEL_PATH)
22
+ scaler_X = joblib.load(SCALER_X_PATH)
23
+ scaler_y = joblib.load(SCALER_Y_PATH)
24
+ return True
25
+ return False
26
+
27
+ @app.route('/')
28
+ def index():
29
+ return render_template('index.html')
30
+
31
+ @app.route('/predict', methods=['POST'])
32
+ def predict():
33
+ if model is None:
34
+ if not load_resources():
35
+ return jsonify({'error': 'Model or scalers not found. Run training first.'}), 500
36
+
37
+ try:
38
+ data = request.get_json()
39
+ num_courses = float(data['num_courses'])
40
+ time_study = float(data['time_study'])
41
+
42
+ # Preprocess
43
+ input_data = np.array([[num_courses, time_study]])
44
+ input_scaled = scaler_X.transform(input_data)
45
+ input_reshaped = input_scaled.reshape((1, 1, 2))
46
+
47
+ # Predict
48
+ prediction_scaled = model.predict(input_reshaped)
49
+ prediction = scaler_y.inverse_transform(prediction_scaled)
50
+
51
+ result = float(prediction[0][0])
52
+ return jsonify({'marks': round(result, 2)})
53
+
54
+ except Exception as e:
55
+ return jsonify({'error': str(e)}), 400
56
+
57
+ if __name__ == '__main__':
58
+ load_resources()
59
+ app.run(debug=True, port=5000)
implementation_plan.md ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Implementation Plan - Student Marks Prediction using RNN
2
+
3
+ This document outlines the steps to build an end-to-end Recurrent Neural Network (RNN) model to predict student marks based on the number of courses and study time.
4
+
5
+ ## 1. Data Exploration & Preprocessing
6
+ - Load `Student_Marks.csv`.
7
+ - Inspect data quality and statistics.
8
+ - Normalize features (`number_courses`, `time_study`) and target (`Marks`) using `MinMaxScaler` or `StandardScaler`.
9
+ - Split the dataset into training (80%) and testing (20%) sets.
10
+ - **RNN Reshaping**: Reshape the input data to `(samples, time_steps, features)`. Since this is a simple tabular dataset, we will use `time_steps = 1`.
11
+
12
+ ## 2. Model Architecture
13
+ - **Input Layer**: Shape `(1, 2)`.
14
+ - **RNN Layer**: Use `SimpleRNN` or `LSTM` with 64 units.
15
+ - **Dense Layer**: Hidden layer with 32 units, ReLU activation.
16
+ - **Output Layer**: Single neuron for regression (predicted Marks).
17
+ - **Compile**: Use `Adam` optimizer and `Mean Squared Error` (MSE) loss.
18
+
19
+ ## 3. Training
20
+ - Train for 100 epochs (adjustable).
21
+ - Use a validation split to monitor overfitting.
22
+
23
+ ## 4. Evaluation & Visualization
24
+ - Evaluate the model on the test set.
25
+ - Plot training and validation loss curves.
26
+ - Compare predicted values with actual values.
27
+
28
+ ## 5. Inference
29
+ - Create a script to make predictions on new data.
loss_plot.png ADDED
predict.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import tensorflow as tf
2
+ import numpy as np
3
+ import joblib
4
+ import os
5
+
6
+ def predict_marks(num_courses, time_study):
7
+ # Load model and scalers
8
+ if not (os.path.exists('student_marks_rnn_model.h5') and
9
+ os.path.exists('scaler_X.pkl') and
10
+ os.path.exists('scaler_y.pkl')):
11
+ return "Error: Model or scalers not found. Please run train_rnn.py first."
12
+
13
+ model = tf.keras.models.load_model('student_marks_rnn_model.h5')
14
+ scaler_X = joblib.load('scaler_X.pkl')
15
+ scaler_y = joblib.load('scaler_y.pkl')
16
+
17
+ # Preprocess input
18
+ input_data = np.array([[num_courses, time_study]])
19
+ input_scaled = scaler_X.transform(input_data)
20
+
21
+ # Reshape for RNN (Samples, TimeSteps, Features)
22
+ input_reshaped = input_scaled.reshape((1, 1, 2))
23
+
24
+ # Predict
25
+ prediction_scaled = model.predict(input_reshaped)
26
+ prediction = scaler_y.inverse_transform(prediction_scaled)
27
+
28
+ return prediction[0][0]
29
+
30
+ if __name__ == "__main__":
31
+ print("--- Student Marks Prediction RNN ---")
32
+ try:
33
+ nc = float(input("Enter number of courses: "))
34
+ ts = float(input("Enter time spent studying (hours): "))
35
+
36
+ result = predict_marks(nc, ts)
37
+ if isinstance(result, str):
38
+ print(result)
39
+ else:
40
+ print(f"\nPredicted Marks: {result:.2f}")
41
+ except ValueError:
42
+ print("Invalid input. Please enter numeric values.")
43
+ except Exception as e:
44
+ print(f"Error: {e}")
scaler_X.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5fc9609d21ece6a569e3e84700fa0352b1c94950bfdd1d2e9d36766f4a6baebb
3
+ size 743
scaler_y.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:91b3966771b5cab72bd1dd1f32373cc9fd6616a85606a61852b0b61381a76f3d
3
+ size 719
static/css/style.css ADDED
@@ -0,0 +1,333 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --primary: #6366f1;
3
+ --secondary: #a855f7;
4
+ --accent: #ec4899;
5
+ --bg: #0f172a;
6
+ --text: #f8fafc;
7
+ --glass: rgba(255, 255, 255, 0.05);
8
+ --border: rgba(255, 255, 255, 0.1);
9
+ }
10
+
11
+ * {
12
+ margin: 0;
13
+ padding: 0;
14
+ box-sizing: border-box;
15
+ }
16
+
17
+ body {
18
+ font-family: 'Outfit', sans-serif;
19
+ background-color: var(--bg);
20
+ color: var(--text);
21
+ overflow-x: hidden;
22
+ min-height: 100vh;
23
+ display: flex;
24
+ justify-content: center;
25
+ align-items: center;
26
+ }
27
+
28
+ /* Background Animations */
29
+ .background-blobs {
30
+ position: fixed;
31
+ top: 0;
32
+ left: 0;
33
+ width: 100%;
34
+ height: 100%;
35
+ z-index: -1;
36
+ overflow: hidden;
37
+ }
38
+
39
+ .blob {
40
+ position: absolute;
41
+ border-radius: 50%;
42
+ filter: blur(80px);
43
+ opacity: 0.4;
44
+ transition: all 1s ease;
45
+ animation: float 20s infinite alternate;
46
+ }
47
+
48
+ .blob-1 {
49
+ width: 400px;
50
+ height: 400px;
51
+ background: var(--primary);
52
+ top: -100px;
53
+ right: -100px;
54
+ }
55
+
56
+ .blob-2 {
57
+ width: 500px;
58
+ height: 500px;
59
+ background: var(--secondary);
60
+ bottom: -150px;
61
+ left: -150px;
62
+ animation-delay: -5s;
63
+ }
64
+
65
+ .blob-3 {
66
+ width: 300px;
67
+ height: 300px;
68
+ background: var(--accent);
69
+ top: 50%;
70
+ left: 10%;
71
+ animation-delay: -10s;
72
+ }
73
+
74
+ @keyframes float {
75
+ 0% {
76
+ transform: translate(0, 0) scale(1);
77
+ }
78
+
79
+ 100% {
80
+ transform: translate(100px, 50px) scale(1.1);
81
+ }
82
+ }
83
+
84
+ /* Layout */
85
+ .container {
86
+ max-width: 1000px;
87
+ width: 90%;
88
+ padding: 2rem 0;
89
+ }
90
+
91
+ header {
92
+ text-align: center;
93
+ padding: 2rem;
94
+ margin-bottom: 2rem;
95
+ }
96
+
97
+ h1 {
98
+ font-size: 3rem;
99
+ font-weight: 800;
100
+ letter-spacing: -1px;
101
+ margin-bottom: 0.5rem;
102
+ }
103
+
104
+ .accent {
105
+ background: linear-gradient(to right, var(--primary), var(--secondary));
106
+ -webkit-background-clip: text;
107
+ background-clip: text;
108
+ -webkit-text-fill-color: transparent;
109
+ }
110
+
111
+ header p {
112
+ color: #94a3b8;
113
+ font-size: 1.1rem;
114
+ }
115
+
116
+ /* Glass Card */
117
+ .glass-card {
118
+ background: var(--glass);
119
+ backdrop-filter: blur(12px);
120
+ -webkit-backdrop-filter: blur(12px);
121
+ border: 1px solid var(--border);
122
+ border-radius: 24px;
123
+ padding: 2.5rem;
124
+ box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
125
+ }
126
+
127
+ .prediction-grid {
128
+ display: grid;
129
+ grid-template-columns: 1fr 1fr;
130
+ gap: 2rem;
131
+ }
132
+
133
+ @media (max-width: 768px) {
134
+ .prediction-grid {
135
+ grid-template-columns: 1fr;
136
+ }
137
+ }
138
+
139
+ /* Inputs */
140
+ .input-group {
141
+ margin-bottom: 1.5rem;
142
+ }
143
+
144
+ label {
145
+ display: block;
146
+ margin-bottom: 0.5rem;
147
+ font-weight: 600;
148
+ color: #cbd5e1;
149
+ }
150
+
151
+ input {
152
+ width: 100%;
153
+ background: rgba(0, 0, 0, 0.2);
154
+ border: 1px solid var(--border);
155
+ border-radius: 12px;
156
+ padding: 1rem;
157
+ color: white;
158
+ font-size: 1rem;
159
+ font-family: inherit;
160
+ transition: all 0.3s ease;
161
+ }
162
+
163
+ input:focus {
164
+ outline: none;
165
+ border-color: var(--primary);
166
+ box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.2);
167
+ }
168
+
169
+ /* Button */
170
+ button {
171
+ width: 100%;
172
+ background: linear-gradient(135deg, var(--primary), var(--secondary));
173
+ border: none;
174
+ border-radius: 12px;
175
+ padding: 1rem;
176
+ color: white;
177
+ font-size: 1.1rem;
178
+ font-weight: 700;
179
+ cursor: pointer;
180
+ transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
181
+ position: relative;
182
+ overflow: hidden;
183
+ }
184
+
185
+ button:hover {
186
+ transform: translateY(-3px);
187
+ box-shadow: 0 10px 20px -5px rgba(99, 102, 241, 0.5);
188
+ }
189
+
190
+ button:active {
191
+ transform: translateY(0);
192
+ }
193
+
194
+ button:disabled {
195
+ opacity: 0.7;
196
+ cursor: not-allowed;
197
+ }
198
+
199
+ /* Loader */
200
+ .loader-dots {
201
+ display: none;
202
+ justify-content: center;
203
+ gap: 5px;
204
+ position: absolute;
205
+ top: 50%;
206
+ left: 50%;
207
+ transform: translate(-50%, -50%);
208
+ }
209
+
210
+ .loader-dots span {
211
+ width: 8px;
212
+ height: 8px;
213
+ background: white;
214
+ border-radius: 50%;
215
+ animation: bounce 0.6s infinite alternate;
216
+ }
217
+
218
+ .loader-dots span:nth-child(2) {
219
+ animation-delay: 0.2s;
220
+ }
221
+
222
+ .loader-dots span:nth-child(3) {
223
+ animation-delay: 0.4s;
224
+ }
225
+
226
+ @keyframes bounce {
227
+ to {
228
+ transform: translateY(-10px);
229
+ opacity: 0.3;
230
+ }
231
+ }
232
+
233
+ /* Result Section */
234
+ .result-section {
235
+ display: flex;
236
+ flex-direction: column;
237
+ justify-content: center;
238
+ align-items: center;
239
+ text-align: center;
240
+ min-height: 300px;
241
+ }
242
+
243
+ .placeholder .icon-pulse {
244
+ font-size: 4rem;
245
+ margin-bottom: 1rem;
246
+ animation: pulse 2s infinite;
247
+ }
248
+
249
+ @keyframes pulse {
250
+ 0% {
251
+ transform: scale(1);
252
+ }
253
+
254
+ 50% {
255
+ transform: scale(1.1);
256
+ }
257
+
258
+ 100% {
259
+ transform: scale(1);
260
+ }
261
+ }
262
+
263
+ .hidden {
264
+ display: none;
265
+ }
266
+
267
+ .score-circle {
268
+ width: 180px;
269
+ height: 180px;
270
+ border: 5px solid var(--primary);
271
+ border-radius: 50%;
272
+ display: flex;
273
+ flex-direction: column;
274
+ justify-content: center;
275
+ align-items: center;
276
+ margin-bottom: 1.5rem;
277
+ background: radial-gradient(circle, rgba(99, 102, 241, 0.1) 0%, transparent 70%);
278
+ }
279
+
280
+ #scoreValue {
281
+ font-size: 3rem;
282
+ font-weight: 800;
283
+ color: var(--text);
284
+ }
285
+
286
+ .score-circle p {
287
+ text-transform: uppercase;
288
+ font-size: 0.8rem;
289
+ letter-spacing: 2px;
290
+ color: #94a3b8;
291
+ }
292
+
293
+ /* Animations */
294
+ .animate-fade-in {
295
+ animation: fadeIn 1s ease-out;
296
+ }
297
+
298
+ .animate-slide-up {
299
+ animation: slideUp 0.8s ease-out;
300
+ }
301
+
302
+ .animate-slide-up-delayed {
303
+ animation: slideUp 0.8s ease-out 0.2s backwards;
304
+ }
305
+
306
+ @keyframes fadeIn {
307
+ from {
308
+ opacity: 0;
309
+ }
310
+
311
+ to {
312
+ opacity: 1;
313
+ }
314
+ }
315
+
316
+ @keyframes slideUp {
317
+ from {
318
+ opacity: 0;
319
+ transform: translateY(30px);
320
+ }
321
+
322
+ to {
323
+ opacity: 1;
324
+ transform: translateY(0);
325
+ }
326
+ }
327
+
328
+ footer {
329
+ text-align: center;
330
+ margin-top: 3rem;
331
+ color: #64748b;
332
+ font-size: 0.9rem;
333
+ }
student_marks_rnn_model.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:29409cd915cafaa6e6f738015011063472d41074c74985eaecfa3d139f156745
3
+ size 411720
templates/index.html ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>GradePredict AI | Student Performance Analytics</title>
7
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
8
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&display=swap" rel="stylesheet">
9
+ </head>
10
+ <body>
11
+ <div class="background-blobs">
12
+ <div class="blob blob-1"></div>
13
+ <div class="blob blob-2"></div>
14
+ <div class="blob blob-3"></div>
15
+ </div>
16
+
17
+ <main class="container">
18
+ <header class="glass-card animate-fade-in">
19
+ <h1>GradePredict <span class="accent">AI</span></h1>
20
+ <p>Advanced RNN-based Student Performance Forecasting</p>
21
+ </header>
22
+
23
+ <section class="prediction-grid">
24
+ <div class="glass-card input-section animate-slide-up">
25
+ <h2>Predict Your Score</h2>
26
+ <form id="predictionForm">
27
+ <div class="input-group">
28
+ <label for="num_courses">Number of Courses</label>
29
+ <input type="number" id="num_courses" name="num_courses" placeholder="e.g. 5" min="1" max="100" required>
30
+ </div>
31
+ <div class="input-group">
32
+ <label for="time_study">Daily Study Hours</label>
33
+ <input type="number" id="time_study" name="time_study" placeholder="e.g. 4.5" step="0.1" min="0" max="24" required>
34
+ </div>
35
+ <button type="submit" id="predictBtn">
36
+ <span class="btn-text">Calculate Score</span>
37
+ <div class="loader-dots" id="loader">
38
+ <span></span><span></span><span></span>
39
+ </div>
40
+ </button>
41
+ </form>
42
+ </div>
43
+
44
+ <div class="glass-card result-section animate-slide-up-delayed">
45
+ <div id="resultPlaceholder" class="placeholder">
46
+ <div class="icon-pulse">🎓</div>
47
+ <p>Enter your details to generate prediction</p>
48
+ </div>
49
+ <div id="resultOutput" class="output hidden">
50
+ <div class="score-circle">
51
+ <span id="scoreValue">0.00</span>
52
+ <p>Marks</p>
53
+ </div>
54
+ <h3>Predicted Efficiency</h3>
55
+ <p id="analysisText">Based on your input, here is your forecasted performance.</p>
56
+ </div>
57
+ </div>
58
+ </section>
59
+
60
+ <footer class="animate-fade-in">
61
+ <p>&copy; 2026 GradePredict AI | Powered by LSTM Recurrent Neural Networks</p>
62
+ </footer>
63
+ </main>
64
+
65
+ <script>
66
+ document.getElementById('predictionForm').addEventListener('submit', async (e) => {
67
+ e.preventDefault();
68
+
69
+ const btn = document.getElementById('predictBtn');
70
+ const loader = document.getElementById('loader');
71
+ const btnText = btn.querySelector('.btn-text');
72
+ const placeholder = document.getElementById('resultPlaceholder');
73
+ const output = document.getElementById('resultOutput');
74
+ const scoreValue = document.getElementById('scoreValue');
75
+
76
+ // UI State: Loading
77
+ btn.disabled = true;
78
+ btnText.style.opacity = '0';
79
+ loader.style.display = 'flex';
80
+
81
+ const formData = {
82
+ num_courses: document.getElementById('num_courses').value,
83
+ time_study: document.getElementById('time_study').value
84
+ };
85
+
86
+ try {
87
+ const response = await fetch('/predict', {
88
+ method: 'POST',
89
+ headers: { 'Content-Type': 'application/json' },
90
+ body: JSON.stringify(formData)
91
+ });
92
+
93
+ const data = await response.json();
94
+
95
+ if (data.marks !== undefined) {
96
+ placeholder.classList.add('hidden');
97
+ output.classList.remove('hidden');
98
+
99
+ // Animate counter
100
+ animateValue(scoreValue, 0, data.marks, 1000);
101
+ } else {
102
+ alert('Error: ' + data.error);
103
+ }
104
+ } catch (err) {
105
+ alert('Connection Error: ' + err.message);
106
+ } finally {
107
+ btn.disabled = false;
108
+ btnText.style.opacity = '1';
109
+ loader.style.display = 'none';
110
+ }
111
+ });
112
+
113
+ function animateValue(obj, start, end, duration) {
114
+ let startTimestamp = null;
115
+ const step = (timestamp) => {
116
+ if (!startTimestamp) startTimestamp = timestamp;
117
+ const progress = Math.min((timestamp - startTimestamp) / duration, 1);
118
+ obj.innerHTML = (progress * (end - start) + start).toFixed(2);
119
+ if (progress < 1) {
120
+ window.requestAnimationFrame(step);
121
+ }
122
+ };
123
+ window.requestAnimationFrame(step);
124
+ }
125
+ </script>
126
+ </body>
127
+ </html>
train_rnn.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ import matplotlib.pyplot as plt
4
+ import tensorflow as tf
5
+ from tensorflow.keras.models import Sequential
6
+ from tensorflow.keras.layers import SimpleRNN, Dense, LSTM, Dropout
7
+ from sklearn.model_selection import train_test_split
8
+ from sklearn.preprocessing import MinMaxScaler
9
+ import joblib
10
+ import os
11
+
12
+ # 1. Load Data
13
+ def load_data(file_path):
14
+ df = pd.read_csv(file_path)
15
+ print("Dataset Head:")
16
+ print(df.head())
17
+ return df
18
+
19
+ # 2. Preprocessing
20
+ def preprocess_data(df):
21
+ X = df[['number_courses', 'time_study']].values
22
+ y = df['Marks'].values.reshape(-1, 1)
23
+
24
+ # Scaling
25
+ scaler_X = MinMaxScaler()
26
+ scaler_y = MinMaxScaler()
27
+
28
+ X_scaled = scaler_X.fit_transform(X)
29
+ y_scaled = scaler_y.fit_transform(y)
30
+
31
+ # Split
32
+ X_train, X_test, y_train, y_test = train_test_split(X_scaled, y_scaled, test_size=0.2, random_state=42)
33
+
34
+ # Reshape for RNN: (samples, time_steps, features)
35
+ # Here time_steps = 1
36
+ X_train = X_train.reshape((X_train.shape[0], 1, X_train.shape[1]))
37
+ X_test = X_test.reshape((X_test.shape[0], 1, X_test.shape[1]))
38
+
39
+ return X_train, X_test, y_train, y_test, scaler_X, scaler_y
40
+
41
+ # 3. Build Model
42
+ def build_model(input_shape):
43
+ model = Sequential([
44
+ LSTM(64, activation='relu', input_shape=input_shape, return_sequences=True),
45
+ Dropout(0.2),
46
+ LSTM(32, activation='relu'),
47
+ Dense(16, activation='relu'),
48
+ Dense(1) # Output for regression
49
+ ])
50
+
51
+ model.compile(optimizer='adam', loss='mse', metrics=['mae'])
52
+ return model
53
+
54
+ # 4. Main Execution
55
+ if __name__ == "__main__":
56
+ file_path = 'Student_Marks.csv'
57
+ if not os.path.exists(file_path):
58
+ print(f"Error: {file_path} not found.")
59
+ exit()
60
+
61
+ df = load_data(file_path)
62
+ X_train, X_test, y_train, y_test, scaler_X, scaler_y = preprocess_data(df)
63
+
64
+ print(f"X_train shape: {X_train.shape}")
65
+ print(f"y_train shape: {y_train.shape}")
66
+
67
+ model = build_model((X_train.shape[1], X_train.shape[2]))
68
+ model.summary()
69
+
70
+ # Training
71
+ print("\nStarting training...")
72
+ history = model.fit(
73
+ X_train, y_train,
74
+ epochs=100,
75
+ batch_size=8,
76
+ validation_split=0.1,
77
+ verbose=1
78
+ )
79
+
80
+ # Evaluation
81
+ print("\nEvaluating model...")
82
+ loss, mae = model.evaluate(X_test, y_test)
83
+ print(f"Test Loss (MSE): {loss:.4f}")
84
+ print(f"Test MAE: {mae:.4f}")
85
+
86
+ # Plot History
87
+ plt.figure(figsize=(10, 5))
88
+ plt.plot(history.history['loss'], label='Train Loss')
89
+ plt.plot(history.history['val_loss'], label='Val Loss')
90
+ plt.title('Model Loss (MSE)')
91
+ plt.xlabel('Epochs')
92
+ plt.ylabel('Loss')
93
+ plt.legend()
94
+ plt.savefig('loss_plot.png')
95
+ print("Loss plot saved as 'loss_plot.png'")
96
+
97
+ # Predictions
98
+ y_pred_scaled = model.predict(X_test)
99
+ y_pred = scaler_y.inverse_transform(y_pred_scaled)
100
+ y_actual = scaler_y.inverse_transform(y_test)
101
+
102
+ # Compare first 5
103
+ print("\nSample Predictions:")
104
+ for i in range(5):
105
+ print(f"Actual: {y_actual[i][0]:.2f}, Predicted: {y_pred[i][0]:.2f}")
106
+
107
+ # Save Model and Scalers
108
+ model.save('student_marks_rnn_model.h5')
109
+ joblib.dump(scaler_X, 'scaler_X.pkl')
110
+ joblib.dump(scaler_y, 'scaler_y.pkl')
111
+ print("\nModel saved as 'student_marks_rnn_model.h5'")
112
+ print("Scalers saved as 'scaler_X.pkl' and 'scaler_y.pkl'")