參:馬爾可夫鏈與溫度采樣實戰(zhàn))
簡介面向人工智能與自然語言處理學(xué)習(xí)者的自動寫詩實驗資源包涵蓋從古詩數(shù)據(jù)集構(gòu)建、數(shù)據(jù)預(yù)處理、深度學(xué)習(xí)模型訓(xùn)練到生成效果評估的完整閉環(huán)。資源共18個文件以Python源碼與編譯緩存文件為主輔以實驗指導(dǎo)書、實驗報告、PPT演示文稿、文本數(shù)據(jù)及numpy數(shù)據(jù)文件等壓縮包約23.83MB。已有169人學(xué)習(xí)下載。包內(nèi)核心Python腳本分別承擔(dān)模型定義、訓(xùn)練流程與工具函數(shù)可直接運行復(fù)現(xiàn)自動寫詩實驗numpy數(shù)據(jù)文件提供了預(yù)處理后的詩歌張量數(shù)據(jù)降低自行準(zhǔn)備數(shù)據(jù)集的成本。兩份Word文檔分別講解實驗步驟與結(jié)果分析PPT則以可視化方式展示模型架構(gòu)與實驗流程可參考BLEU、ROUGE等指標(biāo)評估生成質(zhì)量并能對比RNN、LSTM與Transformer在詩歌風(fēng)格與韻律模仿上的差異。適合具備一定Python和深度學(xué)習(xí)基礎(chǔ)、希望結(jié)合實踐理解文本生成技術(shù)的讀者。1. “自動寫詩.rar”不只是壓縮包先想清楚你要 demo 還是產(chǎn)品“自動寫詩.rar”這個名字乍看像壓縮包合集實際上是你在某個技術(shù)論壇或網(wǎng)盤里見到的自動寫詩項目包。里面可能是訓(xùn)練好的模型權(quán)重、生成腳本、古詩語料也可能只是幾個沒有注釋的 Python 文件。我接過不少這類包第一個建議是別急著雙擊解壓先問自己拿到它要干嘛。如果想半天內(nèi)跑通、給朋友演示“AI 寫的詩”那路線是解壓、裝環(huán)境、生成如果想把它沉淀成一個能自動寫出工整七律的工具那路線完全不同你得從數(shù)據(jù)清洗、模型選型和調(diào)參開始重做。這篇文章按第一條路給你一個最小閉環(huán)再帶你把第二條路上的坑標(biāo)出來適合想動手又不想被黑匣子擋在門外的人。2. 用 7-Zip 解開 .rar文件結(jié)構(gòu)、密碼恢復(fù)與最小環(huán)境搭建2.1 解壓前先看清單.rar 包里一般是這幾樣?xùn)|西拿到任何 .rar 包我一般不會直接右鍵解壓而是先列出壓縮包內(nèi)容確認(rèn)里面到底是什么形態(tài)的項目。很多自動寫詩包并沒有統(tǒng)一的目錄規(guī)范但反復(fù)出現(xiàn)的基本就這幾類家當(dāng)文件/目錄常見內(nèi)容在管線里的作用model.bin / weights.pt訓(xùn)練好的網(wǎng)絡(luò)權(quán)重生成的核心沒有它只能跑模板vocab.json / char2id.json字符表與索引映射把漢字編碼成模型能讀的數(shù)字generate.py / poem.py生成入口腳本調(diào)用模型或統(tǒng)計方法輸出詩句corpus/ 或 data/古詩語料重新訓(xùn)練或微調(diào)時用requirements.txt依賴列表還原作者當(dāng)時的運行環(huán)境README.md / 說明.txt使用方式與參數(shù)含義優(yōu)先級最高先讀它先讀說明文件這個動作看似多余其實能省下大量試錯時間。有的腳本要求 Python 3.7有的要求在 Windows 下運行且用了 Windows 專有路徑這些信息往往只在說明里寫了一句。如果解壓后連說明文件都沒有那就要做好自己當(dāng)作者的心理準(zhǔn)備從一堆命名隨意的腳本里推斷調(diào)用關(guān)系。2.2 解壓命令與密碼恢復(fù)7-Zip 的三種打開姿勢很多人問 7-Zip 能不能解 .rar答案是可以。7-Zip 默認(rèn)支持解開 RAR 格式不需要安裝 WinRAR但要注意它只負(fù)責(zé)解壓不負(fù)責(zé)創(chuàng)建 RAR所以別指望用 7-Zip 直接壓出 .rar。我常用的命令是這三條# 安裝 7-ZipDebian/Ubuntu 系列 sudo apt install p7zip-full # 列出壓縮包內(nèi)容不急著解壓 7z l 自動寫詩.rar # 解壓到指定目錄避免在當(dāng)前目錄散落一堆文件 7z x 自動寫詩.rar -o./autopoet -y參數(shù)說明l是 list先看包內(nèi)文件數(shù)量和路徑結(jié)構(gòu)x是解壓并保留目錄結(jié)構(gòu)-o指定輸出目錄注意-o后面沒有空格-y表示覆蓋已存在文件時不再逐個詢問。如果包里文件很多且路徑很深建議先7z l看一眼確認(rèn)沒有藏著絕對路徑或可疑腳本再動手。遇到帶密碼的壓縮包時7-Zip 會直接提示密碼錯誤。網(wǎng)上那些“強制解壓”的說法大多數(shù)不可靠更加靠譜的做法是先回下載頁找密碼沒找到就按常見密碼列表逐個試再不行才考慮用字典工具跑一輪弱密碼。如果包是用 WinRAR 較新版本默認(rèn)的 AES-256 加密字典跑不出來基本就只能放棄把時間花在重新找一個沒加密的版本上更劃算。與其事后后悔不如下載前先確認(rèn)發(fā)布頁有沒有標(biāo)注密碼。2.3 環(huán)境依賴檢查讓一段詩先跑起來解壓完成后的第一件事不是讀代碼而是復(fù)現(xiàn)運行環(huán)境。我會新建一個虛擬環(huán)境把依賴全部裝進(jìn)去避免解出來的第三方庫污染系統(tǒng) Python。下面是一套很通用的初始化流程# 1) 創(chuàng)建并激活虛擬環(huán)境 python -m venv .venv source .venv/bin/activate # Windows PowerShell 用 .venv\Scripts\Activate.ps1 # 2) 安裝依賴 pip install -r requirements.txt # 3) 查看生成入口腳本的幫助信息 python generate.py --help邏輯說明venv把項目的 numpy、torch 等依賴與系統(tǒng)環(huán)境隔離不同項目之間不打架。requirements.txt如果缺失或裝不上先看報錯發(fā)生在哪個包PyTorch 系經(jīng)常遇到版本與 Python 版本不匹配常見做法是按報錯信息安裝對應(yīng)的 CPU 版。--help是判斷腳本是否支持命令行參數(shù)最快的辦法不支持的話就直接打開源碼看if __name__ __main__部分。跑通一遍最重要。哪怕生成結(jié)果很爛只要沒有報錯就說明模型文件、字符表和腳本三者對得上。很多 .rar 包本身沒問題問題出在解壓后手動移動了模型文件或改了目錄名導(dǎo)致腳本找不到相對路徑。保持解壓后的目錄結(jié)構(gòu)原樣不變能少踩一堆坑。3. 自動寫詩的最小可運行管線從馬爾可夫鏈到溫度采樣的生成邏輯3.1 先分清三類做法規(guī)則、統(tǒng)計與神經(jīng)網(wǎng)絡(luò)自動寫詩的項目包拆開來看實現(xiàn)思路大致分三類。第一類是模板填充預(yù)置一些句式結(jié)構(gòu)再往空位里填意象詞比如“春風(fēng)又綠江南岸”這類模板簡單但幾分鐘就能被看穿第二類是統(tǒng)計語言模型最常見的就是馬爾可夫鏈通過統(tǒng)計語料里字與字的接續(xù)關(guān)系來生成新句子第三類是神經(jīng)網(wǎng)絡(luò)小到單層 LSTM大到基于 Transformer 的預(yù)訓(xùn)練語言模型生成質(zhì)量上限最高但對數(shù)據(jù)量和訓(xùn)練資源的要求也最高。如果 .rar 包里只有一個幾 KB 的腳本那基本是模板或馬爾可夫如果帶著幾十 MB 到幾百 MB 的權(quán)重文件那才是真正的神經(jīng)網(wǎng)絡(luò)。我建議不管包里是哪一類都先用馬爾可夫鏈跑通一個最小數(shù)據(jù)流。原因很實際它不需要 GPU不需要裝深度學(xué)習(xí)框架幾十行代碼就能把“語料進(jìn)、詩句出”的完整鏈路打通也能幫你理解后續(xù)調(diào)參時遇到的所有基本概念。3.2 馬爾可夫鏈生成五言詩一個能跑的數(shù)據(jù)流馬爾可夫鏈的思路是把一首詩看成字與字的接龍。訓(xùn)練階段統(tǒng)計“春”后面經(jīng)常跟哪些字“春風(fēng)”后面又經(jīng)常跟哪些字生成階段從任意起點開始按統(tǒng)計概率挑下一個字。下面這個實現(xiàn)可以直接復(fù)制運行import random from collections import defaultdict def build_chain(corpus, order2): 構(gòu)建字符級 n-gram 轉(zhuǎn)移表。 order2 表示用前 2 個字預(yù)測下一個字。 chain defaultdict(list) for line in corpus: chars list(line.replace( , )) for i in range(len(chars) - order): prefix tuple(chars[i:i order]) next_char chars[i order] chain[prefix].append(next_char) return chain def sample_poem(chain, verse_len5, num_lines4, order2, seedNone): 生成一首五言絕句每句單獨起頭。 if seed is not None: random.seed(seed) lines [] for _ in range(num_lines): line [] start random.choice(list(chain.keys())) line.extend(start) while len(line) verse_len: prefix tuple(line[-order:]) if prefix not in chain: break line.append(random.choice(chain[prefix])) lines.append(.join(line)) return lines邏輯說明build_chain把語料里每行詩拆成字符流用長度為order的前綴做鍵、下一個字符做值得到一張“接龍表”。sample_poem生成每一行時隨機取一個出現(xiàn)在語料里的前綴作為起點這樣詩句的開頭至少是“合法”的隨后不斷用當(dāng)前行末尾order個字查表隨機選后繼字符。每句獨立起頭能避免四句共用一條概率鏈導(dǎo)致的內(nèi)容趨同。參數(shù)上order2是最常用的折中order1生成結(jié)果亂飄幾乎不成詞order3以上句子會越來越像語料里的原句重復(fù)率也明顯升高。seed固定住隨機種子后同一份語料和參數(shù)能復(fù)現(xiàn)同一首詩這對調(diào)試很重要。3.3 關(guān)鍵參數(shù)n-gram 階數(shù)、溫度與采樣策略馬爾可夫鏈生成最大的問題是死板和重復(fù)。要緩解它得引入兩個采樣層面的概念溫度和 top-k。溫度采樣的代碼很直觀本質(zhì)是把候選字的概率分布做一次“軟化”import math def temperature_sample(candidates, temperature0.8): 從候選字列表里按溫度縮放后的概率抽一個。 counts {} for c in candidates: counts[c] counts.get(c, 0) 1 exp_scores {c: math.exp(count / temperature) for c, count in counts.items()} total sum(exp_scores.values()) r random.random() * total cumulative 0 for c, score in exp_scores.items(): cumulative score if r cumulative: return c參數(shù)說明temperature越小概率分布越尖銳輸出越保守越大分布越平坦越容易跳出高頻字的循環(huán)。在字符級古詩生成里0.8起步比較穩(wěn)效果太平淡就往上調(diào)重復(fù)太嚴(yán)重就往下調(diào)。top-k 的常做做法是從概率最高的 k 個字里再采樣一般 k 取 8 到 20這樣能擋住“之、乎、也、者”這類耐造但沒詩意的虛詞刷屏。我見過不少人在這步直接把random.choice換成argmax結(jié)果生成的詩每句都差不太多還以為是模型訓(xùn)練出了問題。其實問題在于確定性解碼天然適合機器翻譯卻不適合創(chuàng)作類任務(wù)。創(chuàng)作類生成要的是每次跑都略有不同采樣策略和模型本身同等重要。4. 訓(xùn)練數(shù)據(jù)與超參調(diào)整讓輸出從押韻到像詩的三個必調(diào)參數(shù)4.1 語料準(zhǔn)備清洗古詩文本的四個細(xì)節(jié)語料質(zhì)量直接決定生成結(jié)果下限這個環(huán)節(jié)偷懶后面所有調(diào)參都是白費。常見做法是拿公開的古詩數(shù)據(jù)集但網(wǎng)上流傳的版本里往往混著作者名、朝代、注釋和現(xiàn)代標(biāo)點這些東西進(jìn)語料后模型會把“唐”和“白居易”當(dāng)作詩句的一部分學(xué)進(jìn)去。我一般先做一輪清洗import re def clean_corpus(raw_text): # 只保留漢字與全角標(biāo)點 text re.sub(r[^\u4e00-\u9fff。], , raw_text) # 去掉句讀轉(zhuǎn)成不換行的字符流適合字符級訓(xùn)練 text re.sub(r[。], , text) return text邏輯說明第一行正則把字母、數(shù)字、空格、半角符號全部過濾掉第二行再把句讀也去掉。對字符級語言模型來說標(biāo)點符號是多余的模型只需要學(xué)“字接字”的規(guī)律。注意兩個細(xì)節(jié)繁體字要不要轉(zhuǎn)簡體取決于你希望生成古風(fēng)還是現(xiàn)代風(fēng)文本編碼必須統(tǒng)一成 UTF-8否則讀進(jìn)來全是亂碼這一步出錯最隱蔽因為程序不報錯只是生成結(jié)果莫名其妙。清洗后的語料如果只有幾千首生成的詩歌會很窄翻來覆去就是那幾千句的影子理想情況是幾萬首以上的唐詩規(guī)模。不需要全部喂給神經(jīng)網(wǎng)絡(luò)馬爾可夫鏈模型在幾萬首語料上就能跑出不錯的效果。4.2 五言、七言與絕句約束用格式后處理兜底生成模型天然不保證長度和結(jié)構(gòu)哪怕訓(xùn)練語料都是五言模型輸出的句子也可能七零八落。解決這個問題的不是改模型而是在解碼后加一層格式約束。我常用的后處理函數(shù)如下def to_verse(text, verse_len5, num_lines4): 把任意字符串強制切分成五言或七言絕句。 text clean_corpus(text) lines [] for i in range(0, min(len(text), verse_len * num_lines), verse_len): lines.append(text[i:i verse_len]) while len(lines) num_lines: lines.append(春 * verse_len) # 不足時補位寧可露怯也不報錯 return lines[:num_lines]參數(shù)說明verse_len5是五言換成 7 就是七言num_lines默認(rèn) 4 對應(yīng)絕句。這個函數(shù)的思路是從模型輸出的字符流里按固定步長切塊多余的字符直接丟掉不足的地方用“春”字補位。你要是覺得補位太難看也可以用“花”或“山”替代或者干脆截斷整個流重新生成一次。很多項目包會在生成腳本里內(nèi)置類似的強制切分邏輯但做得不夠干凈有的忘了去掉標(biāo)點導(dǎo)致五言句子里混著句號有的沒有處理生成流過短的情況直接越界報錯。自己寫一遍這個后處理能幫你理解那些腳本為什么在個別例子上會翻車。4.3 三個必調(diào)參數(shù)學(xué)習(xí)率、批次大小與序列長度如果你拿到的是帶訓(xùn)練代碼的自動寫詩包最終還是要面對訓(xùn)練參數(shù)這三件套。我整理過一份自己常用的參考區(qū)間參數(shù)常用范圍調(diào)整方向失敗跡象學(xué)習(xí)率1e-3 ~ 1e-4loss 震蕩就調(diào)小一個量級loss 不降、NaN批次大小32 ~ 128顯存不夠就減半OOM、訓(xùn)練極慢序列長度64 ~ 128 字符古詩短句可降到 64整句割裂、不連貫學(xué)習(xí)率是這里最帶玄學(xué)色彩的一個。同樣一份唐詩語料1e-3 能跑但 loss 上下亂跳降到 5e-4 反而平穩(wěn)再往下到 1e-4訓(xùn)練速度又慢得讓人失去耐心。我的經(jīng)驗是先按 1e-3 跑 10 個 epoch 看 loss 曲線如果持續(xù)不降直接砍到 1e-4不要浪費時間找其他原因。批次大小的影響相對弱但對于字符級模型過小的批次會讓梯度估計充滿噪音大批次則要求更多內(nèi)存。序列長度方面古詩不像長文檔那樣需要很長的上下文依賴64 個字符足夠讓模型看到大半個句子。訓(xùn)練過程中最值得盯的指標(biāo)不是 loss 本身而是“生成的樣例詩”。每隔固定輪次抽幾首出來看如果連續(xù)押韻、句子通順但意思飄忽那是正常的創(chuàng)作狀態(tài)如果句子重復(fù)、韻腳混亂才需要回頭調(diào)參。死磕 loss 數(shù)值容易誤導(dǎo)生成結(jié)果才是唯一的判斷標(biāo)準(zhǔn)。5. 自動寫詩常見翻車點排查五個必踩的坑與恢復(fù)手段5.1 現(xiàn)象解壓后跑起來就報 ModuleNotFoundError原因requirements.txt 缺失或版本沒鎖住最常見的是 Python 3.11 以上環(huán)境里裝不上某些舊版依賴也可能是腳本用了某一臺機器上的全局包換個環(huán)境就露餡。解決先看報錯里缺的是哪個模塊。如果是 torch 或 tensorflow直接按官方指引安裝當(dāng)前 Python 版本對應(yīng)的版本如果缺的是小眾工具庫先查它是否被維護(hù)維護(hù)停滯的就改腳本繞過去。建議在虛擬環(huán)境里操作不要pip install到系統(tǒng)環(huán)境否則下個項目又會踩同一顆雷。5.2 現(xiàn)象生成的每一句都重復(fù)同一個字原因馬爾可夫鏈的 order 設(shè)得太大前綴一旦進(jìn)入某個高頻路徑就出不來了神經(jīng)網(wǎng)絡(luò)這邊則大概率是解碼時用了argmax或者溫度設(shè)得太低模型陷入自我重復(fù)的循環(huán)。解決先把 order 降到 2再加溫度采樣temperature 提到 0.9 以上試一輪如果還重復(fù)給生成加重復(fù)懲罰常見做法是當(dāng)某字已經(jīng)在前一句出現(xiàn)過時降低它的采樣權(quán)重。你可以先跑 short 的語料測試比如拿 1000 首絕句重復(fù)生成 20 次統(tǒng)計重復(fù)率比自己肉眼看好使。5.3 現(xiàn)象詩太平淡像在念說明書原因溫度太低、語料里混了太多白話文或者語料規(guī)模太小。有些公開“古詩數(shù)據(jù)集”其實包含現(xiàn)代白話翻譯清洗那一步?jīng)]做干凈模型學(xué)到的就是“這是一首什么樣的詩”之類的現(xiàn)代句子結(jié)構(gòu)。解決把 temperature 調(diào)到 1.2 左右配合 top-p0.92 試試同時回頭統(tǒng)計語料里頻率最高的 50 個字如果出現(xiàn)“我、你、這、那、是”這類詞說明清洗不到位需要重新過一遍正則。真正干凈的古詩語料高頻字應(yīng)該是“風(fēng)、花、雪、月、人、山、水、春、秋”。5.4 現(xiàn)象輸出長度對不上五言/七言原因后處理切分邏輯不對或者生成流里有標(biāo)點。最常見的是把句讀 保留在字符流里就按 5 字符切塊結(jié)果一句里混進(jìn)“?!币曈X上長短不齊。解決先統(tǒng)一過一遍clean_corpus把所有標(biāo)點去掉再用定長切塊。從那些老牌項目包移植代碼時尤其注意它們是否對全角逗號做了處理。有的包寫的是text[::6]這種按索引步長切遇到標(biāo)點也一樣翻車。這個坑我自己也踩過代碼邏輯看半天沒毛病最后發(fā)現(xiàn)是語料里混進(jìn)了換行符。5.5 現(xiàn)象CPU 推理慢到懷疑人生原因如果是神經(jīng)網(wǎng)絡(luò)方案CPU 上跑大模型本來就慢加上腳本沒有做任何加速處理每生成一句要推理一次四句下來要等幾十秒。解決先確認(rèn)腳本是不是每次生成都重新加載了模型這是最常見的性能 bug。模型加載一次后放內(nèi)存里復(fù)用速度能快一個量級。進(jìn)一步的做法是把模型轉(zhuǎn)成 ONNX用推理引擎跑PyTorch 環(huán)境下一行代碼就能導(dǎo)出import torch model.eval() dummy_input torch.randint(0, 1000, (1, 64)) torch.onnx.export(model, dummy_input, poem.onnx, opset_version12)參數(shù)說明dummy_input的維度要匹配模型輸入形狀opset_version是 ONNX 算子版本太舊可能不支持新算子。轉(zhuǎn)完之后用 onnxruntime 加載在 CPU 上的推理速度通常能提升 2 到 5 倍。不過這一步屬于優(yōu)化先把前面幾項坑排查完再動手否則可能在錯誤的方向上加速。6. 進(jìn)階用押韻校驗與困惑度給生成詩做體檢生成結(jié)果好不好光靠肉眼判斷不夠。我常用的兩個客觀指標(biāo)是韻腳一致性和困惑度前者檢查是否押韻后者評估句子通順程度。韻腳的檢查依賴一份韻部表。中華新韻把漢字分成十八個韻部同一個韻部的字可以互相押韻。先把韻部表整理成集合然后校驗每句尾字是否落在目標(biāo)韻部里def check_rhyme(lines, rhyme_set): 檢查絕句偶數(shù)句或全詩尾字是否同一個韻部。 tails [line[-1] for line in lines if len(line) 0] if len(tails) 2: return False return all(t in rhyme_set for t in tails[1::2])參數(shù)說明rhyme_set是你選定的韻部漢字集合tails[1::2]取出第 2、4 句的尾字。絕句一般要求第 2、4 句押韻第 1 句可押可不押。跑一遍這個函數(shù)就能量化“看起來押韻”這個主觀判斷。困惑度適合用來對比兩個模型的生成質(zhì)量。具體做法是拿一個現(xiàn)成的語言模型對生成的詩句打分困惑度越低說明這句詩在統(tǒng)計意義上是越自然的表達(dá)。沒有現(xiàn)成模型時可以拿你自己訓(xùn)練的馬爾可夫鏈當(dāng)打分器計算每個字在給定前綴下的條件概率把整句概率連乘再取負(fù)對數(shù)得到一個簡化版困惑度用于橫向?qū)Ρ炔煌瑓?shù)下的生成結(jié)果。我自己的習(xí)慣是每調(diào)一輪參數(shù)就跑押韻校驗和簡化困惑度兩個分?jǐn)?shù)都變好才進(jìn)入下一輪。最后悔的經(jīng)歷是早期只顧著讓生成結(jié)果“看起來像詩”沒留任何客觀指標(biāo)后來換語料和調(diào)參時根本說不清哪個改動起了作用一切回到了原點?,F(xiàn)在每次拿到這類壓縮包我都會先建虛擬環(huán)境、保留原始 rar 備份、跑通最小生成再開始談?wù){(diào)優(yōu)。這個順序能替你省下大量不該踩的坑希望幫到你。本文還有配套的精品資源點擊獲取