MIF_E31231708/.history/project_svm_suara/app_20260524002543.py

117 lines
4.1 KiB
Python

import tempfile
from pathlib import Path
import joblib
import streamlit as st
from audio_utils import SUPPORTED_AUDIO_EXTENSIONS, convert_to_wav
from features import LABEL_PD, LABEL_TPD, extract_features
BASE_DIR = Path(__file__).resolve().parent
MODEL_PATH = BASE_DIR / "models" / "svm_voice_confidence_model.joblib"
CONFIDENCE_THRESHOLD = 0.75
MARGIN_THRESHOLD = 0.25
SUPPORTED_UPLOAD_TYPES = [extension.replace(".", "") for extension in sorted(SUPPORTED_AUDIO_EXTENSIONS)]
LABEL_DESCRIPTION = {
LABEL_PD: "Percaya Diri",
LABEL_TPD: "Tidak Percaya Diri",
}
@st.cache_resource
def load_model(model_mtime):
"""
model_mtime dipakai sebagai cache key.
Jika model dilatih ulang dan file berubah, Streamlit otomatis memuat model baru.
"""
return joblib.load(MODEL_PATH)
def save_uploaded_file(uploaded_file, suffix):
with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as temp_audio:
temp_audio.write(uploaded_file.getbuffer())
return Path(temp_audio.name)
def predict_uploaded_audio(uploaded_file):
model = load_model(MODEL_PATH.stat().st_mtime)
original_extension = Path(uploaded_file.name).suffix.lower()
if original_extension not in SUPPORTED_AUDIO_EXTENSIONS:
allowed = ", ".join(SUPPORTED_UPLOAD_TYPES).upper()
raise ValueError(f"Format file tidak didukung. Gunakan salah satu: {allowed}")
temp_input_path = save_uploaded_file(uploaded_file, original_extension)
temp_wav_path = Path(tempfile.NamedTemporaryFile(delete=False, suffix=".wav").name)
# Semua input disiapkan ulang menjadi WAV mono 22050 Hz sebelum ekstraksi fitur.
try:
convert_to_wav(temp_input_path, temp_wav_path)
features = extract_features(temp_wav_path).reshape(1, -1)
expected_features = model.named_steps["scaler"].n_features_in_
if features.shape[1] != expected_features:
raise ValueError(
"Jumlah fitur audio tidak sesuai dengan model. "
f"Audio menghasilkan {features.shape[1]} fitur, "
f"sedangkan model mengharapkan {expected_features}. "
"Jalankan ulang `python train_model.py`, lalu restart Streamlit."
)
predicted_label = model.predict(features)[0]
probabilities = model.predict_proba(features)[0]
class_probabilities = dict(zip(model.classes_, probabilities))
confidence = class_probabilities[predicted_label]
finally:
temp_input_path.unlink(missing_ok=True)
temp_wav_path.unlink(missing_ok=True)
return predicted_label, confidence, class_probabilities
st.set_page_config(
page_title="Klasifikasi Percaya Diri dari Suara",
layout="centered",
)
st.title("Klasifikasi Percaya Diri dari Suara")
st.write("Upload file audio untuk memprediksi kelas PD atau TPD.")
if not MODEL_PATH.exists():
st.error("Model belum ditemukan. Jalankan `python train_model.py` terlebih dahulu.")
st.stop()
uploaded_file = st.file_uploader(
"Pilih file audio",
type=SUPPORTED_UPLOAD_TYPES,
)
if uploaded_file is not None:
uploaded_extension = Path(uploaded_file.name).suffix.lower().replace(".", "")
st.audio(uploaded_file, format=f"audio/{uploaded_extension}")
if st.button("Prediksi"):
with st.spinner("Mengekstraksi fitur dan memprediksi..."):
try:
label, confidence, probabilities = predict_uploaded_audio(uploaded_file)
except Exception as error:
st.error(f"Gagal memproses audio: {error}")
st.stop()
st.subheader("Hasil Prediksi")
probability_pd = probabilities.get(LABEL_PD, 0.0)
probability_tpd = probabilities.get(LABEL_TPD, 0.0)
margin = abs(probability_pd - probability_tpd)
if confidence >= CONFIDENCE_THRESHOLD and margin >= MARGIN_THRESHOLD:
st.success(f"Hasil utama: {label} - {LABEL_DESCRIPTION[label]}")
else:
st.warning(
"Model belum cukup yakin. Suara perlu direkam ulang atau data training perlu ditambah."
)
st.progress(float(probability_pd), text=f"PD: {probability_pd * 100:.2f}%")
st.progress(float(probability_tpd), text=f"TPD: {probability_tpd * 100:.2f}%")