Fine-tuning Whisper для доменной распознавания речи
Базовый Whisper large-v3 показывает WER 8–15% на общей речи. На узкоспециализированной лексике (медицина, юриспруденция, финансы, технический жаргон) — WER вырастает до 25–40%. Fine-tuning на доменном датасете снижает его до 3–8%.
Когда нужен fine-tuning
Базовый Whisper справляется плохо при:
- Терминологии с редкими словами: «гепатоспленомегалия», «форс-мажорные обстоятельства», аббревиатуры типа «КТ», «МРТ», «ДДУ»
- Акцентной речи определённого региона
- Шумных условиях записи (колл-центр, производство)
- Смешанной речи (код-свитчинг: «сделаем deploy в staging»)
Подготовка датасета
Минимальный объём для заметного улучшения WER — 10–20 часов, оптимальный — 50–100 часов:
from datasets import Dataset, Audio
import pandas as pd
def prepare_whisper_dataset(
audio_dir: str,
transcripts_csv: str,
target_language: str = "russian"
) -> Dataset:
"""
transcripts_csv: columns = [audio_file, transcription]
"""
df = pd.read_csv(transcripts_csv)
dataset = Dataset.from_dict({
"audio": [f"{audio_dir}/{f}" for f in df["audio_file"]],
"sentence": df["transcription"].tolist()
})
dataset = dataset.cast_column("audio", Audio(sampling_rate=16000))
return dataset
Препроцессинг с feature extractor Whisper:
from transformers import WhisperFeatureExtractor, WhisperTokenizer
feature_extractor = WhisperFeatureExtractor.from_pretrained("openai/whisper-large-v3")
tokenizer = WhisperTokenizer.from_pretrained(
"openai/whisper-large-v3",
language="Russian",
task="transcribe"
)
def prepare_dataset(batch):
audio = batch["audio"]
batch["input_features"] = feature_extractor(
audio["array"],
sampling_rate=audio["sampling_rate"]
).input_features[0]
batch["labels"] = tokenizer(batch["sentence"]).input_ids
return batch
dataset = dataset.map(prepare_dataset, remove_columns=["audio", "sentence"])
Fine-tuning с Seq2SeqTrainer
from transformers import (
WhisperForConditionalGeneration,
Seq2SeqTrainingArguments,
Seq2SeqTrainer
)
from dataclasses import dataclass
import evaluate
import torch
model = WhisperForConditionalGeneration.from_pretrained("openai/whisper-large-v3")
model.generation_config.language = "russian"
model.generation_config.task = "transcribe"
model.generation_config.forced_decoder_ids = None
training_args = Seq2SeqTrainingArguments(
output_dir="./whisper-finetuned-ru-medical",
per_device_train_batch_size=16,
gradient_accumulation_steps=1,
learning_rate=1e-5,
warmup_steps=500,
max_steps=4000,
gradient_checkpointing=True,
fp16=True,
evaluation_strategy="steps",
per_device_eval_batch_size=8,
predict_with_generate=True,
generation_max_length=225,
save_steps=500,
eval_steps=500,
logging_steps=25,
report_to=["tensorboard"],
load_best_model_at_end=True,
metric_for_best_model="wer",
greater_is_better=False,
push_to_hub=False
)
wer_metric = evaluate.load("wer")
def compute_metrics(pred):
pred_ids = pred.predictions
label_ids = pred.label_ids
label_ids[label_ids == -100] = tokenizer.pad_token_id
pred_str = tokenizer.batch_decode(pred_ids, skip_special_tokens=True)
label_str = tokenizer.batch_decode(label_ids, skip_special_tokens=True)
wer = 100 * wer_metric.compute(predictions=pred_str, references=label_str)
return {"wer": wer}
trainer = Seq2SeqTrainer(
args=training_args,
model=model,
train_dataset=dataset["train"],
eval_dataset=dataset["test"],
data_collator=data_collator,
compute_metrics=compute_metrics,
tokenizer=feature_extractor
)
trainer.train()
LoRA для экономии памяти
При ограниченных ресурсах (< 24 GB VRAM) используем PEFT/LoRA:
from peft import get_peft_model, LoraConfig, TaskType
lora_config = LoraConfig(
r=32,
lora_alpha=64,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.05,
bias="none",
task_type=TaskType.SEQ_2_SEQ_LM
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# trainable params: 15,728,640 || all params: 1,557,741,568 (1.01%)
Результаты по доменам
| Домен | Базовый WER | После fine-tuning | Датасет |
|---|---|---|---|
| Медицинские диктовки | 31% | 6.2% | 40 ч протоколов |
| Юридические договоры | 24% | 4.8% | 30 ч слушаний |
| Колл-центр (финансы) | 18% | 5.1% | 50 ч звонков |
| Технический support | 22% | 7.3% | 25 ч тикетов |
Инференс с fine-tuned моделью
from transformers import pipeline
asr = pipeline(
"automatic-speech-recognition",
model="./whisper-finetuned-ru-medical",
device=0,
torch_dtype=torch.float16,
generate_kwargs={
"language": "russian",
"task": "transcribe",
"num_beams": 5
}
)
result = asr("patient_recording.wav", chunk_length_s=30, stride_length_s=5)
print(result["text"])
Сроки: разметка 30-часового корпуса — 2–3 недели. Fine-tuning + подбор гиперпараметров — 1 неделя. Интеграция в продакшн-пайплайн — 3–5 дней.







