机器学习实战——笔记(朴素贝叶斯)

朴素贝叶斯(naive bayes)

目录
  • 朴素贝叶斯算法原理
  • 文本分类练习
一、朴素贝叶斯——算法原理

优点:在数据较少的情况下仍然有效,可以处理多分类问题
缺点:对于输入数据的准备方式较为敏感
适用数据类型:标称型数据

首先,我们来讲一下贝叶斯决策理论核心思想

场景:假设有一个数据集,它由两类数据组成,数据分类如下:
在这里插入图片描述

p1(x,y)表示数据点(x,y)属于红色一类的概率

p2(x,y)表示数据点(x,y)属于蓝色一类的概率

那么对于一个新数据点(x,y),可以用下面规则来判断其类别:

如果p1(x,y) > p2(x,y),则(x,y)为红色一类。
如果p1(x,y) < p2(x,y), 则(x,y)为蓝色一类。

所以,其核心思想就是选择具有最高概率的决策。

抛出问题,那么p1和p2怎么计算呀?

那就得说说条件概率啦~

  • 条件概率(很懵的一块)

条件概率公式:
P ( B A ) = P ( A B ) P ( A ) P(B | A) = \frac{P(AB)}{P(A)}
定义:设事件A和事件B,且 P ( A ) P(A) 大于0,在已知事件A发生的条件下,事件B发生的概率,叫做条件概率,记 P ( B A ) P(B | A)

小学生数学题:
罐子里有7块石头,3个灰色,4个黑色,你抓一块是黑色的概率是多少?抓一块是灰色的概率呢?

答: 4 7 \frac{4}{7} 3 7 \frac{3}{7}

往下看:
在这里插入图片描述
于是有:
P(灰色 | B桶)= P(灰色且B桶)/ P(B桶)

1 7 \frac{1}{7} / 3 7 \frac{3}{7} = 1 3 \frac{1}{3}

如果已知 P(c | x),求 P(x | c)?
在这里插入图片描述
这个公式就不要死记硬背了,推一下就出来了~

P ( c x ) = P ( c x ) P ( x ) P(c | x) = \frac{P(cx)}{P(x)}

P ( x c ) = P ( c x ) P ( c ) P(x | c) = \frac{P(cx)}{P(c)}

P ( c x ) P ( x ) P ( x ) P ( c ) \frac{P(cx)}{P(x)} * \frac{P(x)}{P(c)} = P ( c x ) P ( x ) P ( c ) P(c | x) * \frac{P(x)}{P(c)} = P ( x c ) P(x | c)


二、文本分类练习

场景:
在线社区有很多留言,留言中包括:侮辱性与非侮辱性两种,我们需要过滤侮辱性留言。

实验思路:

  • 首先,将文本转化为数字向量
  • 其次,基于这些向量来计算条件概率,并构建分类器
  • 最后,介绍一些利用python实现朴素贝叶斯过程需要考虑的问题

1、从文本中构建词向量

 #创建实验样本
def loadDataSet():
    postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
                 ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
                 ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
                 ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
                 ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
                 ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
    classVec = [0, 1, 0, 1, 0, 1]    # 1表示侮辱类语言, 0表示非侮辱类语言
    return postingList, classVec


#创建一个列表,包含文档中出现的所有词,且不重复
#(不会出现重复的单词)
def createVocabList(dataSet):
    vocabSet = set([])          # 创建一个空集合
    for document in dataSet:
        vocabSet = vocabSet | set(document)     # 创建两个集合的并集
        # print(vocabSet)
    return list(vocabSet)

#inputSet在vocabList中是否出现
#出现为1,反之为0
def setOfWords2Vec(vocabList, inputSet):
    returnVec = [0]*len(vocabList)      # 和词汇表等长的0向量
    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


listOPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
print(myVocabList)
print(listOPosts[0])
myVec = setOfWords2Vec(myVocabList, listOPosts[0])
print(myVec)

>>>
['I', 'dog', 'cute', 'help', 'garbage', 'please', 'stop', 'dalmation', 'worthless', 'take', 'maybe', 'mr', 'problems', 'stupid', 'love', 'licks', 'not', 'food', 'my', 'is', 'posting', 'ate', 'flea', 'steak', 'how', 'quit', 'so', 'him', 'park', 'has', 'to', 'buying']
['my', 'dog', 'has', 'flea', 'problems', 'help', 'please']
[0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0]

2、从词向量计算概率

def trainNB0(trainMatrix, trainCategory):
  numTrainDocs = len(trainMatrix)     # 文档数目,这里是6篇
  numWords = len(trainMatrix[0])      # 唯一词表的长度
  print(sum(trainCategory))           # 求和,[0, 1, 0, 1, 0, 1],和是3
  pAbusive = sum(trainCategory)/float(numTrainDocs)   # 6篇里有3篇是侮辱性的,概率为0.5
  p0Num = zeros(numWords)              # 全为0的矩阵
  p1Num = zeros(numWords)              # 全为0的矩阵
  p0Denom = 0.0
  p1Denom = 0.0                       # 初始化工作
  for i in range(numTrainDocs):       # 对每一篇
      if trainCategory[i] == 1:       # 该篇如果是侮辱性的
          p1Num += trainMatrix[i]     # 因为是二分类所以与0矩阵求和
          p1Denom += sum(trainMatrix[i])  # 矩阵内各元素求和
      else:                           # 非侮辱性
          p0Num += trainMatrix[i]     # 因为是二分类所以与0矩阵求和
          p0Denom += sum(trainMatrix[i])  # 矩阵内各元素求和
  p1Vect = p1Num/p1Denom      # 对矩阵各元素做除法
  p0Vect = p0Num/p0Denom
  return p0Vect, p1Vect, pAbusive


listOPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)

trainMat = []
for postinDoc in listOPosts:
  trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0V, p1V, pAb = trainNB0(trainMat, listClasses)
print(p0V)		# 给定文档类别条件下(非侮辱),词汇表中单词出现概率
print(p1V)		# 给定文档类别条件下(侮辱),词汇表中单词出现概率
print(pAb)		# 文档属于侮辱类的概率

要计算多个概率的乘积以获得文档属于某个类别的概率,即计算 p(w0|1) * p(w1|1) * p(w2|1)

如果其中一个概率值为 0,那么最后的乘积也为 0。为降低这种影响,可以将所有词的出现数初始化为 1,并将分母初始化为 2 (取1 或 2 的目的主要是为了保证分子和分母不为0,大家可以根据业务需求进行更改)

另一个遇到的问题是下溢出,这是由于太多很小的数相乘造成的。当计算乘积 p(w0|ci) * p(w1|ci) * p(w2|ci)… p(wn|ci) 时,由于大部分因子都非常小,所以程序会下溢出或者得到不正确的答案。(用 Python 尝试相乘许多很小的数,最后四舍五入后会得到 0)。

一种解决办法是对乘积取自然对数。在代数中有 ln(a * b) = ln(a) + ln(b), 于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。

代码修改:

    p0Num = zeros(numWords)              # 全为0的矩阵
    p1Num = zeros(numWords)              # 全为0的矩阵
    p0Denom = 0.0
    p1Denom = 0.0 
    p1Vect = p1Num/p1Denom      # 对矩阵各元素做除法
    p0Vect = p0Num/p0Denom
    
    #改为:
    p0Num = ones(numWords)              # 全为1的矩阵
    p1Num = ones(numWords)              # 全为1的矩阵
    p0Denom = 2.0
    p1Denom = 2.0 
    p1Vect = log(p1Num/p1Denom)      # 对矩阵各元素做除法
    p0Vect = log(p0Num/p0Denom)

构造分类器:

def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    print(sum(vec2Classify * p1Vec))
    p1 = sum(vec2Classify * p1Vec) + log(pClass1)    
    # log()是因为我们之前得出的概率取了对数,所以这里变成了相加
    # 由于是取对数,所以乘积变为直接求和即可,注意这里list和list是无法相乘,vec2Classify需要为array格式
    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(setOfWords2Vec(myVocabList, postinDoc))
    p0V, p1V, pAb = trainNB0(array(trainMat), array(listClasses))
    testEntry = ['love', 'my', 'dalmation']
    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
    print(thisDoc)
    print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))
    testEntry = ['stupid', 'garbage']
    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
    print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb))

在这里插入图片描述

这个blog写的也不错:
https://blog.csdn.net/c369624808/article/details/78906630

词袋模型:
之前,我们只记录单词有没有出现,但是出现的次数并没考虑进去
而词袋模型则是每遇到一个单词时,它会增加词向量中的对应值,而不是将对应的数值设为1。

def bagOfWords2VecMN(vocabList, inputSet):
    returnVec = [0]*len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
    return returnVec

猜你喜欢

转载自blog.csdn.net/m0_37970224/article/details/86362308