import os import gradio as gr import torch import numpy as np # ── Constants ────────────────────────────────────────────────────────────────── BASE_MODEL_ID = "openai/whisper-tiny" LORA_MODEL_ID = "dungca/whisper-tiny-ja-lora" SAMPLING_RATE = 16000 HF_TOKEN = os.environ.get("HF_TOKEN", None) # ── Lazy model loading ───────────────────────────────────────────────────────── _processor = None _model = None def get_model(): global _processor, _model if _model is None: from transformers import AutoProcessor, WhisperForConditionalGeneration from peft import PeftModel print("Loading processor...") _processor = AutoProcessor.from_pretrained(BASE_MODEL_ID, token=HF_TOKEN) print("Loading base model...") base_model = WhisperForConditionalGeneration.from_pretrained( BASE_MODEL_ID, token=HF_TOKEN ) print("Loading LoRA adapter...") _model = PeftModel.from_pretrained(base_model, LORA_MODEL_ID, token=HF_TOKEN) _model.eval() if torch.cuda.is_available(): _model = _model.cuda() print("Using GPU ✓") else: print("Using CPU") return _processor, _model # ── Inference ────────────────────────────────────────────────────────────────── def transcribe(audio): if audio is None: return "⚠️ Vui lòng cung cấp audio (upload file hoặc record từ mic)." try: processor, model = get_model() except Exception as e: return f"❌ Lỗi load model: {str(e)}" sr, audio_array = audio audio_array = audio_array.astype(np.float32) if audio_array.ndim > 1: audio_array = audio_array.mean(axis=1) max_val = np.abs(audio_array).max() if max_val > 0: audio_array = audio_array / max_val if sr != SAMPLING_RATE: import librosa audio_array = librosa.resample(audio_array, orig_sr=sr, target_sr=SAMPLING_RATE) inputs = processor(audio_array, sampling_rate=SAMPLING_RATE, return_tensors="pt") if torch.cuda.is_available(): inputs = {k: v.cuda() for k, v in inputs.items()} with torch.no_grad(): predicted_ids = model.generate( input_features=inputs["input_features"], language="japanese", task="transcribe", max_new_tokens=256, ) transcription = processor.batch_decode(predicted_ids, skip_special_tokens=True)[0] return transcription.strip() if transcription.strip() else "(音声が認識できませんでした)" # ── UI — Gradio 6.x compatible ──────────────────────────────────────────────── CSS = """ .gradio-container { max-width: 800px !important; margin: auto !important; } footer { display: none !important; } """ with gr.Blocks(title="Whisper Japanese ASR Demo") as demo: gr.HTML("""
LoRA fine-tuned on ReazonSpeech ·
Base: openai/whisper-tiny ·
Adapter:
dungca/whisper-tiny-ja-lora