在 Scala Spark 中使用数据框的朴素贝叶斯多项式文本分类器

Posted

技术标签:

【中文标题】在 Scala Spark 中使用数据框的朴素贝叶斯多项式文本分类器【英文标题】:Naive-bayes multinomial text classifier using Data frame in Scala Spark 【发布时间】:2016-04-23 16:50:22 【问题描述】:

我正在尝试构建一个 NaiveBayes 分类器,将数据库中的数据加载为包含(标签、文本)的 DataFrame。 这是数据样本(多项标签):

label|             feature|
+-----+--------------------+
|    1|combusting prepar...|
|    1|adhesives for ind...|
|    1|                    |
|    1| salt for preserving|
|    1|auxiliary fluids ...|

我对标记化、停用词、n-gram 和 hashTF 使用了以下转换:

val selectedData = df.select("label", "feature")
// Tokenize RDD
val tokenizer = new Tokenizer().setInputCol("feature").setOutputCol("words")
val regexTokenizer = new   RegexTokenizer().setInputCol("feature").setOutputCol("words").setPattern("\\W")
val tokenized = tokenizer.transform(selectedData)
tokenized.select("words", "label").take(3).foreach(println)

// Removing stop words
val remover = new        StopWordsRemover().setInputCol("words").setOutputCol("filtered")
val parsedData = remover.transform(tokenized) 

// N-gram
val ngram = new NGram().setInputCol("filtered").setOutputCol("ngrams")
val ngramDataFrame = ngram.transform(parsedData) 
ngramDataFrame.take(3).map(_.getAs[Stream[String]]("ngrams").toList).foreach(println)

//hashing function
val hashingTF = new HashingTF().setInputCol("ngrams").setOutputCol("hash").setNumFeatures(1000)
val featurizedData = hashingTF.transform(ngramDataFrame)

转换的输出:

+-----+--------------------+--------------------+--------------------+------    --------------+--------------------+
|label|             feature|               words|            filtered|                  ngrams|                hash|
+-----+--------------------+--------------------+--------------------+------    --------------+--------------------+
|    1|combusting prepar...|[combusting, prep...|[combusting, prep...|    [combusting prepa...|(1000,[124,161,69...|
|    1|adhesives for ind...|[adhesives, for, ...|[adhesives, indus...| [adhesives indust...|(1000,[451,604],[...|
|    1|                    |                  []|                  []|                     []|        (1000,[],[])|
|    1| salt for preserving|[salt, for, prese...|  [salt, preserving]|   [salt   preserving]|  (1000,[675],[1.0])|
|    1|auxiliary fluids ...|[auxiliary, fluid...|[auxiliary, fluid...|[auxiliary fluids...|(1000,[661,696,89...|

要构建朴素贝叶斯模型,我需要将标签和特征转换为LabelPoint。以下方法我尝试将数据帧转换为 RDD 并创建标签点:

val rddData = featurizedData.select("label","hash").rdd

val trainData = rddData.map  line =>
  val parts = line.split(',')
  LabeledPoint(parts(0), parts(1))



val rddData = featurizedData.select("label","hash").rdd.map(r =>   (Try(r(0).asInstanceOf[Integer]).get.toDouble,   Try(r(1).asInstanceOf[org.apache.spark.mllib.linalg.SparseVector]).get))

val trainData = rddData.map  line =>
  val parts = line.split(',')
  LabeledPoint(parts(0).toDouble,   Vectors.dense(parts(1).split(',').map(_.toDouble)))

我收到以下错误:

 scala> val trainData = rddData.map  line =>
 |   val parts = line.split(',')
 |   LabeledPoint(parts(0).toDouble, Vectors.dense(parts(1).split(',').map(_.toDouble)))
 | 
 <console>:67: error: value split is not a member of (Double,    org.apache.spark.mllib.linalg.SparseVector)
     val parts = line.split(',')
                      ^
<console>:68: error: not found: value Vectors
     LabeledPoint(parts(0).toDouble,   Vectors.dense(parts(1).split(',').map(_.toDouble)))

编辑 1:

根据以下建议,我已经创建了 LabelPoint 并训练了模型。

val trainData = featurizedData.select("label","features")

val trainLabel = trainData.map(line =>  LabeledPoint(Try(line(0).asInstanceOf[Integer]).get.toDouble,Try(line(1).asInsta nceOf[org.apache.spark.mllib.linalg.SparseVector]).get))

val splits = trainLabel.randomSplit(Array(0.8, 0.2), seed = 11L)
val training = splits(0)
val test = splits(1)

val model = NaiveBayes.train(training, lambda = 1.0, modelType = "multinomial")

val predictionAndLabels = test.map  point => 
   val score = model.predict(point.features)
   (score, point.label)

使用 N-gram 和不使用 N-gram 以及不同的哈希特征编号时,我的准确率降低了 40% 左右。我的数据集包含 5000 行和 45 个多项式标签。有什么方法可以提高模型性能?在此先感谢

【问题讨论】:

【参考方案1】:

您不需要将您的featurizedData 转换为RDD,因为Apache Spark 有两个库MLMLLib,第一个与DataFrames 一起工作,而MLLib 工作使用RDDs。因此,您可以使用ML,因为您已经拥有DataFrame

为了实现这一点,您只需将列重命名为 (label, features),并适合您的模型,如 NaiveBayes 所示,示例如下。

df = sqlContext.createDataFrame([
    Row(label=0.0, features=Vectors.dense([0.0, 0.0])),
    Row(label=0.0, features=Vectors.dense([0.0, 1.0])),
    Row(label=1.0, features=Vectors.dense([1.0, 0.0]))])
nb = NaiveBayes(smoothing=1.0, modelType="multinomial")
model = nb.fit(df)

关于您得到的错误,是因为您已经有一个SparseVector,而该类没有split 方法。因此,考虑更多这一点,您的RDD 几乎具有您实际需要的结构,但您必须将Tuple 转换为LabeledPoint

有一些技巧可以提高性能,我想到的第一个是删除停用词(例如 the、a、an、to、though 等),第二个是数数文本中的不同单词,然后手动构造向量,即这是因为如果散列数较低,那么不同的单词可能具有相同的散列,因此性能不佳。

【讨论】:

感谢@alberto 的回复。如果我错了,请纠正我,我正在尝试使用特征化数据将数据转换为 LabelPoint。您能建议我将数据转换为 LabelPoint 的任何方法吗? @ManishV 阅读我留言的最后一部分,我给你一个提示:) 感谢@alberto 的建议,我已经更新了代码并创建了模型。你能建议我提高模型的性能吗?

以上是关于在 Scala Spark 中使用数据框的朴素贝叶斯多项式文本分类器的主要内容,如果未能解决你的问题,请参考以下文章

大数据:Spark mlib Naive bayes朴素贝叶斯分类之多元朴素贝叶斯源码分析

贝叶斯朴素贝叶斯及调用spark官网 mllib NavieBayes示例

基于的朴素贝叶斯的文本分类(附完整代码(spark/java)

Spark MLlib 中的朴素贝叶斯

Spark MLlib速成宝典模型篇04朴素贝叶斯Naive Bayes(Python版)

使用 Apache Spark MLlib 的朴素贝叶斯