whisper.cpp
是 OpenAI's Whisper 的 inference
支援多個語言的 speech recognition (ASR)
我玩了英文跟中文,效果很不錯
英文我用這個 talk,這是聊程式領域相關的話題
結果在這邊
選用的模型是 medium.en
(下面會解釋)
中文我是用好味小姐的 podcast
結果在這邊
選用的模型是 large
(下面會解釋)
下面簡單教學怎麼使用
- clone repo
https://github.com/ggerganov/whisper.cpp
- clone 完之後,要下載它的 model,模型有分小、中、大,跑出來的準確度也就有差
- 中文我建議用
large
(2.9 GB),英文的話,我用medium.en
(1.5 GB) 就蠻準了,英文我沒試過用large
- (Updated: 我後來測試
large
模型來辨識英文了,有比medium.en
還要準,但就比較花時間)
- (Updated: 我後來測試
- 先用 cli 切到
whisper.cpp
的 folder,然後指令如下 medium.en
指令:bash ./models/download-ggml-model.sh medium.en
large
指令:bash ./models/download-ggml-model.sh large
- 20231121 update:
large
有出v2
跟v3
的 model 了,不過我看許多人反應v3
的效果很差large-v2
指令:bash ./models/download-ggml-model.sh large-v2
- 如果指令認不得
large-v2
的話,你需要git pull
更新你的 code
- 如果指令認不得
- 中文我建議用
- model 下載後,cli 輸入
make
來編譯
準備檔案
- 目前只支援
16-bit WAV
檔案,官方有提供ffmpeg
指令來轉檔案。所以mp4
,mp3
都要先這樣轉過 ffmpeg -i input.mp3 -ar 16000 -ac 1 -c:a pcm_s16le output.wav
接著就可以跑了,基本的指令為
./main -m models/ggml-medium.en.bin -f samples/jfk.wav
但上面指令沒有指定出類型,結果只會 print 在 cli 上面
所以我會加上這些 flag -osrt -otxt -ovtt
來輸出 srt, vtt, txt
檔案
輸出會跟檔案同一個目錄
針對英文語音(medium.en
模型),產生 srt, vtt, txt
檔案
./main -m models/ggml-medium.en.bin -f ~/Downloads/white.wav -osrt -otxt -ovtt
針對中文語音(large
模型),產生 srt, vtt, txt
檔案
./main -m models/ggml-large.bin -l zh -f ~/Downloads/white.wav -osrt -otxt -ovtt
(20231121 update)
large-v2
產生 srt, vtt, txt
檔案
./main -m models/ggml-large-v2.bin -f ~/Downloads/white.wav -osrt -otxt -ovtt
./main -m models/ggml-large-v2.bin -l zh -f ~/Downloads/white.wav -osrt -otxt -ovtt
時間方面:
- 1 小時的英文 talk、
medium.en
模型,大概花 12 分鐘 - 1 小時的中文 talk、
medium
模型,大概花 17 分鐘 - 1 小時的中文 talk、
large
模型,大概花 30 分鐘- 執行的電腦是
MacBook Pro M1
- 執行的電腦是
有個 option 是加速 talk,但準確率會下降,我沒試過效果如何
支援的語言列表、用 -l
flag 來指定
static const std::map<std::string, std::pair<int, std::string>> g_lang = {
{ "en", { 0, "english", } },
{ "zh", { 1, "chinese", } },
{ "de", { 2, "german", } },
{ "es", { 3, "spanish", } },
{ "ru", { 4, "russian", } },
{ "ko", { 5, "korean", } },
{ "fr", { 6, "french", } },
{ "ja", { 7, "japanese", } },
{ "pt", { 8, "portuguese", } },
{ "tr", { 9, "turkish", } },
{ "pl", { 10, "polish", } },
{ "ca", { 11, "catalan", } },
{ "nl", { 12, "dutch", } },
{ "ar", { 13, "arabic", } },
{ "sv", { 14, "swedish", } },
{ "it", { 15, "italian", } },
{ "id", { 16, "indonesian", } },
{ "hi", { 17, "hindi", } },
{ "fi", { 18, "finnish", } },
{ "vi", { 19, "vietnamese", } },
{ "iw", { 20, "hebrew", } },
{ "uk", { 21, "ukrainian", } },
{ "el", { 22, "greek", } },
{ "ms", { 23, "malay", } },
{ "cs", { 24, "czech", } },
{ "ro", { 25, "romanian", } },
{ "da", { 26, "danish", } },
{ "hu", { 27, "hungarian", } },
{ "ta", { 28, "tamil", } },
{ "no", { 29, "norwegian", } },
{ "th", { 30, "thai", } },
{ "ur", { 31, "urdu", } },
{ "hr", { 32, "croatian", } },
{ "bg", { 33, "bulgarian", } },
{ "lt", { 34, "lithuanian", } },
{ "la", { 35, "latin", } },
{ "mi", { 36, "maori", } },
{ "ml", { 37, "malayalam", } },
{ "cy", { 38, "welsh", } },
{ "sk", { 39, "slovak", } },
{ "te", { 40, "telugu", } },
{ "fa", { 41, "persian", } },
{ "lv", { 42, "latvian", } },
{ "bn", { 43, "bengali", } },
{ "sr", { 44, "serbian", } },
{ "az", { 45, "azerbaijani", } },
{ "sl", { 46, "slovenian", } },
{ "kn", { 47, "kannada", } },
{ "et", { 48, "estonian", } },
{ "mk", { 49, "macedonian", } },
{ "br", { 50, "breton", } },
{ "eu", { 51, "basque", } },
{ "is", { 52, "icelandic", } },
{ "hy", { 53, "armenian", } },
{ "ne", { 54, "nepali", } },
{ "mn", { 55, "mongolian", } },
{ "bs", { 56, "bosnian", } },
{ "kk", { 57, "kazakh", } },
{ "sq", { 58, "albanian", } },
{ "sw", { 59, "swahili", } },
{ "gl", { 60, "galician", } },
{ "mr", { 61, "marathi", } },
{ "pa", { 62, "punjabi", } },
{ "si", { 63, "sinhala", } },
{ "km", { 64, "khmer", } },
{ "sn", { 65, "shona", } },
{ "yo", { 66, "yoruba", } },
{ "so", { 67, "somali", } },
{ "af", { 68, "afrikaans", } },
{ "oc", { 69, "occitan", } },
{ "ka", { 70, "georgian", } },
{ "be", { 71, "belarusian", } },
{ "tg", { 72, "tajik", } },
{ "sd", { 73, "sindhi", } },
{ "gu", { 74, "gujarati", } },
{ "am", { 75, "amharic", } },
{ "yi", { 76, "yiddish", } },
{ "lo", { 77, "lao", } },
{ "uz", { 78, "uzbek", } },
{ "fo", { 79, "faroese", } },
{ "ht", { 80, "haitian creole", } },
{ "ps", { 81, "pashto", } },
{ "tk", { 82, "turkmen", } },
{ "nn", { 83, "nynorsk", } },
{ "mt", { 84, "maltese", } },
{ "sa", { 85, "sanskrit", } },
{ "lb", { 86, "luxembourgish", } },
{ "my", { 87, "myanmar", } },
{ "bo", { 88, "tibetan", } },
{ "tl", { 89, "tagalog", } },
{ "mg", { 90, "malagasy", } },
{ "as", { 91, "assamese", } },
{ "tt", { 92, "tatar", } },
{ "haw", { 93, "hawaiian", } },
{ "ln", { 94, "lingala", } },
{ "ha", { 95, "hausa", } },
{ "ba", { 96, "bashkir", } },
{ "jw", { 97, "javanese", } },
{ "su", { 98, "sundanese", } },
};
有時候一些比較長 (> 20 mins) 的聲音使用時,會發生辨識失敗,然後一直輸出同樣的句子
像這樣
謝謝你打電話來
謝謝
謝謝
謝謝
akaaka
edt
edt
edt
edt
edt
k
edt
edt
edt
edt
...
github 上有蠻多討論的,看來還沒有完美解答
目前看到有兩個建議方案
- 用
--condition_on_previous_text False
,但它也可能導致輸出品質比較差 - 有人說用
--temperature_increment_on_fallback
比 1 好
我嘗試給這些 flag,但 whisper.cpp
不認識,我還沒找出問題
下次再研究看看