預測問題記住一點:最垃圾的預測就是使用平均值,若是你的預測連比直接給出平均值效果都要差,那就省省吧!
統計學誕生一個多世紀以後,隨着如今機器學習和數據科學的產生,咱們依舊使用迴歸的思想來進行預測,儘管迴歸 就是用平均值向後不斷回滾來預測。迴歸的技術和分類的技術緊密相關。一般狀況下,當目標變量是連續數值時指的是迴歸,例如預測 身高和體重。當預測的目標變量是名義或者說是類別變量時,指的就是分類,例如預測郵件是不是垃圾郵件。
不管是分類仍是迴歸,都須要給定已知信息去預測未知信息,因此它們都須要從輸入輸出來學習。它們須要包括問題和答案。這類算法所以也稱爲監督學習的方法。
迴歸和分類是使用年代最近研究的最充分的預測分析技術。不少算法開源包都包含通用的這些方法。好比:支持向量機,邏輯迴歸,樸素貝葉斯,神經網絡和深度學習。
本文的重點是討論:決策樹和它的擴展隨機森林。決策樹是通用並且靈活的分類迴歸算法。html
注意:特別當心分類變量(尤爲是那些用數字表示的分類變量,不要隨便放到算法中去訓練)和數值變量
注意:不是全部的算法都能處理分類變量,或者都能處理迴歸分類問題,可是放心決策樹均可以linux
算法名字 | 決策樹,DT,Decision Trees |
算法描述 | 適合任何數據類型的分類和迴歸算法 關鍵概念:樹、信息增益、中止條件、過擬合 |
算法原理 | 根據已有樣本訓練獲得一顆樹,樹的葉子節點就是預測的值,新的樣本將按照樣本的值遍歷樹的相對應節點最後到達葉子節點,葉子節點的值就是新樣本的預測值。<br/> 決策樹原理很是簡單,以下圖:<br/> ![]() |
使用場景 | 強大的算法,適合迴歸和預測的不少場合 |
算法優缺點 | 優勢: 1. 可以天然處理分類和數值特徵 2. 容易並行計算 3. 對異常值具備很好的魯棒性(意味着小量的異常值或者錯誤數據不影響分類結果) 4. 能夠處理不一樣類型和不一樣維度的數據不須要數據預處理或者預先正則化數據 5. 結果容易理解和解釋<br /> 缺點: 1. 容易過擬合(不過它的變種隨機森林已經改進了這個問題) |
數據類型 | 數值型和標稱型數據 |
參考資料 | 1. 算法原理 機器學習實戰 <br /> 2. MLlib實現 DesisionTree<br /> 3. MLlib實現 RandomForest |
本文將使用著名的 Covtype 數據集合,能夠在 http://bit.ly/1KiJRfg 這裏下載。下載以後是一個壓縮的 csv 文件, linux 用戶能夠用命令:tar -xzvf 解壓縮,windows用戶可使用 .7-zip 解壓縮,同時下載數據集的描述文件 covtype.info 數據集記錄的是美國 Colorado 植被覆蓋類型數據,也是惟一一個關心真實森林的數據。每條記錄都包含不少指標描述每一塊土地。 例如:高度、坡度、到水的距離、樹蔭下的面積、土壤的類型等等。森林的覆蓋類型是須要根據其餘54個特徵進行預測的特徵。 這是一個有趣的數據集,它包含分類和數值特徵。總共有581012條記錄。每條記錄有55列,其中一列是土壤的類型,其餘54列是輸入特徵。 雖然這個數據集還不能算得上真正的大數據,可是也能說明不少問題。很幸運,這個數據集已是csv文件,因此不須要太多的數據清洗或者其餘的準備就能夠給 Spark Mllib 使用。 數據集可上傳到 HDFS,固然也能夠先放到本地進行這個有趣的測試。無論哪一種方式,Spark 都只須要改變一兩個參數。 這裏不得再也不次提醒一個問題,分類變量如何編碼,下面是編碼的方式:算法
當算法中把編碼看成數字的時候只能使用第一種編碼,第二種編碼會得出滑稽的結果。具體緣由是沒有大小的東西被強制成有大小之分。
Covtype 數據集中有不少類型的特徵,不過很幸運,它已經幫咱們轉換成 one-hot 形勢,具體來講:apache
這個數據集每一列的變量單位都不必定相同,有的表示距離,有的表示度數等等windows
下面給出一個初步的利用Spark MLlib 實驗的決策樹模型,具體的意圖,代碼都有詳細的註釋:微信
//本地測試 val rootDir = "your sample data directory" def main(args: Array[String]) { val conf = new SparkConf().setAppName("SparkInAction").setMaster("local[4]") val sc = new SparkContext(conf) val covTypeData = sc.textFile(rootDir + "/covtype.data") val data = dataPrepare(covTypeData) //選擇測試集合和訓練集合 val Array(train, cvData, test) = data.randomSplit(Array(0.8, 0.1, 0.1)) train.cache() cvData.cache() test.cache() val model = buildDecisionTree(train, cvData) } /** * Spark MLlib 表示特徵向量的對象是 LabeledPoint * 這個對象由表示特徵的向量和目標變量組成 */ def dataPrepare(data: RDD[String]) = { val sample = data.map{ line => //所有數據轉化爲 double val array = line.split(",").map(_.toDouble) //前面54列是特徵向量,後面一列是目標變量 label val featureVector = Vectors.dense(array.init) //決策樹目標變量必須從0開始,按照1遞增 LabeledPoint(array.last - 1, featureVector) } sample } /** * 決策樹模型創建,這裏很容易改進爲十擇交叉驗證。 * 對每一份數據創建模型時,都須要隨機選出部分數據來調整模型參數到最優。 * 經過交叉驗證的方式調整參數。 * @param train * @param cvData */ def buildDecisionTree(train: RDD[LabeledPoint], cvData: RDD[LabeledPoint]) = { def getMetrics(model: DecisionTreeModel, data: RDD[LabeledPoint]) = { val predictionsAndLabels = data.map { example => (model.predict(example.features), example.label) } new MulticlassMetrics(predictionsAndLabels) } val model = DecisionTree.trainClassifier( train, 7, Map[Int, Int](), "gini", 4, 100 ) val matrics = getMetrics(model, cvData) println(matrics.confusionMatrix) (0 until 7).map( cat => (matrics.precision(cat), matrics.recall(cat)) ).foreach(println) }
這個是初步的運行結果:網絡
#總體的準確率和召回率 (0.7012384971978136,0.7012384971978136) #每個類別的準確率和召回率 (0.685108051158916,0.6668097486526446) (0.7255299659774928,0.7930627570177007) (0.6194455768446799,0.8685338668190912) (0.3771043771043771,0.39436619718309857) (0.55,0.011727078891257996) (0.0,0.0) (0.7174657534246576,0.4134188455846078)
70%的準確率和召回率彷佛效果還不錯,可是咱們如今不能盲目的認爲咱們的效果就真的不錯了,有時候瞎猜效果也會不錯。 例如:70%的數據屬於類別1,每次都猜想類別是1,那麼效果也能達到70%的準確率,下面咱們肯定一下瞎猜的準確率: 回答瞎猜猜對的機率,這個問題也不是簡單的,回到機率論課堂上,在訓練樣本每類機率已知的狀況下,測試樣本瞎猜對的機率有多大呢? 隨機給出一個樣本:猜想類A的機率是由訓練樣本決定的,同時猜對的機率是由測試樣本決定的,因此瞎猜猜對的機率是訓練樣本每類的機率分別 乘以測試樣本對應類的機率之和dom
/** * 獲取模型瞎猜的機率 * @param train 測試數據集 * @param cvData 驗證數據集 */ def guessProb(train: RDD[LabeledPoint], cvData: RDD[LabeledPoint]) { /** * 返回數據集合中,每個類別的機率 * @param data 訓練數據集 */ def labelProb(data: RDD[LabeledPoint]): Array[Double] = { val labelCnt = data.map(_.label).countByValue() val labelProb = labelCnt.toArray.sortBy(_._1).map(_._2) labelProb.map(_.toDouble/labelProb.sum) } val trainProb = labelProb(train) val cvProb = labelProb(cvData) val prob = trainProb.zip(cvProb).map { case (a, b) => a * b }.sum println(prob) }
能夠看到瞎猜的結果只有:0.3771281350885772 的準確率。說明70%的準確率效果確實不錯,可是請注意,咱們尚未優化參數, 說明咱們的模型還有優化的空間。機器學習
主要的參數有下面幾個:學習
/** * 模型評估 * @param trainData 訓練數據 * @param cvData 交叉驗證數據 */ def evaluate(trainData: RDD[LabeledPoint], cvData: RDD[LabeledPoint]): Unit = { val evaluations = for (impurity <- Array("gini", "entropy"); depth <- Array(1, 20); bins <- Array(10, 300)) yield { val model = DecisionTree.trainClassifier( trainData, 7, Map[Int,Int](), impurity, depth, bins) val predictionsAndLabels = cvData.map(example => (model.predict(example.features), example.label) ) val accuracy = new MulticlassMetrics(predictionsAndLabels).precision ((impurity, depth, bins), accuracy) } evaluations.sortBy(_._2).reverse.foreach(println) }
歡迎關注本人微信公衆號,會定時發送關於大數據、機器學習、Java、Linux 等技術的學習文章,並且是一個系列一個系列的發佈,無任何廣告,純屬我的興趣。