??本文主要介紹了20 Newsgroups數據集及其在文本分類任務中的應用。20 Newsgroups數據集包含約20,000篇新聞組文檔,分為20個不同主題的新聞組,數據集被分為訓練集和測試集。在數據預處理階段,使用了CountVectorizer和TfidfVectorizer兩種方法將文本數據轉換為數值特征,最終選擇了TF-IDF特征用于模型訓練和評估。通過10折交叉驗證評估了多種算法的性能,包括邏輯回歸(LR)、支持向量機(SVM)、分類與回歸樹(CART)、多項式樸素貝葉斯(MNB)和K近鄰(KNN),其中SVM和LR表現較好。進一步對邏輯回歸進行了網格搜索調參準確率達到0.9214%,最終在測試集上驗證了調參后的模型準確率,并生成了分類報告。完整代碼已開源在個人GitHub:https://github.com/KLWU07/Text-classification-of-newsgroup-documents
一、數據集介紹
1.數據集來源與構成
??20 Newsgroups 數據集收集了大約 20,000 左右的新聞組文檔,均勻分為 20 個不同主題的新聞組集合。 20news - bydate - train 和 20news - bydate - test 來自 20 Newsgroups 數據集的 bydate 版本,該版本按時間順序將數據集分為訓練集(約占 60%)和測試集(約占 40%),不包含重復文檔和新聞組名,共包含 18,846 個文檔。數據集信息來自于https://archive.ics.uci.edu/dataset/113/twenty+newsgroups,可下載。
2.數據特點
- 分類多樣:涵蓋計算機技術、體育、政治、宗教等 20 個不同的主題。
- 高維度:每篇文章都是由大量的單詞組成。
3.類別信息
1.計算機技術相關
comp.graphics
主題:計算機圖形學、圖像渲染、圖形軟件等(如 OpenGL、3D 建模)。
comp.os.ms-windows.misc
主題:Windows 操作系統相關問題、軟件使用、系統故障等。
comp.sys.ibm.pc.hardware
主題:IBM PC 硬件(如主板、顯卡、硬盤等硬件故障與配置)。
comp.sys.mac.hardware
主題:蘋果 Mac 硬件(如 Mac 電腦、配件、性能優化等)。
comp.windows.x
主題:Windows 系統下的 X 窗口系統(如圖形界面開發、X11 配置等)。2.科學與學術相關
sci.astro
主題:天文學、宇宙學、天文觀測、星系研究等。
sci.crypt
主題:密碼學、加密算法、安全協議、區塊鏈技術(早期相關討論)等。
sci.electronics
主題:電子工程、電路設計、半導體技術、電子元件等。
sci.med
主題:醫學研究、疾病診斷、生物技術、醫療設備等(非臨床實踐,偏學術)。
sci.space
主題:航天工程、太空探索、衛星技術、星際旅行等。3.娛樂與社會文化
rec.autos
主題:汽車愛好、車型討論、汽車改裝、賽車運動等。
rec.motorcycles
主題:摩托車愛好、摩托車型號、騎行體驗、摩托車賽事等。
rec.sport.hockey
主題:冰球運動(如 NHL 賽事、規則討論、球員動態等)。
rec.sport.baseball
主題:棒球運動(如 MLB 賽事、戰術分析、球員數據等)。
misc.forsale
主題:二手交易、商品出售、拍賣信息、商業廣告等(綜合性分類)。4.政治與宗教
talk.politics.misc
主題:綜合政治話題(如政策辯論、選舉分析、國際關系等)。
talk.politics.guns
主題:美國槍支政策、槍械權利、控槍辯論等。
talk.politics.mideast
主題:中東政治(如地區沖突、宗教矛盾、國際關系等)。
talk.religion.misc
主題:綜合宗教討論(如信仰比較、神學爭議、宗教節日等)。
alt.atheism
主題:無神論、宗教批判、哲學討論(與宗教相關的非信仰觀點)。
4.其中一個文檔樣本展示
From: MJMUISE@1302.watstar.uwaterloo.ca (Mike Muise)
Subject: Re: Drinking and Riding %% 主題 :回復:飲酒與騎行
Lines: 19 %% 行數 :19
Organization: Waterloo Engineering %% 組織 :滑鐵盧工程學院In article <C4wKBp.B9w@eskimo.com>, maven@eskimo.com (Norman Hamer) writes:
> What is a general rule of thumb for sobriety and cycling? Couple hours
> after you "feel" sober? What? Or should I just work with "If I drink
> tonight, I don't ride until tomorrow"?
%% 醒酒和騎自行車的一般經驗法則是什么?在你“感覺”清醒后幾個小時?是什么?還是我應該堅持“如果我今晚喝酒,我就等到明天再騎車”?1 hr/drink for the first 4 drinks. %% 前4杯酒,每杯酒1小時。
1.5 hours/drink for the next 6 drinks. %% 接下來的6杯酒,每杯酒1.5小時。
2 hours/drink for the rest. %% 其余的酒,每杯酒2小時。These are fairly cautious guidelines, and will work even if you happen to
have a low tolerance or body mass.
I think the cops and "Don't You Dare Drink & Drive" (tm) commercials will
usually say 1hr/drink in general, but after about 5 drinks and 5 hrs, you
could very well be over the legal limit.
Watch yourself.
%% 這些是比較謹慎的指導方針,即使你碰巧酒量低或者體重輕,也適用。
我認為警察和“不要酒后駕車”(商標)廣告通常會說一般每杯酒1小時,但喝了大約5杯酒并且過了5小時后,你很可能仍然超過法定限度。
注意自己。-Mike________________________________________________/ Mike Muise / mjmuise@1302.watstar.uwaterloo.ca \ no quotes, no jokes,\ Electrical Engineering, University of Waterloo / no disclaimer, no fear.
二、文件特征提取(數據預處理)
??這是自然語言處理(NLP)中處理文本數據的經典流程,用于將非結構化文本轉換為機器學習模型可處理的數值特征。兩種方法都有調用,代碼中同時使用了 CountVectorizer 和 TfidfVectorizer,但最終只使用了 TF-IDF 特征(即 X_train_counts_tf)進行模型訓練和評估。
方法 | CountVectorizer | TfidfVectorizer |
---|---|---|
核心思想 | 統計詞匯在文本中出現的次數 | 評估詞匯對文本的區分能力(詞頻 × 逆文檔頻率) |
權重計算 | 每個詞的權重 = 出現次數 | 每個詞的權重 = TF × IDF |
對高頻詞的處理 | 無處理(所有詞平等對待) | 抑制通用高頻詞(如 “the”),提升稀有詞權重 |
特征矩陣特點 | 數值非負,可能存在大量重復值 | 數值范圍更廣,重要詞權重更高 |
文本分類 | 適用于短文本、詞匯分布均勻的場景 | 通常表現更好,尤其在長文本、多類別場景 |
垃圾郵件檢測 | 可能被 “buy”“free” 等高頻詞誤導 | 能有效識別低頻但關鍵的垃圾詞模式 |
主題模型(如 LDA) | 直接使用詞頻更符合概率模型假設 | 可能因權重調整導致主題偏移 |
計算效率 | 更快(僅需計數) | 稍慢(需額外計算 IDF) |
特征稀疏性 | 更高(大量零值) | 相對較低(重要詞權重更突出) |
1. 詞頻統計(CountVectorizer)
詞頻統計:使用CountVectorizer計算詞頻矩陣。
stop_words='english'
- 自動過濾英語停用詞(如 “the”, “and”, “is” 等無實際語義的高頻詞),減少噪聲特征。
decode_error='ignore'
- 忽略無法解碼的字符(如亂碼),確保文本處理的魯棒性。
fit_transform()
- 訓練(fit):分析所有文本,構建詞匯表(如{“apple”: 0, “banana”: 1, …})。
- 轉換(transform):將每篇文本轉換為向量,向量維度等于詞匯表大小,每個位置的值表示對應詞匯在文本中的出現次數。
2.TF-IDF 轉換(TfidfVectorizer)
- TF-IDF 轉換:將詞頻矩陣轉換為 TF-IDF(詞頻 - 逆文檔頻率)矩陣,突出重要詞匯的權重。
- TF-IDF(Term Frequency-Inverse Document Frequency)是一種統計方法,用于評估詞匯在文本集合中的重要性。
- 詞頻(TF):詞匯在單個文本中出現的頻率。
公式:TF = 詞匯在文本中出現次數 / 文本總詞數 - 逆文檔頻率(IDF):衡量詞匯的普遍重要性(若某詞在所有文本中頻繁出現,則重要性低)。
公式:IDF = log(總文檔數 / 包含該詞的文檔數 + 1) - TF-IDF 值:TF × IDF,值越高表示詞匯對文本的區分能力越強。
3.詞頻統計的局限性
- 維度災難:詞匯表可能包含數萬甚至數十萬個詞,導致特征矩陣維度極高。
- 噪聲問題:高頻詞(如 “the”)對分類無幫助,但會占據大量特征空間。
4.TF-IDF 的優勢
- 特征降維:通過權重調整,弱化普遍詞匯,突出有區分性的詞匯。
- 語義增強:提升與類別相關的關鍵詞(如在 “comp.graphics” 類別中,“pixel” 的 TF-IDF 值會更高)的權重。
5.特征擴展
- 詞嵌入(Word Embedding):使用 Word2Vec、GloVe 等方法捕捉詞匯語義關系(優于 TF-IDF 的詞袋模型)。
- 文本預處理:結合詞干提取(Stemming)或詞形還原(Lemmatization)進一步減少特征維度。
6.CountVectorizer和TF-IDF()舉例比較
對比還是不一樣的。
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfVectorizershili = ['What is a general rule of thumb for sobriety and cycling? Couple hours after you "feel" sober? What? Or should I just work with "If I drink tonight, I do not ride until tomorrow"? ','1 hour drink for the first 4 drinks.','1.5 hours drink for the next 6 drinks.','2 hours drink for the rest.'
]tz = CountVectorizer()
tz1 = CountVectorizer(stop_words='english', decode_error='ignore')
X = tz.fit_transform(shili)
X1 = tz.fit_transform(shili)print(X.shape,X1.shape)
print(X.toarray(),'\n\n',X1.toarray())
print('-'*100)
tz3 = TfidfVectorizer()
Y1 = tz3.fit_transform(shili)tz4 = TfidfVectorizer(stop_words='english', decode_error='ignore')
Y2 = tz4.fit_transform(shili)
print(Y1.shape,Y2.shape)
print(Y1.toarray(),'\n\n',Y2.toarray())
(4, 35) (4, 35)
[[1 1 1 1 1 1 0 1 0 1 1 0 1 1 1 1 0 1 1 1 0 1 1 1 1 1 0 1 1 1 1 2 1 1 1][0 0 0 0 0 1 1 0 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0][0 0 0 0 0 1 1 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0][0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0]] [[1 1 1 1 1 1 0 1 0 1 1 0 1 1 1 1 0 1 1 1 0 1 1 1 1 1 0 1 1 1 1 2 1 1 1][0 0 0 0 0 1 1 0 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0][0 0 0 0 0 1 1 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0][0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0]]
----------------------------------------------------------------------------------------------------
(4, 35) (4, 18)
[[0.18272028 0.18272028 0.18272028 0.18272028 0.18272028 0.095351020. 0.18272028 0. 0.09535102 0.18272028 0.0.11662799 0.18272028 0.18272028 0.18272028 0. 0.182720280.18272028 0.18272028 0. 0.18272028 0.18272028 0.182720280.18272028 0.18272028 0. 0.18272028 0.18272028 0.182720280.18272028 0.36544055 0.18272028 0.18272028 0.18272028][0. 0. 0. 0. 0. 0.27604710.41705904 0. 0.52898651 0.2760471 0. 0.528986510. 0. 0. 0. 0. 0.0. 0. 0. 0. 0. 0.0. 0. 0.33764523 0. 0. 0.0. 0. 0. 0. 0. ][0. 0. 0. 0. 0. 0.302241390.45663404 0. 0. 0.30224139 0. 0.0.36968461 0. 0. 0. 0.57918237 0.0. 0. 0. 0. 0. 0.0. 0. 0.36968461 0. 0. 0.0. 0. 0. 0. 0. ][0. 0. 0. 0. 0. 0.33972890. 0. 0. 0.3397289 0. 0.0.41553722 0. 0. 0. 0. 0.0. 0. 0.65101935 0. 0. 0.0. 0. 0.41553722 0. 0. 0.0. 0. 0. 0. 0. ]] [[0.27037171 0.27037171 0.14109118 0. 0.27037171 0.270371710. 0.17257476 0.27037171 0. 0.27037171 0.270371710.27037171 0.27037171 0.27037171 0.27037171 0.27037171 0.27037171][0. 0. 0.37919167 0.5728925 0. 0.0.72664149 0. 0. 0. 0. 0.0. 0. 0. 0. 0. 0. ][0. 0. 0.4574528 0.69113141 0. 0.0. 0.55953044 0. 0. 0. 0.0. 0. 0. 0. 0. 0. ][0. 0. 0.40264194 0. 0. 0.0. 0.49248889 0. 0.77157901 0. 0.0. 0. 0. 0. 0. 0. ]]
三、算法評估
1.10折交叉驗證和準確率(箱線圖)
實際加載的類別數: 20
類別名稱: ['alt.atheism', 'comp.graphics', 'comp.os.ms-windows.misc', 'comp.sys.ibm.pc.hardware', 'comp.sys.mac.hardware', 'comp.windows.x', 'misc.forsale', 'rec.autos', 'rec.motorcycles', 'rec.sport.baseball', 'rec.sport.hockey', 'sci.crypt', 'sci.electronics', 'sci.med', 'sci.space', 'soc.religion.christian', 'talk.politics.guns', 'talk.politics.mideast', 'talk.politics.misc', 'talk.religion.misc']
(11314, 129782)
(11314, 129782)
LR : 0.902158 (0.008985)
SVM : 0.905958 (0.006622)
CART : 0.660421 (0.010269)
MNB : 0.883951 (0.012686)
KNN : 0.799187 (0.006339)
2.邏輯回歸LR網格搜索GridSearchCV
# 4)算法調參
# 調參LR
param_grid = {}
param_grid['C'] = [0.1, 5, 13, 15]
model = LogisticRegression(C=13, max_iter=1000)
kfold = KFold(n_splits=num_folds,shuffle=True, random_state=seed)
grid = GridSearchCV(estimator=model, param_grid=param_grid, scoring=scoring, cv=kfold)
grid_result = grid.fit(X=X_train_counts_tf, y=dataset_train.target)
print('最優 : %s 使用 %s' % (grid_result.best_score_, grid_result.best_params_))
最優 : 0.9214261277895981 使用 {'C': 15}
3.驗證集驗證準確率
# 6)生成模型
model = LogisticRegression(C=15)
model.fit(X_train_counts_tf, dataset_train.target)
X_test_counts = tf_transformer.transform(dataset_test.data)
predictions = model.predict(X_test_counts)
print(accuracy_score(dataset_test.target, predictions))
print(classification_report(dataset_test.target, predictions))
0.846388741370154precision recall f1-score support0 0.82 0.77 0.79 3191 0.73 0.81 0.77 3892 0.77 0.75 0.76 3943 0.71 0.75 0.73 3924 0.82 0.85 0.84 3855 0.84 0.76 0.80 3956 0.80 0.89 0.84 3907 0.92 0.90 0.91 3968 0.96 0.95 0.96 3989 0.91 0.94 0.92 39710 0.96 0.97 0.96 39911 0.96 0.92 0.94 39612 0.78 0.79 0.78 39313 0.90 0.87 0.88 39614 0.91 0.92 0.91 39415 0.86 0.93 0.89 39816 0.75 0.90 0.82 36417 0.98 0.89 0.93 37618 0.82 0.61 0.70 31019 0.73 0.61 0.66 251accuracy 0.85 7532macro avg 0.85 0.84 0.84 7532
weighted avg 0.85 0.85 0.85 7532
四、完整代碼
from sklearn.datasets import load_files
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import KFold
from joblib import dump, load # 導入模型保存和加載的函數# 1) 導入數據
categories = ['alt.atheism','rec.sport.hockey','comp.graphics','sci.crypt','comp.os.ms-windows.misc','sci.electronics','comp.sys.ibm.pc.hardware','sci.med','comp.sys.mac.hardware','sci.space','comp.windows.x','soc.religion.christian','misc.forsale','talk.politics.guns','rec.autos','talk.politics.mideast','rec.motorcycles','talk.politics.misc','rec.sport.baseball','talk.religion.misc']
# 導入訓練數據
train_path = '20news-bydate-train'
dataset_train = load_files(container_path=train_path, categories=categories)
# 導入評估數據
test_path = '20news-bydate-test'
dataset_test = load_files(container_path=test_path, categories=categories)print("實際加載的類別數:", len(dataset_train.target_names))
print("類別名稱:", dataset_train.target_names)# 計算TF-IDF
tf_transformer = TfidfVectorizer(stop_words='english', decode_error='ignore')
X_train_counts_tf = tf_transformer.fit_transform(dataset_train.data)
# 查看數據維度
print(X_train_counts_tf.shape)# 保存TF-IDF向量器
dump(tf_transformer, 'tfidf_vectorizer.joblib')# 設置評估算法的基準
num_folds = 10
seed = 7
scoring = 'accuracy'# 生成算法模型
models = {}
models['LR'] = LogisticRegression(C=15,n_jobs=-1, max_iter=1000)# 比較算法
results = []
for key in models:kfold = KFold(n_splits=num_folds, shuffle=True, random_state=seed)cv_results = cross_val_score(models[key], X_train_counts_tf, dataset_train.target, cv=kfold, scoring=scoring)results.append(cv_results)print('%s : %f (%f)' % (key, cv_results.mean(), cv_results.std()))# 訓練完整模型(不使用交叉驗證)model = models[key]model.fit(X_train_counts_tf, dataset_train.target)# 保存模型model_filename = f'{key}_model.joblib'dump(model, model_filename)print(f'模型已保存為 {model_filename}')# 保存類別名稱
dump(dataset_train.target_names, 'target_names.joblib')
五.預測
GPT隨機汽車發布會的郵件——car _txt文本格式。
from joblib import load# 加載組件
tf_transformer = load('tfidf_vectorizer.joblib') # 加載TF-IDF向量化器
model = load('LR_model.joblib') # 加載訓練好的模型
target_names = load('target_names.joblib') # 加載類別名稱# 1. 讀取 `txt` 文件內容
file_path = 'car_1.txt' # 確保文件路徑正確
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:text = f.read()# 2. 向量化文本
X_new = tf_transformer.transform([text]) # 注意:輸入必須是列表形式(即使單樣本)print(X_new.shape)
print(X_new.toarray())
# 3. 預測類別
predicted = model.predict(X_new)
predicted_class = target_names[predicted[0]] # 獲取類別名稱# 4. 輸出結果
print(f"文件 '{file_path}' 的預測類別是: {predicted_class}")
(1, 129782)
[[0. 0. 0. ... 0. 0. 0.]]
文件 'car_1.txt' 的預測類別是: rec.autos