EmbeddingGemma2-Bashkir-CV25
Башкирская модель распознавания речи на основе аудиоэнкодера Google EmbeddingGemma 2 и CTC. Сначала обучена на синтетическом AigizK/bashkort_voice, затем дообучена на Common Voice 25 Train, с выбором чекпойнта по Dev. На полном Test: WER 9,63%, CER 2,41%. Ниже приведены архитектура, история обучения, результаты и воспроизводимый замер скорости на RTX 5090.
English: Bashkir offline speech recognition using the EmbeddingGemma 2 audio tower and a bidirectional Transformer CTC reader. Synthetic-data training followed by Common Voice 25 adaptation; full CV25 Test WER 9.63%, CER 2.41%. Complete safetensors weights, a standalone Python loader and batched inference are included.
Использование
Проверено: Python 3.12, PyTorch 2.8.0+cu128, Transformers 5.19.0, RTX 5090. Установите подходящую CUDA-сборку PyTorch; для CPU подходит обычная сборка.
pip install -r requirements.txt
import sys
from huggingface_hub import snapshot_download
directory = snapshot_download("AigizK/EmbeddingGemma2-Bashkir-CV25")
sys.path.insert(0, directory)
from bashkir_gemma import BashkirGemma
asr = BashkirGemma.from_pretrained(directory, device="cuda")
print(asr.transcribe("audio.wav"))
# Файлы автоматически группируются по длительности; порядок ответов сохраняется.
texts = asr.transcribe(["first.wav", "second.mp3"], batch_size=32)
print(texts)
# Массив mono float32 с явно указанной частотой:
# text = asr.transcribe(waveform, sample_rate=16000)
# CPU: BashkirGemma.from_pretrained(directory, device="cpu")
Скрипт из скачанного репозитория:
python transcribe.py --model . --device cuda --batch-size 32 first.wav second.mp3
Используйте приложенный загрузчик bashkir_gemma.py. Репозиторий содержит собственную CTC-архитектуру и не зарегистрирован как стандартная модель для transformers.pipeline. Все веса аудиоэнкодера и reader находятся в model.safetensors; после скачивания репозитория инференс работает без обращения к исходному чекпойнту Google. GPU использует BF16 autocast с FP32 весами; CPU — FP32. FP16 и ONNX для этого релиза не проверялись.
Входные файлы декодируются через soundfile, stereo усредняется в mono, частота пересчитывается в 16 кГц через soxr. Принимаются WAV/FLAC/MP3 и другие форматы, доступные в установленном libsndfile. Длинные записи разбиваются на фрагменты до 30 секунд по минимуму энергии около границы. Это простой способ обработки файлов: возможны ошибки на стыках; нет diarization, timestamps и потокового интерфейса. Reader двунаправленный.
Исходный код и связь с Gemma
Основа эксперимента — qnguyen3/gemma-asr, зафиксированный commit 343c86faab52fdd09c312945ad21b8f8f15e76ee. Идея эксперимента была взята из публикации @stablequan. Обучение запускалось на локальной адаптации этого кода для башкирского языка.
Предобученная модель — google/embeddinggemma-2, revision 914f7f89142e33e77833254d9c9b90c3cef7303b. Это мультимодальная embedding-модель Google DeepMind с модульным аудиоэнкодером семейства Gemma 4. В данном ASR используется ее аудиобашня и строки токеновых эмбеддингов для инициализации CTC-head. Полные текстовая и визуальная части при распознавании не нужны.
Из Gemma сделали ASR с CTC, добавив обучаемый reader, который преобразует последовательность аудиопризнаков в покадровые вероятности токенов. CTC loss обучает сопоставление аудио и текста без ручной разметки временных границ. При распознавании выбирается наиболее вероятный токен на каждом кадре, повторы схлопываются, blank удаляется. Внешняя языковая модель и beam search не используются.
mono 16 kHz
→ GPU log-mel, 128 bins
→ Gemma 4 audio encoder: 12 conformer layers, hidden 1024, ~25 frames/s
→ learned softmax mixture of 13 hidden states
→ LayerNorm + projection 1024→768 + depthwise convolution, kernel 15
→ 6 bidirectional Transformer reader blocks, width 768, 8 heads, FFN 3072
→ LayerNorm + projection 768→512
→ dot-product CTC head: 7576 units
→ greedy CTC decoding → Bashkir text
CTC-словарь: 7576 units = blank (0) + unk (1) + 7574 токена Gemma, встретившихся в синтетическом Train. Это подмножество исходного tokenizer, а не алфавит из отдельных букв. Строки head инициализированы нормализованными токеновыми эмбеддингами Gemma, затем обучались. Порядок units не менялся при дообучении на CV25.
Всего 352 435 141 параметр: аудиоэнкодер 304 824 608, reader с head 47 610 533. В обоих этапах обучались reader/head и верхние два слоя аудиоэнкодера — всего 97 971 109 параметров. Нижние десять слоев заморожены. Dropout аудиоэнкодера отключен при обучении; dropout reader — 0,1.
Основные изменения относительно исходного проекта: нормализация с сохранением башкирских букв, подготовка и проверка Bashkir-датасета, детерминированное разделение по нормализованному тексту, Bashkir CTC vocabulary, reader шириной 768, обучение двух верхних слоев энкодера, накопление градиентов, отдельный этап CV25 с новым optimizer/scheduler и неизменным словарем, полная валидация Dev, оценка всех исходных строк Test, проверка сохранности исходного чекпойнта и переносимый экспорт полных весов. Приложенный bashkir_gemma.py — отдельная реализация инференса с проверенным соответствием обученной архитектуре.
Данные
Этап 1: синтетическая башкирская речь
AigizK/bashkort_voice: синтетическая речь OmniVoice с башкирскими текстами, лицензия датасета CC BY 4.0. После подготовки:
| Split | Записей | Часов |
|---|---|---|
| Train | 903 111 | 1706,17 |
| Valid | 9 366 | 17,77 |
| Test | 9 382 | 17,86 |
Из 928 519 входных записей исключены 6660 с длительностью вне 0,5–30 секунд. Разделение примерно 98/1/1 основано на SHA256 нормализованного текста: одинаковый нормализованный текст не попадает в разные split. Это не гарантирует разделение по дикторам. Синтетический Test не является оценкой качества на живой речи.
Нормализация: Unicode NFC, нижний регистр, знаки пунктуации и дефисы заменяются пробелами, пробелы схлопываются. Башкирские ә ө ү ғ ҡ ң ҙ ҫ һ сохраняются. Для синтетических обучающих targets предусмотрено исключение строк с цифрами вместо автоматического озвучивания чисел.
Этап 2: Common Voice Scripted Speech 25.0, Bashkir
Использованы исходные train.tsv, dev.tsv, test.tsv Common Voice 25.0 Bashkir, без фильтров, ранее применявшихся в эксперименте GigaAM:
| Split | Записей | Часов | Роль |
|---|---|---|---|
| Train | 119 133 | 143,12 | Дообучение |
| Dev | 14 527 | 19,47 | Валидация и выбор чекпойнта |
| Test | 14 572 | 19,89 | Итоговая оценка |
Для обучения/валидации аудио Train/Dev преобразовано в mono 16 кГц FLAC PCM16. Итоговый Test прочитан из исходных MP3, с пересчетом частоты в 16 кГц. Все строки каждого split сохранены. CV25-аудио и транскрипты не включены в этот репозиторий.
Как обучали
Оба этапа выполнены на одной NVIDIA GeForce RTX 5090 32 GB под tmux, PyTorch 2.8.0+cu128, Transformers 5.19.0, BF16 autocast. AdamW: betas=(0,9; 0,98), eps=1e-8, weight decay=0,01, gradient clipping=1. SpecAugment: две частотные маски шириной до 27 и десять временных масок шириной до 4% длительности. Размер microbatch ограничивался суммой паддированной длительности: max_duration × batch_size ≤ 240 секунд; число записей менялось с длительностью.
| Настройка | Синтетический этап | CV25 дообучение |
|---|---|---|
| Optimizer steps | 30 000 | 12 000 |
| Reader/head learning rate | 3e-4 | 1e-4 |
| Верхние 2 слоя encoder LR | 1e-5 | 1e-5 |
| Warmup steps | 1000 | 500 |
| Scheduler | cosine, min LR = 5% | cosine, min LR = 5% |
| Microbatch budget | 240 padded audio seconds | 240 padded audio seconds |
| Gradient accumulation | 4 | 2 |
| DataLoader workers | 8 | 8 |
| Валидация каждые | 500 steps | 500 steps |
| Размер валидации | фиксированные 1500 Valid | полный Dev, 14 527 |
| Лучший checkpoint | 29 500 | 11 500 |
CV25 этап начат с лучшего синтетического чекпойнта: перенесены все обученные веса, включая верхние слои энкодера. Optimizer и scheduler созданы заново, счетчик шага сброшен. Предусмотрен early stopping с patience=8 проверок; он не сработал, выполнены все 12 000 шагов. Публикуется best step 11 500, выбранный по минимальному Dev WER, а не последний checkpoint. Исходный синтетический checkpoint сохранен отдельно; SHA256 проверен после завершения.
Test не использовался для градиентов или выбора чекпойнта. До дообучения уже была выполнена базовая оценка исходной модели на CV25 Train/Dev/Test; после дообучения Test оценен для выбранного по Dev checkpoint. Дополнительно Test используется для фиксированного performance benchmark, не для подбора весов.
Качество
WER/CER ниже приведены в процентах, на полных split, кроме явно обозначенной синтетической валидации. Нормализация refs и hyps одинакова: NFC, lowercase, пунктуация → пробелы, схлопывание пробелов; буквы, цифры и Latin сохраняются. WER считается по словам, CER — по символам нормализованного текста, с помощью jiwer 4.0.0.
| Данные | До CV25 дообучения: WER / CER | После CV25 дообучения: WER / CER |
|---|---|---|
| Synthetic Valid, фиксированные 1500 | 5,38 / 2,29 | не оценивалось |
| Synthetic Test, 9382 | 5,44 / 2,25 | не оценивалось |
| CV25 Train, 119133 | 13,09 / 3,27 | не оценивалось |
| CV25 Dev, 14527 | 16,00 / 4,32 | 9,96 / 2,56 |
| CV25 Test, 14572 | 15,56 / 4,14 | 9,63 / 2,41 |
На CV25 Test WER снизился 15,56% → 9,63%, относительное уменьшение ошибки 38,11%. Итоговый Test: 89533 reference words, 6926 substitutions, 1072 deletions, 622 insertions; 13768 character errors на 570817 reference characters.
В начале CV25-цикла baseline на подготовленном Dev составил 15,909% WER, лучший — 9,957% на шаге 11500, последний — 9,969% на шаге 12000. Отличие baseline от 16,003% в исходной оценке связано с отдельным путем подготовки аудио и batching/численной точностью; это два разных запуска, приведены оба значения. Результаты после CV25 на синтетическом Test повторно не измерялись: сохранение качества на этом домене не утверждается.
Кривые имеют разные validation sets и масштабы шагов; значения между панелями не следует напрямую сравнивать. Все точки находятся в validation_history.json.
Скорость и batch inference
Повторный замер выполнен на публикуемых финальных весах на RTX 5090. Выбраны 256 равномерно распределенных по индексам записей CV25 Test, затем отсортированы по длительности. На каждый batch size: warmup, три повтора, CUDA synchronization. Время включает перенос waveform CPU→GPU, GPU log-mel, forward, greedy CTC и декодирование токенов. Не включает загрузку модели, чтение/ресемплинг файлов, CPU padding и warmup. × real time = длительность аудио / время обработки; это throughput, а не обещание задержки потокового распознавания.
| Batch | × real time | Clips/s | Batch p50 / p95, ms | Peak GPU allocated, GB |
|---|---|---|---|---|
| 1 | 122× | 24.5 | 39.9 / 44.3 | 1.48 |
| 8 | 912× | 183.6 | 42.6 / 46.5 | 1.90 |
| 32 | 2846× | 572.8 | 49.4 / 93.2 | 3.35 |
| 64 | 2789× | 561.3 | 95.3 / 198.8 | 5.28 |
| 128 | 2108× | 424.2 | 300.5 / 437.2 | 9.13 |
Для коротких файлов этого benchmark batch 32 дает лучший либо близкий к лучшему throughput; batch 128 не обязательно быстрее из-за padding. API поддерживает batch_size и batch_seconds (по умолчанию 32 и 600 padded audio seconds). Для своего распределения длительностей подберите размер на реальных данных. Память в таблице — torch.cuda.max_memory_allocated, а не полный объем процесса по nvidia-smi.
При повторной проверке полного Test через подготовку с CPU-prefetch: 14572 файлов, 19.89 часа аудио за 26.58 секунды (2693× real time), из них 22.12 секунды суммарного inference (3236×). Batch cap 128, padded audio cap 600 s, 12 CPU decode workers, модель уже загружена; включает чтение, ресемплинг, padding, scoring и запись результатов. Это отдельный оптимизированный evaluation pipeline с предварительной загрузкой трех batches, а не замер Python API transcribe целиком. При первом итоговом Test до экспорта тот же checkpoint обработал 19,89 ч за 38,22 с; времена меняются между запусками.
CPU поддерживается для функционального инференса, его скорость не измерялась. Зависимость от дискового кеша, длительности файлов, CPU decoding и группировки может существенно менять end-to-end время.
Файлы и проверка экспорта
model.safetensors: полные FP32 веса аудиоэнкодера и CTC-reader, без optimizer и pickle.bashkir_gemma.py,transcribe.py: переносимый Python API и CLI.asr_config.json,audio_config.json,units.json,frontend/,tokenizer/: архитектура, признаки и словарь.provenance.json: зафиксированные revisions, размеры и SHA256 чекпойнтов/экспорта.validation_history.json,test_metrics.json,benchmark_rtx5090.json: машиночитаемые результаты.export_verification.json: проверка точного совпадения весов и logits, batched API и метрик полного Test с обученным checkpoint.TRAINING_RECIPE.md: настройки запуска и последовательность воспроизведения адаптации.
Экспорт включает и замороженные веса, и дообученные верхние слои. Одинаковый итоговый Test WER/CER проверен на всех 14572 записях. Выполнен отдельный smoke test CPU и проверка загрузки опубликованного snapshot.
Ограничения и атрибуция
Данные CV25 — преимущественно читаемая речь. Качество на разговорной речи, шуме, дальнем микрофоне, диалектах, код-свитчинге и длинных записях отдельно не измерялось. Выход в основном lowercase без восстановления пунктуации. Возможны замены и пропуски; значения WER не гарантируют точность каждой записи.
Базовые веса Google EmbeddingGemma 2 опубликованы под Apache 2.0; этот релиз весов и новый переносимый inference-код — Apache 2.0. Благодарности Google DeepMind за базовую модель, qnguyen3 за исходный ASR-подход и код, авторам/участникам Common Voice и датасета Bashkort Voice. Исходный upstream training repository остается по указанной ссылке и под его собственными условиями; его полная копия здесь не распространяется. Датасеты имеют собственные условия использования.
Model tree for AigizK/EmbeddingGemma2-Bashkir-CV25
Base model
google/embeddinggemma-2Dataset used to train AigizK/EmbeddingGemma2-Bashkir-CV25
Evaluation results
- WER on Common Voice Scripted Speech 25.0 Bashkir Testtest set self-reported9.628
- CER on Common Voice Scripted Speech 25.0 Bashkir Testtest set self-reported2.412
