天天看點

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

本章節覆寫特征相關的算法部分,可粗分為以下幾組:  1> 抽取: 從原始資料中抽取特征 2> 變換:縮放,轉化,或修改特征 3> 選擇: 從特征集合中選擇子集 4>  Locality Sensitive Hashing (LSH) : 将特征變換和其它算法組合在一起的一類算法。

目錄: 

  • Feature Extractors
    • TF-IDF
    • Word2Vec
    • CountVectorizer
  • Feature Transformers
    • Tokenizer
    • StopWordsRemover
    • nn-gram
    • Binarizer
    • PCA
    • PolynomialExpansion
    • Discrete Cosine Transform (DCT)
    • StringIndexer
    • IndexToString
    • OneHotEncoder
    • VectorIndexer
    • Interaction
    • Normalizer
    • StandardScaler
    • MinMaxScaler
    • MaxAbsScaler
    • Bucketizer
    • ElementwiseProduct
    • SQLTransformer
    • VectorAssembler
    • QuantileDiscretizer
  • Feature Selectors
    • VectorSlicer
    • RFormula
    • ChiSqSelector
  • Locality Sensitive Hashing
    • LSH Operations
      • Feature Transformation
      • Approximate Similarity Join
      • Approximate Nearest Neighbor Search
    • LSH Algorithms
      • Bucketed Random Projection for Euclidean Distance
      • MinHash for Jaccard Distance

1 特征抽取

1.1 TF-IDF Term frequency-inverse document frequency (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 : http://en.wikipedia.org/wiki/Feature_hashing) .  原始特征映射到詞條索引,這個索引是通過哈希函數計算得到。此處的哈希函數是MurmurHash 3 ( https://en.wikipedia.org/wiki/MurmurHash).

詞條的頻次是映射到出現詞條的唯一索引上, 這樣避免了計算全部詞條索引的映射, 這樣對于大的語料庫來說,會消耗很多資源。 但有可能出現不同的原始特征哈希映射到相同的詞條。為了降低這種出現機率,我們需要增加目标特征向量的次元,例如, hash 算法的分桶的數目。可以對哈希函數的結果使用簡單的求模得到列索引, 但還是建議使用2的極數作為特征向量的次元, 否則可能有特征無法映射到的列上。 預設特征向量的次元是pow(2,18) = 262144 , 一個二開關變量可以控制詞條頻次計數,當設定成true時, 所有非 0 頻次都設定為1 。 這樣可以離散二進制機率模型,而不用整型計數。

CountVectorizer 轉化文本為詞條向量, 詳見: CountVectorizer :  http://spark.apache.org/docs/latest/ml-features.html#countvectorizer

IDF : IDF 是一個Estimator , 它将拟合資料集并生成一個IDFModel 模型。 IDFModel 将特征向量(通過 HashingTF 或 CountVectorizer)每列做伸縮變換。直覺來看,它會将語料庫中頻次為參考,對列值進行權重調整。

注意到: spark.ml 并不支援文本分詞, 建議檢視 Standford NLP Group :  http://nlp.stanford.edu/  , 或 scalanlp/chalk :  https://github.com/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  :  http://spark.apache.org/docs/latest/mllib-feature-extraction.html#word2vec

下面代碼, 我們輸入一些文本, 然後将文本變換為特征向量。 然後将特征向量輸入學習算法。

詳見: Word2Vec Scala docs :  http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.ml.feature.Word2Vec

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

繼續閱讀