贝叶斯分类器
判别模型和生成模型
判别模型,也就是根据特征值来求结果的概率。形式化表示为 p(y|x;\theta) p(y|x;θ) ,在参数\theta θ 确定的情况下,求解条件概率 p(y|x) p(y|x) 。通俗的解释为在给定特征后预测结果出现的概率。
比如说要确定一只羊是山羊还是绵羊,用判别模型的方法是先从历史数据中学习到模型,然后通过提取这只羊的特征来预测出这只羊是山羊或绵羊的概率。
换一种思路,我们可以先建立绵羊和山羊特征模型,然后对一只需要判断类型的羊,我们提取相应的特征,放到山羊概率是多少,再放到绵羊模型中看看概率,哪个大就是哪个。形式化表示为 p(x|y) p(x|y) ,y是类型,x是特征。
常见的判别模型有:线性回归、对数回归、线性判别分析、支持向量机、boosting、条件随机场、神经网络等。
常见的生产模型有隐马尔科夫模型、朴素贝叶斯模型、高斯混合模型、LDA、Restricted Boltzmann Machine 等。
贝叶斯分类器
实际中,p(x|y)
p(x|y)
并不便于计算,所以我们利用贝叶斯公式将其转换为方便求的概率。贝叶斯公式如下:
p(x|y) 称为后验概率, p(y) 为先验概率。
由于我们关心的是哪一个
p(x=ci|y)
比较大(比如说绵羊和山羊的哪个的概率大),不关心起具体的值,并且朴素贝叶斯分类器一般见得个特征相互独立,所以上式改写为:
贝叶斯分类器例子
一个医院早上收了六个门诊病人,如下表。
症状 | 职业 | 疾病 |
---|---|---|
打喷嚏 | 护士 | 感冒 |
打喷嚏 | 农夫 | 过敏 |
头痛 | 建筑工人 | 脑震荡 |
头痛 | 建筑工人 | 感冒 |
打喷嚏 | 教师 | 感冒 |
头痛 | 教师 | 脑震荡 |
现在又来了第七个病人,是一个打喷嚏的建筑工人。请问他患上感冒的概率有多大?
求的是 P(感冒|打喷嚏∗建筑工人) ,根据贝叶斯公式:
其中 P(打喷嚏|感冒)=23,P(建筑工人|感冒)=13,P(感冒)=12,P(打喷嚏)=12,P(建筑工人)=13
贝叶斯进行文本分类
朴素贝叶斯的一个最著名的应用——电子邮件垃圾过滤:
给出已经人工分好类的文档,分别放在ham文件夹(有用邮件)和spam文件夹(垃圾邮件),每个文件夹有25封邮件。建立分类器来识别垃圾邮件。其中过程如下:
- 遍历所有训练数据,将文本解析成词条向量。
- 建立词条向量表,再次遍历每一条训练数据(这里是每一封邮件),对于每一条数据,在词条向量表中标记出现的单词。遍历时记录每种类别数据的数目,以及在类别C情况下词条次数。
- 计算每个类别下词条出现的概率即p(x|y)和每种类别出现的概率p(y)(此步需进行拉普拉斯平滑处理)
- 返回
- 词条向量是一个包含训练数据中出现的每一个单词的集合。
- 拉普拉斯平滑:对于某些不出现的词条,其概率为0,最后计算可能导致连乘积为0,所以,对所有词条数目进行加1,相应的,计算概率时分母加上类别总数。
- 防止数值过小产生下溢,对概率取对数后计算
python代码如下
import numpy as np;
返回每种类别的概率和每个类别下单词出现的概率
def trainNB0(trainSet,trainClass):
numTrainData = len(trainSet);
numWords = len(trainSet[0]);
pAb = sum(trainClass)/float(numTrainData);
p0Num = np.ones(numWords);
p1Num = np.ones(numWords);
p0Denom =2.0;
p1Denom =2.0;
for i in range(numTrainData):
if(trainClass[i] == 1):
p1Num += trainSet[i];
p1Denom += sum(trainSet[i]);
else:
p0Num += trainSet[i];
p0Denom += sum(trainSet[i]);
p1Vect = np.log(p1Num/p1Denom);
p0Vect = np.log(p0Num/p0Denom);
return p0Vect,p1Vect,pAb;
def classifyNB(v,p0Vec,p1Vec,pClass1):
p0 = sum(v*p0Vec)+np.log(pClass1);
p1 = sum(v*p1Vec)+np.log(1-pClass1);
if (p1>p0):
return 1;
return 0;
#将词条转化为向量
def setOfWord2VecMN(vocList,inputSet):
returnVec=[0]*len(vocList);
for word in inputSet:
if word in vocList:
returnVec[vocList.index(word)] = +1;
else:
print("the word %s is not in my vocSet" %word);
return returnVec;
#对词条预处理,丢弃标点符号和长度小于2的词条并将词条全部转换为小写
def textParse(bigString):
import re;
listofTokens = re.split(r'\W*',bigString);
return [tok.lower() for tok in listofTokens if len(tok) > 2];
def spanTest():
docList=[];
classList=[]; #类别标签
fullText=[]; #词条向量
#读入数据,解析成词条向量
for i in range (1,26):
wordList = textParse(open('./spam/%d.txt' %i).read());
docList.append(wordList);
fullText.extend(wordList);
classList.append(1);
wordList = textParse(open('./ham/%d.txt' %i).read());
docList.append(wordList);
fullText.extend(wordList);
classList.append(0);
voaList = createVocList(docList); #将词条向量去重,转化为集合,
#交叉验证
trainingSet = [i for i in range(50)];
testSet=[];
for i in range(10):
randIndex = int(np.random.uniform(0,len(trainingSet)));
testSet.append(trainingSet[randIndex]);
del(trainingSet[randIndex]);
trainMat=[];
trainClasses=[];
for docIndex in trainingSet:
trainMat.append(setOfWord2VecMN(voaList,docList[docIndex]));
trainClasses.append(classList[docIndex]);
p0v,p1v,pSpam = trainNB0(np.array(trainMat),np.array(trainClasses));
error = 0;
for docIndex in testSet :
voc = setOfWord2VecMN(voaList,docList[docIndex]);
tlabel = classifyNB(voc,p0v,p1v,pSpam);
if(tlabel != classList[docIndex]):
error+=1;
print("error rate = ",float(error)/len(testSet));
spanTest();