上篇文章已經介紹了語料資料的預處理,對資料集中的音訊資料分別做了重取樣、靜音切除、降噪以及在Fbank和MFCC中特徵提取方式中選擇了Fbank對音訊資料進行特徵提取的方法;在經過上面的處理後已經可以將提取出的音訊資料Fbank丟到Transformer中進行訓練,但還漏了一個比較重要的資料預處理,音訊對應的文字預處理。下面除了介紹文字預處理之外還會介紹模型訓練的相關內容;
資料集結構
資料集結構如下,train資料夾下存放訓練集wav音訊檔案,trains.text檔案每行對應一個音訊檔案的中文標註,trans.paths檔案每行對應一個wav音訊檔案的路徑其與trains.text檔案同一行對應同一個檔案標註。
train/x.wav
trains.text
trains.paths
標籤文字預處理
在ASR模型中其預測出來的結果文字並不是固定長度的,模型推理出的結果也不會是隻是輸出中文,它輸出的只是一句話或某個詞的向量/數字標識,基於此我們需要對語料集中的音訊所對應的文字進行資料預處理將其轉為向量/數字標識,建立其對應關係。
音訊所對應的文字可能是一個句子也可能是一兩個詞我們就將文字分詞(tokenization),在分詞,按分詞粒度分目前存在三種演算法方案:char、word、subword;
char: 對於英文來說由26個字母詞表以及其他特殊字元組成,對於中文由5000多箇中文字以及特殊組成,詞表很簡單。
word: 詞表由單詞、中文片語成,但可能詞覆蓋得不夠全,詞表大。
subword: 介於char與word之間分詞粒度比char大又比word小,如承上啟下,可能分為了:承上、啟下兩個子詞。
目前subword(子詞)分詞演算法主要有三種:Byte Pair Encoding (BPE)、WordPiece、Unigram Language Model。在這裡使用了Sentencepiece分詞框架,其支援BPE、ULM子詞演算法、支char, word分詞。它使用了Unicode編碼字元支援多語言、編解碼可逆、可控詞表大小。
詞表生成
這裡使用了Sentencepiece用於生成詞表,會生成兩個檔案,一個為模型檔案、另一個為詞表檔案,具體詞表生成程式碼如下:
pip install sentencepiece
parser = ArgumentParser()
parser.add_argument("--txt_file_path", type=str, required=True)
parser.add_argument("--vocab_size", type=int, required=True)
parser.add_argument("--model_type", type=str, required=True)
parser.add_argument("--model_prefix", type=str, required=True)
parser.add_argument("--sos", type=int, required=True)
parser.add_argument("--eos", type=int, required=True)
parser.add_argument("--unk", type=int, required=True)
parser.add_argument("--norm", type=str, default="identity", help="no normalization")
parser.add_argument("--unk_str", type=str, default=chr(ord('a') + 72))
args = parser.parse_args()
os.makedirs(os.path.dirname(args.model_prefix), exist_ok=True)
spm.SentencePieceTrainer.train(' '.join([
f"--input={args.txt_file_path} ",
f"--model_prefix={args.model_prefix}",
f"--vocab_size={args.vocab_size}",
f"--model_type={args.model_type}",
f"--normalization_rule_name={args.norm}",
f"--control_symbols=<blank>", # for CTC loss
f"--bos_id={args.sos} --eos_id={args.eos} --unk_id={args.unk}", # we don't need to set `pad_id` since it's -1 by default
f"--pad_piece=<ig> --bos_piece=<sos> --eos_piece=<eos> --unk_piece={args.unk_str}",
]))
引數描述
txt_file_path:資料集文字標籤文件
model_prefix:模型字首
model_type:模型型別
vocab_size:詞表大小
sos:文字開始標誌
eos:文字結束標誌
簡單看詞表就是經過分詞後詞與數字的對映關係表,詞表檔案在語音識別模型中至關重要,在ASR訓練模型時會將音訊檔案所對應的中文詞彙(子詞)所對應的數字編碼與音訊檔案丟到模型中訓練。訓練處模型完成後語音識別模型在推理時推理出來的也是機率相對較高的一個/一串數字,將數字丟到Sentencepiece分詞模型就可得到某個音訊所對應的中文。
tokenizer = SubwordTokenizer('hainan.model')
tokenizer.tokenize('檳榔') #獲取檳榔獲取在詞彙表中的編碼870
tokenizer.detokenize([870]) #獲取編碼870在詞彙表中對應的詞 檳榔
模型推理
由於資料集規模較小,模型泛化能力不太好。通常在語音識別中透過使用詞錯誤率(Word Error Rate) 與字元錯誤率(Character Error Rate) 這兩個指標來了評估語音識別模型效能,觀察模型訓練的效果。在英文中可以用兩者來評估。英文最小單位為Word(單詞)中文最小單位為字(字元)所以在中文語音識別中通常用CER來表示字錯誤率。
WER、CER其原理為編輯距離(Edit distance)中的Levenshtein Distance的計算過程,編輯距離指兩個字串之間,由一個轉成另一個所需的最少編輯操作次數。操作包括:插入、刪除、替換。
還有一個常用指標SER(Sentence Error Rate)句錯率描述識別句子錯誤的機率。
WER:在詞維度評估錯誤率,關注詞的插入、刪除和替換錯誤。WER的計算公式是將識別結果中的替換、刪除和插入詞的數量除以參考文字中的總詞數。WER的值越低,說明語音識別系統的效能越好,因為它表示識別出的詞序列與參考詞序列之間的差異越小。
CER:則是在字元維度上評估錯誤率,關注的是字元的插入、刪除和替換錯誤。CER的計算公式是將識別結果中的替換(S)、刪除(D)和插入(I)字元的數量除以參考文字中的總字元數。CER的值越低,同樣表示生成文字與參考文字越接近,系統效能越好。
公式:CER = (S+D+I)/N
S:替換數(識別錯誤) D:刪除數(識別少了) I:插入次數(預測文字比參考文字多出的)
參考文字(真實): 東線高速 預測文字: 西線高速
CER = (1+0+0)/4 = 0.25
參考文字(真實): 你真的有錢啊 預測文字: 什麼時候去工作啊
CER = (6+0+2)/6 = 1.33
SER: 識別錯誤的文字個數除以參考文字總數*100,只要有一個字錯都算識別錯誤。
這裡使用了SER(句錯率)與字錯率(CER)來評估模型訓練的效能。
經過評測句錯誤率(SER)比較高達到54%,字錯誤率(CER)為36%,兩者都比較高不太理想。理想語音識別系統通常準確率要達到95%+以上才會有比較好的可用性。
後續將透過各種方法繼續擴大資料集規模、最佳化訓練模型,爭取提高海南話語音識別模型的正確率。
參考資料:
Edit distance
WER
Automatic-Speech-Recognition-from-Scratch