rdsarjito commited on
Commit
7e019c7
Β·
1 Parent(s): f7834ca
app.py CHANGED
@@ -2,9 +2,8 @@ import streamlit as st
2
  import pandas as pd
3
  import pickle
4
  import numpy as np
5
- from sklearn.feature_extraction.text import TfidfVectorizer
6
- from sklearn.multioutput import MultiOutputClassifier
7
  import os
 
8
 
9
  # Konfigurasi halaman
10
  st.set_page_config(
@@ -13,23 +12,27 @@ st.set_page_config(
13
  layout="wide"
14
  )
15
 
16
- # Fungsi untuk memuat model dan vectorizer
17
- @st.cache_resource
 
 
 
 
18
  def load_models():
19
- """Memuat semua model dan TF-IDF vectorizer"""
20
  models = {}
21
  model_files = {
22
- 'XGBoost': 'saved_models/XGBoost_model.pkl',
23
- 'KNN': 'saved_models/KNN_model.pkl',
24
- 'Random Forest': 'saved_models/Random Forest_model.pkl'
25
  }
26
 
27
- # Load TF-IDF Vectorizer
28
  try:
29
- with open('saved_models/tfidf_vectorizer.pkl', 'rb') as f:
30
  tfidf_vectorizer = pickle.load(f)
31
  except FileNotFoundError:
32
- st.error("❌ TF-IDF Vectorizer tidak ditemukan! Pastikan file 'saved_models/tfidf_vectorizer.pkl' tersedia.")
33
  return None, None
34
 
35
  # Load models
@@ -44,188 +47,176 @@ def load_models():
44
 
45
  # Fungsi prediksi
46
  def predict_allergens(text, model, vectorizer):
47
- """Melakukan prediksi alergen berdasarkan teks input"""
48
- # Transform text menggunakan TF-IDF
49
- text_tfidf = vectorizer.transform([text])
50
 
51
  # Prediksi
52
- prediction = model.predict(text_tfidf)
53
- prediction_proba = model.predict_proba(text_tfidf)
54
 
55
  return prediction[0], prediction_proba
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  # Label alergen
58
- ALLERGEN_LABELS = ['Susu', 'Kacang', 'Telur', 'Makanan Laut', 'Gandum']
59
- ALLERGEN_EMOJIS = ['πŸ₯›', 'πŸ₯œ', 'πŸ₯š', '🦐', '🌾']
60
 
61
- def main():
62
- st.title("🍽️ Sistem Prediksi Alergen Makanan")
63
- st.markdown("---")
64
-
65
- # Load models
66
- models, tfidf_vectorizer = load_models()
67
-
68
- if models is None or tfidf_vectorizer is None:
69
- st.stop()
70
-
71
- # Sidebar untuk konfigurasi
72
- st.sidebar.header("βš™οΈ Konfigurasi")
73
-
74
- # Pilihan model
75
- available_models = list(models.keys())
76
- if available_models:
77
- selected_model = st.sidebar.selectbox(
78
- "Pilih Model:",
79
- available_models,
80
- help="Pilih model machine learning untuk prediksi"
81
- )
82
- else:
83
- st.error("❌ Tidak ada model yang tersedia!")
84
- st.stop()
85
-
86
- # Threshold untuk prediksi
87
- threshold = st.sidebar.slider(
88
- "Threshold Prediksi:",
89
- min_value=0.1,
90
- max_value=0.9,
91
- value=0.5,
92
- step=0.1,
93
- help="Nilai ambang batas untuk menentukan prediksi positif"
94
  )
95
 
96
- # Main interface
97
- col1, col2 = st.columns([2, 1])
98
-
99
- with col1:
100
- st.header("πŸ“ Input Makanan")
101
-
102
- # Text input
103
- food_text = st.text_area(
104
- "Masukkan deskripsi makanan atau bahan-bahan:",
105
- placeholder="Contoh: nasi goreng dengan telur, udang, dan kecap manis",
106
- height=150,
107
- help="Masukkan deskripsi makanan atau daftar bahan-bahan yang ingin diprediksi"
108
- )
109
-
110
- # Tombol prediksi
111
- predict_button = st.button("πŸ” Prediksi Alergen", type="primary")
112
-
113
- # Contoh input
114
- st.subheader("πŸ’‘ Contoh Input:")
115
- examples = [
116
- "nasi goreng dengan telur dan kecap manis",
117
- "roti gandum dengan selai kacang",
118
- "sup seafood dengan udang dan cumi",
119
- "kue coklat dengan susu dan mentega",
120
- "salad buah dengan yogurt"
121
- ]
122
-
123
- for i, example in enumerate(examples):
124
- if st.button(f"πŸ“‹ {example}", key=f"example_{i}"):
125
- st.session_state.food_text = example
126
- st.rerun()
127
-
128
- # Update text area jika ada contoh yang dipilih
129
- if 'food_text' in st.session_state:
130
- food_text = st.session_state.food_text
131
-
132
- with col2:
133
- st.header("ℹ️ Informasi Model")
134
-
135
- if selected_model in models:
136
- st.success(f"βœ… Model aktif: **{selected_model}**")
137
-
138
- # Informasi tentang alergen
139
- st.subheader("🏷️ Alergen yang Diprediksi:")
140
- for emoji, label in zip(ALLERGEN_EMOJIS, ALLERGEN_LABELS):
141
- st.write(f"{emoji} {label}")
142
-
143
- # Hasil prediksi
144
- if predict_button and food_text.strip():
145
- st.markdown("---")
146
- st.header("πŸ“Š Hasil Prediksi")
147
 
148
- with st.spinner("Sedang memproses prediksi..."):
 
 
 
 
 
 
 
 
 
 
 
149
  try:
150
- # Dapatkan prediksi
151
- predictions, probabilities = predict_allergens(
152
- food_text,
153
  models[selected_model],
154
  tfidf_vectorizer
155
  )
156
 
157
- # Tampilkan hasil dalam bentuk kolom
158
- result_cols = st.columns(len(ALLERGEN_LABELS))
159
 
160
- detected_allergens = []
 
161
 
162
- for i, (col, label, emoji) in enumerate(zip(result_cols, ALLERGEN_LABELS, ALLERGEN_EMOJIS)):
163
- with col:
164
- # Ambil probabilitas untuk label ini
165
- if hasattr(models[selected_model], 'predict_proba'):
166
- try:
167
- proba = probabilities[i][0][1] if len(probabilities[i][0]) > 1 else probabilities[i][0][0]
168
- except:
169
- proba = 0.5 # Default jika error
170
- else:
171
- proba = predictions[i]
172
-
173
- # Tentukan status berdasarkan threshold
174
- is_detected = predictions[i] == 1 or proba >= threshold
175
-
176
- if is_detected:
177
- st.error(f"{emoji} **{label}**\n\n⚠️ TERDETEKSI\n\n({proba:.2%})")
178
  detected_allergens.append(label)
179
  else:
180
- st.success(f"{emoji} **{label}**\n\nβœ… AMAN\n\n({proba:.2%})")
181
-
182
- # Ringkasan hasil
183
- st.subheader("πŸ“‹ Ringkasan Hasil")
184
 
 
 
185
  if detected_allergens:
186
- st.warning(f"⚠️ **PERINGATAN ALERGEN TERDETEKSI:**")
187
- for allergen in detected_allergens:
188
- st.write(f"β€’ {allergen}")
189
-
190
- st.info("πŸ’‘ **Saran:** Hindari makanan ini jika Anda memiliki alergi terhadap bahan-bahan yang terdeteksi.")
191
  else:
192
- st.success("βœ… **TIDAK ADA ALERGEN TERDETEKSI**")
193
- st.info("πŸ’‘ Makanan ini kemungkinan aman dari alergen yang diprediksi oleh sistem.")
194
 
195
- # Detail teknis (collapsible)
196
- with st.expander("πŸ”§ Detail Teknis"):
197
- st.write(f"**Model yang digunakan:** {selected_model}")
198
- st.write(f"**Threshold:** {threshold}")
199
- st.write(f"**Input text:** {food_text}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
 
201
- # Tabel detail probabilitas
202
- detail_df = pd.DataFrame({
203
- 'Alergen': ALLERGEN_LABELS,
204
- 'Prediksi': ['Ya' if p == 1 else 'Tidak' for p in predictions],
205
- 'Probabilitas': [f"{(probabilities[i][0][1] if len(probabilities[i][0]) > 1 else probabilities[i][0][0]):.2%}"
206
- if hasattr(models[selected_model], 'predict_proba') else f"{p:.0%}"
207
- for i, p in enumerate(predictions)]
208
- })
209
- st.dataframe(detail_df, use_container_width=True)
 
 
 
 
210
 
211
  except Exception as e:
212
  st.error(f"❌ Terjadi kesalahan saat prediksi: {str(e)}")
213
- st.write("Pastikan model dan vectorizer telah dimuat dengan benar.")
214
-
215
- elif predict_button and not food_text.strip():
216
- st.warning("⚠️ Silakan masukkan deskripsi makanan terlebih dahulu!")
217
-
218
- # Footer
219
- st.markdown("---")
220
- st.markdown(
221
- """
222
- <div style='text-align: center; color: gray;'>
223
- πŸ”¬ Sistem Prediksi Alergen Makanan menggunakan Machine Learning<br>
224
- ⚠️ Hasil prediksi bersifat estimasi. Selalu konsultasikan dengan ahli gizi untuk keputusan medis.
225
- </div>
226
- """,
227
- unsafe_allow_html=True
228
- )
 
 
 
 
 
 
 
 
229
 
230
- if __name__ == "__main__":
231
- main()
 
2
  import pandas as pd
3
  import pickle
4
  import numpy as np
 
 
5
  import os
6
+ from datetime import datetime
7
 
8
  # Konfigurasi halaman
9
  st.set_page_config(
 
12
  layout="wide"
13
  )
14
 
15
+ # Judul aplikasi
16
+ st.title("🍽️ Sistem Prediksi Alergen Makanan")
17
+ st.markdown("---")
18
+
19
+ # Cache untuk memuat model
20
+ @st.cache_data
21
  def load_models():
22
+ """Memuat semua model dan vectorizer yang tersimpan"""
23
  models = {}
24
  model_files = {
25
+ "XGBoost": "models/XGBoost_model.pkl",
26
+ "KNN": "models/KNN_model.pkl",
27
+ "Random Forest": "models/Random Forest_model.pkl"
28
  }
29
 
30
+ # Load TF-IDF vectorizer
31
  try:
32
+ with open('models/tfidf_vectorizer.pkl', 'rb') as f:
33
  tfidf_vectorizer = pickle.load(f)
34
  except FileNotFoundError:
35
+ st.error("❌ TF-IDF vectorizer tidak ditemukan! Pastikan file 'models/tfidf_vectorizer.pkl' ada.")
36
  return None, None
37
 
38
  # Load models
 
47
 
48
  # Fungsi prediksi
49
  def predict_allergens(text, model, vectorizer):
50
+ """Melakukan prediksi alergen dari teks input"""
51
+ # Transform text menggunakan TF-IDF vectorizer
52
+ text_vector = vectorizer.transform([text])
53
 
54
  # Prediksi
55
+ prediction = model.predict(text_vector)
56
+ prediction_proba = model.predict_proba(text_vector)
57
 
58
  return prediction[0], prediction_proba
59
 
60
+ # Load models
61
+ models, tfidf_vectorizer = load_models()
62
+
63
+ if models is None or tfidf_vectorizer is None:
64
+ st.stop()
65
+
66
+ # Sidebar untuk pemilihan model
67
+ st.sidebar.header("βš™οΈ Pengaturan")
68
+ selected_model = st.sidebar.selectbox(
69
+ "Pilih Model:",
70
+ list(models.keys()),
71
+ help="Pilih model machine learning untuk prediksi"
72
+ )
73
+
74
  # Label alergen
75
+ allergen_labels = ['Susu', 'Kacang', 'Telur', 'Makanan Laut', 'Gandum']
76
+ allergen_emojis = ['πŸ₯›', 'πŸ₯œ', 'πŸ₯š', '🦐', '🌾']
77
 
78
+ # Main interface
79
+ col1, col2 = st.columns([2, 1])
80
+
81
+ with col1:
82
+ st.header("πŸ“ Input Teks Makanan")
83
+
84
+ # Text input
85
+ user_input = st.text_area(
86
+ "Masukkan deskripsi makanan atau ingredients:",
87
+ placeholder="Contoh: nasi goreng dengan telur, udang, dan kacang tanah",
88
+ height=100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  )
90
 
91
+ # Contoh input
92
+ st.subheader("πŸ’‘ Contoh Input:")
93
+ examples = [
94
+ "pizza dengan keju mozzarella dan seafood",
95
+ "roti gandum dengan selai kacang",
96
+ "cake coklat dengan butter dan telur",
97
+ "sup tom yum dengan udang dan cumi",
98
+ "mie instan rasa ayam"
99
+ ]
100
+
101
+ example_cols = st.columns(len(examples))
102
+ for i, example in enumerate(examples):
103
+ if example_cols[i].button(f"Contoh {i+1}", help=example):
104
+ user_input = example
105
+ st.rerun()
106
+
107
+ with col2:
108
+ st.header("ℹ️ Informasi Model")
109
+ if selected_model in models:
110
+ st.success(f"βœ… Model {selected_model} siap digunakan")
111
+ st.info(f"πŸ“Š Jumlah label: {len(allergen_labels)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
+ # Model info
114
+ model_info = {
115
+ "XGBoost": "Gradient Boosting yang efisien",
116
+ "KNN": "K-Nearest Neighbors",
117
+ "Random Forest": "Ensemble dari decision trees"
118
+ }
119
+ st.write(f"**Deskripsi:** {model_info.get(selected_model, 'Model machine learning')}")
120
+
121
+ # Prediksi
122
+ if st.button("πŸ” Prediksi Alergen", type="primary", use_container_width=True):
123
+ if user_input.strip():
124
+ with st.spinner("Sedang melakukan prediksi..."):
125
  try:
126
+ # Prediksi
127
+ prediction, prediction_proba = predict_allergens(
128
+ user_input,
129
  models[selected_model],
130
  tfidf_vectorizer
131
  )
132
 
133
+ st.markdown("---")
134
+ st.header("πŸ“Š Hasil Prediksi")
135
 
136
+ # Hasil prediksi dalam bentuk metrics
137
+ st.subheader("🎯 Deteksi Alergen")
138
 
139
+ # Buat columns untuk menampilkan hasil
140
+ cols = st.columns(len(allergen_labels))
141
+
142
+ detected_allergens = []
143
+ for i, (label, emoji) in enumerate(zip(allergen_labels, allergen_emojis)):
144
+ with cols[i]:
145
+ if prediction[i] == 1:
146
+ st.success(f"{emoji} **{label}**\n\nβœ… **TERDETEKSI**")
 
 
 
 
 
 
 
 
147
  detected_allergens.append(label)
148
  else:
149
+ st.info(f"{emoji} **{label}**\n\n❌ Tidak terdeteksi")
 
 
 
150
 
151
+ # Ringkasan
152
+ st.subheader("πŸ“‹ Ringkasan")
153
  if detected_allergens:
154
+ st.warning(f"⚠️ **Alergen terdeteksi:** {', '.join(detected_allergens)}")
155
+ st.write("**Rekomendasi:** Harap berhati-hati jika Anda memiliki alergi terhadap bahan-bahan tersebut.")
 
 
 
156
  else:
157
+ st.success("βœ… **Tidak ada alergen utama yang terdeteksi**")
158
+ st.write("**Catatan:** Selalu periksa label produk untuk memastikan keamanan.")
159
 
160
+ # Probability scores (jika tersedia)
161
+ try:
162
+ st.subheader("πŸ“ˆ Tingkat Kepercayaan")
163
+ prob_data = []
164
+
165
+ for i, (label, emoji) in enumerate(zip(allergen_labels, allergen_emojis)):
166
+ # Ambil probabilitas untuk kelas positif (indeks 1)
167
+ if hasattr(prediction_proba[i], 'shape') and len(prediction_proba[i][0]) > 1:
168
+ prob = prediction_proba[i][0][1] # Probabilitas kelas 1 (positif)
169
+ else:
170
+ prob = 0.5 # Default jika tidak ada probabilitas
171
+
172
+ prob_data.append({
173
+ 'Alergen': f"{emoji} {label}",
174
+ 'Probabilitas': prob,
175
+ 'Persentase': f"{prob*100:.1f}%"
176
+ })
177
+
178
+ prob_df = pd.DataFrame(prob_data)
179
 
180
+ # Progress bars
181
+ for _, row in prob_df.iterrows():
182
+ st.write(f"**{row['Alergen']}**")
183
+ st.progress(row['Probabilitas'])
184
+ st.write(f"Kepercayaan: {row['Persentase']}")
185
+ st.write("")
186
+
187
+ except Exception as e:
188
+ st.info("πŸ’‘ Tingkat kepercayaan tidak tersedia untuk model ini")
189
+
190
+ # Timestamp
191
+ st.markdown("---")
192
+ st.caption(f"Prediksi dilakukan pada: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
193
 
194
  except Exception as e:
195
  st.error(f"❌ Terjadi kesalahan saat prediksi: {str(e)}")
196
+ st.write("Pastikan semua file model sudah tersedia dan format input benar.")
197
+ else:
198
+ st.warning("⚠️ Silakan masukkan teks untuk prediksi!")
199
+
200
+ # Footer
201
+ st.markdown("---")
202
+ st.markdown("""
203
+ ### πŸ“Œ Catatan Penting:
204
+ - Sistem ini adalah alat bantu dan tidak menggantikan konsultasi medis profesional
205
+ - Selalu periksa label produk dan konsultasikan dengan dokter untuk alergi yang serius
206
+ - Akurasi prediksi tergantung pada kualitas data training dan input yang diberikan
207
+ """)
208
+
209
+ # Informasi tambahan di sidebar
210
+ st.sidebar.markdown("---")
211
+ st.sidebar.header("πŸ“‹ Informasi Alergen")
212
+ st.sidebar.markdown("""
213
+ **Alergen yang dideteksi:**
214
+ - πŸ₯› **Susu**: Produk dairy, keju, yogurt
215
+ - πŸ₯œ **Kacang**: Kacang tanah, almond, dll
216
+ - πŸ₯š **Telur**: Telur ayam dan produk turunannya
217
+ - 🦐 **Makanan Laut**: Udang, ikan, kerang
218
+ - 🌾 **Gandum**: Tepung terigu, roti, pasta
219
+ """)
220
 
221
+ st.sidebar.markdown("---")
222
+ st.sidebar.info("πŸ’‘ **Tips:** Semakin detail deskripsi makanan yang Anda berikan, semakin akurat hasil prediksinya.")
{saved_models β†’ models}/KNN_model.pkl RENAMED
File without changes
{saved_models β†’ models}/Random Forest_model.pkl RENAMED
File without changes
{saved_models β†’ models}/XGBoost_model.pkl RENAMED
File without changes
{saved_models β†’ models}/tfidf_vectorizer.pkl RENAMED
File without changes
requirements.txt CHANGED
@@ -3,4 +3,4 @@ pandas
3
  scikit-learn
4
  xgboost
5
  numpy
6
- pickle-mixin
 
3
  scikit-learn
4
  xgboost
5
  numpy
6
+ pickle