ML3

Naive Beyes Classifier

朴素贝叶斯方法的学习与分类

设输入空间$\chi \subseteq {R}^{n}$为n维向量的集合,输出空间为类标记集合$\gamma={ c_{1}, c_{2},\cdots,c_{K} }$。输入为特征向量$x \in \chi$, 输出位类标记$y \in \gamma$。X是定义在输入空间$\chi$上的随机向量,Y是定义在输出空间$\gamma$上的随机向量。P(X,Y)为X和Y的联合概率分布。
训练数据集 $T = {(x_{1},y_{1}),(x_{2},y_{2}),\cdots,(x_{N},y_{N})}$由P(X,Y)独立同分布产生。
先验概率分布 $P(Y=c_{k}), k = 1,2,\cdots,K$
条件概率分布 $P(X=x|Y=c_{k})$
条件独立性假设,则$$P(X=x|Y=c_{k})=\prod_{j=1}^{n}P \left( \left. X^{(j)}=x^{(j)} \right| Y=c_{k}\right)$$
朴素贝叶斯方法实际上学习到生成数据的机制,属于生成模型。利用朴素贝叶斯分类时,对于给定的输入$x$,通过学习到的模型计算后验概率$$P(Y=c_{k}|X=x)=\frac{P(X=x|Y=c_{k})P(Y=c_{k})}{\sum_{k}P(X=x|Y=c_{k})P(Y=c_{k})}$$
分类器(后验概率最大化准则,即期望风险最小化准则)为:
$$
y = \arg \mathop{\max}{c{k}}P(Y=c_{k})\prod_{j}P \left( \left. X^{(j)}=x^{(j)} \right| Y=c_{k}\right)
$$

朴素贝叶斯方法的参数估计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue May 18 19:02:27 2020

@author: chen
"""
import numpy as np
from functools import reduce

"""
函数说明:创建实验样本

Parameters:
None

Returns:
postingList - 样本词表
classVec - 类别标签向量
"""
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']]
# 类别标签,1代表侮辱性词汇,0代表不是
classVec = [0, 1, 0, 1, 0, 1]
# 返回样本词表,类别标签向量
return postingList, classVec

"""
函数说明:将切分的实验样本词条整理成不重复的词汇表

Parameters:
dataSet - 整理的样本数据集

Returns:
vocabSet - 返回不重复的词汇表
"""
def createVocabList(dataSet):
# 创建一个空的不重复列表
# set是一个无序且不重复的元素集合
vocabSet = set([])
for document in dataSet:
# 取并集
vocabSet = vocabSet | set(document)
return list(vocabSet)

"""
函数说明:根据vocabList词汇表,将inputSet向量化,向量的每个元素为1或0

Parameters:
vocabList - createVocabList返回的列表
inputSet - 切分的词条列表

Returns:
returnVec - 文档向量,词集模型
"""
def setOfWords2Vec(vocabList, inputSet):
# 创建一个其中所含元素都为0的向量
returnVec = [0] * len(vocabList)
# 遍历每个词条
for word in inputSet:
if word in vocabList:
# 如果词条存在于词汇表中,则置1
# index返回word出现在vocabList中的索引
# 若改为+=则就是基于词袋的模型
returnVec[vocabList.index(word)] = 1
else:
print("the word: %s is not in my Vocabulary" % word)
# 返回文档向量
return returnVec


"""
函数说明:朴素贝叶斯分类器训练函数

Parameters:
trainMatrix - 训练文档矩阵,即setOfWords2Vec返回的returnVec构成的矩阵
trainCategory - 训练类标签向量,即loadDataSet返回的classVec

Returns:
p0Vect - 侮辱类的条件概率数组
p1Vect - 非侮辱类的条件概率数组
pAbusive - 文档属于侮辱类的概率
"""
def trainNB0(trainMatrix, trainCategory):
# 计算训练文档数目
numTrainDocs = len(trainMatrix)
# 计算每篇文档的词条数目
numWords = len(trainMatrix[0])
# 文档属于侮辱类的概率
pAbusive = sum(trainCategory)/float(numTrainDocs)
# 创建numpy.zeros数组,词条出现数初始化为0
# p0Num = np.zeros(numWords)
# p1Num = np.zeros(numWords)
# 创建numpy.ones数组,词条出现数初始化为1,拉普拉斯平滑
p0Num = np.ones(numWords)
p1Num = np.ones(numWords)
# 分母初始化为0
# p0Denom = 0.0
# p1Denom = 0.0
# 分母初始化为2,拉普拉斯平滑
p0Denom = 2.0
p1Denom = 2.0
for i in range(numTrainDocs):
# 统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)...
if trainCategory[i] == 1:
# 统计所有侮辱类文档中每个单词出现的个数
p1Num += trainMatrix[i]
# 统计一共出现的侮辱单词的个数
p1Denom += sum(trainMatrix[i])
# 统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)...
else:
# 统计所有非侮辱类文档中每个单词出现的个数
p0Num += trainMatrix[i]
# 统计一共出现的非侮辱单词的个数
p0Denom += sum(trainMatrix[i])
# 每个侮辱类单词分别出现的概率
# p1Vect = p1Num / p1Denom
# 取对数,防止下溢出
p1Vect = np.log(p1Num / p1Denom)
# 每个非侮辱类单词分别出现的概率
# p0Vect = p0Num / p0Denom
# 取对数,防止下溢出
p0Vect = np.log(p0Num / p0Denom)
# 返回属于侮辱类的条件概率数组、属于非侮辱类的条件概率数组、文档属于侮辱类的概率
return p0Vect, p1Vect, pAbusive


"""
函数说明:朴素贝叶斯分类器分类函数

Parameters:
vec2Classify - 待分类的词条数组
p0Vec - 侮辱类的条件概率数组
p1Vec - 非侮辱类的条件概率数组
pClass1 - 文档属于侮辱类的概率

Returns:
0 - 属于非侮辱类
1 - 属于侮辱类
"""
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
# 对应元素相乘
# p1 = reduce(lambda x,y:x*y, vec2Classify * p1Vec) * pClass1
# p0 = reduce(lambda x,y:x*y, vec2Classify * p0Vec) * (1.0 - pClass1)
# 对应元素相乘,logA*B = logA + logB所以这里是累加
p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)
p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
# print('p0:', p0)
# print('p1:', p1)
if p1 > p0:
return 1
else:
return 0


"""
函数说明:测试朴素贝叶斯分类器

Parameters:
None

Returns:
None
"""
def testingNB():
# 创建实验样本
listOPosts, listclasses = loadDataSet()
# 创建词汇表,将输入文本中的不重复的单词进行提取组成单词向量
myVocabList = createVocabList(listOPosts)
trainMat = []
for postinDoc in listOPosts:
# 将实验样本向量化若postinDoc中的单词在myVocabList出现则将returnVec该位置的索引置1
# 将6组数据list存储在trainMat中
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
# 训练朴素贝叶斯分类器
p0V, p1V, pAb = trainNB0(np.array(trainMat), np.array(listclasses))
# 测试样本1
testEntry = ['love', 'my', 'dalmation']
# 测试样本向量化返回这三个单词出现位置的索引
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
if classifyNB(thisDoc, p0V, p1V, pAb):
# 执行分类并打印结果
print(testEntry, '属于侮辱类')
else:
# 执行分类并打印结果
print(testEntry, '属于非侮辱类')
# 测试样本2
testEntry = ['stupid', 'garbage']
# 将实验样本向量化
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
if classifyNB(thisDoc, p0V, p1V, pAb):
# 执行分类并打印结果
print(testEntry, '属于侮辱类')
else:
# 执行分类并打印结果
print(testEntry, '属于非侮辱类')

def main():
testingNB()


if __name__ == '__main__':
main()

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!