Spark机器学习库(ML)之one-vs-All

​        相信大家一定了解多分类问题了,这里就不过多阐述了。前面说到的垃圾邮件识别是一个二分类问题,多分类就是多个类别。这次呢,我们使用20news-group来简单介绍如何实现这个多分类问题。

        我们先来介绍一下20news-group吧,它其实就是一个20个类别得新闻组,比如说体育、娱乐、国事...。下面给出下载地址,官网上解释很清楚。目录结构和上篇文章中得垃圾邮件是一样得,这里就不上图了。

下载地址:http://qwone.com/~jason/20Newsgroups/

        一般情况下,SVM有两种多分类方法,一种是one-vs-all,一种是pairwise。

one-vs-all: 假设我们有4个类,A\B\C\D,他的流程是:将A设为正类,然后BCD为一类,这就是一个二分类,记为PA,然后就是B为一类,ACD为一类,记为PB,依次类推。然后最后就有PA,PB,PC,PD的四个概率,然后取最大的。但是这种方法有种缺陷,训练集是1:K-1,这种情况下存在biased.因而不是很实用.

pairwise:也称one-vs-one,同样假设ABCD4个类,它是怎么操作的呢,他也是构建了多个二分类,但是是这样的,AB,AC,AD,BC,BD,CD一共六个。训练结果,在测试的时候则是分别对6个分类器进行测试,然后就是通过投票的形式,获取最终的类别。

        投票过程:

                 最开始A=B=C=D=0

                AB:假设AB分类器中,分类为A,则A=A+1,B=0,反之B=B+1,A=0

                AC:同理,假设为A,A=A+1,C=0,反之

                。。。

            ·  CD:假设结果为C,则C=C+1,D=0,反之

最后取数最大的哪一个作为分类结果。

这个方法虽然好,但是当类别非常多的时候,那么就会有n(n-1)/2个二分类,计算代价是非常昂贵的


接下来我们就通过Spark来实现一下one-vs-all的方式,Spark中也只实现了该方式。这里使用的是pipeline的方式来处理数据,以及模型的建立同时还能够通过保存模型的方式,这里我们没有对数据进行过多的预处理,使用的是tf-idf,这里也没有进行stopword的处理,效果上肯定还可以在进行提升。 


import org.apache.spark.ml.feature.{HashingTF, IDF, Tokenizer}

import org.apache.spark.ml.classification.{LogisticRegression, OneVsRest}

import org.apache.spark.ml.evaluation.MulticlassClassificationEvaluator

import org.apache.spark.ml.feature.IndexToString;

import org.apache.spark.ml.feature.StringIndexer

import org.apache.spark.ml.Pipeline

import org.apache.spark.ml.PipelineModel

import org.apache.spark.ml.PipelineStage

val dataDF = spark.sparkContext

                    .wholeTextFiles("file:///opt/data/20_newsgroups/*")

                    .map{case(p,c) =>(p.split("/").takeRight(2).head,c)}

                    .toDF("label","context")

val Array(trainData,validationData,testData) = dataDF.randomSplit(Array(0.7,0.2,0.1))

 val Array(trainData,validationData,testData) = dataDF.randomSplit(Array(0.7,0.2,0.1))

 val tokenizer = new Tokenizer().setInputCol("context").setOutputCol("words")

 val hashingTF = new HashingTF().setInputCol("words").setOutputCol("rawFeatures")

val idf = new IDF().setInputCol("rawFeatures").setOutputCol("features")

val labelIndexer = new StringIndexer().setInputCol("label").setOutputCol("indexedLabel").fit(dataDF)

// 构建one-vs-all的基分类器

val classifier = new LogisticRegression().setMaxIter(100).setTol(1E-6).setFitIntercept(true)

 // 初始化one-va-all模型

 val ovr = new OneVsRest().setClassifier(classifier).setFeaturesCol("features").setLabelCol("indexedLabel")

 val labelConverter = new IndexToString().setInputCol("prediction").setOutputCol("predictionLabel").setLabels(labelIndexer.labels)

val pipeline = new Pipeline().setStages(Array(tokenizer,hashingTF,idf,labelIndexer,ovr,labelConverter))

val model = pipeline.fit(trainData)

val predictionResultDF = model.transform(validationData)

val evaluator = new MulticlassClassificationEvaluator().setLabelCol("indexedLabel").setPredictionCol("prediction").setMetricName("accuracy")

val predictionAccuracy = evaluator.evaluate(predictionResultDF)

println("Test set accuracy = " + predictionAccuracy)

// save

model.write.overwrite().save("/tmp/saveModelonevsall")

//测试

val sameModel = PipelineModel.load("/tmp/saveModelonevsall")

val out = sameModel.transform(testData)

evaluator.evaluate(out)


迭代了100次,所以或有这么多,所以前面就要先缓存下数据,这样就会比较快了。 

看样子我们保存的模型还是有效果的。 

        我们再来看看使用NB的效果如何,我们知道nb可以做二分类也可以做多分类,且在词袋模型中效果还不错,对数据量的要求也不是特别的大。我们来试试。

        我们只需要更改pipeline就可以了,并不需要重写代码,只需要定义模型的实现,然后再管道中实现,我们只需要在原有的spark-shell下添加一下代码就可以了。当然也是可以移植到idea编译器中的哈


  import org.apache.spark.ml.classification.NaiveBayes

  val nb = new NaiveBayes().setFeaturesCol("features").setLabelCol("indexedLabel").setSmoothing(0.1)

val nbPipeline = new Pipeline().setStages(Array(tokenizer,hashingTF,idf,labelIndexer,nb,labelConverter))

val nbModel = nbPipeline.fit(trainData)

val predictionNB = nbModel.transform(validationData)

evaluator.evaluate(predictionNB)


从结果上来看比one-vs-all效果是要好那么一点 

然而更重要的是 NB在整个模型的构建中只花费了5s,然而one-vs-all至少运行了几分钟 

我们也可以试着去修改平滑度参数,查看准确率的变化,这个参数主要是防止过拟合的。

将学习率改成0.5


val nb = new NaiveBayes().setFeaturesCol("features").setLabelCol("indexedLabel").setSmoothing(0.5)

val nbPipeline = new Pipeline().setStages(Array(tokenizer,hashingTF,idf,labelIndexer,nb,labelConverter))

val nbModel = nbPipeline.fit(trainData)

val predictionNB = nbModel.transform(validationData)

evaluator.evaluate(predictionNB)


效果还是有点的。其他的就慢慢调试吧,这里就不演示了。当然这个平滑度也不是越大越好,越大容易欠拟合,越小容易过拟合。

好了,今天的分享就到这里结束啦。 

更多文章请关注公众号:

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在pyspark中计算余弦相似度可以使用Spark ML中的VectorAssembler和CosineSimilarity来实现。首先,使用VectorAssembler将需要计算相似度的特征向量组合成一个向量列。然后,使用CosineSimilarity计算这些向量之间的余弦相似度。 以下是一个示例代码: ```python from pyspark.ml.feature import VectorAssembler from pyspark.ml.feature import Normalizer from pyspark.ml.linalg import Vectors from pyspark.ml.feature import StringIndexer from pyspark.ml.feature import IndexToString from pyspark.ml.recommendation import ALS from pyspark.ml.evaluation import RegressionEvaluator from pyspark.ml.recommendation import ALSModel from pyspark.ml import Pipeline from pyspark.ml.feature import HashingTF, IDF from pyspark.ml.feature import CountVectorizer from pyspark.ml.feature import StopWordsRemover from pyspark.ml.feature import NGram from pyspark.ml.feature import Tokenizer from pyspark.ml.feature import PCA from pyspark.ml.feature import OneHotEncoder from pyspark.ml.feature import PCA # 创建一个DataFrame df = spark.createDataFrame([ (0, Vectors.dense([1.0, 0.5])), (1, Vectors.dense([2.0, 1.0])), (2, Vectors.dense([4.0, 2.0])), (3, Vectors.dense([6.0, 3.0])) ], ["id", "features"]) # 使用VectorAssembler将特征向量组合成一个向量列 assembler = VectorAssembler( inputCols=["features"], outputCol="vector" ) output = assembler.transform(df) # 使用CosineSimilarity计算余弦相似度 cosine_similarity = output.select("vector").crossJoin(output.select("vector")).toDF("v1", "v2").selectExpr("v1", "v2", "float(CosineSimilarity(v1, v2)) as similarity") cosine_similarity.show() ``` 这段代码中,我们首先创建一个包含特征向量的DataFrame,然后使用VectorAssembler将特征向量组合成一个向量列。接着,我们使用CosineSimilarity计算向量之间的余弦相似度。最后,我们将计算结果打印出来。 请注意,这只是一个示例代码,你需要根据你的实际数据和需求进行相应的修改和调整。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Spark机器学习——余弦相似性算法](https://blog.csdn.net/a805814077/article/details/113267214)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [推荐系统01--余弦相似度](https://blog.csdn.net/weixin_40008870/article/details/110766812)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值