本章節覆寫特征相關的算法部分,可粗分為以下幾組: 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