朴素贝叶斯分类
贝叶斯原理的由来:贝叶斯为了解决一个叫“逆向概率”问题写了一篇文章,尝试解答在缺乏太多可靠证据的情况下,怎样做出更符合数学逻辑的推测。
逆向概率:逆向概率是相对正向概率而言。正向概率的问题很容易理解,比如我们已经知道袋子里面有 N 个球,不是黑球就是白球,其中 M 个是黑球,那么把手伸进去摸一个球,就能知道摸出黑球的概率是多少。这是在了解了事情的全貌再做判断。在现实生活中,我们很难知道事情的全貌。贝叶斯则从实际场景出发,提了一个问题:如果我们事先不知道袋子里面黑球和白球的比例,而是通过我们摸出来的球的颜色,怎么判断出袋子里面黑白球的比例?
就是基于这个问题贝叶斯提出了贝叶斯原理。贝叶斯原理与其他统计学推断方法截然不同,它是建立在主观判断的基础上:在不了解所有客观事实的情况下,同样可以先估计一个值,然后根据实际结果不断进行修正。
贝叶斯原理中的几个概念:
先验概率:就是通过经验来判断事情发生的概率。比如北方的大雪季是11-12 月,就是通过往年的气候总结出来的经验,这个时候下大雪的概率就比其他时间高出很多。
后验概率:后验概率就是发生结果之后,推测原因的概率。比如说某人查出来了患有心脏病,那么患病的原因可能是A、B或C。患有心脏病是因为原因 A的概率就是后验概率。它是属于条件概率的一种。
条件概率:事件A在另外一个事件B已经发生条件下的发生概率,表示为 P(A|B),读作“在B发生的条件下A发生的概率”。比如原因A的条件下,患有心脏病的概率,就是条件概率。
似然函数(likelihood function)
在数理统计学中,似然函数是一种关于统计模型中的参数的函数,表示模型参数中的似然性。你可以把概率模型的训练过程理解为求参数估计的过程。举个例子,考虑投掷一枚硬币的实验。假如已知投出的硬币正面朝上的概率是 0.5,便可以知道投掷若干次后出现各种结果的可能性。比如说,投两次都是正面朝上的概率是0.25。
贝叶斯原理,实际上贝叶斯原理就是求解后验概率。贝叶斯的公式如下:
朴素贝叶斯:
朴素贝叶斯分类是常用的贝叶斯分类方法。 它是一种简单但极为强大的预测建模算法 。之所以称为朴素贝叶斯,是因为它假设每个输入变量是独立的。
朴素贝叶斯模型由两种类型的概率组成:
- 每个类别的概率 P(Cj);
- 每个属性的 条件概率 P(Ai|Cj)。
在朴素贝叶斯中,要统计的是属性的条件概率,即P(Ai|Cj)。
因为属性A可能会有很多,即
因为
所以
朴素贝叶斯分类常用于文本分类,如垃圾文本过滤、情感预测、推荐系统等。
常用的朴素贝叶斯算法:
高斯朴素贝叶斯 :特征变量是连续变量,符合高斯分布,比如说人的身高,物体的长度。连续变量属性,考虑概率密度函数,假定p(xi|c)∼N(μc,i,σ2c,i),其中μc,i,σ2c,i分别是c类样本在第i个属性上取值的均值和标准差,则有
多项式朴素贝叶斯 :特征变量是离散变量,符合多项分布,在文档分类中特征变量体现在一个单词出现的次数,或者是单词的 TF-IDF 值等。
伯努利朴素贝叶斯 :特征变量是布尔变量,符合 0/1 分布,在文档分类中特征是单词是否出现。
伯努利朴素贝叶斯是以文件为粒度,如果该单词在某文件中出现了即为 1,否则为 0。而多项式朴素贝叶斯是以单词为粒度,会计算在某个文件中的具体次数。而高斯朴素贝叶斯适合处理特征变量是连续变量,且符合正态分布(高斯分布)的情况。比如身高、体重这种自然界的现象就比较适合用高斯朴素贝叶斯来处理。而文本分类是使用多项式朴素贝叶斯或者伯努利朴素贝叶斯。
补码朴素贝叶斯:补码朴素贝叶斯(ComplementNB,cNB)是标准多项式朴素贝叶斯(MNB)算法的一种自适应算法,特别适用于不平衡的数据集。
处理技巧:
- 如果连续特征不是正态分布的,我们应该使用各种不同的方法将其转换正态分布。
- 如果测试数据集具有“零频率”的问题,应用平滑技术“拉普拉斯估计”修正数据集。
零概率问题:就是在计算实例的概率时,如果某个量x,在观察样本库(训练集)中没有出现过,会导致整个实例的概率结果是0。在文本分类的问题中,当一个词语没有在训练样本中出现,该词语调概率为0,使用连乘计算文本出现概率时也为0。这是不合理的,不能因为一个事件没有观察到就武断的认为该事件的概率是0。
- 删除重复出现的高度相关的特征,可能会丢失频率信息,影响效果。
- 朴素贝叶斯分类在参数调整上选择有限。建议把重点放在数据的预处理和特征选择。
拉普拉斯平滑:
就是对每个类别下所有划分的计数加1,这样如果训练样本集数量充分大时,并不会对结果产生影响,并且解决了零概率的尴尬局面。
其中ajl,代表第j个特征的第l个选择,代表第j个特征的个数,K代表种类的个数。
为1,这也很好理解,加入拉普拉斯平滑之后,避免了出现概率为0的情况,又保证了每个值都在0到1的范围内,又保证了最终和为1的概率性质。如:
长相特征的个数为帅,不帅,俩种情况,那么Sj为2,则最终概率p(长相帅|嫁)为4/8 (嫁的个数为6+特征个数为2)
朴素贝叶斯实例:
已知如下数据集:
序号 |
Age |
Work |
House |
Loan |
Class |
1 |
中年 |
否 |
否 |
一般 |
否 |
2 |
中年 |
否 |
否 |
好 |
否 |
3 |
中年 |
是 |
是 |
好 |
是 |
4 |
中年 |
否 |
是 |
非常好 |
是 |
5 |
老年 |
否 |
是 |
非常好 |
是 |
已知某人Age 中年、Work 是,House 是,Loan 一般,请问该人属于哪个分类?
分析:用朴素贝叶斯公式可把问题转化为:
P(Class | Age,Work ,House,Loan)= P(Age,Work,House,Loan| Class )* P(Class)/P(Age,Work,House,Loan)=
P(Age|Class)* P(Work |Class) * P(House |Class) * P(Loan |Class)* P(Class)/ [P(Age)*P( Work)* P( House) *P( Loan)]
1、数据处理
先把各个特征转换为数字化表示:
描述 |
编号 |
中年 |
0 |
老年 |
1 |
否 |
0 |
是 |
1 |
是 |
1 |
非常好 |
0 |
好 |
1 |
一般 |
2 |
上表表示为:
序号 |
Age |
Work |
House |
Loan |
Class |
1 |
0 |
0 |
0 |
2 |
0 |
2 |
0 |
0 |
0 |
1 |
0 |
3 |
0 |
1 |
1 |
1 |
1 |
4 |
0 |
0 |
1 |
0 |
1 |
5 |
1 |
0 |
1 |
0 |
1 |
代码实例:
输入:naviebays.txt
0 0 0 2 0
0 0 0 1 0
0 1 1 1 1
0 0 1 0 1
1 0 1 0 1
package sparkmlNaiveBayes;
import java.util.*;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.ml.classification.NaiveBayes;
import org.apache.spark.ml.classification.NaiveBayesModel;
import org.apache.spark.ml.evaluation.MulticlassClassificationEvaluator;
import org.apache.spark.ml.feature.VectorAssembler;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.RowFactory;
import org.apache.spark.sql.SparkSession;
import org.apache.spark.sql.types.DataTypes;
import org.apache.spark.sql.types.StructField;
import org.apache.spark.sql.types.StructType;
public class navieBayes {
public static void main(String[] args) {
SparkConf spconf=new SparkConf().setMaster("local[4]").setAppName("testNaiveByes");
SparkSession spsession=SparkSession.builder().config(spconf).getOrCreate();
String schemaString = "a b c d label";
List<StructField> fields = new ArrayList<>();
for (String fieldName : schemaString.split(" ")) {
StructField field = DataTypes.createStructField(fieldName, DataTypes.IntegerType, true);
fields.add(field);
}
StructType schema = DataTypes.createStructType(fields);
JavaRDD<String> javaRDDstr=spsession.sparkContext().textFile(".\\naviebays.txt",2).toJavaRDD();
JavaRDD<Row> rowRDD=javaRDDstr.map((Function<String, Row>) r->
{
String[] attributes = r.split(" ");
int[] arr = new int[attributes.length];
for (int i = 0; i < attributes.length; i++)
{
arr[i] = Integer.parseInt(attributes[i]);
}
return RowFactory.create(arr[0],arr[1],arr[2],arr[3],arr[4]);
} );
Dataset<Row> df1 = spsession.createDataFrame(rowRDD, schema);
df1.show();
VectorAssembler v=new VectorAssembler();
v.setInputCols( new String[]{"a","b","c","d"});
v.setOutputCol("items");
Dataset df2= v.transform(df1);
df2.show(false);
NaiveBayes nb = new NaiveBayes();
nb.setFeaturesCol("items");
nb.setLabelCol("label");
NaiveBayesModel model = nb.fit(df2);
Dataset<Row> predictions = model.transform(df2);
predictions.show(false);
MulticlassClassificationEvaluator evaluator = new MulticlassClassificationEvaluator()
.setLabelCol("label")
.setPredictionCol("prediction")
.setMetricName("accuracy");
double accuracy = evaluator.evaluate(predictions);
System.out.println("Test set accuracy = " + accuracy);
}
}