寫詞編曲:如何用NLP讓AI變身文藝青年

友情提示:結尾有個價值10萬元的福利機會!python

本文會展現基於RNN的生成模型如何編寫歌詞和創做鋼琴樂曲。git

咱們會利用一些最流行的歌星的歌詞數據集訓練一個RNN字符級語言模型。藉助訓練過的模型,咱們將採樣幾首歌曲,將歌星們不一樣風格的做品進行有趣的混搭。以後,咱們將模型更新成爲一個RNN字符級條件模型,使咱們可以對歌星們的歌曲進行有條件的採樣。最後,咱們會用由鋼琴曲組成的MIDI數據集訓練咱們的模型,得出一些有趣的結論。在解決以上這些任務時,咱們會簡要探討一些與RNN訓練和推斷相關的有趣概念,好比字符級RNN,條件字符級RNN,從RNN採樣,經過時間和梯度檢查點截斷反向傳播。全部的代碼和訓練模型均可以在github上得到,並在Pytorch上實現(代碼地址見文末)。固然了,若是你已經很熟悉字符級語言模型和循環神經網絡,能夠直接跳過這部分理論講解,查看後面的結果部分。github

在選擇模型以前,先來仔細看看咱們的任務。咱們將嘗試根據當前的字符和前面的全部字符,來預測下一個字符。在訓練過程當中,咱們只須要一個序列,使用除最後一個字符之外的全部字符做爲輸入,以及用從第二個字符開始的同一序列做爲參考實值(參見上圖;來源)。咱們將從最簡單的模型開始,它在作出預測的時候會忽略前面的全部字符,而後改進此模型,讓它僅考慮必定數量的先前字符,最後將模型優化爲一個能考慮全部前面字符的模型。spring

咱們的語言模型定義在字符級別。咱們將建立一個字典,包含全部英文字符和一些特殊符號,如句點,逗號和行尾符號。咱們會以獨熱編碼張量的形式表示每一個字符。有關字符級模型和示例的更多信息,推薦這篇資源。bash

有了字符後,咱們能夠生成字符序列。即便是如今,咱們也能夠經過以固定機率對字符進行隨機抽樣來生成句子。這是最簡單的字符級語言模型。咱們能夠作得更好嗎?固然能夠,咱們能夠從訓練語料庫中計算每一個字母的出現機率(一個字母出現的次數除以咱們的數據集的大小),而且用這些機率隨機抽樣。這個模型性能更好可是它徹底忽略了每一個字母的相對位置。例如,想一想你是怎麼讀單詞的:你閱讀前一個字母的時候一般很難預測下一個字母,可是當你讀到單詞的末尾時,你有時能猜到下一個字母是什麼。當你閱讀任何單詞時,你都潛在地使用了一些經過閱讀其餘文本學習到的規則:例如,你所讀單詞中每增多一個字母,空格字符的機率就會增長(真正很長的單詞是罕見的),或者在字母"r"以後一般跟隨元音,其出現輔音的機率就會變低。咱們但願咱們的模型可以從數據中學習這些相似的規則。而爲了讓咱們的模型有機會學習這些規則,咱們須要擴展模型的能力。網絡

咱們一點點的改進模型,讓每一個字母的機率只取決於先前出現的字母(馬爾科夫假設)。基本上咱們能實現這點。這是一個馬爾科夫鏈模型。若是你對馬爾科夫鏈不熟悉,能夠看看咱們的這篇專欄架構

咱們還能夠利用訓練數據預估機率的分佈情況。但這個模型有侷限性,由於大多數狀況下,當前字母的機率不只僅取決於前一個字母。app

咱們的建模任務起初看起來很棘手,由於前面的字母數量是可變的,並且在序列很長的狀況下字母數量可能會很大。結果代表,在必定程度上,利用共享權重和固定大小的隱藏狀態,循環神經網絡能夠解決這個問題,這就引出了咱們接下來要討論的一個部分——循環神經網絡。函數

循環神經網絡(RNN)是一種用於處理序列數據的神經網絡。與前饋神經網絡不一樣,RNN可使用其內部存儲器來處理任意輸入序列。因爲任意大小的輸入序列,它們被簡潔地描述爲一個具備循環週期的圖(見上圖:源)。可是,若是已知輸入序列的大小,它就能夠被"展開"。定義一個非線性映射,從當前輸入xt 和先前隱藏狀態 st−1 到輸出ot 和隱藏狀態 st。隱藏狀態大小具備預約義的大小,而且存儲每一步更新的特徵,並影響映射的結果。工具

如今,將字符級語言模型的前一張圖片與摺疊的RNN圖片對齊,瞭解咱們如何使用RNN模型來學習字符級語言模型。

雖然圖片描繪了基礎的RNN架構,可是咱們後面會使用LSTM,由於它更容易訓練,而且一般能夠得到更好的結果。

歌詞數據集

咱們選擇了55000+ Song Lyrics Kaggle 數據集,其中包含了不少近期的藝歌星做品和一些經典做品。咱們將其存儲爲pandas文件,並用python封裝,以便用於訓練。 爲了能更好地解釋結果,咱們選取其中一些歌星做品子集做爲訓練集:

artists = [
'ABBA',
'Ace Of Base',
'Aerosmith',
'Avril Lavigne',
'Backstreet Boys',
'Bob Marley',
'Bon Jovi',
'Britney Spears',
'Bruno Mars',
'Coldplay',
'Def Leppard',
'Depeche Mode',
'Ed Sheeran',
'Elton John',
'Elvis Presley',
'Eminem',
'Enrique Iglesias',
'Evanescence',
'Fall Out Boy',
'Foo Fighters',
'Green Day',
 'HIM',
 'Imagine Dragons',
 'Incubus',
 'Jimi Hendrix',
 'Justin Bieber',
 'Justin Timberlake',
'Kanye West',
 'Katy Perry',
 'The Killers',
 'Kiss',
 'Lady Gaga',
 'Lana Del Rey',
 'Linkin Park',
 'Madonna',
 'Marilyn Manson',
 'Maroon 5',
 'Metallica',
 'Michael Bolton',
 'Michael Jackson',
 'Miley Cyrus',
 'Nickelback',
 'Nightwish',
 'Nirvana',
 'Oasis',
 'Offspring',
 'One Direction',
 'Ozzy Osbourne',
 'P!nk',
 'Queen',
 'Radiohead',
 'Red Hot Chili Peppers',
 'Rihanna',
 'Robbie Williams',
 'Rolling Stones',
 'Roxette',
 'Scorpions',
 'Snoop Dogg',
 'Sting',
 'The Script',
 'U2',
 'Weezer',
 'Yellowcard',
 'ZZ Top']
複製代碼

訓練無條件的字符級語言模型

第一個實驗是在整個語料庫上訓練咱們的字符級語言模型RNN。在訓練時,咱們沒有考慮歌星們的信息。

從RNN採樣

在訓練完模型以後,咱們試着抽出幾首歌。基本上,RNN每一步都會輸出logits,咱們能夠利用softmax函數從分佈中取樣。或者能夠直接使用Gumble-Max技巧採樣,這和直接使用logits是同樣的。 抽樣的一個有趣之處是,咱們能夠對輸入序列進行部分定義,並在初始條件下開始採樣。舉個例子,咱們採樣以"Why"開頭的歌曲:

Why do you have to leave me?
I think I know I'm not the only one
I don't know if I'm gonna stay awake
I don't know why I go along
I don't know why I can't go on
I don't know why I don't know
I don't know why I don't know
I don't know why I keep on dreaming of you

嗯,這聽起來倒還像是一首歌。

那咱們來看一下以「Well」開頭的歌曲:

Well, I was a real good time
I was a rolling stone
I was a rock and roller
Well, I never had a rock and roll
There were times I had to do it
I had a feeling that I was found
I was the one who had to go

採樣過程當中使用了"temperature"參數控制採樣過程的隨機性。當這個參數接近零時,採樣等於argmax,當它接近無窮時,採樣等同於均勻分佈的採樣。能夠看一下相關論文中的圖(Jang et al):

當τ=10時,分佈不受影響。若是咱們減小 τ,分佈變得更加明顯,意味着有更大機率質量的值會增長。當τ 接近0時,採樣將至關於argmax,由於該值的機率接近於1。當咱們開始增長τ 時,分佈變得愈來愈均勻。

以前的樣本是在"temperature"參數等於0.5的狀況下生成的,咱們來看看當咱們增長到1.0時會發生什麼:

Why can't we drop out of time?
We were born for words to see.
Won't you love this. You're still so amazing.
This could be that down on Sunday Time.
Oh, Caroline, a lady floor.
I thought of love, oh baby.

咱們進一步增長:

Why - won't we grow up naked?
We went quietly what we would've still give
That girl you walked before our bedroom room
I see your mind is so small to a freak
Stretching for a cold white-heart of crashing
Truth in the universal daughter

I lose more and more hard
I love you anytime at all
Ah come let your help remind me
Now I've wanted waste and never noticed

I swear I saw you today
You needed to get by
But you sold a hurricane
Well out whispered in store

咱們爲何不直接一次性增長呢?

那麼,你能夠看到這樣的趨勢:當咱們增長"temperature"時,採樣的句子變得愈來愈隨機。

訓練有條件的字符級語言模型

想象一下,若是咱們可以以某個特定歌星的風格生成歌詞。咱們改變一下模型,以便在訓練期間使用歌星風格這個信息。 咱們能夠經過爲循環神經網絡增長一個額外的輸入來作到這一點。到目前爲止,咱們的RNN模型每一步只接受含有獨熱編碼字符的張量。

咱們的模型擴展會很是簡單:咱們將會用一個額外的獨熱編碼張量來表明歌星。因此每一步RNN都會接受一個張量,這個張量由表明字符和歌星的連續張量組成。

從條件語言模型RNN中抽樣 訓練結束後,咱們抽取了一些歌星的歌曲,部分結果以下所示: HIM

My fears
And the moment don't make me sing
So free from you
The pain you love me yeah
Whatever caused the warmth
You smile you're happy
You sit away
You say it's all in vain

彷佛真的有可能,尤爲是使用了痛苦這個詞,這在不少歌星的歌詞中很是常見。

ABBA

Oh, my love it makes me close a thing
You've been heard, I must have waited
I hear you
So I say
Thank you for the music, that makes me cry
And you moving my bad as me, ah-hang wind in the hell
I was meant to be with you, I'll never be playing up

Bob Marley:

Mercy on judgment, we got so much
Alcohol, cry, cry, cry
Why don't try to find our own
I want to know, Lord, I wanna give you
Just saving it, learned
Is there any more?
All that damage done
That's all reason, don't worry
Need a hammer
I need you more and more

Coldplay:

Look at the stars
Into life matter where you lay
Saying no doubt
I don't want to fly
In my dreams and fight today I will fall for you
All I know
And I want you to stay
Into the night
I want to live waiting
With my love and always
Have I wouldn't wasted
Would it hurt you

Kanye West:

I'm everywhere for you
The way that it couldn't stop
I mean it too late and love I made in the world
I told you so I took the studs full cold-stop
The hardest stressed growin'
The hustler raisin' on my tears
I know I'm true, one of your love

看起來很酷,但請記住,咱們尚未查看模型的驗證準確率,因此一些樣本行可能已經被rnn模型記住了。一個更好的方法是選擇在訓練期間得到最高驗證分數的模型(見下一節咱們用這種方式進行訓練的代碼)。咱們也注意到了一件有趣的事情:當你想用一個指定的起始字符串進行採樣時,無條件模型一般更好地表現出來。咱們的直覺是,當從一個具備特定起始字符串的條件模型中抽樣時,咱們實際上把兩個條件放在了咱們的模型開始字符串和一個歌星之間。並且咱們沒有足夠的數據來模擬這個條件分佈(每一個歌星的歌曲數量相對有限)。

這裏的代碼和模型都是可用的,即便沒有gpu,你也能夠從咱們訓練好的模型中採樣歌曲,由於它計算量並不大。

MIDI數據集

接下來,咱們將使用由大約700首鋼琴歌曲組成的小型midi數據集。咱們使用了Nottingam鋼琴數據集(僅限於訓練分割)。

任何MIDI文件均可以轉換爲鋼琴鍵軸,這只是一個時頻矩陣,其中每一行是不一樣的MIDI音高,每一列是不一樣的時間片。所以,咱們數據集中的每首鋼琴曲都會被表示成一個大小矩陣,其中個數字是鋼琴曲的音高。下圖是一個鋼琴鍵軸矩陣的例子:

即便對於一個不熟悉音樂理論的人來講,這種表現方式也很直觀,容易理解。每行表明一個音高:高處的行表明低頻音高,低處的行表明高頻音高。另外,咱們有一個表明時間的橫軸。因此若是咱們在必定時間內播放必定音調的聲音,咱們會看到一條水平線。

如今,咱們來看看字符級模型和新任務之間的類似之處。在目前的狀況下,根據之前播放過的全部音調,咱們須要預測下一個時間步將要播放的音調。因此,若是你看一下鋼琴鍵軸的圖,每一列表明某種音樂字符,咱們想根據全部之前的音樂字符預測下一個音樂字符。咱們注意一下文字字符與音樂字符的區別。回憶一下,語言模型中的每一個字符都是由獨熱向量表示的(意思是向量中只有一個值是1,其餘都是0)。對於音樂字符,能夠一次按下多個鍵(由於咱們正在處理多調數據集)。在這種狀況下,每一個時間步將由一個能夠包含多個1的向量表示。

訓練音高水平的鋼琴音樂模型

在開始訓練以前,咱們須要調整咱們用於語言模型的損失函數,來講明上個部分討論的不一樣輸入。在語言模型中,咱們在每一個時間步上都有獨熱編碼張量(字符級)做爲輸入,用一個獨熱編碼張量做爲輸出(預測的下一個字符)。因爲預測的下一個字符時使用獨佔,咱們使用交叉熵損失。

可是如今咱們的模型輸出一個再也不是獨熱編碼的向量(能夠按多個鍵)。固然,咱們能夠將全部可能的按鍵組合做爲一個單獨的類來處理,可是這是比較難作的。相反,咱們將輸出向量的每一個元素做爲一個二元變量(1表示正在按鍵,0表示沒有按鍵)。咱們將爲輸出向量的每一個元素定義一個單獨的損失爲二叉交叉熵。而咱們的最終損失將是求這些二元交叉熵的平均和。能夠閱讀代碼以得到更好的理解。 按照上述的修改後,咱們對模型進行了訓練。在下一節中,咱們會進行採樣並檢查結果。

從音調級RNN中採樣

在優化的早期階段,咱們採樣了鋼琴鍵軸:

能夠看到,模型正在開始一種常見模式,並且在咱們的數據集歌曲中也很常見:1首歌曲由2個不一樣的部分組成。第一部分包含一系列獨立播放的節奏,很是易辨,一般能夠吟唱(也稱爲旋律)。若是在採樣的鋼琴鍵軸圖上,這部分會在底部。若是觀察鋼琴卷軸的頂部,能夠看到一組一般一塊兒演奏的音高 ——這是伴隨着旋律的和聲或和音(在整個歌曲中一塊兒播放的部分)的進行。

訓練結束後,從模型中抽取樣本以下圖所示:

如圖所示,它們和前面章節中的所看到的真實狀況類似。

訓練結束後,抽取歌曲進行分析。咱們獲得一個有趣的做品樣本,戳這裏。而另外一個樣本,具備很好的風格轉換,戳這裏。同時,咱們生成了一些低速參數的例子,它們致使歌曲的速度慢了:這裏是第一首曲子,這是第二首曲子。點擊這裏能夠查看整個播放列表。

序列長度和相關問題

如今咱們從GPU內存消耗和速度的角度來看待咱們的問題。 咱們經過批量處理咱們的序列大大加快了計算速度。同時,隨着序列變長(取決於數據集),咱們的最大批量開始減小。爲何是這種狀況?當咱們使用反向傳播來計算梯度時,咱們須要存儲全部對內存消耗貢獻最大的中間激活量。隨着咱們的序列變長,咱們須要存儲更多的激活量,所以,咱們能夠在批次中用更少許的樣本。

有時候,咱們須要用很長的序列來工做,或者增長批次的大小,而你只有1個有少許內存的GPU。在這種狀況下,有多種可能的解決方案來減小內存消耗,這裏,咱們只提到兩種解決方案,它們之間須要作出取捨。

首先是一個截斷反向傳播。這個想法是將整個序列拆分紅子序列,並把它們分紅不一樣的批次,除了咱們按照拆分的順序處理這些批次,每個下一批次都使用前一批次的隱藏狀態做爲初始隱藏狀態。咱們提供了這種方法的實現,以便能更好地理解。這種方法顯然不等於處理整個序列,但它能讓更新更加頻繁,同時消耗更少的內存。另外一方面,咱們有可能沒法捕捉超過一個子序列的長期依賴關係。

第二個是梯度檢查點。這種方法使咱們有可能在使用更少內存的同時,在整個序列上訓練咱們的模型,以執行更多的計算。回想一下,以前咱們提到過訓練中的大部份內存資源會被激活值佔用。梯度檢查點的思想包括僅存儲每一個第n個激活值,並在稍後從新計算未保存的激活值。這個方法已經在TensorFlowPyTorch中實現。

結論和將來的工做

在上面的工做中,咱們訓練了簡單的文本生成模型,而後對模型進行了擴展以處理復調音樂,並簡要介紹了採樣如何工做以及temperature參數如何影響咱們的文本和音樂樣本 ——低temperature值提供了更穩定的結果,而高temperature值增長了更多的隨機性,這有時會產生很是有趣的樣本。

將來的工做能夠包括兩個方向——對訓練好的模型用於更多方面或進行更深刻的分析。例如,能夠將模型應用於Spotify收聽歷史記錄。在訓練完收聽歷史數據後,能夠給它一段前一小時左右收聽的歌曲序列,並在當天餘下時間爲你播放一個播放列表。另外,也能夠爲你的瀏覽歷史作一樣的事情,那麼這會是一個很酷的工具。能夠用來分析你的瀏覽行爲模式。在進行不一樣的活動(在健身房鍛鍊,在辦公室工做,睡覺)時,從手機中獲取加速度計和陀螺儀數據,並學習分類這些活動階段。以後,能夠根據本身的活動自動更改音樂播放列表(睡眠——舒緩的音樂,在健身房鍛鍊——奔放的音樂)。在醫學應用方面,模型能夠應用於基於脈搏和其餘數據檢測心臟問題。

分析訓練咱們的循環神經網絡生成音樂時的神經元激活情況,也會很是有意思。咱們能夠看到模型是否暗中學習了一些簡單的音樂概念(就像咱們對和聲和旋律的討論)。RNN的隱藏表示能夠用來彙集咱們的音樂數據集,從中找到類似的歌曲。

最後,咱們以無條件模型寫的一段歌詞結束本文吧:

The story ends
The sound of the blue
The tears were shining
The story of my life
I still believe
The story of my life

點擊這裏,查看本項目完整代碼。


價值10萬的福利機會來了

百度NLP智能問答大賽重磅來襲,10萬元獎金邀你來戰!

本次大賽會爲選手提供百度雲CPU與GPU計算資源,選手須要根據數據創建基於文本與問題,輸出正確答案的模型,考驗模型的概括總結與改述能力。

歡迎NLP領域感興趣的研究者及高校學生參與,一塊兒來挑戰NLP-人工通用智能吧!

賽事詳細信息及報名通道,請查看本文,10萬元獎金等你來拿!

相關文章
相關標籤/搜索