讓巴赫彈搖滾會是什麼樣的體驗?在最近谷歌主頁的Doodle上,我們可以嘗試一下。
3 月 21 日是著名音樂家約翰·塞巴斯蒂安·巴赫的生日,谷歌決定以一種特殊的方式向他致敬:讓人人都能以巴赫的風格創作自己的樂曲。
透過機器學習演算法,谷歌開發了 Coconet 多功能模型,可以讓你用巴赫的風格演奏自己寫下的樂譜。你也可以透過這個小工具來體驗 AI 演算法如何將一些我們熟悉的旋律「巴赫化」,亦或你和巴赫「合作」的樂曲將呈現出怎樣更加現代搖滾的曲風。
Coconet的工作原理
Coconet獲取不完整的樂譜,並填充缺失的材料。為了訓練Coconet,我們從巴赫的四聲部眾讚歌資料集中取例,隨意抹去一些音符,然後讓模型重寫。巴赫作曲與Coconet創作之間的差別為我們提供一個學習訊號,我們可以透過該訊號訓練模型。
透過隨意地抹去音符,我們希望得到一個可以處理任何不完整輸入的模型。在下文的「Coconet 為什麼可以運轉?」章節中,我們給出了該訓練流程有趣的解讀,將其等同於多種模型的同時訓練,並且每種模型適用於不同的場景。
我們將「樂譜」看做三維物體。巴赫的眾讚歌以四聲寫就,即女高音(S)、女低音(A)、男高音(T)和男低音(B)。每一種聲音的音樂透過鋼琴卷軸演奏:二維陣列(其中離散節拍水平延伸,音調垂直分佈)。我們假設每一種聲音在任何特定的時間內準確唱出一個音調。所以一般來講,對於每一時間點的每一種聲音而言,我們得到一個one-hot音調向量,並且除了指示被唱音調的單個元素以外,該向量的其餘元素皆為零。在不確定的情況下(如在模型輸出時),該音調向量將包含音調的分類機率分佈。
我們將這堆鋼琴卷軸視作卷積特徵圖,其中節拍和音調形成二維卷積空間,每一種聲音提供一個通道。由於要饋入模型的樂譜是不完整的,所以我們為每一種聲音提供帶掩碼的通道:二進位制在每一個時間點顯示聲音的音調是否可知。如此一來,進入模型的是八通道特徵圖。
該模型是一種相當簡單且具有批歸一化和殘差連線的卷積神經網路。對於在瀏覽器中使用Tensorflow.js.implementation執行模型的doodle而言,我們能夠透過轉換到深度可分離卷積(deepwise-separable convolution)來加速計算。這些卷積與常規卷積的不同之處在於前者將空間軸的卷積與通道軸上的混合卷積分離開來。此外,這些卷積需要的引數更少,也更易於在瀏覽器中加速。得益於空洞卷積,我們可以在不損失效能的情況下透過減少模型層數獲得進一步加速。
該模型再次為每一種聲音輸出一堆鋼琴卷軸,但這次包含抹去音符的音調的機率分佈。該模型試圖利用給定的音符來計算抹去的音符,從而在每個時間點上針對每一種聲音得到被唱音調的分類分佈。
我們訓練模型給真實音調分配高機率。這促使模型理解給定的不完整樂譜的音樂含義--在哪個調上、唱哪些和絃、去往哪裡以及從何而來?
訓練好模型後,我們有數種方法從該模型生成的機率分佈中提取音樂。與此同時,我們可以根據音調的分佈來對其進行取樣。但是,正如下文中「Coconet 為什麼可以運轉?」中的詳細描述,這並不能解釋被取樣的音調之間的互動。通常,確定一個音調將改變其他音調的分佈。
一種能夠解釋這些互動的方法是對其中一個音調進行取樣,將其新增到不完整的樂譜中,再次透過模型傳遞結果,從而對餘下音調的分佈進行再計算。重複這一過程直至確定所有音調,我們在完成樂譜的同時將所有互動考慮在內。這種連續取樣流程希望該模型能夠依次對未知音調進行精準確定。
相反,我們使用一種更穩健的流程:將模型輸出視為一個草圖,透過反覆重寫而逐步完善。與此同時,我們對所有音調進行取樣,獲得一個完整(但通常無任何意義)樂譜,之後將部分樂譜抹去,再次傳遞到模型中,再之後重複這一過程。隨著時間的推移,我們抹去和重寫的音符越來越少,使該流程得到一致的結果。
Coconet 為什麼可以運轉?
Coconet 是一個自迴歸結構的集合,包含了序列模型中常見的時間結構(chronological structure)。為了將演示簡單化,我們將考慮建模3個變數:X_1、X_2、X_3。具體來講,我們可以把這視為一個三音符旋律或三音和絃,每個變數以音高作為值。建模X_1、X_2 和 X_3 就是表徵和學習給定序列 X_1、X_2、X_3 在自然資料中出現可能性的聯合機率分佈 P(X_1,X_2,X_3)。
這是一個很難的問題,因為變數相互作用,光建模獨立(邊緣)分佈 P(X_1)、P(X_2)、P(X_3) 是不夠的。對於X_1的每個可能值,依賴於X_1值的其它變數存在條件分佈 P(X_2|X_1) 和 P(X_3|X_1)。如果有 P(X_1) 和 P(X_2|X_1) 的模型,那我們可以將它們組合起來獲得 P(X_1,X_2)=P(X_1)P(X_2|X_1) 的模型。而如果有 P(X_3|X_1,X_2) 的模型,我們可以將這三個模型組合起來獲得期望聯合分佈 P(X_1,X_2,X_3)=P(X_1)P(X_2|X_1)P(X_3|X_1,X_2)。
一次建模一個變數
上述因式分解對序列資料來說最自然不過,因為它遵循序列順序。而在單音音樂(monophonic music)中,這意味著每個音符的分佈是由指向它的音符決定的。這給出了正向排序 (1,2,3)。另一種自然的因式分解是逆向排序 (3,2,1):先建立結論,然後往前推導。如下圖所示:
一般來說,變數的每個可能的排序都存在自迴歸因式分解。在有N個變數的問題中,就存在 N! 個因式分解。在上面提到的三個變數的例子中,我們可以列舉出六個自迴歸因式分解:
複音音樂(polyphonic music)由多個同步序列組成:多個樂器一起演奏。在這種情況中,雖然有兩種明顯的方式展開多個序列,但變數沒有真正的自然排序。
在左圖中,我們交錯排列樂器,將其排序為S、A、T、B、S、A、T、B等。這種順序有利於和聲:模型會以一次生成一個和絃的方式生成音樂。在右圖中,我們以另一種方式將樂器連線起來,將其排序為S、S、S、S、A、A、A、A等。這種方式有利於調式,因為模型會一行接一行地生成音符。這兩種截然不同的觀點是導致音樂理論中常見衝突的根源。
無序建模
當我們將部分抹去的樂譜輸入至模型時,輸出的結果可以解釋為抹去變數的條件獨立分佈。回到三個變數序列 X_1、X_2、X_3 的例子,假設抹去了 X_2 和 X_3,然後模型觀測到 X_1 並生成 X_2 和 X_3 的條件分佈。
所以得到的條件分佈 P(X_2|X_1 )和 P(X_3|X_1) 作為三個變數2種排序(總共6種排序)中的兩個因子出現。通常,根據抹去的變數,我們可以從任何排序中計算任何條件因子。透過組合此類條件因子,我們可以形成對應於任何預期排序的模型。本質上,修復模型提供了一個自迴歸模型集合,每個可能的排序有一個自迴歸模型。
而且,我們可以更高效地訓練這個集合,而不是簡單地對排序進行取樣並逐個評估其條件因子。關鍵的觀測是每個條件在很多排序中是共享的:即使在低維樣本中,P(X_3|X_1,X_2) 也是由兩種排序 (1,2,3 和 2,1,3) 共享的。一般來說,所有這些條件分佈 P(X_i|X_C)(X_C 是變數的任何子集,不包括 X_i)都與 X_i 之前或之後的變數排序無關,這大大減少了我們需要學習的不同機率分佈的數量。
為了訓練 Coconet,我們從資料集中選擇了一個訓練樣本,統一選擇要抹去的變數數量,並統一選擇需要抹去的變數的特定子集。我們將部分抹去的樂譜輸入至模型(以及指示哪個變數需要抹去的掩碼),獲得了抹去變數值的一組獨立分佈。我們計算了真實值的對數似然和抹去變數的平均值,由此糾正了微妙的縮放問題。我們藉此得到損失函式,然後和以前一樣使用反向傳播和隨機梯度下降來最小化損失。
使用吉布斯取樣根據多個排序生成
儘管無序NADE學習一組排序,但相關的取樣過程仍然根據單個排序進行有效的取樣。Uria等人提出統一選擇一個排序,然後根據這個排序依次生成變數。作曲仍然是在一個單一的過程中完成的,沒有經過任何迭代的改進。
在一個單一的過程中作曲難在哪裡?假設從一張白紙開始,我們必須寫下第一個音符,而且知道之後不能改動。這是一個艱難的決定:我們必須考慮未來所有的可能,這個音符必須是正確的。接下來,有了更多的音符之後,我們就能參考前後的音符做出決定。那麼,如果我們不必從無到有地進行作曲呢?如果我們從一開始就有一些音符作參考呢?
事實證明,我們可以透過吉布斯取樣做到這一點!吉布斯取樣是透過反覆對單個變數重新取樣,從聯合分佈中抽取樣本的過程。可以用它來比喻反覆修改樂譜的過程。在每一步中,抹去樂譜的某些部分並讓模型重寫。這樣的話,模型一直都會有參考材料。儘管參考材料本身可能不斷變動,也可能在之後的迭代中被重寫。這沒關係,因為模型當前的決策也不是一成不變的。漸漸地,樂譜就成了一首和諧的曲子。
更確切地說,這個過程可以叫做分塊吉布斯取樣,因為每次重新取樣的不止一個變數。如果把機率分佈想象成一幅風景畫,可以看到處在合適位置的山峰被位置不當的巨大山谷隔開。大規模重新取樣有助於透過較大的跳躍來探索可能性空間,而一次重取樣一個變數往往會停留在附近的峰值上。因此,我們退火塊大小:我們開始重寫大部分的樂譜,以探索這個空間,然後逐漸重寫得越來越少,以確定一個合理的樂譜。