Learning Objectives
- 接收語音訊息事件,用 MessagingApiBlob 下載音檔
- 用本地的 faster-whisper 把音檔轉成文字,說明 initial_prompt 的作用與限制
- 把辨識結果交給使用者確認,處理聽錯與沒有人說話的情況
- 說明音訊經過哪些地方,並在轉錄後刪除音檔
語音訊息要多走一段路
使用者傳文字時,內容直接放在 webhook 裡。傳語音時,webhook 只告訴你「有一段語音,編號是多少、多長」,音檔要另外下載,轉成文字後才能接上前面學的流程。
語音轉文字也稱為語音辨識(Automatic Speech Recognition,ASR;Speech To Text,STT)。作法大致是把聲音轉成時頻圖這類特徵,再由模型輸出文字。早期多用隱藏式馬可夫模型,現在多用深度學習。這一課用的 Whisper 是 Transformer 序列到序列模型,以大量網路影音的逐字稿訓練(論文);faster-whisper 是它的另一種實作,可以在自己的機器上執行。
這一課的教材重點不是「它很準」,而是它會聽錯,甚至沒有人說話也會生出字來,所以程式要處理這些情況。
逐步看:收到語音,下載音檔
模擬使用者傳一段語音。這段語音是合成的,不是真人。注意最後印出的 API:除了 reply,多了一個下載內容的 GET。
音訊經過哪些地方
語音比文字更私人,裡面有聲音本身,也可能錄到旁邊的人。開發前先弄清楚它經過哪裡:
| 階段 | 音訊在哪裡 | 說明 |
|---|---|---|
| 使用者按下傳送 | LINE 的伺服器 | 官方文件寫明內容一段時間後會自動刪除,但不保證保存多久,所以要盡快下載 |
| 你的程式下載 | 你的伺服器 | 成為一個暫存檔,由你負責 |
| 轉錄 | 你的機器 | 模型在本機執行,音訊不經過外部語音服務 |
| 轉完 | 只剩文字 | 刪掉音檔,文字也只存需要的 |
講話的人要知道這些:在加好友的歡迎訊息或隱私說明裡寫清楚語音會轉成文字、存多久、怎麼刪除。
使用者傳很大的檔案時,內容可能還在準備,這時 API 回 202、拿不到資料,官方另有查詢準備狀態的 API。這個模擬平台沒有這種情況。
逐步看:轉成文字,加上提示
兩段語音各轉錄兩次:第一輪沒有提示,第二輪加上 initial_prompt。a1 是一句問上課的話,a5 沒有人說話,只有雜音。
瀏覽器跑不動 Whisper,所以這裡的 WhisperModel 是模擬模組,重播事先在本機用真的 faster-whisper(large-v3 模型)跑出來的結果。音檔或提示和事先準備的組合對不上時,會丟 TranscriptNotFound,不會編造文字。
實際的轉錄結果
五段語音(都是合成的)的真實結果:
| 音檔 | 實際內容 | 沒有提示 | initial_prompt='以下是繁體中文的句子。' |
|---|---|---|---|
| a1 問上課 | 請問明天下午三點的資料庫導論還有上課嗎? | 请问明天下午三点的资料库导论还有上课吗? | 請問明天下午三點的資料庫討論,還有上課嗎? |
| a2 改時間 | 幫我把明天早上九點的讀書會改到十點。 | 帮我把明天早上9点的读书会改到10点 | 帮我把明天早上9点的读书会改到10点。 |
| a3 人名與英文 | 我想預約林怡君老師星期四的 office hour。 | 我想预约林怡君老师星期四的office hour | 我想預約林怡君老師星期四的office hour。 |
| a4 有雜音 | 同 a1,加上很大的白雜音 | 請問,明天下午3點16分左右,還有上班嗎? | 請問,明天下午3點16部訪問還有上課嗎? |
| a5 只有雜音 | 沒有人說話 | 感谢观看 | 這個字幕由 Amara.org 社群提供 |
從這張表可以看到:
- 預設可能輸出簡體字。 語言設成
zh不會區分繁簡。 - 提示有幫助,但不保證。 a1、a3 變成繁體,a2 仍是簡體;a1 還因此把「導論」聽成「討論」。官方說明寫的是提示提供給第一個視窗,它只是給模型參考,不是規則。要穩定輸出繁體,可以轉錄後再接簡轉繁工具(例如 OpenCC),這個課程沒有實際跑過,請自己測試,它也修不了聽錯的字。
- 雜音讓結果變得離譜。 a4 把「上課」聽成「上班」,還多出「16分左右」。
- 沒有人說話也會生出字來。 a5 的結果看起來像影片字幕的片尾。Whisper 是用網路影音的逐字稿訓練的,這類句子很可能來自訓練資料裡的字幕片尾。如果機器人照單全收,使用者什麼都沒說,它就會回一句莫名其妙的話。
這些是這五段合成語音的結果,換一段語音、換一個模型,結果都可能不同。
真實環境怎麼處理沒有語音
faster-whisper 提供兩個工具,這個課程沒有錄這些結果,模擬模組會忽略多餘的參數,片段上也沒有 no_speech_prob,所以下面的程式只能在真實環境執行:
segments, info = model.transcribe(path, language='zh', vad_filter=True) # 先用語音活動偵測濾掉沒有人說話的部分
for s in segments:
print(s.text, s.no_speech_prob) # 這個片段沒有語音的機率
門檻要用你自己的語音資料試過才知道,以官方文件為準。下面的練習先用比較陽春的方法:比對常見的字幕片尾字句。a5 還有一個線索:片段的結束時間是 29.98 秒,音檔只有 3.01 秒。這些都只是在這幾段語音上看到的現象,不是通用規則。
在自己的電腦上安裝: pip install faster-whisper。依官方文件:需要 Python 3.9 以上、不需要另外安裝 FFmpeg;第一次用 WhisperModel('large-v3') 會自動下載模型;用 NVIDIA GPU(device='cuda', compute_type='float16')要另外裝 cuBLAS 與 cuDNN,沒有 GPU 就用 device='cpu', compute_type='int8'。這些安裝步驟本課程沒有逐一實測,版本要求以官方文件為準。在 CPU 上轉錄大型模型可能要等一陣子,記得搭配第 10 課的 loading 動畫或背景處理。
下面的練習把語音接上查課表:語音和打字走同一段查詢,課表是虛構的。
動手做:從下載到回覆
完成 on_audio:下載語音、存成暫存檔、轉錄(language='zh',不加提示)、刪除暫存檔,最後回覆「我聽到:」加上文字。
音檔是合成的,轉錄結果是事先跑好的。
想一想
使用者傳了語音訊息,webhook 的事件裡有什麼?
機器人回了「感謝觀看」,使用者其實只傳了一段環境雜音。最可能的原因是?
加上 initial_prompt 之後,下列哪一個說法符合這一課看到的結果?
使用者用語音說「幫我把讀書會改到十點」,機器人轉錄後要修改資料庫。比較好的流程是?
關於這一課的語音處理流程,下列哪一個說法正確?
重點整理
- 語音訊息的 webhook 只有編號與長度,音檔用
MessagingApiBlob.get_message_content下載 model.transcribe回傳segments與info,segments要走訪才會開始轉錄,文字要接起來- 語音辨識會聽錯:要把結果回給使用者確認,會改變資料的動作尤其如此
- 預設可能輸出簡體字,提示有幫助但不保證;沒有人說話也可能生出字來
- 音訊先到 LINE 的伺服器,再到你的機器;轉完就刪,並讓講話的人知道
下一課把前面學的東西整合起來,做一個完整的讀書會機器人。