dimhold.by
← Артыкулы

Мой жывы транскрайбер на Whisper памыляўся ў 2,6 раза часцей, чым Whisper на цэлым файле

З верасня я праганяю запісы званкоў праз Whisper, калі званок ужо скончыўся. Гэта працуе, але тэкст прыходзіць, калі ён мне ўжо не патрэбны. Я хацеў атрымліваць яго, пакуль людзі яшчэ гавораць, таму напісаў самую простую жывую версію, якую змог прыдумаць: прачытаць 5 секунд гуку, аддаць іх у transcribe(), надрукаваць тэкст, паўтарыць.

Гэта каля 40 радкоў на Python. ffmpeg піша сыры PCM 16 кГц у stdout, скрыпт яго чытае і раз на некалькі секунд з’яўляецца радок тэксту. Нейкі час гэта мяне задавальняла. Потым я прагнаў тую ж нарэзку па раздзеле аўдыякнігі, прачытаў цэлую старонку вываду і ўбачыў вось гэта:

... a golden fortune, and a-
a happy life. He was like unto my father in a way, and yet was not my f-.
father. Also, there was a stripling page who turned into a maid.

Кожны радок гэта адзін кавалак па 5 секунд. У канцы кавалка Whisper бачыць палову слова і піша f-.. У наступным кавалку ён бачыць другую палову і піша father яшчэ раз. Канец іншага кавалка выйшаў як thank waitags heißa, а гэта не мова.

Я меркаваў, што нарэзка будзе часам губляць слова і ні разу гэтага не мераў. Цяпер памераў.

Стэнд

Мне патрэбна было маўленне з загадзя вядомым правільным тэкстам, таму свае званкі я не браў. У LibriSpeech test-clean ляжаць кнігі, прачытаныя ўголас, з дакладнымі расшыфроўкамі. Я ўзяў 4 чытальнікі, 2 жанчыны і 2 мужчыны. Для кожнага склеіў першы раздзел у адзін паток, а дзе раздзел даўжэйшы, абрэзаў яго на 5 хвілінах. Разам гэта 19 хвілін і каля 3200 слоў.

Whisper узяты з рэпазіторыя па стане на 9 снежня, мадэль base, на працэсары ў fp32, мова зададзена англійская. На машыне 4 віртуальныя ядры AMD EPYC 7713 і torch 1.12.1. Долю няправільных слоў я лічу пасля англійскага нармалізатара тэксту з самога Whisper, так што коскі і “Mr” супраць “mister” памылкай не лічацца.

Пункт адліку гэта цэлы 5-хвілінны файл, аддадзены ў transcribe() за адзін раз. Так я карыстаўся Whisper з верасня і гэта дало 5,79% няправільных слоў.

Што каштуе нарэзка

Версія з кавалкамі па 5 секунд дала 14,89%, гэта значыць у 2,6 раза больш памылак на тым жа гуку. Я падзяліў памылкі паводле месца ў кавалку. Суседнімі са стыкам я лічыў першыя 2 і апошнія 2 словы кожнага кавалка. У гэтую зону трапляе каля 30% слоў і 252 памылкі. На астатнія 70% прыпадае 224, а ў цэлага файла на ўсе яго словы было 185 памылак. Значыць, сярэдзіну кавалка Whisper таксама чытае горш, проста не так дрэнна, як краі. Лішніх слоў стала 148 замест 18 і прыкладна палова з іх стаіць якраз каля стыку.

Спачатку я западозрыў выпадковасць. Можа, гэтая сетка разрэзаў проста трапіла на шмат слоў, таму я зрушыў яе на 2,5 секунды і прагнаў яшчэ раз. Выйшла 14,11%, гэта значыць размяшчэнне сеткі мяняе вынік менш чым на пункт.

файл цалкам a golden fortune and a happy life. He was like unto my father, in a way, and yet was not my father. кавалкі па 5 с a golden fortune, and a- a happy life. He was like unto my father in a way, and yet was not my f-. father. Also ... рэз у цішыні ад 4 да 6 с a golden fortune and a ha- be life, he was like unto my father in a way, and yet was not my father. радок = адзін кавалак
Whisper base, адзін і той жа чытальнік, адна і тая ж фраза. Колерам адзначаны словы, зламаныя рэзам.

Потым я паспрабаваў 3 выпраўленні, якія здаваліся відавочнымі.

  1. Браць даўжэйшыя кавалкі. 10 секунд далі 11,76%, 30 секунд далі 8,20%. Лепш, але на 30 секундах тэкст прыходзіць са спазненнем на паўхвіліны.
  2. Аддаваць Whisper тэкст папярэдняга кавалка як initial_prompt. Гэта дало 14,02%, практычна тое ж самае. Я чакаў ад гэтага крыху большага і не ведаю, чаму дапамагло так мала.
  3. Рэзаць у ціхім месцы. Скрыпт шукае паміж 4 і 6 секундамі самыя ціхія 100 мілісекунд. Рэжа ён пасярэдзіне іх. Гэта дало 10,01%, самы вялікі выйгрыш пры той жа затрымцы.

Рэз у цішыні заадно паказаў, дзе я памыляўся. У той жа фразе ён выдаў a ha- і be life, бо самыя ціхія 100 мілісекунд у гэтым акне апынуліся ўнутры слова “happy”. Падазраю кароткую паўзу перад “p”, але на форме хвалі я гэта не правяраў.

Чаму нават 30 секунд горш за цэлы файл

Гэта мяне здзівіла. Whisper і так глядзіць на гук вокнамі па 30 секунд, таму я чакаў, што кавалкі па 30 секунд дадуць тое ж, што цэлы файл. Яны далі 8,20% супраць 5,79%.

Частка адказу ляжыць у transcribe(). Калі ён чытае доўгі файл, наступнае акно ён ставіць на апошнюю часавую метку, дзе скончылася фраза. Слова, разрэзанае краем акна, проста чытаецца яшчэ раз разам з тым, што ідзе пасля яго. Мой скрыпт рэжа гук да таго, як яго ўбачыць Whisper, таму вярнуцца назад Whisper не можа. Усяго гэта не тлумачыць. З 77 памылак звыш цэлага файла 33 прыпадаюць на сярэдзіну кавалкаў. Магчыма, справа ў страчаным тэксце папярэдняга кавалка, які transcribe() унутры файла трымае як кантэкст, але гэтага я асобна не вылучаў.

Той жа код тлумачыць смецце на канцах кавалкаў. Калі кавалак канчаецца пасярод фразы, transcribe() дэкадуе рэшту хваста яшчэ раз асобным акном. Калі вынік дэкодэра выглядае дрэнна, Whisper паўтарае спробу пры больш высокай тэмпературы. У 20 з 235 кавалкаў па 5 секунд нейкая пазнейшая частка дэкадавалася пры тэмпературы 0,6 і вышэй. У кавалку з heißa тэмпература дайшла да 1,0.

Колькі гэта каштуе ў вылічэннях

Перад энкодэрам transcribe() дапаўняе цішынёй спектраграму кожнага кавалка да 30 секунд. На гэтай машыне энкодэр base траціць каля 1,2 секунды на дапоўнены кавалак у 5 секунд. На сапраўдныя 30 секунд сыходзіць столькі ж. Цэлы файл каштуе 0,17 секунды працы на секунду гуку, кавалкі па 5 секунд каштуюць 0,67, гэта ў 4 разы больш.

як падаецца гук няправільных слоў нагрузка файл цалкам 5,79% 0,17 кавалкі па 30 с 8,20% 0,24 рэз у цішыні 10,01% 0,61 кавалкі па 10 с 11,76% 0,64 5 с + падказка 14,02% 0,56 5 с са зрухам 14,11% 0,68 кавалкі па 5 с 14,89% 0,67 base small
Кожная канфігурацыя прагнана адзін раз на 4 ядрах працэсара. Нагрузка гэта секунды працы на секунду гуку.

Асноўныя варыянты я прагнаў і на small, наступнай па памеры мадэлі. На цэлым файле яна дала 3,60%, на кавалках па 5 секунд 13,80%, з рэзам у цішыні 7,57%. На гэтай машыне кавалак у 5 секунд займае ў small каля 12 секунд, так што ўжывую яна адставала б ад маўлення.

Што я ўзяў у жывую версію

Я пакінуў рэз у цішыні паміж 4 і 6 секундамі і мадэль base. Адзін раздзел я прайграў праз яе ў рэальным часе. На 51 кавалку з 52 Whisper траціў ад 1,6 да 4,4 секунды. Адзін раз сышло 11,4 секунды і гэты радок скончыўся на “Who couldn’t believe for safety”, чаго чытальнік не казаў. Калі дадаць кавалак даўжынёй да 6 секунд, слова звычайна трапляла на экран прыкладна праз 10 секунд. Каля таго завісання слова даходзіла да экрана прыкладна за 17 секунд.

У гэтага чытальніка жывы тэкст даў 3,83% няправільных слоў супраць 2,63% у цэлага файла. Па ўсіх 4 чытальніках рэз у цішыні дае ў 1,7 раза больш памылак, чым цэлы файл, таму захоўваю я па-ранейшаму файл, расшыфраваны пасля званка. Жывы тэкст патрэбны мне толькі для таго, каб сачыць за размовай.

Правільнае выпраўленне гэта, хутчэй за ўсё, вокны, якія перакрываюцца. Пакідаць частку папярэдняга гуку і расшыфроўваць яе яшчэ раз, як гэта робіць transcribe() унутры файла. У whisper.cpp нешта падобнае ўжо ёсць, у прыкладзе stream. Кожныя 3 секунды ён наноў расшыфроўвае акно даўжынёй да 10 секунд. Пачынаючы новы радок, ён пакідае 200 мілісекунд старога гуку. whisper_mic рэжа гук з мікрафона на паўзах ад 0,8 секунды. Ні таго, ні другога я не мераў.

Чаго я не правяраў

  • Сапраўднай размовы. Гэта кнігі, якія чытае адзін чалавек у ціхім пакоі. Двое, якія перабіваюць адзін аднаго, зменяць усе лічбы, хутчэй за ўсё не ў адной прапорцыі.
  • Сапраўднага мікрафона. Праз ffmpeg у рэальным часе прайшоў толькі жывы прагон. З мікрафона я нічога не запісваў. Усе астатнія лічбы атрыманы нарэзкай файлаў напрамую.
  • Больш за адзін прагон. З-за паўторных спроб пры павышанай тэмпературы некаторыя кавалкі пры паўторы могуць дэкадавацца інакш, так што дробныя адрозненні накшталт 14,11 супраць 14,02 нічога не значаць.
  • Відэакарты. Я яе не спрабаваў. Там дапаўненне да 30 секунд павінна каштаваць значна менш часу.