【机器学习】实验3,贝叶斯垃圾邮件识别(完整代码实现+报告)

清华大学驭风计划课程链接

学堂在线 - 精品在线课程学习平台 (xuetangx.com)

代码和报告均为本人自己实现(实验满分),此次实验开源代码,如果需要数据集可以私聊博主

有任何疑问或者问题,也欢迎私信博主,大家可以相互讨论交流哟~~求点赞+关注

后续持续更新机器学习专栏

实验说明

电子邮件是互联网的一项重要服务,在大家的学习、工作和生活中会广泛使用。但是大家的邮箱常常被各种各样的垃圾邮件填充了。有统计显示,每天互联网上产生的垃圾邮件有几百亿近千亿的量级。因此,对电子邮件服务提供商来说,垃圾邮件过滤是一项重要功能。而朴素贝叶斯算法在垃圾邮件识别任务上一直表现非常好,至今仍然有很多系统在使用朴素贝叶斯算法作为基本的垃圾邮件识别算法。

本次实验数据集来自Trec06的中文垃圾邮件数据集,目录解压后包含三个文件夹,其中data目录下是所有的邮件(未分词),已分词好的邮件在data_cut目录下。邮件分为邮件头部分和正文部分,两部分之间一般有空行隔开。标签数据在label文件夹下,文件中每行是标签和对应的邮件路径。‘spam’表示垃圾邮件,‘ham’表示正常邮件。

本次实验

基本要求:

  1. 提取正文部分的文本特征;

  2. 划分训练集和测试集(可以借助工具包。一般笔记本就足够运行所有数据,认为实现困难或算力不够的同学可以采样一部分数据进行实验。);

  3. 使用朴素贝叶斯算法完成垃圾邮件的分类与预测,要求测试集准确率Accuracy、精准率Precision、召回率Recall均高于0.9(本次实验可以使用已有的一些工具包完成如sklearn);

  4. 对比特征数目(词表大小)对模型效果的影响;

  5. 提交代码和实验报告。

扩展要求:

  1. 邮件头信息有时也可以协助判断垃圾邮件,欢迎学有余力的同学们尝试;

  2. 尝试自行实现朴素贝叶斯算法细节;

  3. 尝试对比不同的概率计算方法。

实验结果

导入工具包

'''
提示:
若调用已有工具包,sklearn中提供了一些可能会用到的类。
'''
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer # 提取文本特征向量的类
from sklearn.naive_bayes import MultinomialNB, BernoulliNB, ComplementNB # 三种朴素贝叶斯算法,差别在于估计p(x|y)的方式
from sklearn.metrics import recall_score, precision_score, accuracy_score
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import os

idx_path=r'./trec06c-utf8/trec06c-utf8/label/index'
"""
读取数据集,并按照标签,邮件头,正文划分成三个列表
"""
labels = []
headers = []
datas = []
    
with open(idx_path, 'r', encoding='utf-8') as f:
    for line in f:
        label, path = line.strip().split(' ')
        labels.append(1 if label == 'spam' else 0)
            
        email_path = os.path.join(os.path.dirname(idx_path), path.replace('data', 'data_cut'))
            
        with open(email_path, 'r', encoding='utf-8') as email:
            head, data = email.read().split('\n\n', maxsplit=1)
            headers.append(head)
            datas.append(data)
# 划分数据集
y_train, y_test, X0_train, X0_test, X_train, X_test = train_test_split(
    labels, headers, datas, test_size=0.3, random_state=42)

# 输出划分后的数据集大小
print("训练集大小:", len(X_train))
print("测试集大小:", len(X_test))

训练集大小: 45234

测试集大小: 19386 


def vectorizer(X_train, X_test, min_df=0.1, max_df=0.95, max_features=40, method='count'):
    '''
    对正文部分进行特征提取,两种方法:count不考虑词频,term_frequency考虑词频
    '''
    if method == 'count':
        vectorizer = CountVectorizer(
            min_df=min_df, max_df=max_df, max_features=max_features)
    elif method == 'term_frequency':
        vectorizer = TfidfVectorizer(
            min_df=min_df, max_df=max_df, max_features=max_features, ngram_range=(1, 1))
    
    features_train = vectorizer.fit_transform(X_train).toarray()
    features_test = vectorizer.transform(X_test).toarray()
    features_name = vectorizer.get_feature_names_out()

    return features_name, features_train, features_test
for method in ['count', 'term_frequency']:
    features_name, features_train, features_test = vectorizer(X_train, X_test, method=method)
    classifiers = [MultinomialNB(), BernoulliNB(), ComplementNB()]
    classifier_names = ['MultinomialNB', 'BernoulliNB', 'ComplementNB']
    if method == 'count':
        print('count方法')
    else:
        print('term_frequency方法')    
    for classifier, classifier_name in zip(classifiers, classifier_names):
        classifier.fit(features_train, y_train)
        y_pred = classifier.predict(features_test)
        accuracy = accuracy_score(y_true=y_test, y_pred=y_pred)
        precision = precision_score(y_true=y_test, y_pred=y_pred)
        recall = recall_score(y_true=y_test, y_pred=y_pred) 
        print(classifier_name + ':  ' + 'accuracy ' + str(accuracy) +'  precision ' + str(precision) + '  recall ' + str(recall))

使用对三种朴素贝叶斯算法都实现,发现BnoullinB不受到词频的影响,所以在探索特征数目对模型的影响时我决定使用BnoullinB+考虑词频进行。 

max_features = [10,30,50,70,100,300,500,1000,2000]#特征数目

accuracys, precisions, recalls = [], [], []
model2 = BernoulliNB()
for _max_feature in max_features:
    print('特征数目:' + str(_max_feature))
    features_name, features_train, features_test = vectorizer(
        X_train, X_test, max_features=_max_feature, method='term_frequency')
    
    model2.fit(features_train, y_train)
    y_pred = model2.predict(features_test)
    
    accuracy = accuracy_score(y_true=y_test, y_pred=y_pred)
    precision = precision_score(y_true=y_test, y_pred=y_pred)
    recall = recall_score(y_true=y_test, y_pred=y_pred)
    
    accuracys.append(accuracy)
    precisions.append(precision)
    recalls.append(recall)
    print('accuracy '+str(accuracy) +'  precision '+str(precision)+'  recall '+str(recall))

通过画图可以看出来,随着特征数目增加,正确率和召回率都是先上升然后下降

但是精准率几乎一直上升,特征数目大概在50左右三种分数最优。

plt.plot(max_features, accuracys, marker='o', label='Accuracy')
plt.plot(max_features, precisions, marker='o', label='Precision')
plt.plot(max_features, recalls, marker='o', label='Recall')
plt.xlabel('Max Features')
plt.ylabel('Score')
plt.title('Max Features vs. Model Evaluation Scores')
plt.legend()
plt.show()

  • 52
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
以下是一个简单的朴素贝叶斯垃圾邮件文档识别代码的示例: ```python import os import string # 定义垃圾邮件和非垃圾邮件的路径 spam_path = 'spam/' ham_path = 'ham/' # 定义一个函数,用于读取文件内容并进行处理 def read_file(path): with open(path, 'r', encoding='utf-8', errors='ignore') as f: content = f.read() # 去掉标点符号和空格,并将所有单词转换为小写 content = content.translate(str.maketrans('', '', string.punctuation)).lower() content = content.replace('\n', ' ').replace('\r', ' ') words = content.split() return words # 定义一个函数,用于计算单词在所有文件中出现的次数 def count_words(path): words_count = {} for filename in os.listdir(path): words = read_file(os.path.join(path, filename)) for word in words: if word in words_count: words_count[word] += 1 else: words_count[word] = 1 return words_count # 计算垃圾邮件和非垃圾邮件中单词出现的次数 spam_words_count = count_words(spam_path) ham_words_count = count_words(ham_path) # 计算所有文件中单词出现的总次数 total_words_count = {} for word in spam_words_count: if word in total_words_count: total_words_count[word] += spam_words_count[word] else: total_words_count[word] = spam_words_count[word] for word in ham_words_count: if word in total_words_count: total_words_count[word] += ham_words_count[word] else: total_words_count[word] = ham_words_count[word] # 定义一个函数,用于计算单词在垃圾邮件中出现的概率 def spam_prob(word): return (spam_words_count[word] + 1) / (sum(spam_words_count.values()) + len(total_words_count)) # 定义一个函数,用于计算单词在非垃圾邮件中出现的概率 def ham_prob(word): return (ham_words_count[word] + 1) / (sum(ham_words_count.values()) + len(total_words_count)) # 定义一个函数,用于判断一封邮件是否是垃圾邮件 def is_spam(path): words = read_file(path) spam_score = 0 ham_score = 0 for word in words: if word in total_words_count: spam_score += spam_prob(word) ham_score += ham_prob(word) if spam_score > ham_score: return True else: return False # 测试代码 spam_count = 0 ham_count = 0 for filename in os.listdir(spam_path): if is_spam(os.path.join(spam_path, filename)): spam_count += 1 for filename in os.listdir(ham_path): if not is_spam(os.path.join(ham_path, filename)): ham_count += 1 print('垃圾邮件识别准确率:{:.2f}%'.format(spam_count / len(os.listdir(spam_path)) * 100)) print('非垃圾邮件识别准确率:{:.2f}%'.format(ham_count / len(os.listdir(ham_path)) * 100)) ``` 这个代码使用朴素贝叶斯算法来对垃圾邮件和非垃圾邮件进行分类。首先,它会读取所有的垃圾邮件和非垃圾邮件文档,并计算每个单词在所有文档中出现的次数。然后,它会使用这些单词计算出在垃圾邮件和非垃圾邮件中每个单词出现的概率。最后,它会读取一封邮件,并根据这些概率计算这封邮件是垃圾邮件的概率和非垃圾邮件的概率,然后将这两个概率进行比较,以确定这封邮件是垃圾邮件还是非垃圾邮件

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

X.AI666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值