基于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)多项式模型

p(c) = 类c下单词出现的总次数/整个训练样本的单词总数
p(tk|c) = 单词tk在类c的文档中出现的总次数+1/类c下单词总数+|V| 
|V|是单词表的数量,这是一种平滑手段

2)伯努利模型

p(c) = 类c下文件总数/整个训练样本的文件总数
p(tk|c) = (类c下包含单词tk的文件数+1)/类c文件数+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的话,还会一发现还可以手动设置后验概率输出到类别的阈值。

 

posted @ 2016-08-12 10:22  HOLD  阅读(608)  评论(0编辑  收藏  举报