量的關(guān)鍵機(jī)制)
AI作曲這幾年火得不行從隨手哼兩句變成完整的編曲再到給短視頻配BGM幾乎每個(gè)環(huán)節(jié)都有模型的身影。但真正上手做過音樂生成項(xiàng)目的人都會(huì)撞上同一個(gè)尷尬開頭兩小節(jié)驚艷八小節(jié)之后開始原地轉(zhuǎn)圈到十六小節(jié)基本已經(jīng)忘了前面在寫什么。旋律不停重復(fù)、和弦走向莫名其妙突變、主題動(dòng)機(jī)走著走著就丟了問題并不在“模型不會(huì)寫曲子”而在于模型根本“沒記住自己寫過什么”。這篇文章想聊的就是AI作曲里那個(gè)經(jīng)常被忽視、卻決定生成質(zhì)量上限的關(guān)鍵機(jī)制——短期記憶。標(biāo)題里的“秘密”不是故弄玄虛。過去兩年我先后做過幾次音樂生成方向的嘗試從最早基于規(guī)則拼接的MIDI生成到后來用序列模型做旋律續(xù)寫再到帶注意力結(jié)構(gòu)的生成方案越到后面越發(fā)現(xiàn)決定一首AI曲子是“能聽”還是“沒法聽”的往往不是生成網(wǎng)絡(luò)有多深、訓(xùn)練數(shù)據(jù)有多大而是它在生成過程中怎么處理和保留已經(jīng)輸出的歷史信息。這個(gè)機(jī)制放到深度學(xué)習(xí)里多數(shù)時(shí)候表現(xiàn)為注意力窗口、緩存狀態(tài)或者隱向量放到音樂層面它就是模型的短期記憶。它能決定模型是死守著一兩個(gè)動(dòng)機(jī)反復(fù)倒帶還是有能力讓音樂在幾十秒內(nèi)流動(dòng)起來、發(fā)展下去。這篇內(nèi)容適合正在做音樂生成、旋律續(xù)寫、AI編曲相關(guān)項(xiàng)目的朋友參考也適合那些好奇“為什么AI寫的歌總有股莫名其妙的重復(fù)感”的人。我會(huì)從記憶機(jī)制的本質(zhì)講起結(jié)合自己做過的幾個(gè)實(shí)驗(yàn)把這套東西拆開揉碎包括模型結(jié)構(gòu)上的取舍、實(shí)際訓(xùn)練中的參數(shù)策略以及那些不跑幾個(gè)通宵絕對(duì)發(fā)現(xiàn)不了的坑。1. 音樂生成里的“記憶”到底指什么很多人第一次接觸AI作曲以為模型是“理解”了音樂才寫出來的。實(shí)際上到目前為止主流的生成模型本質(zhì)上做的是條件概率預(yù)測(cè)也就是給定前面已經(jīng)生成的音符序列預(yù)測(cè)下一個(gè)最合理的音符是什么。聽起來好像很簡(jiǎn)單但這里有一個(gè)隱藏前提為了讓“下一個(gè)音符”合理模型必須知道“前面的音符”到底發(fā)生了什么。這個(gè)“前面”的范圍有多大信息能被記住多少就是短期記憶要回答的問題。1.1 從人的創(chuàng)作習(xí)慣理解短期記憶的必要性人類作曲家作曲的時(shí)候短期記憶的作用太明顯了。隨便寫一段八小節(jié)的旋律你會(huì)發(fā)現(xiàn)第一小節(jié)和第二小節(jié)往往存在某種呼應(yīng)第三、四小節(jié)會(huì)有一點(diǎn)發(fā)展到了第七、八小節(jié)可能又回到開頭的元素。這不是偶然而是創(chuàng)作者在落筆的時(shí)候腦子里始終保留著前面音樂片段的“余音”知道現(xiàn)在這句是從哪來的、要往哪去。專業(yè)的說法叫主題發(fā)展或者動(dòng)機(jī)貫穿翻譯成大白話就是創(chuàng)作者記得自己寫了什么。AI模型呢如果模型架構(gòu)里沒有足夠的記憶能力那它本質(zhì)上就是一個(gè)隨時(shí)“失憶”的創(chuàng)作者。它生成第100個(gè)音符的時(shí)候可能完全不記得第1個(gè)音符長什么樣只能靠最近一小段信息瞎猜。結(jié)果就是旋律不停地在局部打轉(zhuǎn)——因?yàn)樽罱膸讉€(gè)音符可能剛好構(gòu)成了一個(gè)穩(wěn)定的進(jìn)行模型覺得“這地方很安全”就反復(fù)往相似的方向走。這就是大家常說的AI音樂“幾秒鐘后開始復(fù)讀”的根本原因。1.2 短期記憶和長期記憶在技術(shù)上的分界順著這個(gè)思路往下模型記憶能力的討論通常會(huì)分成兩個(gè)層次。短期記憶一般指模型在生成當(dāng)前音符時(shí)能直接“看到”或“讀取”到的上下文信息。在Transformer類模型里就是這個(gè)意思生成的時(shí)候前面生成的所有token都會(huì)通過注意力機(jī)制參與當(dāng)前步的計(jì)算。窗口有多長記憶就有多大。長期記憶則不一樣它更接近于“模型從訓(xùn)練數(shù)據(jù)里學(xué)到的規(guī)律”比如大調(diào)里導(dǎo)音傾向于解決到主音比如四小節(jié)通常構(gòu)成一個(gè)樂句。這些東西已經(jīng)不依賴具體某一次生成的上下文了是沉淀在權(quán)重里的經(jīng)驗(yàn)。這兩者很容易被混淆。經(jīng)常有人跟我說“我的模型學(xué)了很多莫扎特為什么生成的東西還是不像”問題往往出在短期記憶不夠模型學(xué)到了莫扎特的和聲規(guī)則卻記不住自己在前八小節(jié)里已經(jīng)用了什么素材。1.3 為什么音樂比其他序列任務(wù)更依賴短期記憶文本生成也依賴上下文但音樂生成對(duì)短期記憶的要求有它非常特殊的地方。音樂的核心結(jié)構(gòu)特征在于“重復(fù)中的變化”——同一個(gè)動(dòng)機(jī)反復(fù)出現(xiàn)每次出現(xiàn)都要有細(xì)微的變化這需要模型既記得動(dòng)機(jī)的原型也記得每次變化后的差異。這和語言不同語言里一個(gè)詞錯(cuò)了語義可能就偏了但音樂里同一個(gè)旋律原樣重復(fù)多遍或者變化得離譜都可能成為“聽感崩塌”的元兇。具體到數(shù)值上一段120BPM的四四拍音樂一小節(jié)大約兩秒一個(gè)典型的樂句是四小節(jié)也就是八秒。要讓模型生成的音樂聽起來有結(jié)構(gòu)感它至少需要記住過去四到八小節(jié)的素材換算成音符數(shù)量大概就是64到128個(gè)音符假設(shè)每拍兩個(gè)音。這是非常高的記憶要求比大多數(shù)文本生成的上下文要密集得多因?yàn)橐舴鄬?duì)簡(jiǎn)單信息熵卻靠排列組合撐得很高。2. 藏在Transformer和RNN里的記憶機(jī)制聊完了“為什么需要記憶”再說說當(dāng)前主流模型到底是怎么實(shí)現(xiàn)記憶的。這里繞不開兩種最常見的架構(gòu)路徑一種是以自注意力為核心的Transformer方案另一種是更早期的循環(huán)神經(jīng)網(wǎng)絡(luò)方案。兩種方案對(duì)短期記憶的理解完全不同實(shí)現(xiàn)方式也各有優(yōu)劣。我個(gè)人的經(jīng)驗(yàn)是了解這兩條路才能在實(shí)際項(xiàng)目里做出合適的選擇。2.1 Transformer的注意力窗口就是顯式的短期記憶現(xiàn)在音樂生成用得最多的一般是Transformer家族。它的短期記憶機(jī)制非常直觀生成第t個(gè)音符的時(shí)候模型會(huì)把第1到第t-1個(gè)音符的表示全部拿出來跟當(dāng)前步做注意力計(jì)算。理論上只要窗口夠長模型就能“記住”從樂曲開頭到現(xiàn)在的所有信息。這個(gè)設(shè)計(jì)非常優(yōu)雅也讓訓(xùn)練過程變得簡(jiǎn)單直接。但“理論上”三個(gè)字后面通常跟著一個(gè)“不過”。注意力窗口長不代表模型真的會(huì)用這些信息。如果訓(xùn)練數(shù)據(jù)里大部分曲子并不需要特別遠(yuǎn)距離的依賴模型學(xué)到的注意力權(quán)重會(huì)自然地偏向相鄰音符遠(yuǎn)端的信息雖然在那里注意力卻很少分配過去。這就好比一個(gè)學(xué)生桌上擺了一整套教材但他復(fù)習(xí)的時(shí)候只看最近兩頁那遠(yuǎn)端教材對(duì)他考試成績的影響幾乎為零。所以不少團(tuán)隊(duì)在Transformer之外還要引入一些機(jī)制來“強(qiáng)制”模型看重遠(yuǎn)處的信息這個(gè)后面會(huì)詳細(xì)說。2.2 KV Cache工程實(shí)現(xiàn)里最容易被忽略的記憶載體如果真正去實(shí)現(xiàn)過基于Transformer的自回歸生成大概率會(huì)遇到一個(gè)叫KV Cache的概念。它的全稱是Key-Value Cache作用是緩存已經(jīng)計(jì)算過的注意力鍵值對(duì)避免每一步都從頭算一遍前面的內(nèi)容。在推理解釋上這就是Transformer的“短期記憶倉庫”生成第10個(gè)音符時(shí)前9個(gè)音符的K和V向量都存在緩存里直接取出來參與計(jì)算。這里有一個(gè)很多初學(xué)者容易忽略的事實(shí)窗口長度決定了KV Cache的大小而KV Cache的大小直接跟延遲和顯存掛鉤。在音樂生成場(chǎng)景里如果序列很長比如生成一整首鋼琴曲的子旋律可能有幾千個(gè)音符KV Cache會(huì)持續(xù)膨脹推理速度越來越慢。我自己跑實(shí)驗(yàn)的時(shí)候出現(xiàn)過生成前幾十個(gè)音符飛快、后面越來越慢的情況一開始還以為是模型出問題了后來才發(fā)現(xiàn)就是KV Cache吃滿了顯存。為了控制這個(gè)問題的常見做法是限制注意力窗口長度或者用滑動(dòng)窗口注意力只讓模型關(guān)注最近N個(gè)token。# 偽代碼示例限制注意力窗口的KV Cache保留范圍 def generate_with_sliding_window(model, token_ids, window_size128): cache_k, cache_v [], [] for t in range(len(token_ids)): start max(0, t - window_size) context token_ids[start:t 1] k, v model.encode(context) cache_k.append(k) cache_v.append(v) if len(cache_k) window_size: cache_k.pop(0) cache_v.pop(0) logits model.decode(cache_k, cache_v) next_token sample(logits) return next_token2.3 RNN的隱狀態(tài)另一種形態(tài)的短期記憶Transformer流行之前音樂生成領(lǐng)域其實(shí)走過很長一段RNN的路。LSTM和GRU這類循環(huán)網(wǎng)絡(luò)沒有顯式的注意力窗口它們的記憶全部裝在一個(gè)固定維度的隱狀態(tài)向量里每生成一個(gè)音符隱狀態(tài)被更新一次。如果說Transformer的短期記憶是一本可以被隨意翻閱的筆記本那RNN的短期記憶就像一個(gè)人腦中的印象容量有限而且越久遠(yuǎn)的內(nèi)容越容易被壓縮、變形。這就帶來了一個(gè)非常有意思的對(duì)比RNN因?yàn)殡[狀態(tài)容量有限天然會(huì)把遠(yuǎn)近信息做壓縮處理早期信息很容易被后續(xù)信息“覆蓋”生成出來的旋律后段離前段越來越遠(yuǎn)而Transformer因?yàn)楸A羧繗v史token理論上可以做到“每一頁都被記得清清楚楚”但也正因?yàn)橛浀锰宄炊赡茏屇P屯祽小l(fā)現(xiàn)只要參考最近幾個(gè)音符就能把下一個(gè)音符預(yù)測(cè)得差不多就不會(huì)刻意去挖掘遠(yuǎn)端的結(jié)構(gòu)線索。說句實(shí)在話這兩種模型在音樂生成上各有各的難纏。2.4 為什么說位置編碼是短期記憶的坐標(biāo)系統(tǒng)理解了注意力機(jī)制之后還有一個(gè)細(xì)節(jié)不能跳過位置編碼。注意力計(jì)算本身是不分先后順序的它把序列當(dāng)成一個(gè)集合來處理。如果沒有位置信息模型就分不清“前面出現(xiàn)過的主旋律”和“剛過去的那一下裝飾音”在時(shí)間上的差別。所以幾乎所有的Transformer模型都會(huì)在輸入里加入位置編碼讓每個(gè)token帶上“我是第幾個(gè)出現(xiàn)的”這個(gè)信息。在音樂生成任務(wù)中位置編碼還有一個(gè)比較特殊的意義它不僅標(biāo)記順序還直接影響模型對(duì)節(jié)奏和時(shí)值的感知。不同的音符有不同的時(shí)值同樣的音高出現(xiàn)在第一拍和第三拍含義完全不同。如果把音樂token化之后再加上絕對(duì)位置編碼模型等于拿著一個(gè)刻度尺去丈量記憶里的事件這對(duì)生成穩(wěn)定的節(jié)奏律動(dòng)非常重要。我見過一個(gè)實(shí)驗(yàn)把絕對(duì)位置編碼換成相對(duì)位置編碼之后模型生成的三連音明顯更穩(wěn)定了因?yàn)橄鄬?duì)位置讓模型在記憶里更容易識(shí)別“從這個(gè)音往后推多少拍是下一個(gè)重音”這類規(guī)律。3. 我踩過的短期記憶工程坑這一節(jié)說說實(shí)操。我做過幾次嘗試踩過不少坑。有些問題看起來是模型能力不行結(jié)果排查到最后發(fā)現(xiàn)問題出在對(duì)短期記憶機(jī)制的理解上。挑幾個(gè)印象深刻的跟大家聊聊都是真實(shí)遇到過的場(chǎng)景。3.1 復(fù)現(xiàn)某開源作曲模型時(shí)發(fā)現(xiàn)生成永遠(yuǎn)停在三小節(jié)有一次我復(fù)現(xiàn)一個(gè)開源的旋律續(xù)寫模型訓(xùn)練了很久一到生成階段就出問題模型最多生成三小節(jié)新內(nèi)容之后就開始無限循環(huán)第一小節(jié)的節(jié)奏型偶爾換一兩個(gè)音但整體上已經(jīng)完全失去推進(jìn)感。一開始我懷疑是訓(xùn)練數(shù)據(jù)的毛病后來檢查了推理代碼才發(fā)現(xiàn)問題出在我使用的窗口長度上。在那個(gè)模型的設(shè)計(jì)里訓(xùn)練時(shí)序列被切成了固定長度片段每個(gè)片段內(nèi)的信息最多跨越16小節(jié)但我的推理代碼把窗口長度錯(cuò)設(shè)成了4小節(jié)等于模型只能看到過去1秒的內(nèi)容。打個(gè)比方一個(gè)只看了前兩頁書的人讓他續(xù)寫整本小說的后續(xù)情節(jié)他除了反復(fù)描述前兩頁出現(xiàn)的人物之外確實(shí)也沒什么別的辦法。這個(gè)排查過程提醒我短期記憶的窗口設(shè)置必須跟訓(xùn)練時(shí)保持一致不能只看模型文檔里寫的“支持長序列”還得自己確認(rèn)推理階段的有效記憶范圍是多少。3.2 位置編碼類型選錯(cuò)導(dǎo)致的“節(jié)奏崩潰”還有一個(gè)印象更深的坑出現(xiàn)在位置編碼的選擇上。當(dāng)時(shí)我對(duì)比了兩種位置編碼方案一種是絕對(duì)位置編碼另一種是相對(duì)位置編碼。實(shí)驗(yàn)結(jié)果顯示用絕對(duì)位置編碼訓(xùn)練出來的模型在短句子上表現(xiàn)不錯(cuò)但生成長句子時(shí)逐漸出現(xiàn)節(jié)奏混亂——原本均勻的八分音符跑著跑著變成不均勻的附點(diǎn)節(jié)奏又莫名其妙地變回八分音符整個(gè)律動(dòng)像喝醉了酒。翻論文的時(shí)候看到一個(gè)解釋說絕對(duì)位置編碼在長距離外推時(shí)會(huì)失去精度模型對(duì)“隔了多遠(yuǎn)”這件事的感知會(huì)變得模糊。音符時(shí)值本身就是靠位置關(guān)系定義的信息距離感知一模糊節(jié)奏自然要出問題。換用相對(duì)位置編碼之后類似情況明顯減少。這里想給的建議是如果項(xiàng)目里的音樂對(duì)節(jié)奏穩(wěn)定性要求高優(yōu)先考慮相對(duì)位置編碼或者能處理任意長度外推的編碼方案不要為了省事直接用絕對(duì)位置編碼。3.3 部分記憶單元失效導(dǎo)致的主題丟失還有一種情況更隱蔽模型的記憶機(jī)制本身是完整的但訓(xùn)練過程沒有讓模型學(xué)會(huì)有效利用記憶。我做過一次對(duì)比實(shí)驗(yàn)兩組訓(xùn)練數(shù)據(jù)完全相同唯一區(qū)別是一組在訓(xùn)練時(shí)給loss加了一個(gè)“對(duì)遠(yuǎn)端token注意力權(quán)重”的正則化約束另一組不加。結(jié)果顯示加了正則化約束的那組生成結(jié)果的樂句結(jié)構(gòu)明顯更完整主題動(dòng)機(jī)能在16小節(jié)后重新出現(xiàn)而不加的那組前8小節(jié)和后8小節(jié)聽起來像是兩首不同的曲子。這讓我意識(shí)到對(duì)很多序列模型來說短期記憶不只是“有沒有”的問題更重要的是“模型有沒有被訓(xùn)練去用它”。如果訓(xùn)練數(shù)據(jù)的句子普遍偏短模型就會(huì)形成一種惰性只關(guān)注近處信息就能把loss降得很低遠(yuǎn)端的記憶通道雖然技術(shù)在但根本沒有被激活。后面我在設(shè)計(jì)訓(xùn)練策略時(shí)會(huì)專門構(gòu)造一些需要“回溯遠(yuǎn)處主題”的訓(xùn)練樣本逼著模型學(xué)會(huì)使用短期記憶里的遠(yuǎn)程信息。3.4 訓(xùn)練與推理窗口不一致帶來的性能鐵幕這個(gè)問題在工程上非常容易踩。很多模型在訓(xùn)練時(shí)用的是128 token的窗口但到了生成階段理想情況下想讓它一次性生成幾百上千個(gè)音符。如果你直接把推理窗口拉長到512甚至更長模型表現(xiàn)往往會(huì)斷崖式下降。原因也不難理解——模型在訓(xùn)練時(shí)根本沒看過那么長的上下文你讓它在一個(gè)陌生的“記憶長度”下工作它當(dāng)然不知道該把注意力放在哪里。我個(gè)人的習(xí)慣是訓(xùn)練階段就至少準(zhǔn)備兩檔窗口一檔用于常規(guī)訓(xùn)練另一檔偶爾用長窗口做一次“預(yù)熱式”微調(diào)這樣到了推理階段長窗口生成時(shí)的表現(xiàn)會(huì)穩(wěn)定很多。有人管這招叫窗口一致性訓(xùn)練也有人叫它課程學(xué)習(xí)本質(zhì)上都是在做一件事讓模型習(xí)慣在一個(gè)足夠長的短期記憶范圍里工作。4. 把短期記憶用到極致的幾個(gè)實(shí)戰(zhàn)做法聊完踩坑說說哪些做法在實(shí)際項(xiàng)目中是有效的。下面這些策略有的是從論文里看來的有的是我自己試出來的都在不同程度上解決過“AI記不住自己寫了什么”的問題。為了方便對(duì)照我用一個(gè)模擬的旋律續(xù)寫項(xiàng)目來串起這些方案。4.1 用“主題token”做顯式的記憶錨點(diǎn)第一個(gè)做法是在輸入序列里增加一個(gè)特殊的主題token用來表征整段音樂的核心動(dòng)機(jī)。具體來說在生成每一段新音樂之前先把目前的“主題摘要”作為特殊token拼在序列的開頭。模型生成每個(gè)音符時(shí)除了能看到最近的內(nèi)容還能時(shí)不時(shí)注意到開頭那個(gè)代表主題的token相當(dāng)于給短期記憶加了一個(gè)“書簽”。實(shí)現(xiàn)上并不復(fù)雜。在tokenization階段可以用一個(gè)小型的編碼器去壓縮前8小節(jié)的內(nèi)容得到一組向量然后把這組向量映射成一個(gè)或幾個(gè)special token插入到輸入序列的最前面。音樂生成時(shí)模型每走一步都會(huì)回頭“看”一眼這個(gè)錨點(diǎn)相當(dāng)于逼著短期記憶留存最核心的信息。即便注意力窗口內(nèi)部的普通音符已經(jīng)被沖得很遠(yuǎn)主題token仍然能被頻繁訪問起到一種壓縮記憶的作用。4.2 局部重復(fù)建模讓短期記憶自動(dòng)形成結(jié)構(gòu)第二種做法更貼近音樂的本質(zhì)——重復(fù)。音樂好聽很大程度上是因?yàn)橛泻魬?yīng)與重復(fù)而這種重復(fù)發(fā)生后短期記憶里的信息密度會(huì)更高。可以專門設(shè)計(jì)一種輔助任務(wù)訓(xùn)練模型預(yù)測(cè)“當(dāng)前這個(gè)小節(jié)是否與前面某一個(gè)小節(jié)相似”或者“當(dāng)前旋律應(yīng)該在哪個(gè)位置回到前面的素材”。這種輔助任務(wù)不需要改動(dòng)模型主體架構(gòu)只需要在訓(xùn)練loss里加一個(gè)額外的分類頭或者在數(shù)據(jù)構(gòu)造時(shí)做一下類似對(duì)比學(xué)習(xí)的采樣。效果非常明顯因?yàn)槟P鸵坏W(xué)會(huì)“識(shí)別重復(fù)”它生成的時(shí)候就更容易主動(dòng)構(gòu)建短距離的呼應(yīng)結(jié)構(gòu)樂句之間的黏合感會(huì)強(qiáng)很多。路徑依賴的角度看這也等于在引導(dǎo)模型主動(dòng)去短期記憶里翻找以前的素材而不是每次都憑空創(chuàng)造新內(nèi)容。4.3 讓記憶窗口做“分段滑動(dòng)”而不是“整體滑動(dòng)”傳統(tǒng)滑動(dòng)窗口是丟掉最老的token保留最近的N個(gè)token。這種做法有一個(gè)問題如果一首曲子的主題出現(xiàn)在第1小節(jié)而當(dāng)前生成到第40小節(jié)滑動(dòng)窗口早就把主題沖掉了。雖然理論模型可以通過位置編碼回溯但工程上很多實(shí)現(xiàn)會(huì)把窗口外的信息直接截?cái)?。一種更穩(wěn)妥的做法是分段滑動(dòng)。窗口分成兩部分固定長度的“全局段”和滑動(dòng)更新的“局部段”。全局段始終保留開頭若干小節(jié)的摘要信息局部段則存放最近生成的細(xì)節(jié)信息。生成時(shí)模型同時(shí)看這兩部分相當(dāng)于既有“長期的主題記憶”又有“短期的細(xì)節(jié)記憶”。我在模擬項(xiàng)目里試過這個(gè)方案生成60小節(jié)以上的曲子時(shí)前后結(jié)構(gòu)的一致性提升非常明顯遠(yuǎn)勝于單純的整體滑動(dòng)窗口。4.4 用Midi編碼的字段信息增強(qiáng)記憶強(qiáng)度還有一個(gè)偏數(shù)據(jù)層面的做法容易被技術(shù)文章忽略但效果很實(shí)在處理好MIDI事件本身的編碼密度。很多音頻/MIDI編碼方案會(huì)把音符的“位置、時(shí)值、力度、音高”全部分開成不同的token甚至有些方案會(huì)把休止符也切成多個(gè)邏輯碎片。這會(huì)導(dǎo)致一個(gè)問題同樣一段四小節(jié)的信息被拆成了一大串token短期記憶里裝的“噪音”太多有效音樂信息反而被稀釋。如果能把MIDI事件編碼成更緊致的token序列比如一個(gè)音符包含“起始位置、時(shí)值、音高”三元組一段四小節(jié)的旋律可能只需要32個(gè)token而分開編碼可能需要100多個(gè)token。同樣窗口容量下緊致編碼等于把短期記憶的有效容量翻了倍這對(duì)前文提到的高密度音樂信息需求是非常友好的。我一般會(huì)先用一個(gè)統(tǒng)計(jì)腳本來跑一下訓(xùn)練集平均每小節(jié)的token數(shù)再?zèng)Q定窗口大小這個(gè)習(xí)慣幫我避開了不少容量規(guī)劃上的坑。5. 生成時(shí)的記憶衰減診斷與調(diào)參建議實(shí)操到后面你會(huì)發(fā)現(xiàn)一個(gè)更折磨人的問題就算模型架構(gòu)和訓(xùn)練都沒問題生成時(shí)還是會(huì)遇到“記憶衰減”。這里說的衰減并不是顯存或者緩存問題而是生成過程中的累積誤差導(dǎo)致模型逐漸偏離初始主題越到后面越不知道自己在干什么。診斷這類問題需要一些可量化的手段而不是靠耳朵聽。5.1 用自相似矩陣量化檢查記憶留存檢查模型有沒有在“正確回憶”之前的素材一個(gè)好用的工具是自相似矩陣self-similarity matrix。把生成的一段MIDI轉(zhuǎn)成特征序列比如每個(gè)小節(jié)的音高輪廓或節(jié)奏向量然后計(jì)算兩兩之間的相似度畫成一張熱力圖。如果曲子存在主題重現(xiàn)熱力圖里會(huì)看到清晰的塊狀結(jié)構(gòu)如果模型已經(jīng)失憶熱力圖會(huì)呈現(xiàn)一片模糊的漸變或者完全散亂的結(jié)構(gòu)。我自己每次調(diào)整完配置都會(huì)拿這個(gè)矩陣看一眼再?zèng)Q定是否繼續(xù)訓(xùn)練或推理。它比單純聽音頻靠譜得多也方便橫向?qū)Ρ炔煌瑓?shù)下的生成結(jié)果。有一次我把訓(xùn)練窗口從128提升到256其他什么都不變生成的曲子自相似矩陣?yán)锩黠@多出很多塊狀結(jié)構(gòu)說明模型確實(shí)開始利用更長的記憶跨度了。5.2 溫度與采樣策略對(duì)記憶利用的影響同樣一個(gè)模型溫度參數(shù)不同生成結(jié)果對(duì)短期記憶的利用程度會(huì)差很多。溫度偏低時(shí)模型傾向于選擇概率最高的token通常會(huì)緊緊跟隨最近的上下文容易出現(xiàn)局部循環(huán)溫度偏高時(shí)模型隨機(jī)性大容易脫離記憶約束寫一會(huì)兒就跑到陌生的區(qū)域。理想值取決于項(xiàng)目和風(fēng)格但我建議以0.8到1.0為起點(diǎn)做小范圍網(wǎng)格搜索。還有一個(gè)容易被忽略的參數(shù)是top-p或top-k采樣。如果采樣范圍限制得太窄模型在選擇下一個(gè)音符時(shí)幾乎只看最近幾個(gè)音符給出的高概率候選遠(yuǎn)端記憶對(duì)當(dāng)前步的影響會(huì)被壓縮。稍微放寬采樣范圍等于給模型更多機(jī)會(huì)去檢索不同位置的記憶信息。這里面有一個(gè)平衡要摸索采樣太寬會(huì)亂采樣太窄會(huì)復(fù)讀找到那個(gè)臨界點(diǎn)生成質(zhì)量會(huì)上一大截。5.3 從損失曲面反推訓(xùn)練窗口是否合適訓(xùn)練階段的損失曲線也能透露短期記憶問題。如果訓(xùn)練loss下降很快但驗(yàn)證loss一直偏高大概率是模型過度依賴局部上下文沒有學(xué)會(huì)使用更遠(yuǎn)的記憶信息。這種情況有一個(gè)特征把驗(yàn)證集里的序列截?cái)喑筛痰钠蝜oss會(huì)明顯下降而如果用完整長度序列評(píng)估loss就很差。一旦出現(xiàn)這種情況最簡(jiǎn)單的干預(yù)是人為構(gòu)造一些“遠(yuǎn)期依賴”的訓(xùn)練樣本比如把一首曲子的開頭兩個(gè)小節(jié)拼接到結(jié)尾前面再要求模型預(yù)測(cè)“結(jié)尾應(yīng)該接什么”迫使它回去翻看序列前面的信息。這種做法雖然粗暴但效果直接。做過幾次之后模型的驗(yàn)證loss通常會(huì)有可見的改善生成的樂曲結(jié)構(gòu)感也會(huì)上一個(gè)臺(tái)階。5.4 參數(shù)速查我常用的短期記憶相關(guān)設(shè)置最后整理一份我實(shí)際操作中常用的參數(shù)速查表給想動(dòng)手試的朋友一個(gè)起點(diǎn)。這些參數(shù)不是標(biāo)準(zhǔn)答案但作為基線基本能跑通參數(shù)推薦起點(diǎn)調(diào)整方向說明注意力窗口長度256 token窗口過短時(shí)樂句會(huì)失去結(jié)構(gòu)過長則訓(xùn)練成本明顯上升滑動(dòng)窗口分段比例全局段25%/局部段75%偏重主題一致性就加大全局段偏重細(xì)節(jié)豐富性就加大局部段位置編碼相對(duì)位置編碼對(duì)節(jié)奏穩(wěn)定性要求高時(shí)優(yōu)先選這類方案溫度0.8 - 1.0風(fēng)格偏古典可以更低偏即興爵士可以更高top-p0.9 - 0.95采樣范圍過窄時(shí)主題重現(xiàn)有明顯下降訓(xùn)練窗口128與256混用以128為主按比例摻入256的樣本讓模型適應(yīng)長窗口主題token數(shù)量4 - 8個(gè)太少可能表達(dá)力不足太多會(huì)占用普通音符的記憶空間6. 未來方向短期記憶之外還缺什么短期記憶解決了AI作曲就順了嗎還遠(yuǎn)遠(yuǎn)不夠。我自己跑完這些實(shí)驗(yàn)后有一個(gè)比較清醒的認(rèn)識(shí)短期記憶解決的是“記得住”的問題但在音樂創(chuàng)作里比“記得住”更難的是“知道什么時(shí)候該忘掉什么”。人類作曲家寫一曲子的過程中不會(huì)事無巨細(xì)地保留每一個(gè)裝飾音的記憶他們會(huì)有選擇地記住主題、和聲走向、節(jié)奏骨架忽略那些無關(guān)緊要的細(xì)節(jié)。這種“選擇性記憶”機(jī)制目前的Transformer窗口式記憶是做不到的。業(yè)界也在往這個(gè)方向探索。記憶增強(qiáng)的Transformer把外部存儲(chǔ)跟模型結(jié)合模型學(xué)會(huì)從一個(gè)大內(nèi)存里讀取與當(dāng)前最相關(guān)的片段而不只是盯著固定窗口。還有人在嘗試用分層結(jié)構(gòu)來處理記憶把音樂先壓縮成樂句級(jí)、段級(jí)的高層摘要再在低層生成細(xì)節(jié)信息。這些做法的共同點(diǎn)是開始把“記憶”當(dāng)成一個(gè)可以主動(dòng)控制的目標(biāo)而不是結(jié)構(gòu)帶來的副產(chǎn)品。我相信接下來一兩年這個(gè)方向會(huì)有更多實(shí)用成果出來。7. 最后分享兩個(gè)實(shí)際使用中的小技巧作為收尾分享兩個(gè)我在實(shí)踐中覺得特別有用的技巧都很簡(jiǎn)單但經(jīng)常被忽略。第一個(gè)技巧是生成后做“回看校驗(yàn)”。當(dāng)模型生成完一整段音樂后把最開始幾個(gè)小節(jié)的旋律抽出來跟中間某幾個(gè)小節(jié)的旋律做相似度對(duì)比如果相似度低于某個(gè)閾值就自動(dòng)加一個(gè)懲罰項(xiàng)重新生成那一段。這相當(dāng)于給短期記憶上了一道保險(xiǎn)哪怕模型生成時(shí)忘了后處理也能把它拉回來。第二個(gè)技巧是刻意把主題動(dòng)機(jī)放在訓(xùn)練序列的“中段”而不是“開頭”。很多模型對(duì)序列開頭的記憶都很好但對(duì)中間部分的記憶最差。如果訓(xùn)練時(shí)經(jīng)常讓主題動(dòng)機(jī)構(gòu)出現(xiàn)在中段模型就會(huì)被逼著練出更強(qiáng)的中段記憶能力。這個(gè)做法有點(diǎn)反直覺但我試過幾次后生成長曲子的穩(wěn)定性確實(shí)好了不少。說到底短期記憶在AI作曲里的作用遠(yuǎn)不止是技術(shù)指標(biāo)層面的“上下文長度”那么簡(jiǎn)單。它直接決定了一個(gè)模型到底是在“創(chuàng)作”還是在“碰運(yùn)氣”。今天寫這篇文章也是希望更多做音樂生成的朋友能少走我曾經(jīng)走過的彎路。如果你們?cè)趯?shí)踐中也遇到過其他跟記憶相關(guān)的坑歡迎一起交流。