Spaces:
Sleeping
Sleeping
File size: 13,129 Bytes
22a4c03 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
import streamlit as st
import torch
import numpy as np
from pydub import AudioSegment
from transformers import pipeline, WhisperProcessor
# webrtcvad: Bu kütüphane genel VAD için kullanılır ancak mevcut analiz akışınızda doğrudan bir işlevi yok.
# Eğer kullanmayacaksanız requirements.txt'den ve importtan kaldırabilirsiniz.
import webrtcvad
from phonemizer.backend import EspeakBackend
from fastdtw import fastdtw
from scipy.signal import find_peaks
import librosa
import io
import os # Dosya yollarını yönetmek için
# Model ve tokenizer yolları (yerel klasörler için göreceli yollar)
# Hugging Face Spaces'te bu klasörler projenizin kök dizininde olacaktır.
model_path = './model_whisper-large-v3-turbo-aze-60hours(part15_aug)-lab/'
tokenizer_path = './tokenizer_whisper-large-v3-turbo-aze-60hours(part15_aug)-lab/'
# Model ve tokenizer klasörlerinin varlığını kontrol et
if not os.path.exists(model_path) or not os.path.exists(tokenizer_path):
st.error(f"Hata: Model veya tokenizer klasörleri bulunamadı. Lütfen '{model_path}' ve '{tokenizer_path}' yollarını ve dosya yapısını kontrol edin.")
st.stop() # Uygulamayı durdur
@st.cache_resource
def load_asr_components():
"""ASR modelini ve işlemcisini önbelleğe alır."""
try:
processor = WhisperProcessor.from_pretrained(tokenizer_path, language='aze', task='transcribe')
pipeline_config = {
"task": "automatic-speech-recognition",
"model": model_path,
"tokenizer": processor.tokenizer,
"feature_extractor": processor.feature_extractor,
"device": 0 if torch.cuda.is_available() else -1, # CUDA varsa GPU kullan
"torch_dtype": torch.float16 if torch.cuda.is_available() else torch.float32,
"generate_kwargs": {
"task": "transcribe",
"language": "azerbaijani",
"num_beams": 2,
"condition_on_prev_tokens": False,
"compression_ratio_threshold": 2.4,
"temperature": (0.0, 0.2, 0.4),
"logprob_threshold": -1.0,
"return_timestamps": True,
}
}
asr_pipeline = pipeline(**pipeline_config)
return processor, asr_pipeline
except Exception as e:
st.error(f"ASR bileşenleri yüklenirken hata oluştu: {e}. Lütfen model ve tokenizer yollarını kontrol edin.")
return None, None
processor, asr_pipeline = load_asr_components()
class DictionAnalyzer:
def __init__(self):
# phonemizer backend'i de önbelleğe alınabilir veya burada başlatılabilir
# EspeakBackend'in başlatılması biraz zaman alabilir, ilk yüklemede sorun olmaması için burada.
self.phonemizer = EspeakBackend("az")
def analyze_prosody(self, y, sr):
try:
pitch, _, _ = librosa.pyin(y, fmin=librosa.note_to_hz('C2'), fmax=librosa.note_to_hz('C7'))
except Exception:
pitch = np.array([np.nan]) # Handle cases where pyin might fail
energy = librosa.feature.rms(y=y)[0]
peaks, _ = find_peaks(energy, height=np.mean(energy) if energy.size > 0 else 0)
return {
"average_pitch": np.nanmean(pitch) if not np.all(np.isnan(pitch)) else 0,
"pitch_variance": np.nanvar(pitch) if not np.all(np.isnan(pitch)) else 0,
"energy_peaks": len(peaks)
}
def analyze_speed_and_fluency(self, transcript, audio_duration_seconds):
words = len(transcript.split())
if audio_duration_seconds > 0:
words_per_second = words / audio_duration_seconds
else:
words_per_second = 0
return {"words_per_second": words_per_second, "word_count": words}
def compare_pronunciation(self, transcript, reference_text):
phonetic_transcript = self.phonemizer.phonemize([transcript], strip=True)
phonetic_reference = self.phonemizer.phonemize([reference_text], strip=True)
def text_to_numeric(phonetic_text):
if not phonetic_text or not phonetic_text[0]:
return np.array([])
return np.array([ord(char) for char in phonetic_text[0]])
numeric_transcript = text_to_numeric(phonetic_transcript)
numeric_reference = text_to_numeric(phonetic_reference)
if numeric_transcript.size == 0 or numeric_reference.size == 0:
return {"phonetic_distance": float('inf')}
try:
dtw_distance, _ = fastdtw(numeric_transcript, numeric_reference, dist=lambda x, y: np.linalg.norm(x - y))
except ValueError: # Handle empty sequences for DTW
dtw_distance = float('inf')
return {"phonetic_distance": dtw_distance}
def evaluate_score(self, value, good_range, medium_range, is_lower_better=False):
"""
Dəyəri yaxşı, orta, pis kateqoriyasına uyğun olaraq 1-10 arası bal verir.
is_lower_better: Əgər dəyər nə qədər aşağı olarsa, o qədər yaxşıdırsa True qeyd edin.
"""
if value is None or np.isnan(value) or value == float('inf'): # Handle invalid values
return "Hesaplanamadı", 0
if is_lower_better:
if value <= good_range[1]:
return "Yaxşı", 10
elif good_range[1] < value <= medium_range[1]:
return "Orta", 6
else:
return "Pis", 2
else:
if value >= good_range[0]:
return "Yaxşı", 10
elif medium_range[0] <= value < good_range[0]:
return "Orta", 6
else:
return "Pis", 2
def full_analysis(self, audio_data, reference_text):
# audio_data dictionary olarak gelmeli: {"raw": np.array, "sampling_rate": int}
y = audio_data["raw"]
sr = audio_data["sampling_rate"]
audio_duration = librosa.get_duration(y=y, sr=sr)
if asr_pipeline is None:
st.error("ASR pipeline yüklenemedi. Analiz yapılamıyor.")
return None
try:
transcript_result = asr_pipeline({"raw": y, "sampling_rate": sr})
transcript = transcript_result['text']
except Exception as e:
st.error(f"Transkripsiyon sırasında hata oluştu: {e}")
transcript = "" # Boş transkript ile devam et
prosody = self.analyze_prosody(y, sr)
fluency = self.analyze_speed_and_fluency(transcript, audio_duration)
pronunciation = self.compare_pronunciation(transcript, reference_text)
# Değerlendirme aralıkları: Bu aralıklar, modelinizin performansı ve istediğiniz metriklere göre ayarlanmalıdır.
# Bunlar sadece örneklerdir.
prosody_avg_pitch_eval = self.evaluate_score(prosody["average_pitch"], (100, 250), (70, 300), is_lower_better=False)
prosody_pitch_variance_eval = self.evaluate_score(prosody["pitch_variance"], (500, 3000), (200, 5000), is_lower_better=False)
# energy_peaks için audio_duration'a göre bir oran düşünebilirsiniz. Örneğin: 0.1-0.5 peak/saniye iyi olabilir.
prosody_energy_peaks_eval = self.evaluate_score(prosody["energy_peaks"] / audio_duration if audio_duration > 0 else 0, (0.1, 0.5), (0.05, 0.7), is_lower_better=False)
fluency_wps_eval = self.evaluate_score(fluency["words_per_second"], (2.0, 3.0), (1.5, 3.5), is_lower_better=False)
pronunciation_phonetic_distance_eval = self.evaluate_score(pronunciation["phonetic_distance"], (0, 200), (201, 1000), is_lower_better=True)
return {
"transcript": transcript,
"prosody": {
"average_pitch": prosody["average_pitch"],
"average_pitch_score": prosody_avg_pitch_eval,
"pitch_variance": prosody["pitch_variance"],
"pitch_variance_score": prosody_pitch_variance_eval,
"energy_peaks": prosody["energy_peaks"],
"energy_peaks_score": prosody_energy_peaks_eval
},
"fluency": {
"words_per_second": fluency["words_per_second"],
"words_per_second_score": fluency_wps_eval,
"word_count": fluency["word_count"]
},
"pronunciation": {
"phonetic_distance": pronunciation["phonetic_distance"],
"phonetic_distance_score": pronunciation_phonetic_distance_eval
}
}
# Streamlit UI
st.title("Diksiyon Analiz Uygulaması")
st.write("Ses dosyanızı yükleyin ve referans metin girerek diksiyonunuzu analiz edin.")
uploaded_file = st.file_uploader("Ses Dosyası Yükleyin (.wav, .mp3)", type=["wav", "mp3"])
reference_text_input = st.text_area("Referans Metin Girin",
"Salam, hər vaxtınız xeyr, mənim adım Əlidir. Salam, Əli bəy. Sizə necə kömək edə bilərəm? Kartımın şifrəsini dəyişmək istəyirəm və şifrəmi unutmuşam. Şifrənizi dəyişdirmək üçün sizdən bəzi məlumatlar istəyəcəm. Zəhmət olmasa, adınızı, soyadınızı və bir də vəsigənin fin kodunu deyə bilərsiniz. Əlbəttə, deyə bilərəm. Mənim adım Ələl Əkbəlli. Fin kodum isə 7CRH7UE. Zəhmət olmasa, yenə təkrar bilərsiniz fin kodu? 7-C-R-H-7-U-E Təşəkkür edirəm, Əli bəy. Şifrənizi sıfırlamaq üçün sizə indi kod gəlməlidir telefona, gəldi, hal-hazırda? Bəli, bəli, kod gəldi. İndi parolu dəyişə bilərəm. Bəli, dəyişə bilərsiniz? Oldu, hazırda şifrəmi dəyişdirdim. Aha, hal-hazırda əməliyyatınız uğurla tamamlanıb və şifrəniz dəyişilib. Yeni şifrədən istifadə edə bilərsiniz, kart hesabınıza daxil olmaq üçün. Başqa sualınız varmı? Xeyr, çox təşəkkür edəm sizə. Buyurun, gününüz xoş geçsin. Sağ olun.")
if uploaded_file is not None and reference_text_input:
st.audio(uploaded_file, format='audio/wav') # Kullanıcının yüklediği sesi oynat
with st.spinner('Ses dosyası işleniyor...'):
try:
audio_bytes = uploaded_file.read()
audio_segment = AudioSegment.from_file(io.BytesIO(audio_bytes))
audio_array = np.array(audio_segment.get_array_of_samples(), dtype=np.float32)
# Ses stereo ise mono'ya çevir
if audio_segment.channels == 2:
audio_array = audio_array.reshape((-1, 2)).mean(axis=1)
# Normalizasyon
audio_array /= np.iinfo(audio_segment.array_type).max
# Eğer sample rate 16000 değilse, yeniden örnekle
if audio_segment.frame_rate != 16000:
st.info(f"Ses dosyası {audio_segment.frame_rate} Hz. 16000 Hz'e dönüştürülüyor...")
audio_array = librosa.resample(y=audio_array, orig_sr=audio_segment.frame_rate, target_sr=16000)
audio_input_for_analysis = {"raw": audio_array, "sampling_rate": 16000}
except Exception as e:
st.error(f"Ses dosyası okunurken veya işlenirken hata oluştu: {e}")
audio_input_for_analysis = None
if audio_input_for_analysis:
analyzer = DictionAnalyzer()
st.write("Analiz ediliyor, lütfen bekleyin...")
with st.spinner('Analiz tamamlanıyor...'):
report = analyzer.full_analysis(audio_input_for_analysis, reference_text_input)
if report:
st.subheader("Analiz Sonuçları")
st.write(f"**Transkript:** {report['transcript']}")
st.subheader("Prosoya Analizi")
st.write(f"Ortalama Ton: {report['prosody']['average_pitch']:.2f} Hz - **Kategori: {report['prosody']['average_pitch_score'][0]} ({report['prosody']['average_pitch_score'][1]}/10)**")
st.write(f"Ton Farkı: {report['prosody']['pitch_variance']:.2f} - **Kategori: {report['prosody']['pitch_variance_score'][0]} ({report['prosody']['pitch_variance_score'][1]}/10)**")
st.write(f"Enerji Pikleri: {report['prosody']['energy_peaks']} - **Kategori: {report['prosody']['energy_peaks_score'][0]} ({report['prosody']['energy_peaks_score'][1]}/10)** (Saniyedeki tepe noktası oranı)")
st.subheader("Sürat ve Akıcılık Analizi")
st.write(f"Saniyedeki Kelime Sayısı: {report['fluency']['words_per_second']:.2f} - **Kategori: {report['fluency']['words_per_second_score'][0]} ({report['fluency']['words_per_second_score'][1]}/10)**")
st.write(f"Toplam Kelime Sayısı: {report['fluency']['word_count']}")
st.subheader("Tələffüz Müqayisəsi")
st.write(f"Fonetik Məsafə (DTW): {report['pronunciation']['phonetic_distance']:.2f} - **Kategori: {report['pronunciation']['phonetic_distance_score'][0]} ({report['pronunciation']['phonetic_distance_score'][1]}/10)**")
else:
st.error("Analiz tamamlanamadı.")
elif uploaded_file is None and st.button("Analiz Et"):
st.warning("Lütfen bir ses dosyası yükleyin ve referans metin girin.") |