Tüm rehberler
Rehber Yapay Zeka 10 · Mimari

Speech Models
Whisper, TTS.

Sesi dijital sinyalden dile dönüştür. Whisper ile ASR, mel spectrogram ve encoder/decoder pipeline. Bark ve Coqui ile text-to-speech. Gerçek zamanlı streaming ve dil tespiti.

00 Sesin Dijital Temsili

Fiziksel ses dalgasını sayısal biçimde temsil etmek için örnekleme, nicemleme ve kanal gibi kavramları anlamak, tüm ses işleme pipeline'larının temelidir.

Ses, havadaki basınç dalgasının zamanla değişimidir. Bir mikrofon bu analog sinyali yakalar ve dijitalleştirmek için iki adım gerekir. Birincisi, örnekleme (sampling): saniyede belirli sayıda anlık basınç ölçümü alınır. Bu sayı örnekleme hızı (sample rate) ya da örnekleme frekansı olarak adlandırılır ve Hertz (Hz) ile ölçülür. Telefon kalitesi için 8 kHz, insan sesi için 16 kHz, CD kalitesi için 44.1 kHz, profesyonel ses için 48 kHz standardı kullanılır. Whisper gibi konuşma modelleri 16 kHz bekler.

İkincisi, nicemleme (quantization): her anlık basınç değeri sayısal bir tamsayıya dönüştürülür. Bit derinliği (bit depth) bu sayısal aralığı belirler: 16-bit → 65.536 farklı değer aralığı, dinamik aralık ~96 dB. 24-bit → 16.7 milyon değer, ~144 dB; stüdyo kaydında tercih edilir.

Kanal sayısı da önemlidir. Mono (tek kanal): bir mikrofon, toplam ses bilgisi tek kanalda. Stereo (çift kanal): sol ve sağ kanallar. ASR ve konuşma işleme modelleri genellikle mono 16-bit 16 kHz formatını bekler; stereo veya yüksek örnekleme hızındaki dosyaları dönüştürmek gerekir.

Waveform, zaman ekseninde amplitüdün gösterimidir. Her nokta bir örnektir. 1 saniyelik 16 kHz ses → 16.000 örnek. Bu ham sayı dizisi doğrudan bir sinyal işlemciye ya da Fourier dönüşümüne verilebilir.

audio_load.py
import torch
import torchaudio
import numpy as np

# ── torchaudio ile ses yükleme ────────────────────────────────
waveform, sample_rate = torchaudio.load("audio.wav")
# waveform: (channels, num_samples) tensor

print(f"Kanal sayısı    : {waveform.shape[0]}")
print(f"Örnek sayısı    : {waveform.shape[1]}")
print(f"Örnekleme hızı  : {sample_rate} Hz")
print(f"Süre            : {waveform.shape[1] / sample_rate:.2f} sn")
print(f"Min / Max       : {waveform.min():.4f} / {waveform.max():.4f}")
# 16-bit PCM normalleştirilmiş: [-1.0, 1.0] arası float

# ── Stereo → Mono dönüşümü ───────────────────────────────────
if waveform.shape[0] > 1:
    waveform = waveform.mean(dim=0, keepdim=True)   # ortalama kanallar
print(f"Mono waveform: {waveform.shape}")            # (1, num_samples)

# ── Yeniden örnekleme: 44.1kHz → 16kHz ──────────────────────
if sample_rate != 16000:
    resampler = torchaudio.transforms.Resample(
        orig_freq=sample_rate, new_freq=16000
    )
    waveform    = resampler(waveform)
    sample_rate = 16000

# ── librosa karşılaştırması ───────────────────────────────────
import librosa

# librosa her zaman mono float32 döndürür, sr ile birlikte
y, sr = librosa.load("audio.wav", sr=16000, mono=True)
# y: numpy array (num_samples,) — dtype float32
print(f"librosa waveform: {y.shape}, sr={sr}")

# Fourier dönüşümü: frekans domenine bak
fft        = np.fft.rfft(y[:4096])       # ilk 4096 örnek
magnitudes = np.abs(fft)
freqs      = np.fft.rfftfreq(4096, d=1.0/sr)
dominant   = freqs[np.argmax(magnitudes)]
print(f"Baskın frekans: {dominant:.1f} Hz")
NOT

Nyquist teoremi: dijital sistemde kaydedilebilecek maksimum frekans, örnekleme hızının yarısıdır. 16 kHz örnekleme hızı → maksimum 8 kHz frekans. İnsan sesi büyük ölçüde 300 Hz–3.4 kHz arasında yoğunlaşır; bu yüzden 16 kHz ASR için yeterlidir. Müzik için 44.1 kHz tercih edilir (20 kHz üstü harmonikler).

01 Mel Spectrogram

Ham waveform yerine log-mel spectrogram kullanmak, hem insan kulağının işitsel algısını yansıtır hem de modelin frekans özelliklerini öğrenmesini kolaylaştırır.

Ham waveform modele verildiğinde model hem frekans hem zaman örüntülerini ham örneklerden öğrenmek zorunda kalır; bu çok zordur. Bunun yerine önce sinyali hem zaman hem frekans domaininde temsil eden spectrogram'a dönüştürmek standart yaklaşımdır.

Short-Time Fourier Transform (STFT), sinyali küçük zaman pencerelere bölerek her pencereye Fourier dönüşümü uygular. Sonuç: yatay eksende zaman, dikey eksende frekans, renk şiddetiyle genlik gösteren bir 2D matristir. Whisper 25ms pencere, 10ms adım kullanır.

Standart spectrogram lineer frekans eksenine sahiptir; oysa insan kulağı frekansları logaritmik algılar — düşük frekanslardaki küçük değişimleri yüksek frekanslardaki büyük değişimlerden daha iyi ayırt eder. Mel scale bu algıyı modelleyen logaritmik bir frekans ölçeğidir: mel = 2595 × log10(1 + f/700). Mel filterbank, bu ölçekte eşit aralıklı üçgen filtreler uygular ve spectrogram'ı mel frekans eksenine dönüştürür.

Son adım log-compression: mel spectrogram değerlerinin logaritmasını almak, geniş amplitüd aralığını sıkıştırır ve modelin hem sessiz hem gürültülü sesleri eşit öğrenmesine yardımcı olur. Whisper 80 mel bandı ve log-mel spectrogram kullanır; bu değerler [-11, 2] aralığında ön normalize edilir.

mel_spectrogram.py
import torch
import torchaudio
import torchaudio.transforms as T
import librosa
import numpy as np

# ── torchaudio MelSpectrogram ─────────────────────────────────
# Whisper konfigürasyonuna yakın ayarlar
mel_transform = T.MelSpectrogram(
    sample_rate    = 16000,
    n_fft          = 400,     # 25ms pencere @ 16kHz
    hop_length     = 160,     # 10ms adım @ 16kHz
    n_mels         = 80,      # Whisper 80 mel band kullanır
    f_min          = 0.0,
    f_max          = 8000.0,
    window_fn      = torch.hann_window,
    power          = 2.0,     # güç spektrumu
)

# ── Waveform → Log-Mel Spectrogram ───────────────────────────
waveform, sr = torchaudio.load("audio.wav")

if sr != 16000:
    waveform = T.Resample(sr, 16000)(waveform)

if waveform.shape[0] > 1:
    waveform = waveform.mean(0, keepdim=True)

mel_spec  = mel_transform(waveform)         # (1, 80, T)
log_mel   = torch.log(mel_spec + 1e-9)       # log-compression

print(f"Mel spectrogram: {log_mel.shape}")
# (1, n_mels=80, time_frames)
# time_frames ≈ duration_sec * 100  (10ms per frame)

# ── librosa karşılaştırması ───────────────────────────────────
y, _ = librosa.load("audio.wav", sr=16000)

mel_librosa = librosa.feature.melspectrogram(
    y          = y,
    sr         = 16000,
    n_fft      = 400,
    hop_length = 160,
    n_mels     = 80,
    fmin       = 0.0,
    fmax       = 8000.0,
)
log_mel_librosa = librosa.power_to_db(mel_librosa, ref=np.max)
print(f"librosa mel shape: {log_mel_librosa.shape}")  # (80, T)

# ── Whisper'ın kendi mel hesabı ───────────────────────────────
import whisper

# whisper.log_mel_spectrogram: [-11, 2] normalize eder
mel_whisper = whisper.log_mel_spectrogram("audio.wav")
print(f"Whisper mel: {mel_whisper.shape}")
# torch.Size([80, 3000]) — 30 saniye @ 100 frame/sn
# 30 sn'den kısa dosyalar sıfırla doldurulur (padding)
DİKKAT

Whisper girdi olarak tam olarak 30 saniyelik log-mel spectrogram bekler: şekil (80, 3000). Daha kısa dosyalar sıfır ile doldurulur (zero-padding), daha uzun dosyalar ise 30 saniyelik parçalara bölünür. Bu sabitleme, modelin her zaman aynı boyutlu girdi almasını garanti eder ancak çok uzun dosyalarda ek işlem gerektirir.

02 Whisper Mimarisi

OpenAI Whisper, encoder-decoder transformer mimarisini ses sinyaline uygulayan ve 99 dilde konuşma tanıyabilen bir multi-task modeldir.

Whisper (Robust Speech Recognition via Large-Scale Weak Supervision), 2022 yılında OpenAI tarafından yayımlandı. 680.000 saat çok dilli ses verisiyle eğitildi. Mimarisi klasik encoder-decoder transformer'dır ancak giriş yerine log-mel spectrogram alır.

Encoder: Log-mel spectrogram önce iki adet 1D konvolüsyon katmanından (convolutional stem) geçer. Bu katmanlar lokal özellik çıkarımı yapar ve zamansal çözünürlüğü azaltır. Ardından sinüsoidal pozisyonel kodlama eklenir ve standart transformer encoder blokları devreye girer. Encoder çıktısı, sesi kompakt bir temsile (context embedding) dönüştürür.

Decoder: GPT benzeri causal (otoregressif) transformer. Encoder çıktısına cross-attention uygular. Token-by-token metin üretir. Decoder girişine özel tokenlar eklenerek görev bildirilir: dil tespiti için <|en|> gibi dil token'ları, transkripsiyon için <|transcribe|>, çeviri için <|translate|>.

Bu multitask eğitim yaklaşımı, Whisper'ı aynı anda transkripsiyon, çeviri, dil tespiti ve timestamp çıkarma görevlerini yapabilen tek bir model haline getirir.

ModelParametreEncoder KatmanDecoder Katmand_modelHız*
tiny39M4438432×
base74M6651216×
small244M1212768
medium769M24241024
large-v21550M32321280
large-v31550M32321280

* Hız çarpanı large-v2 referans alınarak CPU üzerinde yaklaşık değerlerdir.

whisper_setup.py
# ── Kurulum ───────────────────────────────────────────────────
# pip install openai-whisper
# ffmpeg kurulu olmalı: apt install ffmpeg  /  brew install ffmpeg

import whisper
import torch

# ── Model yükle ───────────────────────────────────────────────
# "tiny" | "base" | "small" | "medium" | "large" | "large-v2" | "large-v3"
model = whisper.load_model("base")

# GPU varsa otomatik oraya gider
device = "cuda" if torch.cuda.is_available() else "cpu"
model.to(device)

# Parametre sayısı
params = sum(p.numel() for p in model.parameters())
print(f"Model: whisper-base | Parametre: {params:,} | Cihaz: {device}")

# ── Model boyutları ve bileşenleri ────────────────────────────
print("\nEncoder:")
print(f"  Konvolüsyon katmanları : 2")
print(f"  Transformer katmanları : {len(model.encoder.blocks)}")
print(f"  d_model               : {model.dims.n_audio_state}")

print("\nDecoder:")
print(f"  Transformer katmanları : {len(model.decoder.blocks)}")
print(f"  Vocab boyutu          : {model.dims.n_vocab}")
print(f"  Max seq uzunluğu      : {model.dims.n_text_ctx}")
NOT

Whisper large-v3, large-v2'ye kıyasla özellikle Türkçe, Arapça ve düşük kaynaklı diller için geliştirilmiş performans sunar. Türkçe transkripsiyon için large-v3 kullanmak önerilir. Bellek kısıtlıysa small model iyi bir denge noktasıdır.

03 Whisper ile Transkripsiyon

Whisper Python API'si birkaç satır kodla ses dosyasını metne çevirir; HuggingFace pipeline ise farklı ASR modelleriyle tutarlı bir arayüz sunar.

Whisper'ın model.transcribe() yöntemi, ses dosyasını otomatik olarak yükler, önce bir dil tespiti yapar (ilk 30 saniye üzerinden) ve ardından tam metni üretir. Çıktı üç anahtarlı bir sözlüktür: text (tam transkripsiyon), segments (her segment için başlangıç/bitiş zamanı ve metin) ve language (tespit edilen dil kodu).

faster-whisper, CTranslate2 motorunu kullanan yüksek performanslı bir Whisper implementasyonudur. Aynı modeli 4 kat daha hızlı ve %50 daha az bellek kullanarak çalıştırır. Üretim ortamları için resmi openai-whisper yerine faster-whisper tercih edilmelidir.

whisper_transcribe.py
import whisper
import json

# ── Temel transkripsiyon ──────────────────────────────────────
model  = whisper.load_model("base")
result = model.transcribe("konusma.wav")

print("Tam metin:", result["text"])
print("Dil      :", result["language"])

# ── Segment bazlı çıktı (timestamp ile) ──────────────────────
print("\nSegmentler:")
for seg in result["segments"]:
    start = seg["start"]
    end   = seg["end"]
    text  = seg["text"]
    print(f"  [{start:6.2f}s → {end:6.2f}s] {text.strip()}")

# ── Gelişmiş seçenekler ───────────────────────────────────────
result_tr = model.transcribe(
    "konusma.wav",
    language         = "tr",     # dil tespitini atla, direk Türkçe
    word_timestamps  = True,     # kelime bazlı timestamp
    temperature      = 0.0,     # greedy decoding (deterministik)
    beam_size        = 5,       # beam search (daha iyi ama yavaş)
    best_of          = 1,
    fp16             = True,    # half-precision (GPU için)
    verbose          = False,
    initial_prompt   = "Teknik bir sunum:",  # bağlam ipucu
)

# Kelime timestamp'leri
for seg in result_tr["segments"][:2]:
    for word in seg.get("words", []):
        print(f"  {word['word']:15s} {word['start']:.2f}s–{word['end']:.2f}s")
whisper_alternatives.py
# ── HuggingFace pipeline ile ASR ─────────────────────────────
from transformers import pipeline
import torch

# Whisper HuggingFace üzerinden (chunk_length_s ile uzun ses desteği)
asr = pipeline(
    "automatic-speech-recognition",
    model              = "openai/whisper-large-v3",
    device             = 0 if torch.cuda.is_available() else -1,
    torch_dtype        = torch.float16,
    chunk_length_s     = 30,       # 30 saniyelik parçalar
    batch_size         = 8,
    return_timestamps  = True,
)

result_hf = asr("uzun_konusma.wav", generate_kwargs={"language": "turkish"})
print(result_hf["text"])

# ── faster-whisper: üretim için önerilen ────────────────────
# pip install faster-whisper
from faster_whisper import WhisperModel

# int8 quantization → bellek ve hız optimizasyonu
fw_model = WhisperModel("large-v3", device="cuda", compute_type="int8_float16")

segments, info = fw_model.transcribe(
    "konusma.wav",
    beam_size          = 5,
    language           = "tr",
    word_timestamps    = True,
    vad_filter         = True,   # sessizlikleri otomatik atla
    vad_parameters     = {"min_silence_duration_ms": 500},
)

print(f"Tespit edilen dil: {info.language} ({info.language_probability:.2f})")
for segment in segments:
    print(f"[{segment.start:.2f}s → {segment.end:.2f}s] {segment.text}")
NOT

initial_prompt parametresi Whisper'ın bağlamını yönlendirmenin güçlü bir yoludur. Teknik terimleri, özel isimleri veya konuşma stilini ipucu olarak vermek, doğruluğu önemli ölçüde artırabilir. Maksimum 224 token uzunluğunda prompt kabul edilir.

04 Dil Tespiti ve Çeviri

Whisper, 99 dili tanıyabilir ve aynı model üzerinden doğrudan İngilizce çevirisi yapabilir; bu onu çok dilli pipeline'lar için güçlü kılar.

Whisper'ın dil tespiti şöyle çalışır: ilk 30 saniyenin log-mel spectrogram'ı encoder'dan geçirilir ve decoder başında bir dil token olasılık dağılımı üretilir. Bu dağılımdaki en yüksek olasılıklı token konuşmanın dilini belirtir. Bu işlem transkripsiyon öncesinde otomatik yapılır; language parametresini atlamak yeterlidir.

Çeviri görevi için task="translate" parametresi kullanılır. Bu, decoder'a başka dildeki konuşmayı doğrudan İngilizce'ye çevirmesini söyleyen bir multitask direktifidir. Türkçe → İngilizce, Almanca → İngilizce gibi çevirmeler için ek bir çeviri modeli gerekmez; Whisper'ın kendisi yeterlidir.

Türkçe ASR kalitesi açısından: Whisper large-v3, Türkçe için en iyi açık kaynak sonuçlarından birini sunar. small ve base modelleri Türkçe için daha hata eğilimlidir; üretimde en az medium önerilir.

whisper_multilingual.py
import whisper
import os

model = whisper.load_model("medium")

# ── Otomatik dil tespiti ──────────────────────────────────────
def detect_language(audio_path: str) -> tuple[str, float]:
    """Dosyanın dilini ve güvenilirlik skorunu döndür."""
    audio   = whisper.load_audio(audio_path)
    audio   = whisper.pad_or_trim(audio)                   # 30 sn
    mel     = whisper.log_mel_spectrogram(audio).to(model.device)
    _, probs = model.detect_language(mel)
    lang     = max(probs, key=probs.get)
    return lang, probs[lang]

lang, confidence = detect_language("konusma.wav")
print(f"Dil: {lang}, Güven: {confidence:.2%}")

# ── Türkçe transkripsiyon ─────────────────────────────────────
result_tr = model.transcribe(
    "konusma.wav",
    language    = "tr",
    task        = "transcribe",
    temperature = 0.0,
    beam_size   = 5,
)
print("\nTürkçe transkripsiyon:")
print(result_tr["text"])

# ── Türkçe → İngilizce çeviri ─────────────────────────────────
result_en = model.transcribe(
    "konusma.wav",
    language = "tr",
    task     = "translate",     # doğrudan İngilizce çeviri
)
print("\nİngilizce çeviri:")
print(result_en["text"])

# ── Toplu çok dilli dosya işleme ──────────────────────────────
audio_files = ["tr_speech.wav", "de_speech.wav", "ja_speech.mp3"]

for audio_file in audio_files:
    if not os.path.exists(audio_file):
        continue

    result = model.transcribe(audio_file)   # dil otomatik tespit
    print(f"\n{audio_file}")
    print(f"  Dil    : {result['language']}")
    print(f"  Metin  : {result['text'][:80]}...")

# ── Whisper'ın desteklediği 99 dilden örnekler ───────────────
supported = whisper.tokenizer.LANGUAGES
print(f"\nDesteklenen dil sayısı: {len(supported)}")  # 99
# {"af": "afrikaans", "ar": "arabic", "tr": "turkish", ...}
NOT

Çeviri görevi (task="translate") yalnızca kaynak dilden İngilizce'ye çevirir. Türkçe → Almanca çeviri için ayrı bir çeviri modeli (örn. HuggingFace'den Helsinki-NLP/opus-mt-tr-de) gerekir; Whisper'ı önce transkripsiyon için, ardından çeviriyi farklı bir model için kullanabilirsiniz.

05 TTS Modelleri

Text-to-speech için kullanım senaryosuna göre farklı modeller öne çıkar: Bark doğal sesler, Coqui TTS üretim ortamı ve voice cloning için öne çıkar.

Text-to-Speech (TTS) pipeline genel olarak şu adımlardan oluşur: metin ön işleme (normalizasyon, sayıları kelimeye çevirme) → fonetik dönüşüm (grapheme-to-phoneme) → akustik model (metin → mel spectrogram veya doğrudan dalga formu) → vocoder (mel spectrogram → ses dalgası). Modern uçtan uca modeller bu adımları tek bir sinir ağında birleştirir.

Bark (Suno AI): GPT tarzı bir dil modeli olarak metni sese dönüştürür. Yalnızca konuşma değil, kahkaha, müzik, ses efektleri de üretebilir. Çok sesli konuşmayı destekler; belirli bir ses için prompta konuşmacı etiketi eklenir. Kalite olağanüstüdür ancak yavaştır ve büyük GPU belleği gerektirir.

Coqui TTS: VITS, YourTTS, Tortoise gibi modellere ev sahipliği yapar. Voice cloning (ses klonlama) için referans ses dosyası yeterlidir. Python API ve REST API sunar. Türkçe dahil çok sayıda dilde önceden eğitilmiş modeller mevcuttur. Üretim ortamları için önerilir.

pyttsx3: işletim sistemi TTS motorunu (Microsoft SAPI, eSpeak) saran bir wrapper. İnternet bağlantısı gerektirmez, anında çalışır. Ses kalitesi düşük, Türkçe desteği kısıtlı ancak offline demo için uygundur.

ModelKaliteHızOfflineTürkçeVoice Clone
BarkÇok yüksekYavaşEvetKısıtlıHayır
Coqui TTS (VITS)YüksekOrtaEvetEvetEvet
Coqui TTS (Tortoise)Çok yüksekÇok yavaşEvetKısıtlıEvet
pyttsx3DüşükÇok hızlıEvetEvetHayır
gTTS (Google)İyiHızlıHayırEvetHayır
ElevenLabs APIÇok yüksekHızlıHayırEvetEvet
bark_tts.py
# pip install bark
from bark import SAMPLE_RATE, generate_audio, preload_models
import scipy.io.wavfile as wavfile
import numpy as np

# ── Model ağırlıklarını yükle (ilk çalıştırmada indirir) ─────
preload_models()

# ── Temel ses üretimi ─────────────────────────────────────────
text = "Merhaba! Yapay zeka ses sentezi gerçekten etkileyici bir teknoloji."

audio_array = generate_audio(text)
# audio_array: numpy float32, SAMPLE_RATE=24000

wavfile.write("bark_output.wav", SAMPLE_RATE, audio_array)
print(f"Ses kaydedildi: {len(audio_array)/SAMPLE_RATE:.2f} sn")

# ── Ses konuşmacı seçimi ──────────────────────────────────────
# Bark önceden tanımlı ses geçmişlerine sahiptir
VOICE_PRESETS = [
    "v2/en_speaker_0",     # İngilizce, erkek
    "v2/en_speaker_6",     # İngilizce, kadın
    "v2/tr_speaker_0",     # Türkçe (sınırlı)
]

audio = generate_audio(
    text,
    history_prompt = "v2/en_speaker_6",
)
wavfile.write("bark_voice.wav", SAMPLE_RATE, audio)

# ── Özel işaretler: kahkaha, müzik ───────────────────────────
funny_text = "Bu kod mükemmel çalışıyor! [laughs] Eh, neredeyse..."
music_text = "♪ Yapay zeka günleri ♪ [sings]"

audio_funny = generate_audio(funny_text)
wavfile.write("bark_funny.wav", SAMPLE_RATE, audio_funny)

# ── Coqui TTS alternatifi ─────────────────────────────────────
# pip install TTS
from TTS.api import TTS

# Mevcut Türkçe modellerini listele
tts_api = TTS()
# tts_api.list_models()  # tüm modelleri göster

# Türkçe VITS modeli
tts = TTS("tts_models/tr/common-voice/glow-tts")
tts.tts_to_file(
    text     = "Merhaba dünya, bu bir test cümlesidir.",
    file_path = "coqui_output.wav"
)
DİKKAT

Bark, tam modelle (~5 GB) çalışırken GPU'da 10-30 saniye ses üretim süresi beklenebilir. CPU'da bu süre dakikaları bulabilir. generate_audio() çağrısında silent=True parametresi ilerleme çubuğunu gizler; batch üretim için döngü yerine Bark'ın küçük model varyantını (suno/bark-small) HuggingFace'den yüklemek daha pratiktir.

06 Ses Ön İşleme

Gerçek dünya ses dosyaları ham gelir: yanlış format, yüksek örnekleme hızı, stereo kanal, gürültü — bunların tamamını pipeline başlamadan önce çözmek gerekir.

ffmpeg, ses ve video dönüşümünün standart aracıdır. Neredeyse her format ve kodeci destekler. Python'dan subprocess ile çağrılabilir ya da pydub kütüphanesi ffmpeg üstüne kurulu daha Pythonic bir API sunar. Yaygın dönüşümler: MP4/MP3 → WAV, stereo → mono, 44.1kHz → 16kHz, bit derinliği normalleştirme.

Gürültü azaltma konuşmadan arka plan gürültüsünü uzaklaştırır. noisereduce kütüphanesi istatistiksel spektral çıkarma yapar. Sinyal-gürültü oranı düşükse Whisper transkripsiyon doğruluğu belirgin biçimde düşer; gürültü azaltma ön adım olarak değer taşır.

Voice Activity Detection (VAD), ses dosyasındaki konuşma olan bölümleri tespit eder ve sessizlikleri atar. Bu, Whisper'ın gereksiz boş bölgeler üzerinde zaman harcamasını engeller ve uzun dosyalar için %50'ye varan hızlanma sağlayabilir. silero-vad, küçük (2 MB) ve hızlı bir VAD modelidir.

audio_preprocess.py
import subprocess
import torch
import torchaudio

# ── ffmpeg ile format dönüşümü ────────────────────────────────
def convert_audio(input_path: str, output_path: str,
                   target_sr: int = 16000, mono: bool = True) -> None:
    """ffmpeg ile ses dosyasını 16kHz mono WAV'a dönüştür."""
    ac_flag = "1" if mono else "2"
    cmd = [
        "ffmpeg", "-y",           # -y: var olan dosyanın üstüne yaz
        "-i", input_path,
        "-ar", str(target_sr),   # örnekleme hızı
        "-ac", ac_flag,            # kanal sayısı
        "-acodec", "pcm_s16le",  # 16-bit PCM
        output_path
    ]
    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        raise RuntimeError(f"ffmpeg hatası:\n{result.stderr}")

# MP4 video ses parçası → 16kHz mono WAV
convert_audio("video.mp4", "audio_clean.wav")

# ── torchaudio ile resample ───────────────────────────────────
waveform, sr = torchaudio.load("audio_44k.wav")

if sr != 16000:
    resampler = torchaudio.transforms.Resample(
        orig_freq = sr,
        new_freq  = 16000,
        resampling_method = "sinc_interp_hann"   # yüksek kalite
    )
    waveform = resampler(waveform)
    sr = 16000

# ── Ses normalleştirme ────────────────────────────────────────
def normalize_audio(waveform: torch.Tensor, target_rms: float = 0.1) -> torch.Tensor:
    """RMS bazlı ses normalleştirme."""
    rms = waveform.pow(2).mean().sqrt()
    if rms > 0:
        waveform = waveform * (target_rms / rms)
    return waveform.clamp(-1.0, 1.0)

waveform = normalize_audio(waveform)

# ── Gürültü azaltma: noisereduce ─────────────────────────────
# pip install noisereduce
import noisereduce as nr
import numpy as np

audio_np  = waveform.squeeze().numpy()
# İlk 0.5 sn'yi gürültü profili olarak kullan
noise_sample = audio_np[:int(0.5 * sr)]
reduced      = nr.reduce_noise(y=audio_np, sr=sr, y_noise=noise_sample,
                                 prop_decrease=0.8)
waveform_denoised = torch.from_numpy(reduced).unsqueeze(0)
silero_vad.py
# ── silero-vad ile Voice Activity Detection ───────────────────
# pip install silero-vad  (veya torch.hub)
import torch
import torchaudio
from silero_vad import load_silero_vad, get_speech_timestamps

# VAD modelini yükle
vad_model, utils = torch.hub.load(
    repo_or_dir  = "snakers4/silero-vad",
    model        = "silero_vad",
    force_reload = False
)
get_speech_ts, save_audio, read_audio, VADIterator, collect_chunks = utils

# Ses dosyasını yükle (8000 veya 16000 Hz desteklenir)
wav = read_audio("audio.wav", sampling_rate=16000)

# Konuşma bölgelerini tespit et
speech_timestamps = get_speech_ts(
    wav, vad_model,
    sampling_rate           = 16000,
    min_speech_duration_ms  = 250,   # 250ms'den kısa konuşmaları yok say
    min_silence_duration_ms = 100,   # 100ms'den kısa sessizlikleri birleştir
    threshold               = 0.5,   # konuşma olasılığı eşiği
)

print(f"Konuşma bölge sayısı: {len(speech_timestamps)}")
for i, ts in enumerate(speech_timestamps[:3]):
    start_s = ts["start"] / 16000
    end_s   = ts["end"]   / 16000
    print(f"  Bölge {i+1}: {start_s:.2f}s – {end_s:.2f}s")

# Sadece konuşma olan bölümleri birleştir
speech_only = collect_chunks(speech_timestamps, wav)
save_audio("speech_only.wav", speech_only, sampling_rate=16000)

toplam_sure  = len(wav) / 16000
konusma_sure = len(speech_only) / 16000
print(f"Toplam süre   : {toplam_sure:.1f} sn")
print(f"Konuşma süresi: {konusma_sure:.1f} sn ({konusma_sure/toplam_sure:.0%})")
NOT

faster-whisper'ın vad_filter=True seçeneği silero-vad'ı dahili olarak kullanır; ayrı bir VAD adımı eklemenize gerek kalmaz. Bununla birlikte VAD'ı önceden ayrı çalıştırmak, çok uzun dosyaları Whisper'a vermeden önce küçük parçalara bölerek bellek yönetimi açısından avantaj sağlar.

07 Gerçek Zamanlı Streaming ASR

Mikrofon girdisini küçük parçalara bölerek her parçayı Whisper'a beslemek, canlı altyazı ve sesli asistan gibi uygulamaların temelini oluşturur.

Streaming ASR'nin temel zorluğu şudur: Whisper 30 saniyelik sabit giriş için tasarlanmıştır, ancak gerçek zamanlı kullanımda sesin ne zaman biteceği bilinmez. Çözüm chunk-based (parça tabanlı) işleme'dir: mikrofon N milisaniyede bir tampondan okur ve her chunk Whisper'a verilir. En basit yaklaşım her N saniyede bir bağımsız inference yapmaktır.

Daha gelişmiş yaklaşım sliding window'dur: önceki pencerenin bir kısmını yeni pencereye katarak bağlam sürekliliği sağlanır. Bu sayede cümle sınırlarında kesik transkripsiyon azalır. Ancak çakışan bölgelerin tekilleştirilmesi (deduplication) gerekir.

pyaudio, PortAudio'nun Python sarmalayıcısıdır ve mikrofon erişimi sağlar. Ses akışı bir callback fonksiyonu veya blok okuma ile alınır. whisper-live projesi bu akışı WebSocket üzerinden bir sunucuya ileterek birden fazla istemciye gerçek zamanlı transkripsiyon sunar.

realtime_asr.py
# pip install pyaudio openai-whisper numpy
import whisper
import pyaudio
import numpy as np
import queue
import threading
import time

# ── Konfigürasyon ─────────────────────────────────────────────
SAMPLE_RATE   = 16000
CHUNK_SIZE    = 1024          # her okuma bloğu (64ms @ 16kHz)
BUFFER_SEC    = 3             # transkripsiyon için biriktir
BUFFER_FRAMES = SAMPLE_RATE * BUFFER_SEC

audio_queue   = queue.Queue()

# ── Whisper modeli yükle ─────────────────────────────────────
model = whisper.load_model("tiny")     # tiny: hızlı, gerçek zamanlı için uygun

# ── Mikrofon okuma thread'i ───────────────────────────────────
def audio_capture_thread(stop_event: threading.Event):
    p      = pyaudio.PyAudio()
    stream = p.open(
        format            = pyaudio.paInt16,
        channels          = 1,
        rate              = SAMPLE_RATE,
        input             = True,
        frames_per_buffer = CHUNK_SIZE
    )
    print("Dinleniyor... (Ctrl+C ile dur)")
    while not stop_event.is_set():
        data = stream.read(CHUNK_SIZE, exception_on_overflow=False)
        audio_queue.put(data)
    stream.stop_stream()
    stream.close()
    p.terminate()

# ── Transkripsiyon döngüsü ────────────────────────────────────
def transcription_loop(stop_event: threading.Event):
    buffer = np.array([], dtype=np.float32)

    while not stop_event.is_set():
        # Kuyruktaki tüm veriyi topla
        frames = []
        while not audio_queue.empty():
            raw   = audio_queue.get()
            chunk = np.frombuffer(raw, dtype=np.int16).astype(np.float32)
            chunk = chunk / 32768.0        # int16 → [-1, 1]
            frames.append(chunk)

        if frames:
            buffer = np.concatenate([buffer] + frames)

        # Yeterli ses birikince transkribe et
        if len(buffer) >= BUFFER_FRAMES:
            segment = buffer[:BUFFER_FRAMES]
            buffer  = buffer[BUFFER_FRAMES:]   # işlenen kısmı at

            # Sessizlik tespiti: RMS çok düşükse atla
            rms = np.sqrt(np.mean(segment ** 2))
            if rms < 0.01:
                continue

            result = model.transcribe(segment, language="tr",
                                         fp16=False)
            text = result["text"].strip()
            if text:
                print(f"\r[{time.strftime('%H:%M:%S')}] {text}", end="", flush=True)

        time.sleep(0.05)   # 50ms polling

# ── Ana program ───────────────────────────────────────────────
if __name__ == "__main__":
    stop_event = threading.Event()
    try:
        t1 = threading.Thread(target=audio_capture_thread, args=(stop_event,))
        t2 = threading.Thread(target=transcription_loop,  args=(stop_event,))
        t1.start()
        t2.start()
        t1.join()
        t2.join()
    except KeyboardInterrupt:
        print("\nDurduruldu.")
        stop_event.set()
whisper_live_alt.py
# ── whisper-live: WebSocket tabanlı streaming ────────────────
# pip install whisper-live
#
# Sunucu tarafı:
from whisper_live.server import TranscriptionServer

server = TranscriptionServer()
server.run("0.0.0.0", 9090)   # WebSocket sunucusu başlat

#
# İstemci tarafı (başka bir terminalde):
from whisper_live.client import TranscriptionClient

client = TranscriptionClient(
    host           = "localhost",
    port           = 9090,
    lang           = "tr",
    translate      = False,
    model          = "small",
    use_vad        = True,
)
client()   # mikrofonu aç ve transkripsiyon başlat

# ── Dosya üzerinden streaming testi ──────────────────────────
client_file = TranscriptionClient(
    host    = "localhost",
    port    = 9090,
    lang    = "tr",
    model   = "small",
)
client_file("uzun_konusma.wav")
01 pyaudio → mikrofon → int16 PCM chunk (64ms)
02 queue.Queue aracılığıyla thread-safe transfer
03 Buffer birik (3 sn) → float32 normalize → RMS kontrol
04 whisper.transcribe(segment, language="tr")
05 Metin → stdout / WebSocket / dosyaya yaz
DİKKAT

Whisper tiny modeli ile 3 saniyelik buffer gerçek zamanlıya yakın transkripsiyon sağlasa da gecikme kaçınılmazdır. Gecikme oranını azaltmak için buffer süresini kısaltabilirsiniz ancak kısa segmentlerde bağlam eksikliği nedeniyle kelime hataları artar. Üretim için 2-4 saniyelik buffer ve base model dengeli bir başlangıç noktasıdır.