Tutorial OpenAI Whisper: Speech-to-Text dan Audio Transcription

# OpenAI Whisper - Tutorial Lengkap Speech-to-Text ## Daftar Isi 1. [Pendahuluan](#pendahuluan) 2. [Prasyarat](#prasyarat) 3. [Memahami Ukuran Model Whisper](#memahami-ukuran-model-whisper) 4. [Tran...

By Ruby Abdullah · · tutorial
WhisperSpeech-to-TextAudioOpenAITranscriptionPython

OpenAI Whisper - Tutorial Lengkap Speech-to-Text

Daftar Isi

  • Pendahuluan
  • Prasyarat
  • Memahami Ukuran Model Whisper
  • Transkripsi Audio Dasar
  • Dukungan Multi-Bahasa
  • Fine-Tuning Whisper dengan Dataset Kustom
  • Transkripsi Real-Time
  • Optimasi dengan Faster-Whisper
  • Integrasi dengan FastAPI
  • Pembuatan Subtitle (SRT/VTT)
  • Praktik Terbaik
  • Kesimpulan

  • Pendahuluan

    OpenAI Whisper adalah model pengenalan suara serbaguna yang dilatih dengan 680.000 jam data terawasi multibahasa dan multitugas yang dikumpulkan dari web. Model ini mampu melakukan pengenalan suara multibahasa, penerjemahan suara, dan identifikasi bahasa. Tutorial ini akan memandu Anda dari transkripsi dasar hingga deployment tingkat produksi menggunakan FastAPI.

    Whisper sangat cocok untuk berbagai skenario penggunaan di Indonesia, mulai dari transkripsi rapat, pembuatan subtitle video YouTube, hingga sistem customer service otomatis yang memerlukan konversi suara ke teks.

    Prasyarat

    • Python 3.9 atau lebih tinggi
    • GPU yang kompatibel dengan CUDA (disarankan untuk model yang lebih besar)
    • Pemahaman dasar tentang Python dan konsep machine learning

    Pasang dependensi utama:

    pip install openai-whisper
    

    pip install faster-whisper

    pip install fastapi uvicorn python-multipart

    pip install torch torchaudio

    pip install datasets transformers

    Memahami Ukuran Model Whisper

    Whisper hadir dalam beberapa ukuran model, masing-masing menawarkan pertukaran antara kecepatan dan akurasi:

    | Model | Parameter | Khusus Inggris | Multibahasa | VRAM Diperlukan | Kecepatan Relatif |

    |--------|-----------|----------------|-------------|-----------------|-------------------|

    | tiny | 39 M | tiny.en | tiny | ~1 GB | ~32x |

    | base | 74 M | base.en | base | ~1 GB | ~16x |

    | small | 244 M | small.en | small | ~2 GB | ~6x |

    | medium | 769 M | medium.en | medium | ~5 GB | ~2x |

    | large | 1550 M | N/A | large-v3 | ~10 GB | 1x |

    Pemilihan model yang tepat bergantung pada kebutuhan Anda:

    import whisper
    
    

    Untuk prototyping cepat atau lingkungan dengan sumber daya terbatas

    model = whisper.loadmodel("tiny")

    Untuk produksi dengan akurasi yang baik

    model = whisper.loadmodel("medium")

    Untuk akurasi terbaik (memerlukan GPU dengan VRAM 10GB+)

    model = whisper.loadmodel("large-v3")

    Untuk transkripsi Bahasa Indonesia, gunakan model multibahasa (tanpa akhiran .en) karena model khusus Inggris tidak mendukung bahasa lain.

    Transkripsi Audio Dasar

    Penggunaan paling sederhana adalah mentranskripsi file audio menjadi teks:

    import whisper
    
    

    model = whisper.loadmodel("medium")

    Transkripsi dasar

    result = model.transcribe("rekamanaudio.mp3")

    print(result["text"])

    Akses informasi segmen secara detail

    for segment in result["segments"]:

    print(f"[{segment['start']:.2f}d -> {segment['end']:.2f}d] {segment['text']}")

    Anda dapat menyesuaikan perilaku transkripsi dengan berbagai parameter:

    result = model.transcribe(
    

    "rekamanaudio.mp3",

    language="id", # Paksa bahasa Indonesia (lewati deteksi)

    temperature=0.0, # Lebih rendah = lebih deterministik

    wordtimestamps=True, # Dapatkan timestamp tingkat kata

    fp16=True, # Gunakan presisi setengah (khusus GPU)

    beamsize=5, # Pencarian beam untuk akurasi lebih baik

    bestof=5, # Jumlah kandidat saat sampling

    nospeechthreshold=0.6, # Ambang batas untuk menyaring segmen sunyi

    conditiononprevioustext=True, # Gunakan konteks dari segmen sebelumnya

    initialprompt="Rapat teknis tentang model machine learning."

    )

    Dengan timestamp tingkat kata

    for segment in result["segments"]:

    for wordinfo in segment.get("words", []):

    print(f" {wordinfo['word']} [{wordinfo['start']:.2f}d - {wordinfo['end']:.2f}d]")

    Dukungan Multi-Bahasa

    Whisper mendukung 99+ bahasa, termasuk Bahasa Indonesia. Anda dapat mendeteksi bahasa secara otomatis atau menentukannya:

    import whisper
    
    

    model = whisper.loadmodel("large-v3")

    Deteksi bahasa otomatis

    audio = whisper.loadaudio("audioasing.mp3")

    audio = whisper.padortrim(audio)

    mel = whisper.logmelspectrogram(audio).to(model.device)

    , probs = model.detectlanguage(mel)

    detectedlang = max(probs, key=probs.get)

    print(f"Bahasa terdeteksi: {detectedlang} (kepercayaan: {probs[detectedlang]:.2%})")

    Transkripsi dalam bahasa asli

    result = model.transcribe("audioasing.mp3", language=detectedlang)

    print(f"Transkripsi: {result['text']}")

    Terjemahkan ke Bahasa Inggris (penerjemahan suara)

    resulttranslated = model.transcribe(

    "audioasing.mp3",

    task="translate" # Menerjemahkan bahasa apa pun ke Inggris

    )

    print(f"Terjemahan: {resulttranslated['text']}")

    Pemrosesan batch untuk banyak file dengan bahasa berbeda:

    import os
    

    from pathlib import Path

    def batchtranscribe(audiodir: str, modelname: str = "medium") -> dict:

    model = whisper.loadmodel(modelname)

    results = {}

    for audiofile in Path(audiodir).glob(".mp3"):

    result = model.transcribe(str(audiofile))

    results[audiofile.name] = {

    "bahasa": result["language"],

    "teks": result["text"],

    "segmen": result["segments"]

    }

    print(f"Diproses: {audiofile.name} (bahasa: {result['language']})")

    return results

    semuahasil = batchtranscribe("/data/fileaudio/")

    Fine-Tuning Whisper dengan Dataset Kustom

    Fine-tuning Whisper pada data spesifik domain dapat meningkatkan akurasi secara drastis untuk kosakata khusus. Ini sangat berguna jika Anda bekerja dengan terminologi teknis, dialek daerah, atau jargon industri tertentu:

    from datasets import loaddataset, Audio
    

    from transformers import (

    WhisperFeatureExtractor,

    WhisperTokenizer,

    WhisperProcessor,

    WhisperForConditionalGeneration,

    Seq2SeqTrainingArguments,

    Seq2SeqTrainer,

    )

    import torch

    from dataclasses import dataclass

    from typing import Any, Dict, List, Union

    Muat komponen prosesor

    featureextractor = WhisperFeatureExtractor.frompretrained("openai/whisper-small")

    tokenizer = WhisperTokenizer.frompretrained(

    "openai/whisper-small", language="Indonesian", task="transcribe"

    )

    processor = WhisperProcessor.frompretrained(

    "openai/whisper-small", language="Indonesian", task="transcribe"

    )

    Muat dataset kustom Anda (contoh dengan Common Voice Indonesia)

    dataset = loaddataset("mozilla-foundation/commonvoice110", "id", split="train[:1000]")

    dataset = dataset.castcolumn("audio", Audio(samplingrate=16000))

    def persiapkandataset(batch):

    audio = batch["audio"]

    batch["inputfeatures"] = featureextractor(

    audio["array"], samplingrate=audio["samplingrate"]

    ).inputfeatures[0]

    batch["labels"] = tokenizer(batch["sentence"]).inputids

    return batch

    dataset = dataset.map(persiapkandataset, removecolumns=dataset.columnnames, numproc=4)

    Data collator untuk padding dinamis

    @dataclass

    class DataCollatorSpeechSeq2SeqWithPadding:

    processor: Any

    decoderstarttokenid: int

    def call(self, features: List[Dict[str, Union[List[int], torch.Tensor]]]) -> Dict[str, torch.Tensor]:

    inputfeatures = [{"inputfeatures": f["inputfeatures"]} for f in features]

    batch = self.processor.featureextractor.pad(inputfeatures, returntensors="pt")

    labelfeatures = [{"inputids": f["labels"]} for f in features]

    labelsbatch = self.processor.tokenizer.pad(labelfeatures, returntensors="pt")

    labels = labelsbatch["inputids"].maskedfill(labelsbatch.attentionmask.ne(1), -100)

    if (labels[:, 0] == self.decoderstarttokenid).all().cpu().item():

    labels = labels[:, 1:]

    batch["labels"] = labels

    return batch

    Muat model dan konfigurasi pelatihan

    model = WhisperForConditionalGeneration.frompretrained("openai/whisper-small")

    model.generationconfig.language = "Indonesian"

    model.generationconfig.task = "transcribe"

    model.generationconfig.forceddecoderids = None

    datacollator = DataCollatorSpeechSeq2SeqWithPadding(

    processor=processor,

    decoderstarttokenid=model.config.decoderstarttokenid,

    )

    trainingargs = Seq2SeqTrainingArguments(

    outputdir="./whisper-finetuned-id",

    perdevicetrainbatchsize=16,

    gradientaccumulationsteps=2,

    learningrate=1e-5,

    warmupsteps=500,

    maxsteps=4000,

    fp16=True,

    evaluationstrategy="steps",

    evalsteps=500,

    savesteps=1000,

    loggingsteps=25,

    generationmaxlength=225,

    predictwithgenerate=True,

    reportto=["tensorboard"],

    )

    trainer = Seq2SeqTrainer(

    args=trainingargs,

    model=model,

    traindataset=dataset,

    datacollator=datacollator,

    tokenizer=processor.featureextractor,

    )

    trainer.train()

    model.savepretrained("./whisper-finetuned-id-final")

    processor.savepretrained("./whisper-finetuned-id-final")

    Transkripsi Real-Time

    Implementasikan transkripsi real-time menggunakan input mikrofon dengan pendekatan sliding window:

    import numpy as np
    

    import sounddevice as sd

    import whisper

    import queue

    import threading

    class TranskripsiRealTime:

    def init(self, namamodel: str = "base", samplerate: int = 16000, durasiblok: float = 3.0):

    self.model = whisper.loadmodel(namamodel)

    self.samplerate = samplerate

    self.durasiblok = durasiblok

    self.antrianaudio = queue.Queue()

    self.sedangberjalan = False

    self.buffer = np.array([], dtype=np.float32)

    def callbackaudio(self, indata, frames, timeinfo, status):

    if status:

    print(f"Status audio: {status}")

    self.antrianaudio.put(indata.copy().flatten())

    def prosesaudio(self):

    while self.sedangberjalan:

    try:

    potonganaudio = self.antrianaudio.get(timeout=1.0)

    self.buffer = np.concatenate([self.buffer, potonganaudio])

    # Proses ketika buffer mencapai durasi yang diinginkan

    minsamples = int(self.samplerate self.durasiblok)

    if len(self.buffer) >= minsamples:

    dataaudio = self.buffer.copy()

    self.buffer = np.array([], dtype=np.float32)

    # Normalisasi audio

    dataaudio = dataaudio.astype(np.float32)

    if np.max(np.abs(dataaudio)) > 0:

    dataaudio = dataaudio / np.max(np.abs(dataaudio))

    result = self.model.transcribe(

    dataaudio,

    fp16=False,

    language="id",

    nospeechthreshold=0.5

    )

    teks = result["text"].strip()

    if teks:

    print(f">> {teks}")

    except queue.Empty:

    continue

    def mulai(self):

    self.sedangberjalan = True

    threadproses = threading.Thread(target=self.prosesaudio, daemon=True)

    threadproses.start()

    print("Mendengarkan... Tekan Ctrl+C untuk berhenti.")

    with sd.InputStream(

    samplerate=self.samplerate,

    channels=1,

    dtype="float32",

    callback=self.callbackaudio,

    blocksize=int(self.samplerate 0.5)

    ):

    try:

    while True:

    sd.sleep(100)

    except KeyboardInterrupt:

    self.sedangberjalan = False

    print("\nBerhenti.")

    Penggunaan

    transkriber = TranskripsiRealTime(namamodel="base", durasiblok=3.0)

    transkriber.mulai()

    Optimasi dengan Faster-Whisper

    faster-whisper menggunakan CTranslate2 untuk inferensi hingga 4x lebih cepat dengan penggunaan memori lebih rendah:
    from fasterwhisper import WhisperModel
    
    

    Muat model dengan optimasi CTranslate2

    model = WhisperModel(

    "large-v3",

    device="cuda",

    computetype="float16", # Opsi: float16, int8float16, int8

    cputhreads=4,

    numworkers=2

    )

    Transkripsi dengan faster-whisper

    segments, info = model.transcribe(

    "rekamanaudio.mp3",

    beamsize=5,

    language="id",

    vadfilter=True, # Filter Deteksi Aktivitas Suara

    vadparameters=dict(

    minsilencedurationms=500,

    speechpadms=400

    ),

    wordtimestamps=True

    )

    print(f"Bahasa terdeteksi: {info.language} (probabilitas: {info.languageprobability:.2f})")

    print(f"Durasi: {info.duration:.2f}d")

    for segment in segments:

    print(f"[{segment.start:.2f}d -> {segment.end:.2f}d] {segment.text}")

    if segment.words:

    for word in segment.words:

    print(f" {word.word} ({word.start:.2f}d - {word.end:.2f}d) p={word.probability:.2f}")

    Perbandingan benchmark:

    import time
    
    

    def benchmarkmodel(jaluraudio: str):

    # Whisper Standar

    import whisper

    modelstd = whisper.loadmodel("large-v3")

    mulai = time.time()

    resultstd = modelstd.transcribe(jaluraudio)

    waktustd = time.time() - mulai

    # Faster-Whisper

    from fasterwhisper import WhisperModel

    modelcepat = WhisperModel("large-v3", device="cuda", computetype="float16")

    mulai = time.time()

    segments, = modelcepat.transcribe(jaluraudio)

    = list(segments) # Konsumsi generator

    waktucepat = time.time() - mulai

    print(f"Whisper Standar: {waktustd:.2f}d")

    print(f"Faster-Whisper: {waktucepat:.2f}d")

    print(f"Percepatan: {waktustd / waktucepat:.1f}x")

    benchmarkmodel("audiouji.mp3")

    Integrasi dengan FastAPI

    Bangun API transkripsi siap produksi:

    from fastapi import FastAPI, UploadFile, File, HTTPException, BackgroundTasks
    

    from fastapi.responses import JSONResponse

    from fasterwhisper import WhisperModel

    import tempfile

    import os

    import uuid

    from typing import Optional

    from pydantic import BaseModel

    app = FastAPI(title="API Transkripsi Whisper", version="1.0.0")

    Instance model global

    model = WhisperModel("medium", device="cuda", computetype="float16")

    Penyimpanan job di memori (gunakan Redis untuk produksi)

    jobs: dict = {}

    class ResponTranskripsi(BaseModel):

    teks: str

    bahasa: str

    durasi: float

    segmen: list

    @app.post("/transkripsi", responsemodel=ResponTranskripsi)

    async def transkripsiaudio(

    file: UploadFile = File(...),

    bahasa: Optional[str] = None,

    tugas: str = "transcribe"

    ):

    formatdidukung = (".mp3", ".wav", ".m4a", ".flac", ".ogg", ".webm")

    if not file.filename.endswith(formatdidukung):

    raise HTTPException(statuscode=400, detail="Format audio tidak didukung")

    with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.filename)[1]) as tmp:

    konten = await file.read()

    tmp.write(konten)

    jalurtmp = tmp.name

    try:

    segmentsgen, info = model.transcribe(

    jalurtmp,

    language=bahasa,

    task=tugas,

    beamsize=5,

    vadfilter=True

    )

    daftarsegmen = []

    bagianteks = []

    for seg in segmentsgen:

    daftarsegmen.append({

    "mulai": round(seg.start, 2),

    "selesai": round(seg.end, 2),

    "teks": seg.text.strip()

    })

    bagianteks.append(seg.text.strip())

    return ResponTranskripsi(

    teks=" ".join(bagianteks),

    bahasa=info.language,

    durasi=round(info.duration, 2),

    segmen=daftarsegmen

    )

    finally:

    os.unlink(jalurtmp)

    def prosesjobtranskripsi(jobid: str, jalurfile: str, bahasa: Optional[str]):

    try:

    segmentsgen, info = model.transcribe(jalurfile, language=bahasa, vadfilter=True)

    daftarsegmen = []

    tekslengkap = []

    for seg in segmentsgen:

    daftarsegmen.append({

    "mulai": seg.start, "selesai": seg.end, "teks": seg.text.strip()

    })

    tekslengkap.append(seg.text.strip())

    jobs[jobid] = {

    "status": "selesai",

    "hasil": {

    "teks": " ".join(tekslengkap),

    "bahasa": info.language,

    "durasi": info.duration,

    "segmen": daftarsegmen

    }

    }

    except Exception as e:

    jobs[jobid] = {"status": "gagal", "error": str(e)}

    finally:

    os.unlink(jalurfile)

    @app.post("/transkripsi/async")

    async def transkripsiasync(

    backgroundtasks: BackgroundTasks,

    file: UploadFile = File(...),

    bahasa: Optional[str] = None

    ):

    jobid = str(uuid.uuid4())

    with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.filename)[1]) as tmp:

    konten = await file.read()

    tmp.write(konten)

    jalurtmp = tmp.name

    jobs[jobid] = {"status": "memproses"}

    backgroundtasks.addtask(prosesjobtranskripsi, jobid, jalurtmp, bahasa)

    return {"jobid": jobid, "status": "memproses"}

    @app.get("/transkripsi/status/{jobid}")

    async def ambilstatusjob(jobid: str):

    if jobid not in jobs:

    raise HTTPException(statuscode=404, detail="Job tidak ditemukan")

    return jobs[jobid]

    Jalankan dengan: uvicorn whisperapi:app --host 0.0.0.0 --port 8000

    Pembuatan Subtitle (SRT/VTT)

    Buat subtitle profesional dari audio:

    from fasterwhisper import WhisperModel
    
    

    def formattimestampsrt(detik: float) -> str:

    jam = int(detik // 3600)

    menit = int((detik % 3600) // 60)

    dtk = int(detik % 60)

    milis = int((detik % 1) 1000)

    return f"{jam:02d}:{menit:02d}:{dtk:02d},{milis:03d}"

    def formattimestampvtt(detik: float) -> str:

    jam = int(detik // 3600)

    menit = int((detik % 3600) // 60)

    dtk = int(detik % 60)

    milis = int((detik % 1) * 1000)

    return f"{jam:02d}:{menit:02d}:{dtk:02d}.{milis:03d}"

    def buatsrt(jaluraudio: str, jaluroutput: str, makskarakter: int = 42):

    model = WhisperModel("medium", device="cuda", computetype="float16")

    segments, info = model.transcribe(jaluraudio, wordtimestamps=True, vadfilter=True)

    kontensrt = []

    indeks = 1

    for segment in segments:

    teks = segment.text.strip()

    # Pecah baris yang terlalu panjang

    if len(teks) > makskarakter:

    tengah = len(teks) // 2

    posspasi = teks.rfind(" ", 0, tengah)

    if posspasi != -1:

    teks = teks[:posspasi] + "\n" + teks[posspasi + 1:]

    mulai = formattimestampsrt(segment.start)

    selesai = formattimestampsrt(segment.end)

    kontensrt.append(f"{indeks}\n{mulai} --> {selesai}\n{teks}\n")

    indeks += 1

    with open(jaluroutput, "w", encoding="utf-8") as f:

    f.write("\n".join(kontensrt))

    print(f"SRT tersimpan: {jaluroutput} ({indeks - 1} subtitle)")

    def buatvtt(jaluraudio: str, jaluroutput: str):

    model = WhisperModel("medium", device="cuda", computetype="float16")

    segments, info = model.transcribe(jaluraudio, wordtimestamps=True, vadfilter=True)

    barisvtt = ["WEBVTT", ""]

    for segment in segments:

    mulai = formattimestampvtt(segment.start)

    selesai = formattimestampvtt(segment.end)

    barisvtt.append(f"{mulai} --> {selesai}")

    barisvtt.append(segment.text.strip())

    barisvtt.append("")

    with open(jaluroutput, "w", encoding="utf-8") as f:

    f.write("\n".join(barisvtt))

    print(f"VTT tersimpan: {jaluroutput}")

    Buat kedua format

    buatsrt("kuliah.mp3", "kuliah.srt")

    buatvtt("kuliah.mp3", "kuliah.vtt")

    Praktik Terbaik

  • Pemilihan Model: Mulai dengan base atau small untuk pengembangan, gunakan medium atau large-v3 untuk produksi. Untuk Bahasa Indonesia, selalu gunakan model multibahasa.
  • Preprocessing Audio: Pastikan audio adalah WAV mono 16kHz untuk hasil optimal. Gunakan ffmpeg untuk konversi:
  •    ffmpeg -i input.mp4 -ar 16000 -ac 1 -c:a pcms16le output.wav

  • Manajemen Memori: Hapus model dari memori saat tidak digunakan. Gunakan faster-whisper dengan tipe komputasi int8 pada server tanpa GPU.
  • Filter VAD: Selalu aktifkan vadfilter=True di faster-whisper untuk melewati bagian sunyi dan meningkatkan kecepatan serta akurasi.
  • Prompt Awal: Berikan kosakata spesifik domain dalam parameter initialprompt untuk meningkatkan pengenalan istilah teknis. Contoh: "Rapat tentang implementasi microservice dan Kubernetes."
  • Pemrosesan Batch: Untuk volume besar, gunakan batching GPU dan proses file secara paralel dengan worker pool.
  • Penanganan Error: Selalu validasi file audio sebelum diproses dan implementasikan logika retry untuk API produksi.
  • Pemantauan: Catat durasi transkripsi, jumlah karakter, dan bahasa yang terdeteksi untuk pemantauan kualitas.
  • Kesimpulan

    OpenAI Whisper adalah sistem pengenalan suara yang kuat dan serbaguna. Dengan menggabungkannya dengan faster-whisper untuk optimasi, FastAPI untuk penyajian, dan fine-tuning yang tepat untuk adaptasi domain, Anda dapat membangun sistem transkripsi tingkat produksi yang menangani berbagai bahasa dan kondisi audio. Kunci utamanya adalah memilih ukuran model yang tepat untuk kebutuhan latensi dan akurasi Anda, serta memanfaatkan filter VAD dan preprocessing audio yang tepat untuk memaksimalkan kualitas.

    Untuk penggunaan di Indonesia, model large-v3 memberikan hasil terbaik untuk Bahasa Indonesia, sementara medium menawarkan keseimbangan yang baik antara akurasi dan kecepatan. Jangan lupa untuk melakukan fine-tuning jika Anda bekerja dengan dialek daerah atau terminologi industri yang spesifik.

    Artikel Terkait

    faster-whisper: Transkripsi Audio 4x Lebih Cepat dengan Memori Setengahnya

    faster-whisper: Transkripsi Audio 4x Lebih Cepat dengan Memori Setengahnya Halo temen-temen, kalau kalian pernah pakai W...

    Helicone: Cara Monitor dan Kontrol Semua LLM Call di Produksi

    Helicone: Cara Aku Memonitor dan Mengontrol Semua LLM Call di Produksi Temen-temen, kalau kalian sudah mulai serius bang...

    LiteLLM: Universal API Gateway untuk 100+ Model LLM

    LiteLLM: Universal API Gateway untuk 100+ Model LLM Dalam dunia AI yang berkembang pesat, kita dihadapkan dengan puluhan...

    PaddleOCR: Ekstraksi Teks dari Gambar dan Dokumen dengan Akurasi Tinggi

    PaddleOCR: Ekstraksi Teks dari Gambar dan Dokumen dengan Akurasi Tinggi Halo temen-temen, kali ini kita bahas salah satu...