Skip to content

Commit 1dcdb32

Browse files
authored
Merge pull request #136 from jiangzhonglian/master
完善 贝叶斯项目案例
2 parents 7654919 + cd73d93 commit 1dcdb32

2 files changed

Lines changed: 49 additions & 41 deletions

File tree

docs/4.朴素贝叶斯.md

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ def setOfWords2Vec(vocabList, inputSet):
215215

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

218-
首先可以通过类别 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) 来计算上述概率,这样就极大地简化了计算的过程。
218+
首先可以通过类别 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) 来计算上述概率,这样就极大地简化了计算的过程。
219219

220220
朴素贝叶斯分类器训练函数
221221

@@ -242,22 +242,35 @@ def _trainNB0(trainMatrix, trainCategory):
242242
p0Denom = 0.0
243243
p1Denom = 0.0
244244
for i in range(numTrainDocs):
245-
# 遍历所有的文件,如果是侮辱性文件,就计算此侮辱性文件中出现的侮辱性单词的个数
245+
# 是否是侮辱性文件
246246
if trainCategory[i] == 1:
247-
p1Num += trainMatrix[i] #[0,1,1,....]->[0,1,1,...]
247+
# 如果是侮辱性文件,对侮辱性文件的向量进行加和
248+
p1Num += trainMatrix[i] #[0,1,1,....] + [0,1,1,....]->[0,2,2,...]
249+
# 对向量中的所有元素进行求和,也就是计算所有侮辱性文件中出现的单词总数
248250
p1Denom += sum(trainMatrix[i])
249251
else:
250-
# 如果不是侮辱性文件,则计算非侮辱性文件中出现的侮辱性单词的个数
251252
p0Num += trainMatrix[i]
252253
p0Denom += sum(trainMatrix[i])
253254
# 类别1,即侮辱性文档的[P(F1|C1),P(F2|C1),P(F3|C1),P(F4|C1),P(F5|C1)....]列表
254-
# 即 在1类别下,每个单词出现次数的占比
255+
# 即 在1类别下,每个单词出现的概率
255256
p1Vect = p1Num / p1Denom# [1,2,3,5]/90->[1/90,...]
256257
# 类别0,即正常文档的[P(F1|C0),P(F2|C0),P(F3|C0),P(F4|C0),P(F5|C0)....]列表
257-
# 即 在0类别下,每个单词出现次数的占比
258+
# 即 在0类别下,每个单词出现的概率
258259
p0Vect = p0Num / p0Denom
259260
return p0Vect, p1Vect, pAbusive
261+
```
262+
263+
> 测试算法: 根据现实情况修改分类器
264+
265+
在利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算 p(w0|1) * p(w1|1) * p(w2|1)。如果其中一个概率值为 0,那么最后的乘积也为 0。为降低这种影响,可以将所有词的出现数初始化为 1,并将分母初始化为 2 (取1 或 2 的目的主要是为了保证分子和分母不为0,大家可以根据业务需求进行更改)。
266+
267+
另一个遇到的问题是下溢出,这是由于太多很小的数相乘造成的。当计算乘积 p(w0|ci) * p(w1|ci) * p(w2|ci)... p(wn|ci) 时,由于大部分因子都非常小,所以程序会下溢出或者得到不正确的答案。(用 Python 尝试相乘许多很小的数,最后四舍五入后会得到 0)。一种解决办法是对乘积取自然对数。在代数中有 ln(a * b) = ln(a) + ln(b), 于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。
268+
269+
下图给出了函数 f(x) 与 ln(f(x)) 的曲线。可以看出,它们在相同区域内同时增加或者减少,并且在相同点上取到极值。它们的取值虽然不同,但不影响最终结果。
260270

271+
![函数图像](/images/4.NaiveBayesian/NB_7.png )
272+
273+
```python
261274
def trainNB0(trainMatrix, trainCategory):
262275
"""
263276
训练数据优化版本
@@ -296,22 +309,9 @@ def trainNB0(trainMatrix, trainCategory):
296309
# 类别0,即正常文档的[log(P(F1|C0)),log(P(F2|C0)),log(P(F3|C0)),log(P(F4|C0)),log(P(F5|C0))....]列表
297310
p0Vect = log(p0Num / p0Denom)
298311
return p0Vect, p1Vect, pAbusive
299-
```
300-
301-
> 测试算法: 根据现实情况修改分类器
302312

303-
在利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概率,即计算 p(w0 | 1)p(w1 | 1)p(w2 | 1)。如果其中一个概率值为 0,那么最后的乘积也为 0。为降低这种影响,可以将所有词的出现数初始化为 1,并将分母初始化为 2 。
304-
305-
另一个遇到的问题是下溢出,这是由于太多很小的数相乘造成的。当计算乘积 p(w0 | ci)p(w1 | ci)p(w2 | ci)...p(wn | ci) 时,由于大部分因子都非常小,所以程序会下溢出或者得到不正确的答案。(用 Python 尝试相乘许多很小的数,最后四舍五入后会得到 0)。一种解决办法是对乘积取自然对数。在代数中有 ln(a * b) = ln(a) + ln(b), 于是通过求对数可以避免下溢出或者浮点数舍入导致的错误。同时,采用自然对数进行处理不会有任何损失。
306-
307-
下图给出了函数 f(x) 与 ln(f(x)) 的曲线。可以看出,它们在相同区域内同时增加或者减少,并且在相同点上取到极值。它们的取值虽然不同,但不影响最终结果。通过修改 return 前的两行代码,将上述做法用到分类器中:
308-
309-
```python
310-
p1Vect = log(p1Num /p1Denom)
311-
p0Vect = log(p0Num / p0Denom)
312313
```
313314

314-
![函数图像](/images/4.NaiveBayesian/NB_7.png )
315315

316316
> 使用算法: 对社区留言板言论进行分类
317317
@@ -331,10 +331,12 @@ def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
331331
:return: 类别1 or 0
332332
"""
333333
# 计算公式 log(P(F1|C))+log(P(F2|C))+....+log(P(Fn|C))+log(P(C))
334+
# 大家可能会发现,上面的计算公式,没有除以贝叶斯准则的公式的分母,也就是 P(w) (P(w) 指的是此文档在所有的文档中出现的概率)就进行概率大小的比较了,
335+
# 因为 P(w) 针对的是包含侮辱和非侮辱的全部文档,所以 P(w) 是相同的。
334336
# 使用 NumPy 数组来计算两个向量相乘的结果,这里的相乘是指对应元素相乘,即先将两个向量中的第一个元素相乘,然后将第2个元素相乘,以此类推。
335337
# 我的理解是:这里的 vec2Classify * p1Vec 的意思就是将每个词与其对应的概率相关联起来
336-
p1 = sum(vec2Classify * p1Vec) + log(pClass1)
337-
p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
338+
p1 = sum(vec2Classify * p1Vec) + log(pClass1) # P(w|c1) * P(c1) ,即贝叶斯准则的分子
339+
p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1) # P(w|c0) * P(c0) ,即贝叶斯准则的分子·
338340
if p1 > p0:
339341
return 1
340342
else:
@@ -403,21 +405,6 @@ Eugene
403405

404406
> 准备数据: 将文本文件解析成词条向量
405407
406-
文档词袋模型
407-
408-
我们将每个词的出现与否作为一个特征,这可以被描述为 <b>词集模型(set-of-words model)</b>。如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法被称为 <b>词袋模型(bag-of-words model)</b>。在词袋中,每个单词可以出现多次,而在词集中,每个词只能出现一次。为适应词袋模型,需要对函数 setOfWords2Vec() 稍加修改,修改后的函数为 bagOfWords2Vec() 。
409-
410-
如下给出了基于词袋模型的朴素贝叶斯代码。它与函数 setOfWords2Vec() 几乎完全相同,唯一不同的是每当遇到一个单词时,它会增加词向量中的对应值,而不只是将对应的数值设为 1 。
411-
412-
```python
413-
def bagOfWords2VecMN(vocaList, inputSet):
414-
returnVec = [0] * len(vocabList)
415-
for word in inputSet:
416-
if word in inputSet:
417-
returnVec[vocabList.index(word)] += 1
418-
return returnVec
419-
```
420-
421408
使用正则表达式来切分文本
422409

423410
```python
@@ -431,7 +418,7 @@ def bagOfWords2VecMN(vocaList, inputSet):
431418

432419
> 分析数据: 检查词条确保解析的正确性
433420
434-
> 训练算法: 使用我们之前建立的 trainNB() 函数
421+
> 训练算法: 使用我们之前建立的 trainNB0() 函数
435422
436423
```python
437424
def trainNB0(trainMatrix, trainCategory):
@@ -576,6 +563,21 @@ def spamTest():
576563

577564
> 准备数据: 将文本文件解析成词条向量
578565
566+
文档词袋模型
567+
568+
我们将每个词的出现与否作为一个特征,这可以被描述为 <b>词集模型(set-of-words model)</b>。如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法被称为 <b>词袋模型(bag-of-words model)</b>。在词袋中,每个单词可以出现多次,而在词集中,每个词只能出现一次。为适应词袋模型,需要对函数 setOfWords2Vec() 稍加修改,修改后的函数为 bagOfWords2Vec() 。
569+
570+
如下给出了基于词袋模型的朴素贝叶斯代码。它与函数 setOfWords2Vec() 几乎完全相同,唯一不同的是每当遇到一个单词时,它会增加词向量中的对应值,而不只是将对应的数值设为 1 。
571+
572+
```python
573+
def bagOfWords2VecMN(vocaList, inputSet):
574+
returnVec = [0] * len(vocabList)
575+
for word in inputSet:
576+
if word in inputSet:
577+
returnVec[vocabList.index(word)] += 1
578+
return returnVec
579+
```
580+
579581
```python
580582
#创建一个包含在所有文档中出现的不重复词的列表
581583
def createVocabList(dataSet):

src/python/4.NaiveBayes/bayes.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
p(xy)=p(x|y)p(y)=p(y|x)p(x)
1212
p(x|y)=p(y|x)p(x)/p(y)
1313
"""
14+
15+
1416
# 项目案例1: 屏蔽社区留言板的侮辱性言论
1517

1618
def loadDataSet():
@@ -213,6 +215,7 @@ def textParse(bigString):
213215
listOfTokens = re.split(r'\W*', bigString)
214216
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
215217

218+
216219
def spamTest():
217220
'''
218221
Desc:
@@ -264,12 +267,13 @@ def spamTest():
264267
def testParseTest():
265268
print textParse(open('input/4.NaiveBayes/email/ham/1.txt').read())
266269

270+
267271
# -----------------------------------------------------------------------------------
268272
# 项目案例3: 使用朴素贝叶斯从个人广告中获取区域倾向
269273

270274
# 将文本文件解析成 词条向量
271275
def setOfWords2VecMN(vocabList,inputSet):
272-
returnVec=[0]*len(vocabList) #创建一个其中所含元素都为0的向量
276+
returnVec=[0]*len(vocabList) # 创建一个其中所含元素都为0的向量
273277
for word in inputSet:
274278
if word in vocabList:
275279
returnVec[vocabList.index(word)]+=1
@@ -279,9 +283,10 @@ def setOfWords2VecMN(vocabList,inputSet):
279283
#文件解析
280284
def textParse(bigString):
281285
import re
282-
listOfTokens=re.split(r'\W*',bigString)
286+
listOfTokens=re.split(r'\W*', bigString)
283287
return [tok.lower() for tok in listOfTokens if len(tok)>2]
284288

289+
285290
#RSS源分类器及高频词去除函数
286291
def calcMostFreq(vocabList,fullText):
287292
import operator
@@ -343,7 +348,8 @@ def getTopWords(ny,sf):
343348
for item in sortedNY:
344349
print item[0]
345350

351+
346352
if __name__ == "__main__":
347-
testingNB()
348-
# spamTest()
353+
# testingNB()
354+
spamTest()
349355
# laTest()

0 commit comments

Comments
 (0)