接下來的幾天我們會比較詳細的介紹幾個演算法是如何運用在作曲上,但在我們開始進入演算法作曲之前,我們要先來做一些基本觀念的建設以及對於基本的參數設定做一些介紹。
首先我們來複習一下前面介紹的吃泡麵演算法步驟:吹含吸舔摳
以上是一個演算法的基本步驟,就如同我們前面幾天介紹的各種AI有趣的演算法的基礎一般。
那我們今天要來聊的就是,如果我們要把這些演算法套到特定的領域/應用上(例如AI作曲),那麼我們要如何先針對問題來做一些定義以及參數的設定。
我們一樣以吃泡麵演算法來當作範例:
首先,我們已經有了基本的泡麵演算法,那麼我們希望用這個泡麵演算法達成一個特別的目標,例如:我希望吃到軟硬度適中的康師父排骨酥桶麵裡面還要有顆滷蛋
供參
(裡面有黑人我知道QQ)
那麼我們就要針對演算法去做一些定義,例如在:
第一個步驟時,我們就不能隨便買/找一包泡麵,而是一定要找到康師父排骨酥桶麵 ;
第二個步驟撕開包裝,基本上沒有任何影響 ;
第三個步驟加入調味料的時候,因為我們一定要吃到滷蛋,所以這個步驟一定要注意裡面有沒有滷蛋,有的話最好,沒有的話自己加 ;
第四個步驟倒入熱水,不影響 ;
第五個步驟,由於我們的目標是吃到軟硬度適中的麵,所以我們必須要設定好泡麵要加蓋後等多久,具體到底要等多久就看每個人自己心裡的那把尺 ;
第六個步驟,依照設定的目標吃到軟硬度適中的康師父排骨酥桶麵裡面還要有顆滷蛋,目標成功達成。
終於可以準備進入正題了
首先我們要先把作曲這件事情放進演算法裡面,因此我們必須要給電腦一個可以用來 表達音樂/音符/旋律的定義 (Representation) ; 並且要告訴電腦我們的 目標 是什麼,例如我們想要好聽的音樂? 我們想要中國風的音樂? 我們想要難聽的音樂?
(這部份很複雜,我們後面會在針對如何告訴電腦什麼是 好聽的音樂 做詳細的說明)
今天我們先把目標定為 希望產生出一組包含八個音符且裡面沒有任何一個音符是重複的
; 此外,也要針對演算法裡面的 運作方法 (Opertor) 去做特別的設定(就像我們對於要吃到軟硬適中的麵的話,就要去特別調整加蓋等待的時間)。
我們拿前幾天講解過的爬山法 (Hill Climbing) 來當例子:
首先我們先告訴電腦音樂長什麼樣子,我們可以用數字的方式來對於每一個音符做對應,而範圍限定在兩個八度,例如我們從中音的Do開始,把它設定為0 ; 而每往上一個半音,就增加1,以此類推,在最高音的部份會來到24,可以參考下面的對應圖。
那麼我們在初始化 (Initialization) 的時候,我們先亂數隨機產生一組八個音符當作起始位置,例如
對應到五線譜上就是
而我們的運作方法 (Opertor),在這裡要設定的就是每次可以移動到的鄰居的距離,我們把它設定為八個音符中照順序選擇後把該音符隨機轉換成另一個音符。
因次我們初始的音符[0, 0, 3, 7, 12, 10, 12, 4],產生了以下了八組鄰居:
產生出來第1 組鄰居[3, 0, 3, 7, 12, 10, 12, 4] => (第1個音符由0隨機轉換成3)
產生出來第2 組鄰居[0, 8, 3, 7, 12, 10, 12, 4] => (第2個音符由0隨機轉換成8)
產生出來第3 組鄰居[0, 0, 9, 7, 12, 10, 12, 4] => (第3個音符由3隨機轉換成9)
產生出來第4 組鄰居[0, 0, 3, 6, 12, 10, 12, 4] => (第4個音符由7隨機轉換成6)
產生出來第5 組鄰居[0, 0, 3, 7, 20, 10, 12, 4] => (第5個音符由12隨機轉換成20)
產生出來第6 組鄰居[0, 0, 3, 7, 12, 12, 12, 4] => (第6個音符由10隨機轉換成12)
產生出來第7 組鄰居[0, 0, 3, 7, 12, 10, 18, 4] => (第7個音符由12隨機轉換成18)
產生出來第8 組鄰居[0, 0, 3, 7, 12, 10, 12, 7] => (第8個音符由4隨機轉換成7)
接著我們依照我們定義好的目標希望產生出一組包含八個音符且裡面沒有任何一個音符是重複的
去計算初始音符的狀態以及八組鄰居的狀態,由於我們希望得到的是完全沒有重複音符的八個音符,因此我們只要把重複音符越多的部份當成缺點來計算就好(目標讓分數為0,表示0個重複音符),故我們可以得到
初始音符: [0, 0, 3, 7, 12, 10, 12, 4] => 分數(狀態)為4分 (4個重複音符)
第 1 鄰居: [3, 0, 3, 7, 12, 10, 12, 4] => 分數(狀態)為4分 (4個重複音符)
第 2 鄰居: [0, 8, 3, 7, 12, 10, 12, 4] => 分數(狀態)為2分 (2個重複音符)
第 3 鄰居: [0, 0, 9, 7, 12, 10, 12, 4] => 分數(狀態)為4分 (4個重複音符)
第 4 鄰居: [0, 0, 3, 6, 12, 10, 12, 4] => 分數(狀態)為4分 (4個重複音符)
第 5 鄰居: [0, 0, 3, 7, 20, 10, 12, 4] => 分數(狀態)為2分 (2個重複音符)
第 6 鄰居: [0, 0, 3, 7, 12, 12, 12, 4] => 分數(狀態)為5分 (5個重複音符)
第 7 鄰居: [0, 0, 3, 7, 12, 10, 18, 4] => 分數(狀態)為2分 (2個重複音符)
第 8 鄰居: [0, 0, 3, 7, 12, 10, 12, 7] => 分數(狀態)為6分 (6個重複音符)
由於目標是希望分數為0分,因此跟初始狀態的4分相比,
鄰居2號、5號以及7號是最好的結果 (2分) ;
鄰居1號、3號以及4號分數持平 (4分) ;
而最慘的就是8號了,裡面都沒洗乾淨還有沱屎 6個重複音符根本可謂惡鄰中的惡鄰。
在2號、5號以及7號一樣好的情況下,我們可以隨意選擇其中一組當作新的狀態,接著再持續重複以上的步驟,直到我們找到一組八個音符都沒有重複的狀態 (0分)為止。
至於好不好聽呢,由於我們的目標並非要產生好聽的音樂
,而是單純的只要沒有任何音符重複
就好,因此出來的音樂好不好聽就不是這次實驗的重點。
那麼今天花了很長的篇幅(超過五千字了= =+)來介紹我們在音樂與演算法之間要如何去做對應,希望能先藉由簡單的爬山法來讓大家理解整個運作的狀態,當然爬山法並不適合來做音樂作曲,也不是主流會運用在音樂上的演算法,但是看在它簡單易懂的分上,拿出來當作範例跟大家做介紹。
那麼明天開始,就來講講幾個主流的演算法到底是怎麼實做音樂作曲的。或是放棄