前言¶
MASR是一款基於Pytorch實現的語音識別框架,MASR致力於簡單,實用的語音識別項目。可部署在服務器,Nvidia Jetson設備,未來還計劃支持Android等移動設備。
源碼地址:https://github.com/yeyupiaoling/MASR
本項目使用的環境:
- Anaconda 3
- Python 3.7
- Pytorch 1.10.0
- Windows 10 or Ubuntu 18.04
模型下載¶
| 數據集 | 使用模型 | 預處理方式 | 語言 | 測試集字錯率(詞錯率) | 下載地址 |
|---|---|---|---|---|---|
| aishell (179小時) | deepspeech2 | linear | 中文 | 0.06346 | 點擊下載 |
| free_st_chinese_mandarin_corpus (109小時) | deepspeech2 | linear | 中文 | 0.13941 | 點擊下載 |
| thchs_30 (34小時) | deepspeech2 | linear | 中文 | 0.06751 | 點擊下載 |
| 超大數據集(1600多小時真實數據)+(1300多小時合成數據) | deepspeech2 | linear | 中文 | 0.06215 | 點擊下載 |
| 超大數據集(1600多小時真實數據)+(1300多小時合成數據) | deepspeech2_big | linear | 中文 | 0.05517 | 先star項目再點擊下載 |
| Librispeech (960小時) | deepspeech2 | 英文 | linear | 0.12842 | 點擊下載 |
說明:
1. 這裏字錯率是使用eval.py程序並使用集束搜索解碼ctc_beam_search方法計算得到的。
2. 下載的壓縮文件已經包含了mean_std.npz和vocabulary.txt,需要把解壓得到的全部文件複製到項目根目錄下。
有問題歡迎提 issue 交流
快速預測¶
- 下載作者提供的模型或者訓練模型,然後執行導出模型,使用
infer_path.py預測音頻,通過參數--wav_path指定需要預測的音頻路徑,完成語音識別,詳情請查看模型部署。
```shell script
python infer_path.py –wav_path=./dataset/test.wav
輸出結果:
----------- Configuration Arguments -----------
alpha: 1.2
beam_size: 10
beta: 0.35
cutoff_prob: 1.0
cutoff_top_n: 40
decoding_method: ctc_greedy
enable_mkldnn: False
is_long_audio: False
lang_model_path: ./lm/zh_giga.no_cna_cmn.prune01244.klm
mean_std_path: ./dataset/mean_std.npz
model_dir: ./models/infer/
to_an: True
use_gpu: True
use_tensorrt: False
vocab_path: ./dataset/zh_vocab.txt
wav_path: ./dataset/test.wav
消耗時間:82, 識別結果: 近幾年不但我用書給女兒兒壓歲也勸說親朋不要給女兒壓歲錢而改送壓歲書, 得分: 94
## 數據準備
1. 在`download_data`目錄下是公開數據集的下載和製作訓練數據列表和詞彙表的,本項目提供了下載公開的中文普通話語音數據集,分別是Aishell,Free ST-Chinese-Mandarin-Corpus,THCHS-30 這三個數據集,總大小超過28G。下載這三個數據只需要執行一下代碼即可,當然如果想快速訓練,也可以只下載其中一個。**注意:** `noise.py`可下載可不下載,這是用於訓練時數據增強的,如果不想使用噪聲數據增強,可以不用下載。
```shell script
cd download_data/
python aishell.py
python free_st_chinese_mandarin_corpus.py
python thchs_30.py
python noise.py
注意: 以上代碼只支持在Linux下執行,如果是Windows的話,可以獲取程序中的DATA_URL單獨下載,建議用迅雷等下載工具,這樣下載速度快很多。然後把download()函數改爲文件的絕對路徑,如下,我把aishell.py的文件單獨下載,然後替換download()函數,再執行該程序,就會自動解壓文件文本生成數據列表。
# 把這行代碼
filepath = download(url, md5sum, target_dir)
# 修改爲
filepath = "D:\\Download\\data_aishell.tgz"
- 如果開發者有自己的數據集,可以使用自己的數據集進行訓練,當然也可以跟上面下載的數據集一起訓練。自定義的語音數據需要符合以下格式,另外對於音頻的採樣率,本項目默認使用的是16000Hz,在
create_data.py中也提供了統一音頻數據的採樣率轉換爲16000Hz,只要is_change_frame_rate參數設置爲True就可以。- 語音文件需要放在
dataset/audio/目錄下,例如我們有個wav的文件夾,裏面都是語音文件,我們就把這個文件存放在dataset/audio/。 - 然後把數據列表文件存在
dataset/annotation/目錄下,程序會遍歷這個文件下的所有數據列表文件。例如這個文件下存放一個my_audio.txt,它的內容格式如下。每一行數據包含該語音文件的相對路徑和該語音文件對應的中文文本,他們之間用\t隔開。要注意的是該中文文本只能包含純中文,不能包含標點符號、阿拉伯數字以及英文字母。
```shell script
dataset/audio/wav/0175/H0175A0171.wav 我需要把空調溫度調到二十度
dataset/audio/wav/0175/H0175A0377.wav 出彩中國人
dataset/audio/wav/0175/H0175A0470.wav 據克而瑞研究中心監測
dataset/audio/wav/0175/H0175A0180.wav 把溫度加大到十八
- 語音文件需要放在
3. 最後執行下面的數據集處理程序,詳細參數請查看該程序。這個程序是把我們的數據集生成三個JSON格式的數據列表,分別是`manifest.test、manifest.train、manifest.noise`。然後建立詞彙表,把所有出現的字符都存放子在`vocabulary.txt`文件中,一行一個字符。最後計算均值和標準差用於歸一化,默認使用全部的語音計算均值和標準差,並將結果保存在`mean_std.npz`中。以上生成的文件都存放在`dataset/`目錄下。數據劃分說明,如果`dataset/annotation`存在`test.txt`,那全部測試數據都使用這個數據,否則使用全部數據的1/500的數據,直到指定的最大測試數據量。
```python
python create_data.py
訓練模型¶
-
訓練流程,首先是準備數據集,具體看數據準備部分,重點是執行
create_data.py程序,執行完成之後檢查是否在dataset目錄下生成了manifest.test、manifest.train、mean_std.npz、vocabulary.txt這四個文件,並確定裏面已經包含數據。然後才能往下執行開始訓練。 -
執行訓練腳本,開始訓練語音識別模型,詳細參數請查看該程序。每訓練一輪和每10000個batch都會保存一次模型,模型保存在
models/<use_model>/epoch_*/目錄下,默認會使用數據增強訓練,如何不想使用數據增強,只需要將參數augment_conf_path設置爲None即可。關於數據增強,請查看數據增強部分。如果沒有關閉測試,在每一輪訓練結果之後,都會執行一次測試計算模型在測試集的準確率,注意爲了加快訓練速度,默認使用的是ctc_greedy解碼器,如果需要使用ctc_beam_search解碼器,請設置decoder參數。如果模型文件夾下包含last_model文件夾,在訓練的時候會自動加載裏面的模型,這是爲了方便中斷訓練的之後繼續訓練,無需手動指定,如果手動指定了resume_model參數,則以resume_model指定的路徑優先加載。如果不是原來的數據集或者模型結構,需要刪除last_model這個文件夾。
# 單卡訓練
CUDA_VISIBLE_DEVICES=0 python train.py
# 多卡訓練
CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.launch --nproc_per_node=2 train.py
訓練輸出結果如下:
----------- Configuration Arguments -----------
alpha: 2.2
augment_conf_path: conf/augmentation.json
batch_size: 32
beam_size: 300
beta: 4.3
cutoff_prob: 0.99
cutoff_top_n: 40
dataset_vocab: dataset/vocabulary.txt
decoder: ctc_greedy
lang_model_path: lm/zh_giga.no_cna_cmn.prune01244.klm
learning_rate: 5e-05
max_duration: 20
mean_std_path: dataset/mean_std.npz
min_duration: 0.5
num_epoch: 65
num_proc_bsearch: 10
num_workers: 8
pretrained_model: None
resume_model: None
save_model_path: models/
test_manifest: dataset/manifest.test
train_manifest: dataset/manifest.train
use_model: deepspeech2
------------------------------------------------
............
[2021-09-17 08:41:16.135825] Train epoch: [24/50], batch: [5900/6349], loss: 3.84609, learning rate: 0.00000688, eta: 10:38:40
[2021-09-17 08:41:38.698795] Train epoch: [24/50], batch: [6000/6349], loss: 0.92967, learning rate: 0.00000688, eta: 8:42:11
[2021-09-17 08:42:04.166192] Train epoch: [24/50], batch: [6100/6349], loss: 2.05670, learning rate: 0.00000688, eta: 10:59:51
[2021-09-17 08:42:26.471328] Train epoch: [24/50], batch: [6200/6349], loss: 3.03502, learning rate: 0.00000688, eta: 11:51:28
[2021-09-17 08:42:50.002897] Train epoch: [24/50], batch: [6300/6349], loss: 2.49653, learning rate: 0.00000688, eta: 12:01:30
======================================================================
[2021-09-17 08:43:01.954403] Test batch: [0/65], loss: 13.76276, cer: 0.23105
[2021-09-17 08:43:07.817434] Test epoch: 24, time/epoch: 0:24:30.756875, loss: 6.90274, cer: 0.15213
======================================================================
- 在訓練過程中,程序會使用VisualDL記錄訓練結果,可以通過以下的命令啓動VisualDL。
visualdl --logdir=log --host=0.0.0.0
- 然後再瀏覽器上訪問
http://localhost:8040可以查看結果顯示,如下。

評估¶
執行下面這個腳本對模型進行評估,通過字符錯誤率來評價模型的性能,詳細參數請查看該程序。
python eval.py --resume_model=models/deepspeech2/best_model
輸出結果:
----------- Configuration Arguments -----------
alpha: 2.2
batch_size: 32
beam_size: 300
beta: 4.3
cutoff_prob: 0.99
cutoff_top_n: 40
dataset_vocab: dataset/vocabulary.txt
decoder: ctc_beam_search
lang_model_path: lm/zh_giga.no_cna_cmn.prune01244.klm
mean_std_path: dataset/mean_std.npz
num_proc_bsearch: 10
num_workers: 8
resume_model: models/deepspeech2/best_model/
test_manifest: dataset/manifest.test
use_model: deepspeech2
------------------------------------------------
W0918 10:33:58.960235 16295 device_context.cc:404] Please NOTE: device: 0, GPU Compute Capability: 7.5, Driver API Version: 11.0, Runtime API Version: 10.2
W0918 10:33:58.963088 16295 device_context.cc:422] device: 0, cuDNN Version: 7.6.
100%|██████████████████████████████| 45/45 [00:09<00:00, 4.50it/s]
評估消耗時間:10s,字錯率:0.095808
導出模型¶
訓練保存的或者下載作者提供的模型都是模型參數,我們要將它導出爲預測模型,這樣可以直接使用模型,不再需要模型結構代碼,同時使用Inference接口可以加速預測,詳細參數請查看該程序。
python export_model.py --resume_model=models/deepspeech2/epoch_50/
輸出結果:
----------- Configuration Arguments -----------
dataset_vocab: dataset/vocabulary.txt
mean_std_path: dataset/mean_std.npz
resume_model: models/deepspeech2/epoch_50
save_model: models/deepspeech2/
use_model: deepspeech2
------------------------------------------------
[2021-09-18 10:23:47.022243] 成功恢復模型參數和優化方法參數:models/deepspeech2/epoch_50/model.pdparams
預測模型已保存:models/deepspeech2/infer
本地預測¶
我們可以使用這個腳本使用模型進行預測,如果如何還沒導出模型,需要執行導出模型操作把模型參數導出爲預測模型,通過傳遞音頻文件的路徑進行識別,通過參數--wav_path指定需要預測的音頻路徑。支持中文數字轉阿拉伯數字,將參數--to_an設置爲True即可,默認爲True。
```shell script
python infer_path.py –wav_path=./dataset/test.wav
輸出結果:
----------- Configuration Arguments -----------
alpha: 2.2
beam_size: 300
beta: 4.3
cutoff_prob: 0.99
cutoff_top_n: 40
decoder: ctc_beam_search
is_long_audio: False
lang_model_path: lm/zh_giga.no_cna_cmn.prune01244.klm
model_dir: models/deepspeech2/infer/
real_time_demo: False
to_an: True
use_gpu: True
use_model: deepspeech2
vocab_path: dataset/vocabulary.txt
wav_path: ./dataset/test.wav
消耗時間:101, 識別結果: 近幾年不但我用書給女兒兒壓歲也勸說親朋不要給女兒壓歲錢而改送壓歲書, 得分: 94
## 長語音預測
通過參數`--is_long_audio`可以指定使用長語音識別方式,這種方式通過VAD分割音頻,再對短音頻進行識別,拼接結果,最終得到長語音識別結果。
```shell script
python infer_path.py --wav_path=./dataset/test_vad.wav --is_long_audio=True
輸出結果:
----------- Configuration Arguments -----------
alpha: 2.2
beam_size: 300
beta: 4.3
cutoff_prob: 0.99
cutoff_top_n: 40
decoding_method: ctc_greedy
is_long_audio: 1
lang_model_path: ./lm/zh_giga.no_cna_cmn.prune01244.klm
model_dir: ./models/deepspeech2/infer/
to_an: True
use_gpu: True
vocab_path: ./dataset/zh_vocab.txt
wav_path: dataset/test_vad.wav
------------------------------------------------
第0個分割音頻, 得分: 70, 識別結果: 記的12鋪地補買上過了矛亂鑽喫出滿你都着們現上就只有1良解太窮了了臭力量緊不着還綁在大理達高的鐵股上
第1個分割音頻, 得分: 86, 識別結果: 我們都是騎自行說
第2個分割音頻, 得分: 91, 識別結果: 他李達康知不知道黨的組織原則
第3個分割音頻, 得分: 71, 識別結果: 沒是把就都路通着獎了李達方就是請他作現長件2着1把爽他作收記書就是發爽
第4個分割音頻, 得分: 76, 識別結果: 那的當了熊掌我還得聽她了哈哈他這太快還裏生長還那得聊嘛安不乖怎麼說
第5個分割音頻, 得分: 97, 識別結果: 他老婆總是出事了嘛
第6個分割音頻, 得分: 63, 識別結果: 就是前急次
第7個分割音頻, 得分: 87, 識別結果: 歐陽箐是他前妻
第8個分割音頻, 得分: 0, 識別結果:
第9個分割音頻, 得分: 97, 識別結果: 我最後再說1句啊
第10個分割音頻, 得分: 84, 識別結果: 能不能幫我個的小忙
第11個分割音頻, 得分: 86, 識別結果: 說
第12個分割音頻, 得分: 85, 識別結果: 她那陳清泉放了別再追究的
第13個分割音頻, 得分: 93, 識別結果: 這陳清泉
第14個分割音頻, 得分: 79, 識別結果: 跟你有生我來啊
第15個分割音頻, 得分: 87, 識別結果: 我不認識個人
第16個分割音頻, 得分: 81, 識別結果: 就是高小琴的人那你管這麼寬幹嘛啊
第17個分割音頻, 得分: 94, 識別結果: 真以天下爲己任了
第18個分割音頻, 得分: 76, 識別結果: 你天下爲竟人那是哪那耍我就是上在上晚上你們再山水張院的人讓我照片和寧練個在我整麼那不那板法
第19個分割音頻, 得分: 67, 識別結果: 你就生涯真說晚啦是長微臺過會來決定了
最終結果,消耗時間:1587, 得分: 79, 識別結果: ,記的12鋪地補買上過了矛亂鑽喫出滿你都着們現上就只有1良解太窮了了臭力量緊不着還綁在大理達高的鐵股上,我們都是騎自行說,他李達康知不知道黨的組織原則,沒是把就都路通着獎了李達方就是請他作現長件2着1把爽他作收記書就是發爽,那的當了熊掌我還得聽她了哈哈他這太快還裏生長還那得聊嘛安不乖怎麼說,他老婆總是出事了嘛,就是前急次,歐陽箐是他前妻,,我最後再說1句啊,能不能幫我個的小忙,說,她那陳清泉放了別再追究的,這陳清泉,跟你有生我來啊,我不認識個人,就是高小琴的人那你管這麼寬幹嘛啊,真以天下爲己任了,你天下爲竟人那是哪那耍我就是上在上晚上你們再山水張院的人讓我照片和寧練個在我整麼那不那板法,你就生涯真說晚啦是長微臺過會來決定了
模擬即時識別¶
這裏提供一個簡單的即時識別例子,如果想完整使用即時識別,可以使用infer_gui.py中的錄音即時識別功能。在--real_time_demo指定爲True。
python infer_path.py --wav_path=./dataset/test.wav --real_time_demo=True
輸出結果:
----------- Configuration Arguments -----------
alpha: 2.2
beam_size: 300
beta: 4.3
cutoff_prob: 0.99
cutoff_top_n: 40
decoder: ctc_beam_search
feature_method: linear
is_long_audio: False
lang_model_path: lm/zh_giga.no_cna_cmn.prune01244.klm
model_dir: models/deepspeech2/infer/
pun_model_dir: models/pun_models/
real_time_demo: True
to_an: False
use_gpu: True
use_model: deepspeech2
use_pun: False
vocab_path: dataset/vocabulary.txt
wav_path: ./dataset/test.wav
------------------------------------------------
======================================================================
初始化解碼器...
language model: is_character_based = 1, max_order = 5, dict_size = 0
初始化解碼器完成!
======================================================================
【即時結果】:消耗時間:19ms, 識別結果: , 得分: -15
【即時結果】:消耗時間:22ms, 識別結果: , 得分: -15
【即時結果】:消耗時間:24ms, 識別結果: 近幾年, 得分: -8
【即時結果】:消耗時間:31ms, 識別結果: 近幾年不, 得分: -10
【即時結果】:消耗時間:34ms, 識別結果: 近幾年不但我用, 得分: -7
【即時結果】:消耗時間:43ms, 識別結果: 近幾年不但我用輸給女兒, 得分: -4
【即時結果】:消耗時間:46ms, 識別結果: 近幾年不但我用輸給女兒壓歲, 得分: -6
【即時結果】:消耗時間:51ms, 識別結果: 近幾年不但我用輸給女兒壓歲也, 得分: -7
【即時結果】:消耗時間:52ms, 識別結果: 近幾年不但我用輸給女兒壓歲也勸說, 得分: -6
【即時結果】:消耗時間:68ms, 識別結果: 近幾年不但我用輸給女兒壓歲也勸說親朋不, 得分: -6
【即時結果】:消耗時間:85ms, 識別結果: 近幾年不但我用輸給女兒壓歲也勸說親朋不要給女兒, 得分: -3
【即時結果】:消耗時間:152ms, 識別結果: 近幾年不但我用輸給女兒壓歲也勸說親朋不要給女兒壓歲, 得分: -6
【即時結果】:消耗時間:76ms, 識別結果: 近幾年不但我用輸給女兒壓歲也勸說親朋不要給女兒壓歲錢, 得分: -3
【即時結果】:消耗時間:91ms, 識別結果: 近幾年不但我用輸給女兒壓歲也勸說親朋不要給女兒壓歲錢而改, 得分: -7
【即時結果】:消耗時間:134ms, 識別結果: 近幾年不但我用輸給女兒壓歲也勸說親朋不要給女兒壓歲錢而改送壓, 得分: -6
【即時結果】:消耗時間:163ms, 識別結果: 近幾年不但我用輸給女兒壓歲也勸說親朋不要給女兒壓歲錢而改送壓歲書, 得分: -2
【即時結果】:消耗時間:102ms, 識別結果: 近幾年不但我用輸給女兒壓歲也勸說親朋不要給女兒壓歲錢而改送壓歲書, 得分: -2
Web部署¶
在服務器執行下面命令通過創建一個Web服務,通過提供HTTP接口來實現語音識別。啓動服務之後,如果在本地運行的話,在瀏覽器上訪問http://localhost:5000,否則修改爲對應的 IP地址。打開頁面之後可以選擇上傳長音或者短語音音頻文件,也可以在頁面上直接錄音,錄音完成之後點擊上傳,播放功能只支持錄音的音頻。支持中文數字轉阿拉伯數字,將參數--to_an設置爲True即可,默認爲True。
```shell script
python infer_server.py
打開頁面如下:

## GUI界面部署
通過打開頁面,在頁面上選擇長語音或者短語音進行識別,也支持錄音識別,同時播放識別的音頻。默認使用的是貪心解碼策略,如果需要使用集束搜索方法的話,需要在啓動參數的時候指定。
```shell script
python infer_gui.py
打開界面如下:

相關項目¶
- 基於Pytorch實現的聲紋識別:VoiceprintRecognition-Pytorch
- 基於Pytorch實現的分類:AudioClassification-Pytorch
- 基於PaddlePaddle實現的語音識別:PPASR
參考資料¶
- https://github.com/yeyupiaoling/PPASR
- https://github.com/jiwidi/DeepSpeech-pytorch
- https://github.com/wenet-e2e/WenetSpeech
- https://github.com/SeanNaren/deepspeech.pytorch