1. 基本概念
根据先验概率和似然函数来求后验概率。一般用于分类任务。
先验概率:
似然函数:
后验概率:
根据条件独立性假设:
目标函数:即求解使后验概率最大的类。
训练过程:即求各个单词的条件概率,和类别的先验概率。
测试过程:根据已经得到的条件概率和先验概率,计算不同类别的后验概率,取最大的类。
2. 优缺点
优点:简单,易于实现。
缺点:由于条件独立性假设,使得分类性能不是很好。并且对输入数据格式有限制。
3. 应用题(影评分类)
样本 | 标签 |
Good movie. | 1 |
I like it. | 1 |
So amazing. | 1 |
Great movie. | 1 |
Bad movie. | 0 |
I hate it. | 0 |
So boring. | 0 |
Boring movie. | 0 |
求影评“I hate bad movie.”的类别(1代表好的影评,0代表坏的。)
令 w = “I hate bad movie.”
,
因此:
因此:
, 则为“坏评价”。
总结:
1. 朴素贝叶斯常用模型:多项式模型,伯努利模型,高斯模型
多项式模型:基於单词出现次数
先验概率:P(c) = 类c下所有文档的单词总数 / 所有文档单词总数
条件概率:P(wi|c) = (类c下单词wi出现的次数 + 1)/ (类c下所有文档的单词总数 + 类别数)
伯努利模型:基於单词是否出现(二项特征)
先验概率:P(c) = 类c下文档数 / 总文档数
条件概率指示函数:P(i|c) = 类c下单词wi出现过的文档数 / 类c下所有文档数
条件概率: P(wi|c) = P(i|c)wi + (1-P(i|c))(1-wi)
因此多项式模型通过smoothing同等对待未出现单词,而伯努利模型则显示的对待(反向作用)。
混合模型:既考虑词频但是又限制词语出现次数的模型为,其通过类别的分类不均来计算一个权重。
文本分类时一般可选择多项式模型和伯努利模型,当句子较短时,伯努利的效果可能会更好。
高斯模型:特征值符合高斯分布的连续型变量,比如说人的身高,物体的长度。
2. 对于未出现词,比如‘hate’ 在正例里未出现,为了防止其条件概率为0,可使用smoothing方法,比如add-1(laplacian smoothing)。
3. 上述条件概率乘积太小,为了防止向下溢出,可以使用自然对数log,将乘法转化为加法。
4. 上述例子仅为一个参考。当训练集十分庞大时(比如烂番茄评论数据库),则我们可以剔除“停用词”,例如:the, a, I ......,只选择形容词,如:good,bad......
5. 词袋模型,仅考虑单词出现次数,不考虑前后顺序。对应上述的多项式模型。词集模型:仅考虑单词是否出现,若出现记为1,不计数。词袋模型要优于词集模型。
4. 编程实现(垃圾邮件分类)
参考:《机器学习实战》
源码地址以及数据:https://github.com/JieruZhang/MachineLearninginAction_src
手写python朴素贝叶斯:
import re
import random
from numpy import *
#tokenization,分词
def textParse(s):
tokens = re.split(r'\W*', s)
#转化成小写,且只取长度大于2的单词
return [tok.lower() for tok in tokens if len(tok) > 2]
#通过set来创建无重复单词的字典
def createVocab(fullText):
return list(set(fullText))
#将一个单词list转化为向量表示, 某单词存在,则把vocabs中对应位置赋为1(此处应用的是伯努利模型,即不考虑次数,只考虑是否出现)
def words2Vec(vocabs, words):
vec = [0 for _ in range(len(vocabs))]
for word in words:
if word in vocabs:
vec[vocabs.index(word)] = 1
return vec
#训练过程
def trainNB(trainMat, trainClasses):
numDocs = len(trainMat)
numWords = len(trainMat[0])
#垃圾邮件的概率
pSpam = sum(trainClasses)/float(numDocs)
#分子p0是类别为0的概率
p0num = zeros(numWords)
p1num = zeros(numWords)
#分母
p0denom = 0.0
p1denom = 0.0
for i in range(numDocs):
#计算1类的分子分母,0类的分子分母
if trainClasses[i] == 1:
p1num += trainMat[i]
p1denom += sum(trainMat[i])
else:
p0num += trainMat[i]
p0denom += sum(trainMat[i])
#计算概率,p1,p0,这里两者均为向量表示,每个位置时该位置对应的单词的概率p1[i] = p(wi|c=1), p0[i] = p(wi|c=0)
#取自然对数是为了转化乘法为加法,防止向下溢出
p1 = log(p1num/p1denom)
p0 = log(p0num/p0denom)
return p0, p1, pSpam
#分类过程,传入的概率p0和p1都是取了自然对数的,pSpam没有取
def classifyNB(wordvec,p0vec,p1vec,pSpam):
p1 = sum(wordvec*p1vec) + log(pSpam)
p0 = sum(wordvec*p0vec) + log(1.0-pSpam)
#返回概率大的类别
if p1 > p0:
return 1
else:
return 0
def spamTest():
docs = []
classes = []
fullText = []
#总共有25个正例,25个反例
for i in range(1,26):
#每封邮件作为一个大字符串,使用textParse分词放入list
#docs存放每一封分词过后的邮件,fullText存放所有的单词,classes存放类别(spam中是正类)
words = textParse(open('email/spam/%d.txt'%i).read())
docs.append(words)
fullText.extend(words)
classes.append(1)
#同理求负例
words = textParse(open('email/ham/%d.txt'%i,encoding='gbk').read())
docs.append(words)
fullText.extend(words)
classes.append(0)
#构建字典
vocabs = createVocab(fullText)
#总共50封邮件,随机选择10封作为测试集,剩余40封为训练集,trainIndex和testIndex存的是选取的邮件的index
trainIndex = [i for i in range(50)]
testIndex = []
for i in range(10):
randIndex = int(random.uniform(0,len(trainIndex)))
testIndex.append(trainIndex[randIndex])
del trainIndex[randIndex]
#对于训练集,将每封邮件的单词列表转化成向量表示, 并存入相应的list
trainMat = []
trainClasses = []
for index in trainIndex:
trainMat.append(words2Vec(vocabs, docs[index]))
trainClasses.append(classes[index])
#训练模型,得到条件概率向量,以及先验概率pSpam
p0, p1, pSpam = trainNB(array(trainMat),array(trainClasses))
#在测试集上测试
errorCount = 0
for index in testIndex:
wordvec = words2Vec(vocabs, docs[index])
if classifyNB(array(wordvec), p0, p1, pSpam) != classes[index]:
errorCount += 1
print('error rate is:', float(errorCount)/len(testIndex))
spamTest()
使用sklearn包实现:
#使用sklearn工具包进行分类
from sklearn.naive_bayes import MultinomialNB
def spamNBsklearn():
#数据准备过程同上
docs = []
classes = []
fullText = []
for i in range(1,26):
words = textParse(open('email/spam/%d.txt'%i).read())
docs.append(words)
fullText.extend(words)
classes.append(1)
#同理求负例
words = textParse(open('email/ham/%d.txt'%i,encoding='gbk').read())
docs.append(words)
fullText.extend(words)
classes.append(0)
#构建字典
vocabs = createVocab(fullText)
#总共50封邮件,随机选择10封作为测试集,剩余40封为训练集,trainIndex和testIndex存的是选取的邮件的index
trainIndex = [i for i in range(50)]
testIndex = []
for i in range(10):
randIndex = int(random.uniform(0,len(trainIndex)))
testIndex.append(trainIndex[randIndex])
del trainIndex[randIndex]
#对于训练集,将每封邮件的单词列表转化成向量表示, 并存入相应的list
trainMat = []
trainClasses = []
for index in trainIndex:
trainMat.append(words2Vec(vocabs, docs[index]))
trainClasses.append(classes[index])
#对于测试集,将每封邮件的单词列表转化成向量表示, 并存入相应的list
testMat = []
testClasses = []
for index in testIndex:
testMat.append(words2Vec(vocabs, docs[index]))
testClasses.append(classes[index])
#使用sklearn包训练(使用多项式模型)
clf = MultinomialNB()
clf.fit(trainMat, trainClasses)
#test, clf.score 输出对测试样本的预测准确率平均值
score = clf.score(testMat, testClasses)
print('error rate is:', 1-score)
spamNBsklearn()