写程序学ML:朴素贝叶斯算法原理及实现(二)

来源:互联网 发布:金蝶软件打不开 编辑:程序博客网 时间:2024/05/23 15:09

[题外话]近期申请了一个微信公众号:平凡程式人生。有兴趣的朋友可以关注,那里将会涉及更多更新机器学习、OpenCL+OpenCV以及图像处理方面的文章。

2、朴素贝叶斯算法的实现

2.1   朴素贝叶斯算法的实现

按照朴素贝叶斯算法的原理,我们需要实现一个朴素贝叶斯分类器。首先,需要使用文本样例对贝叶斯分类器进行训练。可以按照下面的流程进行处理:

1、  定义函数:deftrainNB0(trainMatrix, trainCategory)来实现贝叶斯分类器相关数据的训练。

2、  收入参数:trainMatrix:存储每个文档样本中各个词汇在词汇表里出现情况的数据,与样本数目相同。trainCategory: 存储每个文档样本所属类别的标签,即分类信息。当前我们分为类别1和类别0两大类。类别1在trainCategory中用1表示,类别0用0表示。

3、  获取trainMatrix中样本个数、词汇表的长度以及样本中类别1所占的比例。

4、  利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算p(w0|1)p(w1|1)p(w2|1)。如果其中一个概率值为0,那么最后的乘积也为0。为降低这种影响,将所有词的出现数初始化为1,并将分母初始化为2。

5、  对trainMatrix中的每个样本数据进行处理,如果该样本为类别1,将样本中对应词汇出现的次数累加起来,同时统计该类别词汇出现的总数;如果是类别0,也做类似的处理。

6、  对类别1和类别0中,将各个词汇出现概率计算出来,并以对数方式计算。采用对数方式计算是为了避免计算下溢。当计算乘积p(w0|ci)p(w1|ci)p(w2|ci)...p(wN|ci)时,由于大部分因子都非常小,所以程序会下溢出或者得到不正确的答案。一种解决办法是对乘积取自然对数。在代数中有ln(a*b)=ln(a)*ln(b),于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。

7、  最后返回类别1和类别0的样本中各个词汇出现的概率,及样本集中类别1的概率。

具体代码实现如下:

#trainMatrix: 存储每个文档样本在词汇表中各个词汇出现情况的集合,与样本数目相同#这是一个二维数组,第一维对应各个文档样本id,第二维对应该样本中词汇在词汇表中出现情况#trainCategory: 存储每个文档样本所属类别的标签,即分类信息#获取侮辱性和非侮辱性样本中各个词汇出现的概率,及样本集的侮辱性概率def trainNB0(trainMatrix, trainCategory):    numTrainDocs = len(trainMatrix) #获取文档样本个数    numWords = len(trainMatrix[0]) #获取词汇表的长度    pAbusive = sum(trainCategory) / float(numTrainDocs) #获取所有样本的分类概率    #利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,    #即计算p(w0|1)p(w1|1)p(w2|1)。如果其中一个概率值为0,那么最后的乘积也为0。    #为降低这种影响,将所有词的出现数初始化为1,并将分母初始化为2。    #p0Num = zeros(numWords) #创建有numWords个元素的数组,且每个元素初始化为0    #p1Num = zeros(numWords) #创建有numWords个元素的数组,且每个元素初始化为0    #p0Denom = 0.0 #存储所有样本中非侮辱性词汇出现次数总和    #p1Denom = 0.0 #存储所有样本中侮辱性词汇出现次数总和    p0Num = ones(numWords) #创建有numWords个元素的数组,且每个元素初始化为1    p1Num = ones(numWords) #创建有numWords个元素的数组,且每个元素初始化为1    p0Denom = 2.0 #存储所有样本中非侮辱性词汇出现次数总和    p1Denom = 2.0 #存储所有样本中侮辱性词汇出现次数总和        for i in range(numTrainDocs): #逐个样本进行循环处理        if trainCategory[i] == 1: #如果当前样本的分类结果为1,即abusive,则对p1xx进行调整            #p1Num和trainMatrix[i]为含相同数目元素的list,此处的+是对应位置上元素的相加            p1Num += trainMatrix[i]            #sum(trainMatrix[i])求得第i个样本中所有侮辱性词汇出现的个数            p1Denom += sum(trainMatrix[i])        else:            p0Num += trainMatrix[i]            p0Denom += sum(trainMatrix[i])    #当计算乘积p(w0|ci)p(w1|ci)p(w2|ci)...p(wN|ci)时,由于大部分因子都非常小,所以程序会下溢出或者得到不正确的答案。    #一种解决办法是对乘积取自然对数。在代数中有ln(a*b)=ln(a)*ln(b),于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。    #同时,采用自然对数进行处理不会有任何损失。                #对list变量p1Num中每个元素都除以p1Denom,求得词汇表中每个词汇在所有侮辱性样本中出现的概率    #p1Vect = p1Num / p1Denom    p1Vect = log(p1Num / p1Denom)    #对list变量p0Num中每个元素都除以p0Denom,求得词汇表中每个词汇在所有非侮辱性样本中出现的概率    #p0Vect = p0Num / p0Denom    p0Vect = log(p0Num / p0Denom)    return p0Vect, p1Vect, pAbusive

         为了产生函数trainNB0()的输入参数,还实现了如下几个关键的函数。

         函数createVocabList()

         该函数定义为:def createVocabList(dataSet)

         该函数用来创建词汇表,即文档样本集dataSet中所有不重复的单词。

         具体代码实现如下:

#dataSet:文档样本集#创建dataSet包含的所有文档中出现的不重复词的列表def createVocabList(dataSet):    vocabSet = set([]) #创建一个空集合变量    for document in dataSet:        #set(document)获得document中所有不重复的词        #vocabSet | set(document)求集合的并集,返回不重复的词集合        #在数学符号表示上,按位或操作与集合求并操作使用相同记号"|"        vocabSet = vocabSet | set(document)    return list(vocabSet) #将集合转换为列表并返回

         函数setOfWords2Vec()

         函数定义为:def setOfWords2Vec(vocabList, inputSet)

         该函数用来获取测试样本inputSet中单词在词汇表中是否出现的列表,如果出现记录为1,否则为0。

         具体代码实现如下:

#vocabList: 词汇表#inputSet: 测试样本文档#获取测试样本中单词在词汇表中是否出现的列表def setOfWords2Vec(vocabList, inputSet):    #定义一个列表变量,其长度与vocabList一样,其内容初始化为0    returnVec = [0] * len(vocabList)    for word in inputSet:        if word in vocabList: #判断测试文档中的单词是否存在于词汇表            #index() 函数用于从列表中找出某个值第一个匹配项的索引位置            #寻找当前单词在词汇表vocabList中的位置,在列表returnVec对应位置写1,表示该单词存在于词汇表            returnVec[vocabList.index(word)] = 1        else:            print "the word: %s is not in my Vocabulary!" % word    return returnVec

函数bagOfWords2Vec()

函数定义为:def bagOfWords2Vec(vocabList, inputSet)

该函数用来获取测试样本inputSet中单词在词汇表中出现次数的列表。如果出现记录出现的次数,否则为0。

         具体代码实现如下:

#vocabList: 词汇表#inputSet: 测试样本文档#获取表示测试样本中单词在词汇表中出现次数的列表def bagOfWords2Vec(vocabList, inputSet):    #定义一个列表变量,其长度与vocabList一样,其内容初始化为0    returnVec = [0] * len(vocabList)    for word in inputSet:        if word in vocabList: #判断测试文档中的单词是否存在于词汇表            #index() 函数用于从列表中找出某个值第一个匹配项的索引位置            #寻找当前单词在词汇表vocabList中的位置,在列表returnVec对应位置累加1,记录该单词在词汇表中出现的次数            returnVec[vocabList.index(word)] += 1        else:            print "the word: %s is not in my Vocabulary!" % word    return returnVec

函数classifyNB()

函数定义为:def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1)

         该函数用来判断输入样本vec2Classify是否为类别1。

         具体代码实现如下:

#vec2Classify:测试样本中单词在词汇表中出现情况信息#p0Vec:非侮辱性词汇出现的概率#p1Vec: 侮辱性词汇出现的概率#pClass1: 训练样本中侮辱性样本的概率#判断输入样本信息是否为侮辱性样本def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):    #sum(vec2Classify * p1Vec): 求两个向量对应元素乘积之和,计算测试样本中侮辱性词汇的概率    p1 = sum(vec2Classify * p1Vec) + log(pClass1)    p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)    if p1 > p0: #比较侮辱性概率和非侮辱性概率,p1大,则为侮辱性样本;反之,为非侮辱性样本        return 1    else:        return 0

函数textParse ()

函数定义为:deftextParse(bigString)

         该函数用来解析英文句子bigString,将其中长度大于2单词抽取出来,并将其全部转换为小写。

         具体代码实现如下:

#bigString:英文句子#解析英文句子,将其中长度大于2单词抽取出来,并将其全部转换为小写def textParse(bigString):    #分隔bigString中的单词,分隔符为除单词、数字外的任意字符串    listOfTokens = re.split(r'\W*', bigString)    #选出长度大于2的单词变为小写    return [tok.lower() for tok in listOfTokens if len(tok) > 2]
(未完待续)








阅读全文
0 0