您的当前位置:首页正文

Spark ML 2.1 -- Extracting, transforming and selecting features (持续更新)

2024-12-01 来源:个人技术集锦

本章节覆盖特征相关的算法部分,可粗分为以下几组: 
1> 抽取: 从原始数据中抽取特征
2> 变换:缩放,转化,或修改特征
3> 选择: 从特征集合中选择子集
4>  Locality Sensitive Hashing (LSH) : 将特征变换和其它算法组合在一起的一类算法。

目录: 

1 特征抽取

1.1 TF-IDF
  是常用的特征向量化的方法,通过将语料库中文本转化为词在文档中的出现频次向量,反映词在文本中的重要程度,因此广泛应用于文本挖掘。用t表示一个词条,d表示文本,D表示语料库。 词条的频次 TF(t,d) 表示 词条t 在文档 d中出现的次数,  DF(t,D)表示出现词条 t的 文本D 的个数。如果 单纯用频次表示词条的重要程度,这样会过分强调某些常用词的重要程度,而乎略了这些词对文本信息量的贡献,如, "a" , "the" 和 "of" 这类小品词。如果一个词条在整个语料库中出现的频次高,那么这个词在识别文本时提供信息量低。反向文本频次(IDF)是数值测度,用于描述词条所提供的信息量

此处 |D| 是语料库中文本的个数。 在log 函数中,如果词条出现在所有文本中,那么该词的IDF值为0 . 注意到光滑因子1 是防止除以0 。 TF-IDF 测度是TF和IDF的向量积

TF-IDF 的定义有多种变体,在MLlibk , 我们将TF 和IDF分开,以保证灵活性。

TF:  HashingTF和 countVectorizer  用于生成词条的频次向量。

HashingTF 是一个变换,它将词条的集合转化为固定维度的向量集合。 在文本处理中, “词条集合”表示有很多词目。 HashingTF 实现哈希戏法(hashing trick : ) .  原始特征映射到词条索引,这个索引是通过哈希函数计算得到。此处的哈希函数是MurmurHash 3 ( ).

词条的频次是映射到出现词条的唯一索引上, 这样避免了计算全部词条索引的映射, 这样对于大的语料库来说,会消耗很多资源。 但有可能出现不同的原始特征哈希映射到相同的词条。为了降低这种出现概率,我们需要增加目标特征向量的维度,例如, hash 算法的分桶的数目。可以对哈希函数的结果使用简单的求模得到列索引, 但还是建议使用2的极数作为特征向量的维度, 否则可能有特征无法映射到的列上。 默认特征向量的维度是pow(2,18) = 262144 , 一个二开关变量可以控制词条频次计数,当设置成true时, 所有非 0 频次都设置为1 。 这样可以离散二进制概率模型,而不用整型计数。

CountVectorizer 转化文本为词条向量, 详见: CountVectorizer : 

IDF : IDF 是一个Estimator , 它将拟合数据集并生成一个IDFModel 模型。 IDFModel 将特征向量(通过 HashingTF 或 CountVectorizer)每列做伸缩变换。直觉来看,它会将语料库中频次为参考,对列值进行权重调整。

注意到: spark.ml 并不支持文本分词, 建议查看 Standford NLP Group :    , 或 scalanlp/chalk : 

示例: 
在下面的代码中,我们输入一些句子, 然后使用Tokenizer将句子转化为单词,然后对这些句子,我坐着使用HashingTF 将句子转化为特征向量。 再使用IDF来伸缩特征向量;因为使用文本作为特征,这样可以显著提升性能 。 同时可以将特征向量传给学习算法
HashingTF scala doc 和 IDF scala doc 详见 API


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

val sentenceData  = spark .createDataFrame ( Seq (
  ( 0.0 ,  "Hi I heard about Spark" ),
  ( 0.0 ,  "I wish Java could use case classes" ),
  ( 1.0 ,  "Logistic regression models are neat" ) )).toDF ( "label" ,  "sentence" )

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

val wordsData  = tokenizer .transform (sentenceData )

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

val featurizedData  = hashingTF .transform (wordsData )
// alternatively, CountVectorizer can also be used to get term frequency vectors

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

val rescaledData  = idfModel .transform (featurizedData )
rescaledData .select ( "label" ,  "features" ).show ()
完整的例子详见: examples/src/main/scala/org/apache/spark/examples/ml/TfIdfExample.scala 


1.2 Word2Vec
Word2Vec是一个Estimator , 输入是文档的单词序列, 训练得到Word2VecModel 模型。模型将每个单词映射为唯一固定长度向量。 Word2VecModel 将每个文档变换为一个向量, 这个向量可以作为特征用于预测, 文档相似度计算等。 详细见:MLlib user guide on Word2Vec  : 

下面代码, 我们输入一些文本, 然后将文本变换为特征向量。 然后将特征向量输入学习算法。

详见: Word2Vec Scala docs : 

import  org.apache.spark.ml.feature.Word2Vec
import  org.apache.spark.ml.linalg.Vector
import  org.apache.spark.sql.Row

// Input data: Each row is a bag of words from a sentence or document.
val documentDF  = spark .createDataFrame ( Seq (
  "Hi I heard about Spark" .split ( " " ),
  "I wish Java could use case classes" .split ( " " ),
  "Logistic regression models are neat" .split ( " " ) ).map ( Tuple1 .apply )).toDF ( "text" )

// Learn a mapping from words to Vectors.
val word2Vec  =  new  Word2Vec ()
  .setInputCol ( "text" )
  .setOutputCol ( "result" )
  .setVectorSize ( 3 )
  .setMinCount ( 0 )
val model  = word2Vec .fit (documentDF )

val result  = model .transform (documentDF )result .collect ().foreach  {  case  Row (text :  Seq [ _ ], features :  Vector )  =>
  println ( s"Text: [ ${text .mkString ( ", " ) } ] => \nVector:  $features \n" )  }
完整例子,见:  examples/src/main/scala/org/apache/spark/examples/ml/Word2VecExample.scala






















显示全文