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