參與避坑)
簡(jiǎn)介基于樸素貝葉斯算法的新聞分類項(xiàng)目源碼主要面向需要完成Python期末大作業(yè)或課程設(shè)計(jì)的計(jì)算機(jī)專業(yè)學(xué)生也適合希望在文本分類任務(wù)中實(shí)戰(zhàn)樸素貝葉斯模型的初學(xué)者難度適中能夠覆蓋從數(shù)據(jù)預(yù)處理到分類評(píng)估的完整項(xiàng)目實(shí)踐。資源共2000個(gè)文件含1996個(gè)txt詞頻數(shù)據(jù)、3個(gè)py腳本及1個(gè)md說明文檔壓縮包大小約13.33MBpy腳本分別實(shí)現(xiàn)貝葉斯訓(xùn)練、分類器核心與URL解析等流程txt文件為新聞文本預(yù)處理后的特征詞頻表md文檔幫助快速理解項(xiàng)目結(jié)構(gòu)。源碼已經(jīng)過本地編譯調(diào)試可穩(wěn)定運(yùn)行且作為導(dǎo)師指導(dǎo)下完成的高分大作業(yè)評(píng)審得分為98分在分類效果與代碼規(guī)范方面均有較好表現(xiàn)。目前已有154人學(xué)習(xí)下載讀者可借助源碼與詞頻數(shù)據(jù)完整理解樸素貝葉斯從訓(xùn)練到預(yù)測(cè)的流程并據(jù)此擴(kuò)展實(shí)現(xiàn)其他文本分類場(chǎng)景。1. 樸素貝葉斯做新聞分類為什么這套項(xiàng)目源碼值得你完整跑一遍新聞分類是大多數(shù)人接觸 NLP 的第一個(gè)完整項(xiàng)目樸素貝葉斯算法則是這類項(xiàng)目源碼里最常見的基準(zhǔn)模型。別看它“樸素”在幾千條標(biāo)注數(shù)據(jù)、類別邊界清晰的中文新聞上它常能和復(fù)雜模型打平訓(xùn)練卻只要幾秒鐘。這套項(xiàng)目源碼做的事很具體用帶類別標(biāo)簽的新聞文本訓(xùn)練分類器讓新來的新聞自動(dòng)歸入財(cái)經(jīng)、體育、娛樂、科技等欄目。源碼里通常包含分詞、特征提取、模型訓(xùn)練、評(píng)估的完整鏈路讀懂它文本分類主流程就走通了一遍。適合剛做完 Python 基礎(chǔ)、想用 sklearn 動(dòng)手的初學(xué)者也適合拿到“樸素貝葉斯新聞分類”作業(yè)、需要改跑源碼的讀者。下文按“原理 → 跑通 → 調(diào)參 → 避坑 → 進(jìn)階”講透。2. 樸素貝葉斯的原理與選型新聞分類為什么首選多項(xiàng)式模型拿到“基于樸素貝葉斯解決新聞分類”的源碼第一件事不是急著跑代碼而是先把模型看懂。很多初學(xué)者直接調(diào) sklearn 的 MultinomialNB三行代碼跑完換一個(gè)數(shù)據(jù)集就翻車根因是沒搞懂這個(gè)“樸素”到底樸素在哪、多項(xiàng)式模型又比另外兩個(gè)變體好在哪。這一章把原理和選型一次講清。2.1 貝葉斯定理到“樸素”假設(shè)新聞分類的數(shù)學(xué)底子貝葉斯定理本身不復(fù)雜P(類別 | 文本) P(文本 | 類別) × P(類別) / P(文本)我們要的是給定一篇新聞它屬于每個(gè)類別的后驗(yàn)概率取最大者作為預(yù)測(cè)類別。真正的難點(diǎn)在 P(文本 | 類別)一篇新聞?dòng)袔资畟€(gè)詞詞與詞之間的聯(lián)合分布幾乎沒法直接估。樸素貝葉斯在這里做了一步大膽簡(jiǎn)化——假設(shè)文本里的詞在給定類別條件下互相獨(dú)立于是P(文本 | 類別) Π P(詞_i | 類別)拿“湖人隊(duì)擊敗勇士隊(duì)”舉例簡(jiǎn)化后只需要估計(jì) P(“湖人隊(duì)” | 體育) × P(“擊敗” | 體育) × P(“勇士隊(duì)” | 體育)每個(gè)詞的概率都能從訓(xùn)練語(yǔ)料里數(shù)出來。這個(gè)假設(shè)在真實(shí)語(yǔ)言里顯然不成立“湖人隊(duì)”和“勇士隊(duì)”出現(xiàn)在同一篇新聞里絕非獨(dú)立事件。但有意思的是分類任務(wù)只需要比較后驗(yàn)概率的大小不需要概率值精確所以哪怕假設(shè)錯(cuò)了排序往往仍然正確。這正是樸素貝葉斯“錯(cuò)了還能打”的底層原因。接下去要選變體。sklearn 提供三個(gè)常用實(shí)現(xiàn)GaussianNB 面向連續(xù)特征假設(shè)特征服從高斯分布BernoulliNB 面向 0/1 布爾特征適合“詞是否出現(xiàn)”的表示MultinomialNB 面向詞頻或 TF-IDF 這類非負(fù)數(shù)值假設(shè)特征服從多項(xiàng)式分布。新聞文本經(jīng)過分詞和向量化后得到的是“每個(gè)詞在每篇文檔里的重要度”矩陣恰好落在多項(xiàng)式模型的射程內(nèi)所以項(xiàng)目源碼里幾乎清一色用 MultinomialNB。如果你看到有人用 GaussianNB 處理詞頻矩陣基本可以判斷是誤用——詞頻是離散計(jì)數(shù)硬套高斯分布會(huì)損失大量分布信息。從概率公式還能看出樸素貝葉斯天生適合新聞分類它把 P(類別) 先驗(yàn)概率和 P(詞 | 類別) 似然分開建模類別邊界越清晰、各類別用詞差異越大樸素假設(shè)造成的誤差越小。財(cái)經(jīng)和科技的高頻詞幾乎不重疊時(shí)即使獨(dú)立性假設(shè)不成立乘積排序依然可靠。這也是為什么在小樣本新聞數(shù)據(jù)上樸素貝葉斯經(jīng)常比詞向量加神經(jīng)網(wǎng)絡(luò)的組合還要穩(wěn)——它不需要海量數(shù)據(jù)來擬合詞與詞的交互關(guān)系。2.2 中文分詞與特征表示詞頻向量還是 TF-IDF中文新聞沒有天然空格訓(xùn)練前第一步永遠(yuǎn)是分詞。這類源碼最常見的工具是 jieba把整篇新聞切成詞序列過濾停用詞后用空格拼成字符串交給 sklearn 向量化器。分詞粒度直接影響特征質(zhì)量“湖人隊(duì)”被切成“湖人”“隊(duì)”還是保持完整取決于 jieba 詞庫(kù)和是否加載了自定義詞典。我一般會(huì)在項(xiàng)目里維護(hù)一個(gè)領(lǐng)域詞表把球隊(duì)名、人名、產(chǎn)品名用 jieba.add_word() 加進(jìn)去。特征表示有兩條路線。CountVectorizer 統(tǒng)計(jì)詞在每篇文檔的出現(xiàn)次數(shù)直接喂給 MultinomialNB語(yǔ)義上最貼合多項(xiàng)式分布TfidfVectorizer 對(duì)詞頻做 IDF 加權(quán)壓制“的、了、在”這類高頻但不帶信息的詞還能用 sublinear_tf 對(duì)詞頻取對(duì)數(shù)壓縮量級(jí)。對(duì)比項(xiàng)CountVectorizerTfidfVectorizer特征值含義詞在文檔中出現(xiàn)次數(shù)詞頻 × 逆文檔頻率是否適合 MultinomialNB最貼合多項(xiàng)式分布可以特征值非負(fù)即可對(duì)高頻無(wú)意義詞的壓制無(wú)靠停用詞表有IDF 自動(dòng)降權(quán)新聞分類實(shí)測(cè) F1基準(zhǔn)水平通常高 13 個(gè)點(diǎn)我的經(jīng)驗(yàn)是新聞分類優(yōu)先用 TfidfVectorizer但樸素貝葉斯對(duì)兩種表示都不排斥真正影響大的是 max_features。新聞?wù)Z料詞表動(dòng)輒幾萬(wàn)大量低頻詞只出現(xiàn)一兩次對(duì)分類沒有貢獻(xiàn)還拖慢訓(xùn)練。常見做法是把特征維度限制在 500010000配合 min_df2 過濾只出現(xiàn)一次的詞、max_df0.8 過濾出現(xiàn)在八成以上文檔的大路詞。調(diào)這三個(gè)參數(shù)比糾結(jié) CountVectorizer 還是 TfidfVectorizer 收益大得多。import jieba from sklearn.feature_extraction.text import TfidfVectorizer # 加載停用詞表常見的做法是每行一個(gè)詞 with open(stopwords.txt, encodingutf-8) as f: stopwords {line.strip() for line in f} def cut_news(text: str) - str: words [w for w in jieba.cut(text) if w.strip() and w not in stopwords] return .join(words) sample 湖人隊(duì)主場(chǎng)擊敗勇士隊(duì)詹姆斯砍下三雙 print(cut_news(sample)) # 輸出示例湖人隊(duì) 主場(chǎng) 擊敗 勇士隊(duì) 詹姆斯 砍下 三雙 vectorizer TfidfVectorizer( max_features8000, min_df2, max_df0.8, sublinear_tfTrue )代碼邏輯說明cut_news 先分詞再過濾空串和停用詞最后空格拼接——不做這一步向量化器會(huì)把整句當(dāng)成一個(gè)“詞”特征維度徹底亂掉。TfidfVectorizer 的參數(shù)里max_features 限制詞表規(guī)模min_df 和 max_df 從頻次兩端裁剪sublinear_tf 做詞頻對(duì)數(shù)壓縮。這四個(gè)參數(shù)是新聞分類特征工程的標(biāo)配第 4 章還會(huì)展開講。3. 把項(xiàng)目源碼跑通從數(shù)據(jù)到模型的最小可運(yùn)行路徑3.1 環(huán)境準(zhǔn)備與數(shù)據(jù)格式先讓數(shù)據(jù)能進(jìn)模型這類源碼通?;?Python 3.7 以上核心依賴是 scikit-learn、pandas、jieba裝起來沒什么玄學(xué)pip 一行解決。真正卡住新手的是數(shù)據(jù)格式源碼 README 里可能寫著 THUCNews 數(shù)據(jù)集也可能只是一個(gè) data 目錄但模型能吃的只有一種約定——結(jié)構(gòu)化表格。常見做法是把語(yǔ)料整理成 CSV兩列l(wèi)abel 是新聞?lì)悇econtent 是正文UTF-8 編碼。pip install scikit-learn pandas jieba joblib裝完之后先用 pandas 讀一遍數(shù)據(jù)打印類別分布。這一步別跳過類別數(shù)量如果差一個(gè)數(shù)量級(jí)模型會(huì)悄悄偏向大類整體準(zhǔn)確率看著不錯(cuò)逐類看 F1 就露餡。我的習(xí)慣是把 value_counts() 的結(jié)果掛到項(xiàng)目 README 里每次復(fù)現(xiàn)先核對(duì)數(shù)據(jù)再談模型。另一種常見的數(shù)據(jù)組織方式是按類別分目錄每個(gè)目錄下是一篇篇 .txt 新聞。遇到這種結(jié)構(gòu)需要先寫一個(gè)小腳本把目錄結(jié)構(gòu)展平成 CSV再進(jìn)入下面的流程。我一般會(huì)在 utils.py 里放一個(gè) load_data 函數(shù)統(tǒng)一處理 CSV 和目錄兩種輸入返回 DataFrame這樣 train.py 和 predict.py 都不關(guān)心數(shù)據(jù)原始長(zhǎng)什么樣。3.2 訓(xùn)練主流程分詞、向量化、訓(xùn)練、評(píng)估一條龍下面這段代碼涵蓋了此類源碼的核心鏈路可以直接存成 train.py每步加了注釋方便你對(duì)照自己拿到的源碼找差異。import jieba import pandas as pd from sklearn.model_selection import train_test_split from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.naive_bayes import MultinomialNB from sklearn.metrics import classification_report # 1. 讀數(shù)據(jù) df pd.read_csv(news_data.csv, encodingutf-8) df df.dropna(subset[content, label]) print(df[label].value_counts()) # 2. 分詞整篇新聞切成詞序列空格拼回字符串 df[cut] df[content].apply(lambda x: .join(jieba.cut(x))) # 3. 切分先切分再向量化stratify 保持類別比例 X_train, X_test, y_train, y_test train_test_split( df[cut], df[label], test_size0.2, random_state42, stratifydf[label] ) # 4. 向量化fit 只允許出現(xiàn)在訓(xùn)練集上 vectorizer TfidfVectorizer(max_features8000, sublinear_tfTrue) X_train_vec vectorizer.fit_transform(X_train) X_test_vec vectorizer.transform(X_test) # 5. 訓(xùn)練alpha 是拉普拉斯平滑系數(shù) model MultinomialNB(alpha1.0) model.fit(X_train_vec, y_train) # 6. 評(píng)估 y_pred model.predict(X_test_vec) print(classification_report(y_test, y_pred))邏輯說明分三段。切分階段stratify 讓訓(xùn)練集和測(cè)試集保持與原始數(shù)據(jù)一致的類別比例這是對(duì)付樣本不平衡的第一道防線很多源碼省略它小類樣本會(huì)在切分時(shí)被切沒。向量化階段fit_transform 只在訓(xùn)練集上調(diào)用測(cè)試集只走 transform兩者順序一旦顛倒就是數(shù)據(jù)泄漏第 5 章會(huì)專門講。訓(xùn)練階段alpha 默認(rèn) 1.0 解決“某個(gè)詞在訓(xùn)練集中沒出現(xiàn)導(dǎo)致概率為零”的零概率問題取值大小直接影響模型保守還是激進(jìn)。第一次跑通準(zhǔn)確率在 0.850.95 都算正常具體看數(shù)據(jù)集類別數(shù)和文本質(zhì)量。如果低于 0.8先別換模型回頭查分詞和停用詞表八成是那兩步出了問題。還有一個(gè)自查點(diǎn)把詞表前幾十個(gè)詞打出來看如果滿是“的、了、在”這類虛詞說明停用詞沒生效。這段代碼在幾千條新聞、八千維特征上訓(xùn)練通常在一秒以內(nèi)慢的是分詞那一步建議把分詞結(jié)果緩存到磁盤下次直接讀 cut 列能省下大半時(shí)間。3.3 源碼目錄結(jié)構(gòu)一份合格的新聞分類項(xiàng)目長(zhǎng)什么樣拿到項(xiàng)目源碼第一步是看目錄不是跑代碼。我見過的最小可用結(jié)構(gòu)是這樣的news_classifier/ ├── data/ # 原始語(yǔ)料CSV 或按類別分好的文本目錄 ├── train.py # 訓(xùn)練主腳本產(chǎn)物是模型文件 ├── predict.py # 加載模型對(duì)單條新聞?lì)A(yù)測(cè) ├── utils.py # 分詞、停用詞、數(shù)據(jù)加載等公共函數(shù) ├── model.pkl # 模型文件注意是否包含向量化器 └── stopwords.txt # 停用詞表每行一個(gè)詞判斷源碼是否合格看兩個(gè)點(diǎn)。第一predict.py 是否同時(shí)加載了向量化器和模型。只保存 model.pkl、丟掉 vectorizer 的源碼不在少數(shù)預(yù)測(cè)時(shí)詞表對(duì)不上輸出基本是亂猜。第二train.py 是否固定了隨機(jī)種子。不設(shè) random_state每次跑出來的劃分和結(jié)果都不一樣你復(fù)現(xiàn)不了作者聲稱的準(zhǔn)確率還會(huì)誤以為是自己環(huán)境的問題。predict.py 的常見寫法長(zhǎng)這樣# predict.py 核心邏輯 import joblib import jieba pipe joblib.load(news_pipeline.pkl) def predict_one(text: str) - str: cut_text .join(jieba.cut(text)) return pipe.predict([cut_text])[0] print(predict_one(湖人隊(duì)主場(chǎng)擊敗勇士隊(duì)詹姆斯砍下三雙)) # 輸出示例體育這段代碼暴露了一個(gè)容易忽略的細(xì)節(jié)分詞必須和訓(xùn)練時(shí)保持完全一致包括停用詞表、jieba 自定義詞典。如果 predict.py 里重新寫了一套分詞邏輯哪怕只是停用詞加載路徑不同預(yù)測(cè)效果就會(huì)和訓(xùn)練時(shí)對(duì)不上。這是“模型文件有了、效果卻復(fù)現(xiàn)不了”的最大來源之一。4. 參數(shù)調(diào)優(yōu)與效果驗(yàn)證alpha、特征維度與交叉驗(yàn)證4.1 alpha 的取值從默認(rèn) 1.0 到網(wǎng)格搜索alpha 是樸素貝葉斯項(xiàng)目源碼里最值得調(diào)的參數(shù)它對(duì)應(yīng)拉普拉斯平滑的強(qiáng)度。訓(xùn)練時(shí)如果某個(gè)詞只在個(gè)別類別里出現(xiàn)過它在其他類別下的概率會(huì)被數(shù)成零乘到整篇新聞的后驗(yàn)概率上直接把這個(gè)類別判死。alpha 給所有詞的計(jì)數(shù)統(tǒng)一加一個(gè)小數(shù)避免零概率一刀切。alpha 越大各類別下的詞概率越均勻模型越保守alpha 越小模型越依賴訓(xùn)練集詞頻過擬合風(fēng)險(xiǎn)越高。在新聞分類場(chǎng)景下我的經(jīng)驗(yàn)是數(shù)據(jù)量大、詞表豐富時(shí)alpha 取 0.1 左右往往比默認(rèn) 1.0 更準(zhǔn)數(shù)據(jù)量小、文本短時(shí)1.0 更穩(wěn)。不要靠感覺猜直接把 alpha 扔進(jìn)網(wǎng)格搜索from sklearn.model_selection import GridSearchCV param_grid {alpha: [0.01, 0.05, 0.1, 0.5, 1.0, 2.0]} gs GridSearchCV( MultinomialNB(), param_grid, cv5, scoringf1_macro, n_jobs-1 ) gs.fit(X_train_vec, y_train) print(gs.best_params_, gs.best_score_)這里 scoring 我特意選 f1_macro 而不是 accuracy原因很現(xiàn)實(shí)新聞數(shù)據(jù)里體育類占 60% 的話模型全猜體育也有 60% 準(zhǔn)確率但每個(gè)類別的 F1 會(huì)立刻暴露短板。f1_macro 對(duì)每個(gè)類別一視同仁地求平均逼著模型把少數(shù)類也學(xué)起來。網(wǎng)格搜索跑完后把最優(yōu) alpha 回填到 train.py 重新訓(xùn)練測(cè)試集上再出一版最終報(bào)告。注意 GridSearchCV 的 cv5 是在訓(xùn)練集內(nèi)部再切五折測(cè)試集從頭到尾不能碰否則調(diào)參過程同樣會(huì)泄漏。4.2 混淆矩陣哪兩類新聞總被搞混分類報(bào)告只能告訴你每類的精確率、召回率和 F1但“哪兩類在打架”得看混淆矩陣。新聞分類里最典型的是財(cái)經(jīng)和商業(yè)、科技和數(shù)碼這類邊界模糊的類別高頻詞大量重疊模型分不清很正常??椿煜仃嚨囊饬x在于——如果錯(cuò)分集中在一兩對(duì)類別你要做的是合并類別或補(bǔ)充語(yǔ)料而不是繼續(xù)調(diào)參。import matplotlib.pyplot as plt from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay cm confusion_matrix(y_test, y_pred, labelsmodel.classes_) disp ConfusionMatrixDisplay(confusion_matrixcm, display_labelsmodel.classes_) disp.plot(xticks_rotation45) plt.tight_layout() plt.savefig(confusion_matrix.png, dpi150)讀矩陣有個(gè)順序先看對(duì)角線再看對(duì)角線之外最大的格子。對(duì)角線占比高說明整體可靠非對(duì)角線上的熱點(diǎn)代表固定的混淆對(duì)比如“科技”經(jīng)常被分到“數(shù)碼”通常是語(yǔ)料來源高度重疊合并成一個(gè)類反而讓任務(wù)更清晰。保存成圖片放進(jìn)項(xiàng)目里比每次訓(xùn)練后打印一屏數(shù)字直觀得多。類別超過十個(gè)時(shí)記得調(diào)大圖片尺寸否則邊緣類別的標(biāo)簽會(huì)擠成一團(tuán)。我習(xí)慣把混淆矩陣和分類報(bào)告一起存成 result 目錄后面換模型、換特征都能直接對(duì)比。4.3 特征維度與卡方檢驗(yàn)把詞表選擇交給算法調(diào)完 alpha 收益不大時(shí)問題大概率在特征工程而不是模型。max_features 從 8000 調(diào)到 15000有時(shí)候漲一兩個(gè)點(diǎn)有時(shí)候反而掉取決于語(yǔ)料里有效特征的密度。判斷方法是打印詞表前幾十個(gè)詞如果混著大量無(wú)關(guān)詞說明停用詞沒加夠或 max_df 設(shè)得太寬松。比人肉看詞表更可靠的做法是卡方檢驗(yàn)sklearn 的 chi2 配合 SelectKBest可以直接在向量化之后選出對(duì)類別區(qū)分貢獻(xiàn)最大的詞from sklearn.feature_selection import SelectKBest, chi2 selector SelectKBest(chi2, k3000) X_train_sel selector.fit_transform(X_train_vec, y_train) X_test_sel selector.transform(X_test_vec) sel_model MultinomialNB(alpha0.1) sel_model.fit(X_train_sel, y_train) print(sel_model.score(X_test_sel, y_test))卡方檢驗(yàn)的思路是對(duì)每個(gè)詞計(jì)算它與類別標(biāo)簽的相關(guān)性保留相關(guān)性最高的前 k 個(gè)。這樣選出來的特征比“憑肉眼刪詞”穩(wěn)定得多尤其適合新聞這種詞表巨大的場(chǎng)景。需要留意的是SelectKBest 同樣只能在訓(xùn)練集上 fit測(cè)試集只做 transform和第 3 章向量化器的規(guī)則一致。如果加了特征選擇后效果反而下降通常是 k 取得太小把有區(qū)分度的詞也裁掉了從 5000 往上加著試。5. 新聞分類源碼落地的五個(gè)坑按這個(gè)順序排查課程項(xiàng)目的源碼能跑通是一回事移植到自己的數(shù)據(jù)上是另一回事。下面五個(gè)坑是我在復(fù)現(xiàn)這類源碼時(shí)真實(shí)踩過的每條按“現(xiàn)象 → 原因 → 解決”寫你遇到同類問題可以直接按這個(gè)順序排查。5.1 現(xiàn)象小類新聞幾乎全被分錯(cuò)輸出類別集中在大類原因訓(xùn)練語(yǔ)料類別嚴(yán)重不平衡又沒做分層切分。娛樂類兩萬(wàn)條、科技類五百條train_test_split 里不寫 stratifydf[label]測(cè)試集可能一條科技新聞都分不到模型學(xué)到的先驗(yàn)概率也徹底偏向大類。整體準(zhǔn)確率看著挺高逐類看報(bào)告就露餡。解決切分時(shí)帶上 stratify 參數(shù)。數(shù)據(jù)還是不平衡的話對(duì)小類做簡(jiǎn)單過采樣把文本重復(fù)采樣幾份或使用 imbalanced-learn 的 RandomOverSampler。這里有個(gè)我當(dāng)初來回確認(rèn)過的點(diǎn)sklearn 的 MultinomialNB 沒有 class_weight 參數(shù)想在樸素貝葉斯里直接給類別加權(quán)行不通只能從數(shù)據(jù)側(cè)動(dòng)手。5.2 現(xiàn)象測(cè)試集上效果不錯(cuò)模型上線后面對(duì)新數(shù)據(jù)效果崩塌原因源碼把向量化器的 fit_transform 用在了全量數(shù)據(jù)上做完特征提取再切訓(xùn)練測(cè)試集。這樣詞表已經(jīng)包含了測(cè)試集的分布信息測(cè)試集指標(biāo)是虛高的新數(shù)據(jù)一進(jìn)來立刻現(xiàn)原形。這是這類源碼里最常見的數(shù)據(jù)泄漏寫法不少課程項(xiàng)目都中招。解決嚴(yán)格按“先切分、后向量化”的順序執(zhí)行。全流程只允許在訓(xùn)練集上調(diào)用 fit_transform測(cè)試集和未來任何新數(shù)據(jù)一律只走 transform。檢查代碼只需要看一個(gè)位置vectorizer 是在 train_test_split 之前還是之后被 fit 的在之前就是泄漏。注意判斷數(shù)據(jù)泄漏最簡(jiǎn)單的標(biāo)準(zhǔn)就是看向量化器的 fit 動(dòng)作發(fā)生在切分之前還是之后。5.3 現(xiàn)象保存模型后predict.py 的預(yù)測(cè)結(jié)果和訓(xùn)練時(shí)對(duì)不上原因只保存了模型對(duì)象沒保存向量化器。predict.py 里重新 new 了一個(gè) TfidfVectorizer詞表跟訓(xùn)練時(shí)的完全不匹配預(yù)測(cè)就成了黑匣子亂輸出。這類“模型文件有了、效果卻復(fù)現(xiàn)不了”的問題在源碼交接時(shí)尤其害人。解決用 joblib.dump 同時(shí)保存向量化器和模型更省事的做法是把兩者包成一個(gè) Pipeline 一次保存第 3 章 predict.py 里已經(jīng)寫好了完整寫法。排查時(shí)有個(gè)十秒鐘出結(jié)果的檢查對(duì)比訓(xùn)練時(shí) vectorizer.get_feature_names_out() 的長(zhǎng)度和 predict.py 里新 vectorizer 的長(zhǎng)度不一致就說明詞表沒對(duì)上。額外提醒一點(diǎn)Pipeline 不會(huì)替你分詞predict 入口的文本必須先用和訓(xùn)練一致的分詞函數(shù)處理否則喂進(jìn)去的是未切分的原始字符串特征空間直接對(duì)不上。5.4 現(xiàn)象中文新聞亂碼或跑出來的特征全是單字和數(shù)字原因數(shù)據(jù)文件編碼不是 UTF-8常見于 GBK 編碼的舊語(yǔ)料也可能是 jieba 沒加載自定義詞典人名、球隊(duì)名被切成單字特征質(zhì)量斷崖式下降。這兩個(gè)原因看起來毫無(wú)關(guān)系但癥狀容易被混淆——都表現(xiàn)為模型效果莫名其妙地差。解決先解決編碼pd.read_csv 報(bào)編碼錯(cuò)誤時(shí)依次試 utf-8、gbk、gb18030不要一個(gè)編碼用到底。再解決分詞把領(lǐng)域詞表通過 jieba.add_word() 加進(jìn)去然后打印兩篇分詞結(jié)果對(duì)比確認(rèn)“湖人隊(duì)”保留成整詞而不是“湖人”“隊(duì)”。這一步值得最先排查因?yàn)樗茏?F1 掉五六個(gè)點(diǎn)而且和模型參數(shù)完全無(wú)關(guān)。排查順序建議先看數(shù)據(jù)編碼和分詞輸出再看切分順序最后才是調(diào)參數(shù)——順序反了會(huì)浪費(fèi)大量時(shí)間。5.5 現(xiàn)象準(zhǔn)確率始終卡在 0.75 上不去換模型也沒用原因類別標(biāo)簽本身有歧義或者語(yǔ)料噪聲大比如娛樂新聞里混著大量明星緋聞和影視資訊邊界本身模糊。這時(shí)候不是模型的鍋是任務(wù)定義的問題。樸素貝葉斯對(duì)標(biāo)注噪聲比深度學(xué)習(xí)更敏感因?yàn)樗强吭~頻累計(jì)的少數(shù)錯(cuò)標(biāo)樣本能直接把某個(gè)詞的類別傾向帶偏。解決先人工抽 100 條錯(cuò)分樣本按“標(biāo)注錯(cuò)誤”“邊界模糊”“模型真正分錯(cuò)”三類統(tǒng)計(jì)再看要不要清洗數(shù)據(jù)。錯(cuò)分樣本分析是這類項(xiàng)目里最值得做卻最常被跳過的環(huán)節(jié)。如果邊界模糊類占了大頭合并類別或重新定義標(biāo)簽比換任何模型都有效。6. 進(jìn)階用增量訓(xùn)練和置信度閾值把樸素貝葉斯用進(jìn)生產(chǎn)6.1 增量更新用 partial_fit 跟隨新聞熱點(diǎn)課程項(xiàng)目里的樸素貝葉斯是一次性訓(xùn)練、一次性評(píng)估。真實(shí)新聞是持續(xù)產(chǎn)生的關(guān)鍵詞和熱點(diǎn)一直在變。每周全量重訓(xùn)倒不貴麻煩的是向量化器每次重新擬合詞表抖動(dòng)導(dǎo)致新舊預(yù)測(cè)結(jié)果對(duì)不齊。更省力的做法是增量更新MultinomialNB 支持 partial_fit可以直接拿新標(biāo)注數(shù)據(jù)繼續(xù)更新模型但向量化器必須固定住不重訓(xùn)。# 新到一批已標(biāo)注新聞按同樣流程分詞后 X_new vectorizer.transform(new_cut_texts) model.partial_fit(X_new, new_labels, classesmodel.classes_)注意第一次調(diào)用 partial_fit 必須傳 classes 參數(shù)模型才能確定類別順序。這個(gè)做法很適合新聞場(chǎng)景每天用小批量人工修正過的標(biāo)注樣本喂給模型讓它跟著新詞、新熱點(diǎn)緩慢演化。代價(jià)是舊知識(shí)衰減慢如果內(nèi)容風(fēng)格突變還是需要全量重訓(xùn)。6.2 置信度閾值讓模型承認(rèn)自己不確定第二個(gè)生產(chǎn)技巧是置信度閾值。新聞分類的錯(cuò)誤大多集中在“模型自己都不確定”的樣本上與其硬給一個(gè)分類不如讓模型承認(rèn)自己拿不準(zhǔn)。用 predict_proba 取出最高概率低于閾值就轉(zhuǎn)人工審核。proba model.predict_proba(single_vec)[0] if proba.max() 0.6: print(低置信度建議人工審核) else: print(預(yù)測(cè)類別:, model.classes_[proba.argmax()])閾值取多少我一般從 0.5 起步拿驗(yàn)證集畫出“閾值 vs 被攔樣本中被人工修正的比例”曲線按業(yè)務(wù)能承受的人工量來定。這個(gè)技巧不挑模型后面換深度學(xué)習(xí)模型審核鏈路可以直接復(fù)用。我自己的習(xí)慣是無(wú)論最終要上什么模型第一版一定先用樸素貝葉斯把數(shù)據(jù)流、評(píng)估口徑、人工審核鏈路打通。它簡(jiǎn)單到?jīng)]有“調(diào)不明白”的借口一旦跑出基準(zhǔn)值后面換復(fù)雜模型才有對(duì)比的錨點(diǎn)。這個(gè)習(xí)慣幫我在好幾個(gè)文本項(xiàng)目里少走了彎路希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取