朴素贝叶斯 (Bayes)
k-NN和决策树这类分类器的结果是非错即对(即明确给出数据预测类别),但有时我们希望分类器给出预测类别的概率估计值,这就引出了基于概率论的分类方法–朴素贝叶斯。(朴素的意思是在形式化过程中,只做最原始,最简单的假设)
在了解贝叶斯分类器之前,我们需要先了解什么是条件概率。
条件概率
条件概率:简单来说就是在A事件发生的条件下,B事件发生的概率,形式化描述为:
p
(
B
∣
A
)
p(B|A)
p(B∣A)
如果已知
p
(
A
∣
B
)
p(A|B)
p(A∣B)、
p
(
B
)
p(B)
p(B)及
p
(
A
)
p(A)
p(A),则根据贝叶斯准则可知:
p
(
B
∣
A
)
p(B|A)
p(B∣A) =
p
(
A
∣
B
)
p
(
B
)
p
(
A
)
\frac{p(A|B)p(B)}{p(A)}
p(A)p(A∣B)p(B)
那么怎么根据条件概率进行分类呢???
使用条件概率分类
如果用
p
1
(
c
1
∣
(
x
,
y
)
)
p_1(c_1|(x,y))
p1(c1∣(x,y))表示数据点
(
x
,
y
)
(x,y)
(x,y)属于类别
c
1
c_1
c1,用
p
2
(
c
2
∣
(
x
,
y
)
)
p_2(c_2|(x,y))
p2(c2∣(x,y))表示数据点
(
x
,
y
)
(x,y)
(x,y)属于类别
c
2
c_2
c2。则当
1)
p
1
(
c
1
∣
(
x
,
y
)
)
>
p
2
(
c
2
∣
(
x
,
y
)
)
p_1(c_1|(x,y)) > p_2(c_2|(x,y))
p1(c1∣(x,y))>p2(c2∣(x,y)) 时,
(
x
,
y
)
(x,y)
(x,y)属于类别
c
1
c_1
c1;
2)
p
1
(
c
1
∣
(
x
,
y
)
)
<
p
2
(
c
2
∣
(
x
,
y
)
)
p_1(c_1|(x,y)) < p_2(c_2|(x,y))
p1(c1∣(x,y))<p2(c2∣(x,y)) 时,
(
x
,
y
)
(x,y)
(x,y)属于类别
c
2
c_2
c2.
根据已知数据是可以计算出:
p
(
c
i
)
p(c_i)
p(ci)、
p
(
x
,
y
)
p(x,y)
p(x,y)、
p
(
(
x
,
y
)
∣
c
i
)
p((x,y)|c_i)
p((x,y)∣ci),因此:
p
i
(
c
i
∣
(
x
,
y
)
)
p_i(c_i|(x,y))
pi(ci∣(x,y)) =
p
(
(
x
,
y
)
∣
c
i
)
p
(
c
i
)
p
(
x
,
y
)
\frac{p((x,y)|c_i)p(c_i)}{p(x,y)}
p(x,y)p((x,y)∣ci)p(ci)
朴素贝叶斯的优缺点
优点:在数据较少的情况下仍然有效,可以处理多分类问题;
缺点:对于输入数据的准备方式较为敏感。
下面使用朴素贝叶斯进行文档分类。
词向量
计算机是看不懂我们所使用的单词或文字的,因此在进行文档分类之前,我们需要把文档中的单词或文字转换成计算机能够识别的数字形式,在此处需要将之处理成词向量的形式。
代码及结果如下(在utils.py模块中):
import numpy as np
def create_data():
'''
创建多个文档及其对应的标签
:return: 文档内容,文档对应的标签
'''
docs_context = [['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']]
docs_labels = [0, 1, 0, 1, 0, 1] # 1 is abusive, 0 not
return docs_context, docs_labels
def create_vocab_list(dataset):
"""
创建词汇表
:param dataset: 输入数据
:return: 输入数据中不重复的词汇
"""
vocab_set = []
for doc in dataset:
vocab_set = np.append(vocab_set, np.unique(doc))
return np.unique(vocab_set)
def set_of_words_2_vec(vocabList, inputSet):
'''
将输入转换成词向量的形式
:param vocabList: 词汇表
:param inputSet: 输入文档
:return: 输入文档对应的词向量形式
'''
re_vec = np.zeros((len(vocabList, )))
for word in inputSet:
if word in vocabList:
re_vec[np.argwhere(np.array(vocabList) == word)] = 1
else:
print('the word: %s is not in Vocabulary' % word)
return re_vec
if __name__ == "__main__":
docs_context, docs_labels = create_data()
vocabs = create_vocab_list(docs_context)
print('多文档生成的词汇表:', vocabs)
vec = set_of_words_2_vec(vocabs, docs_context[3])
print('输入文档对应的词向量:', vec)
# 多文档生成的词汇表: ['I' 'ate' 'buying' 'cute' 'dalmation' 'dog' 'flea' 'food' 'garbage' 'has'
# 'help' 'him' 'how' 'is' 'licks' 'love' 'maybe' 'mr' 'my' 'not' 'park'
# 'please' 'posting' 'problems' 'quit' 'so' 'steak' 'stop' 'stupid' 'take'
# 'to' 'worthless']
# 输入文档对应的词向量: [0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.
# 0. 0. 0. 1. 1. 0. 0. 1.] # ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him']
从词向量计算概率
我们重写条件概率公式,将
(
x
,
y
)
(x,y)
(x,y)替换为
w
w
w,
w
w
w表示一个向量,则:
p
i
(
c
i
∣
w
)
p_i(c_i|w)
pi(ci∣w) =
p
(
w
∣
c
i
)
p
(
c
i
)
p
(
w
)
\frac{p(w|c_i)p(c_i)}{p(w)}
p(w)p(w∣ci)p(ci)
p
(
c
i
)
p(c_i)
p(ci)可以通过类别
i
i
i中文档数除以总的文档数计算;
计算
p
(
w
∣
c
i
)
p(w|c_i)
p(w∣ci)需要用到朴素贝叶斯假设。假设将
w
w
w展开为一个一个独立特征,即所有词相互独立,也称条件独立性假设,则:
p
(
w
∣
c
i
)
=
p
(
w
0
,
w
1
,
w
2
.
.
.
w
n
∣
c
i
)
=
p
(
w
0
∣
c
i
)
p
(
w
1
∣
c
i
)
p
(
w
2
∣
c
i
)
.
.
.
p
(
w
n
∣
c
i
)
p(w|c_i)= p(w_0,w_1,w_2...w_n|c_i)=p(w_0|c_i)p(w_1|c_i)p(w_2|c_i)...p(w_n|c_i)
p(w∣ci)=p(w0,w1,w2...wn∣ci)=p(w0∣ci)p(w1∣ci)p(w2∣ci)...p(wn∣ci)
使用朴素贝叶斯进行文档分类的伪代码如下:
"""
Bayes算法的伪代码
1. 计算每个类别中的文档数目
2. 训练每篇文档:
3. for 文档类别:
4. 对每个类别:
5. 如果词条出现在文档中----->增加该词的计数值
6. 增加所有词条的计数值
7. 对每个类别:
8. 对每个词条:
9. 该词条的数目/总词条数目=条件概率
10. 返回每个类别的条件概率
"""
代码实现如下(在bayes.py模块中):
def cond_probability(train_matrix, train_category):
'''
计算条件概率, 此方法可以进行多分类
:param train_matrix: 所有文档词向量组成的祖矩阵
:param train_category: 文档类别
:return: 词向量中每个元素的概率,每种类别的占比
'''
num_train_docs = len(train_matrix) # 统计样本数量
num_words = len(train_matrix[0]) # 统计词汇量的大小
train_category_ = train_category[:] # 复制 train_category
num_category = len(np.unique(train_category_)) # 统计类别数量
pAbusive = np.zeros((num_category, 1))
for category, count in collections.Counter(train_category_).most_common(): # 计算每个类别的占比
pAbusive[category] = count / num_train_docs
p_num = np.ones((num_category, num_words)) # 初始化为1: 防止某一个概率为0,导致相乘结果为0
p_demon = np.ones((num_category, 1)) * 2
for i in range(num_train_docs):
p_num[train_category[i]] += train_matrix[i]
p_demon[train_category[i]] += np.sum(train_matrix[i])
p_vec = np.log(p_num / p_demon) # 取对数:防止过多较小数相乘,导致结果数值下溢出
del train_category_
return p_vec, pAbusive
求出来条件概率后进行预测(在bayes.py模块中):
def classifyNB(test_data, p_vec, p_class):
'''
对输入文档进行预测
:param test_data: 输入测试文档
:param p_vec: 词向量中每个元素的概率
:param p_class: 每种类别的占比
:return:
'''
p = np.reshape(np.sum(test_data * p_vec, axis=1), (2, 1)) + np.log(p_class)
return np.argmax(p)
测试代码:
from bayes import *
from utils import *
if __name__ == "__main__":
docs_context, docs_labels = create_data()
vocabs = create_vocab_list(docs_context)
train_mat = []
for doc in docs_context:
train_mat.append(set_of_words_2_vec(vocabs, doc))
p_vec, pAbusive = cond_probability(train_mat, docs_labels)
# print(p_vec)
# print(pAbusive)
# exit()
test_entry = ['love', 'my', 'dalmation']
thisDoc = set_of_words_2_vec(vocabs, test_entry)
print(test_entry, 'classified as: ', classifyNB(thisDoc, p_vec, pAbusive))
test_entry = ['stupid', 'garbage']
thisDoc = set_of_words_2_vec(vocabs, test_entry)
print(test_entry, 'classified as: ', classifyNB(thisDoc, p_vec, pAbusive))
# ['love', 'my', 'dalmation'] classified as: 0
# ['stupid', 'garbage'] classified as: 1