赞
踩
特征向量的提取请看我的之前的一篇博客:
https://blog.csdn.net/LOG_IN_ME/article/details/103047796
特征向量提取结果如下图:
这个DataFrame的“TF-IDF”列就是提取的特征向量。
我们把该列取出来:
- val TFIDFResult: DataFrame = idfModel.transform(featureVec).select("TF-IDF")
- TFIDFResult.show()
TFIDFResult字面量即为最后一列的DataFrame,每一行的数据类型为GenericRowWithSchema(GenericRowWithSchema继承自 org.apache.spark.sql.Row)。
关于GenericRowWithSchema的操作可参考:https://blog.csdn.net/Code_LT/article/details/87719115
println(TFIDFResult.first())
我们希望得到一个向量,而上图所示得到的是一个row,两者格式有差别,row外面套有中括号[],里面的()才是向量。
所以可以使用row的.get()方法按索引取出GenericRowWithSchema里的值:
println(TFIDFResult.first().get(0))
取出来后发现GenericRowWithSchema里面是一个org.apache.spark.ml.linalg.SparseVector类型的稀疏向量,才是我们需要的。
(由于我提取特征向量是使用 import org.apache.spark.ml.feature.{HashingTF, IDF} ml库下的类,所以装在GenericRowWithSchema里的是ml库下的向量,并且是稀疏向量SparseVector(因为我提取特征哈希的桶数远大于分得的词数)。)
【悟】
一开始我一直想要把()里第3项也就是TFIDF值的列表取出来计算,我一直以为那个是特征向量,但等我输出数据类型的时候才顿悟:
()整体就是SparseVector类型,第一项2000意义是这个稀疏向量是2000维的(哈希的时候桶数为2000),第二项是该词袋下每个词的哈希值,第三项里面是与第二项一一对应的TFIDF值。整个()描述的就是一个向量整体!
有了特征向量,下面就要计算特征向量之间的相似。
spark.mllib支持两种相关系数的计算,一个是皮尔逊系数(pearson),一个是斯皮尔曼系数(spearman)。这两个系数都是相关系数大于0表示两个向量正相关,小于0表示两个向量负相关,0表示两个向量没有相关性。
【注意】spark.ml暂时还没有这个相关系数计算的API,这是一个大麻烦。
关于这两个系数比较解释可以参考以下三篇博客:
https://blog.csdn.net/lambsnow/article/details/79972145
https://blog.csdn.net/t15600624671/article/details/77247822
https://blog.csdn.net/qq_36384657/article/details/100117039
pearson:
spearman:
总体来说:皮尔逊系数可以理解为对两个向量进行归一化后,计算其余弦距离。但是变量的标准差不能为0(分母不能为0),也就是说你的两个变量中任何一个的值不能都是相同的;实验数据之间的差距不能太大,或者说皮尔森相关性系数受异常值的影响比较大;并且数据是建立在线性相关的基础上,一般指直线,若是曲线则要求两变量数据的间距相同或者数据取自于正态分布数据中。
斯皮尔曼相关性系数,通常也叫斯皮尔曼秩相关系数。“秩”,可以理解成就是一种顺序或者排序,那么它就是根据原始数据的排序位置进行求解,这种表征形式就没有了求皮尔森相关性系数时那些限制。对极端数据,异常数据不敏感。
我们将采用斯皮尔曼相关性系数。
由于spark.mllib库使用RDD,而我们使用spark.ml库的DataFrame得到特征向量,要想使用mllib库下的相关性计算API,就必须要把DataFrame转换成RDD。
使用DataFrame的.rdd
- //特征向量rdd
- var feature:RDD[Vector] = TFIDFResult
- .rdd.map(row=>{
- org.apache.spark.mllib.linalg.Vectors.fromML(row.get(0).asInstanceOf[org.apache.spark.ml.linalg.Vector])
- })
【注意】
1、首先feature要显式声明数据类型:RDD[Vector](意思是RDD里每一行数据类型都是Vector),否则会被判为Any类型,在后面调用计算相关系数方法的时候参数类型不符。(一般而言scala变量不需要声明类型,但是这里需要明确变量类型,这是一个坑。)
2、.rdd是将TFIDFResult转换为RDD类型。
3、.map()操作是对于RDD每一行执行{}里的函数操作,即将{}(row)里面的()(向量)提取出来,spark会智能识别函数最后一行作为返回值。
4、重点来了:
row.get(0)取出的SparseVector前面已经说过是spark.ml.linalg.SparseVector,是ml库里的,而feature:RDD[Vector]由于是spark.mllib的RDD,所以对应的要求里面的Vector是mllib库里的spark.mllib.linalg.Vector。所以如何将ml库的SparseVector转换成mlllib库的Vector是一个非常大的问题。
以下一篇文档挽救了这个数据类型转换!
http://spark.apache.org/docs/latest/ml-migration-guides.html
该文档讲述的是scala下进行ml与mllib两个库之间相应数据类型的转换,同时也提供了Java和Python下的转换方法。
按照文档里所提供的方法,我使用org.apache.spark.mllib.linalg.Vectors.fromML()方法可以将org.apache.spark.ml.linalg.Vector类型数据转换成org.apache.spark.mllib.linalg.Vector。
但是row.get(0)目前是org.apache.spark.ml.linalg.SparseVector而非org.apache.spark.ml.linalg.Vector,那我们需要先做ml库下的数据类型转换,同一库下的数据类型强制转换使用.asInstanceOf[]。
row.get(0).asInstanceOf[org.apache.spark.ml.linalg.Vector]//spark.ml.linalg.SparseVector --> spark.ml.linalg.Vector
注意不可直接这样写:
row.get(0).asInstanceOf[Vector]
因为我们前面引入的是org.apache.spark.mllib.linalg._,所以只写Vector会被默认为mllib库下的Vector,需要显式声明哪个包下的类。
5、输出RDD看看是什么样子:
feature.foreach(println) //RDD输出使用foreach不用print,print输出的是数据类型
- //计算相似度矩阵
- val correlMatrix: Matrix = Statistics.corr(feature,"spearman")//这里选用斯皮尔曼相关系数,皮尔逊系数输入"pearson"
- println(correlMatrix)
加入的包:
- import org.apache.spark.mllib.linalg._
- import org.apache.spark.mllib.stat.Statistics
- import org.apache.spark.rdd.RDD
加在main函数里的代码:
- val TFIDFResult: DataFrame = idfModel.transform(featureVec).select("TF-IDF")
-
- //特征向量rdd
- var feature:RDD[Vector] = TFIDFResult
- .rdd.map(row=>{
- org.apache.spark.mllib.linalg.Vectors.fromML(row.get(0).asInstanceOf[org.apache.spark.ml.linalg.Vector])
- })
- println(feature)
-
- //计算相似度矩阵
- val correlMatrix: Matrix = Statistics.corr(feature,"spearman")
- println(correlMatrix)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。