本章我们来学习朴素贝叶斯算法,贝叶斯分类是一类分类算法的总称,这类算法均以贝叶斯定理为基础,故统称为贝叶斯分类。而朴素贝叶斯分类是贝叶斯分类中最简单,也是常见的一种分类方法。
我们常常遇到这样的场景。与友人聊天时,一开始可能不知道他要说什么,但是他说了一句话之后,你就能猜到接下来他要讲什么内容。友人给的信息越多,我们越能够推断出他想表达的含义,这也是贝叶斯定理所阐述的思考方式。
在学习朴素贝叶斯之前,我们需要先了解一些概率基础。
目录
1.概率基础
2.算法原理
3. API 介绍
4.代码示例
4.1垃圾邮件过滤
1.概率基础
概率学是研究随机事件的一门学科,在概率学中0代表不可能事件,1代表必然事件,介于0和1之间的是概率事件。
在概率学中,事件 A 发生的概率的表现形式如下:
事件 A 不发生的概率表现如下:
假设有一个事件 B,事件 B 和事件 A 之间相互独立(也就是两者的结果相互之间是没有影响的),
即如果 A 和 B 相互独立,则有:
则事件 A 和 B 同时发生的概率表示如下:
对任意两个事件 A 和 B,计算事件 A 发生或事件 B 发生的概率如下(广义加法公式):
在已知 B 事件发生的条件下事件 A 发生的概率被称为条件概率,条件概率的计算公式如下:
将其变形,我们就得到了贝叶斯公式,如下:
这时候我们再来看贝叶斯定理,这个公式说明了两个互换的条件概率之间的关系,它们通过联合概率关联起来。在这种情况下,若知道P(A|B) 的值,就能够计算P(B|A)的值。
当有多个特征时,如果我们假设所有的特征之间相互独立,公式可以理解为:
换个表达形式就会明朗很多,如下:
我们举个例子来感受一下,现有两个类别的文章,分为科技类和娱乐类,统计了一下文章中的高频词汇,如下表格:
特征\统计 | 科技(30篇) | 娱乐(60篇) | 汇总(90篇) |
---|---|---|---|
商场 | 9 | 51 | 60 |
影院 | 8 | 56 | 64 |
支付宝 | 20 | 15 | 35 |
云计算 | 63 | 0 | 63 |
汇总(求和) | 100 | 121 | 221 |
现有一篇被预测文档,出现了影院、支付宝、云计算词汇,计算属于科技、娱乐类别的概率:
所以这篇文章有 34% 的可能是科技类别,66% 的可能是娱乐类别,我们预测这篇文章属于娱乐类别。
2.算法原理
贝叶斯定理是英国学者贝叶斯在18世纪时提出的关于随机事件 A 和 B 的条件概率(或边缘概率)的一则定理。贝叶斯定理的表现形式如公式(4)中所示
现在我们来定义一个包含 n 个样本的数据集,这个数据集中的样本均可以被分到 k 种类别中:
则公式(2)可以被改写为如下形式:
在实际的计算过程中,P(x) 对于P(yk∣x) 来说可以认为是一个常数(特征在数据集不变的情况下出现的概率是恒定的)。因此我们只需要令 P(yk)P(x∣yk) 取最大值就可以令 P(yk∣x) 最大。
由于类别的先验概率 P(yk) 通常是已知的,因此我们需要计算的就是似然P(x∣yk)。在朴素贝叶斯中我们假定样本x 的各个属性相互独立,则可以用如下方式计算P(x∣yk):
接下来的问题就是如何计算每个特征的条件概率P(xn∣yk)。首先假设属性Cn 是离散属性。训练集中属于类别 yk 的样本,在属性 Cn 下的相异属性共有n个;训练集中属于类别 yk,且在属性 Cn 下的属性值为 xn 的样本共有m个,则 P(xn∣yk) 的计算方式如下:
接下来我们假设属性 Cn 是连续属性。这里我们认为这个连续属性服从均值为 μ 、标准差为 σ 的高斯分步(正态分布):
在高斯分布下,条件概率 P(xn∣yk) 的计算方式如下:
其中,μyk,σyk 表示训练集中属于类别yk 的样本在属性 Cn 下的均值和标准差
从上面的计算可以看出,没有复杂的求导和矩阵运算,因此效率很高。
3. API 介绍
sklearn
朴素贝叶斯分类算法 API:
1sklearn.naive_bayes.MultinomialNB(alpha=1.0)
- alpha: 拉普拉斯平滑系数
为了解决零概率的问题,法国数学家拉普拉斯最早提出用加1的方法估计没有出现过的现象的概率,所以加法平滑也叫做拉普拉斯平滑。假定训练样本很大时,每个分量x的计数加1造成的估计概率变化可以忽略不计,但可以方便有效的避免零概率问题。
避免每一项为零的做法就是, 在分子、 分母上各加一个数值。
算法优点:
(1)朴素贝叶斯模型发源于古典数学理论,有稳定的分类效率。
(2)对小规模的数据表现很好,能个处理多分类任务,适合增量式训练,尤其是数据量超出内存时,我们可以一批批的去增量训练。
(3)对缺失数据不太敏感,算法也比较简单,常用于文本分类。
算法缺点:
(1) 理论上,朴素贝叶斯模型与其他分类方法相比具有最小的误差率。但是实际上并非总是如此,这是因为朴素贝叶斯模型给定输出类别的情况下,假设属性之间相互独立,这个假设在实际应用中往往是不成立的,在属性个数比较多或者属性之间相关性较大时,分类效果不好。而在属性相关性较小时,朴素贝叶斯性能最为良好。对于这一点,有半朴素贝叶斯之类的算法通过考虑部分关联性适度改进。
(2)需要知道先验概率,且先验概率很多时候取决于假设,假设的模型可以有很多种,因此在某些时候会由于假设的先验模型的原因导致预测效果不佳。
(3)由于我们是通过先验和数据来决定后验的概率从而决定分类,所以分类决策存在一定的错误率。
(4)对输入数据的表达形式很敏感。
4.代码示例
4.1垃圾邮件过滤
TF-IDF是一种用于信息检索与文本挖掘的常用加权技术。
TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。
TF-IDF的主要思想是:如果某个单词在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。
TF是词频(Term Frequency) 词频(TF)表示词条(关键字)在文本中出现的频率。 这个数字通常会被归一化(一般是词频除以文章总词数), 以防止它偏向长的文件。
数据集:SMSSpam.txt
//数据集太大了,csdn下载文件好像要vip,需要的call:13438784965
1.导入包
#1.导入包
import numpy as np
import os
import re
import sklearn
import matplotlib.pyplot as plt
import pandas as pd
import string
from sklearn.feature_extraction.text import TfidfVectorizer as TFIDF
from sklearn.calibration import CalibratedClassifierCV
from sklearn.linear_model import LogisticRegression
from sklearn.naive_bayes import MultinomialNB
import sklearn.model_selection as sk_model_selection
import nltk
from nltk.corpus import stopwords
2.切片和清理文本
#2.切分和清理文本
def Text_process(text,stem=True):
#人造特征(网址+电话)
man_made_features = [0,0]
# 初始化一个名为man_made_features的列表,包含两个元素,分别用于表示文本中是否包含网址和电话号码。初始值都设为0(表示不存在)。
#是否有网址
pattern = re.compile(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+')
# 使用re模块的compile函数编译一个正则表达式,用于匹配网址。
# 正则表达式解释:
# r'http[s]?:// 匹配字符串"http://"或"https://"
# (?: ...) 是一个非捕获组,用于分组但不保存匹配结果
# [a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),] 匹配字母、数字、特定符号
# (?:%[0-9a-fA-F][0-9a-fA-F]) 匹配URL中的百分比编码(例如%20代表空格)
# + 表示前面的字符集可以出现一次或多次
if re.findall(pattern,text):
man_made_features[0]=1
# 使用re.findall函数查找所有匹配正则表达式的子串,如果找到匹配项,将man_made_features列表的第一个元素设置为1,表示文本包含网址。
#是否有电话
if re.findall(r'[0-9]{4,}',text):
man_made_features[1]=1
# 使用re.findall函数查找所有至少包含4个数字的子串,如果找到匹配项,将man_made_features列表的第二个元素设置为1,表示文本包含电话号码。
# 正则表达式解释:
# [0-9]{4,} 匹配至少连续4个数字
#去除缩写"'m","'re","'s"替换部分缩写
short = ["'m","'re","'s","'ve",' c u ',"n't",' u ']
repla = ['am', 'are', 'is', 'have', ' see you ', ' not', ' you ']
# 定义两个列表,short包含需要替换的缩写,repla包含相应的替换文本。
for i,w in enumerate(short):
text=text.replace(w,repla[i])
# 遍历short列表,使用字符串的replace方法将每个缩写替换为对应的完整形式。
#去除标点
remove = str.maketrans('','',string.punctuation)
text = text.translate(remove)
# 使用str.maketrans创建一个转换表,用于删除所有标点符号,然后使用translate方法去除文本中的所有标点符号。
#分词
tokens = text.split()
# 使用split方法将文本分割成单词列表(分词)。
#去停用词(代码被注释掉了)
# doc = [w for w in tokens if not w in stopwords.words('english')]
doc = tokens
# 将分词后的结果赋值给变量doc,注意:这里注释掉了去停用词的代码。
#词干提取(默认开启)
if stem:
s = nltk.stem.SnowballStemmer('english')
doc = [s.stem(ws) for ws in doc]
# 如果stem参数为True,则创建一个SnowballStemmer对象用于英语词干提取,并使用列表推导式对doc中的每个单词进行词干提取。
result = ' '.join(doc)
# 使用join方法将处理后的单词列表doc连接成一个字符串,存储在result变量中。
return man_made_features,result
# 返回一个元组,包含man_made_features列表和result字符串,表示人造特征和处理后的文本。
def File_process(filename, stem=True):
# 初始化三个列表,分别用于存储邮件的标签、特征和内容
labels = [] # 存储邮件标签(通常是垃圾邮件或非垃圾邮件的标识)
features = [] # 存储邮件的特征,这些特征可能是从文本中提取的
contents = [] # 存储邮件的原始内容
# 使用with语句打开文件,确保文件在操作完成后会被正确关闭
with open(filename, 'r', encoding='utf-8') as f:
# 读取文件中的所有行,并存储在lines列表中
lines = f.readlines()
# 初始化一个计数器i,但在后续代码中并未使用,可能是用于调试或其他目的
i = 0
# 遍历文件中的每一行
for line in lines:
# 使用制表符('\t')分割每行,得到标签和数据
target = line.split('\t')
# 将分割后的第一部分(target[0])作为标签,如果标签是'ham'则设为1,否则设为0
label = 1 if target[0] == 'ham' else 0
# 调用Text_process函数处理邮件内容,可能包括文本处理和特征提取
# target[1]是邮件内容,.lower()将其转换为小写,stem参数用于控制是否进行词干提取
feature, content = Text_process(target[1].lower(), stem)
# 将处理后的标签、特征和内容分别添加到对应的列表中
labels.append(label)
features.append(feature)
contents.append(content)
# 以下代码被注释掉了,如果启用,它将检查内容是否非空,然后添加到列表中
# 但由于之前的代码已经添加了内容,这部分代码是多余的
# if len(content):
# labels.append(label)
# features.append(feature)
# contents.append(content)
# 函数返回三个列表:邮件内容、特征和标签
return contents, features, labels
3.TFIDF构建数据集
#3.利用TFIDF构造训练集
def TF_IDF(contents):
# 定义一个函数TF_IDF,它接收一个参数contents,即文本内容列表。
vec = TFIDF(
min_df=3, # 最小文档频率,忽略文档频率低于3的特征词。
max_features=None, # 最多使用的特征数量,None表示无限制。
strip_accents='unicode', # 移除字符的变音符号,'unicode'表示使用Unicode编码的规则。
analyzer='word', # 指定分词方式,'word'表示按单词进行分词。
token_pattern=r'\w{1,}', # 正则表达式模式,用于识别单词,'\w{1,}'表示匹配一个或多个字母数字字符。
ngram_range=(1, 1), # 设置n-gram的上下界,(1, 1)表示只考虑单个单词(unigrams)。
use_idf=True, # 启用逆文档频率权重。
smooth_idf=True, # 平滑IDF权重,防止除以零。
sublinear_tf=True, # 应用子线性TF缩放,即用1 + log(tf)代替tf。
stop_words='english' # 使用英语停用词列表,'english'表示使用内置的英语停用词。
)
# 创建一个TF-IDF向量化器实例vec,并设置其参数。
X = vec.fit_transform(contents)
# 使用向量化器vec拟合并转换文本内容列表contents,得到TF-IDF矩阵X。
result = pd.DataFrame(X.toarray(), columns=vec.get_feature_names_out())
# 将TF-IDF矩阵X转换为DataFrame格式,列名为特征词,并赋值给变量result。
print("shape of data:", result.shape)
# 打印出数据的形状,即矩阵的行数和列数。`
return result, vec
# 返回两个值:TF-IDF矩阵的DataFrame表示result和拟合后的向量化器实例vec。
contents,features,labels=File_process("C:\pythonProject\机器学习\分类算法\朴素贝叶斯\SMSSpam.txt",False)#不提取词干,可能要等一会
"""
os.path.join('data', 'data19334', 'SMSSpam.txt'):这个表达式使用了os.path.join函数来构造一个跨平台的文件路径。它会将字符串参数连接起来形成一个完整的路径,这里指的是data/data19334/SMSSpam.txt。
False:这是一个布尔值,作为第二个参数传递给File_process函数。根据您之前提供的Fileprocess函数(注意大小写差异),这个布尔值可能用于控制是否进行词干提取(stemming)。在这里,False表示不进行词干提取。
File_process:这是一个函数调用,它应该定义在您的代码中的某个地方,并且负责处理文件。您提供的参数是文件的路径和布尔值。函数的预期行为是读取指定的文件,并返回三个列表:contents(邮件内容)、features(特征)和labels(标签)。
contents, features, labels = ...:这是一个赋值语句,它将File_process函数返回的三个值分别赋给contents、features和labels这三个变量。
"""
print(contents[:5]) #前五条数据
print(len(contents)) #数据集长度
X_1, vec = TF_IDF(contents) # 将邮件内容转换为TF-IDF矩阵,X_1是无人造特征的训练集,vec是转换工具。
F = pd.DataFrame(features, columns=['网址', '电话']) # 将人造特征列表转换为DataFrame,列名为'网址'和'电话'。
X_2 = pd.concat([F, X_1], axis=1) # 将人造特征DataFrame与TF-IDF矩阵横向合并,形成完整的特征集X_2。
Y = pd.DataFrame(labels, columns=['labels']) # 将标签列表转换为DataFrame,列名为'labels'。
print(Y.head(5))
print("无人造特征:",X_1.shape)
print("有人造特征:",X_2.shape)
baseline = sum(labels)/len(labels) #0.866 非垃圾邮件占所有邮件的 86.6%,只有结果超过 86.6%,模型才有意义
print("非垃圾邮件占比:"+str(baseline))
4. 训练模型
#4.模型训练
#最高分:不提取词干+人造特征(网址+电话)+补集朴素贝叶斯+sigmid校准 准确率: 0.9872643820427529
#10折交叉验证
CV = 10 #交叉验证次数,嫌弃速度慢可以改小
names = ["LR"
,"LR + Isotonic"
,"LR + Sigmoid"
,"Naive Bayes"
]
# 各类模型
models = [
# LR
LogisticRegression()
# LR + Isotonic
,CalibratedClassifierCV(LogisticRegression(), cv=2, method='isotonic')
# LR + Sigmoid
,CalibratedClassifierCV(LogisticRegression(), cv=2, method='sigmoid')
# Naive Bayes
,MultinomialNB()
]
print("无人造特征")
for name,model in zip(names,models): # 遍历names和models两个列表,将每个元素配对给name和model变量。
accs = sum(sk_model_selection.cross_val_score(model, X_1, y=Y.values.ravel(), cv=10, n_jobs=-1)) / 10
"""
sk_model_selection.cross_val_score: 这是scikit-learn库中的一个函数,用于执行交叉验证并返回每次验证的得分。
model: 这是您要评估的模型对象。在交叉验证过程中,这个模型将被训练并评估多次。
X_1: 这是您的特征数据集,用于训练模型。
y=Y: 这是您的目标变量(标签),用于评估模型的性能。,y=Y.values.ravel(): 这是将标签转换为一维数组的一种方式。
cv=10: 这指定了交叉验证的折数,在这里是10折交叉验证。这意味着数据集将被分成10个不同的部分,每个部分轮流作为验证集,其余部分作为训练集。
n_jobs=-1: 这个参数告诉函数使用所有可用的CPU核心来并行计算。这可以显著加快交叉验证的过程。
sum(...): 这个函数将交叉验证的所有得分相加。
/ 10: 最后,将总得分除以折数(10),以得到平均得分。
"""
# 对每个模型执行10折交叉验证,计算平均准确率,并将结果赋值给accs。
#,scoring='roc_auc'
print(name, '交叉验证结果:', accs) # 打印每个模型的名称和其交叉验证的平均准确率结果。
print("有人造特征")
for name,model in zip(names,models):
accs=sum(sk_model_selection.cross_val_score(model, X_2, y=Y.values.ravel(),cv=CV, n_jobs=-1
#,scoring='roc_auc'
))/10
print(name,'交叉验证结果:',accs)
完整代码:
#1.导入包
import numpy as np
import os
import re
import sklearn
import matplotlib.pyplot as plt
import pandas as pd
import string
from sklearn.feature_extraction.text import TfidfVectorizer as TFIDF
from sklearn.calibration import CalibratedClassifierCV
from sklearn.linear_model import LogisticRegression
from sklearn.naive_bayes import MultinomialNB
import sklearn.model_selection as sk_model_selection
import nltk
from nltk.corpus import stopwords
#2.切分和清理文本
def Text_process(text,stem=True):
#人造特征(网址+电话)
man_made_features = [0,0]
# 初始化一个名为man_made_features的列表,包含两个元素,分别用于表示文本中是否包含网址和电话号码。初始值都设为0(表示不存在)。
#是否有网址
pattern = re.compile(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+')
# 使用re模块的compile函数编译一个正则表达式,用于匹配网址。
# 正则表达式解释:
# r'http[s]?:// 匹配字符串"http://"或"https://"
# (?: ...) 是一个非捕获组,用于分组但不保存匹配结果
# [a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),] 匹配字母、数字、特定符号
# (?:%[0-9a-fA-F][0-9a-fA-F]) 匹配URL中的百分比编码(例如%20代表空格)
# + 表示前面的字符集可以出现一次或多次
if re.findall(pattern,text):
man_made_features[0]=1
# 使用re.findall函数查找所有匹配正则表达式的子串,如果找到匹配项,将man_made_features列表的第一个元素设置为1,表示文本包含网址。
#是否有电话
if re.findall(r'[0-9]{4,}',text):
man_made_features[1]=1
# 使用re.findall函数查找所有至少包含4个数字的子串,如果找到匹配项,将man_made_features列表的第二个元素设置为1,表示文本包含电话号码。
# 正则表达式解释:
# [0-9]{4,} 匹配至少连续4个数字
#去除缩写"'m","'re","'s"替换部分缩写
short = ["'m","'re","'s","'ve",' c u ',"n't",' u ']
repla = ['am', 'are', 'is', 'have', ' see you ', ' not', ' you ']
# 定义两个列表,short包含需要替换的缩写,repla包含相应的替换文本。
for i,w in enumerate(short):
text=text.replace(w,repla[i])
# 遍历short列表,使用字符串的replace方法将每个缩写替换为对应的完整形式。
#去除标点
remove = str.maketrans('','',string.punctuation)
text = text.translate(remove)
# 使用str.maketrans创建一个转换表,用于删除所有标点符号,然后使用translate方法去除文本中的所有标点符号。
#分词
tokens = text.split()
# 使用split方法将文本分割成单词列表(分词)。
#去停用词(代码被注释掉了)
# doc = [w for w in tokens if not w in stopwords.words('english')]
doc = tokens
# 将分词后的结果赋值给变量doc,注意:这里注释掉了去停用词的代码。
#词干提取(默认开启)
if stem:
s = nltk.stem.SnowballStemmer('english')
doc = [s.stem(ws) for ws in doc]
# 如果stem参数为True,则创建一个SnowballStemmer对象用于英语词干提取,并使用列表推导式对doc中的每个单词进行词干提取。
result = ' '.join(doc)
# 使用join方法将处理后的单词列表doc连接成一个字符串,存储在result变量中。
return man_made_features,result
# 返回一个元组,包含man_made_features列表和result字符串,表示人造特征和处理后的文本。
def File_process(filename, stem=True):
# 初始化三个列表,分别用于存储邮件的标签、特征和内容
labels = [] # 存储邮件标签(通常是垃圾邮件或非垃圾邮件的标识)
features = [] # 存储邮件的特征,这些特征可能是从文本中提取的
contents = [] # 存储邮件的原始内容
# 使用with语句打开文件,确保文件在操作完成后会被正确关闭
with open(filename, 'r', encoding='utf-8') as f:
# 读取文件中的所有行,并存储在lines列表中
lines = f.readlines()
# 初始化一个计数器i,但在后续代码中并未使用,可能是用于调试或其他目的
i = 0
# 遍历文件中的每一行
for line in lines:
# 使用制表符('\t')分割每行,得到标签和数据
target = line.split('\t')
# 将分割后的第一部分(target[0])作为标签,如果标签是'ham'则设为1,否则设为0
label = 1 if target[0] == 'ham' else 0
# 调用Text_process函数处理邮件内容,可能包括文本处理和特征提取
# target[1]是邮件内容,.lower()将其转换为小写,stem参数用于控制是否进行词干提取
feature, content = Text_process(target[1].lower(), stem)
# 将处理后的标签、特征和内容分别添加到对应的列表中
labels.append(label)
features.append(feature)
contents.append(content)
# 以下代码被注释掉了,如果启用,它将检查内容是否非空,然后添加到列表中
# 但由于之前的代码已经添加了内容,这部分代码是多余的
# if len(content):
# labels.append(label)
# features.append(feature)
# contents.append(content)
# 函数返回三个列表:邮件内容、特征和标签
return contents, features, labels
#3.利用TFIDF构造训练集
def TF_IDF(contents):
# 定义一个函数TF_IDF,它接收一个参数contents,即文本内容列表。
vec = TFIDF(
min_df=3, # 最小文档频率,忽略文档频率低于3的特征词。
max_features=None, # 最多使用的特征数量,None表示无限制。
strip_accents='unicode', # 移除字符的变音符号,'unicode'表示使用Unicode编码的规则。
analyzer='word', # 指定分词方式,'word'表示按单词进行分词。
token_pattern=r'\w{1,}', # 正则表达式模式,用于识别单词,'\w{1,}'表示匹配一个或多个字母数字字符。
ngram_range=(1, 1), # 设置n-gram的上下界,(1, 1)表示只考虑单个单词(unigrams)。
use_idf=True, # 启用逆文档频率权重。
smooth_idf=True, # 平滑IDF权重,防止除以零。
sublinear_tf=True, # 应用子线性TF缩放,即用1 + log(tf)代替tf。
stop_words='english' # 使用英语停用词列表,'english'表示使用内置的英语停用词。
)
# 创建一个TF-IDF向量化器实例vec,并设置其参数。
X = vec.fit_transform(contents)
# 使用向量化器vec拟合并转换文本内容列表contents,得到TF-IDF矩阵X。
result = pd.DataFrame(X.toarray(), columns=vec.get_feature_names_out())
# 将TF-IDF矩阵X转换为DataFrame格式,列名为特征词,并赋值给变量result。
print("shape of data:", result.shape)
# 打印出数据的形状,即矩阵的行数和列数。`
return result, vec
# 返回两个值:TF-IDF矩阵的DataFrame表示result和拟合后的向量化器实例vec。
contents,features,labels=File_process("/机器学习/分类算法/朴素贝叶斯/SMSSpam-gyp.txt", False)#不提取词干,可能要等一会
"""
os.path.join('data', 'data19334', 'SMSSpam-gyp.txt'):这个表达式使用了os.path.join函数来构造一个跨平台的文件路径。它会将字符串参数连接起来形成一个完整的路径,这里指的是data/data19334/SMSSpam-gyp.txt。
False:这是一个布尔值,作为第二个参数传递给File_process函数。根据您之前提供的Fileprocess函数(注意大小写差异),这个布尔值可能用于控制是否进行词干提取(stemming)。在这里,False表示不进行词干提取。
File_process:这是一个函数调用,它应该定义在您的代码中的某个地方,并且负责处理文件。您提供的参数是文件的路径和布尔值。函数的预期行为是读取指定的文件,并返回三个列表:contents(邮件内容)、features(特征)和labels(标签)。
contents, features, labels = ...:这是一个赋值语句,它将File_process函数返回的三个值分别赋给contents、features和labels这三个变量。
"""
print(contents[:5]) #前五条数据
print(len(contents)) #数据集长度
X_1, vec = TF_IDF(contents) # 将邮件内容转换为TF-IDF矩阵,X_1是无人造特征的训练集,vec是转换工具。
F = pd.DataFrame(features, columns=['网址', '电话']) # 将人造特征列表转换为DataFrame,列名为'网址'和'电话'。
X_2 = pd.concat([F, X_1], axis=1) # 将人造特征DataFrame与TF-IDF矩阵横向合并,形成完整的特征集X_2。
Y = pd.DataFrame(labels, columns=['labels']) # 将标签列表转换为DataFrame,列名为'labels'。
print(Y.head(5))
print("无人造特征:",X_1.shape)
print("有人造特征:",X_2.shape)
baseline = sum(labels)/len(labels) #0.866 非垃圾邮件占所有邮件的 86.6%,只有结果超过 86.6%,模型才有意义
print("非垃圾邮件占比:"+str(baseline))
#4.模型训练
#最高分:不提取词干+人造特征(网址+电话)+补集朴素贝叶斯+sigmid校准 准确率: 0.9872643820427529
#10折交叉验证
CV = 10 #交叉验证次数,嫌弃速度慢可以改小
names = ["LR"
,"LR + Isotonic"
,"LR + Sigmoid"
,"Naive Bayes"
]
# 各类模型
models = [
# LR
LogisticRegression()
# LR + Isotonic
,CalibratedClassifierCV(LogisticRegression(), cv=2, method='isotonic')
# LR + Sigmoid
,CalibratedClassifierCV(LogisticRegression(), cv=2, method='sigmoid')
# Naive Bayes
,MultinomialNB()
]
print("无人造特征")
for name,model in zip(names,models): # 遍历names和models两个列表,将每个元素配对给name和model变量。
accs = sum(sk_model_selection.cross_val_score(model, X_1, y=Y.values.ravel(), cv=10, n_jobs=-1)) / 10
"""
sk_model_selection.cross_val_score: 这是scikit-learn库中的一个函数,用于执行交叉验证并返回每次验证的得分。
model: 这是您要评估的模型对象。在交叉验证过程中,这个模型将被训练并评估多次。
X_1: 这是您的特征数据集,用于训练模型。
y=Y: 这是您的目标变量(标签),用于评估模型的性能。,y=Y.values.ravel(): 这是将标签转换为一维数组的一种方式。
cv=10: 这指定了交叉验证的折数,在这里是10折交叉验证。这意味着数据集将被分成10个不同的部分,每个部分轮流作为验证集,其余部分作为训练集。
n_jobs=-1: 这个参数告诉函数使用所有可用的CPU核心来并行计算。这可以显著加快交叉验证的过程。
sum(...): 这个函数将交叉验证的所有得分相加。
/ 10: 最后,将总得分除以折数(10),以得到平均得分。
"""
# 对每个模型执行10折交叉验证,计算平均准确率,并将结果赋值给accs。
#,scoring='roc_auc'
print(name, '交叉验证结果:', accs) # 打印每个模型的名称和其交叉验证的平均准确率结果。
print("有人造特征")
for name,model in zip(names,models):
accs=sum(sk_model_selection.cross_val_score(model, X_2, y=Y.values.ravel(),cv=CV, n_jobs=-1
#,scoring='roc_auc'
))/10
print(name,'交叉验证结果:',accs)