贝叶斯算法用于邮件过滤的过程
独立性假设是指一个词的出现概率并不依赖于文档中的其他词,这也是称为朴素贝叶斯的原因。
1.创建一个包含所有文档的不重复词汇的词汇表createVocabList()
2.将每个文档转换成特征向量[[data,label],…,[data,label]],如果词语在词汇表中出现,就在词汇表相应位置置1,最后形成包含0,1的特征向量setOfWord2Vec()
3.计算贝叶斯概率中的p(W|Ci),p(w1|c1),…p(wn|c1);p(w1|c0),…p(wn|c0)trainNB0()
4.计算输入向量的贝叶斯概率p(w|Ci)p(Ci),进行比较,输出可能性最大的分类
5.保存训练的结果
6.测试数据
基于概率论的分类方法:朴素贝叶斯
分类器有时会产生错误结果,这时可以要求分类器给出一个最优的类别猜测结果,同时给出这个猜测的概率估计值
4.1 基于贝叶斯决策理论的分类方法
-
朴素贝叶斯
优点:在数据较少的情况下仍然有效,可以处理多类别问题
缺点:对于输入数据的准备方式较为敏感
使用数据类型:标称型数据 -
贝叶斯决策理论
1)贝叶斯概率引入先验知识和逻辑推理来处理不确定命题
2)一个数据集,由两类数据组成。两个参数已知的概率分布,参数决定了分布的形状。用p1(x,y)标识数据点(x,y)属于类别1的概率,p2(x,y)表示属于类别2的概率。
3)对于一个新数据点(x,y),可以判定它的类别:
如果p1(x,y)>p2(x,y),那么类别为1
如果p1(x,y)<p2(x,y),那么类别为2
贝叶斯决策理论的核心思想:选择具有最高概率的决策
计算数据点属于每个类别的概率,并进行比较
4.2 条件概率
贝叶斯准则告诉我们如何交换条件概率中的条件与结果,即结果已知p(x|c),要求p(c|x).
p(c|x)=p(x|c)*p©/p(x)
4.3 使用条件概率来分类
p(c1|x,y) 给定某个由x,y表示的数据点,那么该数据点来自类别c1的概率是多少?
p(x,y|Ci) 给定类别,(x,y)属于类别c的概率是多少?
p(Ci|x,y)=p(x,y|Ci)*p(Ci)/p(x,y)
4.4 使用朴素贝叶斯进行文档分类(独立特征)
在文档分类中,整个文档(如一封电子邮件)是实例,而电子邮件中的某些元素则构成特征。
-
朴素贝叶斯的一般过程
1).收集数据
2).准备数据:需要数值型或者布尔型数据
3).分析数据:有大量特征时,绘制特征作用不大,此时使用直方图效果更好
4).训练算法:计算不同的独立特征的条件概率
5).测试算法:计算错误率
6).使用算法:一个常见的朴素贝叶斯应用是文档分类。可以在任意的分类场景中使用朴素贝叶分类器,不一定非要是文本 -
朴素贝叶斯的假设
每个特征需要N个样本,10个特征需要的样本数为N10,所需要的样本数会随着特征数目增大而迅速增长。
1)朴素贝叶斯假设每个特征之间相互独立**,N10就降到10*N个。 就邮件来说,每个词我们认为他没有上下文含义,也就是说是互相独立的。这种假设正是“朴素”的含义。
2)每个特征同等重要**。也就是说忽略特征出现的次数。
4.5 使用python进行文本分类
4.5.1 准备数据:从文本中构建词向量
把文本看成单词向量或词条向量,也就是说将句子转换为向量。考虑出现在所有文档中的所有单词,再决定将哪些词纳入词汇表或者说索要的词汇集合,然后必须要将每一篇文档转换为词汇表上的向量。
朴素贝叶斯通常有两种实现方式:一种是基于伯努利模型实现,一种基于多项式模型实现。这里采用伯努利实现。该实现方式并不考虑词在文档中出现的次数,只考虑初步出现,因此这个意义上相当于假设词是等权重的。
4.5.2 训练算法:从词向量计算概率
重写贝叶斯准则
p(Ci|W)=p(W|Ci)*p(Ci)/p(W)
W是一个向量,由多个数值组成。
-
首先可以通过类别i(侮辱性留言或非侮辱性留言)中文档数除以总的文档数来计算概率p(ci)
-
计算p(W|ci).使用贝叶斯假设,将W展开为一个个独立特征,那么有p(W|ci)=p(w1,w2,w3,…,wn|ci)=p(w1|ci)p(w2|ci)…p(wn|ci)
-
伪代码如下:
计算每个类别中的文档数目
对每篇训练文档:
对每个类别:
如果词条出现在文档中->增加该词条的计数值
增加所有词条的计数值
对每个类别:
对每个词条:
将该词条的数目除以总词条数目得到条件概率
返回每个类别的条件概率
4.5.3 测试算法:根据现实情况修改分类器
- 利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算p(w1|ci)p(w2|ci)…p(wn|ci)。如果其中一个概率值为0,那么最后乘积为0,为消除这种影响,可以将所有词的出现数初始化为1,并将分母初始化为2
- 另一个问题是下溢出,这是由于太多很小的数相乘造成的(太多很小的数相乘,最后python四舍五入得到0).一般解决办法是对乘积取自然对数。
def loadDataSet():
"""创建实验样本
return postingList:对词条切分后的文档集合
return classVec:类别标签的集合。这些标注信息人工标注,用于训练程序以便自动检测侮辱性留言
"""
postingList = [
['my','dog','has','flea','problems','help','please'],
['maybe','not','take','him','to','dog','park','stupid'],
['maybe','not','take','him','to','dog','park','stupid']
]
classVec = [0,1,1] #1代表侮辱性文字,0代表正常言论
return postingList,classVec
def createVocabList(dataSet):
"""创建一个包含在所有文档中出现的不重复词的列表
dataset:文档的列表,遍历列表构建词汇表"""
vocabSet = set([])
#每篇文档返回新词集合加入到词汇列表中
for document in dataSet:
vocabSet = vocabSet|set(document) #并集
return sorted(list(vocabSet))
def setOfWord2Vec(vocabList,inputSet):
"""词集模型:输入参数为词汇表vocablist及某个文档inputset,输出的是文档特征向量,向量的每一个元素为1或0,分别表示词汇表中的单词在输入文档中是否出现。"""
returnVec = [0]*len(vocabList) #与词汇表等长的向量
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1
else:
print("the word:%s is not in my vocabulary!" % word)
return returnVec
def trainNB0(trainMatrix,trainCategory):
"""朴素贝叶斯分类器训练函数p(wi|c1)\n
因为后续的计算使用的numpy,故传入的两个数组需为numpy数组\n
trainMatrix:文档矩阵,由一个一个的文档特征向量组成\n
trainCategory:由每篇文档标签(是否是侮辱性文档)所构成的向量\n
return p0Vect,p1Vect,pAbusive:两个类别的概率向量([p(w1|ci),p(w2|ci),...,p(wn|ci)])以及属于侮辱性文档的概率"""
numTrainDocs = len(trainMatrix)
print(trainMatrix)
numWords = len(trainMatrix[0])
pAbusive = sum(trainCategory)/float(numTrainDocs) #属于侮辱性文档的概率p(c1)
#为计算p(wi|c1)和p(wi|c0),需要初始化程序中的分子和分母变量。由于W中的元素很多,所以使用numpy进行计算
p0Num = np.ones(numWords)
p1Num = np.ones(numWords)
p0Denom = 2.0
p1Denom = 2.0
for i in range(numTrainDocs):
#如果是侮辱性文档,则该词加一(trainMatrix的每个向量的元素都是只含1或0的)
if trainCategory[i] == 1:
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i]) #计算该类别的总词数
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
#p1Vect = log(p1Num/p1Denom) #每个元素除以该类别中的总词数p(wi|c1)
"""TYPEERROR:only size-1 arrays can be converted to Python scalars
对于numpy矩阵,需要调用numpy中的log方法
"""
p1Vect = np.log(p1Num/p1Denom)
print(p1Vect)
p0Vect = np.log(p0Num/p0Denom)
return p0Vect,p1Vect,pAbusive
def classifyNB(vec2Classify:np.array,p0Vec,p1Vec,pClass1):
"""朴素贝叶斯分类函数
vec2Classify:文档转化而来的特征向量\n
p0Vec:[p(w1|c0),p(w2|c0),...,p(wn|c0)]\n
p1Vec:[p(w1|c1),p(w2|c1),...,p(wn|c1)]\n
pClass1:训练集中侮辱性文档的概率"""
#是侮辱性文档的概率为p,则是正常文档的概率为1-p
#[p(w1|c0),p(w2|c0),...,p(wn|c0)]+log[p(c1)]=p(W|c1)*p(c1).因为对于p0和p1,分母的值是一样的,所以不进行计算
p1 = sum(vec2Classify*p1Vec) + log(pClass1) #对应元素相乘,然后加和
p0 = sum(vec2Classify*p0Vec) + log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0
def testingNB():
"""测试贝叶斯算法:过滤恶意留言"""
listOPosts,listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
trainMat = []
for postinDoc in listOPosts:
trainMat.append(setOfWord2Vec(myVocabList,postinDoc))
p0V,p1V,pAb = trainNB0(np.array(trainMat),np.array(listClasses))
testEntry = ['love','my','garbage']
thisDoc = np.array(setOfWord2Vec(myVocabList,testEntry))
print(testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb))
4.5.4 准备数据:词袋模型
目前,我们将每个词的出现与否作为一个特征,这可以被描述为词集模型(set-of-words-model).如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法称为词袋模型(bag-of-words model)
def bagOfWord2VecMN(vocablist,inputSet):
"""词袋模型,允许一个词出现多次"""
retrunVec = [0]*len(vocablist)
for word in inputSet:
if word in vocablist:
returnVec[vocablist.index(word)] +=1
return returnVec
4.6 示例:使用朴素贝叶斯过滤垃圾邮件
使用朴素贝叶斯解决问题时,需要先从文本内容得到字符串列表,然后生成词向量。
1.收集数据
2.准备数据:将文本文件解析成词条向量
3.分析数据:检查词条确保解析的正确性
4.训练算法
5.测试算法:使用classifyNB(),并构建一个新的测试函数来计算文档集的错误率
6.使用算法:构建一个完整的程序对一组文档进行分类,将错分的文档输出到屏幕上
4.6.1 准备数据:切分文本
对于一个文本字符串,可以使用python的string.split()方法将其切分
#垃圾邮件过滤器
def textParse(bigString):
"""切分文本"""
bigString = re.sub('\\W','',bigString) #去掉bigString中除单词、数字外的任意字符串
listOfTokens = re.split(' ',bigString)
print(listOfTokens)
return [tok.lower() for tok in listOfTokens if len(tok)>2] #lower(),python函数,将字符串所有字母转化为小写字母,过滤掉长度小于3的无效词汇
def spamTest():
"""使用朴素贝叶斯进行交叉验证,过滤垃圾邮件
#导入并解析文档
#随机构建训练集:测试集、训练集中的邮件都是随机选出的
#训练模型,返回训练集中不同类别文档的概率p(w|ci)
#testSet测试集验证模型准确率计算错误率"""
docList = [] #文档的列表list of list
classList = [] #标签
fullText = [] #保存全部的文档
for i in range(1,26):
#导入并解析文档
fileContent = open('./M_04_bayes_email/spam/%d.txt' % i).read()
print(fileContent)
wordList = textParse(open('./M_04_bayes_email/spam/%d.txt' % i).read())
print(wordList)
docList.append(wordList)
fullText.extend(wordList)
classList.append(1)
wordList = textParse(open('./M_04_bayes_email/ham/%d.txt' % i).read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList = createVocabList(docList)
print(vocabList)
trainingSet = list(range(50)) #一共五十个文档
testSet = []
for i in range(10):
"""这种随机选择数据的一部分作为训练集,而剩余部分作为测试集的过程称为留存交叉验证(hold-out cross validation),可多次迭代后获得较为精确的错误率"""
#随机构建测试集
randIndex = int(random.uniform(0,len(trainingSet))) #随机生成范围内的浮点数
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex]) #从训练集中删除
trainMat = []
trainClasses = []
for docIndex in trainingSet:
#训练模型,返回训练集中不同类别文档的概率p(w|ci)
trainMat.append(setOfWord2Vec(vocabList,docList[docIndex]))
trainClasses.append(classList[docIndex])
print(trainMat)
p0V,p1V,pSpam = trainNB0(np.array(trainMat),np.array(trainClasses))
errorCount = 0
for docIndex in testSet:
#testSet测试集验证模型准确率计算错误率
wordVector = setOfWord2Vec(vocabList,docList[docIndex])
if classifyNB(np.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
errorCount += 1
print('the misclassify document is number %s ' % docIndex)
print('the error rate is:',float(errorCount)/len(testSet))
4.7 示例:使用朴素贝叶斯分类器从个人广告中获取区域倾向
目标:从两个城市中选取一些人,通过分析这些人发布的征婚广告信息,来比较这两个城市的人们在广告用词上是否不同
#从个人广告中获取区域倾向
def clacMostFreq(vocabList,fullText:list):
"""计算单词表中的词汇在fullText中出现的频率,返回频率前30的词语"""
freqDict = {}
for token in vocabList:
freqDict[token] = fullText.count(token)
sortedFreq = sorted(freqDict.iteritems(),key=operator.itemgetter(1),reverse=True)
print(sortedFreq[:30])
return sortedFreq[:30]
def localWords(feed1,feed0):
"""从个人广告中获取区域倾向
feed1,feed0为两个RSS源
移除高频词汇,还可以再移除停用词词汇,这都会使错误率降低"""
#一个RSS文件就是一段规范的XML数据,该文件一般以rss,xml或者rdf作为后缀。
docList=[]
classList = []
fullText = []
minLen = min(len(feed1['entries']),len(feed0['entries']))
for i in range(minLen):
wordList = textParse(feed1['entries'][i]['summary']) #每次访问一条RSS源
docList.append(wordList)
fullText.extend(wordList)
classList.append(1)
wordList = textParse(feed0['entries'][i]['summary']) #每次访问一条RSS源
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList = createVocabList(docList)
top30Words = clacMostFreq(vocabList,fullText)
for pairW in top30Words:
#去掉出现次数最高的那些词,如a,the
if pairW[0] in vocabList:
vocabList.remove(pairW[0])
trainingSet = list(range(2*minLen))
testSet = []
for i in range(20):
randIndex = int(random.uniform(0,len(trainingSet)))
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex])
trainMat = []
traingClasses = []
for docIndex in trainingSet:
#向量化训练集
trainMat.append(bagOfWord2VecMN(vocabList,docList[docIndex]))
traingClasses.append(classList[docIndex])
p0V,p1V,pSpam = trainNB0(np.array(trainMat),np.array(traingClasses))
errorCount = 0.0
for docIndex in testSet:
wordVector = bagOfWord2VecMN(vocabList,docList[docIndex])
if classifyNB(np.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
errorCount += 1
print('the error rate is:',float(errorCount)/len(testSet))
return vocabList,p0V,p1V
4.8 贝叶斯的应用
推荐:搜集一个人的相关信息,各类广告中判断他对一个广告是否感兴趣
过滤器:垃圾邮件过滤
分类:例子中的内容