adn commited on
Commit
d689965
Β·
verified Β·
1 Parent(s): 4c417c1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -37
app.py CHANGED
@@ -8,6 +8,7 @@ import os
8
  import logging
9
  from typing import Dict, Any, List
10
  from transformers import AutoTokenizer
 
11
 
12
  # Setup logging
13
  logging.basicConfig(level=logging.INFO)
@@ -18,11 +19,20 @@ MODEL_PATH = "model.tflite"
18
  TOKENIZER_PATH = "tokenizer"
19
  MAX_LENGTH = 128
20
 
 
 
 
 
 
 
 
 
 
21
  # Inisialisasi FastAPI
22
  app = FastAPI(
23
  title="Damkar Classification API (TFLite)",
24
- description="API untuk klasifikasi tipe laporan damkar menggunakan TFLite model - Raw Output",
25
- version="1.0.0"
26
  )
27
 
28
  # Global variables
@@ -69,14 +79,14 @@ async def load_model():
69
  raise e
70
 
71
  def predict_tflite(text: str) -> Dict[str, Any]:
72
- """Fungsi prediksi menggunakan TFLite model - mengembalikan raw output"""
73
  global interpreter, tokenizer, input_details, output_details
74
 
75
  if not all([interpreter, tokenizer]):
76
  raise HTTPException(status_code=503, detail="Model components not loaded")
77
 
78
  try:
79
- # Resize input tensors
80
  interpreter.resize_tensor_input(input_details[0]['index'], [1, MAX_LENGTH])
81
  interpreter.resize_tensor_input(input_details[1]['index'], [1, MAX_LENGTH])
82
  interpreter.resize_tensor_input(input_details[2]['index'], [1, MAX_LENGTH])
@@ -96,7 +106,7 @@ def predict_tflite(text: str) -> Dict[str, Any]:
96
  token_type_ids = encoded['token_type_ids'].astype(np.int32)
97
  attention_mask = encoded['attention_mask'].astype(np.int32)
98
 
99
- # Set tensors - gunakan urutan yang benar
100
  interpreter.set_tensor(input_details[0]['index'], attention_mask)
101
  interpreter.set_tensor(input_details[1]['index'], input_ids)
102
  interpreter.set_tensor(input_details[2]['index'], token_type_ids)
@@ -104,7 +114,7 @@ def predict_tflite(text: str) -> Dict[str, Any]:
104
  # Run inference
105
  interpreter.invoke()
106
 
107
- # Get raw output
108
  raw_output = interpreter.get_tensor(output_details[0]['index'])
109
 
110
  # Hitung probabilitas dengan softmax
@@ -114,8 +124,12 @@ def predict_tflite(text: str) -> Dict[str, Any]:
114
  predicted_class_index = int(np.argmax(raw_output, axis=1)[0])
115
  max_confidence = float(np.max(probabilities))
116
 
 
 
 
117
  return {
118
  "predicted_class_index": predicted_class_index,
 
119
  "confidence": max_confidence,
120
  "raw_output": raw_output[0].tolist(), # Convert numpy array to list
121
  "probabilities": probabilities.tolist(),
@@ -136,6 +150,7 @@ class InputText(BaseModel):
136
 
137
  class PredictionResponse(BaseModel):
138
  predicted_class_index: int
 
139
  confidence: float
140
  raw_output: List[float]
141
  probabilities: List[float]
@@ -148,7 +163,7 @@ HTML_TEMPLATE = """
148
  <!DOCTYPE html>
149
  <html>
150
  <head>
151
- <title>Damkar Classification - Raw Output</title>
152
  <meta charset="UTF-8">
153
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
154
  <style>
@@ -166,7 +181,7 @@ HTML_TEMPLATE = """
166
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
167
  }
168
  h1 {
169
- color: #333;
170
  text-align: center;
171
  margin-bottom: 30px;
172
  }
@@ -255,28 +270,33 @@ HTML_TEMPLATE = """
255
  color: #0056b3;
256
  }
257
  .raw-output {
258
- background-color: #f8f9fa;
259
  padding: 10px;
260
  border-radius: 4px;
261
  font-family: monospace;
262
  font-size: 12px;
263
  margin: 10px 0;
264
- max-height: 200px;
265
  overflow-y: auto;
 
 
266
  }
267
- .model-info {
268
- background-color: #e7f3ff;
 
 
 
 
269
  padding: 10px;
270
- border-radius: 4px;
271
- margin: 10px 0;
272
- font-size: 14px;
273
  }
274
  </style>
275
  </head>
276
  <body>
277
  <div class="container">
278
- <h1>πŸš’ Damkar Classification - Raw Output</h1>
279
- <p style="text-align: center; color: #666;">Menampilkan output mentah dari TFLite model tanpa label encoder</p>
280
 
281
  <div class="form-group">
282
  <label for="textInput">Masukkan teks laporan:</label>
@@ -299,16 +319,23 @@ HTML_TEMPLATE = """
299
  <div class="example-text" onclick="setExample('ular masuk ke dalam rumah warga')">
300
  🐍 "ular masuk ke dalam rumah warga"
301
  </div>
302
- <div class="example-text" onclick="setExample('kucing terjebak di atas pohon tinggi')">
303
- 🐱 "kucing terjebak di atas pohon tinggi"
304
- </div>
305
- <div class="example-text" onclick="setExample('pohon tumbang menghalangi jalan raya')">
306
  🌳 "pohon tumbang menghalangi jalan raya"
307
  </div>
 
 
 
308
  </div>
309
  </div>
310
 
311
  <script>
 
 
 
 
 
 
 
312
  function setExample(text) {
313
  document.getElementById('textInput').value = text;
314
  }
@@ -341,34 +368,35 @@ HTML_TEMPLATE = """
341
  const data = await response.json();
342
 
343
  if (response.ok) {
 
 
344
  let resultHTML = `
345
  <h3>Hasil Prediksi:</h3>
346
- <p><strong>Predicted Class Index:</strong> ${data.predicted_class_index}</p>
347
  <p><strong>Confidence:</strong> ${(data.confidence * 100).toFixed(2)}%</p>
348
 
349
- <div class="model-info">
350
- <strong>Model Info:</strong><br>
351
- Output Shape: ${JSON.stringify(data.model_info.output_shape)}<br>
352
- Number of Classes: ${data.model_info.num_classes}
353
- </div>
354
-
355
- <h4>Probabilitas per Class:</h4>
356
  `;
357
 
358
  data.probabilities.forEach((prob, index) => {
359
  const percentage = (prob * 100).toFixed(4);
360
  const isMax = index === data.predicted_class_index;
 
361
  resultHTML += `
362
  <div class="prob-item" style="${isMax ? 'background-color: #fff3cd; font-weight: bold;' : ''}">
363
- <span>Class ${index}</span>
364
  <span>${percentage}%</span>
365
  </div>
366
  `;
367
  });
368
 
369
  resultHTML += `
370
- <h4>Raw Output (Logits):</h4>
371
- <div class="raw-output">${JSON.stringify(data.raw_output, null, 2)}</div>
 
 
 
 
372
  `;
373
 
374
  showResult('success', resultHTML);
@@ -390,9 +418,9 @@ HTML_TEMPLATE = """
390
  resultDiv.style.display = 'block';
391
  }
392
 
393
- // Allow Enter key to submit
394
- document.getElementById('textInput').addEventListener('keypress', function(e) {
395
- if (e.key === 'Enter' && e.ctrlKey) {
396
  predict();
397
  }
398
  });
@@ -433,7 +461,8 @@ def health_check():
433
  "dtype": str(detail['dtype'])
434
  } for i, detail in enumerate(output_details)
435
  ],
436
- "max_length": MAX_LENGTH
 
437
  }
438
  }
439
 
@@ -463,7 +492,7 @@ def test_endpoint():
463
  return {
464
  "message": "TFLite API is working!",
465
  "status": "ok",
466
- "version": "raw_output",
467
  "endpoints": {
468
  "ui": "/",
469
  "predict": "/predict",
 
8
  import logging
9
  from typing import Dict, Any, List
10
  from transformers import AutoTokenizer
11
+ import json
12
 
13
  # Setup logging
14
  logging.basicConfig(level=logging.INFO)
 
19
  TOKENIZER_PATH = "tokenizer"
20
  MAX_LENGTH = 128
21
 
22
+ # Class label mapping
23
+ CLASS_LABELS = {
24
+ 0: "Evakuasi/Penyelamatan Hewan",
25
+ 1: "Kebakaran",
26
+ 2: "Layanan Lingkungan & Fasilitas Umum",
27
+ 3: "Penyelamatan Non Hewan & Bantuan Teknis"
28
+ }
29
+
30
+
31
  # Inisialisasi FastAPI
32
  app = FastAPI(
33
  title="Damkar Classification API (TFLite)",
34
+ description="API untuk klasifikasi tipe laporan damkar menggunakan TFLite model",
35
+ version="1.1.0"
36
  )
37
 
38
  # Global variables
 
79
  raise e
80
 
81
  def predict_tflite(text: str) -> Dict[str, Any]:
82
+ """Fungsi prediksi menggunakan TFLite model - mengembalikan output dengan label"""
83
  global interpreter, tokenizer, input_details, output_details
84
 
85
  if not all([interpreter, tokenizer]):
86
  raise HTTPException(status_code=503, detail="Model components not loaded")
87
 
88
  try:
89
+ # Resize input tensors (jika diperlukan)
90
  interpreter.resize_tensor_input(input_details[0]['index'], [1, MAX_LENGTH])
91
  interpreter.resize_tensor_input(input_details[1]['index'], [1, MAX_LENGTH])
92
  interpreter.resize_tensor_input(input_details[2]['index'], [1, MAX_LENGTH])
 
106
  token_type_ids = encoded['token_type_ids'].astype(np.int32)
107
  attention_mask = encoded['attention_mask'].astype(np.int32)
108
 
109
+ # Set tensors - gunakan urutan yang benar sesuai model
110
  interpreter.set_tensor(input_details[0]['index'], attention_mask)
111
  interpreter.set_tensor(input_details[1]['index'], input_ids)
112
  interpreter.set_tensor(input_details[2]['index'], token_type_ids)
 
114
  # Run inference
115
  interpreter.invoke()
116
 
117
+ # Get raw output (logits)
118
  raw_output = interpreter.get_tensor(output_details[0]['index'])
119
 
120
  # Hitung probabilitas dengan softmax
 
124
  predicted_class_index = int(np.argmax(raw_output, axis=1)[0])
125
  max_confidence = float(np.max(probabilities))
126
 
127
+ # Dapatkan label kelas dari index
128
+ predicted_class_label = CLASS_LABELS.get(predicted_class_index, "Unknown Class")
129
+
130
  return {
131
  "predicted_class_index": predicted_class_index,
132
+ "predicted_class_label": predicted_class_label,
133
  "confidence": max_confidence,
134
  "raw_output": raw_output[0].tolist(), # Convert numpy array to list
135
  "probabilities": probabilities.tolist(),
 
150
 
151
  class PredictionResponse(BaseModel):
152
  predicted_class_index: int
153
+ predicted_class_label: str
154
  confidence: float
155
  raw_output: List[float]
156
  probabilities: List[float]
 
163
  <!DOCTYPE html>
164
  <html>
165
  <head>
166
+ <title>Damkar Classification</title>
167
  <meta charset="UTF-8">
168
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
169
  <style>
 
181
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
182
  }
183
  h1 {
184
+ color: #d32f2f; /* Red color for Damkar */
185
  text-align: center;
186
  margin-bottom: 30px;
187
  }
 
270
  color: #0056b3;
271
  }
272
  .raw-output {
273
+ background-color: #f0f0f0;
274
  padding: 10px;
275
  border-radius: 4px;
276
  font-family: monospace;
277
  font-size: 12px;
278
  margin: 10px 0;
279
+ max-height: 150px;
280
  overflow-y: auto;
281
+ white-space: pre-wrap;
282
+ word-wrap: break-word;
283
  }
284
+ .predicted-label {
285
+ font-size: 1.5em;
286
+ font-weight: bold;
287
+ color: #0056b3;
288
+ text-align: center;
289
+ margin: 15px 0;
290
  padding: 10px;
291
+ background-color: #e7f3ff;
292
+ border-radius: 6px;
 
293
  }
294
  </style>
295
  </head>
296
  <body>
297
  <div class="container">
298
+ <h1>πŸš’ Klasifikasi Laporan Damkar</h1>
299
+ <p style="text-align: center; color: #666;">Masukkan teks laporan untuk diklasifikasikan oleh model AI.</p>
300
 
301
  <div class="form-group">
302
  <label for="textInput">Masukkan teks laporan:</label>
 
319
  <div class="example-text" onclick="setExample('ular masuk ke dalam rumah warga')">
320
  🐍 "ular masuk ke dalam rumah warga"
321
  </div>
322
+ <div class="example-text" onclick="setExample('pohon tumbang menghalangi jalan raya')">
 
 
 
323
  🌳 "pohon tumbang menghalangi jalan raya"
324
  </div>
325
+ <div class="example-text" onclick="setExample('cincin tidak bisa dilepas dari jari')">
326
+ πŸ’ "cincin tidak bisa dilepas dari jari"
327
+ </div>
328
  </div>
329
  </div>
330
 
331
  <script>
332
+ const CLASS_LABELS = {
333
+ 0: "🐍 Evakuasi/Penyelamatan Hewan",
334
+ 1: "πŸ”₯ Kebakaran",
335
+ 2: "🌳 Layanan Lingkungan & Fasilitas Umum",
336
+ 3: "πŸ’ Penyelamatan Non Hewan & Bantuan Teknis"
337
+ };
338
+
339
  function setExample(text) {
340
  document.getElementById('textInput').value = text;
341
  }
 
368
  const data = await response.json();
369
 
370
  if (response.ok) {
371
+ const label = CLASS_LABELS[data.predicted_class_index] || "Label tidak diketahui";
372
+
373
  let resultHTML = `
374
  <h3>Hasil Prediksi:</h3>
375
+ <div class="predicted-label">${label}</div>
376
  <p><strong>Confidence:</strong> ${(data.confidence * 100).toFixed(2)}%</p>
377
 
378
+ <h4>Probabilitas per Kelas:</h4>
 
 
 
 
 
 
379
  `;
380
 
381
  data.probabilities.forEach((prob, index) => {
382
  const percentage = (prob * 100).toFixed(4);
383
  const isMax = index === data.predicted_class_index;
384
+ const classLabel = CLASS_LABELS[index] || `Class ${index}`;
385
  resultHTML += `
386
  <div class="prob-item" style="${isMax ? 'background-color: #fff3cd; font-weight: bold;' : ''}">
387
+ <span>${classLabel}</span>
388
  <span>${percentage}%</span>
389
  </div>
390
  `;
391
  });
392
 
393
  resultHTML += `
394
+ <details>
395
+ <summary style="cursor: pointer; margin-top: 15px;">Lihat Raw Kategori Laporan (untuk developer)</summary>
396
+ <p><strong>Predicted Class Index:</strong> ${data.predicted_class_index}</p>
397
+ <h4>Raw Kategori Laporan (Logits):</h4>
398
+ <div class="raw-output">${JSON.stringify(data.raw_output, null, 2)}</div>
399
+ </details>
400
  `;
401
 
402
  showResult('success', resultHTML);
 
418
  resultDiv.style.display = 'block';
419
  }
420
 
421
+ // Allow Ctrl+Enter to submit
422
+ document.getElementById('textInput').addEventListener('keydown', function(e) {
423
+ if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
424
  predict();
425
  }
426
  });
 
461
  "dtype": str(detail['dtype'])
462
  } for i, detail in enumerate(output_details)
463
  ],
464
+ "max_length": MAX_LENGTH,
465
+ "class_labels": CLASS_LABELS
466
  }
467
  }
468
 
 
492
  return {
493
  "message": "TFLite API is working!",
494
  "status": "ok",
495
+ "version": "1.1.0",
496
  "endpoints": {
497
  "ui": "/",
498
  "predict": "/predict",