SparkML機器學習之特徵工程(一)特徵提取(TF-IDF、Word2Vec、CountVectorizer)

liuyanling41發表於2018-04-08

特徵工程

我們都知道特徵工程在機器學習中是很重要的,然而特徵工程到底是什麼?怎麼樣通俗的理解它呢?打個比方,即使你有再好的漁具,如果給你一片沒有魚的池塘,那也是白費力氣的。而特徵工程就是找有魚的那片水域。所以我們可以這麼理解,特徵是資料中抽取出來的對結果預測有用的資訊(水域),而特徵工程就是使用專業知識來處理資料,篩選出具有價值的特徵(從100個水域中挑選出魚最多最好的水域)。所以有句話是這麼說的:演算法再牛逼,其上限也是由特徵工程決定的,就像你漁具再好,捕魚多少也是由水域這個特徵決定的。
在SparkML中、對於特徵工程的操作主要分為特徵提取,特徵轉化、特徵選擇

特徵提取

從原始資料中提取特徵

TF-IDF (Term frequency-inverse document frequency)

TF-IDF稱為詞頻-逆檔案頻率,先搞清楚它有什麼作用吧!很經典的一個問題,如何得到一篇文章的關鍵詞??大家都能想到,看看這篇文章什麼詞出現最多!思路是沒問題,但是,一篇文章,出現最多的,應該都是諸如“的”之類的停用詞吧?這就沒意義了啊!那就把這些停用詞過濾掉唄,這樣還是會出問題。比如一篇文章,叫做中國功夫,中國和功夫出現了同樣多次數,可是顯而易見,該文重點應該是功夫。而出現問題的原因,是因為中國是個熱門詞。這讓我想到我曾寫過的基於物品的協同過濾演算法,也是要將熱門物品做一個懲罰,否則會導致推薦不精確。
image.png
TF-IDF完美的解決了這個問題,TF-IDF作用就是體現一個文件中詞語重要程度。TF是某個詞或短語在一篇文章中出現的頻率。而IDF,就是一種對熱門詞語的懲罰,對於較熱門詞語比如”中國”會給予較小的權重,較少見的詞“功夫”給予較大的權重。至於如何判斷它是否為熱門詞,則通過該詞在整個語料庫的出現次數決定。比如中國這個詞,語料庫一共1000篇文章他就出現了100次,自然為熱門詞,而功夫,1000篇文章只有1篇出現了,那就為冷門詞了。
image.png
image.png

package ml.test
import org.apache.spark.ml.feature.{HashingTF, IDF, Tokenizer}
import org.apache.spark.sql.SparkSession
/**
  * Created by LYL on 2018/4/4.
  */
object TFDemo {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder().appName("TF-IDF Demo").master("local").getOrCreate()
    val sentenceData = spark.createDataFrame(Seq(
      (0.0, "china kungfu kungfu is good"),
      (1.0, "I lova china"),
      (2.0, "I love china shenzhen")
    )).toDF("label", "sentence")
    //Tokenizer分詞器 將句子分成單詞
    val tokenizer = new Tokenizer().setInputCol("sentence").setOutputCol("words")
    val wordsData = tokenizer.transform(sentenceData)
    //將每個詞轉換成Int型,並計算其在文件中的詞頻(TF)
    //setNumFeatures(200)表示將Hash分桶的數量設定為200個,可以根據你的詞語數量來調整,一般來說,這個值越大不同的詞被計算為一個Hash值的概率就越小,資料也更準確,但需要消耗更大的記憶體
    val hashingTF = new HashingTF().setInputCol("words").setOutputCol("TF Features").setNumFeatures(200)
    val featurizedData = hashingTF.transform(wordsData)
    //計算IDF
    val idf = new IDF().setInputCol("TF Features").setOutputCol("TF-IDF features")
    val idfModel = idf.fit(featurizedData)
    val rescaledData = idfModel.transform(featurizedData)
    rescaledData.select("words","TF Features","TF-IDF features")show(false)
  }
}

輸出結果為:
由於china在三個文件中都出現了,所以TF-IDF=0.0,而kungfu只在第一個文件出現(說明是冷門詞),卻是第一個文件中出現次數最多的,因此計算出來的TF-IDF=1.3862943611198906也是最高的

+---------------------------------+----------------------------------------+---------------------------------------------------------------------------------------+
|words                            |TF Features                             |TF-IDF features                                                                        |
+---------------------------------+----------------------------------------+---------------------------------------------------------------------------------------+
|[china, kungfu, kungfu, is, good]|(200,[81,168,169,198],[1.0,1.0,1.0,2.0])|(200,[81,168,169,198],[0.6931471805599453,0.28768207245178085,0.0,1.3862943611198906]) |
|[i, lova, china]                 |(200,[91,129,169],[1.0,1.0,1.0])        |(200,[91,129,169],[0.6931471805599453,0.28768207245178085,0.0])                        |
|[i, love, china, shenzhen]       |(200,[40,129,168,169],[1.0,1.0,1.0,1.0])|(200,[40,129,168,169],[0.6931471805599453,0.28768207245178085,0.28768207245178085,0.0])|
+---------------------------------+----------------------------------------+---------------------------------------------------------------------------------------+

Word2Vec

word2vec是用一個向量去表示一個物件(因為計算機是無法識別物件實體的),物件可以是單詞,句子,文章,使用者等等。然後基於向量相似度去計算物件的相似度,找到相關的物件,發現相關關係,可以用來做分類、聚類、也可以做詞的相似度計算。應用非常廣泛,比如:相關詞(搜尋賈伯斯會出來蘋果),補全句子中缺失的單詞,推薦系統,分析使用者關係等等。

object Word2VecDemo {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder().master("local[2]").appName("Word2VecDemo").getOrCreate()
    val documentDF = spark.createDataFrame(Seq(
      "Hi I love Spark".split(" "),
      "Hi I love java".split(" "),
      "Logistic regression models are neat".split(" ")
    ).map(Tuple1.apply)).toDF("text")
    // setVectorSize 目標數值向量的維度大小 setMinCount 只有當某個詞出現的次數大於或者等於 minCount 時,才會被包含到詞彙表裡,否則會被忽略掉
    val word2Vec = new Word2Vec()
      .setInputCol("text")
      .setOutputCol("result")
      .setVectorSize(3)
      .setMinCount(0)
    val model = word2Vec.fit(documentDF)
    //​利用Word2VecModel把文件轉變成特徵向量。
    val result = model.transform(documentDF)
    result.show(false)
  }
}

輸出結果為:

+-----------------------------------------+-------------------------------------------------------------------+
|text                                     |result                                                             |
+-----------------------------------------+-------------------------------------------------------------------+
|[Hi, I, love, Spark]                     |[-0.03605498746037483,-0.02823249064385891,0.06127407215535641]    |
|[Hi, I, love, java]                      |[-0.046827200800180435,-0.052235052920877934,0.0025074686855077744]|
|[Logistic, regression, models, are, neat]|[0.04324783757328987,0.030185341089963916,-5.047338083386422E-4]   |
+-----------------------------------------+-------------------------------------------------------------------+

CountVectorizer

由於計算機是不能識別單詞的,所以我們要把它轉為向量。Countvectorizer和Countvectorizermodel旨在通過計數來將一個文件轉換為向量。

object CountVectorizerDemo {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder().master("local[2]").getOrCreate()
    val dataFrame = spark.createDataFrame(Seq(
      (0, Array("a", "b","b","c","d","d")),
      (1, Array("a","c","b" ))
    )).toDF("id", "words")

    //setVocabSize設定詞彙表的最大容量為3,setMinDF設定詞彙表中的詞至少要在2個文件中出現過。
    //如果setMinDF=2 那麼就不會出現d(只在一個文件存在)了。
    val cv = new CountVectorizer().setVocabSize(3).setMinDF(2).setInputCol("words").setOutputCol("features")
    //如果setVocabSize=2 那麼就不會出現a,c(次數少)了。
    val cv1 = new CountVectorizer().setVocabSize(2).setInputCol("words").setOutputCol("features")

    val cvModel = cv.fit(dataFrame)
    val cvModel1 = cv1.fit(dataFrame)

    cvModel.transform(dataFrame).show(truncate = false)
    cvModel1.transform(dataFrame).show(truncate = false)

  }
}

輸出結果為:

//3代表詞彙表的容量,[0,1,2]分別對應b,a,c,[2.0,1.0,1.0]代表出現次數
+---+------------------+-------------------------+
|id |words             |features                 |
+---+------------------+-------------------------+
|0  |[a, b, b, c, d, d]|(3,[0,1,2],[2.0,1.0,1.0])|
|1  |[a, c, b]         |(3,[0,1,2],[1.0,1.0,1.0])|
+---+------------------+-------------------------+


+---+------------------+-------------------+
|id |words             |features           |
+---+------------------+-------------------+
|0  |[a, b, b, c, d, d]|(2,[0,1],[2.0,2.0])|
|1  |[a, c, b]         |(2,[0],[1.0])      |
+---+------------------+-------------------+


相關文章