这是一个基于概率的分类器,朴素是指具有简单的假设:一是哪个分类的概率大,就是哪一类,二是独立性假设详情见下。
贝叶斯公式:p(c|x)=p(x|c)p(c)/p(x);在学习概率论的时候,这个公式是具有逻辑的公式。
比如,我们对一句评论分为两类,一类是褒义,一类是贬义。
一、文本处理,将句子分词,大小写统一化
二、建立词集(优化后为词袋模型),计算词向量
三、
分子:每个词在不同类别下出现的概率和不同类别出现的概率(其中x为词向量,所以这里假设各个词是独立的)
分母:每个词出现的概率
通过贝叶斯公式可以得到不同类别在不同词向量出现的概率,取这个两个类别对应大的的那个类别
朴素贝叶斯在基于公式的基础上十分简单,但是重点对细节的处理,十分重要,比如对下溢出浮点数舍入的处理,文本处理时对字符长度的把我以及高频词去除、停用词表、词集转换为词袋模型对分类都十分有效。可以在不同的优化方式上通过迭代计算平均错误率观察效果。
先把书上的基础代码放上来:
1 from numpy import * 2 def loadDataSet(): 3 postingList=[['my','dog','has','flea','problems','help','please'], 4 ['maybe','not','take','him','to','dog','park','stupid'], 5 ['my','dalmation','is','so','cute','I','love','him'], 6 ['stop','posting','stupid','worthless','garbage'], 7 ['mr','licks','ate','my','steak','how','to','stop','him'], 8 ['quit','buying','worthless','dog','food','stupid']] 9 classVec=[0,1,0,1,0,1] 10 return postingList,classVec 11 # 得到词集 12 def createVocabList(dataset): 13 vocabSet=set([]) 14 for document in dataset: 15 vocabSet=vocabSet|set(document) 16 return list(vocabSet) 17 #vacabList为词集,inputSet为某句话的分词,返回的是这句话的词向量 18 def setofWords2Vec(vocabList,inputSet): 19 returnVec=[0]*len(vocabList) 20 for word in inputSet: 21 if word in vocabList: 22 returnVec[vocabList.index(word)]=1 23 else:print ("the world:"+word+" is not in my Vocabulary!") 24 return returnVec 25 #计算训练矩阵和训练类别返回的三个概率 26 def trainNB0(trainMatrix,trainCategory): 27 numTrainDocs=len(trainMatrix) 28 numWords=len(trainMatrix[0]) 29 pAbusive=sum(trainCategory)/float(numTrainDocs)#侮辱性文档概率 30 #p0Num=zeros(numWords);p1Num=zeros(numWords) 31 #为了避免独立性中的某一个是0整体就为0的情况 32 p0Num = ones(numWords);p1Num = ones(numWords) 33 p0Denom=2.0;p1Denom=2.0#为什么是2? 34 for i in range(numTrainDocs): 35 if trainCategory[i]==1: 36 p1Num+=trainMatrix[i] 37 p1Denom+=sum(trainMatrix[i]) 38 else: 39 p0Num += trainMatrix[i] 40 p0Denom += sum(trainMatrix[i]) 41 #这里可不可以最后再计算p1Denom和p0Denom 42 #p1Vect=p1Num/p1Denom;p0Vect=p0Num/p0Denom 43 #为了避免下溢出和浮点数舍入,change to log 44 p1Vect = log(p1Num / p1Denom) 45 p0Vect = log(p0Num / p0Denom) 46 return p0Vect,p1Vect,pAbusive 47 #vec2Classify是需要分类的词向量 48 def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1): 49 #使用sum是因为对数的乘法变加法,vec2Classify中如果有这个词就加(乘)上这个概率 50 p1=sum(vec2Classify*p1Vec)+log(pClass1) 51 p0 = sum(vec2Classify * p0Vec) + log(1.0-pClass1) 52 if p1>p0: 53 return 1 54 else: 55 return 0 56 def testingNB(): 57 listOPosts,listClasses=loadDataSet() 58 myVocabList=createVocabList(listOPosts) 59 trainMat=[] 60 for postinDoc in listOPosts: 61 trainMat.append(setofWords2Vec(myVocabList,postinDoc)) 62 p0V,p1V,pAb=trainNB0(array(trainMat),array(listClasses)) 63 testEntry=['love','my','dalmation'] 64 thisDoc=array(setofWords2Vec(myVocabList,testEntry)) 65 print(testEntry,"classified as",classifyNB(thisDoc,p0V,p1V,pAb)) 66 testEntry=['stupid','garbage'] 67 thisDoc=array(setofWords2Vec(myVocabList,testEntry)) 68 print(testEntry, "classified as", classifyNB(thisDoc, p0V, p1V, pAb))
书上的例子是基于一个狗狗评论网站的。
在敲完之后,对代码有如下理解:
首先是贝叶斯公式的使用,因为它是两个概率的比较,相同的分母所以分母不用求不参与计算。
其次,是注红的地方,这里使用求和是因为log的求积变求和,需要计算的是这个词向量在这个类别下出现的概率,已经通过训练集得出了词典中的每个词在这个类别下出现的概率,计算的是p(xi|c)每个词在这个类别下出现的概率之和,出现了这个词就把概率加上,没有出现就不加再乘(加)不同类别出现的概率就可以了。
因为刚参加完2020美赛C所以自己写了一个处理评论的分类器:
from numpy import *
#分词:文本->单词列表
def fenci(text):
import re
return [word.lower() for word in re.split(r'W*', text) if len(word) > 2]
#分成以句为单位的列表,每句以词隔开
def fenju(bigString):
import re
#处理数据,将bigString变为以句为单位的列表
sentencelist=bigString.split('
')
#处理每句话,将每句话分词加格式化
wordfromsentence=[]
for sentence in sentencelist:
wordfromsentence.append(fenci(sentence))
return wordfromsentence
#创建词集
def createWordofSet(bigString):
import re
sentencelist=fenci(bigString)
wordfromsentence=set([])
for sentence in sentencelist:
wordfromsentence.add(sentence)
return wordfromsentence
#得到一句话的词向量
def getVec(myVacb,inputword):
vec=[0]*len(myVacb)
for word in inputword:
if word in myVacb:
vec[inputword.index(word)]=1
else:print("the world:" + word + " is not in my Vocabulary!")
return vec
#从星级得到褒贬1为好0为坏
def getGradeFromstar(bigString):
starlist = bigString.split('
')
gradlist=[]
for star in starlist:
if len(star)>0:
if int(star)>=4:
gradlist.append(1)
else:gradlist.append(0)
else:gradlist.append(0)
return gradlist
#计算三个概率,传入训练集词向量和训练的目标变量
#每个词向量/不同类别 不同类别 不同词向量
def get3p(trainMatrix,trainClasses):
#不同类别:每个类别个数/总个数
sunnum=len(trainClasses)
pgood=sum(trainClasses)/float(sunnum)
lenofwordset=len(trainMatrix[0])
p0Num = ones(lenofwordset);
p1Num = ones(lenofwordset);
p0Denom = 2.0;
p1Denom = 2.0; # 为什么是2?
for i in range(sunnum):
if trainClasses[i] == 1:
p1Num += trainMatrix[i]
if trainClasses[i] == 0:
p0Num += trainMatrix[i]
p1 = log(p1Num / (p1Denom + sum(p1Num)))
p0 = log(p0Num / (p0Denom + sum(p0Num)))
return p0,p1,pgood
#目标:计算某个类别在某个词向量下的概率然后作比较,因为分母是相同的所以没有同时除以
#所以只需要求得每个词在1类别的情况下求得的概率求积(通过log转化为求和)可得这个词向量在1类别情况下的概率,通过贝叶斯公式再乘1类别的概率就可以得到分子
#训练集的作用是得到所有词再各个类别中的概率,然后传入所求得词向量*各个词所出现得概率就是这个词向量在这个类别下得概率
#inputVec是传入的词向量
def getp(inputVec,p0,p1,pgoog):
pp1=sum(inputVec*p1)+log(pgoog)
pp0= sum(inputVec * p0) + log(1-pgoog)
if pp1>pp0:
return 1
else:return 0
def test():
wordofSet=createWordofSet(open('D:/b.txt',encoding='utf-8').read())
trainMatrix=[]
for sentence in fenju(open('D:/b.txt',encoding='utf-8').read()):
trainMatrix.append(getVec(wordofSet,sentence))
trainClasses=getGradeFromstar(open('D:/a.txt').read())
p0,p1,pgood=get3p(array(trainMatrix),array(trainClasses))
for sentence in fenju(open('D:/c.txt').read()):
print(str(sentence)+":"+str(getp(array(getVec(wordofSet,sentence)),p0,p1,pgood)))
test()
这是第一个问题:
写完之后我选择了250条用作训练集,50条用作测试集错误率为0.2,但是很奇诡的是我的所有分类得到的都是第一类。我发现pgood>>1-pgood所以导致第一类的可能性比较大,但是这不应该影响到公式啊,因为都是概率所以也不需要再归一化。但是这个概率过大的影响到了分类,而且错误率为0.2是因为所有的成分中错误的确实大概占0.2,而它把所有的都分为了第1类,自然就要0.2的错误产生。
第二个问题:
如果出现了不在这个词集中的单词这里的处理是不计算它所产生的向量,能不能做其他处理。
第三个问题:
为了保证分母不是0,在处理是为什么另它为2,这会不会影响之后的概率
A:也可是其他数字,这个对最后结果有影响但不是最大,我在最新一版代码中把它去掉了
其实还是很悲伤的因为它并没有起到分类的作用,所有的分类都是第一类。这个代码可能有问题,将来慢慢改。