特徵選擇方法總結

一、引言apache

最近,在作用戶畫像,利用文本分類方法挖掘用戶興趣模型。雖然文本分類不是很難,可是簡單的事情,細節倒是至關的重要。這篇文章我主要是想記錄一下,我在作分類的時候,使用到的特徵選擇的方法,以及相關的是實現方法。ide

二、特徵選擇的方法spa

(1)信息增益scala

  信息增益這一詞來自通訊領域,香濃提出的信息熵理論。信息熵的定義以下:rest

  它的本質是衡量一個事件的不肯定性的大小。而信息增益則是相對於某一個特定的特徵而言的:例如,對與某個特徵X,其對應的取值有n種(x1,x2,x3...xn),分別計算特徵X在x1,x2,x3...xn的取值下的信息熵,並根據每一個取值的機率,計算出全部取值信息熵的平均值:(以下所示)code

而信息增益就能夠表示爲原信息熵-條件熵。以下所示:orm

(2)信息增益率blog

特徵取值的個數對信息增益有較大的干擾,爲了不有些特徵取值個數較多,有些特徵取值取值較小,形成信息增益沒法公正的衡量一個特徵的好壞。因而,信息增益率是在原特徵的基礎之上,至關與對信息增益作了歸一化。其表達式是:事件

  IGR=IG(T)/H(C)ci

(3)相關係數

  相關係數是判斷兩個變量之間的相關性,比較經常使用的是person相關係數。

(4)Gini指數

  基尼指數是表示一個變量的重要程度,其值越小越重要。

(5)卡方檢驗

  卡方檢驗是檢驗兩個變量之間是否相互獨立的,在特徵選擇中的應用就是檢驗特徵與目標變量之間的是否獨立。在這裏原價假設是特徵與目標變量是相互獨立的。統計計算特徵的值與指望值之間的誤差,因爲誤差不能取負值,因此計算特徵的值與指望值之間的誤差的平方,並求和。最終結果做爲判斷原假設是否成立的標準。其公式以下:(這裏須要注意一點就是指望值是咱們本身計算獲得的)

    

可是,在特徵選擇的過程當中,咱們須要選擇的是原假設不成立的特徵,即誤差較大的特徵。如下介紹一下卡方檢驗在文本分類中的應用的例子:

假設如今有N篇文檔,其中有M篇是關於體育的,咱們想考察一個詞「籃球」與類別「體育」之間的相關性。咱們有四個觀察值可使用:

1.    包含「籃球」且屬於「體育」類別的文檔數,命名爲A

2.    包含「籃球」但不屬於「體育」類別的文檔數,命名爲B

3.    不包含「籃球」但卻屬於「體育」類別的文檔數,命名爲C

4.    既不包含「籃球」也不屬於「體育」類別的文檔數,命名爲D

那麼如何計算特徵「籃球」的指望值呢?由於A+B是包含「籃球」的文章數,除以總文檔數就是「籃球」出現的機率,固然,這裏認爲在一篇文章中出現便可,而無論出現了幾回,而屬於體育類的文章數爲A+C,在這些個文檔中,應該有

      

此時,咱們已經獲得了「籃球」這個特徵的指望值,接下來就是計算實際值與指望值之間的誤差了。以下所示:

      

代入上是公式,能夠獲得:

    

可是,咱們在實際工程上是經過其大小來選擇特徵詞的,對於全部的特徵詞,A+C和B+D都是同樣的,因此,上面的公式只須要計算以下式子便可:

    

最後咱們選擇其值較大的K個特徵。

(6)經過建模的方式,來選擇特徵

該方法主要是經過模型的方式來選擇特徵,具體的作法:

  1)首先根據原始數據,進行數據的預處理(如缺失值,異常值,歸一化)

  2)對原始數據進行建模,通常採用邏輯迴歸模型。

  3)根據模型的評價指標(通常用正確率或AUC)來對模型進行預測調優。

  4)若上述模型的評價指標較好,根據模型的權值的大小來肯定特徵的重要性。對應權值越大的特徵,其重要性越大。

三、特徵選擇方法的實現

(1)信息增益率和相關係數:

 1 package com.welab.BDL.UserInterests
 2 
 3 import org.apache.spark.mllib.linalg.Vectors
 4 import org.apache.spark.mllib.linalg.Vector
 5 import org.apache.spark.SparkContext
 6 import org.apache.spark.rdd.RDD
 7 import org.apache.spark.SparkConf
 8 import org.apache.spark.mllib.stat.Statistics
 9 import org.apache.spark.mllib.linalg.Matrix
10 
11 /**
12  * @author LJY
13  */
14 object InformationGain {
15 
16   //計算某個屬性的信息熵
17   def inforEntropy(target_attribute: Array[Double]): Double = {
18     var temp = scala.collection.mutable.HashMap[Double, Int]()
19     for (item <- target_attribute) {
20       if (temp.contains(item)) {
21         temp(item) = temp(item) + 1
22       } else {
23         temp.put(item, 1)
24       }
25     }
26     var Entropy = 0.0
27     for (item <- temp) {
28       Entropy += (-1) * (item._2.toDouble / target_attribute.length) * log2(item._2.toDouble / target_attribute.length)
29     }
30 
31     Entropy
32   }
33 
34   def log2(x: Double): Double = scala.math.log(x) / scala.math.log(2)
35 
36   //計算特徵與目標特徵之間的信息增益
37   def inforGain(sc: SparkContext, feature_attribute: RDD[(Double, Double)]): (Double, Double) = {
38     val target = feature_attribute.map { x => x._2 }.toArray()
39     val Entropy1 = inforEntropy(target)
40 
41     val all_Entropy = sc.accumulator(0.0)
42     feature_attribute.groupBy(x => x._1).foreach { x => all_Entropy += (x._2.size.toDouble / target.length) * inforEntropy(x._2.map(x => x._2).toArray)
43     }
44 
45     val X = feature_attribute.map { x => x._1 }
46     val Y = feature_attribute.map { x => x._2 }
47 
48     val correlation: Double = Statistics.corr(X, Y, "pearson")
49     /*    // calculate the correlation matrix using Pearson's method. Use "spearman" for Spearman's method.
50     // If a method is not specified, Pearson's method will be used by default. 
51     val correlMatrix: Matrix = Statistics.corr(data, "pearson")*/
52 
53     //    println(Entropy1)
54     //    println(all_Entropy.value)
55     ((Entropy1 - all_Entropy.value), correlation)
56   }
57 
58   //計算特徵與目標特徵之間的信息增益率
59   def inforGainRate(sc: SparkContext, feature_attribute: RDD[(Double, Double)]): (Double, Double) = {
60     val target = feature_attribute.map { x => x._2 }.toArray()
61     val Entropy1 = inforEntropy(target)
62 
63     val all_Entropy = sc.accumulator(0.0)
64     feature_attribute.groupBy(x => x._1).foreach { x => all_Entropy += (x._2.size.toDouble / target.length) * inforEntropy(x._2.map(x => x._2).toArray)
65     }
66 
67     val X = feature_attribute.map { x => x._1 }
68     val Y = feature_attribute.map { x => x._2 }
69 
70     val correlation: Double = Statistics.corr(X, Y, "pearson")
71     /*    // calculate the correlation matrix using Pearson's method. Use "spearman" for Spearman's method.
72     // If a method is not specified, Pearson's method will be used by default. 
73     val correlMatrix: Matrix = Statistics.corr(data, "pearson")*/
74     
75     //    println(Entropy1)
76     //    println(all_Entropy.value)
77     ((Entropy1 - all_Entropy.value).toDouble/inforEntropy(X.toArray()), correlation)
78   }
79 
80   def main(args: Array[String]): Unit = {
81     //    Vectors.dense()
82     val conf = new SparkConf().setAppName("OneLevelClassification").setMaster("local")
83     val sc = new SparkContext(conf)
84 
85     val data = sc.textFile("/user/hive/warehouse/bairong_summary")
86 
87     val result = data.take(10).foreach { x =>
88       val fields = x.split("\t")
89       fields.foreach { x => print(x + " ") }
90     }
91 
92   }
93 }
View Code

(2)卡方檢驗自定義實現:

  1 def my_chiSqTest3(sc: SparkContext, filename: String, p_thr: Double): Array[String] = {
  2     val data = sc.textFile(filename, 10000).cache()
  3     val allnum = data.count()
  4     val result = data.map { x =>
  5       val fields = x.split("::")
  6       val wds = fields(0).split(",")
  7       val label = fields(1).split("#")(0)
  8       var words = scala.collection.mutable.HashSet[String]() //準備對每一個文檔中的關鍵詞進行去重
  9       for (word <- wds) {
 10         words.add(word)
 11       }
 12       var ss = ""
 13       for (word <- words) {
 14         if (ss.isEmpty()) {
 15           ss = word + ":" + label
 16         } else {
 17           ss = ss + "," + word + ":" + label
 18         }
 19       }
 20       ss
 21     }.flatMap { x => x.split(",") }.map { x =>
 22       val fields = x.split(":")
 23       (fields(0), fields(1)) //(word,label)
 24     }.reduceByKey { (x, y) => x + "," + y }.cache()
 25 
 26     println("全部的特徵詞的總數爲:" + result.count())
 27 
 28     /*
 29      * A ——表示包含某個關鍵詞T,且屬於某類X的文檔數
 30      * B ——表示包含某個關鍵詞T,但不屬於某類X的文檔數
 31      * C ——表示不包含某個關鍵詞T,但屬於某類X的文檔數
 32      * D ——表示即不包含某個關鍵詞T,也不屬於某類X的文檔數
 33      * A+C ——表示屬於某類X的全部文檔數
 34      * C+D ——表示不包含關鍵詞T的全部文檔數
 35      */
 36 
 37     val result_A_B = result.map { x =>
 38       val word = x._1
 39       var label_occur = scala.collection.mutable.HashMap[String, Int]() //(label,occur)
 40       val labels = x._2.split(",") //包含該關鍵詞全部類別
 41       var A = 0
 42       var B = 0
 43       //統計關鍵詞在每一個類別中出現的次數
 44       for (label <- labels) {
 45         label_occur.get(label) match {
 46           case Some(a) => label_occur.put(label, a + 1)
 47           case None    => label_occur.put(label, 1)
 48         }
 49       }
 50       //相對每一個類來講,對應的A和B分別以下
 51       var word_A_B = ""
 52       for (label_num <- label_occur) {
 53         A = label_num._2
 54         B = labels.length - label_num._2
 55         if (word_A_B.isEmpty()) {
 56           word_A_B = word + ":" + label_num._1 + "," + A + "#" + B
 57         } else {
 58           word_A_B = word_A_B + "@" + word + ":" + label_num._1 + "," + A + "#" + B
 59         }
 60 
 61       }
 62       word_A_B
 63     }.flatMap { x => x.split("@") }.map { x =>
 64       val fields = x.split(",")
 65       val word_label = fields(0)
 66       val A = fields(1).split("#")(0).toInt
 67       val B = fields(1).split("#")(1).toInt
 68       (word_label, (A, B)) //(word:label,(A,B))
 69     }
 70 
 71     val result_CplusD = result.map { x =>
 72       val word = x._1
 73       val C_and_D = allnum - x._2.split(",").length
 74       (word, C_and_D) //相對於關鍵詞T來講,C+D
 75     }
 76 
 77     val result_Aplus_C = data.map { x =>
 78       val fields = x.split("::")
 79       val label = fields(1).split("#")(0)
 80       (label, 1)
 81     }.reduceByKey(_ + _)
 82 
 83     val res = result_A_B.map { x =>
 84       val word_label = x._1.split(":")
 85       val word = word_label(0)
 86       val label = word_label(1)
 87       (word, (label, x._2)) //(word,(label,(A,B)))
 88     }.leftOuterJoin(result_CplusD).map { x =>
 89       val word = x._1
 90       val label_A_B = x._2._1
 91       var C_plus_D = 0
 92       x._2._2 match {
 93         case Some(a) => C_plus_D = a.toInt
 94         case None    =>
 95       }
 96       (label_A_B._1, (word, label_A_B._2, C_plus_D)) //(label,(word,(A,B),C+D))
 97     }.leftOuterJoin(result_Aplus_C).map { x =>
 98       val label = x._1
 99       val word_A_B_CplusD = x._2._1 //(word,(A,B),C+D)
100       var AplusC = 0
101       x._2._2 match {
102         case Some(a) => AplusC = a
103         case None    =>
104       }
105       val A = word_A_B_CplusD._2._1
106       val B = word_A_B_CplusD._2._2
107       val C = AplusC - A
108       val D = word_A_B_CplusD._3 - C
109       (word_A_B_CplusD._1, ((A * D - B * C) * (A * D - B * C)).toDouble / ((A + B) * word_A_B_CplusD._3))
110     }.reduceByKey { (x, y) =>
111       var maxvalue = 0.0
112       if (x > y) {
113         maxvalue = x
114       } else {
115         maxvalue = y
116       }
117       maxvalue
118     }.map(x => (x._2, x._1)).sortByKey(false).map { x => (x._2, x._1) }
119 
120     val res1 = res.take((res.count() * p_thr).toInt).map(x => x._1)
121     res1
122   }
View Code
相關文章
相關標籤/搜索