机器学习实战(笔记):第 4 章 基于概率论的分类方法:朴素贝叶斯

第 4 章 基于概率论的分类方法:朴素贝叶斯

[TOC]

本章内容:

  • 使用概率分布进行分类
  • 学习朴素贝叶斯分类器
  • 解析 RSS 源数据
  • 使用朴素贝叶斯来分析不同地区的态度

1. 基于贝叶斯决策理论的分类方法

朴素贝叶斯:

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

朴素贝叶斯是贝叶斯决策理论的一部分,所以有必要快速了解一下贝叶斯决策理论。

假设现在又一个数据集,它由两类数据组成,数据分布如图4-1:

图4-1

假设找到了描述图中两类数据的统计参数,现在用 p1(x,y) 表示数据点 (x,y) 属于类别 1 (图中用圆点表示的类别)的概率,用 p2(x,y) 表示数据点 (x,y) 属于类别 2 (图中用三角形表示的类别)的概率,那么对于一个新数据点 (x,y) ,可以用下面的规则来判断它的类别:

  • 如果 p1(x,y) > p2(x,y) ,那么类别为 1
  • 如果 p2(x,y) > p1(x,y) ,那么类别为 2

也就是说,我们会选择高概率对应的类别,这就是贝叶斯决策理论的核心思想,即选择具有最高概率的决策。

类似上图应该选择哪种方法来进行分类?

  • 使用决策树不会非常成功
  • 使用 kNN 的计算量太大
  • 最好是使用贝叶斯

2. 条件概率

假设现在有一个装了 7 块石头的罐子,其中 3 块是灰色的,4 块是黑色的。如果从罐子中随机取出一块石头,那么是灰色石头的概率为 3/7,是黑色石头的概率为 4/7,。使用 P(gray) 来表示取到灰色石头的概率,其概率值可以通过灰色石头数目除以总的石头数目来得到。

图4-3

要计算 P(gray) 或者 P(black),事先知道石头所在桶的信息会不会改变结果?计算从 B 桶中取到灰色石头的概率的办法,这就是所谓的 条件概率(conditional probability)。假定计算的是从 B 桶取到灰色石头的概率,这个概率可以记作 P(gray|bucketB),我们称之为“在已知石头出自 B 桶取到灰色石头的概率。不难得到,P(gray|bucketA) = 2/4 ,P(gray|bucketB) = 1/3.

条件概率的计算公式如下:

​ P(gray|bucketB) = P(gray and bucketB) / P(bucketB)

另一种有效计算条件概率的方法称为 贝叶斯准则。贝叶斯准则告诉我们如何交换条件概率中的条件与结果,即如果已知 P(x|c),要求 P(c|x),那么可以使用下面的计算方法:

​ P(c|x) = P(x|c)P(c) / P(x)

3. 使用条件概率来分类

第 1 节提到贝叶斯决策理论要求计算两个概率 p1(x,y) 和 p2(x,y):

  • 如果 p1(x,y) > p2(x,y) ,那么类别为 1
  • 如果 p2(x,y) > p1(x,y) ,那么类别为 2

但这两个准则并不是贝叶斯决策理论的所有内容。真正需要计算和比较的是 p(c1|x,y) 和 p(c2|x,y),即:给定某个由 x,y 表示的数据点,那么该数据点来自类别 c1 的概率是多少?数据点来自 c2 的概率又是多少?

具体地,应用贝叶斯准则得到:

p(c_i|x,y)=\frac{p(x,y|c_i)p(c_i)}{p(x,y)}

使用这些定义,可以定义贝叶斯分类准则为:

  • 如果 p(c1|x,y) > p(c2|x,y),那么属于类别 c1
  • 如果 p(c1|x,y) < p(c2|x,y),那么属于类别 c2

使用贝叶斯准则,可以通过已知的三个概率值来计算未知的概率值。

4. 使用朴素贝叶斯进行文档分类

机器学习的一个重要应用就是文档的自动分类。可以观察文档中出现的此,并把每个此的出现或者不出现作为一个特征,这样得到的特征数目就会跟词汇表中的词目一样多。

使用每个此作为特征并观察它们是否出现,这样得到的特征数目会有多少呢?

朴素贝叶斯的一本过程:

  1. 收集数据:可以使用任何方法。本章使用 RSS 源。
  2. 准备数据:需要数值型或者布尔型数据。
  3. 分析数据:有大量特征时,绘制特征作用不大,此时使用直方图效果更好。
  4. 训练算法:计算不同的独立特征的条件概率。
  5. 测试算法:计算错误率。
  6. 使用算法:一个常见的朴素贝叶斯应用是文档分类。可以贼拉任意的分类场景中使用朴素贝叶斯分类器,不一定非要是文本。

假定样本数为 N,由统计学知,如果每个特征需要 N 的样本,那么对于 1000 个特征将需要 $N^{1000}$ 个样本。可以看到,所需要的样本数会随着特征数目增大而迅速增长。

如果特征之间相互独立,那么样本数就可以从 $N^{1000}$ 减少到 1000*N 。所谓 独立(independence) 指的是统计意义上的独立,即一个特征或者单词出现的可能性与它和其他相邻没有关系。

这个假设正是朴素贝叶斯分类器中 朴素(naive) 一词的含义。朴素贝叶斯分类器中的另一个假设是,每个特征同等重要

5. 使用 python 进行文本分类

要从文本中获取特征,需要先拆分文本。这里的特征是来自文本的词条(token),一个词条是字符的任意组合。然后将每一个文本片段表示为一个词条向量,其中值为 1 表示词条出现在文档中,

以在线社区的留言板为例。为了不影响社区的发展,我们要屏蔽侮辱性的言论,所以要构建一个快速过滤器,如果某条留言使用了负面或侮辱性的语言,那么久将该留言标识为内容不当。对此问题建立两个类别:侮辱类和非侮辱类,使用 1 和 0 分别表示。

朴素贝叶斯分类器通常有两种实现方式:一种基于贝努利模型实现(只考虑出不出现,不考虑出现的次数),一种基于多项式模型实现(会考虑词在文档中出现的次数)。

5.1 准备数据:从文本中构建词向量

我们将把文本看成单词向量或者词条向量,也就是说将橘子转换成向量。考虑出现在所有文档中的所有单词,再决定将哪些词纳入词汇表或者说所要的词汇集合,然后必须将每一篇文档转换成词汇表上的向量。

创建一个叫 bayes.py 的文件,添加 loadDataSet() 函数:

def loadDataSet():
    """
    创建样本
    :return: postingList:进行词条切分后的文档集合,classVec:类别标签集合
    """
    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

添加 createVocabList(dataSet) 函数:

def createVocabList(dataSet):
    """
    得到集合的并集
    :param dataSet:
    :return:
    """
    vocabSet = set([])
    for document in dataSet:
        vocabSet = vocabSet | set(document)
    return list(vocabSet)

添加 setOfWords2Vec(vocabList, inputSet) 函数:

def setOfWords2Vec(vocabList, inputSet):
    """
    输出文档在词汇表中形成的文档向量:文档中出现某个词,那么文档向量中这个词的对应值就是 1
    :param vocabList: 词汇表
    :param inputSet: 文档
    :return: 文档向量
    """
    returnVec = [0] * len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] = 1
        else:
            print("the word %s is not in my vocabularty!" % word)
    return returnVec

查看运行效果:

listOPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
print(myVocabList) # ['my', 'dog', 'flea', 'worthless', 'dalmation', 'how', 'maybe', ' has', 'stop', 'buying', 'love', 'help', 'to', 'not', 'problems', 'please', 'stupid', 'him', 'posting', 'licks', 'so', 'I', 'is', 'quit', 'garbage', 'ate', 'take', 'food', 'steak', 'mr', 'cute', 'park']
vec1 = setOfWords2Vec(myVocabList,listOPosts[0])
print(vec1) # [1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

5.2 训练算法:从词向量计算概率

​ $$ p(c_i\vec{w})=\frac{p(\vec{w}c_i)p(c_i)}{p(\vec{w})}$$

我们将使用上述公式,对每个类计算该值,然后比较这两个概率值的大小。如何计算呢?

  1. 可以通过类别 i (侮辱性留言或非侮辱性留言)中文档数除以总的文档数来计算概率 p(Ci)。
  2. 再计算 p(W|Ci),这里就要用到朴素贝叶斯假设。如果将 w 展开为一个个独立特征,那么久可以将上述概率写作 p(W0,W1,...,Wn|Ci)。这里假设所有词都互相独立,该假设也称作条件独立性假设,它意味着可以使用 p(W0|Ci)p(W1|Ci)...p(Wn|Ci) 来计算上述概率,这就极大地简化了计算的过程。

该函数的伪代码如下:

计算每个类别中的文档数目
对每篇训练文档:
    对每个类别:
        如果词条出现文档中 >> 增加该词条的计数值
        增加所有词条的计数值
    对每个类别:
        对每个词条:
            将该词条的数目除以总词条数目得到条件概率
    返回每个类别的条件概率

按照上述伪代码的逻辑,添加 trainNB0(trainMatrix, trainCategory) 函数来实现:

def trainNB0(trainMatrix, trainCategory):
    """
    朴素贝叶斯分类器训练函数
    :param trainMatrix: 文档矩阵
    :param trainCategory: 由每篇文档类别标签所构成的向量
    :return:
    """
    numTrainDocs = len(trainMatrix)
    numWords = len(trainMatrix[0])
    pAbusive = sum(trainCategory)/float(numTrainDocs) # 任意文档属于侮辱性文档的概率

    # 初始化概率
    p0Num = np.zeros(numWords) # 非侮辱性文档向量的加和
    p1Num = np.zeros(numWords) # 侮辱性文档向量的加和
    p0Denom = 0.0 # 非侮辱性文档的概率
    p1Denom = 0.0 # 侮辱性文档的概率
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])
        else:
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    p1Vect = p1Num/p1Denom # change to log()
    p0Vect = p0Num/p0Denom # change to log()
    return p0Vect,p1Vect,pAbusive

运行验证:

listOPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
# print(myVocabList) # ['my', 'dog', 'flea', 'worthless', 'dalmation', 'how', 'maybe', ' has', 'stop', 'buying', 'love', 'help', 'to', 'not', 'problems', 'please', 'stupid', 'him', 'posting', 'licks', 'so', 'I', 'is', 'quit', 'garbage', 'ate', 'take', 'food', 'steak', 'mr', 'cute', 'park']
# vec1 = setOfWords2Vec(myVocabList,listOPosts[0])
# print(vec1) # [1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

trainMat = []
for postinDoc in listOPosts:
    trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
print('myVocabList:',myVocabList) # ['my', 'I', 'maybe', 'food', 'buying', 'so', 'to', 'mr', 'park', 'dog', 'is', ' has', 'ate', 'take', 'how', 'stop', 'flea', 'steak', 'worthless', 'love', 'help', 'quit', 'cute', 'dalmation', 'him', 'licks', 'posting', 'not', 'problems', 'please', 'stupid', 'garbage']
print('trainMat:',trainMat) #  [[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0], [0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0], [1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1], [1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]]
print('listClasses:',listClasses) # [0, 1, 0, 1, 0, 1]
p0v,p1v,pAb = trainNB0(trainMat, listClasses)
print('p0v:',p0v) 
# [ 0.125       0.04166667  0.          0.          0.          0.04166667
# 0.04166667  0.04166667  0.          0.04166667  0.04166667  0.04166667
# 0.04166667  0.          0.04166667  0.04166667  0.04166667  0.04166667
# 0.          0.04166667  0.04166667  0.          0.04166667  0.04166667
# 0.08333333  0.04166667  0.          0.          0.04166667  0.04166667
# 0.          0.        ]
print('p1v:',p1v)
# [ 0.          0.          0.05263158  0.05263158  0.05263158  0.
#   0.05263158  0.          0.05263158  0.10526316  0.          0.          0.
#   0.05263158  0.          0.05263158  0.          0.          0.10526316
#   0.          0.          0.05263158  0.          0.          0.05263158
#   0.          0.05263158  0.05263158  0.          0.          0.15789474
#   0.05263158]
print('pAb:',pAb) # 0.5

5.3 测试算法:根据现实情况修改分类器

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

即 trainNB0() 中修改为:

    # 初始化概率
    p0Num = np.ones(numWords) # 非侮辱性文档向量的加和
    p1Num = np.ones(numWords) # 侮辱性文档向量的加和
    p0Denom = 2.0 # 非侮辱性文档的概率
    p1Denom = 2.0 # 侮辱性文档的概率

另一个问题是下溢出,这是由于太多很小的数相乘造成的。当计算乘积 p(W0|Ci)p(W1|Ci)...p(Wn|Ci) 时,由于大部分因子都非常小,所以程序会下溢出或者得不到正确的答案。一种解决办法是对乘积取自然对数。在代数中有 ln(a*b) = ln(a) + ln(b),于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。

图404 给出函数 f(x) 与 ln(f(x)) 的曲线。检查这两条曲线,就会发现它们在相同区域内同时增加或减少,并且在相同点上取到极值。它们的取值虽然不同,但不影响最终结果。通过修改 return 前的两行代码,将上述做法用到分类器中:

    p1Vect = np.log(p1Num/p1Denom) # change to log()
    p0Vect = np.log(p0Num/p0Denom) # change to log()
图4-4

添加 classifyNB(vec2Classify, p0Vec, p1Vec, pClass1) 函数:

def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    """
    朴素贝叶斯分类
    :param vec2Classify: 要分类的向量
    :param p0Vec:非侮辱性文档的向量
    :param p1Vec:侮辱性文档的向量
    :param pClass1:
    :return:
    """
    p1 = sum(vec2Classify * p1Vec) + np.log(pClass1) # 向量元素相乘,然后加到类别的对数概率上
    p0 = sum(vec2Classify * p0Vec) + np.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(np.array(trainMat),np.array(listClasses))
    testEntry = ['love', 'my', 'dalmation']
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
    print(testEntry,'classified as:',classifyNB(thisDoc, p0V,p1V,pAb)) # ['love', 'my', 'dalmation'] classified as: 0
    testEntry = ['stupid', 'garbage']
    thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
    print(testEntry,'classified as:',classifyNB(thisDoc, p0V,p1V,pAb)) # ['stupid', 'garbage'] classified as: 1
    
testingNB()

5.4 准备数据:文档词袋模型

我们将每个此的出现与否作为一个特征,这可以被描述为 词集模型(set-of-words model)。如歌一个词再来文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法被称为 词袋模型(bag-of-words mode;)。在词袋中,每个单词可以出现多次,而在词集中,每个此只能出现一次。为适应词袋模型,需要对函数 setOfWords2Vec() 做修改,修改后的函数称为 bagOfWords2Vec():

def bagOfWords2Vec(vocabList, inputSet):
    """
    朴素贝叶斯词袋模型
    :param vocabList: 词汇表
    :param inputSet: 文档
    :return: 文档向量
    """
    returnVec = [0] * len(vocabList)
    for word in inputSet:
        if word in inputSet: # 每当遇到一个单词时,就增加向量中的对应值,而不只是将对应的数值设为1
            returnVec[vocabList.index(word)] += 1
    return returnVec

6. 示例:使用朴素贝叶斯过滤垃圾邮件

示例:使用朴素贝叶斯对电子邮件进行分类:

  1. 收集数据:提供文本文件
  2. 准备数据:将文本文件解析成词条向量
  3. 分析数据:检查词条确保解析的正确性
  4. 训练算法:使用我们之前建立的 trainNB0() 函数
  5. 测试算法:使用 classifyNB() 函数,并且构建一个新的测试函数来计算文档集的错误率
  6. 使用算法:构建一个完整的程序对一组文档进行分类,将错分的文档输出到屏幕上

6.1 准备数据:切分文本

切分,去除标点、空格,大写转小写等:

def textParse(bigString):
    import  re
    listOfTokens = re.split(r'\W*',bigString)
    return [tok.lower() for tok in listOfTokens if len(tok)>2] # 去掉少于两个字符的单词,转小写

6.2 测试算法:使用朴素贝叶斯进行交叉验证

添加 spamTest() 函数:

def spamTest():
    """
    对贝叶斯垃圾邮件分类器进行自动化处理
    :return: 错误率
    """
    docList = []
    classList = []
    fullText = []
    for i in range(1,26): # 取数据
        filetxt = open('email/spam/%d.txt' % i).read() # 读取文本文件
        wordList = textParse(filetxt)
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(1)
        filetxt = open('email/ham/%d.txt' % i).read()
        wordList = textParse(filetxt)
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)
    vocabList = createVocabList(docList)
    trainingSet = list(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(setOfWords2Vec(vocabList, docList[docIndex]))
        trainClasses.append(classList[docIndex])
    p0V,p1V,pSpam = trainNB0(np.array(trainMat), np.array(trainClasses))

    errorCount = 0
    for docIndex in testSet:
        wordVector = setOfWords2Vec(vocabList, docList[docIndex])
        re_learn = classifyNB(np.array(wordVector), p0V, p1V, pSpam)
        print('re_learn:',re_learn)
        print('classList[docIndex]:',classList[docIndex])
        if re_learn != classList[docIndex]:
            errorCount += 1
    print('the error rate is :', float(errorCount)/len(testSet))

  1. 导入文件夹 spam 与 ham 下的文本文件,并将它们解析为词列表
  2. 接下来构建一个测试集与一个训练集,两个集合中的邮件都是随机选出的
  3. 循环遍历训练集的所有文档,对每封邮件基于词汇表并使用 setOfWords2Vec() 函数来构建词向量。这些词在 trainNB0() 函数中用于计算分类所需的概率
  4. 遍历测试集,对其中每封邮件进行分类,计算出错误数及错误率。

测试代码:

spamTest() # the error rate is : 0.1

如果发现错误的话,函数会输出错分文档的词表,这样就可以了解到底是哪篇文档发生了错误,如果想要更好地估计错误率,那么久应该讲上述过程重复多次,然后求取平均值。

注意:这里一直出现吗的错误是将垃圾邮件误判为正常邮件。相比之下,将垃圾邮件误判为正常邮件要比将正常邮件归到垃圾邮件好。

7. 示例:使用朴素贝叶斯分类器从个人广告中获取区域倾向

在这个例子中,我们将分析中美国的两个城市中选取一些人,通过分析这些人发布的征婚广告信息,来比较这两个城市的人们在广告用词上是否不同。若干结论确实是不同,那么他们各自常用的词是哪些?从人们的用词当中,我们能否对不同城市的人所关心的内容有所了解?

示例:使用朴素贝叶斯来发现地区相关的用词

  1. 收集数据:从 RSS 源收集内容,这里需要对 RSS 源构建一个接口
  2. 准备数据:将文本文件解析成词条向量
  3. 分析数据:检查词条确保解析的正确性
  4. 训练算法:使用我们之前建立的 trainNB0() 函数
  5. 测试算法:观察错误率,确保分类器可用。可以修改切分程序,以降低错误率,提高分类结果。
  6. 使用算法:构建一个完整的程序,封装所有内容。给定两个 RSS 源,该程序会显示最常用的公共词

我们的目的不是使用分类器进行分类,而是通过观察单词和条件概率值来发现与特定城市相关的内容。

7.1 收集数据:导入 RSS 源

构建一个类似于 spamTest() 函数来对测试过程自动化 localWords:

def localWords(feed1, feed0):
    """
    数据源来自:RSS,移除高频单词,其他基本与 spamTest() 一致
    :param feed1:
    :param feed0:
    :return:
    """
    import feedparser
    docList = []
    classList = []
    fullText = []
    minlen = min(len(feed1['entries']),len(feed0['entries'])) # 获取条目较少的RSS源的条目数
    for i in range(minlen): # 解析和处理数据
        wordList = textParse(feed1['entries'][i]['summary'])
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(1)
        wordList = textParse(feed0['entries'][i]['summary'])
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)
    vocabList = createVocabList(docList) # 所有词条列表
    top30Words = calcMostFreq(vocabList, fullText) # 高频的30个单词
    for pairW in top30Words:
        if pairW[0] in vocabList:vocabList.remove(pairW[0])
    trainingSet = list(range(2*minlen))
    testSet = []
    for i in range(20):
        randIndex = int(np.random.uniform(0,len(trainingSet)))
        testSet.append(trainingSet[randIndex])
        del(trainingSet[randIndex])
    trainMat = []
    trainClasses = []
    for docIndex in trainingSet:
        trainMat.append(bagOfWords2Vec(vocabList, docList[docIndex]))
        trainClasses.append(classList[docIndex])
    p0V, p1V, pSpam = trainNB0(np.array(trainMat), np.array(trainClasses))
    errorCount = 0
    for docIndex in testSet:
        wordVector = bagOfWords2Vec(vocabList, docList[docIndex])
        if classifyNB(np.array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
            errorCount += 1
    print('the error rate is: ',float(errorCount)/len((testSet)))
    return vocabList, p0V, p1V

辅助函数 calcMostFreq() 函数:

def calcMostFreq(vocabList, fullText):
    """
    遍历词汇表中的每个此并统计它在文本中出现的次数,然后根据出现次数从高到低对词典排序,最后返回排序最高的30个
    :param vocabList: 
    :param fullText: 
    :return: 
    """
    import operator
    freqDict = {}
    for token in vocabList:
        freqDict[token]=fullText.count(token)
    sortedFreq = sorted(freqDict.items(),key=operator.itemgetter(1),reverse=True)
    return sortedFreq[:30]

移除高频单词的原因:高频单词会占据较高的次数,产生这种现象的原因是语言中大部分都是冗余和结构辅助性内容,另一个常用的方法是不仅移除高频单词,同时从某个预定词表中移除结构上的辅助词。该词表称为 停用词表(stop word list)

7.2分析数据:显示地域相关的用词

添加 getTopWords() 函数:

def getTopWords(ny,sf):
    import operator
    vocaList,p0V,p1V=localWords(ny,sf) # 训练并测试朴素贝叶斯分类器
    topNY = []
    topSF = []
    for i in range(len(p0V)): #存储大于某个阈值(0.6)的所有词
        if p0V[i] > -6.0: topSF.append((vocaList[i], p0V[i]))
        if p1V[i] > -6.0: topNY.append((vocaList[i], p1V[i]))
    sortedSF = sorted(topSF, key=lambda pair:pair[1],reverse=True)
    print('------------SF-------------')
    for item in sortedSF:
        print(item[0])
    sortedNF = sorted(topNY, key=lambda  pair:pair[1], reverse=True)
    print('-----------NF-----------')
    for item in sortedNF:
        print(item[0])

8. 本章小结

对于分类而言,使用概率有时要比使用硬规则更为有效。贝叶斯概率即贝叶斯准则提供了一种可以利用已知值来估计未知概率的有效方法。

可以通过特征之间的条件独立性假设,降低对数据量的需求。独立性假设是指一个词的出现概率并不依赖于文档中的其他词。因为这个假设过于简单,所有称为朴素贝叶斯。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容