Sesi Metne Çevirme: Whisper API Kurulumu ve Kullanımı
Ses dosyalarını metne çevirmek, eskiden ciddi bir altyapı ve uzmanlık gerektiriyordu. Kendi modelini eğitmek, ya da pahalı üçüncü parti servislere bağımlı olmak zorundaydın. OpenAI’ın Whisper modelini API üzerinden sunmaya başlamasıyla bu iş hem ucuzladı hem de inanılmaz derecede kolaylaştı. Bu yazıda Whisper API’yi sıfırdan kurup, gerçek dünya senaryolarında nasıl kullanacağını adım adım anlatacağım.
Whisper Nedir ve Neden API Kullanmalısın?
Whisper, OpenAI’ın geliştirdiği açık kaynaklı bir ses tanıma (ASR – Automatic Speech Recognition) modelidir. 680.000 saatlik çok dilli ses verisiyle eğitilmiş bu model, 99 dili destekliyor ve Türkçe dahil pek çok dilde oldukça başarılı sonuçlar üretiyor.
İki kullanım seçeneğin var: Modeli kendi sunucunda çalıştırmak ya da OpenAI’ın API’sini kullanmak. Kendi sunucunda çalıştırmak için ciddi GPU kaynağı gerekiyor. whisper-large-v3 modelini rahat çalıştırmak için en az 10GB VRAM’e ihtiyacın var. Eğer elimde güçlü bir GPU sunucusu yoksa ya da bu işi production’da düşük maliyetle halletmek istiyorsan API kullanmak çok daha mantıklı. Dakika başına $0.006 gibi bir fiyatla ciddi miktarda ses dosyası işleyebiliyorsun.
Ortamı Hazırlamak
Önce temiz bir Python sanal ortamı oluşturalım. Ben genellikle proje bazlı sanal ortam kullanırım, sistem Python’unu kirletmemek için.
# Python sanal ortamı oluştur
python3 -m venv whisper-env
source whisper-env/bin/activate
# Gerekli kütüphaneleri kur
pip install openai python-dotenv pydub requests
# Versiyonları kontrol et
pip list | grep -E "openai|pydub"
API anahtarını güvenli şekilde saklamak için .env dosyası kullanacağız. Anahtarı doğrudan koda gömmek kötü alışkanlık, özellikle git repo’larında bu hatayı çok sık görüyorum.
# .env dosyası oluştur
cat > .env << 'EOF'
OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxxxxxxxxxxxxxxx
EOF
# İzinleri kısıtla, sadece sahibi okuyabilsin
chmod 600 .env
# .gitignore'a ekle, unutma!
echo ".env" >> .gitignore
İlk Transkripsiyon: Basit Başlangıç
Ortamı hazırladıktan sonra en basit kullanım senaryosundan başlayalım. Bir ses dosyasını metne çevirip sonucu ekrana yazdıracağız.
#!/usr/bin/env python3
# transcribe_basic.py
import os
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def transcribe_audio(file_path: str, language: str = "tr") -> str:
"""
Ses dosyasını metne çevirir.
Desteklenen formatlar: mp3, mp4, mpeg, mpga, m4a, wav, webm
Maksimum dosya boyutu: 25MB
"""
with open(file_path, "rb") as audio_file:
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=audio_file,
language=language,
response_format="text"
)
return transcript
if __name__ == "__main__":
result = transcribe_audio("ornek_ses.mp3")
print(f"Transkripsiyon:n{result}")
Bu kadar basit. Ama production’da bu kadar sade bir yapıyla gidemezsin. Hata yönetimi, büyük dosya desteği ve toplu işleme gibi konulara da bakmamız gerekiyor.
Desteklenen Formatlar ve Dosya Boyutu Limiti
Whisper API’nin bazı kısıtlamaları var. En önemli kısıtlama 25MB dosya boyutu limiti. Uzun toplantı kayıtları ya da podcast dosyaları bu limiti kolayca aşabiliyor. Bu durumda dosyayı parçalara bölmen gerekiyor.
pydub kütüphanesiyle bunu otomatikleştirebilirsin. Ama pydub arka planda ffmpeg kullanıyor, önce onu kur:
# Ubuntu/Debian
sudo apt update && sudo apt install -y ffmpeg
# CentOS/RHEL/Rocky Linux
sudo dnf install -y ffmpeg
# macOS
brew install ffmpeg
# ffmpeg kurulumunu doğrula
ffmpeg -version | head -n 1
Büyük Dosyaları Parçalara Bölerek İşlemek
Şimdi gerçek dünyadaki en sık karşılaşılan sorunu çözelim: 25MB’ı aşan dosyalar.
#!/usr/bin/env python3
# transcribe_large.py
import os
import math
from pathlib import Path
from openai import OpenAI
from dotenv import load_dotenv
from pydub import AudioSegment
import tempfile
load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def split_audio(file_path: str, chunk_duration_ms: int = 600000) -> list:
"""
Ses dosyasını belirtilen sürede parçalara böler.
Varsayılan: 10 dakikalık parçalar (600000ms)
"""
audio = AudioSegment.from_file(file_path)
total_duration = len(audio)
chunks = []
num_chunks = math.ceil(total_duration / chunk_duration_ms)
print(f"Toplam süre: {total_duration/1000:.1f}s | {num_chunks} parçaya bölünüyor...")
for i in range(num_chunks):
start = i * chunk_duration_ms
end = min((i + 1) * chunk_duration_ms, total_duration)
chunk = audio[start:end]
chunks.append(chunk)
return chunks
def transcribe_large_audio(file_path: str, language: str = "tr") -> str:
"""
Büyük ses dosyalarını parçalara bölerek transkribe eder.
"""
chunks = split_audio(file_path)
full_transcript = []
with tempfile.TemporaryDirectory() as tmp_dir:
for idx, chunk in enumerate(chunks):
chunk_path = os.path.join(tmp_dir, f"chunk_{idx:03d}.mp3")
chunk.export(chunk_path, format="mp3", bitrate="64k")
chunk_size = os.path.getsize(chunk_path) / (1024 * 1024)
print(f"Parça {idx+1}/{len(chunks)} işleniyor... ({chunk_size:.1f}MB)")
with open(chunk_path, "rb") as audio_file:
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=audio_file,
language=language,
response_format="text"
)
full_transcript.append(transcript.strip())
return "n".join(full_transcript)
if __name__ == "__main__":
result = transcribe_large_audio("uzun_toplanti.mp4")
output_file = "transkript.txt"
with open(output_file, "w", encoding="utf-8") as f:
f.write(result)
print(f"nTranskripsiyon tamamlandı. Çıktı: {output_file}")
print(f"Toplam karakter sayısı: {len(result)}")
Zaman Damgalı Transkripsiyon
Sadece metni değil, konuşmanın tam olarak ne zaman yapıldığını da bilmek isteyebilirsin. Toplantı kayıtlarında “kim, ne zaman ne dedi” sorusuna cevap vermek için verbose_json formatını kullanmalısın.
#!/usr/bin/env python3
# transcribe_timestamps.py
import os
import json
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def transcribe_with_timestamps(file_path: str, language: str = "tr") -> dict:
"""
Zaman damgalı transkripsiyon döner.
verbose_json formatı segment ve kelime bazlı timestamp içerir.
"""
with open(file_path, "rb") as audio_file:
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=audio_file,
language=language,
response_format="verbose_json",
timestamp_granularities=["segment", "word"]
)
return transcript
def format_transcript_with_timestamps(transcript) -> str:
"""
Transkripsiyon sonucunu okunabilir formata çevirir.
"""
output_lines = []
output_lines.append(f"Dil: {transcript.language}")
output_lines.append(f"Toplam süre: {transcript.duration:.1f} saniyen")
output_lines.append("=" * 60)
for segment in transcript.segments:
start = segment['start']
end = segment['end']
text = segment['text'].strip()
# Zamanı dakika:saniye formatına çevir
start_fmt = f"{int(start//60):02d}:{int(start%60):02d}"
end_fmt = f"{int(end//60):02d}:{int(end%60):02d}"
output_lines.append(f"[{start_fmt} - {end_fmt}] {text}")
return "n".join(output_lines)
if __name__ == "__main__":
transcript = transcribe_with_timestamps("toplanti_kaydi.mp3")
# JSON olarak kaydet (ham veri)
with open("transkript_raw.json", "w", encoding="utf-8") as f:
json.dump(transcript.model_dump(), f, ensure_ascii=False, indent=2)
# Okunabilir format olarak kaydet
formatted = format_transcript_with_timestamps(transcript)
with open("transkript_zamanli.txt", "w", encoding="utf-8") as f:
f.write(formatted)
print(formatted)
SRT Altyazı Dosyası Üretmek
Video içeriklerin için otomatik altyazı oluşturmak istiyor olabilirsin. Whisper API doğrudan srt formatı destekliyor, bunu bilmeyenler çok oluyor.
#!/usr/bin/env python3
# generate_subtitles.py
import os
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def generate_srt(file_path: str, output_path: str, language: str = "tr") -> None:
"""
Ses/video dosyasından SRT formatında altyazı üretir.
Doğrudan VLC, Premiere, DaVinci vb. ile kullanılabilir.
"""
with open(file_path, "rb") as audio_file:
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=audio_file,
language=language,
response_format="srt"
)
with open(output_path, "w", encoding="utf-8") as srt_file:
srt_file.write(transcript)
print(f"SRT dosyası oluşturuldu: {output_path}")
def generate_vtt(file_path: str, output_path: str, language: str = "tr") -> None:
"""
WebVTT formatında altyazı üretir.
Web tabanlı video oynatıcılar için idealdir.
"""
with open(file_path, "rb") as audio_file:
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=audio_file,
language=language,
response_format="vtt"
)
with open(output_path, "w", encoding="utf-8") as vtt_file:
vtt_file.write(transcript)
print(f"VTT dosyası oluşturuldu: {output_path}")
if __name__ == "__main__":
# Eğitim videosu için hem SRT hem VTT üret
generate_srt("egitim_videosu.mp4", "altyazi.srt")
generate_vtt("egitim_videosu.mp4", "altyazi.vtt")
Prompt ile Doğruluğu Artırmak
Whisper API’nin az bilinen ama çok işe yarayan bir özelliği var: prompt parametresi. Bu parametreyle modele bağlam bilgisi verebiliyorsun. Teknik terimler, özel isimler ya da alan jargonu içeren konuşmalarda bu fark yaratıyor.
#!/usr/bin/env python3
# transcribe_with_prompt.py
import os
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def transcribe_technical_meeting(file_path: str) -> str:
"""
Teknik toplantı kayıtları için optimize edilmiş transkripsiyon.
Prompt ile teknik terimler ve kişi isimleri modele öğretiliyor.
"""
# Teknik toplantı için bağlam prompt'u
context_prompt = """
Bu bir yazılım geliştirme ekibinin sprint toplantısı kaydıdır.
Sık geçen terimler: Kubernetes, Helm chart, CI/CD pipeline,
GitLab Runner, Redis cluster, PostgreSQL, microservice,
load balancer, ingress controller, HPA (Horizontal Pod Autoscaler).
Katılımcılar: Ahmet (Backend Lead), Zeynep (DevOps), Can (Frontend).
"""
with open(file_path, "rb") as audio_file:
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=audio_file,
language="tr",
prompt=context_prompt,
response_format="text",
temperature=0.0 # Daha deterministik sonuçlar için
)
return transcript
def transcribe_customer_call(file_path: str, customer_name: str, product_name: str) -> str:
"""
Müşteri görüşmeleri için özelleştirilmiş transkripsiyon.
"""
context_prompt = f"""
Bu {customer_name} ile yapılan müşteri destek görüşmesidir.
Ürün adı: {product_name}.
Konuşma Türkçe olup müşteri hizmetleri kaydıdır.
"""
with open(file_path, "rb") as audio_file:
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=audio_file,
language="tr",
prompt=context_prompt,
response_format="verbose_json"
)
return transcript.text
if __name__ == "__main__":
# Teknik toplantı transkripti
result = transcribe_technical_meeting("sprint_meeting.mp3")
print(result)
Toplu İşleme: Klasördeki Tüm Sesleri İşlemek
Gerçek dünya senaryosunda genellikle tek bir dosya değil, yüzlerce dosya işlemen gerekir. Bir çağrı merkezinin aylık ses kayıtları, bir podcast arşivi ya da video kursunun tüm bölümleri gibi. Bunun için paralel işleme yapısı kurmalısın.
#!/usr/bin/env python3
# batch_transcribe.py
import os
import time
import json
import logging
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
from openai import OpenAI, RateLimitError, APIError
from dotenv import load_dotenv
load_dotenv()
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.FileHandler("transcription.log"),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
SUPPORTED_FORMATS = {".mp3", ".mp4", ".wav", ".m4a", ".webm", ".mpga", ".mpeg"}
MAX_FILE_SIZE_MB = 25
MAX_WORKERS = 3 # Rate limit aşmamak için düşük tut
def transcribe_single(file_path: Path, language: str = "tr", retries: int = 3) -> dict:
"""
Tek bir dosyayı transkribe eder, hata durumunda retry uygular.
"""
file_size = file_path.stat().st_size / (1024 * 1024)
if file_size > MAX_FILE_SIZE_MB:
return {
"file": str(file_path),
"status": "error",
"message": f"Dosya çok büyük: {file_size:.1f}MB (max {MAX_FILE_SIZE_MB}MB)"
}
for attempt in range(retries):
try:
with open(file_path, "rb") as audio_file:
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=audio_file,
language=language,
response_format="text"
)
logger.info(f"OK: {file_path.name}")
return {
"file": str(file_path),
"status": "success",
"transcript": transcript
}
except RateLimitError:
wait_time = 2 ** attempt * 5 # Exponential backoff: 5, 10, 20 saniye
logger.warning(f"Rate limit! {wait_time}s bekleniyor... ({file_path.name})")
time.sleep(wait_time)
except APIError as e:
logger.error(f"API hatası ({file_path.name}): {e}")
if attempt == retries - 1:
return {"file": str(file_path), "status": "error", "message": str(e)}
return {"file": str(file_path), "status": "error", "message": "Max retry aşıldı"}
def batch_transcribe(input_dir: str, output_dir: str, language: str = "tr") -> None:
"""
Bir klasördeki tüm ses dosyalarını toplu olarak işler.
"""
input_path = Path(input_dir)
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
# Desteklenen dosyaları topla
audio_files = [
f for f in input_path.iterdir()
if f.suffix.lower() in SUPPORTED_FORMATS
]
if not audio_files:
logger.warning(f"İşlenecek ses dosyası bulunamadı: {input_dir}")
return
logger.info(f"{len(audio_files)} dosya işlenecek...")
results = []
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
future_to_file = {
executor.submit(transcribe_single, f, language): f
for f in audio_files
}
for future in as_completed(future_to_file):
result = future.result()
results.append(result)
if result["status"] == "success":
# Her başarılı transkripti ayrı dosyaya kaydet
txt_file = output_path / (Path(result["file"]).stem + ".txt")
with open(txt_file, "w", encoding="utf-8") as f:
f.write(result["transcript"])
# Özet rapor
success = sum(1 for r in results if r["status"] == "success")
failed = len(results) - success
summary = {
"total": len(results),
"success": success,
"failed": failed,
"results": results
}
with open(output_path / "summary.json", "w", encoding="utf-8") as f:
json.dump(summary, f, ensure_ascii=False, indent=2)
logger.info(f"Tamamlandı: {success} başarılı, {failed} hatalı")
if __name__ == "__main__":
batch_transcribe(
input_dir="./ses_kayitlari",
output_dir="./transkriptler",
language="tr"
)
Maliyet Takibi
Production kullanımında maliyet kontrolü kritik. Whisper API şu an dakika başına $0.006 ücretiyle oldukça ucuz ama ölçek büyüyünce takip etmezsen fatura sürpriz yapabilir.
# Bir klasördeki toplam ses süresini hesapla (ffmpeg ile)
find ./ses_kayitlari -name "*.mp3" -o -name "*.wav" -o -name "*.m4a" |
xargs -I{} ffprobe -v quiet -show_entries format=duration
-of csv=p=0 {} |
awk '{sum += $1} END {
dakika = sum / 60
maliyet = dakika * 0.006
printf "Toplam süre: %.1f dakikanTahmini maliyet: $%.4fn", dakika, maliyet
}'
Gerçek Dünya Senaryoları
Çağrı Merkezi Analizi: Günlük yüzlerce müşteri görüşmesini otomatik transkribe edip, ardından GPT-4 ile duygu analizi yapabilirsin. Şikayet konularını otomatik kategorize etmek, kalite güvence ekibinin manuel dinleme yükünü ciddi ölçüde azaltıyor.
Podcast ve YouTube İçerikleri: Tüm bölümlerin transkriptini çıkarıp SEO odaklı makale haline getirmek, ya da içerik arama sistemi kurmak için idealdir.
Toplantı Zekası: Zoom, Teams veya Google Meet kayıtlarını otomatik transkribe edip action item’ları GPT ile çıkarmak, ardından Slack veya Jira’ya otomatik görev açmak güçlü bir otomasyon zinciri oluşturur.
Eğitim Platformları: Video kurs içeriklerini transkribe edip hem erişilebilirlik (işitme engelli kullanıcılar) hem de arama özelliği sağlamak için kullanılabilir.
Dikkat Edilmesi Gereken Noktalar
- KVKK ve Veri Gizliliği: Ses kayıtlarını OpenAI sunucularına göndermeden önce içeriğin kişisel veri barındırıp barındırmadığını kontrol et. Hassas sektörlerde (sağlık, hukuk, finans) yerel Whisper kurulumu daha uygun olabilir.
- Rate Limiting: Ücretsiz tier’da dakikada 3 istek limiti var. Production hesaplarda bu limit daha yüksek ama toplu işlemede exponential backoff mutlaka uygula.
- Ses Kalitesi Önemli: 8kHz telefon kalitesindeki ses kayıtlarında doğruluk düşer. Mümkünse 16kHz veya üzeri örnekleme hızı kullan.
- Dil Tespiti:
languageparametresini belirtmezsen model dili otomatik tahmin eder. Bu bazı durumlarda hata yapabilir, özellikle kod switch (dil geçişi) içeren konuşmalarda.
Sonuç
Whisper API, ses-metin dönüşümü işini gerçekten demokratikleştirdi. Birkaç satır Python koduyla enterprise seviye doğrulukta transkripsiyon yapabiliyorsun. Bu yazıda temel kullanımdan başlayıp büyük dosya işleme, zaman damgalı transkripsiyon, altyazı üretimi ve toplu işleme gibi production seviye konulara kadar kapsamlı bir tablo çizdik.
Bir sonraki adım olarak Whisper çıktısını GPT-4 ile entegre edip özetleme, anahtar kelime çıkarma ya da konuşma analizi gibi katma değerli işler yapmayı düşünebilirsin. API’ler birbirini bu kadar iyi tamamlıyor ki bu kombinasyonu bir kez kullandıktan sonra manuel transkripsiyon fikrine artık katlanamıyorsun.
Herhangi bir konuda takılırsan ya da özel bir senaryon varsa yorumlarda belirt, birlikte çözelim.
