Ластівка — офлайн голосовий репетитор у вашій кишені
Gemma 4 E2B, що розмовляє українською та англійською — повністю офлайн, приватно, без підписок.
Проблема (особиста)
Коли почалася війна, моя сім'я виїхала з України до США — дружина, теща, 15-річна донька, п'ятирічний син і я. За одну ніч повсякденне життя перетворилося на мовний іспит, до якого ніхто не готувався: бланк у клініці, записка від учителя, слово на дорожньому знаку. Найважчим питанням майже ніколи не було «що це означає» — з цим справляються перекладачі. Питанням було «а як це насправді сказати?» І воно завжди виникало в найгірший момент: без інтернету, в черзі, на телефоні без тарифного плану, з дитиною, якій треба зараз.
Наявні інструменти ламаються саме тут. Їм потрібне з'єднання, хороші голоси заховані за підписками, а питання вашої родини відлітають на чужий сервер. Телефон біженця — часто єдиний комп'ютер у родині, тож асистент має жити саме на ньому.
Ідея
Ластівка — це голосовий асистент, який працює повністю офлайн на телефоні. Ви питаєте звичайною мовою — текстом чи голосом — «Скажи повільно: Привіт, як справи?» — і вона відповідає вголос. Без акаунта, без мережі, без даних, що залишають пристрій.
Технічна ставка, яка робить це можливим: замість того, щоб причепити до LLM окремий TTS-рушій, ми вчимо одну модель Gemma 4 генерувати мовлення самостійно. Gemma 4 E2B нативно мультимодальна і приймає аудіо на вхід, тож та сама модель згодом зможе слухати вимову користувача і виправляти її — але ядро, збудоване й доведене тут, — це розуміння мови та генерація мовлення всередині одного набору ваг. Єдиний додатковий компонент — крихітний стріміговий аудіодекодер.
Архітектура: як Gemma 4 стає мовцем
Токенізатор Gemma 4 резервує великий блок невикористаних слотів словника (<unusedN>). Ми відображаємо аудіокодбук LSCodec на 25 Гц (1024 записи) безпосередньо на 1024 таких слоти. Мовлення стає просто ще одними токенами. Gemma 4 генерує їх авторегресивно в одному потоці з текстом, а легкий декодер LSCodec (кілька МБ, зі стрімінгом — може почати звучати ще до завершення генерації) перетворює ці токени назад на хвилю.
Отже, рантайм — одна модель + один маленький декодер. Без хмарного TTS, без другої LLM.
Мовлення винесено в окремий канал, щоб текстове мислення й аудіо ніколи не змішувалися:
User: Скажи голосом швидко: Привіт я Ластівка
Model: <|channel>voice:uk:fast
<unused65><unused556><unused225>…
<channel|>
Канал кодує мову та швидкість (voice:uk:fast, voice:en:slow, …). Для учня повільно — це не дрібниця, це і є фіча. Інструкції в навчанні семпляться українською, англійською та російською — щоб модель відповідала на те, як реально розмовляє двомовна родина.
Навчити Gemma 4 видавати аудіо, не забувши, як бути Gemma, — ось справжня інженерія. Рецепт:
- LoRA (r=32) на attention + MLP, плюс
embed_tokensяк тренований модуль, щоб нові аудіорядки взагалі могли вчитися. - Градієнтна маска, яка дозволяє оновлюватися лише 1024 аудіорядкам ембедингів — текстові/структурні ембединги заморожені. Це усунуло ранній збій, коли модель втрачала перемикач текст↔аудіо і видавала змішане сміття.
- Ручне перев'язування
lm_headдо тренованої копії ембедингів. Gemma 4 зв'язує вхідні та вихідні ембединги; LoRA-обгортка тихо розривала цей зв'язок, тож аудіотокени ніколи не вчилися генеруватися. Це перев'язування — найрезультативніший фікс усього проєкту. - Репетиція здібностей: ~30% навчання — звичайні питання-відповіді, щоб модель залишалася корисним асистентом, а не папугою. Ластівка досі думає — просто тепер ще й розмовляє.
Датасет
Усе зібрано з відкрито ліцензованих джерел, повністю відтворювано:
- Реальне мовлення — українська:
Yehor/opentts-uk(студія, 5 іменованих спікерів, Apache-2.0); англійська:parler-tts/libritts_r_filtered(LibriTTS-R, ~1.1 тис. спікерів, CC-BY-4.0). Оброблено VAD, нормалізовано за LUFS, закодовано в LSCodec. - Синтетичне мовлення — промпти написані LLM, озвучені мультиспікерною моделлю (10+ голосів на мову — одноголосий синтез запікає один тембр у коди; я дізнався це на власному досвіді). Приблизно чверть українського синтетичного набору навмисно міксує англійські IT-запозичення (Slack, vscode, deploy) — саме цим регістром живе працююча родина біженців, і студійний корпус його ніколи не покриває.
- Репетиція здібностей — самодистильовані Q&A від Gemma проти забування.
Фінальний маніфест v3 — ~535 тис. прикладів із довгими кліпами (до ~30 с → 750+ кодів), контекст 1024 токени. Верифікований фільтр колапсу (відкидаємо кліпи >15 с з ентропією кодів H<6.5; видалено 7 931 рядок, 0 порушень при повторному скані) не пускає в навчання рідкісні тиша/сміття-виходи енкодера — доведено на даних, а не задекларовано.
Виклики, які довелося подолати
Катастрофічне забування та зв'язка ембедингів. Описано вище — перев'язування lm_head разом з аудіо-маскою градієнтів перетворили нерозбірливу модель на таку, чиї слова чітко впізнавані обома мовами (~98% згенерованих токенів — валідне аудіо наприкінці навчання, стабільно між запусками).
Вартість. Я збудував автономний оркестратор bid-хостів для vast.ai: орендувати найдешевший переривчастий GPU, полагодити проблему застарілого SSH-ключа, розгорнутися, відновитися з останнього чекпоінта на Hugging Face, стежити за живістю та мігрувати при евікшені — без нагляду. Повний прогін пережив ~30 міграцій хостів за ~$0.10–0.62/год з якістю, ідентичною безперервному запуску. (Цей оркестратор заслуговує на окрему статтю — вона наступна в серії.)
Квантизація для пристрою — найважча частина і справжній прорив. Щоб запхати дотреновану модель у телефон з 8 ГБ (клас iPhone 15 Pro), не зламавши ані текст, ані мовлення, знадобилося чотири рецепти:
| Рецепт | Розмір | Результат |
|---|---|---|
| uniform INT4 (channelwise) | 2.8 GB | швидко, але мультимовний «салат» у тексті + провали аудіо (INT4 знищив embed/lm_head) ❌ |
| uniform INT8 | 5.1 GB | якість bf16, але не влазить у RAM → ~1 токен/хв, мертво ❌ |
| v6-mixed (embed/lm_head INT8 + decoder INT4 channelwise) | 4.05 GB | працює: чистий текст, чистий TTS — але великий, залишкові артефакти ✅ |
| v7-all-blockwise-32 (embed і FC повністю INT4, blockwise-32) | 3.04 GB | 🏆 ідеально чистий текст (нуль випадкових <unused>, без салату), чистий TTS, −1.1 GB, комфортно влазить у 8 ГБ iPhone |
Ключовий інсайт: «INT4 ламає embeddings/lm_head» — правда лише для channelwise-квантизації. Blockwise-32 — незалежний масштаб на кожні 32 ваги — повертає цим шарам потрібну точність, тож уся модель може лишатися INT4 і бути водночас малою та якісною. Саме цей результат робить одномодельний офлайн-асистент реально відвантажуваним на телефон. Експорт іде в LiteRT-LM з патчингом ваг (перевірено end-to-end на пристрої), плюс хвіст quantization-aware навчання — модель навчена переживати власну деплойну квантизацію, а не просто сподівається на це.
Що вже публічно
- Повний файнтюн — злиті ваги Gemma 4 E2B + LoRA-адаптер на Hugging Face.
- Датасет і відтворюваний пайплайн — маніфест + кожен крок підготовки даних.
- Квантизована модель для пристрою — один файл
.litertlm, який уже розмовляє. - Консольне демо — вкажіть на
.litertlm, наберітьскажи голосом: Привіт— і воно генерує мовлення. Без GPU, без мережі. Це той самий артефакт, який працюватиме всередині мобільного застосунку.
Чесна заява: шлях до деплою пройдено. Застосунок — це пакування, а не дослідницький ризик.
Поточні межі — і що далі
Повна прозорість щодо стану проєкту:
- Метод доведено, полірування — ще ні. Слова чітко впізнавані обома мовами, але вимова поки не студійного рівня. Два відомі важелі — файнтюн аудіоенкодера та масштабування мовленнєвих даних: кожне збільшення даних досі піднімало якість, криві лосу продовжували сходитися, тож це питання інженерного бюджету, а не дослідження.
- Навчання на паузі через бюджет на обчислення. Весь проєкт їхав на дешевих переривчастих GPU; наступний стрибок якості потребує довшого прогону, ніж дозволяє поточний бюджет. Якщо ви керуєте грантами на дослідження відкритих моделей чи GPU-програмами (TPU Research Cloud тощо) — цей проєкт повністю на відкритих даних і відтворюваний, саме для цього такі програми існують.
- Далі: мобільний застосунок (iOS + Android) навколо цієї самої моделі; використання аудіовходу Gemma 4, щоб Ластівка чула, як учень вимовляє слово, і виправляла його; нові мовні пари, навчені тим самим методом — одна модель на пару, все офлайн.
Чому ці рішення були правильними
Окремий пайплайн ASR→LLM→TTS — це три моделі, три точки відмови та сотні мегабайтів TTS — неможливо тримати офлайн на єдиному пристрої родини біженців. Згортання мовлення у власний токен-потік Gemma 4 стискає це до однієї моделі + декодера на кілька МБ, тримає кожне питання на пристрої за конструкцією і безкоштовно успадковує мислення Gemma 4. Українська↔англійська — перша пара, бо це пара моєї родини; метод мовно-агностичний — наступна пара це просто дані.
Я збудував це, бо це було потрібно моїй родині. Я ділюся цим, щоб наступній родині, яка опиниться в новій країні з одним телефоном і без сигналу, не довелося будувати це самій.
Посилання
- Код: github.com/maxbsoft/lastivka
- Стріміговий декодер LSCodec: huggingface.co/maxbsoft/lscodec-decoder-bundle
- Модель для пристрою
gemma-4-E2B-voice-v7allb32.litertlm(3.04 GB, all-INT4 blockwise-32) + консольне демо: Hugging Facemaxbsoft/lastivka-gemma4-voice
