Merge pull request #136 from jiangzhonglian/master

完善 贝叶斯项目案例
This commit is contained in:
Joy yx
2017-09-01 18:10:49 +08:00
committed by GitHub
2 changed files with 50 additions and 42 deletions

View File

@@ -215,7 +215,7 @@ def setOfWords2Vec(vocabList, inputSet):
我们使用上述公式,对每个类计算该值,然后比较这两个概率值的大小。
首先可以通过类别 i (侮辱性留言或者非侮辱性留言)中文档数除以总的文档数来计算概率 p(ci) 。接下来计算 p(<b>w</b> | ci) ,这里就要用到朴素贝叶斯假设。如果将 w 展开为一个个独立特征,那么就可以将上述概率写作 p(w0, w1, w2...wn | ci) 。这里假设所有词都互相独立,该假设也称作条件独立性假设,它意味着可以使用 p(w0 | ci)p(w1 | ci)p(w2 | ci)...p(wn | ci) 来计算上述概率,这样就极大地简化了计算的过程。
首先可以通过类别 i (侮辱性留言或者非侮辱性留言)中文档数除以总的文档数来计算概率 p(ci) 。接下来计算 p(<b>w</b> | ci) ,这里就要用到朴素贝叶斯假设。如果将 w 展开为一个个独立特征,那么就可以将上述概率写作 p(w0, w1, w2...wn | ci) 。这里假设所有词都互相独立,该假设也称作条件独立性假设(例如 A 和 B 两个人抛骰子概率是互不影响的也就是相互独立的A 抛 2点的同时 B 抛 3 点的概率就是 1/6 * 1/6,它意味着可以使用 p(w0 | ci)p(w1 | ci)p(w2 | ci)...p(wn | ci) 来计算上述概率,这样就极大地简化了计算的过程。
朴素贝叶斯分类器训练函数
@@ -242,22 +242,35 @@ def _trainNB0(trainMatrix, trainCategory):
p0Denom = 0.0
p1Denom = 0.0
for i in range(numTrainDocs):
# 遍历所有的文件,如果是侮辱性文件,就计算此侮辱性文件中出现的侮辱性单词的个数
# 是否是侮辱性文件
if trainCategory[i] == 1:
p1Num += trainMatrix[i] #[0,1,1,....]->[0,1,1,...]
# 如果是侮辱性文件,对侮辱性文件的向量进行加和
p1Num += trainMatrix[i] #[0,1,1,....] + [0,1,1,....]->[0,2,2,...]
# 对向量中的所有元素进行求和,也就是计算所有侮辱性文件中出现的单词总数
p1Denom += sum(trainMatrix[i])
else:
# 如果不是侮辱性文件,则计算非侮辱性文件中出现的侮辱性单词的个数
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
# 类别1即侮辱性文档的[P(F1|C1),P(F2|C1),P(F3|C1),P(F4|C1),P(F5|C1)....]列表
# 即 在1类别下每个单词出现次数的占比
# 即 在1类别下每个单词出现的概率
p1Vect = p1Num / p1Denom# [1,2,3,5]/90->[1/90,...]
# 类别0即正常文档的[P(F1|C0),P(F2|C0),P(F3|C0),P(F4|C0),P(F5|C0)....]列表
# 即 在0类别下每个单词出现次数的占比
# 即 在0类别下每个单词出现的概率
p0Vect = p0Num / p0Denom
return p0Vect, p1Vect, pAbusive
```
> 测试算法: 根据现实情况修改分类器
在利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算 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), 于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。
下图给出了函数 f(x) 与 ln(f(x)) 的曲线。可以看出,它们在相同区域内同时增加或者减少,并且在相同点上取到极值。它们的取值虽然不同,但不影响最终结果。
![函数图像](/images/4.NaiveBayesian/NB_7.png )
```python
def trainNB0(trainMatrix, trainCategory):
"""
训练数据优化版本
@@ -296,22 +309,9 @@ def trainNB0(trainMatrix, trainCategory):
# 类别0即正常文档的[log(P(F1|C0)),log(P(F2|C0)),log(P(F3|C0)),log(P(F4|C0)),log(P(F5|C0))....]列表
p0Vect = log(p0Num / p0Denom)
return p0Vect, p1Vect, pAbusive
```
> 测试算法: 根据现实情况修改分类器
在利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算 p(w0 | 1)p(w1 | 1)p(w2 | 1)。如果其中一个概率值为 0那么最后的乘积也为 0。为降低这种影响可以将所有词的出现数初始化为 1并将分母初始化为 2 。
另一个遇到的问题是下溢出,这是由于太多很小的数相乘造成的。当计算乘积 p(w0 | ci)p(w1 | ci)p(w2 | ci)...p(wn | ci) 时,由于大部分因子都非常小,所以程序会下溢出或者得到不正确的答案。(用 Python 尝试相乘许多很小的数,最后四舍五入后会得到 0。一种解决办法是对乘积取自然对数。在代数中有 ln(a * b) = ln(a) + ln(b), 于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。
下图给出了函数 f(x) 与 ln(f(x)) 的曲线。可以看出,它们在相同区域内同时增加或者减少,并且在相同点上取到极值。它们的取值虽然不同,但不影响最终结果。通过修改 return 前的两行代码,将上述做法用到分类器中:
```python
p1Vect = log(p1Num /p1Denom)
p0Vect = log(p0Num / p0Denom)
```
![函数图像](/images/4.NaiveBayesian/NB_7.png )
> 使用算法: 对社区留言板言论进行分类
@@ -331,10 +331,12 @@ def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
:return: 类别1 or 0
"""
# 计算公式 log(P(F1|C))+log(P(F2|C))+....+log(P(Fn|C))+log(P(C))
# 大家可能会发现,上面的计算公式,没有除以贝叶斯准则的公式的分母,也就是 P(w) P(w) 指的是此文档在所有的文档中出现的概率)就进行概率大小的比较了,
# 因为 P(w) 针对的是包含侮辱和非侮辱的全部文档,所以 P(w) 是相同的。
# 使用 NumPy 数组来计算两个向量相乘的结果这里的相乘是指对应元素相乘即先将两个向量中的第一个元素相乘然后将第2个元素相乘以此类推。
# 我的理解是:这里的 vec2Classify * p1Vec 的意思就是将每个词与其对应的概率相关联起来
p1 = sum(vec2Classify * p1Vec) + log(pClass1)
p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
p1 = sum(vec2Classify * p1Vec) + log(pClass1) # P(w|c1) * P(c1) ,即贝叶斯准则的分子
p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1) # P(w|c0) * P(c0) ,即贝叶斯准则的分子·
if p1 > p0:
return 1
else:
@@ -403,21 +405,6 @@ Eugene
> 准备数据: 将文本文件解析成词条向量
文档词袋模型
我们将每个词的出现与否作为一个特征,这可以被描述为 <b>词集模型(set-of-words model)</b>。如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法被称为 <b>词袋模型(bag-of-words model)</b>。在词袋中,每个单词可以出现多次,而在词集中,每个词只能出现一次。为适应词袋模型,需要对函数 setOfWords2Vec() 稍加修改,修改后的函数为 bagOfWords2Vec() 。
如下给出了基于词袋模型的朴素贝叶斯代码。它与函数 setOfWords2Vec() 几乎完全相同,唯一不同的是每当遇到一个单词时,它会增加词向量中的对应值,而不只是将对应的数值设为 1 。
```python
def bagOfWords2VecMN(vocaList, inputSet):
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in inputSet:
returnVec[vocabList.index(word)] += 1
return returnVec
```
使用正则表达式来切分文本
```python
@@ -431,7 +418,7 @@ def bagOfWords2VecMN(vocaList, inputSet):
> 分析数据: 检查词条确保解析的正确性
> 训练算法: 使用我们之前建立的 trainNB() 函数
> 训练算法: 使用我们之前建立的 trainNB0() 函数
```python
def trainNB0(trainMatrix, trainCategory):
@@ -576,6 +563,21 @@ def spamTest():
> 准备数据: 将文本文件解析成词条向量
文档词袋模型
我们将每个词的出现与否作为一个特征,这可以被描述为 <b>词集模型(set-of-words model)</b>。如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法被称为 <b>词袋模型(bag-of-words model)</b>。在词袋中,每个单词可以出现多次,而在词集中,每个词只能出现一次。为适应词袋模型,需要对函数 setOfWords2Vec() 稍加修改,修改后的函数为 bagOfWords2Vec() 。
如下给出了基于词袋模型的朴素贝叶斯代码。它与函数 setOfWords2Vec() 几乎完全相同,唯一不同的是每当遇到一个单词时,它会增加词向量中的对应值,而不只是将对应的数值设为 1 。
```python
def bagOfWords2VecMN(vocaList, inputSet):
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in inputSet:
returnVec[vocabList.index(word)] += 1
return returnVec
```
```python
#创建一个包含在所有文档中出现的不重复词的列表
def createVocabList(dataSet):

View File

@@ -11,6 +11,8 @@ from numpy import *
p(xy)=p(x|y)p(y)=p(y|x)p(x)
p(x|y)=p(y|x)p(x)/p(y)
"""
# 项目案例1: 屏蔽社区留言板的侮辱性言论
def loadDataSet():
@@ -213,6 +215,7 @@ def textParse(bigString):
listOfTokens = re.split(r'\W*', bigString)
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
def spamTest():
'''
Desc:
@@ -264,12 +267,13 @@ def spamTest():
def testParseTest():
print textParse(open('input/4.NaiveBayes/email/ham/1.txt').read())
# -----------------------------------------------------------------------------------
# 项目案例3: 使用朴素贝叶斯从个人广告中获取区域倾向
# 将文本文件解析成 词条向量
def setOfWords2VecMN(vocabList,inputSet):
returnVec=[0]*len(vocabList) #创建一个其中所含元素都为0的向量
returnVec=[0]*len(vocabList) # 创建一个其中所含元素都为0的向量
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)]+=1
@@ -279,9 +283,10 @@ def setOfWords2VecMN(vocabList,inputSet):
#文件解析
def textParse(bigString):
import re
listOfTokens=re.split(r'\W*',bigString)
listOfTokens=re.split(r'\W*', bigString)
return [tok.lower() for tok in listOfTokens if len(tok)>2]
#RSS源分类器及高频词去除函数
def calcMostFreq(vocabList,fullText):
import operator
@@ -343,7 +348,8 @@ def getTopWords(ny,sf):
for item in sortedNY:
print item[0]
if __name__ == "__main__":
testingNB()
# spamTest()
# testingNB()
spamTest()
# laTest()