贝叶斯分类算法
贝叶斯分类算法
朴素贝叶斯(Naive Bayes ,NB)算法是基于贝叶斯定理与特征条件独立假设的分类方法,该算法是有监督的学习算法,解决的是分类问题,是将一个未知样本分到几个预先已知类别的过程。
朴素贝叶斯的思想就是根据某些个先验概率计算Y变量属于某个类别的后验概率,也就是根据先前事件的有关数据估计未来某个事件发生的概率。
举例:
一个学校内有60%的学生是男生,40%的学生是女生。根据统计,男生总是穿长裤,女生则有一半穿长裤,一半穿裙子。
问题:
假设在校园中随机抽取一个穿长裤的学生,推断该学生是女生的概率?
已知:
P(男生) = 60%
P(女生) = 40%
P(长裤|女生) = 50%
P(裙子|女生) = 50%
求: P(女生|长裤)—穿长裤人数中是女生的概率?
要知道穿长裤的人是女生的概率,要知道穿长裤女生的人数,也要知道穿长裤的总人数,两者相除就是长裤中女生的概率。假设学校人数为U
穿长裤总人数 = 穿长裤的男生人数+穿长裤的女生人数
= 60% * U + 40% * U *50%
穿长裤女生的人数 = 40% * U * 50%
随机抽取一个穿长裤的学生是女生的概率
= 穿长裤女生的人数/穿长裤总人数
= 40% * U * 50% / ( 60% * U + 40% * U *50%)
= 0.25
假设学生穿长裤记作事件A,学生穿长裤的概率就是P(A)。学生是女生记作事件B,学生是女生的概率是P(B),求:抽取的这个穿长裤学生是女生的概率(P(B|A))?
= =0.25
朴素贝叶斯公式
也可以简写为:
其中:
P(A)叫做A事件的先验概率,即一般情况下,认为A发生的概率。
P(B|A)叫做似然度,是A假设条件成立的情况下发生B的概率。
P(A|B)叫做后验概率,在B发生的情况下发生A的概率,也就是要求的概率。P(B)叫做标准化常量,即在一般情况下,认为B发生的概率。
理解朴素贝叶斯
假设现在有一堆邮件,正常邮件的比例是80%,垃圾邮件的比例是20%,这堆邮件中,5%的邮件中出现Viagra单词,如果有一封邮件,这封邮件中包含Viagra单词,求这封邮件是垃圾邮件的概率。
显然不能使用5%*20%=1%得到这封邮件是垃圾邮件的概率,因为垃圾邮件中有可能出现Viagra也有可能不会出现Viagra单词。那么根据贝叶斯公式可得包含Viagra单词的邮件是垃圾邮件的概率为:
P(spam)是已知20%,P(Viagra)也是已知 5%,那么如果求出P(Viagra|spam)的概率,结果就可以知道。我们可以根据邮件的数据集统计得到单词频率表:
其中 P(Viagra|spam)表示在垃圾邮件中出现Viagra单词的概率,通过统计得出为4/20。可以得出如果一封邮件中有Viagra单词,这封邮件是垃圾邮件的概率为:
通过同样的计算可以得到,含有Viagra单词但不是垃圾邮件的概率为0.2。那么可以认为这封邮件是垃圾邮件的概率比较大。这里的Viagra可以理解为邮件中的一个特征。那么当一封邮件有额外更多的特征时,贝叶斯如何扩展?
假设所有历史邮件中只出现了4个单词,也就是4个特征,根据历史邮件统计的单词频率表如下:
假设现在给定一封邮件中有Viagra和Unsubscribe(取消订阅)两个单词,求这封邮件是垃圾邮件的概率、不是垃圾邮件的概率?
利用贝叶斯公式,我们可以得到:
是垃圾邮件的概率:
不是垃圾邮件的概率是
拉普拉斯估计
根据以上例子,假设有一封邮件这4个单词都出现了,求这封邮件是垃圾邮件的概率:
是垃圾邮件的概率为:
由于P(W3|spam)的概率为0/20,会导致整个结果是垃圾邮件的概率为0,那么就否定了其他单词出现的权重。
拉普拉斯估计本质上是给频率表中的每个单词的计数加上一个较小的数,这样就保证每一类中每个特征发生的概率非零。通常,拉普拉斯估计中加上的数值为1,这样就保证了每一个特征至少在数据中出现一次。
以上例子如果四个单词都出现情况下计算是否是垃圾邮件,出现P(W3|spam)的概率为0/20,可以增加4封垃圾邮件,使4封邮件中每个邮件中只有一个单词出现,这样就避免了垃圾邮件中有的单词出现概率为0的情况。同样在不是垃圾邮件中也增加4封,避免在正常邮件中出现有的单词出现概率为0的情况。
代码
sms_spam.txt文件的内容如下:
type,text
ham,you are having a good week. Just checking in
ham,K..give back my thanks.
ham,Am also doing in cbe only. But have to pay.
spam,"complimentary 4 STAR Ibiza Holiday or £10,000 cash needs your URGENT collection. 09066364349 NOW from Landline not to lose out! Box434SK38WP150PPM18+"
spam,okmail: Dear Dave this is your final notice to collect your 4* Tenerife Holiday or #5000 CASH award! Call 09061743806 from landline. TCs SAE Box326 CW25WX 150ppm
ham,Aiya we discuss later lar... Pick u up at 4 is it?
ham,Are you this much buzy
ham,Please ask mummy to call father
spam,Marvel Mobile Play the official Ultimate Spider-man game (£4.50) on ur mobile right now. Text SPIDER to 83338 for the game & we ll send u a FREE 8Ball wallpaper
ham,"fyi I'm at usf now, swing by the room whenever"
ham,"Sure thing big man. i have hockey elections at 6, shouldn€˜t go on longer than an hour though"
ham,I anything lor...
ham,"By march ending, i should be ready. But will call you for sure. The problem is that my capital never complete. How far with you. How's work and the ladies"
ham,"Hmm well, night night "
ham,K I'll be sure to get up before noon and see what's what
ham,Ha ha cool cool chikku chikku:-):-DB-)
python
from sklearn.naive_bayes import MultinomialNB
from sklearn.feature_extraction.text import CountVectorizer
print('*************************\nNaive Bayes\n*************************')
if __name__ == '__main__':
# 读取文本构建语料库
corpus = []
labels = []
corpus_test = []
labels_test = []
f = codecs.open("./sms_spam.txt", "rb")
count = 0
while True:
# readline() 方法用于从文件读取整行,包括 "\n" 字符。
line = f.readline().decode("utf-8")
# 读取第一行,第一行数据是列头,不统计
if count == 0:
count = count + 1
continue
if line:
count = count + 1
line = line.split(",")
label = line[0]
sentence = line[1]
corpus.append(sentence)
if "ham" == label:
labels.append(0)
elif "spam" == label:
labels.append(1)
if count > 5550:
corpus_test.append(sentence)
if "ham" == label:
labels_test.append(0)
elif "spam" == label:
labels_test.append(1)
else:
break
# 文本特征提取:
# 将文本数据转化成特征向量的过程
# 比较常用的文本特征表示法为词袋法
#
# 词袋法:
# 不考虑词语出现的顺序,每个出现过的词汇单独作为一列特征
# 这些不重复的特征词汇集合为词表
# 每一个文本都可以在很长的词表上统计出一个很多列的特征向量
# CountVectorizer是将文本向量转换成稀疏表示数值向量(字符频率向量) vectorizer 将文档词块化,只考虑词汇在文本中出现的频率
# 词袋
vectorizer = CountVectorizer()
# 每行的词向量,fea_train是一个矩阵
fea_train = vectorizer.fit_transform(corpus)
print("vectorizer.get_feature_names is ", vectorizer.get_feature_names())
print("fea_train is ", fea_train.toarray())
# vocabulary=vectorizer.vocabulary_ 只计算上面vectorizer中单词的tf(term frequency 词频)
vectorizer2 = CountVectorizer(vocabulary=vectorizer.vocabulary_)
fea_test = vectorizer2.fit_transform(corpus_test)
# print vectorizer2.get_feature_names()
# print fea_test.toarray()
# create the Multinomial Naive Bayesian Classifier
# alpha = 1 拉普拉斯估计给每个单词个数加1
clf = MultinomialNB(alpha=1)
clf.fit(fea_train, labels)
pred = clf.predict(fea_test);
for p in pred:
if p == 0:
print("正常邮件")
else:
print("垃圾邮件")
scala
sample_naive_bayes_data.txt的数据
0,1 0 0
0,2 0 0
0,3 0 0
0,4 0 0
1,0 1 0
1,0 2 0
1,0 3 0
1,0 4 0
2,0 0 1
2,0 0 2
2,0 0 3
2,0 0 4
import org.apache.spark.{ SparkConf, SparkContext }
import org.apache.spark.mllib.classification.{ NaiveBayes, NaiveBayesModel }
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.regression.LabeledPoint
object Naive_bayes {
def main(args: Array[String]) {
//1 构建Spark对象
val conf = new SparkConf().setAppName("Naive_bayes").setMaster("local")
val sc = new SparkContext(conf)
//读取样本数据1
val data = sc.textFile("./sample_naive_bayes_data.txt")
val parsedData = data.map { line =>
val parts = line.split(',')
LabeledPoint(parts(0).toDouble, Vectors.dense(parts(1).split(' ').map(_.toDouble)))
}
//样本数据划分训练样本与测试样本
val splits = parsedData.randomSplit(Array(0.5, 0.5), seed = 11L)
val training = splits(0)
val test = splits(1)
//新建贝叶斯分类模型模型,并训练 ,lambda 拉普拉斯估计
val model = NaiveBayes.train(training, lambda = 1.0)
//对测试样本进行测试
val predictionAndLabel = test.map(p => (model.predict(p.features), p.label))
val accuracy = 1.0 * predictionAndLabel.filter(x => x._1 == x._2).count() / test.count()
println(accuracy)
val result = model.predict(Vectors.dense(Array[Double](1,0,0)))
println("result = "+result)
//保存模型
// val ModelPath = "./naive_bayes_model"
// model.save(sc, ModelPath)
// val sameModel = NaiveBayesModel.load(sc, ModelPath)
}
}