训练测试划分与朴素贝叶斯分类器报错问题求助
问题解析与修正方案
错误原因
这两个警告的核心是你的分类器训练、测试数据完全是单一类别:
- 用
X_train1和y_train1训练伯努利贝叶斯时,y_train1全是easy_ham标签,没有任何spam样本。训练后的模型只会预测easy_ham,根本不会输出spam类别。 - 计算针对
spam的精确率时,因为没有任何预测结果是spam,导致精确率无法计算,被强制设为0.0。 - 同时
y_test1里也全是easy_ham,没有真实的spam样本,召回率同样无法计算,也被设为0.0。
代码核心问题
- 数据集拆分逻辑错误:你把三类数据单独拆分训练测试集,而不是合并成完整的二分类数据集(将easy_ham、hard_ham归为非垃圾邮件,spam归为垃圾邮件)。单独用单一类别数据训练,模型学不到区分垃圾邮件的特征。
- CountVectorizer使用错误:
preprocessing函数里重复创建CountVectorizer()却未使用,且全局vectorizer被多次调用fit_transform,会导致词汇表被覆盖,训练/测试集特征不一致。 - 评估逻辑错误:用单一类别数据集计算针对另一类的二分类指标(精确率、召回率)完全没有意义,因为数据里根本没有对应类别样本。
修正后的代码
import tarfile from sklearn.model_selection import train_test_split from sklearn.feature_extraction.text import CountVectorizer from sklearn.naive_bayes import MultinomialNB, BernoulliNB from sklearn.metrics import accuracy_score, precision_score, recall_score, confusion_matrix def read_data(file): dataset = list() with tarfile.open(file) as tar: for item in tar: if item.isfile(): with tar.extractfile(item) as f: content = f.read() decoded_content = None for charset in ['utf-8', 'iso-8859-1', 'ascii']: try: decoded_content = content.decode(charset) break except: continue if decoded_content is not None: dataset.append(decoded_content) return dataset # 读取所有数据,统一标签:非垃圾邮件(easy_ham/hard_ham)为0,垃圾邮件(spam)为1 easy_ham = read_data('20021010_easy_ham.tar.bz2') hard_ham = read_data('20021010_hard_ham.tar.bz2') spam = read_data('20021010_spam.tar.bz2') # 合并成完整二分类数据集 X = easy_ham + hard_ham + spam y = [0]*(len(easy_ham)+len(hard_ham)) + [1]*len(spam) # 先拆分训练测试集,再做向量化(避免数据泄露) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y) # 向量化:仅在训练集fit,测试集用相同词汇表transform vectorizer = CountVectorizer() X_train_vec = vectorizer.fit_transform(X_train) X_test_vec = vectorizer.transform(X_test) # 打印数据集分布 print(f"总样本数: {len(X)},训练集: {len(X_train)},测试集: {len(X_test)}") print(f"训练集非垃圾邮件数: {y_train.count(0)},垃圾邮件数: {y_train.count(1)}") print(f"测试集非垃圾邮件数: {y_test.count(0)},垃圾邮件数: {y_test.count(1)}") def multinomial_classifier(x_train, y_train, x_test, y_test): clf = MultinomialNB() clf.fit(x_train, y_train) predictions = clf.predict(x_test) accuracy = accuracy_score(y_test, predictions) # 设置zero_division避免极端情况警告 precision = precision_score(y_test, predictions, pos_label=1, zero_division=1) recall = recall_score(y_test, predictions, pos_label=1, zero_division=1) confusion_mat = confusion_matrix(y_test, predictions, labels=[0, 1]) return predictions, accuracy, precision, recall, confusion_mat def bernoulli_classifier(x_train, y_train, x_test, y_test): clf = BernoulliNB() clf.fit(x_train, y_train) predictions = clf.predict(x_test) accuracy = accuracy_score(y_test, predictions) precision = precision_score(y_test, predictions, pos_label=1, zero_division=1) recall = recall_score(y_test, predictions, pos_label=1, zero_division=1) confusion_mat = confusion_matrix(y_test, predictions, labels=[0, 1]) return predictions, accuracy, precision, recall, confusion_mat # 评估伯努利贝叶斯 predictions_b, accuracy_b, precision_b, recall_b, confusion_mat_b = bernoulli_classifier(X_train_vec, y_train, X_test_vec, y_test) print("\n伯努利贝叶斯分类器结果:") print(f"准确率: {accuracy_b:.4f}") print(f"垃圾邮件精确率: {precision_b:.4f}") print(f"垃圾邮件召回率: {recall_b:.4f}") print("混淆矩阵:") print(confusion_mat_b) # 评估多项式贝叶斯 predictions_m, accuracy_m, precision_m, recall_m, confusion_mat_m = multinomial_classifier(X_train_vec, y_train, X_test_vec, y_test) print("\n多项式贝叶斯分类器结果:") print(f"准确率: {accuracy_m:.4f}") print(f"垃圾邮件精确率: {precision_m:.4f}") print(f"垃圾邮件召回率: {recall_m:.4f}") print("混淆矩阵:") print(confusion_mat_m)
关键修正说明
- 数据集合并:将easy_ham、hard_ham合并为非垃圾邮件,spam作为垃圾邮件,构建标准二分类数据集。
- 正确数据拆分与向量化:先拆分原始文本,再用训练集构建词汇表,测试集复用该词汇表,避免数据泄露。
- 处理极端情况:添加
zero_division=1参数,解决无预测/无真实样本时的指标计算警告。 - 统一特征空间:全局仅初始化一次CountVectorizer,保证训练、测试集特征一致。
内容的提问来源于stack exchange,提问作者yehor
相关产品推荐
相关产品推荐

