教面試官ReentrantLock源碼
1293
2025-03-31
WeNet是一款開源端到端ASR工具包,它與ESPnet等開源語音項目相比,最大的優勢在于提供了從訓練到部署的一整套工具鏈,使ASR服務的工業落地更加簡單。如圖1所示,WeNet工具包完全依賴于PyTorch生態:使用TorchScript進行模型開發,使用Torchaudio進行動態特征提取,使用DistributedDataParallel進行分布式訓練,使用torch JIT(Just In Time)進行模型導出,使用LibTorch作為生產環境運行時。本系列將對WeNet云端推理部署代碼進行解析。
圖1:WeNet系統設計[1]
1. 代碼結構
WeNet云端推理和部署代碼位于wenet/runtime/server/x86路徑下,編程語言為C++,其結構如下所示:
其中:
語音文件讀入與特征提取相關代碼位于frontend文件夾下;
端到端模型導入、端點檢測與語音解碼識別相關代碼位于decoder文件夾下,WeNet支持CTC prefix beam search和融合了WFST的CTC beam search這兩種解碼算法,后者的實現大量借鑒了Kaldi,相關代碼放在kaldi文件夾下;
在服務化方面,WeNet分別實現了基于WebSocket和基于gRPC的兩套服務端與客戶端,基于WebSocket的實現位于websocket文件夾下,基于gRPC的實現位于grpc文件夾下,兩種實現的入口main函數代碼都位于bin文件夾下。
日志、計時、字符串處理等輔助代碼位于utils文件夾下。
WeNet提供了CMakeLists.txt和Dockerfile,使得用戶能方便地進行項目編譯和鏡像構建。
2. 前端:frontend文件夾
WeNet只支持44字節header的wav格式音頻數據,wav header定義在WavHeader結構體中,包括音頻格式、聲道數、采樣率等音頻元信息。WavReader類用于語音文件讀入,調用fopen打開語音文件后,WavReader先讀入WavHeader大小的數據(也就是44字節),再根據WavHeader中的元信息確定待讀入音頻數據的大小,最后調用fread把音頻數據讀入buffer,并通過static_cast把數據轉化為float類型。
struct WavHeader { char riff[4]; // "riff" unsigned int size; char wav[4]; // "WAVE" char fmt[4]; // "fmt " unsigned int fmt_size; uint16_t format; uint16_t channels; unsigned int sample_rate; unsigned int bytes_per_second; uint16_t block_size; uint16_t bit; char data[4]; // "data" unsigned int data_size; };
這里存在的一個風險是,如果WavHeader中存放的元信息有誤,則會影響到語音數據的正確讀入。
WeNet使用的特征是fbank,通過FeaturePipelineConfig結構體進行特征設置。默認幀長為25ms,幀移為10ms,采樣率和fbank維數則由用戶輸入。
用于特征提取的類是FeaturePipeline。為了同時支持流式與非流式語音識別,FeaturePipeline類中設置了input_finished_屬性來標志輸入是否結束,并通過set_input_finished()成員函數來對input_finished_屬性進行操作。
提取出來的fbank特征放在feature_queue_中,feature_queue_的類型是BlockingQueue
當feature_queue_中的feature數量超過capacity,則Push線程被掛起,等待feature_queue_.Pop()釋放出空間。
當feature_queue_為空,則Pop線程被掛起,等待feature_queue_.Push()。
線程的掛起和恢復是通過C++標準庫中的線程同步原語std::mutex、std::condition_variable等實現。
線程同步還用在AcceptWaveform和ReadOne兩個成員函數中,AcceptWaveform把語音數據提取得到的fbank特征放到feature_queue_中,ReadOne成員函數則把特征從feature_queue_中讀出,是經典的生產者消費者模式。
3. 解碼器:decoder文件夾
通過torch::jit::load對存在磁盤上的模型進行反序列化,得到一個ScriptModule對象。
torch::jit::script::Module model = torch::jit::load(model_path);
WeNet推理支持的解碼方式都繼承自基類SearchInterface,如果要新增解碼算法,則需繼承SearchInterface類,并提供該類中所有純虛函數的實現,包括:
// 解碼算法的具體實現 virtual void Search(const torch::Tensor& logp) = 0; // 重置解碼過程 virtual void Reset() = 0; // 結束解碼過程 virtual void FinalizeSearch() = 0; // 解碼算法類型,返回一個枚舉常量SearchType virtual SearchType Type() const = 0; // 返回解碼輸入 virtual const std::vector
目前WeNet只提供了SearchInterface的兩種子類實現,也即兩種解碼算法,分別定義在CtcPrefixBeamSearch和CtcWfstBeamSearch兩個類中。
WeNet支持語音端點檢測,提供了一種基于規則的實現方式,用戶可以通過CtcEndpointConfig結構體和CtcEndpointRule結構體進行規則配置。WeNet默認的規則有三條:
檢測到了5s的靜音,則認為檢測到端點;
解碼出了任意時長的語音后,檢測到了1s的靜音,則認為檢測到端點;
解碼出了20s的語音,則認為檢測到端點。
一旦檢測到端點,則結束解碼。另外,WeNet把解碼得到的空白符(blank)視作靜音。
WeNet提供的解碼器定義在TorchAsrDecoder類中。如圖3所示,WeNet支持雙向解碼,即疊加從左往右解碼和從右往左解碼的結果。在CTC beam search之后,用戶還可以選擇進行attention重打分。
圖2:WeNet解碼計算流程[2]
可以通過DecodeOptions結構體進行解碼參數配置,包括如下參數:
struct DecodeOptions { int chunk_size = 16; int num_left_chunks = -1; float ctc_weight = 0.0; float rescoring_weight = 1.0; float reverse_weight = 0.0; CtcEndpointConfig ctc_endpoint_config; CtcPrefixBeamSearchOptions ctc_prefix_search_opts; CtcWfstBeamSearchOptions ctc_wfst_search_opts; };
其中,ctc_weight表示CTC解碼權重,rescoring_weight表示重打分權重,reverse_weight表示從右往左解碼權重。最終解碼打分的計算方式為:
final_score = rescoring_weight * rescoring_score + ctc_weight * ctc_score; rescoring_score = left_to_right_score * (1 - reverse_weight) + right_to_left_score * reverse_weight
TorchAsrDecoder對外提供的解碼接口是Decode(),重打分接口是Rescoring()。Decode()返回的是枚舉類型DecodeState,包括三個枚舉常量:kEndBatch,kEndpoint和kEndFeats,分別表示當前批數據解碼結束、檢測到端點、所有特征解碼結束。
為了支持長語音識別,WeNet還提供了連續解碼接口ResetContinuousDecoding(),它與解碼器重置接口Reset()的區別在于:連續解碼接口會記錄全局已經解碼的語音幀數,并保留當前feature_pipeline_的狀態。
總結
本文主要對WeNet云端推理代碼進行探索,介紹了代碼結構、前端和解碼器部分代碼。在《WeNet云端推理部署代碼解析(下)》中,筆者將繼續解析WeNet云端部署代碼。
參考
[1] WeNet: Production First and Production Ready End-to-End Speech Recognition Toolkit
[2] U2++: Unified Two-pass Bidirectional End-to-end Model for Speech Recognition
SIS
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。