朴素贝叶斯理论概述
朴素贝叶斯(navie bayes)
是贝叶斯决策理论的一部分,只考虑最简单的假设,用 Python 将文本切分为词向量,然后利用词向量对文档分类。
- 优点:在数据较少的情况下仍然有效,可以处理多类别问题。
- 缺点:对于输入数据的准备方式较为敏感。
- 适用数据类型:标称型数据。
和 k-近邻算法 和 决策树 相比,都属于适用于标称型数据的分类器。
考虑下面的情形:
有一个数据集,它由两类数据组成,我们现在用 p1(x,y) 表示数据点(x,y)属于类别1的概率,用 p2(x,y) 表示数据点(x,y)属于类别2的概率。
那么对于一个新数据点(x,y),显然
- 如果 p1(x,y) > p2(x,y) ,那么类别为1。
- 如果 p2(x,y) > p1(x,y) ,那么类别为2。
也就是说,我们会选择高概率对应的类别。这就是贝叶斯决策理论的核心思想。
如果不用贝叶斯决策理论:
- 使用第1章的 kNN,进行1000次距离计算;
- 使用第2章的决策树,分别沿x轴、y轴划分数据;
- kNN算法效率会非常低
- 决策树在划分数据时会遇到困难,选出最优分类的时候不能很好解决。倘若不止两个轴,那么计算量更是成倍增加。
条件概率
贝叶斯准则:
其中 x
是一个向量,对于这个例子
比较 i 取 (0,1)的时候的大小来判断。结合公式,判断分子
p
(
x
,
y
∣
c
i
)
p(x,y|c_i)
p(x,y∣ci)和
p
(
c
i
)
p(c_i)
p(ci)即可。
p
(
c
i
)
p(c_i)
p(ci) 即为
c
i
c_i
ci除以总数。
p
(
x
,
y
∣
c
i
)
p(x,y|c_i)
p(x,y∣ci) 即在
c
i
c_i
ci条件下统计(x,y)出现的概率。
用朴素贝叶斯进行文档分类
后面的例子都可以用文档doc
的模型,整个文档如电子邮件是一个实例,文档中的某些元素构成特征。我们可以统计文档中出现的词word
,并用它出现或不出现,出现次数作为特征。这样就可以用一个长度统一,内容是特征的向量来描述文档。
朴素贝叶斯中朴素naive
的含义是每个特征互相独立,所以根据乘法原理,总概率为各特征相乘。
Python中为了防止下溢出通常计算log(prob)
,把相乘操作转换为相加。
文本分类代码
一般步骤:
1.构建词向量
2.构造包含词向量的 train_matrix
3.train_NB() 计算出词分量的概率
4.构造inputset的词分量
5.调用classify_NB,用 词分量概率和 inputset的词分量 判断结果
from numpy import *
# 创建数据集 class_vec相等于 label
def load_dataset():
posting_list = [['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']]
class_vec = [0, 1, 0, 1, 0, 1] # 1 is abusive, 0 not
return posting_list, class_vec
# 创建包含所有word的list 中间变量
def create_vocab_list(posting_list):
vocab_set = set([])
for i in posting_list:
vocab_set = vocab_set | set(i)
return list(vocab_set)
# 创建向量
# wordset to vec
def docs_2_vec(vocab_list, docs):
vec = [0] * len(vocab_list)
for i in docs:
if i in vocab_list:
vec[vocab_list.index(i)] += 1 # 出现则记为1
else:
print('the word: {} is not in my vocabulary!\n'.format(i))
return vec
# 计算概率
# abusive : 侮辱性
def train_NB0(train_matrix, train_category):
num_docs = len(train_matrix)
num_words = len(train_matrix[0])
prob_abusive = sum(train_category) / float(num_docs)
# init the num ,
# 分子初始化为1
num_0 = ones(num_words)
num_1 = ones(num_words)
# 分母 denom=2
denom_0 = 2.0
denom_1 = 2.0
for i in range(num_docs):
# 计算不同分类下的概率
if train_category[i] == 1:
denom_1 += sum(train_matrix[i])
num_1 += train_matrix[i]
else:
denom_0 += sum(train_matrix[i])
num_0 += train_matrix[i]
prob_1_vec = num_1 / denom_1 # add log()
prob_0_vec = num_0 / denom_0
return prob_1_vec, prob_0_vec, prob_abusive
def classify_NB(input_vec, prob_1_vec, prob_0_vec, prob_abusive):
# 相乘 取对数
prob_1 = sum(input_vec * prob_1_vec) + log(prob_abusive)
prob_0 = sum(input_vec * prob_0_vec) + log(1.0 - prob_abusive)
if prob_1 > prob_0:
return 1
else:
return 0
def test_NB():
post_list, class_vec = load_dataset()
vocab_list = create_vocab_list(post_list)
train_matrix = []
for docs in post_list:
train_matrix.append(docs_2_vec(vocab_list, docs))
prob_1_vec, prob_0_vec, prob_abusive = train_NB0(array(train_matrix), class_vec)
print(prob_1_vec)
print(prob_0_vec)
# 2 test_docs
test_docs_1 = ['love', 'my', 'dalmation']
test_docs_2 = ['stupid', 'garbage']
test_1_vec = docs_2_vec(vocab_list, test_docs_1)
test_2_vec = docs_2_vec(vocab_list, test_docs_2)
ans_1 = classify_NB(test_1_vec, prob_1_vec, prob_0_vec, prob_abusive)
ans_2 = classify_NB(test_2_vec, prob_1_vec, prob_0_vec, prob_abusive)
print('test_docs_1 is {}\n', 'negative' if ans_1 else 'positive')
print('test_docs_2 is {}\n', 'negative' if ans_2 else 'positive')
if __name__ == '__main__':
test_NB()
文档词袋模型
set_of_words
为文档词集模型
def docs_2_vec(vocab_list, docs):
vec = [0] * len(vocab_list)
for i in docs:
if i in vocab_list:
vec[vocab_list.index(i)] = 1# 代码中已改为 +=1
else:
print('the word: {} is not in my vocabulary!\n'.format(i))
return vec
def bagofwords_2_vec(vocab_list, docs):
...
vec[vocab_list.index(i)] += 1
...
词袋模型的意义是特征可能多次出现,意味着它包含着是否出现在外当中以外的更多意义。
实例:朴素贝叶斯过滤垃圾邮件
和样例相比多了两个过程
- 准备数据:切分文本
- 测试算法(随机采样数据)
from NaiveBayes import *
def bagofwords_2_vec(vocab_list, docs):
vec = [0] * len(vocab_list)
for word in docs:
if word in vocab_list:
vec[vocab_list.index(word)] += 1
else:
print('word {} is not in vocab_list!\n'.format(word))
return vec
def test_parse(bigstring):
import re
tokens_list = re.split(r'\W*', bigstring)
return [token.lower() for token in tokens_list if len(token) > 2]
# spam : 垃圾邮件
# ham : not a spam && disired e-mail
def spam_test():
doc_list = []
class_list = []
for i in range(1, 26):
file=open('email/spam/{}.txt'.format(i),'r')
word_list = test_parse(file.read())
doc_list.append(word_list)
class_list.append(1)
file = open('email/ham/{}.txt'.format(i), 'r')
word_list = test_parse(file.read())
doc_list.append(word_list)
class_list.append(0)
vocab_list = create_vocab_list(doc_list)
# select 10 set in vocab as test set
trainset = list(range(50))
testset = [] # include doc_index
for i in range(10):
rand_index = int(random.uniform(0, len(trainset)))
testset.append(rand_index)
del (trainset[rand_index])
train_matrix = []
train_class = []
for doc_index in trainset:
train_matrix.append(bagofwords_2_vec(vocab_list, doc_list[doc_index]))
train_class.append(class_list[doc_index])
prob_1_vec, prob_0_vec, prob_abusive = train_NB0(train_matrix, train_class)
# test
error_count = 0.
for doc_index in testset:
test_vec = bagofwords_2_vec(vocab_list, doc_list[doc_index])
ans = classify_NB(array(test_vec), prob_1_vec, prob_0_vec, prob_abusive)
if ans != class_list[doc_index]:
error_count += 1
print('classification error {}'.format(doc_list[doc_index]))
print('error rate is {}!\n'.format(float(error_count) / len(testset)))
if __name__ == '__main__':
spam_test()