0
本文作者: AI研習(xí)社-譯站 | 2018-07-31 09:50 |
雷鋒網(wǎng)按:本文為 AI 研習(xí)社編譯的技術(shù)博客,原標(biāo)題 Predicting English Pronunciations,作者 Ryan Epp。
翻譯 | 涂世文 整理 | 凡江
1. 動(dòng)機(jī)
我近期在研究一個(gè) NLP 項(xiàng)目,根據(jù)項(xiàng)目的要求,需要能夠通過設(shè)計(jì)算法和模型處理單詞的音節(jié) (Syllables),并對那些沒有在詞典中出現(xiàn)的單詞找到其在詞典中對應(yīng)的押韻詞(注:這類單詞類似一些少見的專有名詞或者通過組合產(chǎn)生的新詞,比如 Brexit,是用 Britain 和 exit 組合在一起創(chuàng)造出來表示英國脫歐的新詞)。在這兩個(gè)任務(wù)中,能夠?qū)卧~的發(fā)音進(jìn)行預(yù)測是非常有必要的。本文詳細(xì)記錄我解決該問題的過程,希望能夠?qū)Τ鯇W(xué)者和具有一定經(jīng)驗(yàn)的朋友有所幫助。本文代碼實(shí)現(xiàn)均基于 Python 3 和 Keras 框架?,F(xiàn)在讓我們開始吧!
2. 數(shù)據(jù)集獲取
我們將使用 CMU Pronunciation Dictionary (http://www.speech.cs.cmu.edu/cgi-bin/cmudict) 作為我們的數(shù)據(jù)集,該詞典收錄了將近 134000 個(gè)單詞以及對應(yīng)的音標(biāo)拼寫。譬如「蘋果」的英文單詞「apple」出現(xiàn)在該詞典中的形式為:「AE1P AH0L」。其中每一個(gè)去除數(shù)字后的音標(biāo)塊(token),表示一個(gè)發(fā)音(如 AE,P,AH 等),在語言學(xué)里稱之為「音素」。音素結(jié)尾的數(shù)字表示發(fā)音的聲調(diào)大小,被稱為「詞匯重音標(biāo)記」。由于只有元音才有重音標(biāo)記,所以在英文中有 39 個(gè)唯一的音素和 84 個(gè)獨(dú)特的符號。
話不多說,讓我們先加載 CMU Pronunciation Dictionary 詞典數(shù)據(jù)并做一下初步數(shù)據(jù)清洗工作:
先輸出幾條數(shù)據(jù),看看我們的詞典數(shù)據(jù)長什么樣兒:
3. 數(shù)據(jù)準(zhǔn)備
接下來,在我們將數(shù)據(jù)交給學(xué)習(xí)算法之前,我們需要想辦法將單詞和發(fā)音用數(shù)值的形式表示。在這里我們將單詞看作是字符序列,發(fā)音看作音素符號的序列(包括重音標(biāo)記)。我們可以給每一個(gè)字符和音素賦予一個(gè)數(shù)值,然后我們就可以將它們表示為 One-Hot 向量的形式。根據(jù)單詞的字母預(yù)測其發(fā)音可以看作一個(gè)字音轉(zhuǎn)換問題。我們需要告訴模型語音拼寫從哪里開始又從哪里結(jié)束,因此我們引入兩個(gè)獨(dú)特的開始和結(jié)束標(biāo)注符號,在這里我使用的制表符'\t' 和換行符'\n'分別來表示。
用這些數(shù)字型的 ID 直接作為模型的輸入看起來非常誘人,但是這樣做的話會(huì)使得字母/音素之間隱含一種并不真正存在的關(guān)系。例如,由于 A=4,C=6,和 U=24,意味著 A 和 C 在某種程度上比 A 和 U 更相似(因?yàn)?4 更接近 6)。顯然事實(shí)情況并非如此。相反,我們可以使用我們的 ID 映射來將字符和音素轉(zhuǎn)換為 one-hot 向量(https://machinelearningmastery.com/why-one-hot-encode-data-in-machine-learning/):
現(xiàn)在我們有一種數(shù)值化表示字母和音素的方法,我們可以把整個(gè)數(shù)據(jù)集轉(zhuǎn)換成兩個(gè)大的三維矩陣(也可以被稱為張量):
4. Baseline 模型
由于我們處理的是序列數(shù)據(jù),對于序列數(shù)據(jù)來說,RNN 模型 [視頻, 博客] 最適合不過了。讓我們先從基于 RNN 的 LSTM 模型 [視頻, 博客] 開始上手吧!
RNN 模型視頻鏈接:https://www.coursera.org/learn/nlp-sequence-models/lecture/ftkzt/recurrent-neural-network-model
RNN 模型博客鏈接:https://www.coursera.org/learn/nlp-sequence-models/lecture/ftkzt/recurrent-neural-network-model
LSTM 模型視頻鏈接:https://www.coursera.org/learn/nlp-sequence-models/lecture/ftkzt/recurrent-neural-network-model
LSTM 模型博客鏈接:http://colah.github.io/posts/2015-08-Understanding-LSTMs/
需要注意的是,單詞中的字符數(shù)通常與發(fā)音中的音素的數(shù)目不相同。我們的輸入和輸出之間并不存在一對一的映射。出于這個(gè)原因,我們將創(chuàng)建一個(gè)帶有編碼器 (encoder) 和解碼器 (decoder) 兩個(gè)部分的 seq2seq [博客鏈接](https://blog.keras.io/a-ten-minute-introduction-to-sequence-to-sequence-learning-in-keras.html) 模型。
我們一次給編碼器 (Encoder) 輸入一個(gè)字符,然后將編碼器 (Encoder) 的狀態(tài)變量傳遞給解碼器 (Decoder)。我們需要稍微不同的解碼器 (Decoder) 設(shè)置的訓(xùn)練與測試時(shí)間。在訓(xùn)練過程中,我們將給解碼器提供正確的讀音,一次一個(gè)音素。在每個(gè)時(shí)間步長,解碼器將預(yù)測下一個(gè)音素。在推理過程(預(yù)測發(fā)音)中,我們不知道正確的音素序列(至少在理論上是這樣)。因此,我們將把解碼器 (Decoder) 的輸出從前一個(gè)時(shí)間步長輸入到下一個(gè)時(shí)間步長作為輸入。這就是為什么我們需要前面提到的 START_PHONE_SYM 的原因。這里的流程圖說明了我們的模型在測試集上如何進(jìn)行預(yù)測:
我們在上面創(chuàng)建的 phone_seq_matrix 將會(huì)作為我們 Decoder 的輸入。我們將通過將所產(chǎn)生的發(fā)音序列向左移動(dòng) 1 步來創(chuàng)建解碼器輸出。因此解碼器輸出將不包含開始標(biāo)記:
4.1 模型訓(xùn)練
首先,我們需要從數(shù)據(jù)集中劃分出測試集以便后期能對模型性能進(jìn)行評估。為了能在 Kaggle 上運(yùn)行,我們會(huì)將測試集的大小減少至 100 個(gè)數(shù)據(jù)樣本。
現(xiàn)在,我們將開始訓(xùn)練我們的 seq2seq 模型直到它開始過擬合為止。我們需要一個(gè)泛化能力強(qiáng)的模型,對于在訓(xùn)練集中未出現(xiàn)的樣本也能有不錯(cuò)的表現(xiàn)。所以在訓(xùn)練過程中我們會(huì)保存那些在驗(yàn)證集上有最低 loss 的模型。
4.2 預(yù)測
在訓(xùn)練過程中,在每個(gè)時(shí)間步長,我們給我們的解碼器正確的輸出,這個(gè)輸出來自于以前的時(shí)間步長。如前所述,我們不知道測試時(shí)間的正確輸出是什么,只有解碼器預(yù)測的是什么。所以我們需要一個(gè)不同的程序來進(jìn)行預(yù)測。
1. 使用編碼器模型將輸入字(字符序列)編碼為狀態(tài)向量。
2. 將編碼器的狀態(tài)變量傳遞給解碼器。
3. 將起始標(biāo)志送到解碼器以在第一時(shí)間步長獲得音素預(yù)測。
4. 將更新的狀態(tài)和第一個(gè)音素預(yù)測作為輸入輸入到解碼器,以得到第二個(gè)預(yù)測音素。
5. 將更新的狀態(tài)和第二個(gè)音素傳遞到解碼器以獲得第三個(gè)音素等,直到解碼器預(yù)測出停止標(biāo)志(stop token),或者我們達(dá)到最大發(fā)音序列長度。
讓我們做一個(gè)快速的手動(dòng)檢查,看看我們的模型是怎么做的:
初步的預(yù)測結(jié)果看起來效果不錯(cuò),部分錯(cuò)誤的預(yù)測也完全可以理解... 畢竟我也不會(huì)讀
4.3 模型評估
我們將使用三種不同的度量指標(biāo)來評估我們的模型。
1. 基于音節(jié)計(jì)數(shù)的準(zhǔn)確率:記住這個(gè)項(xiàng)目的最初目標(biāo)之一是能夠計(jì)算字典中沒有出現(xiàn)的單詞的音節(jié)數(shù)量。從語音拼寫中獲得音節(jié)的計(jì)數(shù)與用重音符號計(jì)算音素一樣簡單:
2. 完全準(zhǔn)確率:這個(gè)指標(biāo)更加的嚴(yán)格,要求在測試集中每一個(gè)預(yù)測的音素和重音符號均正確而且在預(yù)測順序上也要正確,符合這樣三個(gè)要求的預(yù)測正確樣本數(shù)占測試集樣本總數(shù)的百分比即為完全準(zhǔn)確率。
3. BLEU 評分均值:該評分的值介于 0.0-1.0 之間,1.0 表示預(yù)測完全正確,0.0 表示預(yù)測完全錯(cuò)誤。該評價(jià)指標(biāo)經(jīng)常用于評估機(jī)器翻譯模型的水平,如果你稍微想一想,這個(gè)和我們的發(fā)音預(yù)測還是蠻相似的??梢渣c(diǎn)開此鏈接(https://machinelearningmastery.com/calculate-bleu-score-for-text-python/)查看更多。
很棒!模型目前取得的分?jǐn)?shù)看起來還不錯(cuò)!現(xiàn)在讓我們看看有沒有其他什么辦法提升我們的 Baseline 模型。
在我們繼續(xù)之前,我們先將我們的 Baseline 模型從 TensorFlow 計(jì)算圖中移除,以便節(jié)省系統(tǒng)內(nèi)存。
5. 字符 & 音素嵌入
我們將采用 Embedding 技術(shù) [視頻鏈接, 博客鏈接] 來表示字母和音素,而不是將字母和音素表示為 One-Hot 向量,所以我們的模型將學(xué)習(xí)它自己的每一個(gè)符號的表示。Embedding 所產(chǎn)生的向量比一般的 One-Hot 向量更具有描述性的表示。想想字母 C 有時(shí)聽起來像「K」,其他時(shí)候聽起來像是 S。理論上,我們的嵌入層應(yīng)該學(xué)會(huì)這些潛在的關(guān)系。希望這將有助于提高我們的評測分?jǐn)?shù)。
Keras 的 Embedding 層將會(huì)自動(dòng) ID 轉(zhuǎn)換為 Embedding 向量,所以我們需要改變我們單詞數(shù)據(jù)的表示方式。這次我們將只存儲字符和音素 ID 而不是它們的 One-Hot 向量表示。為了簡單起見,我們將繼續(xù)使用音素的 One-Hot 向量表示作為為解碼器的輸出層。
理論上來說我們需要重新劃分訓(xùn)練集和測試集。但需要注意的是,我們設(shè)置的 random_state 與以前相同,所以重新劃分后的集合中的樣本都是相同的。這也意味著,我們不需要為我們的 One-Hot(解碼器輸出)音素矩陣重新劃分,因?yàn)槲覀冋谥赜盟?br/>
最后,我們可以添加新的嵌入層到我們的基線模型。因?yàn)樗麄兘o我們的網(wǎng)絡(luò)增加了更多可訓(xùn)練的參數(shù),所以更容易過擬合。讓我們通過添加一些 Dropout 層來避免這種情況:
5.1 訓(xùn)練 Embedding 模型
5.2 評估 Embedding 模型
為了評估我們的 Embedding 模型我們需要添加一個(gè)新的幫助方法來將單詞的 ID 轉(zhuǎn)換為原來的單詞:
在評測我們的新模型之前,我們需要重寫預(yù)測方法來處理 ID 形式表示的結(jié)果(而不是 one-hot 形式)
很好!在添加了嵌入層和 Dropout 層之后,模型的評分又創(chuàng)新高!
5.3 Embedding 可視化
讓我們提取從模型學(xué)習(xí)到的 Embeddings 并用 t-SNE 技術(shù)來將它們可視化呈現(xiàn)一下吧!
相當(dāng) Cool!可以看到那些發(fā)音類似的字母和音素聚類到了一起?,F(xiàn)在讓我們再次重置一下 TensorFlow 計(jì)算圖并繼續(xù)探索其他模型吧!
6. 雙向編碼器 & 注意力解碼器
到目前為止,我們的 RNN 模型只運(yùn)行在一個(gè)方向上,我們的編碼器和解碼器之間的唯一連接是我們在它們之間傳遞的 2 個(gè)狀態(tài)變量(從編碼器的末端開始)。對于較長的單詞和發(fā)音,這些狀態(tài)變量可能不足以捕獲整個(gè)單詞,并且來自編碼器的信號有可能丟失。
Attention 注意力機(jī)制是避免這個(gè)問題的一種方式。我們需要對模型的結(jié)構(gòu)做一些大的改變。我們將使用編碼器的輸出,而不是它的內(nèi)部狀態(tài)變量。這使得編碼器很容易雙向進(jìn)行。在一個(gè)單詞中,關(guān)于下一個(gè)以及前面的字符的信息應(yīng)該會(huì)在每個(gè)時(shí)間步產(chǎn)生更好的編碼。
6.1 訓(xùn)練 Attention Model
由于我們的模型、輸入和輸出已經(jīng)從我們以前的 2 個(gè)版本中改變了很多,所以我們需要稍微重寫我們訓(xùn)練的過程。當(dāng)我們開始過擬合或者達(dá)到結(jié)果一直處于「平緩」?fàn)顟B(tài)的時(shí)候我們就停止訓(xùn)練,并將達(dá)到最佳驗(yàn)證集損失時(shí)候的權(quán)重以文件的形式存儲起來。
6.2 評估 Attention Model
我們還需要對我們的 baseline 模型的預(yù)測方法做一些修改,使之與我們的注意力模型能夠協(xié)同工作:
從結(jié)果來看,新的 Attention 模型在三個(gè)指標(biāo)上均有進(jìn)一步的提升!
6.3 最終訓(xùn)練和評估
我們已經(jīng)知道了哪個(gè)模型效果最好,現(xiàn)在的問題是我們的模型在其過擬合之前需要訓(xùn)練多久以及超參數(shù)如何調(diào)整到最優(yōu)。讓我們在全部訓(xùn)練數(shù)據(jù)集上訓(xùn)練一個(gè)最終版的模型吧。
我們的數(shù)字看起來不錯(cuò),但是我們還有 1 個(gè)訣竅沒有施展出來呢!
7. 集束搜索
目前當(dāng)我們利用模型作出預(yù)測的時(shí)候,在每一個(gè)時(shí)間步(Time Step)選擇最大概率的單個(gè)音素并不斷繼續(xù)這個(gè)過程。但是我們真正想要的是有著最大概率的音素序列這樣一個(gè)整體。如果我們早早做出錯(cuò)誤的選擇,我們很容易最終得到一個(gè)不太理想的預(yù)測,而我們永遠(yuǎn)也不會(huì)知道。一種解決方案是搜索整個(gè)輸出空間,并選擇所有可能的序列中最好的。這將確保我們找到最有可能的序列(至少根據(jù)我們的模型),但這將花費(fèi)大量的精力。
相反,我們可以使用集束搜索 [視頻鏈接, 博客鏈接] 這種介于兩種極端之間的 Trick。在每個(gè)時(shí)間步長,我們保持最可能的序列并向前移動(dòng)。增加 k 的值意味著我們更有可能找到最優(yōu)序列,但同時(shí)增加搜索時(shí)間。
8. 結(jié)果
這里是我在完整測試集(大約 20K 大小樣本)上跑出來的結(jié)果匯總表格:
不得不吐槽一下英語真是一門奇怪的語言。對于一些新的單詞,甚至是以英語為母語的人們也會(huì)讀錯(cuò)。發(fā)音規(guī)則復(fù)雜多變,有時(shí)候根本無法理解。感興趣的話可以看看這個(gè) Yotube 視頻(https://www.youtube.com/watch?v=1edPxKqiptw)上的一些例子。75.4% 的準(zhǔn)確率雖然看起來不高,但是綜合語言的特點(diǎn)來看,我覺得這還算是個(gè)不錯(cuò)的分?jǐn)?shù)。
8.1 錯(cuò)誤分類抽樣
讓我們看一看被模型錯(cuò)誤預(yù)測發(fā)音的究竟是哪些單詞:
好吧,確實(shí)... 作為人類,我也不會(huì)讀...
8.2 未來改進(jìn)的思路
使用兩個(gè)單獨(dú)模型:模型 1 只預(yù)測音素,而模型 2 在適當(dāng)?shù)奈恢眉由现匾舴〝?shù)字)。知道最后序列的長度意味著我們的第二個(gè)模型的解碼器很容易是雙向的。了解過去和將來的元音發(fā)音似乎有助于改善我們對重音符的預(yù)測效果。
進(jìn)一步超參數(shù)調(diào)整 實(shí)際上在本文我們真的沒有花太多時(shí)間調(diào)整我們的超參數(shù)。如果多花點(diǎn)時(shí)間的話,應(yīng)該很容易找到一些更好的價(jià)值,并提高我們的分?jǐn)?shù)。
使用更復(fù)雜的模型 將另一個(gè)遞歸層添加到編碼器或在解碼器后加入一些 1D 卷積層是值得嘗試的。
更多的數(shù)據(jù) 創(chuàng)建一個(gè)有更多名字、地方和俚語的數(shù)據(jù)集應(yīng)該有幫助。
8.3 進(jìn)階的技術(shù)
當(dāng)然這絕不是發(fā)音預(yù)測問題的最先進(jìn)的方法,距離達(dá)到 SOTA 水平還遠(yuǎn)著呢!如果你對更好的解決方案感興趣,一定要拜讀一下這些論文:
Predicting Pronunciations with Syllabification and Stress with Recurrent Neural Networks by Daan van Esch, Mason Chua, Kanishka Rao(https://www.isca-speech.org/archive/Interspeech_2016/pdfs/1419.PDF)
Text-To-Phoneme Mapping Using Neural Networks by Enik? Beatrice Bilcu(https://tutcris.tut.fi/portal/files/2313141/bilcub.pdf)
GRAPHEME-TO-PHONEME CONVERSION USING LONG SHORT-TERM MEMORY RECURRENT NEURAL NETWORKS by Kanishka Rao, Fuchun Peng, Has?im Sak, Franc?oise Beaufays(https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/43264.pdf)
Joint-Sequence Models for Grapheme-to-PhonemeConversion by Maximilian Bisani, Hermann Ney(https://hal.archives-ouvertes.fr/hal-00499203/document)
8.4 致謝
本文的很多靈感都來自于 Andrew Ng 在 Coursera 開設(shè)的深度學(xué)習(xí)專項(xiàng)課程(https://www.coursera.org/specializations/deep-learning),同時(shí)參考了 Github 上 Keras Example(https://github.com/keras-team/keras/tree/master/examples)用例作為本文的 Baseline 模型。在實(shí)現(xiàn)注意力模型的時(shí)候,借鑒了 PyTorch tutorial(https://pytorch.org/tutorials/intermediate/seq2seq_translation_tutorial.html)上的部分內(nèi)容。
非常感謝閱讀本文!
如果對本文內(nèi)容有任何問題或建議,歡迎在評論區(qū)留言或者聯(lián)系我!
原文鏈接:https://www.kaggle.com/reppic/predicting-english-pronunciations/notebook
號外號外~
一個(gè)專注于
AI技術(shù)發(fā)展和AI工程師成長的求知求職社區(qū)
誕生啦!
歡迎大家訪問以下鏈接或者掃碼體驗(yàn)
https://club.leiphone.com/page/home
雷鋒網(wǎng)雷鋒網(wǎng)
雷峰網(wǎng)原創(chuàng)文章,未經(jīng)授權(quán)禁止轉(zhuǎn)載。詳情見轉(zhuǎn)載須知。