Ilkinism commited on
Commit
45d0379
·
verified ·
1 Parent(s): d63442e

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +58 -54
src/streamlit_app.py CHANGED
@@ -2,51 +2,52 @@ import streamlit as st
2
  import torch
3
  import numpy as np
4
  from pydub import AudioSegment
5
- from transformers import pipeline, WhisperProcessor
6
- # webrtcvad: Bu kütüphane genel VAD için kullanılır ancak mevcut analiz akışınızda doğrudan bir işlevi yok.
7
- # Eğer kullanmayacaksanız requirements.txt'den ve importtan kaldırabilirsiniz.
8
- import webrtcvad
9
  from phonemizer.backend import EspeakBackend
10
  from fastdtw import fastdtw
11
  from scipy.signal import find_peaks
12
  import librosa
13
  import io
14
- import os # Dosya yollarını yönetmek için
15
- # import ffmpeg
16
 
17
- os.environ["STREAMLIT_HOME"] = "/app/.streamlit"
 
 
18
 
19
- # Model ve tokenizer yolları (yerel klasörler için göreceli yollar)
20
- # Hugging Face Spaces'te bu klasörler projenizin kök dizininde olacaktır.
21
 
22
- BASE_DIR = os.path.dirname(os.path.abspath(__file__)) # src klasörü
23
- model_path = os.path.join(BASE_DIR, "../model_whisper-large-v3-turbo-aze-60hours(part15_aug)-lab")
24
- tokenizer_path = os.path.join(BASE_DIR, "../tokenizer_whisper-large-v3-turbo-aze-60hours(part15_aug)-lab")
25
-
26
-
27
-
28
- # model_path = 'model_whisper-large-v3-turbo-aze-60hours(part15_aug)-lab'
29
- # tokenizer_path = 'tokenizer_whisper-large-v3-turbo-aze-60hours(part15_aug)-lab'
30
-
31
- # Model ve tokenizer klasörlerinin varlığını kontrol et
32
  if not os.path.exists(model_path) or not os.path.exists(tokenizer_path):
33
- 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.")
34
  st.stop() # Uygulamayı durdur
35
 
36
  @st.cache_resource
37
  def load_asr_components():
38
  """ASR modelini ve işlemcisini önbelleğe alır."""
39
  try:
 
40
  processor = WhisperProcessor.from_pretrained(tokenizer_path, language='aze', task='transcribe')
41
 
42
- pipeline_config = {
43
- "task": "automatic-speech-recognition",
44
- "model": model_path,
45
- "tokenizer": processor.tokenizer,
46
- "feature_extractor": processor.feature_extractor,
47
- "device": 0 if torch.cuda.is_available() else -1, # CUDA varsa GPU kullan
48
- "torch_dtype": torch.float16 if torch.cuda.is_available() else torch.float32,
49
- "generate_kwargs": {
 
 
 
 
 
 
 
 
 
 
50
  "task": "transcribe",
51
  "language": "azerbaijani",
52
  "num_beams": 2,
@@ -56,33 +57,35 @@ def load_asr_components():
56
  "logprob_threshold": -1.0,
57
  "return_timestamps": True,
58
  }
59
- }
60
- asr_pipeline = pipeline(**pipeline_config)
61
  return processor, asr_pipeline
62
  except Exception as e:
63
- st.error(f"ASR bileşenleri yüklenirken hata oluştu: {e}. Lütfen model ve tokenizer yollarını kontrol edin.")
64
  return None, None
65
 
66
  processor, asr_pipeline = load_asr_components()
67
 
68
  class DictionAnalyzer:
69
  def __init__(self):
70
- # phonemizer backend'i de önbelleğe alınabilir veya burada başlatılabilir
71
- # EspeakBackend'in başlatılması biraz zaman alabilir, ilk yüklemede sorun olmaması için burada.
72
  self.phonemizer = EspeakBackend("az")
73
 
74
  def analyze_prosody(self, y, sr):
75
  try:
76
- pitch, _, _ = librosa.pyin(y, fmin=librosa.note_to_hz('C2'), fmax=librosa.note_to_hz('C7'))
 
 
 
 
77
  except Exception:
78
- pitch = np.array([np.nan]) # Handle cases where pyin might fail
79
-
80
  energy = librosa.feature.rms(y=y)[0]
 
81
  peaks, _ = find_peaks(energy, height=np.mean(energy) if energy.size > 0 else 0)
82
 
83
  return {
84
- "average_pitch": np.nanmean(pitch) if not np.all(np.isnan(pitch)) else 0,
85
- "pitch_variance": np.nanvar(pitch) if not np.all(np.isnan(pitch)) else 0,
86
  "energy_peaks": len(peaks)
87
  }
88
 
@@ -110,8 +113,9 @@ class DictionAnalyzer:
110
  return {"phonetic_distance": float('inf')}
111
 
112
  try:
 
113
  dtw_distance, _ = fastdtw(numeric_transcript, numeric_reference, dist=lambda x, y: np.linalg.norm(x - y))
114
- except ValueError: # Handle empty sequences for DTW
115
  dtw_distance = float('inf')
116
  return {"phonetic_distance": dtw_distance}
117
 
@@ -120,7 +124,7 @@ class DictionAnalyzer:
120
  Dəyəri yaxşı, orta, pis kateqoriyasına uyğun olaraq 1-10 arası bal verir.
121
  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.
122
  """
123
- if value is None or np.isnan(value) or value == float('inf'): # Handle invalid values
124
  return "Hesaplanamadı", 0
125
 
126
  if is_lower_better:
@@ -139,14 +143,14 @@ class DictionAnalyzer:
139
  return "Pis", 2
140
 
141
  def full_analysis(self, audio_data, reference_text):
142
- # audio_data dictionary olarak gelmeli: {"raw": np.array, "sampling_rate": int}
143
  y = audio_data["raw"]
144
  sr = audio_data["sampling_rate"]
145
 
146
  audio_duration = librosa.get_duration(y=y, sr=sr)
147
 
148
  if asr_pipeline is None:
149
- st.error("ASR pipeline yüklenemedi. Analiz yapılamıyor.")
150
  return None
151
 
152
  try:
@@ -160,11 +164,11 @@ class DictionAnalyzer:
160
  fluency = self.analyze_speed_and_fluency(transcript, audio_duration)
161
  pronunciation = self.compare_pronunciation(transcript, reference_text)
162
 
163
- # Değerlendirme aralıkları: Bu aralıklar, modelinizin performansı ve istediğiniz metriklere göre ayarlanmalıdır.
164
- # Bunlar sadece örneklerdir.
165
  prosody_avg_pitch_eval = self.evaluate_score(prosody["average_pitch"], (100, 250), (70, 300), is_lower_better=False)
166
  prosody_pitch_variance_eval = self.evaluate_score(prosody["pitch_variance"], (500, 3000), (200, 5000), is_lower_better=False)
167
- # energy_peaks için audio_duration'a göre bir oran düşünebilirsiniz. Örneğin: 0.1-0.5 peak/saniye iyi olabilir.
168
  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)
169
 
170
  fluency_wps_eval = self.evaluate_score(fluency["words_per_second"], (2.0, 3.0), (1.5, 3.5), is_lower_better=False)
@@ -194,16 +198,16 @@ class DictionAnalyzer:
194
 
195
  # Streamlit UI
196
  st.title("Diksiyon Analiz Uygulaması")
197
- st.write("Ses dosyanızı yükleyin ve referans metin girerek diksiyonunuzu analiz edin.")
198
 
199
- uploaded_file = st.file_uploader("Ses Dosyası Yükleyin (.wav, .mp3)", type=["wav", "mp3"])
200
  reference_text_input = st.text_area("Referans Metin Girin",
201
  "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.")
202
 
203
  if uploaded_file is not None and reference_text_input:
204
- st.audio(uploaded_file, format='audio/wav') # Kullanıcının yüklediği sesi oynat
205
 
206
- with st.spinner('Ses dosyası işleniyor...'):
207
  try:
208
  audio_bytes = uploaded_file.read()
209
  audio_segment = AudioSegment.from_file(io.BytesIO(audio_bytes))
@@ -219,20 +223,20 @@ if uploaded_file is not None and reference_text_input:
219
 
220
  # Eğer sample rate 16000 değilse, yeniden örnekle
221
  if audio_segment.frame_rate != 16000:
222
- st.info(f"Ses dosyası {audio_segment.frame_rate} Hz. 16000 Hz'e dönüştürülüyor...")
223
  audio_array = librosa.resample(y=audio_array, orig_sr=audio_segment.frame_rate, target_sr=16000)
224
 
225
  audio_input_for_analysis = {"raw": audio_array, "sampling_rate": 16000}
226
 
227
  except Exception as e:
228
- st.error(f"Ses dosyası okunurken veya işlenirken hata oluştu: {e}")
229
  audio_input_for_analysis = None
230
 
231
  if audio_input_for_analysis:
232
  analyzer = DictionAnalyzer()
233
 
234
- st.write("Analiz ediliyor, lütfen bekleyin...")
235
- with st.spinner('Analiz tamamlanıyor...'):
236
  report = analyzer.full_analysis(audio_input_for_analysis, reference_text_input)
237
 
238
  if report:
@@ -253,4 +257,4 @@ if uploaded_file is not None and reference_text_input:
253
  else:
254
  st.error("Analiz tamamlanamadı.")
255
  elif uploaded_file is None and st.button("Analiz Et"):
256
- st.warning("Lütfen bir ses dosyası yükleyin ve referans metin girin.")
 
2
  import torch
3
  import numpy as np
4
  from pydub import AudioSegment
5
+ from transformers import pipeline, WhisperProcessor, AutoModelForSpeechSeq2Seq # AutoModelForSpeechSeq2Seq əlavə edildi
6
+ import webrtcvad
 
 
7
  from phonemizer.backend import EspeakBackend
8
  from fastdtw import fastdtw
9
  from scipy.signal import find_peaks
10
  import librosa
11
  import io
12
+ import os
 
13
 
14
+ # Hugging Face Spaces-də `src` qovluğu strukturunu nəzərə alaraq
15
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__)) # src qovluğunun yolu
16
+ PROJECT_ROOT = os.path.join(BASE_DIR, "..") # layihənin kök qovluğu
17
 
18
+ model_path = os.path.join(PROJECT_ROOT, "model_whisper-large-v3-turbo-aze-60hours(part15_aug)-lab")
19
+ tokenizer_path = os.path.join(PROJECT_ROOT, "tokenizer_whisper-large-v3-turbo-aze-60hours(part15_aug)-lab")
20
 
21
+ # Model tokenizer klasörlərinin varlığını kontrol et
 
 
 
 
 
 
 
 
 
22
  if not os.path.exists(model_path) or not os.path.exists(tokenizer_path):
23
+ st.error(f"Hata: Model veya tokenizer klasörleri bulunamadı. Lütfen '{model_path}' ve '{tokenizer_path}' yollarını ve Hugging Face Space-deki dosya yapısını kontrol edin. Model fayllarının ({model_path}/pytorch_model.bin or model.safetensors) mövcud olduğundan əmin olun.")
24
  st.stop() # Uygulamayı durdur
25
 
26
  @st.cache_resource
27
  def load_asr_components():
28
  """ASR modelini ve işlemcisini önbelleğe alır."""
29
  try:
30
+ # Önce işlemciyi yükle
31
  processor = WhisperProcessor.from_pretrained(tokenizer_path, language='aze', task='transcribe')
32
 
33
+ # Modeli doğrudan Whisper model sınıfı olarak yükle
34
+ # Bu, AutoModelForCTC hatasının önüne geçmek için kritikdir.
35
+ # Eğer modeliniz safetensors formatındaysa, from_pretrained otomatik olarak onu tanıyacaktır.
36
+ model = AutoModelForSpeechSeq2Seq.from_pretrained(
37
+ model_path,
38
+ torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
39
+ device_map="auto" if torch.cuda.is_available() else None # GPU varsa otomatik olaraq cihazı seç
40
+ )
41
+
42
+ # ASR pipeline-ı qur
43
+ asr_pipeline = pipeline(
44
+ "automatic-speech-recognition",
45
+ model=model, # Yüklenen model obyektini ötürürük
46
+ tokenizer=processor.tokenizer,
47
+ feature_extractor=processor.feature_extractor,
48
+ device=0 if torch.cuda.is_available() else -1,
49
+ torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
50
+ generate_kwargs={
51
  "task": "transcribe",
52
  "language": "azerbaijani",
53
  "num_beams": 2,
 
57
  "logprob_threshold": -1.0,
58
  "return_timestamps": True,
59
  }
60
+ )
 
61
  return processor, asr_pipeline
62
  except Exception as e:
63
+ st.error(f"ASR bileşenleri yüklenirken hata oluştu: {e}. Lütfen model ve tokenizer yollarını kontrol edin. Modelinizde `pytorch_model.bin` veya `model.safetensors` faylının olduğundan əmin olun.")
64
  return None, None
65
 
66
  processor, asr_pipeline = load_asr_components()
67
 
68
  class DictionAnalyzer:
69
  def __init__(self):
 
 
70
  self.phonemizer = EspeakBackend("az")
71
 
72
  def analyze_prosody(self, y, sr):
73
  try:
74
+ # pyin fasilələr səbəbindən NaN dəyərlər qaytara bilər, bunları idarə edin
75
+ pitch, _, _ = librosa.pyin(y, fmin=librosa.note_to_hz('C2'), fmax=librosa.note_to_hz('C7'), sr=sr, frame_length=2048, hop_length=512)
76
+ # NaN dəyərləri sıfırla dolduraraq və ya qulaq ardına vuraraq orta dəyəri hesablayın
77
+ pitch = pitch[~np.isnan(pitch)] # NaN dəyərləri çıxar
78
+ if pitch.size == 0: pitch = np.array([0.0]) # Boş qalarsa sıfır ver
79
  except Exception:
80
+ pitch = np.array([0.0]) # Hata durumunda da sıfır ver
81
+
82
  energy = librosa.feature.rms(y=y)[0]
83
+ # Enerji pikleri boş enerji dizileri için handle
84
  peaks, _ = find_peaks(energy, height=np.mean(energy) if energy.size > 0 else 0)
85
 
86
  return {
87
+ "average_pitch": np.mean(pitch),
88
+ "pitch_variance": np.var(pitch),
89
  "energy_peaks": len(peaks)
90
  }
91
 
 
113
  return {"phonetic_distance": float('inf')}
114
 
115
  try:
116
+ # DTW üçün numpy.ndarray obyektlərini istifadə edin
117
  dtw_distance, _ = fastdtw(numeric_transcript, numeric_reference, dist=lambda x, y: np.linalg.norm(x - y))
118
+ except ValueError: # Boş ardıcıllıqlar üçün DTW xətasını idarə et
119
  dtw_distance = float('inf')
120
  return {"phonetic_distance": dtw_distance}
121
 
 
124
  Dəyəri yaxşı, orta, pis kateqoriyasına uyğun olaraq 1-10 arası bal verir.
125
  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.
126
  """
127
+ if value is None or np.isnan(value) or value == float('inf'): # Geçərsiz dəyərləri idarə et
128
  return "Hesaplanamadı", 0
129
 
130
  if is_lower_better:
 
143
  return "Pis", 2
144
 
145
  def full_analysis(self, audio_data, reference_text):
146
+ # audio_data dictionary olaraq gəlməlidir: {"raw": np.array, "sampling_rate": int}
147
  y = audio_data["raw"]
148
  sr = audio_data["sampling_rate"]
149
 
150
  audio_duration = librosa.get_duration(y=y, sr=sr)
151
 
152
  if asr_pipeline is None:
153
+ st.error("ASR pipeline yüklənmədi. Analiz yapılamıyor.")
154
  return None
155
 
156
  try:
 
164
  fluency = self.analyze_speed_and_fluency(transcript, audio_duration)
165
  pronunciation = self.compare_pronunciation(transcript, reference_text)
166
 
167
+ # Dəyərləndirmə aralıqları: Bu aralıqlar, modelinizin performansı istədiyiniz metriklərə görə ayarlanmalıdır.
168
+ # Bunlar sadəcə nümunələrdir.
169
  prosody_avg_pitch_eval = self.evaluate_score(prosody["average_pitch"], (100, 250), (70, 300), is_lower_better=False)
170
  prosody_pitch_variance_eval = self.evaluate_score(prosody["pitch_variance"], (500, 3000), (200, 5000), is_lower_better=False)
171
+ # energy_peaks üçün audio_duration'a görə bir oran düşünə bilərsiniz. Örneğin: 0.1-0.5 peak/saniyə yaxşı ola bilər.
172
  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)
173
 
174
  fluency_wps_eval = self.evaluate_score(fluency["words_per_second"], (2.0, 3.0), (1.5, 3.5), is_lower_better=False)
 
198
 
199
  # Streamlit UI
200
  st.title("Diksiyon Analiz Uygulaması")
201
+ st.write("Ses dosyanızı yükləyin referans metin girərək diksiyonunuzu analiz edin.")
202
 
203
+ uploaded_file = st.file_uploader("Ses Dosyası Yükləyin (.wav, .mp3)", type=["wav", "mp3"])
204
  reference_text_input = st.text_area("Referans Metin Girin",
205
  "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.")
206
 
207
  if uploaded_file is not None and reference_text_input:
208
+ st.audio(uploaded_file, format='audio/wav') # Kullanıcının yüklədiyi səsi oynat
209
 
210
+ with st.spinner('Ses dosyası işlənir...'):
211
  try:
212
  audio_bytes = uploaded_file.read()
213
  audio_segment = AudioSegment.from_file(io.BytesIO(audio_bytes))
 
223
 
224
  # Eğer sample rate 16000 değilse, yeniden örnekle
225
  if audio_segment.frame_rate != 16000:
226
+ st.info(f"Ses dosyası {audio_segment.frame_rate} Hz. 16000 Hz'e dönüştürülür...")
227
  audio_array = librosa.resample(y=audio_array, orig_sr=audio_segment.frame_rate, target_sr=16000)
228
 
229
  audio_input_for_analysis = {"raw": audio_array, "sampling_rate": 16000}
230
 
231
  except Exception as e:
232
+ st.error(f"Ses dosyası oxunarkən ya işlənərkən hata oluştu: {e}")
233
  audio_input_for_analysis = None
234
 
235
  if audio_input_for_analysis:
236
  analyzer = DictionAnalyzer()
237
 
238
+ st.write("Analiz edilir, lütfən gözləyin...")
239
+ with st.spinner('Analiz tamamlanır...'):
240
  report = analyzer.full_analysis(audio_input_for_analysis, reference_text_input)
241
 
242
  if report:
 
257
  else:
258
  st.error("Analiz tamamlanamadı.")
259
  elif uploaded_file is None and st.button("Analiz Et"):
260
+ st.warning("Lütfen bir ses dosyası yükləyin referans metin girin.")