目录
一、贝叶斯公式
1.贝叶斯定理
贝叶斯公式(Bayes' theorem)是概率论中一条重要的公式,它用于计算在已知某些先验条件下的事件的后验概率。在机器学习中,贝叶斯公式经常用于进行概率推断和分类任务。
贝叶斯公式的数学表达为: P(A|B) = (P(B|A) * P(A)) / P(B)
P(A|B) 表示在事件 B 已经发生的条件下,事件 A 发生的概率,即后验概率;
P(B|A) 表示在事件 A 已经发生的条件下,事件 B 发生的概率,即似然;
P(A) 表示事件 A 发生的先验概率;
P(B) 表示事件 B 发生的概率。
贝叶斯公式的核心思想是通过已知的先验概率和观测到的数据来计算后验概率。在机器学习中,可以将事件 A 看作是分类的类别,事件 B 看作是观测到的特征或数据。根据贝叶斯公式,我们可以计算给定观测数据的情况下,每个类别的后验概率,并基于概率选择最可能的类别。
贝叶斯公式在机器学习中的应用包括:
朴素贝叶斯分类器:基于贝叶斯公式和特征独立性假设,将样本分类到最有可能的类别;
贝叶斯网络:用于建模变量之间的概率依赖关系,进行概率推断和预测任务;
隐马尔可夫模型(HMM):基于贝叶斯公式,在序列数据中进行状态推断和预测。
贝叶斯公式在机器学习领域有广泛的应用,它提供了一种基于概率推断的方法,能够更好地处理不确定性和噪声,并在许多实际问题中取得了良好的效果。
2.先验概率
指根据以往经验和分析。在实验或采样前就可以得到的概率。
P()代表还没有训练模型之前,根据历史数据/经验估算cj拥有的初始概率。P(
)常被称为c的先验概率(prior probability) ,它反映了
的概率分布,该分布独立于样本。
通常可以用样例中属于的样例数|
|比上总样例数|D|来近似,即:
例子:在抛硬币前我们可以知道抛出正反面的概率各为0.5
3.后验概率
指某件事已经发生,想要计算这件事发生的原因是由某个因素引起的概率。
给定数据样本x时成立的概率P(
| x )被称为后验概率(posterior probability),因为它反映了在看到数据样本 x后
成立的置信度。
公式:P( |X) = P(X|
) * P(
) / P(X)
例子:假设旷课的原因有两种,一种是有意,一种是无意。
今天小a旷课,我们可以通过“小a旷课”这个结果来算一下他有意旷课的概率有多大。
4.全概率公式
全概率公式(Law of Total Probability)是概率论中的一条基本公式,用于计算一个事件的概率。它描述了当事件的发生可能由多个不同的原因引起时,如何计算这个事件的总概率。
具体地,假设有一组互不相交的事件 B1,B2,...,Bn,它们构成了样本空间的一个划分,即 B1 ∪ B2 ∪ ... ∪ Bn = Ω,其中 Ω 表示样本空间。设 A 是一个任意的事件,则全概率公式可以表示为:
P(A) = ∑ P(A|Bi) * P(Bi)
其中,P(A|Bi) 表示在事件 Bi 发生的条件下,事件 A 发生的概率,也称为 A 对于 Bi 的条件概率;P(Bi) 表示事件 Bi 发生的概率,也称为 Bi 的先验概率。因此,全概率公式可以理解为将事件 A 分解成若干个互不相交的情形,并对所有情形的概率求和。
全概率公式在实际问题中的应用非常广泛。例如,在统计学中,我们经常需要计算一个事件的概率,但是由于缺乏完整的信息,无法直接计算。此时,我们可以使用全概率公式来计算该事件的概率,通过计算条件概率和先验概率的乘积来对所有可能的情形进行加权求和。
另外,全概率公式还可以用于贝叶斯推断中的先验概率的更新。在贝叶斯推断中,我们需要根据先验概率和观测数据来计算后验概率,而全概率公式可以用于计算观测数据的边缘概率,从而更新先验概率。
5.最大似然估计
最大似然估计(Maximum Likelihood Estimation,简称MLE)是一种用于参数估计的方法。它的基本思想是:在给定样本的条件下,选取最能使样本出现的概率最大的参数作为真实参数的估计值。
具体来说,假设有一个参数化模型,其中包含一个未知的参数 θ,我们的目标是通过已有的样本来估计 θ 的取值。设 X = {x1, x2, ..., xn} 是一个由 n 个独立同分布的观测数据组成的样本,X 的概率密度函数为 f(x;θ),则似然函数(Likelihood Function)可以表示为
L(θ;X) = ∏ f(xi;θ)
其中 ∏ 表示连乘符号。似然函数 L(θ;X) 可以理解为在给定样本 X 下,参数 θ 的可能性大小。为了得到最大似然估计,我们需要选择一个使似然函数取最大值的参数 θ,即:
θ^ = argmax L(θ;X)
通过对似然函数求导,并令导数等于 0,我们可以得到极大似然估计的解析解或者使用迭代算法来搜索最大值。
最大似然估计在统计学和机器学习中有广泛的应用,例如线性回归、逻辑回归、朴素贝叶斯分类器等。它具有很好的理论性质,可以证明在一定条件下,最大似然估计是渐近无偏、有效的。同时,最大似然估计还可以推导出参数估计的置信区间和假设检验等重要结果。
二、朴素贝叶斯分类器
朴素贝叶斯分类器(Naive Bayes Classifier)是一种基于贝叶斯定理和特征条件独立假设的分类算法。它被广泛应用于文本分类、垃圾邮件过滤、情感分析等领域。
朴素贝叶斯分类器的原理基于贝叶斯定理,即根据已知类别的数据来估计特征与类别之间的概率分布,然后使用这些概率来对新样本进行分类。
具体地,设特征向量为 X = (x1, x2, ..., xn),类别集合为 C = {c1, c2, ..., ck},我们的目标是计算在给定特征向量 X 的条件下,属于每个类别的概率 P(ci|X),然后选择具有最大后验概率的类别作为样本的分类结果。
朴素贝叶斯分类器的"朴素"之处在于它假设特征之间相互独立,即:
其中 P(c_i) 是类别 c_i 的先验概率,P(X|c_i) 是在类别 c_i 下特征向量 X 的条件概率,P(X) 是特征向量 X 的边缘概率。由于 P(X) 对所有类别都是相同的,因此可以忽略掉。
基于上述假设,我们可以计算出每个类别的后验概率,并选择具有最大概率的类别作为样本的分类结果。
朴素贝叶斯分类器的优点在于简单、高效,且对小规模数据表现良好;并且在特征之间条件独立的情况下,即使部分特征缺失,也能够进行有效的分类。然而,它也有一个明显的局限性,就是对特征条件独立的假设在实际问题中并不总是成立,因此在面对高维度、相关性较强的数据时,朴素贝叶斯分类器可能表现不佳。
三、拉普拉斯修正
拉普拉斯修正(Laplace Smoothing)是朴素贝叶斯分类器中一种解决概率值为0的问题的方法。当某个特征在某个类别下没有出现过时,根据朴素贝叶斯分类器的公式,对应的条件概率将为0,这将导致整个后验概率为0,从而无法进行分类。
拉普拉斯修正的思想是在计算条件概率时对每个特征的出现次数加上一个小的常数,以确保每个特征在所有类别下的出现次数都不为0。具体地,假设在训练集中特征 xi 在类别 中出现了 k 次,那么该特征在类别
下的条件概率可以表示为:
P(xi| = (k+1) / (N+K)
其中 N 是类别 中样本的总数,K 是特征 xi 取值的总数,这里的加1即为拉普拉斯常数。在实际应用中,常见的拉普拉斯常数取值为1。
通过拉普拉斯修正,我们可以避免概率值为0的情况,从而使得朴素贝叶斯分类器在面对离散型数据时更加稳健。但需要注意的是,在拉普拉斯修正的过程中,我们引入了一些噪声,这可能会对分类结果产生一定的影响。因此,我们需要谨慎地选择合适的拉普拉斯常数,以便在保证模型鲁棒性的同时,尽量减小噪声的影响。
四、实例:垃圾邮件分类
数据集准备:
email文件夹下有两个文件夹ham和spam。ham文件夹下的txt文件为正常邮件;spam文件下的txt文件为垃圾邮件。
完整代码:
# -*- coding: UTF-8 -*-
import numpy as np
import re
import random
#整理词汇表
def createVocabList(dataSet):
vocabSet = set([]) # 创建一个空的不重复列表
for document in dataSet:
vocabSet = vocabSet | set(document) # 取并集
return list(vocabSet)
def setOfWords2Vec(vocabList, inputSet):
returnVec = [0] * len(vocabList) #创建一个其中所含元素都为0的向量
for word in inputSet: #遍历每个词条
if word in vocabList: #如果词条存在于词汇表中,则置1
returnVec[vocabList.index(word)] = 1
else:
print("the word: %s is not in my Vocabulary!" % word)
return returnVec #返回文档向量
#构建词袋模型
def bagOfWords2VecMN(vocabList, inputSet):
returnVec = [0] * len(vocabList) # 创建一个其中所含元素都为0的向量
for word in inputSet: # 遍历每个词条
if word in vocabList: # 如果词条存在于词汇表中,则计数加一
returnVec[vocabList.index(word)] += 1
return returnVec # 返回词袋模型
#朴素贝叶斯分类训练函数
def trainNB0(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix) # 计算训练的文档数目
numWords = len(trainMatrix[0]) # 计算每篇文档的词条数
pAbusive = sum(trainCategory) / float(numTrainDocs) # 文档属于垃圾邮件类的概率
p0Num = np.ones(numWords)
p1Num = np.ones(numWords) # 创建numpy.ones数组,词条出现数初始化为1,拉普拉斯平滑
p0Denom = 2.0
p1Denom = 2.0 # 分母初始化为2 ,拉普拉斯平滑
for i in range(numTrainDocs):
if trainCategory[i] == 1: # 统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)···
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
else: # 统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p1Vect = np.log(p1Num / p1Denom)
p0Vect = np.log(p0Num / p0Denom) #取对数,防止下溢出
return p0Vect, p1Vect, pAbusive # 返回属于正常邮件类的条件概率数组,属于侮辱垃圾邮件类的条件概率数组,文档属于垃圾邮件类的概率
#朴素贝叶斯分类函数
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
p1=sum(vec2Classify*p1Vec)+np.log(pClass1)
p0=sum(vec2Classify*p0Vec)+np.log(1.0-pClass1)
if p1 > p0:
return 1 #属于正常邮件类
else:
return 0 #属于垃圾邮件类
#提取单词
def textParse(bigString): # 将字符串转换为字符列表
listOfTokens = re.split(r'\W*', bigString) # 将特殊符号作为切分标志进行字符串切分,即非字母、非数字
return [tok.lower() for tok in listOfTokens if len(tok) > 2] # 除了单个字母,例如大写的I,其它单词变成小写
#测试朴素贝叶斯分类器,使用朴素贝叶斯进行交叉验证
def spamTest():
docList = []
classList = []
fullText = []
for i in range(1, 21): # 遍历20个txt文件
wordList = textParse(open('email/spam/%d.txt' % i, 'r').read()) # 读取每个垃圾邮件,并字符串转换成字符串列表
docList.append(wordList)
fullText.append(wordList)
classList.append(1) # 标记垃圾邮件,1表示垃圾文件
wordList = textParse(open('email/ham/%d.txt' % i, 'r').read()) # 读取每个非垃圾邮件,并字符串转换成字符串列表
docList.append(wordList)
fullText.append(wordList)
classList.append(0) # 标记正常邮件,0表示正常文件
vocabList = createVocabList(docList) # 创建词汇表,不重复
trainingSet = list(range(50))
testSet = [] # 创建存储训练集的索引值的列表和测试集的索引值的列表
for i in range(10): # 从50个邮件中,随机挑选出40个作为训练集,10个做测试集
randIndex = int(random.uniform(0, len(trainingSet))) # 随机选取索索引值
testSet.append(trainingSet[randIndex]) # 添加测试集的索引值
del (trainingSet[randIndex]) # 在训练集列表中删除添加到测试集的索引值
trainMat = []
trainClasses = [] # 创建训练集矩阵和训练集类别标签系向量
for docIndex in trainingSet: # 遍历训练集
trainMat.append(setOfWords2Vec(vocabList, docList[docIndex])) # 将生成的词集模型添加到训练矩阵中
trainClasses.append(classList[docIndex]) # 将类别添加到训练集类别标签系向量中
p0V, p1V, pSpam = trainNB0(np.array(trainMat), np.array(trainClasses)) # 训练朴素贝叶斯模型
errorCount = 0 # 错误分类计数
for docIndex in testSet: # 遍历测试集
wordVector = setOfWords2Vec(vocabList, docList[docIndex]) # 测试集的词集模型
if classifyNB(np.array(wordVector), p0V, p1V, pSpam) != classList[docIndex]: # 如果分类错误
errorCount += 1 # 错误计数加1
print("分类错误的测试集:", docList[docIndex])
print('错误率:%.2f%%' % (float(errorCount) / len(testSet) * 100))
if __name__ == '__main__':
spamTest()
createVocabList 函数用于创建词汇表,它接收一个数据集并返回数据集中所有不重复单词的列表。
setOfWords2Vec 函数将输入的文本转换为向量,向量的每个元素表示词汇表中对应单词的出现情况。
bagOfWords2VecMN 函数构建了词袋模型,与 setOfWords2Vec 类似,但它统计了每个单词的出现次数而不是简单地记录是否出现。
trainNB0 函数用于训练朴素贝叶斯分类器,它接收训练数据以及对应的分类标签,计算每个单词在不同分类下的条件概率,并返回相应的概率向量。
cassifyNB 函数用于对新的文本进行分类,它接收一个文本向量以及训练得到的概率向量和类别概率,然后根据朴素贝叶斯分类规则进行分类。
textParse 函数用于将文本解析成单词列表,同时进行了大小写转换和去除长度小于等于2的单词等操作。
spamTest 函数是整个分类器的测试函数,它读取垃圾邮件和正常邮件的数据集,然后进行训练和测试,最后输出分类错误率。
五、小结
学习朴素贝叶斯分类器是机器学习入门的重要一步,因为它是一种简单而有效的分类算法,并且在自然语言处理等领域有广泛的应用。通过学习朴素贝叶斯分类器,我对分类模型的基本原理和实现方法有了更深入的理解。
总结一下,朴素贝叶斯分类器具有以下优点:
1.简单易懂:算法的原理很简单,易于理解和实现。
2.高效性:朴素贝叶斯分类器的计算速度非常快,可以处理大规模的数据集。
3.适用范围广:朴素贝叶斯分类器可以应用于多种分类任务,尤其是文本分类任务。
4.可扩展性强:朴素贝叶斯分类器可以通过添加新的特征来改善模型的性能。
当然,朴素贝叶斯分类器也存在一些缺点:
1.假设过于简单:算法假设所有特征之间是相互独立的,但在实际情况中,这种假设很难满足,因此可能会导致误差较大。
2.对数据分布敏感:朴素贝叶斯分类器是基于概率分布的分类方法,因此对数据分布比较敏感,如果数据集的分布不符合朴素贝叶斯分类器的假设,模型性能可能会受到影响。
3.通过学习和实践,我深刻认识到机器学习是一门需要不断实践和探索的技术,只有不断地尝试和调整,才能达到更好的效果。同时,选择合适的算法和处理方法也是非常重要的,对于不同的问题,需要选择不同的算法来解决,这需要我们具备一定的理论知识和实践经验。