作者:ARAVIND PAI
翻譯:吳金笛
校對(duì):和中華
本文長(zhǎng)度為6800字,建議閱讀15分鐘
本文手把手帶你使用Python編寫(xiě)一個(gè)自動(dòng)生成音樂(lè)的模型。
總覽
- 學(xué)習(xí)如何開(kāi)發(fā)一個(gè)自動(dòng)生成音樂(lè)的端到端模型
- 理解WaveNet架構(gòu)并使用Keras從零開(kāi)始實(shí)現(xiàn)它
- 在建立自動(dòng)音樂(lè)生成模型的同時(shí),比較了WaveNet和Long-Short-Term Memory的性能
介紹
“如果我不是物理學(xué)家,我可能會(huì)成為音樂(lè)家。我經(jīng)常在音樂(lè)中思考。我活在音樂(lè)的白日夢(mèng)里。我從音樂(lè)的角度來(lái)看待我的生活。”——阿爾伯特·愛(ài)因斯坦
我可能不是愛(ài)因斯坦先生那樣的物理學(xué)家,但我完全同意他對(duì)音樂(lè)的看法!我不記得有哪一天我沒(méi)有打開(kāi)音樂(lè)播放器。我上下班都伴隨著音樂(lè)的旋律,老實(shí)說(shuō),它幫助我專(zhuān)注于工作。
我一直夢(mèng)想著作曲,但卻不懂樂(lè)器。直到我遇到了深度學(xué)習(xí),這一切都成了過(guò)去。使用某些技術(shù)和框架,我能夠在不了解任何樂(lè)理的情況下創(chuàng)作自己的原創(chuàng)音樂(lè)樂(lè)譜!
這是我最喜歡的專(zhuān)業(yè)項(xiàng)目之一。我結(jié)合了兩個(gè)愛(ài)好——音樂(lè)和深度學(xué)習(xí)——創(chuàng)建了一個(gè)自動(dòng)的音樂(lè)生成模型。夢(mèng)想成真了!
我很高興與你分享我的方法,包括使你也能夠生成原創(chuàng)音樂(lè)的全部代碼!首先,我們將快速理解自動(dòng)音樂(lè)生成的概念,然后再深入了解用于執(zhí)行此操作的不同方法。最后,我們將啟用Python并設(shè)計(jì)我們自己的自動(dòng)音樂(lè)生成模型。
目錄
1. 什么是音樂(lè)自動(dòng)生成?
2. 音樂(lè)的組成要素是什么?
3. 生成音樂(lè)的不同方法
- 使用WaveNet架構(gòu)
- 使用Long-Short-Term Memory(LSTM)
4. 實(shí)現(xiàn)——使用python進(jìn)行自動(dòng)作曲
什么是音樂(lè)自動(dòng)生成?
“音樂(lè)是一種藝術(shù),是一種通用的語(yǔ)言。”
我把音樂(lè)定義為不同頻率的音調(diào)的集合。因此,自動(dòng)音樂(lè)生成是一個(gè)用最少的人為干預(yù)來(lái)創(chuàng)作一首短曲的過(guò)程。
產(chǎn)生音樂(lè)最簡(jiǎn)單的形式是什么?
這一切都是從隨機(jī)選擇聲音并將它們組合成一段音樂(lè)開(kāi)始的。1787年,莫扎特為這些隨機(jī)聲音的選擇提出了骰子游戲。他手動(dòng)合成了近272個(gè)音調(diào)!然后,他根據(jù)兩個(gè)骰子點(diǎn)數(shù)之和選擇一個(gè)音調(diào)。
另一個(gè)有趣的想法是利用音樂(lè)語(yǔ)法來(lái)生成音樂(lè)。
“音樂(lè)語(yǔ)法是指對(duì)音樂(lè)聲音的合理安排和組合以及對(duì)音樂(lè)作品的正確表現(xiàn)所必需的知識(shí)”
-《音樂(lè)語(yǔ)法基礎(chǔ)》
在20世紀(jì)50年代早期,Iannis Xenakis使用統(tǒng)計(jì)學(xué)和概率的概念來(lái)創(chuàng)作音樂(lè)——通常被稱為隨機(jī)音樂(lè)(Stochastic Music)。他將音樂(lè)定義為偶然出現(xiàn)的一系列元素(或聲音)。因此,他用隨機(jī)理論來(lái)表述它。他對(duì)元素的隨機(jī)選擇完全依賴于數(shù)學(xué)概念。
最近,深度學(xué)習(xí)架構(gòu)已經(jīng)成為自動(dòng)音樂(lè)生成的最新技術(shù)。在本文中,我將討論使用WaveNet和LSTM(Long-Short-Term Memory)架構(gòu)來(lái)實(shí)現(xiàn)自動(dòng)作曲的兩種不同方法。
注意:本文需要對(duì)一些深度學(xué)習(xí)概念有基本的理解。我建議閱讀以下文章:
- 從頭開(kāi)始學(xué)習(xí)卷積神經(jīng)網(wǎng)絡(luò)(CNNs)的全面教程
https://www.analyticsvidhya.com/blog/2018/12/guide-convolutional-neural-network-cnn/?utm_source=blog&utm_medium=how-to-perform-automatic-music-generation
- 深度學(xué)習(xí)要領(lǐng):長(zhǎng)短期記憶(LSTM)入門(mén)
https://www.analyticsvidhya.com/blog/2017/12/fundamentals-of-deep-learning-introduction-to-lstm/?utm_source=blog&utm_medium=how-to-perform-automatic-music-generation
- 學(xué)習(xí)序列建模的必讀教程
https://www.analyticsvidhya.com/blog/2019/01/sequence-models-deeplearning/?utm_source=blog&utm_medium=how-to-perform-automatic-music-generation
音樂(lè)的組成要素是什么?
音樂(lè)本質(zhì)上是由音符和和弦組成的。讓我從鋼琴的角度來(lái)解釋這些術(shù)語(yǔ):
- 音符:?jiǎn)捂I發(fā)出的聲音稱為音符
- 和弦:由兩個(gè)或更多的鍵同時(shí)發(fā)出的聲音稱為和弦。一般來(lái)說(shuō),大多數(shù)和弦包含至少3個(gè)鍵音
- 八度:重復(fù)的模式稱為八度。每個(gè)八度包含7個(gè)白鍵和5個(gè)黑鍵
自動(dòng)生成音樂(lè)的不同方法
我將詳細(xì)討論兩個(gè)基于深度學(xué)習(xí)的自動(dòng)生成音樂(lè)的架構(gòu)——WaveNet和LSTM。但是,為什么只有深度學(xué)習(xí)架構(gòu)?
深度學(xué)習(xí)是一個(gè)受神經(jīng)結(jié)構(gòu)啟發(fā)的機(jī)器學(xué)習(xí)領(lǐng)域。這些網(wǎng)絡(luò)自動(dòng)從數(shù)據(jù)集中提取特征,并能夠?qū)W習(xí)任何非線性函數(shù)。這就是為什么神經(jīng)網(wǎng)絡(luò)被稱為泛函逼近器(感覺(jué)泛函在數(shù)學(xué)上的定義不適合這里,網(wǎng)上也有人翻譯為 萬(wàn)能函數(shù)擬逼近器,因?yàn)樽钤缬幸黄P(guān)于 Universal Approximation Theorem的論文)。
因此,深度學(xué)習(xí)模型是自然語(yǔ)言處理(NLP)、計(jì)算機(jī)視覺(jué)、語(yǔ)音合成等各個(gè)領(lǐng)域的最新技術(shù)。讓我們來(lái)看看如何構(gòu)建這些作曲模型。
方法一:使用WaveNet
“WaveNet是谷歌DeepMind開(kāi)發(fā)的一種基于深度學(xué)習(xí)的原始音頻生成模型。”
WaveNet的主要目的是從原始的數(shù)據(jù)分布中生成新的樣本。因此,它被稱為生成模型。
“WaveNet就像是NLP中的一種語(yǔ)言模型。”
在語(yǔ)言模型中,給定一個(gè)單詞序列,該模型試圖預(yù)測(cè)下一個(gè)單詞。與語(yǔ)言模型類(lèi)似,在WaveNet中,給定一系列樣本,它試圖預(yù)測(cè)下一個(gè)樣本。
方法二:使用Long-Short-Term Memory(LSTM)模型
Long-Short-Term Memory (LSTM)是遞歸神經(jīng)網(wǎng)絡(luò)(RNNs)的一種變體,能夠捕獲輸入序列中的長(zhǎng)期依賴關(guān)系。LSTM在序列到序列(Seq2Seq)建模任務(wù)中有廣泛的應(yīng)用,如語(yǔ)音識(shí)別、文本摘要、視頻分類(lèi)等。
讓我們?cè)敿?xì)討論如何使用這兩種方法訓(xùn)練我們的模型。
WaveNet:訓(xùn)練階段
“這是一個(gè)多對(duì)一的問(wèn)題,其中輸入是一系列振幅值,輸出是后續(xù)值。”
讓我們看看如何準(zhǔn)備輸入和輸出序列。
WaveNet的輸入:
WaveNet將原始音頻波的小塊作為輸入。原始音頻波是指波在時(shí)間序列域中的表示。
在時(shí)間序列域中,音頻波以不同時(shí)間間隔音符的振幅值的形式表示:
WaveNet的輸出:
給定振幅值的序列,WaveNet試圖預(yù)測(cè)連續(xù)的振幅值。
讓我們通過(guò)一個(gè)示例來(lái)理解。考慮一個(gè)5秒的音頻波,采樣率為16,000(即每秒16,000個(gè)樣本)。現(xiàn)在,我們有8萬(wàn)個(gè)樣本在5秒內(nèi)以不同的時(shí)間間隔記錄下來(lái)。讓我們把音頻分成相同大小的塊,比如1024(這是一個(gè)超參數(shù))。
下圖展示了模型的輸入和輸出序列:
前3個(gè)塊的輸入和輸出
對(duì)于其余的塊,我們可以遵循類(lèi)似的過(guò)程。
從上面我們可以推斷出,每個(gè)塊的輸出只依賴于過(guò)去的信息(即以前的時(shí)間步長(zhǎng)),而不依賴于未來(lái)的時(shí)間步長(zhǎng)。因此,該任務(wù)稱為自回歸任務(wù),該模型稱為自回歸模型。
推理階段
在推理階段,我們將嘗試生成新的樣本。讓我們看看怎么做:
1. 選擇一個(gè)隨機(jī)的樣本值數(shù)組作為建模的起點(diǎn)
2. 現(xiàn)在,模型輸出所有樣本的概率分布
3. 選擇概率最大的值并將其追加到先前的樣本值數(shù)組中
4. 刪除第一個(gè)元素并作為下一個(gè)迭代的輸入傳入模型
5. 重復(fù)步驟2和4,進(jìn)行一定次數(shù)的迭代
理解WaveNet架構(gòu)
WaveNet的基本結(jié)構(gòu)是因果擴(kuò)散的一維卷積層。首先讓我們了解相關(guān)概念的重要性。
為什么使用卷積,什么是卷積?
“使用卷積的一個(gè)主要原因是從輸入中提取特征。”
例如,在圖像處理的情況下,用過(guò)濾器對(duì)圖像進(jìn)行卷積可以得到一個(gè)特征圖。
卷積是一種結(jié)合了兩個(gè)函數(shù)的數(shù)學(xué)運(yùn)算。在圖像處理的情況下,卷積是圖像的某些部分與核(kernel)的線性組合。
你可以瀏覽下面的文章閱讀更多關(guān)于卷積的知識(shí):
- 卷積神經(jīng)網(wǎng)絡(luò)(CNNs)結(jié)構(gòu)的解密
https://www.analyticsvidhya.com/blog/2017/06/architecture-of-convolutional-neural-networks-simplified-demystified/?utm_source=blog&utm_medium=how-to-perform-automatic-music-generation
什么是一維卷積?
一維卷積的目標(biāo)類(lèi)似于長(zhǎng)短期記憶模型。它用于解決類(lèi)似于LSTM的任務(wù)。在一維卷積中,核或者叫過(guò)濾器只沿著一個(gè)方向運(yùn)動(dòng):
卷積的輸出取決于核的大小、輸入形狀、填充類(lèi)型和步長(zhǎng)。現(xiàn)在,我將帶領(lǐng)你們了解不同類(lèi)型的填充來(lái)理解使用擴(kuò)張的1D因果卷積層的重要性。
當(dāng)我們將填充設(shè)置為valid時(shí),輸入和輸出序列的長(zhǎng)度會(huì)發(fā)生變化。輸出長(zhǎng)度小于輸入長(zhǎng)度:
當(dāng)我們將填充設(shè)置為same時(shí),在輸入序列的兩側(cè)填充零以使輸入和輸出的長(zhǎng)度相等:
一維卷積的優(yōu)點(diǎn):
- 捕獲輸入序列中出現(xiàn)的序列信息
- 與GRU或LSTM相比,訓(xùn)練的速度要快得多,因?yàn)樗鼈儧](méi)有循環(huán)性的連接
一維卷積的缺點(diǎn):
- 當(dāng)填充設(shè)置為same時(shí),在時(shí)間步長(zhǎng)t處的輸出也與之前的t-1和未來(lái)的時(shí)間步長(zhǎng)t+1進(jìn)行卷積。因此,它違反了自回歸原則
- 當(dāng)填充被設(shè)置為valid時(shí),輸入和輸出序列的長(zhǎng)度會(huì)發(fā)生變化,這是計(jì)算殘差連接所需要的(后面會(huì)講到)
這為因果卷積掃清了道路。
注意: 我在這里提到的利弊是針對(duì)于此問(wèn)題的。
什么是一維因果卷積?
它被定義為這樣一種卷積,即t時(shí)刻的輸出僅與t時(shí)刻以及前一層更早的元素進(jìn)行卷積。
簡(jiǎn)單地說(shuō),正常卷積和因果卷積的區(qū)別僅僅在于填充。在因果卷積中,僅在輸入序列的左邊加0,以保持自回歸的原則:
因果一維卷積的優(yōu)點(diǎn):
- 因果卷積沒(méi)有考慮未來(lái)的時(shí)間步長(zhǎng),而這是建立生成模型的一個(gè)標(biāo)準(zhǔn)
因果一維卷積的缺點(diǎn):
- 因果卷積不能回溯到序列中過(guò)去發(fā)生的時(shí)間步長(zhǎng)。因此,因果卷積的接受域非常低。網(wǎng)絡(luò)的接受域是指影響輸出的輸入數(shù)量:
如你所見(jiàn),輸出只受5個(gè)輸入的影響。因此,網(wǎng)絡(luò)的接受域?yàn)?,非常低。網(wǎng)絡(luò)的接受域也可以通過(guò)增加大尺寸的核來(lái)增加,但是要記住,這樣一來(lái)計(jì)算復(fù)雜度也會(huì)增加。
這將為我們引出擴(kuò)張一維因果卷積的絕佳概念。
什么是擴(kuò)張一維因果卷積?
“在核的值之間有孔或空缺的因果一維卷積層稱為擴(kuò)張的一維卷積。”
所增加的空缺數(shù)由擴(kuò)張率決定。它定義了網(wǎng)絡(luò)的接受域。大小為k、擴(kuò)張率為d的核在核k的每個(gè)值之間都有d-1個(gè)孔。
如你所見(jiàn),將一個(gè)3 * 3的核與一個(gè)7 * 7的輸入,以擴(kuò)張率為2進(jìn)行卷積,最終接受域?yàn)? * 5。
擴(kuò)張一維因果卷積的優(yōu)點(diǎn):
- 擴(kuò)張的一維卷積網(wǎng)絡(luò)通過(guò)指數(shù)增加每一隱藏層的擴(kuò)張率來(lái)增加接受域:
如你所見(jiàn),輸出受所有輸入的影響。因此,網(wǎng)絡(luò)的接受域?yàn)?6。
WaveNet的殘差塊:
為了加速模型的收斂,添加了殘差連接和跳躍連接的構(gòu)件:
WaveNet的工作流程:
- 輸入進(jìn)入一個(gè)因果一維卷積
- 輸出然后進(jìn)入到2個(gè)不同的擴(kuò)張一維卷積層并使用sigmoid和tanh激活
- 兩個(gè)不同激活值逐元素相乘導(dǎo)致跳躍連接
- 而跳躍連接和因果一維輸出的逐元素相加會(huì)導(dǎo)致殘差
Long Short Term Memory (LSTM)方法
另一種自動(dòng)生成音樂(lè)的方法是基于長(zhǎng)短期記憶(LSTM)模型。輸入和輸出序列的準(zhǔn)備類(lèi)似于WaveNet。在每一個(gè)時(shí)間步長(zhǎng),一個(gè)振幅值被輸入到長(zhǎng)短期記憶單元-然后它計(jì)算隱藏的向量,并把它傳遞到下一個(gè)時(shí)間步。
基于當(dāng)前的輸入a(t)和先前的隱藏向量h(t-1)來(lái)計(jì)算當(dāng)前時(shí)間的隱藏向量h(t)。序列信息在任何循環(huán)(一般recursive會(huì)翻譯為遞歸)神經(jīng)網(wǎng)絡(luò)中都是這樣捕獲的:
LSTM的優(yōu)點(diǎn):
- 捕獲輸入序列中出現(xiàn)的順序信息
LSTM的缺點(diǎn):
- 由于它是按順序處理輸入信息的,所以它在訓(xùn)練上會(huì)花費(fèi)大量的時(shí)間
實(shí)現(xiàn)-使用Python進(jìn)行自動(dòng)音樂(lè)生成
等待結(jié)束了!讓我們開(kāi)發(fā)一個(gè)用于自動(dòng)生成音樂(lè)的端到端模型。啟動(dòng)你的Jupyter
notebook或Colab(或任何你喜歡的IDE)。
下載數(shù)據(jù)集:
我從眾多資源中下載并組合了多個(gè)數(shù)字鋼琴(譯者注:Digital piano與電鋼琴Electric Piano的區(qū)別在于音源的產(chǎn)生方式)的古典音樂(lè)文件。你可以從這里下載最終的數(shù)據(jù)集。
(https://drive.google.com/file/d/1qnQVK17DNVkU19MgVA4Vg88zRDvwCRXw/view)
導(dǎo)入庫(kù):
Music 21是MIT開(kāi)發(fā)的用于理解音樂(lè)數(shù)據(jù)的Python庫(kù)。MIDI是存儲(chǔ)音樂(lè)文件的一種標(biāo)準(zhǔn)格式。MIDI代表樂(lè)器數(shù)字接口。MIDI文件包含說(shuō)明而不是實(shí)際的音頻。因此,它只占用很少的內(nèi)存。這就是為什么它在傳輸文件時(shí)通常是首選的。
1. #library for understanding music 2. from music21 import *
讀取音樂(lè)文件:
我們直接定義一個(gè)函數(shù)來(lái)讀取MIDI文件。它返回音樂(lè)文件中存在的音符和和弦的數(shù)組。
1. #defining function to read MIDI files 2. def read_midi(file): 3. 4. print("Loading Music File:",file) 5. 6. notes=[] 7. notes_to_parse = None 8. 9. #parsing a midi file 10. midi = converter.parse(file) 11. 12. #grouping based on different instruments 13. s2 = instrument.partitionByInstrument(midi) 14. 15. #Looping over all the instruments 16. for part in s2.parts: 17. 18. #select elements of only piano 19. if 'Piano' in str(part): 20. 21. notes_to_parse = part.recurse() 22. 23. #finding whether a particular element is note or a chord 24. for element in notes_to_parse: 25. 26. #note 27. if isinstance(element, note.Note): 28. notes.append(str(element.pitch)) 29. 30. #chord 31. elif isinstance(element, chord.Chord): 32. notes.append('.'.join(str(n) for n in element.normalOrder)) 33. 34. return np.array(notes)
現(xiàn)在,將MIDI文件加載到我們的環(huán)境中
1. #for listing down the file names 2. import os 3. 4. #Array Processing 5. import numpy as np 6. 7. #specify the path 8. path='schubert/' 9. 10. #read all the filenames 11. files=[i for i in os.listdir(path) if i.endswith(".mid")] 12. 13. #reading each midi file 14. notes_array = np.array([read_midi(path+i) for i in files])
理解數(shù)據(jù):
在本節(jié)中,我們將探索數(shù)據(jù)集并對(duì)其進(jìn)行詳細(xì)了解。
1. #converting 2D array into 1D array 2. notes_ = [element for note_ in notes_array for element in note_] 3. 4. #No. of unique notes 5. unique_notes = list(set(notes_)) 6. print(len(unique_notes))
輸出:304
如你所這里見(jiàn),不重復(fù)音符的數(shù)量是304。現(xiàn)在,讓我們看一下音符的分布。
1. #importing library 2. from collections import Counter 3. 4. #computing frequency of each note 5. freq = dict(Counter(notes_)) 6. 7. #library for visualiation 8. import matplotlib.pyplot as plt 9. 10. #consider only the frequencies 11. no=[count for _,count in freq.items()] 12. 13. #set the figure size 14. plt.figure(figsize=(5,5)) 15. 16. #plot 17. plt.hist(no)
輸出:
從上圖可以看出,大多數(shù)音符的頻率都很低。因此,我們保留最常用的音符,而忽略低頻率的音符。在這里,我將閾值定義為50。不過(guò),這個(gè)參數(shù)是可以更改的。
1. frequent_notes = [note_ for note_, count in freq.items() if count>=50] 2. print(len(frequent_notes))
輸出:167
如你在這里看到的,經(jīng)常出現(xiàn)的音符大約有170個(gè)。現(xiàn)在,讓我們準(zhǔn)備新的音樂(lè)文件,其中僅包含最常見(jiàn)的音符
1. new_music=[] 2. 3. for notes in notes_array: 4. temp=[] 5. for note_ in notes: 6. if note_ in frequent_notes: 7. temp.append(note_) 8. new_music.append(temp) 9. 10. new_music = np.array(new_music)
準(zhǔn)備數(shù)據(jù):
如文章中所述準(zhǔn)備輸入和輸出序列:
1. no_of_timesteps = 32 2. x = [] 3. y = [] 4. 5. for note_ in new_music: 6. for i in range(0, len(note_) - no_of_timesteps, 1): 7. 8. #preparing input and output sequences 9. input_ = note_[i:i + no_of_timesteps] 10. output = note_[i + no_of_timesteps] 11. 12. x.append(input_) 13. y.append(output) 14. 15. x=np.array(x) 16. y=np.array(y)
現(xiàn)在,我們將為每個(gè)音符分配一個(gè)唯一的整數(shù):
1. unique_x = list(set(x.ravel())) 2. x_note_to_int = dict((note_, number) for number, note_ in enumerate(unique_x))
我們將為輸入數(shù)據(jù)準(zhǔn)備整數(shù)序列
1. #preparing input sequences 2. x_seq=[] 3. for i in x: 4. temp=[] 5. for j in i: 6. #assigning unique integer to every note 7. temp.append(x_note_to_int[j]) 8. x_seq.append(temp) 9. 10. x_seq = np.array(x_seq)
同樣,也為輸出數(shù)據(jù)準(zhǔn)備整數(shù)序列
1. unique_y = list(set(y)) 2. y_note_to_int = dict((note_, number) for number, note_ in enumerate(unique_y)) 3. y_seq=np.array([y_note_to_int[i] for i in y])
讓我們保留80%的數(shù)據(jù)用于訓(xùn)練,其余20%的用于評(píng)估:
1. from sklearn.model_selection import train_test_split 2. x_tr, x_val, y_tr, y_val = train_test_split(x_seq,y_seq,test_size=0.2,random_state=0)
構(gòu)建模型
我在這里定義了2種架構(gòu)– WaveNet和LSTM。請(qǐng)嘗試兩種架構(gòu),以了解WaveNet架構(gòu)的重要性。
1. def lstm(): 2. model = Sequential() 3. model.add(LSTM(128,return_sequences=True)) 4. model.add(LSTM(128)) 5. model.add(Dense(256)) 6. model.add(Activation('relu')) 7. model.add(Dense(n_vocab)) 8. model.add(Activation('softmax')) 9. model.compile(loss='sparse_categorical_crossentropy', optimizer='adam') 10. return model
我簡(jiǎn)化了WaveNet的架構(gòu),沒(méi)有添加殘差連接和跳躍連接,因?yàn)檫@些層的作用是提高收斂速度(WaveNet以原始音頻波作為輸入)。但在我們的例子中,輸入是一組音符和和弦,因?yàn)槲覀冊(cè)谏梢魳?lè):
1. from keras.layers import * 2. from keras.models import * 3. from keras.callbacks import * 4. import keras.backend as K 5. 6. K.clear_session() 7. model = Sequential() 8. 9. #embedding layer 10. model.add(Embedding(len(unique_x), 100, input_length=32,trainable=True)) 11. 12. model.add(Conv1D(64,3, padding='causal',activation='relu')) 13. model.add(Dropout(0.2)) 14. model.add(MaxPool1D(2)) 15. 16. model.add(Conv1D(128,3,activation='relu',dilation_rate=2,padding='causal')) 17. model.add(Dropout(0.2)) 18. model.add(MaxPool1D(2)) 19. 20. model.add(Conv1D(256,3,activation='relu',dilation_rate=4,padding='causal')) 21. model.add(Dropout(0.2)) 22. model.add(MaxPool1D(2)) 23. 24. #model.add(Conv1D(256,5,activation='relu')) 25. model.add(GlobalMaxPool1D()) 26. 27. model.add(Dense(256, activation='relu')) 28. model.add(Dense(len(unique_y), activation='softmax')) 29. 30. model.compile(loss='sparse_categorical_crossentropy', optimizer='adam') 31. 32. model.summary()
定義回調(diào)以在訓(xùn)練期間保存最佳模型:
mc=ModelCheckpoint('best_model.h5', monitor='val_loss', mode='min', save_best_only=True,verbose=1)
讓我們使用128的批大小將模型訓(xùn)練50個(gè)epoch:
history = model.fit(np.array(x_tr),np.array(y_tr),batch_size=128,epochs=50,validation_data=(np.array(x_val),np.array(y_val)),verbose=1, callbacks=[mc])
導(dǎo)入最好的模型:
1. #loading best model 2. from keras.models import load_model 3. model = load_model('best_model.h5')
是時(shí)候創(chuàng)作我們自己的音樂(lè)了。我們將按照推斷階段中提到的步驟進(jìn)行預(yù)測(cè)。
1. import random 2. ind = np.random.randint(0,len(x_val)-1) 3. 4. random_music = x_val[ind] 5. 6. predictions=[] 7. for i in range(10): 8. 9. random_music = random_music.reshape(1,no_of_timesteps) 10. 11. prob = model.predict(random_music)[0] 12. y_pred= np.argmax(prob,axis=0) 13. predictions.append(y_pred) 14. 15. random_music = np.insert(random_music[0],len(random_music[0]),y_pred) 16. random_music = random_music[1:] 17. 18. print(predictions)
現(xiàn)在,我們將整數(shù)還原為音符。
1. x_int_to_note = dict((number, note_) for number, note_ in enumerate(unique_x)) 2. predicted_notes = [x_int_to_note[i] for i in predictions]
最后一步是將預(yù)測(cè)結(jié)果轉(zhuǎn)換回MIDI文件。讓我們定義一個(gè)函數(shù)來(lái)完成此任務(wù)。
1. def convert_to_midi(prediction_output): 2. 3. offset = 0 4. output_notes = [] 5. 6. # create note and chord objects based on the values generated by the model 7. for pattern in prediction_output: 8. 9. # pattern is a chord 10. if ('.' in pattern) or pattern.isdigit(): 11. notes_in_chord = pattern.split('.') 12. notes = [] 13. for current_note in notes_in_chord: 14. 15. cn=int(current_note) 16. new_note = note.Note(cn) 17. new_note.storedInstrument = instrument.Piano() 18. notes.append(new_note) 19. 20. new_chord = chord.Chord(notes) 21. new_chord.offset = offset 22. output_notes.append(new_chord) 23. 24. # pattern is a note 25. else: 26. 27. new_note = note.Note(pattern) 28. new_note.offset = offset 29. new_note.storedInstrument = instrument.Piano() 30. output_notes.append(new_note) 31. 32. # increase offset each iteration so that notes do not stack 33. offset += 1 34. midi_stream = stream.Stream(output_notes) 35. midi_stream.write('midi', fp='music.mid')
將預(yù)測(cè)結(jié)果轉(zhuǎn)換為音樂(lè)文件:
convert_to_midi(predicted_notes)
奧利給,對(duì)嗎,但你的學(xué)習(xí)不該止步于此。請(qǐng)記住,我們已經(jīng)構(gòu)建了一個(gè)基準(zhǔn)(baseline)模型。
有很多方法可以進(jìn)一步提高模型的性能:
- 由于訓(xùn)練數(shù)據(jù)集的規(guī)模較小,我們可以對(duì)預(yù)訓(xùn)練的模型進(jìn)行微調(diào),以建立一個(gè)魯棒的系統(tǒng)
- 盡可能多地收集訓(xùn)練數(shù)據(jù),因?yàn)樯疃葘W(xué)習(xí)模型在更大的數(shù)據(jù)集上泛化更好
結(jié)語(yǔ)
深度學(xué)習(xí)在我們的日常生活中有著廣泛的應(yīng)用。解決任何問(wèn)題的關(guān)鍵步驟都是理解問(wèn)題陳述、闡明并定義解決問(wèn)題的結(jié)構(gòu)。
我在這個(gè)項(xiàng)目中得到了很多樂(lè)趣(和學(xué)習(xí))。音樂(lè)是我的激情所在,將深度學(xué)習(xí)與之結(jié)合是非常有趣的。
原文鏈接:
https://www.analyticsvidhya.com/blog/2020/01/how-to-perform-automatic-music-generation/
原文題目:
Want to Generate your own Music using Deep Learning? Here’s a Guide to do just that!
編輯:于騰凱
校對(duì):洪舒越
譯者簡(jiǎn)介
吳金笛,雪城大學(xué)計(jì)算機(jī)科學(xué)碩士一年級(jí)在讀。迎難而上是我最舒服的狀態(tài),動(dòng)心忍性,曾益我所不能。我的目標(biāo)是做個(gè)早睡早起的Cool Girl。
—完—
想要獲得更多數(shù)據(jù)科學(xué)領(lǐng)域相關(guān)動(dòng)態(tài),誠(chéng)邀關(guān)注清華-青島數(shù)據(jù)科學(xué)研究院官方微信公眾平臺(tái)“ 數(shù)據(jù)派THU ”。