Spark ML的特徵處理實戰

本文來自OPPO互聯網技術團隊,轉載請註名做者。同時歡迎關注咱們的公衆號:OPPO_tech,與你分享OPPO前沿互聯網技術及活動。html

一 、特徵處理的意義

一般狀況下,咱們獲得的數據中包含髒數據或者噪聲。在模型訓練前,須要對這些數據進行預處理,不然再好的模型也只能「garbage in,garbage out」。算法

數據預處理主要包括三部分,特徵提取、特徵轉換和特徵選擇。sql

2、特徵提取

特徵提取通常指從原始數據中抽取特徵的過程。apache

1. 計數向量器(Countvectorizer)

(1) 定義及用途:計數向量器將全部的文本詞語進行編號,並統計該詞語在文檔中的詞頻做爲特徵向量。app

(2) spark ml中代碼實例機器學習

import org.apache.spark.ml.feature.{CountVectorizer, CountVectorizerModel}
val df = spark.createDataFrame(Seq(
      (0, Array("a","e","a","d","b")),
      (1, Array("a","c","b","d","c","f","a","b")),
      (2, Array("a","f"))
)).toDF("id", "words")    
var cv_model = new CountVectorizer().setInputCol("words").setOutputCol("features").setVocabSize(10).setMinDF(2).fit(df)
val cv1 = cv_model.transform(df)
cv1.show(false)

注意點:計數向量器會將全部數據整合到一塊兒進行去重造成一張詞表,經過 setVocabSize 和 setMinDF 這兩個參數來肯定是否進入詞表。其中 setVocabSize 決定詞表的長度,而 setMinDF 決定要在多少個不一樣的樣本中出現才進入詞表。上例中設置詞表的長度爲10,至少在兩個樣本中出現纔會進入詞表,則能進入詞表的只有a,b,d,f。c和e僅在一條數據中出現因此並不會統計詞頻。性能

2. 詞頻-逆向文件頻率(TF-IDF)

(1) 定義及用途:通俗的理解就是計算一個詞區別一篇文檔的程度。經過在一篇文檔中的詞頻和該詞在文檔庫中多少篇文檔中出現綜合來評估。僅經過詞頻來區分一篇文檔是不合理的。學習

好比文檔中會屢次出現能表明通用含義的詞,可是這些詞對於文檔的識別並沒有意義。咱們須要的是一些特別的詞,它出現的次數多,而且能在少數的文檔中出現,這些詞纔可以識別文檔。舉個極端的例子,好比「咱們」這個詞可能出如今N多篇文檔中然而並沒用處。不少童鞋會說咱們能夠經過停用詞去除掉這些詞呀,對。而我說的就是這一類在停用詞以外,出現範圍很廣可是並沒有識別用處的詞。大數據

(2) spark ml中代碼實例編碼

import org.apache.spark.ml.feature.{HashingTF, IDF, Tokenizer}
val wordsData = spark.createDataFrame(Seq(
      "傳奇 遊戲 戰士".split(" "),
      "蘋果 梨 香蕉".split(" "),
      "蘋果 手機 流暢".split(" ")
    ).map(Tuple1.apply)).toDF("words")
wordsData.show(false)    
// step1 hashingTF
val hashingTF = new HashingTF().setInputCol("words").setOutputCol("rawFeatures").setNumFeatures(2000)
val featurizedData = hashingTF.transform(wordsData)
// step2 計算IDF
val idf = new IDF().setInputCol("rawFeatures").setOutputCol("features")
val idfModel = idf.fit(featurizedData)
val rescaledData = idfModel.transform(featurizedData)
rescaledData.select("words","features").show(false)

注意:setNumFeatures是設置特徵的長度。在這三條數據中,除了蘋果外其餘的詞都僅出現一次,因此能夠識別文檔的價值都比較大。而蘋果同時出如今兩條數據中,因此能識別文檔的價值被打壓,變得很低。

3.詞轉向量(Word2Vec)

(1) 定義及用途:詞轉向量是把單詞映射到向量空間中,經過一組向量來表明單詞。經過計算向量的距離能夠表明詞的類似度。

(2) spark ml中代碼實例

import org.apache.spark.ml.feature.Word2Vec
val documentDF = spark.createDataFrame(Seq(
      "傳奇 遊戲 戰士".split(" "),
      "蘋果 梨 香蕉".split(" "),
      "傳奇 遊戲 種類多".split(" "),
      "蘋果 手機 流暢".split(" ")
    ).map(Tuple1.apply)).toDF("text")
val word2Vec = new Word2Vec().setInputCol("text").setOutputCol("result").setVectorSize(10).setMinCount(2)
val model = word2Vec.fit(documentDF)
val result = model.transform(documentDF)
result.show(false)

注意:setVectorSize設置向量的長度。setMinCount設置詞在樣本中出現的最少次數。好比在上例中咱們設置向量長度爲10,至少在兩條樣本中出現纔會轉化向量。則知足條件的有"蘋果"、「傳奇」"遊戲"這三個詞,因此第一條數據和第三條數據向量距離徹底同樣,由於「戰士」和「種類多」均只出現一次,並不會用於轉化成向量,若是setMinCount設置爲1,那麼第一條和第三條的向量空間距離會很近,但不會徹底同樣,由於也會考慮「戰士」和「種類多」這兩個詞。

3、特徵轉換

1. 連續型數據轉換成離散數據

1.1 二值化(Binarizer)
  1. 定義及用途:根據閾值量將連續型數據轉化成0-1特徵的過程。
  2. 注意點:特徵值大於閾值將映射爲1.0,特徵值小於等於閾值將映射爲0.0;二值化輸入inputCol 支持向量(Vector)和雙精度(Double)類型。
1.2 離散化重組(Bucketizer)
  1. 定義及用途:根據分段規則將連續型數據轉化成各自對應的分段區間內。
  2. spark ml中代碼實例:
import org.apache.spark.ml.feature.Bucketizer
val data = Array(-8.0, -0.5, -0.3, 0.0, 0.2, 9.0)
val splits = Array(Double.NegativeInfinity, -0.5, 0.0, 0.5, Double.PositiveInfinity)
val dataFrame = spark.createDataFrame(data.map(Tuple1.apply)).toDF("features")
val bucketizer = new Bucketizer().setInputCol("features").setOutputCol("bucketedFeatures").setSplits(splits)
bucketizer.transform(dataFrame).show(false)

注意點:上例中第三行代碼,分段規則制定爲(負無窮,0.5),[-0.5,0),[0,0.5),[0.5,正負窮)四段。每一個分段是左閉右開[a,b)方式。當不肯定分裂的上下邊界時,應當添加Double.NegativeInfinity和Double.PositiveInfinity以避免越界。

1.3 分位數離散化(QuantileDiscretizer)
  1. 定義及用途:根據分位數規則將連續型數據轉化成各自對應的分段內。
  2. spark ml中代碼實例:
import org.apache.spark.ml.feature.QuantileDiscretizer
val data = Array((0, 18.0), (1, 19.0), (2, 8.0), (3, 5.0), (4, 2.2))
var df = spark.createDataFrame(data).toDF("id", "hour")
val discretizer = new QuantileDiscretizer().setInputCol("hour").setOutputCol("result").setNumBuckets(3)
val result = discretizer.fit(df).transform(df)
result.show()

注意點:setNumBuckets設置分位數分桶數量爲3。則將hour數據分紅3段。

2.字符串和索引相互轉換

2.1 字符串-索引變換(StringIndexer)
  1. 定義及用途:將字符串特徵轉化成索引。不少模型訓練過程當中只接受數值特徵,因此須要將字符串轉換成數值從而進行訓練。
  2. spark ml中代碼實例:
import org.apache.spark.ml.feature.StringIndexer
val df = spark.createDataFrame(
  Seq((0, "a"), (1, "b"), (2, "c"), (3, "a"), (4, "a"), (5, "c"))
).toDF("id", "category")
val indexer = new StringIndexer().setInputCol("category").setOutputCol("categoryIndex")
val indexed = indexer.fit(df).transform(df)
indexed.show(false)

注意:索引按照標籤頻率排序。最多見的標籤索引0即表明頻率最高的標籤。在新數據集中可能會遇到新出現的字符串。好比訓練集中只有a,b,c,在新的數據集中會有a,b,c,d。針對新出現的字符串d,有兩種策略來處理。 第一種是拋出一個異常(默認狀況下),第二種是經過掉用setHandleInvalid(「skip」)來完全忽略包含這類標籤的行。

2.2 索引-字符串(IndexToString)

定義及用途:通常是和上面的字符串-索引變換器配套使用。先經過字符串-索引變換器將字符串特徵轉換成數值類型特徵,模型訓練完成後經過索引-字符串轉換器將數值特徵還原成字符串特徵。

3. 正則化(Normalizer)

  1. 定義及用途:正則化的做用範圍是每一行數據,即每一條樣本數據。將每一條數據經過計算p-範數進行規範化。正則化操做可使輸入數據標準化並提升後期學習算法的效果。
  2. spark ml中代碼實例:
import org.apache.spark.ml.feature.Normalizer
import org.apache.spark.ml.linalg.{Vector,Vectors}
val data=Seq(Vectors.dense(-1,1,1,8,56),Vectors.dense(-1,3,-1,-9,88),Vectors.dense(0,5,1,10,96), Vectors.dense(0,5,1,11,589),Vectors.dense(0,5,1,11,688))
val df=spark.createDataFrame(data.map(Tuple1.apply)).toDF("features")    
val normalizer = new Normalizer().setInputCol("features").setOutputCol("normFeatures").setP(1.0)
normalizer.transform(df).show(false)

4. 規範化(StandardScaler)

  1. 定義及用途:規範化的做用範圍是每一列數據,即每一維特徵。標準化每一個特徵使得其有統一的標準差。一方面,同一個特徵中不一樣的樣本的取值可能會相差很是大,一些異常小或異常大的數據會誤導模型的正確訓練;另外一方面,若是數據的分佈很分散也會影響訓練結果。以上兩種方式都體如今方差會很是大。
  2. spark ml中代碼實例:
import org.apache.spark.ml.feature.StandardScaler
import org.apache.spark.ml.linalg.{Vector,Vectors}
val dataFrame = spark.createDataFrame(Seq(
(0, Vectors.dense(1.0, 0.5, -1.0)),(1, Vectors.dense(2.0, 1.0, 1.0)),
(2, Vectors.dense(4.0, 10.0, 2.0)))).toDF("id", "features")
val scaler = new StandardScaler().setInputCol("features")
.setOutputCol("scaledFeatures").setWithStd(true).setWithMean(false)
val scalerModel = scaler.fit(dataFrame)
val scaledData = scalerModel.transform(dataFrame)
scaledData.show(false)

注意:上述將每一列的標準差縮放到1。若是特徵的標準差爲零,則該特徵在向量中返回的默認值爲0.0。

5. 主成分分析 (PCA)

(1) 定義及用途:主成分分析(PCA)是一種統計學方法,本質是在線性空間中進行一個基變換,使得變換後的數據投影到低維空間的方差最大化。根據變換後方差大小肯定座標軸的權重或者重要性,權重高的成爲主成分。 主要應用於降維。

(2) spark ml中代碼實例:

import org.apache.spark.ml.feature.PCA
import org.apache.spark.ml.linalg.{Vector,Vectors}
val data = Array(
  Vectors.sparse(5, Seq((1, 1.0), (3, 7.0))),
  Vectors.dense(2.0, 0.0, 3.0, 4.0, 5.0), Vectors.dense(4.0, 0.0, 0.0, 6.0, 7.0))
val df = spark.createDataFrame(data.map(Tuple1.apply)).toDF("features")
val scaledDataFrame = new StandardScaler().setInputCol("features").setOutputCol("scaledFeatures").fit(df).transform(df)
val pca = new PCA().setInputCol("features").setOutputCol("pcaFeatures").setK(3).fit(scaledDataFrame)
val pcaDF = pca.transform(scaledDataFrame)
pcaDF.select("features","pcaFeatures").show(false)

注意:經過setK來設置下降到K維空間。上例中原來有5維特徵,經過pca下降到3維特徵中。pca前必定要對特徵向量進行規範化。由於各主成分之間值變化太大,有數量級的差異。標準化特徵向量後各主成分之間基本在同一個水平,結果更合理。K值選擇問題,能夠先選擇一個較大的值,經過pcaModel.explainedVariance計算模型的方差,當方差趨於穩定值,選擇對應的K值是一個不錯的選擇。

6. 向量-索引變換(VectorIndexer)

(1) 定義及用途:主要用於批量將離散型特徵轉化爲類別特徵

(2) spark ml中代碼實例:

import org.apache.spark.ml.feature.VectorIndexer
import org.apache.spark.ml.linalg.Vectors
val data=Seq(Vectors.dense(-1,1,1,8,56),
             Vectors.dense(-1,3,-1,-9,88),
             Vectors.dense(0,5,1,10,96), 
             Vectors.dense(0,5,1,11,589))
val df=spark.createDataFrame(data.map(Tuple1.apply)).toDF("features")    
val indexer = new VectorIndexer().setInputCol("features").setOutputCol("indexed").setMaxCategories(3)
val indexerModel = indexer.fit(df)
indexerModel.transform(df).show(false)

注意:設置setMaxCategories爲K,將特徵數量小於等於K的特徵轉化爲索引。好比上例中設置setMaxCategories爲3,第二列特徵有三類,則從新編碼爲0,1,2。

7. SQL轉換器(SQLTransformer)

(1) 定義及用途:不少習慣了使用sql來進行數據處理的童鞋可使用sql轉換器處理特徵。

(2) spark ml中代碼實例:

import org.apache.spark.ml.feature.SQLTransformer
val df = spark.createDataFrame(
  Seq((0, 1.0, 3.0), (2, 2.0, 5.0))).toDF("id", "v1", "v2")
val sqlTrans = new SQLTransformer().setStatement(
  "SELECT *, (v1 + v2) AS v3, (v1 * v2) AS v4 FROM __THIS__")
sqlTrans.transform(df).show()

8. 獨熱編碼(OneHotEncoder)

獨熱編碼將標籤指標映射爲二值變量。

9. 最大值-最小值縮放(MinMaxScaler)

將獨立的特徵值轉換到指定的範圍內,一般爲[0,1]。

10. 特徵向量合併(VectorAssembler)

將原始特徵和不一樣特徵轉換器生成的特徵合併爲單個特徵向量。輸入列的值將按指定順序依次添加到一個新向量中。

4、特徵選擇

特徵選擇是從特徵向量中選擇那些更簡單有效的特徵。適用於在高維數據分析中剔除冗餘特徵,提高模型的性能。特徵選擇後的特徵是原來特徵的一個子集。

1. 向量機(VectorSlicer)

基於已有的特徵庫,經過索引或者列名來選擇部分須要的特徵。

2. R公式(RFormula)

經過R模型公式產生一個特徵向量和一個標籤列。適合在須要作OneHotEncoder的時候,能夠一個簡單的代碼把全部的離散特徵轉化成數值化表示。

3. 卡方特徵選擇(ChiSqSelector)

(1) 定義及用途:卡方特徵選擇根據分類的卡方獨立性檢驗來對特徵排序。主要適用於有一堆特徵,可是咱們並不知道哪些有用,哪些沒用。能夠經過卡方特徵選擇來快速篩選特徵。缺點是速度比較慢。

(2) spark ml中代碼實例:

import org.apache.spark.ml.feature.ChiSqSelector
import org.apache.spark.ml.feature.VectorIndexer
val data = Seq( (7, Vectors.dense(0.0, 0.0, 18.0, 1.0), 1.0), 
  (8, Vectors.dense(0.0, 1.0, 12.0, 0.0), 0.0), (9, Vectors.dense(1.0, 0.0, 15.0, 0.2), 0.0))
val df = spark.createDataset(data).toDF("id", "features", "clicked")
val selector = new ChiSqSelector().setNumTopFeatures(2).setFeaturesCol("features").setLabelCol("clicked").setOutputCol("selectedFeatures")
val result = selector.fit(df).transform(df)
result.show(false)

參考資料:

  1. http://spark.apache.org/docs/...
  2. http://www.apache.wiki/pages/...
  3. https://blog.csdn.net/liuling...

最後,重點來啦~

OPPO商業中心數據標籤團隊招聘多個崗位,咱們致力於穿透大數據來理解每一個OPPO用戶的商業興趣。數據快速拓展和深挖中,誠邀對數據分析、大數據處理、機器學習/深度學習、NLP等有兩年以上經驗的您加入咱們,與團隊和業務一同成長!

簡歷投遞:ping.wang#oppo.com

相關文章
相關標籤/搜索