基于naive bayes文本分类的spark实现
文本分类有很多种方法,朴素贝叶斯应该算是里面最容易的一种了吧。这篇文章简单介绍朴素贝叶斯的原理,然后是spark上的实现方法。
1,问题描述
现实中可能会一些问题,比如一个邮件是不是垃圾邮件?一个新闻是属于社会类还是科技类还是娱乐类?这些问题都可以抽象成:给定一些已经分类好的样本集合{(di,yi)|di是文本,yi是类别},来了一个新的文档dx,那么它该被分为哪类?
2,解决思路
解决一个机器学习问题的思路一般是:构建样本->提取特征->选择、训练模型->模型评估。对于文本分类问题呢,也可以按照这个思路。
(1)提取特征
要处理的对象是文本,我们需要将文本表示成可量化、可计算的形式。这里用一种非常简单的One-hot Representation方法。如果词典有|V|个单词的话,每个文章会被生成一个|V|维的向量vec。对于每一个出现在文档中的单词wi,vec[i]都会被赋予大于0的某个值ci,其它没有出现在文档中的词,都会被赋予0。ci可以有多种赋值方法,常见的有01,频率,tf-idf值等。如果vec是个二值向量,也就是单词出现的位置为0,不出现的位置为1,这时叫做伯努利模型。如果ci是i位置单词出现的频率,那么这时就是多项式模型,tf-idf算是一种加权过后的频率。下面是具体的计算方式:
1)多项式模型
2)伯努利模型
资料[1]里面总结了很多特征选择的方法,感兴趣的话可以去阅读。
(2)建立模型
模型真的很简单,用贝叶斯概率公式:\( P(y_j|d_i)=\frac{P(y_j)P(d_i|y_j)}{P(d_i)} \),在类别y1,y2...yn上分别算一下,取此概率最大的类别。之所以叫朴素贝叶斯,是因为假设特征之间是相互独立的,所以\(P(d_i|y_j)=P(w_1|y_j)*P(w_2|y_j)*...*P(w_n|y_j)\)。当然了,一篇文章词之间肯定不是独立的,但是没关系,朴素贝叶斯模型也可以用。
朴素贝叶斯模型总结一下:
1)它是一种线性模型,取对之后就变成加和了嘛
2)假设特征之间相互独立
3,spark上的实现
import org.apache.spark.ml.classification.NaiveBayes import org.apache.spark.{ml, SparkContext, SparkConf} import org.apache.spark.ml.{PipelineModel, Pipeline} import org.apache.spark.ml.classification.{NaiveBayesModel, NaiveBayes} import org.apache.spark.ml.evaluation.MulticlassClassificationEvaluator import org.apache.spark.ml.feature._ import org.apache.spark.mllib.linalg.{Vectors, Vector} import org.apache.spark.sql.{DataFrame, SaveMode, Row} import org.apache.spark.sql.functions._ import org.apache.spark.sql.hive.HiveContext object NB { def bayesClassifierTest(dataSeg:DataFrame,sc:SparkContext,hiveCont:HiveContext) { import hiveCont.implicits._ val labelIndexer = new StringIndexer() .setInputCol("new_final_type") .setOutputCol("indexedLabel") .fit(dataSeg) //构建特征 val regexTokenizer = new RegexTokenizer() .setInputCol("food_name_seg") .setOutputCol("words") .setPattern(",") val hashingTF = new HashingTF() .setInputCol(regexTokenizer.getOutputCol) .setOutputCol("features") //分类器 val nb = new ml.classification.NaiveBayes() .setLabelCol(labelIndexer.getOutputCol) .setFeaturesCol(hashingTF.getOutputCol) val labelConverter = new IndexToString() .setInputCol("prediction") .setOutputCol("predictedLabel") .setLabels(labelIndexer.labels) val splits = dataSeg.randomSplit(Array(0.7, 0.3), seed = 11L) val dataTraining = splits(0) val dataTest = splits(1) //训练模型 val pipeline = new Pipeline().setStages(Array(labelIndexer,regexTokenizer,hashingTF,nb,labelConverter)) val model = pipeline.fit(dataTraining) //预测数据 val predictionResultDF = model.transform(dataTest) //计算准确率 val evaluator = new MulticlassClassificationEvaluator() .setLabelCol("indexedLabel") .setPredictionCol("prediction") .setMetricName("precision") val predictionAccuracy = evaluator.evaluate(predictionResultDF) println("Testing Error = " + (1.0 - predictionAccuracy)) } }
基于朴素贝叶斯的文本分类到此为止,以后会接着探索其它的文本分类模型。
这里是一些非常好的学习文本分类的资料:
[1]文本分类相关综述性质的中文文章
上篇:http://www.infoq.com/cn/articles/machine-learning-automatic-classification-of-text-data
下篇:http://www.infoq.com/cn/articles/machine-learning-automatic-classification-of-text-data-part2?utm_source=infoq&utm_medium=related_content_link&utm_campaign=relatedContent_articles_clk
[2]一篇文章彻底理解朴素贝叶斯
http://sebastianraschka.com/Articles/2014_naive_bayes_1.html
[3]spark关于naive bayes的文档
http://spark.apache.org/docs/latest/mllib-naive-bayes.html,这是mlilib的,上文的代码用的是ml里面的。
spark上naive bayes的模型可以调节的参数不多,一个是模型的选择,是多项式模型还是伯努利模型,一个是additive smooth的平滑参数,如果看api的话,还会一发现还可以手动设置后验概率输出到类别的阈值。