贝叶斯定理

w是由待测数据的所有属性组成的向量。p(c|x)表示,在数据为x时,属于c类的概率。

\[p(c|w)=\frac{p(w|c)p(c)}{p(w)}
\]

如果数据的目标变量最后有两个结果,则需要分别计算p(c1|x)p(c2|x)取最大的值为分类的结果

\[p(c_{1}|w)=\frac{p(w|c_{1})p(c_{1})}{p(w)}、
p(c_{2}|w)=\frac{p(w|c_{2})p(c_{2})}{p(w)}
\]

算法的目的就在于找到使p最大的 \(c_{i}\)。由于只需要比较两个概率的大小,则分母p(w)可以不用算,并不影响结果。那 \(p(w|c_{0})p(c_{0})\)又如何计算呢?一条数据w其实包含很多属性w=w1,w2,w3,...,wn.以 \(p(w|c_{0})p(c_{0})\)为例:

p(c0) 表示分类结果为c0的概率:

\[p(c_{0})=\frac{数据集中属于c_{0}类别的数据条数}{数据集的总数}
\]

而 \(p(w|c_{0})\) == \(p(w_{1},w_{2},w_{3},...,w_{n}|c_{0})\)。朴素贝叶斯分类假设所有属性之间是独立的,互不影响。那么就满足如下关系:

\[p(w_{1},w_{2},w_{3},...,w_{n}|c_{0}) = p(w_{1}|c{0})p(w_{2}|c{0})p(w_{3}|c{0})...p(w_{n}|c{0})
\]
\[p(w_{1}|c{0})=\frac{在c_{0}类别的数据中单词w_{1}出现的次数}{属于c_{0}类别的单词总数}
\]

至此,已经计算出了足够数据来计算出\(p(w|c_{1})p(c_{1})\),用这些概率可以给新的数据分类。如果此时有数据 w=w1,w3,w5,那需要分别算出两个概率:

\[p(c_{0}|w_{1},w_{3},w_{5})=>p(w_{1}|c{0})p(w_{3}|c{0})p(w_{5}|c{0})p(c_{0})
\]
\[p(c_{1}|w_{1},w_{3},w_{5})=>p(w_{1}|c{1})p(w_{3}|c{0})p(w_{5}|c{1})p(c_{1})
\]

比较大小,找到最大的概率,最大概率的 \(c_{i}\)就是分类的结果

算法实现

收集数据

1、从文本文件中读取数据,并分割成每个单词,放到一个list中。每个文件一个list,最后是一个二维的list:

[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],

['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid']]

  1. def loadDataSet():
  2. """
  3. 创建数据集
  4. :return: 文档列表 docList, 所属类别classVec
  5. """
  6. docList = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
  7. ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
  8. ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
  9. ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
  10. ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
  11. ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
  12. classVec = [0, 1, 0, 1, 0, 1] # 1 is abusive, 0 not
  13. return docList, classVec

2、遍历上例的二维表,找到所有出现的单词,使用set()去重。这个集合就作为数据集的属性.上例的词汇表为:

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

  1. def createVocabList(docList):
  2. """构造词汇表,统计所有文本中的所有单词
  3. :return list 去重的词汇表
  4. """
  5. vocalSet = set([])
  6. for line in docList:
  7. vocalSet = vocalSet | set(line) # 集合求并集操作
  8. return list(vocalSet)

3、将每个文件的单词列表转换为向量。遍历文件中的每个单词,如果出现在词汇表中则为1,否则为0

[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1]

  1. def setOfWords2Vec(vocabList, inputSet):
  2. """将输入数据转换为向量.存在这个单词记为1,不存在则记为0"""
  3. returnVec = [0] * len(vocabList)
  4. for word in inputSet:
  5. if word in vocabList:
  6. returnVec[vocabList.index(word)] = 1
  7. return returnVec

训练算法

训练算法就是计算一系列概率的过程。要预测一个文本,需要计算以下概率:

  • \(p(c_{0})\) 和 \(p(c_{1})\)
  • \(p(w_{i}|c_{0})\) 和 \(p(w_{i}|c_{1})\)

下面的代码计算出了这些概率,其中p0是一个列表,其中记录了每一个 \(p(w_{i}|c_{0})\)的值。p1同理。pc1表示 \(p(c_{1})\),\(p(c_{0})\)可以用 1-pc1 得到

p0: [[0.04166667 0.04166667 0.04166667 0.04166667 ...]]

p1: [[0. 0.10526316 0. 0. ...]]

pc: 0.5

  1. def trainNB0(trainMatrix, trainCategory):
  2. """分类器训练函数"""
  3. numberOfAttr = len(trainMatrix[0])
  4. numbrOfDoc = len(trainMatrix)
  5. p0 = np.zeros((1, numberOfAttr)) # p(wi|c0)
  6. p1 = np.zeros((1, numberOfAttr)) # p(wi|c1)
  7. p0Total = 0.0
  8. p1Total = 0.0
  9. pc1 = float(sum(trainCategory)) / numbrOfDoc # p(c1)
  10. for i in range(numbrOfDoc):
  11. if trainCategory[i] == 0:
  12. p0 += trainMatrix[i] # 统计先验概率c0下,每个单词出现的次数
  13. p0Total += sum(trainMatrix[i])
  14. else:
  15. p1 += trainMatrix[i] # 统计先验概率c1下,每个单词出现的次数
  16. p1Total += sum(trainMatrix[i])
  17. p0 = p0 / p0Total # 用c0下每个单词出现的次数,分别除以c0下的总数==> p(wi|c0)
  18. p1 = p1 / p1Total # p(wi|c1)
  19. return p0, p1, pc1

分类

根据之前的理论。如果数据有三个单词 w=w1,w3,w5,那需要分别算出两个概率:

\[p(c_{0}|w_{1},w_{3},w_{5})=>p(w_{1}|c{0})p(w_{3}|c{0})p(w_{5}|c{0})p(c_{0})
\]
\[p(c_{1}|w_{1},w_{3},w_{5})=>p(w_{1}|c{1})p(w_{3}|c{0})p(w_{5}|c{1})p(c_{1})
\]

优化代码

1、书上说,对于 \(p(w_{1}|c{0})p(w_{2}|c{0})p(w_{3}|c{0})\)如果其中任何一个概率为0,则总概率为零,所以把所有单词出现的次数初始化为1。(其实不改也行,但是代码中inputData * p0已经过滤出了所有非零元素)

2、 概率都是很小的数,如果直接以小数运算会带来很大的误差。书上采用了对数替代直接的小数运算。本来的概率是这样算的

\[p(w_{1}|c{0})p(w_{3}|c{0})p(w_{5}|c{0})p(c_{0}) == \frac{count(w_{1}|c_{0})}{count(c{0})}\frac{count(w_{3}|c_{0})}{count(c{0})}\frac{count(w_{5}|c_{0})}{count(c{0})}p(c_{0})
\]

现在使用对数,In(fx)并不会影响f(x)的单调性,所以计算的结果可以直接比较大小,不会影响分类结果。计算方式如下:

\[In(p(w_{1}|c{0})p(w_{3}|c{0})p(w_{5}|c{0})p(c_{0})) == In(\frac{count(w_{1}|c_{0})}{count(c{0})})+In(\frac{count(w_{3}|c_{0})}{count(c{0})})+In(\frac{count(w_{5}|c_{0})}{count(c{0})}) + In(p(c_{0}))
\]

优化后的训练代码如下:

  1. def trainNB1(trainMatrix, trainCategory):
  2. """分类器训练函数"""
  3. numberOfAttr = len(trainMatrix[0])
  4. numbrOfDoc = len(trainMatrix)
  5. p0 = np.ones((1, numberOfAttr)) # p(wi|c0)
  6. p1 = np.ones((1, numberOfAttr)) # p(wi|c1)
  7. p0Total = 2.0 # 不唯一
  8. p1Total = 2.0
  9. pc1 = float(sum(trainCategory)) / numbrOfDoc # p(c1)
  10. for i in range(numbrOfDoc):
  11. if trainCategory[i] == 0:
  12. p0 += trainMatrix[i]
  13. p0Total += sum(trainMatrix[i])
  14. else:
  15. p1 += trainMatrix[i]
  16. p1Total += sum(trainMatrix[i])
  17. p0 = np.log(p0 / p0Total)
  18. p1 = np.log(p1 / p1Total)
  19. return p0, p1, pc1

分类的代码如下:

  1. def classifyNB(inputData, p0, p1, pc1):
  2. """使用计算得到的概率分类"""
  3. prob0 = np.sum(inputData * p0) + np.log(1-pc1)
  4. prob1 = np.sum(inputData * p1) + np.log(pc1)
  5. if prob0 > prob1:
  6. return 0
  7. else:
  8. return 1

测试代码:

  1. def testNB():
  2. """测试函数"""
  3. docList, classVec = loadDataSet()
  4. vocabList = createVocabList(docList)
  5. trainMat = [] # 由0/1组成的数据集 : [[0,1,0,0,1,....],[0,0,0,0,0,1,...]]
  6. for doc in docList:
  7. trainMat.append(setOfWords2Vec(vocabList, doc))
  8. p0, p1, pc1 = trainNB0(trainMat, classVec)
  9. testData = ['love', 'my', 'dalmation']
  10. thisDoc = setOfWords2Vec(vocabList, testData)
  11. print("分类结果是:", classifyNB(thisDoc, p0, p1, pc1))
  12. testData = ['stupid', 'garbage']
  13. thisDoc = setOfWords2Vec(vocabList, testData)
  14. print("分类结果是:", classifyNB(thisDoc, p0, p1, pc1))

词袋模型

上面的代码中,文件中出现的单词,记为1,否则为0。这种方式叫词集模型(set-of-words model)。得到的是如下的向量:

[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1]

但是同一个单词在文档中可能多次出现,在向量中记录单词出现的次数的方式叫做词袋模型(bag-of-words model)。得到的向量可能是这样的:

[0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 7, 0, 0, 2]

要实现词袋模型只需要改动很少量的代码:

  1. def bagOfWords2Vec(vocabList, inputSet):
  2. """[词袋模型]将输入数据转换为向量.存在这个单词记为1,不存在则记为0"""
  3. returnVec = [0] * len(vocabList)
  4. for word in inputSet:
  5. if word in vocabList:
  6. returnVec[vocabList.index(word)] += 1
  7. return returnVec

案例:过滤垃圾邮件

  1. def textParse(bigString):
  2. """将字符串返回成单词列表
  3. 1. 以空白字符作为分隔符
  4. 2. 排除长度小于2的单词,他可能没有实际意义
  5. 3. 所有单词转换为小写
  6. """
  7. import re
  8. listOfWords = re.split(r'\W*', bigString)
  9. return [word.lower() for word in listOfWords if len(word) > 2]
  10. def spamTest():
  11. """测试算法。使用交叉验证"""
  12. docList, classList, fullText = [], [], []
  13. # 1. 解析文本文件。一个文件解析成一个list,所有文件保存为一个二维list
  14. for i in range(1, 26):
  15. spam = open("dataset/email/spam/%d.txt" % i)
  16. wordList = textParse(spam.read())
  17. docList.append(wordList)
  18. classList.append(1)
  19. ham = open("dataset/email/ham/%d.txt" % i)
  20. wordList = textParse(ham.read())
  21. docList.append(wordList)
  22. classList.append(0)
  23. # 2.格式化
  24. vocabList = createVocabList(docList) # 创建词汇表
  25. # 3. 随机挑选10个测试数据(可能没有10个)
  26. trainSet = list(range(50)) # 记录了所有用于训练的数据集的下标
  27. testSet = [] # 记录了所有用于测试的数据集的下标
  28. for i in range(10):
  29. randIndex = int(random.uniform(0, len(trainSet)))
  30. testSet.append(trainSet[randIndex])
  31. del trainSet[randIndex]
  32. # 4. 训练
  33. trainMatrix, trainCategory = [], []
  34. for i in range(len(trainSet)):
  35. trainMatrix.append(bagOfWords2Vec(vocabList, docList[trainSet[i]]))
  36. trainCategory.append(classList[trainSet[i]])
  37. p0, p1, pc1 = trainNB1(trainMatrix, trainCategory)
  38. # 5. 测试
  39. testMatrix, testCategory, error = [], [], 0
  40. for i in range(len(testSet)):
  41. line = bagOfWords2Vec(vocabList, docList[testSet[i]])
  42. result = classifyNB(line, p0, p1, pc1)
  43. if result != classList[testSet[i]]:
  44. error += 1
  45. print("错误率为:", (float(error)/len(testSet)))

ML-朴素贝叶斯算法的更多相关文章

  1. [ML学习笔记] 朴素贝叶斯算法(Naive Bayesian)

    [ML学习笔记] 朴素贝叶斯算法(Naive Bayesian) 贝叶斯公式 \[P(A\mid B) = \frac{P(B\mid A)P(A)}{P(B)}\] 我们把P(A)称为"先 ...

  2. 【十大算法实现之naive bayes】朴素贝叶斯算法之文本分类算法的理解与实现

    关于bayes的基础知识,请参考: 基于朴素贝叶斯分类器的文本聚类算法 (上) http://www.cnblogs.com/phinecos/archive/2008/10/21/1315948.h ...

  3. 朴素贝叶斯算法原理及Spark MLlib实例(Scala/Java/Python)

    朴素贝叶斯 算法介绍: 朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法. 朴素贝叶斯的思想基础是这样的:对于给出的待分类项,求解在此项出现的条件下各个类别出现的概率,在没有其它可用信息下,我 ...

  4. 机器学习---用python实现朴素贝叶斯算法(Machine Learning Naive Bayes Algorithm Application)

    在<机器学习---朴素贝叶斯分类器(Machine Learning Naive Bayes Classifier)>一文中,我们介绍了朴素贝叶斯分类器的原理.现在,让我们来实践一下. 在 ...

  5. 朴素贝叶斯算法下的情感分析——C#编程实现

    这篇文章做了什么 朴素贝叶斯算法是机器学习中非常重要的分类算法,用途十分广泛,如垃圾邮件处理等.而情感分析(Sentiment Analysis)是自然语言处理(Natural Language Pr ...

  6. 【数据挖掘】朴素贝叶斯算法计算ROC曲线的面积

    题记:          近来关于数据挖掘学习过程中,学习到朴素贝叶斯运算ROC曲线.也是本节实验课题,roc曲线的计算原理以及如果统计TP.FP.TN.FN.TPR.FPR.ROC面积等等.往往运用 ...

  7. 朴素贝叶斯算法的python实现

    朴素贝叶斯 算法优缺点 优点:在数据较少的情况下依然有效,可以处理多类别问题 缺点:对输入数据的准备方式敏感 适用数据类型:标称型数据 算法思想: 朴素贝叶斯比如我们想判断一个邮件是不是垃圾邮件,那么 ...

  8. C#编程实现朴素贝叶斯算法下的情感分析

    C#编程实现 这篇文章做了什么 朴素贝叶斯算法是机器学习中非常重要的分类算法,用途十分广泛,如垃圾邮件处理等.而情感分析(Sentiment Analysis)是自然语言处理(Natural Lang ...

  9. Naive Bayes(朴素贝叶斯算法)[分类算法]

    Naïve Bayes(朴素贝叶斯)分类算法的实现 (1) 简介: (2)   算法描述: (3) <?php /* *Naive Bayes朴素贝叶斯算法(分类算法的实现) */ /* *把. ...

  10. 腾讯公司数据分析岗位的hadoop工作 线性回归 k-means算法 朴素贝叶斯算法 SpringMVC组件 某公司的广告投放系统 KNN算法 社交网络模型 SpringMVC注解方式

    腾讯公司数据分析岗位的hadoop工作 线性回归 k-means算法 朴素贝叶斯算法 SpringMVC组件 某公司的广告投放系统 KNN算法 社交网络模型 SpringMVC注解方式 某移动公司实时 ...

随机推荐

  1. bbs项目解读

    1.注册功能 具体的效果图如下: 注册功能涉及到的逻辑步骤: 1.搭建前端html页面 2.向后端提交用户输入数据 3.对用户输入的数据格式进行校验 4.页面输入数据格式错误,及时向用户进行提示/正确 ...

  2. 完整实现-通过DelayQueue实现延时任务

    实现延时任务有很多的方法,网上关于延时任务的实现的文章已经不少了.比如:实现延时任务的10种方法等等.但是这些文章基本上都是将方法大概的列举一下,给出部分示例代码,对于有经验的老程序员可能一看就知道该 ...

  3. 【Go实战基础】GO语言是什么,有哪些优势

    一.简介 2007年,为了提高在多核.网络机器(networked machines).大型代码库(codebases)的业务场景下的开发效率,Google 首席软件工程师决定创造一种语言那就是 Go ...

  4. ThreadLocal for Golang

    背景 由于官方不支持 ThreadLocal,在业务中传参经常需要传递 context,造成参数混乱,开发效率低下,跨方法实现传参变得困难. 需要解决的核心问题: 1. 数据存储,g.labels u ...

  5. HTML引用CSS实现自适应背景图

    链接图片背景代码 body {background: url('链接') no-repeat center 0;} 颜色代码 body{background:#FFF} 链接图片背景代码2 <b ...

  6. CodeForces - 1701C

    Problem - C - Codeforces 题意: 每个位置对应一种适合的工人,适合的工人工作消耗1h,不适合2h,每个工人不能同时工作多个机器,问将所有机器工作完毕的最小时间是多少. 题解: ...

  7. Html飞机大战(一):绘制动态背景

    好家伙,飞机大战终于开始弄了 这会有很多很多复杂的东西,但是我们总要从最简单,和最基础的部分开始,   我们先从背景开始弄吧! 1.绘制静态背景 这里我们会用到canvas <!DOCTYPE ...

  8. AXI MCDMA 仿真与工作流程分析

    说明 关于背景知识,可以先看 https://www.cnblogs.com/xingce/p/16386108.html 引用一段官方的说明,AXI MCDMA存在的主要目的是为了节约资源,我们想要 ...

  9. [MRCTF2020]Ez_bypass WP

    首先打开页面 他提示说f12里面有东西,于是直接ctrl+u 查看源代码 I put something in F12 for you include 'flag.php'; $flag='MRCTF ...

  10. Django 使用VScode 创建工程

    一.VSCode 创建Django 工程 VSCode 官方: https://code.visualstudio.com 1 mysite(项目名),创建Django 项目,可以和虚拟环境放在同一目 ...