№ 014互動
三分鐘訓練你的 AI 作曲家:聽它從亂彈到療癒
在瀏覽器裡從零訓練一個會寫四部合唱的小 Transformer,邊練邊聽它從亂彈變成音樂。然後你當評審,點二十下把它調得更放鬆;也看它怎麼找到評審的漏洞。
- 發布
- 閱讀時間
- 7 分鐘
下面這個作曲家現在什麼都不會。按下訓練,它會在你的瀏覽器裡讀巴赫的 333 首四部合唱,一邊學,一邊在六個時刻即興一段給你聽,從第 0 步的亂彈開始。
猜下一個音的誤差
曲線是訓練時的誤差;虛線是 5-gram 在驗證資料上的成績(1.46),拿來和每個片段旁邊的驗證誤差比。
它在這些時刻即興了一段
按「開始訓練」,第 0 步的亂彈會出現在這裡。
女高音女低音男高音男低音
先說這不是什麼。這不是 Suno:它不會唱歌,也不產生聲音檔。它寫的是樂譜,「哪個聲部、什麼音、拖多長」,再由一個從零寫的合成器演奏出來。模型只有六萬五千個參數,權重檔 260 KB,大約是一首三分鐘 MP3 的十分之一。
音樂怎麼變成字
一首合唱被切成一格一格的八分音符。每一格有四個聲部,從女高音到男低音,每個聲部是一個 token:新的音就是它的音高,延續上一個音是 HOLD,休止是 REST。所以模型寫的東西長這樣:女高、女低、男高、男低,女高、女低、男高、男低……
export const HOLD = HI - LO + 1, REST = HOLD + 1, VOCAB = REST + 1; // 54 個音高 + 2 = 56 個字接下來就和 № 004 的 Transformer 一模一樣,連程式都是同一份:看前面的字,猜下一個字。那篇它學的是把數字倒過來寫,這篇學的是巴赫。
這裡有個陷阱:模型只靠「這是第幾個字」分辨聲部,所以每次取最近的 64 個字,開頭都必須剛好是女高音,否則它會把每個聲部都當成隔壁那一個。
它先學會什麼
在圖 1 多練幾次,會發現它每次學會的順序都差不多。我用圖 1 的配方練了兩次,每個時刻寫五段、量同樣的東西,下面是兩次的平均。驗證誤差是它猜下一個音的平均誤差,越低越好;平行五度是四部寫作的頭號禁忌,指兩個聲部隔著純五度一起往同一個方向走:
| 步數 | 驗證誤差 | 在調上 | 聲部交錯 | 拖長的音 | 平行五度(每百格) |
|---|---|---|---|---|---|
| 0 | 4.51 | 82% | 87% | 0.5% | 3.9 |
| 100 | 2.01 | 99% | 36% | 78% | 1.1 |
| 250 | 1.60 | 99.7% | 1.9% | 50% | 9.8 |
| 500 | 1.39 | 99.6% | 1.1% | 55% | 10.0 |
| 1000 | 1.21 | 98.7% | 1.8% | 54% | 12.0 |
| 2000 | 1.07 | 99.2% | 1.3% | 53% | 8.9 |
| 2400 | 1.05 | 99.5% | 0% | 65% | 7.2 |
| 巴赫本人 | 97.7% | 2.6% | 45% | 0.16 |
- 先學會拖長音。 第 100 步有 78% 的音都是拖著的:它發現「繼續上一個音」最常猜對,於是幾乎只會持續音。
- 再學會各守各的音域。 聲部交錯從 87% 掉到 250 步的 2%,之後就一直比巴赫還乾淨。
- 調性穩下來,節奏沒有完全穩。 調內音一路維持在 99%;拖長的音回到 50% 上下,比巴赫的 45% 多一點,到訓練尾聲又升到 65%,聽起來會比巴赫慢。
- 平行五度學不會。 巴赫一百格才犯 0.16 次,它整輪都在 7 到 12 次之間晃。
在我的瀏覽器裡,驗證誤差不到一分鐘就低於 1.46,那是一個 5-gram(只看前面四個字、把次數數起來)在同一份資料上的最好成績。5-gram 的作曲更慘:它看不到四個字以前,一個聲部拖了一格,它就忘了自己在哪個音上,72% 的時間聲部交錯。
它在作曲,還是在背譜?
圖 1 裡的「最長抄襲」,是拿它寫的每一格去比對 333 首訓練曲目,找出和其中一首一模一樣、連續最長的一段。練到 1000 步時是 3 到 6 格(一格是一個八分音符,八格一小節),不到一小節;一首聖詠平均有 107 格。
所以它多半是在作曲。但練得越久,記住的片段越長:2400 步時變成 5 到 10 格。而且十段裡出現過一段整整抄了 32 格、也就是四小節的例外——它確實有能力把整段譜背下來。
你當評審
標題說的「療癒」不是它本來就會的。巴赫的合唱不慢也不安靜;慢下來、安靜下來,是接下來你自己調出來的。
練好的模型寫得像巴赫,但不一定是你想聽的。現在換你:它一次寫兩段,你選一段,它就往你選的那段靠近一點,離另一段遠一點。ChatGPT 這類模型就是用人的偏好調教出來的(RLHF);這裡用的是它後來一個比較簡單的版本 DPO,只是評審換成你。
每選一次,模型往你選的那段靠近一點、離另一段遠一點(DPO)。它同時被拉在原本的樣子附近,不會一下子跑太遠。 大約選 20 次就聽得出差別;選太多次它會只剩持續音。
你一直選比較慢、比較安靜的那段,它就會慢下來。我用一個自動評審試過,設定和這張圖一模一樣:每次選「拖長的音比較多」的那段,選 20 次,拖長音從 48% 升到 64%;選 40 次變成 78%,和弦的種類掉了一半,開始只剩持續音。
它會找評審的漏洞
然後我讓自動評審改考平行五度:每次選犯得比較少的那段。選 40 次,平行五度從每百格 12.3 次只降到 10.9 次,幾乎沒動;拖長音倒是從 48% 升到 58%,和弦的種類少了四分之一。選到 80 次,平行五度降到 8.0,拖長音 63%、和弦種類從 45 掉到 29。
它確實把犯規壓下來了,但用的辦法是少動:不動的聲部不可能走出平行五度。
這叫 reward hacking,是訓練大型語言模型時最頭痛的問題之一:評審量的是一件事,模型最佳化的是「讓評審滿意」,兩者只要有一點縫,它就會找到。書上的修法有兩招:
- 換一個鑽不了漏洞的評分。 改算「每次有聲部移動時,犯了幾次」,不動就拿不到好處。
- 把繩子拉緊。 DPO 裡有一個 β,決定模型可以離原本的自己多遠。
在這張圖的力道下,這兩招只買到一點點:同樣選 80 次,防鑽的評審把平行五度壓到 7.0,可鑽的是 8.0,而兩者的拖長音都是 63%。它們都還是先學會少動。
差別要推得更用力才看得出來。我把更新改成累積四次一起做、學習率調成三倍:可鑽的評審把平行五度壓到 5.9,代價是拖長音衝到 73%、和弦種類只剩一半;防鑽的評審壓到 7.0,拖長音 52%、和弦種類 41,和原本差不多。越用力優化,鑽漏洞越划算,這正是大模型調教時的處境。上面那張圖故意留在溫和的力道,免得你點個二十下它就跑偏。
它贏得過五行數學嗎?
最後是一個不用任何 AI 的對手,五行就寫得完:五聲音階(C D E G A)上的一條 1/f 隨機旋律,底下墊著固定的 C–Am–F–G 四個和弦。
const scale = [60, 62, 64, 67, 69, 72, 74, 76, 79, 81]; // C 五聲音階
const chords = [[48, 55, 64], [45, 57, 64], [41, 53, 60], [43, 55, 62]];
for (let d = 0; d < 4; d++) if (t % (2 << d) === 0) dice[d] = rng(); // 四顆骰子
const soprano = scale[Math.floor(mean(dice) * scale.length)]; // 1/f 旋律
voices.push([soprano, ...chords[Math.floor(t / 8) % 4]]); // 每小節換和弦它永遠不會彈錯音,聲部也永遠不會交錯。
但它的平行五度是每百格 19.8 次,巴赫的一百多倍,和弦種類只有巴赫的一半。四個和弦一起平移,就是一連串的平行五度。
所以,聽得出來嗎?下面三段各四小節,一段是巴赫本人(模型沒看過的那 37 首裡的一段)、一段是練好 1000 步的 AI、一段是五行數學,順序每次都重新洗過。
三段各四小節,一段是巴赫本人、一段是練好 1000 步的 AI、一段是五行數學規則,順序是隨機的。聽完再猜。
對完答案,卡片下面那兩個數字會告訴你,三段的差別不只在聽感上。
資料和聲音
- 資料是 Craig Sapp 整理的巴赫 370 首四部聖詠(開新分頁),授權是 CC BY-NC-SA 4.0;這一頁用到的樂譜資料和練好的模型,也以同樣的授權提供。每一首都移到 C 大調或 a 小調,切成八分音符的格子,333 首拿來訓練,37 首留著驗證。
- 合成器是自己寫的,沒有用瀏覽器內建的樂器或效果:溫暖墊音是基音加一個輕輕的八度,各疊兩個相差 4 音分的音,慢慢起音;殘響照 Freeverb 的結構,尾音會越來越暗。第一版是五個泛音的風琴,我第一次聽就覺得太尖:1–4 kHz 比現在多了 6 到 16 dB。
- 所有的「像不像巴赫」都是代理指標:在不在調上、聲部有沒有交錯、平行五度、拖長音的比例、和弦的種類。它們說得出一段音樂「守不守規矩」,說不出好不好聽。