一、贝叶斯算法概述
贝叶斯算法(Bayesian algorithm)是一种基于贝叶斯定理的机器学习方法,用于估计模型参数和进行概率推断。它是一种统计推断的框架,旨在通过更新先验知识以反映观测数据来获得后验概率分布。
以下是贝叶斯算法的核心概念和数学表达:
-
贝叶斯定理(Bayes' Theorem):
贝叶斯定理表示如何在观测到新数据时更新关于模型参数的信念。其数学表达如下:
其中,
是在给定数据
的情况下,参数
的后验概率。
是在参数
下观测到数据
的似然。
是参数
的先验概率。
是数据
的边际概率,通常作为归一化常数。
-
先验概率(Prior Probability):
先验概率是在观测到数据之前对参数的概率分布的估计。它反映了我们的先验信念或领域知识。
-
似然函数(Likelihood):
似然函数表示在给定模型参数
的情况下观测到数据的概率分布。通常用
表示。
-
后验概率(Posterior Probability):
后验概率是在考虑观测数据后,参数的概率分布。它是通过应用贝叶斯定理计算得出的,通常用
表示。
-
边际概率(Marginal Probability):
边际概率是观测数据的概率,通常用于将后验概率归一化,以获得有效的后验分布。
贝叶斯算法的工作流程如下:
-
定义模型:选择合适的概率分布来描述模型参数和观测数据之间的关系,通常使用参数化的概率模型。
-
设定先验概率:选择适当的先验分布,反映我们对模型参数的初始信念。
-
收集数据:获取观测数据集
。
-
计算后验概率:使用贝叶斯定理结合观测数据和先验概率来计算参数的后验概率分布:
-
进行推断:基于后验概率进行模型参数的估计、预测、分类等任务。
-
更新模型:随着新数据的积累,可以周期性地重复上述过程,不断更新后验概率和模型参数。
贝叶斯算法的优点包括对不确定性建模、能够自然地融入领域知识、适用于小样本数据等。但它也可能受限于计算复杂性,尤其在高维空间或大规模数据集上。因此,在实际应用中,需要谨慎选择模型和先验分布,并利用近似方法来解决计算上的挑战。
二、垃圾邮件过滤实例
1-邮件数据读取
import numpy as np # 导入NumPy库,用于数值计算
import re # 导入re库,用于正则表达式操作
import random # 导入random库,用于生成随机数
# 定义一个函数textParse,用于处理文本数据
def textParse(input_string):
listofTokens = re.split(r'\W+', input_string) # 使用正则表达式切分输入的字符串
return [tok.lower() for tok in listofTokens if len(listofTokens) > 2] # 返回切分后的单词列表,转换为小写并去除长度小于等于2的单词
# 定义一个函数spam,用于处理垃圾邮件数据
def spam():
doclist = [] # 创建一个空列表,用于存储文档数据
classlist = [] # 创建一个空列表,用于存储类别信息
for i in range(1, 26): # 循环处理1到25号文档
# 处理垃圾邮件文档
wordlist = textParse(open('email/spam/%d.txt' % i, 'r').read()) # 读取并处理垃圾邮件文档
doclist.append(wordlist) # 将处理后的单词列表添加到文档列表
classlist.append(1) # 将类别标签1(表示垃圾邮件)添加到类别列表
# 处理非垃圾邮件文档
wordlist = textParse(open('email/ham/%d.txt' % i, 'r').read()) # 读取并处理非垃圾邮件文档
doclist.append(wordlist) # 将处理后的单词列表添加到文档列表
classlist.append(0) # 将类别标签0(表示非垃圾邮件)添加到类别列表
......
以上代码演示了如何使用贝叶斯算法来读取和处理垃圾邮件分类任务的数据。以下是对贝叶斯算法的数据读取过程的简要概括:
-
导入必要的库:代码导入了NumPy、正则表达式(re)和随机数(random)库,这些库用于数据处理和计算。
-
定义文本处理函数textParse:这个函数接受一个输入字符串,使用正则表达式切分字符串,并返回一个单词列表。单词列表中的单词都被转换为小写,并且长度小于等于2的单词被过滤掉。
-
定义主函数spam:这个函数执行以下步骤:
- 创建两个空列表doclist和classlist,用于分别存储文档数据和类别信息。
- 使用一个循环,依次处理垃圾邮件和非垃圾邮件文档,文档编号从1到25。
- 对于每个文档,使用textParse函数读取并处理文档内容,将处理后的单词列表添加到doclist中。
- 对于垃圾邮件文档,将类别标签1(表示垃圾邮件)添加到classlist中;对于非垃圾邮件文档,将类别标签0(表示非垃圾邮件)添加到classlist中。
这样,函数spam会生成两个列表doclist和classlist,其中doclist包含了所有文档的处理后的单词列表,而classlist包含了对应的类别标签(0或1)。这些数据可以用于训练和测试贝叶斯分类器,以进行垃圾邮件分类任务。
2-预料表与特征向量构建
# 定义一个函数,用于创建词汇表
def creatVocablist(doclist):
vocabSet = set([]) # 创建一个空集合,用于存储所有文档中出现的不重复单词
for document in doclist: # 遍历文档列表中的每个文档
vocabSet = vocabSet | set(document) # 将文档中的单词添加到词汇表集合中(去重)
return list(vocabSet) # 将词汇表集合转换为列表并返回
# 定义一个函数,将文本数据转换为词向量
def setOfWord2Vec(vocablist, inputSet):
returnVec = [0] * len(vocablist) # 创建一个与词汇表长度相同的零向量
for word in inputSet: # 遍历输入的单词列表
if word in vocablist: # 如果单词在词汇表中出现
returnVec[vocablist.index(word)] = 1 # 将词向量中对应位置的值设为1
return returnVec # 返回生成的词向量
# 定义主函数spam,处理垃圾邮件数据
def spam():
# ...
vocablist = creatVocablist(doclist) # 创建词汇表
trainSet = list(range(50)) # 创建训练集索引列表,初始包含前50个文档的索引
testSet = [] # 创建测试集索引列表,初始为空
for i in range(10): # 从训练集中随机选择10个样本作为测试集
randIndex = int(random.uniform(0, len(trainSet))) # 随机选择一个索引
testSet.append(trainSet[randIndex]) # 将选择的索引添加到测试集
del(trainSet[randIndex]) # 在训练集中删除已选择的索引
trainMat = [] # 创建训练矩阵,用于存储训练数据的词向量
trainClass = [] # 创建训练类别标签列表,用于存储训练数据的类别
for docIndex in trainSet: # 遍历训练集中的每个文档索引
trainMat.append(setOfWord2Vec(vocablist, doclist[docIndex])) # 将文档转换为词向量并添加到训练矩阵
trainClass.append(classlist[docIndex]) # 添加文档的类别标签
p0Vec, p1Vec, p1 = trainNB(np.array(trainMat), np.array(trainClass)) # 使用朴素贝叶斯算法进行训练
errorCount = 0 # 初始化错误计数器
for docIndex in testSet: # 遍历测试集中的每个文档索引
wordVec = setOfWord2Vec(vocablist, doclist[docIndex]) # 将测试文档转换为词向量
if classifyNB(np.array(wordVec), p0Vec, p1Vec, p1) != classlist[docIndex]: # 使用贝叶斯分类器进行分类
errorCount += 1 # 如果分类错误,错误计数加1
print('当前10个测试样本,错了:', errorCount) # 输出错误计数
以上代码片段展示了如何使用贝叶斯算法来构建预料表(词汇表)和特征向量,以便进行文本分类任务。以下是关于这个过程的简要概括:
-
构建预料表(词汇表):
- 使用
creatVocablist
函数创建一个空集合(vocabSet
),用于存储不重复的单词。 - 遍历文档列表中的每个文档,使用正则表达式切分文档并将单词添加到集合中(去重)。
- 返回词汇表,将集合转换为列表形式。
- 使用
-
特征向量构建:
- 使用
setOfWord2Vec
函数将文本数据转换为特征向量(词向量)。 - 创建一个与词汇表长度相同的零向量(
returnVec
)。 - 遍历输入的单词列表,对于每个单词:
- 如果单词在词汇表中出现,将特征向量中对应位置的值设为1。
- 如果单词不在词汇表中,保持特征向量中对应位置的值为0。
- 返回生成的特征向量。
- 使用
这两个步骤的目的是将文本数据表示为数值形式的特征向量,其中每个特征对应于词汇表中的一个单词。这些特征向量可以用于训练朴素贝叶斯分类器,以进行文本分类任务。预料表包含了训练集中所有可能的单词,而特征向量表示了每个文档中哪些单词出现(值为1)或不出现(值为0),从而构建了文本数据的数值表示。
3-分类别统计词频
# 定义朴素贝叶斯分类器的训练函数
def trainNB(trainMat, trainClass):
numTrainDocs = len(trainMat) # 训练文档的数量
numWords = len(trainMat[0]) # 特征向量的长度(词汇表的大小)
p1 = sum(trainClass) / float(numTrainDocs) # 计算类别1(垃圾邮件)的概率
# 初始化类别0和类别1的计数器和分母
p0Num = np.ones((numWords)) # 初始化类别0的计数器,进行拉普拉斯平滑处理
p1Num = np.ones((numWords)) # 初始化类别1的计数器,进行拉普拉斯平滑处理
p0Denom = 2 # 初始化类别0的分母,通常设置为类别个数
p1Denom = 2 # 初始化类别1的分母,通常设置为类别个数
for i in range(numTrainDocs): # 遍历训练文档
if trainClass[i] == 1: # 如果文档属于垃圾邮件类别
p1Num += trainMat[i] # 增加类别1的计数器,统计词汇在垃圾邮件中出现的次数
p1Denom += sum(trainMat[i]) # 增加类别1的分母,统计垃圾邮件中的总词数
else: # 如果文档属于非垃圾邮件类别
p0Num += trainMat[i] # 增加类别0的计数器,统计词汇在非垃圾邮件中出现的次数
p0Denom += sum(trainMat[i]) # 增加类别0的分母,统计非垃圾邮件中的总词数
# 计算类别0和类别1中每个词汇的条件概率
p0Vect = np.log(p0Num / p0Denom) # 类别0的条件概率向量
p1Vect = np.log(p1Num / p1Denom) # 类别1的条件概率向量
return p0Vect, p1Vect, p1 # 返回条件概率向量和类别1的概率
以上代码中的朴素贝叶斯算法通过统计词频来进行文本分类。以下是关于这个过程的简要概括:
-
初始化概率和计数器:
- 代码开始时,首先初始化了表示类别1(垃圾邮件)和类别0(非垃圾邮件)的概率p1和p0。
- 初始化了类别1和类别0的词汇计数器p1Num和p0Num,它们分别用于统计每个词汇在两个类别中的出现次数。
-
拉普拉斯平滑处理:
- 为了防止概率计算中的分母为零,进行了拉普拉斯平滑处理。分母分别初始化为2,这是一个常见的选择。
-
遍历训练文档集:
- 使用一个循环遍历训练文档集中的每个文档。
- 对于每个文档,根据文档的类别(1表示垃圾邮件,0表示非垃圾邮件)来更新相应类别的词汇计数器和分母。
- 统计每个词汇在文档中的出现次数,并将这些次数添加到相应的计数器中。
-
计算条件概率:
- 在遍历完所有训练文档后,计算每个词汇在两个类别中的条件概率。
- 通过将词汇出现次数除以相应类别的总词数来计算条件概率。
- 使用对数形式的概率向量p0Vect和p1Vect,以便后续的概率计算。
-
返回模型参数:
- 最后,函数返回计算得到的概率向量p0Vect和p1Vect,以及类别1(垃圾邮件)的概率p1。
这个过程利用训练文档中词汇的频率信息来构建了朴素贝叶斯分类器的模型。这些模型参数将用于后续的文本分类任务,根据输入文本的词汇特征,计算其属于类别0和类别1的概率,并进行分类决策。
4-贝叶斯公式对数变换
def trainNB(trainMat,trainClass):
......
p1Vec = np.log(p1Num/p1Denom)
p0Vec = np.log(p0Num/p0Denom)
return p0Vec,p1Vec,p1
def classifyNB(wordVec,p0Vec,p1Vec,p1_class):
p1 = np.log(p1_class) + sum(wordVec*p1Vec)
p0 = np.log(1.0 - p1_class) + sum(wordVec*p0Vec)
if p0>p1:
return 0
else:
return 1
1. 贝叶斯公式: 贝叶斯算法使用贝叶斯公式来计算文档属于某个类别的概率。该公式如下所示(使用LaTeX格式的数学公式):
其中:
表示给定文档
属于类别
的条件概率。
表示文档
在类别
下的概率(似然概率)。
表示类别
的先验概率。
表示文档
的概率。
2. 对数变换: 在实际计算中,为了避免数值下溢(即很小的概率相乘导致结果趋近于零),通常会对贝叶斯公式中的乘法运算进行对数变换。对数变换将乘法转换为加法,使计算更稳定。对数变换的公式如下:
这里,我们对每一项都取对数,将原公式中的乘法操作转换为加法操作。对数变换后,可以通过比较两个类别的对数概率来进行分类决策。
3. 代码中的贝叶斯分类:
-
数据准备:
- 训练数据被表示为一个由特征向量组成的矩阵
trainMat
,其中每行代表一个文档,每列代表词汇表中的一个单词。 trainClass
是一个包含每个文档所属类别标签的列表,1表示垃圾邮件,0表示非垃圾邮件。
- 训练数据被表示为一个由特征向量组成的矩阵
-
垃圾邮件和非垃圾邮件的概率计算:
p1
是垃圾邮件的先验概率,计算为垃圾邮件文档在总文档数中的比例。
-
词频统计和平滑处理:
- 对于每个文档,遍历文档中的每个单词,统计单词在垃圾邮件和非垃圾邮件中的出现次数。
p1Num
和p0Num
分别是垃圾邮件和非垃圾邮件类别中各单词出现的次数(词频统计),它们都初始化为1,以进行拉普拉斯平滑处理。p1Denom
和p0Denom
分别是垃圾邮件和非垃圾邮件类别中的总单词计数,初始化为2,也用于平滑处理。
-
条件概率计算:
- 计算每个单词在垃圾邮件和非垃圾邮件类别下的条件概率(似然度),使用拉普拉斯平滑后的词频信息。
- 对数概率(取自然对数)用于避免小概率相乘时的数值下溢出。
这个过程中,贝叶斯算法通过统计文档中各单词在不同类别下的出现频率(词频统计)来学习类别的条件概率分布。这些条件概率将在分类时用于计算文档属于不同类别的后验概率,从而进行文本分类。拉普拉斯平滑用于处理未在训练数据中出现的单词,以避免概率为零的情况。
5-完成预测模块
完整代码:
import numpy as np # 导入NumPy库,用于数值计算
import re # 导入re库,用于正则表达式操作
import random # 导入random库,用于生成随机数
# 定义一个函数textParse,用于处理文本数据
def textParse(input_string):
listofTokens = re.split(r'\W+', input_string) # 使用正则表达式切分输入的字符串
return [tok.lower() for tok in listofTokens if len(listofTokens) > 2] # 返回切分后的单词列表,转换为小写并去除长度小于等于2的单词
# 定义一个函数,用于创建词汇表
def creatVocablist(doclist):
vocabSet = set([]) # 创建一个空集合,用于存储所有文档中出现的不重复单词
for document in doclist: # 遍历文档列表中的每个文档
vocabSet = vocabSet | set(document) # 将文档中的单词添加到词汇表集合中(去重)
return list(vocabSet) # 将词汇表集合转换为列表并返回
# 定义一个函数,将文本数据转换为词向量
def setOfWord2Vec(vocablist, inputSet):
returnVec = [0] * len(vocablist) # 创建一个与词汇表长度相同的零向量
for word in inputSet: # 遍历输入的单词列表
if word in vocablist: # 如果单词在词汇表中出现
returnVec[vocablist.index(word)] = 1 # 将词向量中对应位置的值设为1
return returnVec # 返回生成的词向量
# 定义朴素贝叶斯分类器的训练函数
def trainNB(trainMat, trainClass):
numTrainDocs = len(trainMat) # 训练文档的数量
numWords = len(trainMat[0]) # 特征向量的长度(词汇表的大小)
p1 = sum(trainClass) / float(numTrainDocs) # 计算类别1(垃圾邮件)的概率
# 初始化类别0和类别1的计数器和分母
p0Num = np.ones((numWords)) # 初始化类别0的计数器,进行拉普拉斯平滑处理
p1Num = np.ones((numWords)) # 初始化类别1的计数器,进行拉普拉斯平滑处理
p0Denom = 2 # 初始化类别0的分母,通常设置为类别个数
p1Denom = 2 # 初始化类别1的分母,通常设置为类别个数
for i in range(numTrainDocs): # 遍历训练文档
if trainClass[i] == 1: # 如果文档属于垃圾邮件类别
p1Num += trainMat[i] # 增加类别1的计数器,统计词汇在垃圾邮件中出现的次数
p1Denom += sum(trainMat[i]) # 增加类别1的分母,统计垃圾邮件中的总词数
else: # 如果文档属于非垃圾邮件类别
p0Num += trainMat[i] # 增加类别0的计数器,统计词汇在非垃圾邮件中出现的次数
p0Denom += sum(trainMat[i]) # 增加类别0的分母,统计非垃圾邮件中的总词数
# 计算类别0和类别1中每个词汇的条件概率
p0Vect = np.log(p0Num / p0Denom) # 类别0的条件概率向量
p1Vect = np.log(p1Num / p1Denom) # 类别1的条件概率向量
return p0Vect, p1Vect, p1 # 返回条件概率向量和类别1的概率
def classifyNB(wordVec,p0Vec,p1Vec,p1_class):
p1 = np.log(p1_class) + sum(wordVec*p1Vec)
p0 = np.log(1.0 - p1_class) + sum(wordVec*p0Vec)
if p0>p1:
return 0
else:
return 1
# 定义一个函数spam,用于处理垃圾邮件数据
def spam():
doclist = [] # 创建一个空列表,用于存储文档数据
classlist = [] # 创建一个空列表,用于存储类别信息
for i in range(1, 26): # 循环处理1到25号文档
# 处理垃圾邮件文档
wordlist = textParse(open('email/spam/%d.txt' % i, 'r').read()) # 读取并处理垃圾邮件文档
doclist.append(wordlist) # 将处理后的单词列表添加到文档列表
classlist.append(1) # 将类别标签1(表示垃圾邮件)添加到类别列表
# 处理非垃圾邮件文档
wordlist = textParse(open('email/ham/%d.txt' % i, 'r').read()) # 读取并处理非垃圾邮件文档
doclist.append(wordlist) # 将处理后的单词列表添加到文档列表
classlist.append(0) # 将类别标签0(表示非垃圾邮件)添加到类别列表
vocablist = creatVocablist(doclist) # 创建词汇表
trainSet = list(range(50)) # 创建训练集索引列表,初始包含前50个文档的索引
testSet = [] # 创建测试集索引列表,初始为空
for i in range(10): # 从训练集中随机选择10个样本作为测试集
randIndex = int(random.uniform(0, len(trainSet))) # 随机选择一个索引
testSet.append(trainSet[randIndex]) # 将选择的索引添加到测试集
del (trainSet[randIndex]) # 在训练集中删除已选择的索引
trainMat = [] # 创建训练矩阵,用于存储训练数据的词向量
trainClass = [] # 创建训练类别标签列表,用于存储训练数据的类别
for docIndex in trainSet: # 遍历训练集中的每个文档索引
trainMat.append(setOfWord2Vec(vocablist, doclist[docIndex])) # 将文档转换为词向量并添加到训练矩阵
trainClass.append(classlist[docIndex]) # 添加文档的类别标签
p0Vec, p1Vec, p1 = trainNB(np.array(trainMat), np.array(trainClass)) # 使用朴素贝叶斯算法进行训练
errorCount = 0 # 初始化错误计数器
for docIndex in testSet: # 遍历测试集中的每个文档索引
wordVec = setOfWord2Vec(vocablist, doclist[docIndex]) # 将测试文档转换为词向量
if classifyNB(np.array(wordVec), p0Vec, p1Vec, p1) != classlist[docIndex]: # 使用贝叶斯分类器进行分类
errorCount += 1 # 如果分类错误,错误计数加1
print('当前10个测试样本,错了:', errorCount) # 输出错误计数
if __name__ == '__main__':
spam()
贝叶斯算法是一种基于贝叶斯定理的概率统计方法,用于解决分类和概率估计问题。它的主要思想是根据已知信息和数据来估计未知事件的概率分布,然后基于这些概率分布进行决策或分类。
以下是关于贝叶斯算法的主要概念和要点:
-
贝叶斯定理:算法基于贝叶斯定理,该定理描述了如何根据先验概率和新的证据来更新事件的概率。在分类问题中,它帮助我们估计一个数据点属于不同类别的后验概率。
-
先验概率:先验概率是我们在观察到任何数据之前对事件的概率的估计。它是基于以往经验或领域知识得出的。
-
似然度:似然度是指在给定某个类别下观察到特定数据点的概率。它描述了数据点与每个类别的匹配程度。
-
后验概率:后验概率是在考虑了观察到的数据点后,更新的事件概率。在分类问题中,它表示给定数据点,属于每个类别的概率。
-
朴素贝叶斯算法:朴素贝叶斯算法是一种贝叶斯分类方法,它假设各个特征之间相互独立,这被称为朴素贝叶斯假设。尽管这个假设在实际问题中可能不成立,但朴素贝叶斯算法仍然表现出色彩丰富,尤其在文本分类和垃圾邮件过滤等应用中。
-
多项式朴素贝叶斯:多项式朴素贝叶斯用于处理多类别文本分类问题,其中特征通常是词汇表中的单词,而似然度是基于单词在文档中的出现频率进行估计。
-
高斯朴素贝叶斯:高斯朴素贝叶斯适用于连续型特征,它假设特征的分布是高斯分布。
-
拉普拉斯平滑:为了避免概率估计中的零概率问题,通常会使用拉普拉斯平滑(或加法平滑)来为所有可能的特征值增加一个计数,以确保每个特征值至少有一个计数。
-
应用领域:贝叶斯算法广泛应用于文本分类、垃圾邮件过滤、情感分析、推荐系统、医学诊断等领域,其中分类和概率估计是关键任务。
总的来说,贝叶斯算法提供了一种基于概率的框架,可以有效地处理分类问题和概率估计问题。它的优点包括简单、易于实现,适用于各种数据类型,尤其在处理高维数据和大规模文本数据时表现出色。