Python手寫決策樹並應對過度擬合問題

2021-02-08 DeepHub IMBA

介紹

決策樹是一種用於監督學習的算法。它使用樹結構,其中包含兩種類型的節點:決策節點和葉節點。決策節點通過在要素上詢問布爾值將數據分為兩個分支。葉節點代表一個類。訓練過程是關於在具有特定特徵的特定特徵中找到「最佳」分割。預測過程是通過沿著路徑的每個決策節點回答問題來從根到達葉節點。

基尼不純度和熵

術語「最佳」拆分是指拆分之後,兩個分支比任何其他可能的拆分更「有序」。我們如何定義更多有序的?這取決於我們選擇哪種指標。通常,度量有兩種類型:基尼不純度和熵。這些指標越小,數據集就越「有序」。

這兩個指標之間的差異非常微妙。但 在大多數應用中,兩個指標的行為類似。以下是用於計算每個指標的代碼。

def gini_impurity(y):
  # calculate gini_impurity given labels/classes of each example
  m = y.shape[0]
  cnts = dict(zip(*np.unique(y, return_counts = True)))
  impurity = 1 - sum((cnt/m)**2 for cnt in cnts.values())
  return impurity

def entropy(y):
  # calculate entropy given labels/classes of each example
  m = y.shape[0]
  cnts = dict(zip(*np.unique(y, return_counts = True)))
  disorder = - sum((cnt/m)*log(cnt/m) for cnt in cnts.values())
  return disorder

構建樹

訓練過程實質上是在樹上建樹。關鍵步驟是確定「最佳」分配。過程如下:我們嘗試按每個功能中的每個唯一值分割數據,然後選擇混亂程度最小的最佳數據。現在我們可以將此過程轉換為python代碼。

def get_split(X, y):
  # loop through features and values to find best combination with the most information gain
  best_gain, best_index, best_value = 0, None, None
  cur_gini = gini_impurity(y)
  n_features = X.shape[1]  

  for index in range(n_features):  
      values = np.unique(X[:, index], return_counts = False)  

      for value in values:  
          left, right = test_split(index, value, X, y)
          if left['y'].shape[0] == 0 or right['y'].shape[0] == 0:
              continue
          gain = info_gain(left['y'], right['y'], cur_gini)
          if gain > best_gain:
              best_gain, best_index, best_value = gain, index, value
  best_split = {'gain': best_gain, 'index': best_index, 'value': best_value}
  return best_split
   
def test_split(index, value, X, y):
  # split a group of examples based on given index (feature) and value
  mask = X[:, index] < value
  left = {'X': X[mask, :], 'y': y[mask]}
  right = {'X': X[~mask, :], 'y': y[~mask]}
  return left, right
   
def info_gain(l_y, r_y, cur_gini):
  # calculate the information gain for a certain split
  m, n = l_y.shape[0], r_y.shape[0]
  p = m / (m + n)
  return cur_gini - p * gini_impurity(l_y) - (1 - p) * gini_impurity(r_y)

在構建樹之前,我們先定義決策節點和葉節點。決策節點指定將在其上拆分的特徵和值。它還指向左,右子項。葉節點包括類似於Counter對象的字典,該字典顯示每個類有多少訓練示例。這對於計算訓練的準確性很有用。另外,它導致到達該葉子的每個示例的結果預測。

class Decision_Node:
  # define a decision node
  def __init__(self, index, value, left, right):
      self.index, self.value = index, value
      self.left, self.right = left, right

class Leaf:
  # define a leaf node
  def __init__(self, y):
      self.counts = dict(zip(*np.unique(y, return_counts = True)))
      self.prediction = max(self.counts.keys(), key = lambda x: self.counts[x])

鑑於其結構,通過遞歸構造樹是最方便的。遞歸的出口是葉節點。當我們無法通過拆分提高數據純度時,就會發生這種情況。如果我們可以找到「最佳」拆分,則這將成為決策節點。然後,我們對其左,右子級遞歸執行相同的操作。

def decision_tree(X, y):
  # train the decision tree model with a dataset
  correct_prediction = 0
   
  def build_tree(X, y):
      # recursively build the tree
      split = get_split(X, y)

      if split['gain'] == 0:
          nonlocal correct_prediction
          leaf = Leaf(y)
          correct_prediction += leaf.counts[leaf.prediction]
          return leaf

      left, right = test_split(split['index'], split['value'], X, y)
      left_node = build_tree(left['X'], left['y'])
      right_node = build_tree(right['X'], right['y'])

      return Decision_Node(split['index'], split['value'], left_node, right_node)
   
  root = build_tree(X, y)
  return correct_prediction/y.shape[0], root

預測

現在我們可以遍歷樹直到葉節點來預測一個示例。

def predict(x, node):
  if isinstance(node, Leaf):
      return node.prediction
   
  if x[node.index] < node.value:
      return predict(x, node.left)
  else:
      return predict(x, node.right)

事實證明,訓練精度為100%,決策邊界看起來很奇怪!顯然,該模型過度擬合了訓練數據。好吧,如果考慮到這一點,如果我們繼續拆分直到數據集變得更純淨,決策樹將過度適合數據。換句話說,如果我們不停止分裂,該模型將正確分類每個示例!訓練準確性為100%(除非具有完全相同功能的不同類別的示例),這絲毫不令人驚訝。

如何應對過度擬合?

從上一節中,我們知道決策樹過擬合的幕後原因。為了防止過度擬合,我們需要在某個時候停止拆分樹。因此,我們需要引入兩個用於訓練的超參數。它們是:樹的最大深度和葉子的最小尺寸。讓我們重寫樹的構建部分。

def decision_tree(X, y, max_dep = 5, min_size = 10):
  # train the decision tree model with a dataset
  correct_prediction = 0
   
  def build_tree(X, y, dep, max_dep = max_dep, min_size = min_size):
      # recursively build the tree
      split = get_split(X, y)

      if split['gain'] == 0 or dep >= max_dep or y.shape[0] <= min_size:
          nonlocal correct_prediction
          leaf = Leaf(y)
          correct_prediction += leaf.counts[leaf.prediction]
          return leaf

      left, right = test_split(split['index'], split['value'], X, y)

      left_node = build_tree(left['X'], left['y'], dep + 1)
      right_node = build_tree(right['X'], right['y'], dep + 1)

      return Decision_Node(split['index'], split['value'], left_node, right_node)
   
  root = build_tree(X, y, 0)
   
  return correct_prediction/y.shape[0], root

現在我們可以重新訓練數據並繪製決策邊界。

樹的可視化

接下來,我們將通過列印出決策樹的節點來可視化決策樹。節點的壓痕與其深度成正比。

def print_tree(node, indent = "|---"):
  # print the tree
  if isinstance(node, Leaf):
      print(indent + 'Class:', node.prediction)
      return

  print(indent + 'feature_' + str(node.index) +
          ' <= ' + str(round(node.value, 2)))
  print_tree(node.left, '|   ' + indent)

  print(indent + 'feature_' + str(node.index) +
          ' > ' + str(round(node.value, 2)))
  print_tree(node.right, '|   ' + indent)

結果如下:

|---feature_1 <= 1.87
|   |---feature_1 <= -0.74
|   |   |---feature_1 <= -1.79
|   |   |   |---feature_1 <= -2.1
|   |   |   |   |---Class: 2
|   |   |   |---feature_1 > -2.1
|   |   |   |   |---Class: 2
|   |   |---feature_1 > -1.79
|   |   |   |---feature_0 <= 1.62
|   |   |   |   |---feature_0 <= -1.31
|   |   |   |   |   |---Class: 2
|   |   |   |   |---feature_0 > -1.31
|   |   |   |   |   |---feature_1 <= -1.49
|   |   |   |   |   |   |---Class: 1
|   |   |   |   |   |---feature_1 > -1.49
|   |   |   |   |   |   |---Class: 1
|   |   |   |---feature_0 > 1.62
|   |   |   |   |---Class: 2
|   |---feature_1 > -0.74
|   |   |---feature_1 <= 0.76
|   |   |   |---feature_0 <= 0.89
|   |   |   |   |---feature_0 <= -0.86
|   |   |   |   |   |---feature_0 <= -2.24
|   |   |   |   |   |   |---Class: 2
|   |   |   |   |   |---feature_0 > -2.24
|   |   |   |   |   |   |---Class: 1
|   |   |   |   |---feature_0 > -0.86
|   |   |   |   |   |---Class: 0
|   |   |   |---feature_0 > 0.89
|   |   |   |   |---feature_0 <= 2.13
|   |   |   |   |   |---Class: 1
|   |   |   |   |---feature_0 > 2.13
|   |   |   |   |   |---Class: 2
|   |   |---feature_1 > 0.76
|   |   |   |---feature_0 <= -1.6
|   |   |   |   |---Class: 2
|   |   |   |---feature_0 > -1.6
|   |   |   |   |---feature_0 <= 1.35
|   |   |   |   |   |---feature_1 <= 1.66
|   |   |   |   |   |   |---Class: 1
|   |   |   |   |   |---feature_1 > 1.66
|   |   |   |   |   |   |---Class: 1
|   |   |   |   |---feature_0 > 1.35
|   |   |   |   |   |---Class: 2
|---feature_1 > 1.87
|   |---Class: 2

總結

與其他回歸模型不同,決策樹不使用正則化來對抗過度擬合。相反,它使用樹修剪。選擇正確的超參數(樹的深度和葉子的大小)還需要進行實驗,例如 使用超參數矩陣進行交叉驗證。

對於完整的工作流,包括數據生成和繪圖決策邊界,完整的代碼在這裡:https://github.com/JunWorks/ML-Algorithm-with-Python/blob/master/decision_tree/decision_tree.ipynb


作者:Jun M

deephub翻譯組


相關焦點

  • 用Python構建和可視化決策樹
    你根本不需要熟悉機器學習技術就可以理解決策樹在做什麼。決策樹圖很容易解釋。利弊決策樹方法的優點是:決策樹方法的缺點是:決策樹的訓練在計算上可能很昂貴。生成決策樹的過程在計算上非常昂貴。在每個節點上,每個候選拆分欄位都必須進行排序,才能找到其最佳拆分。在某些算法中,使用欄位組合,必須搜索最佳組合權重。剪枝算法也可能是昂貴的,因為許多候選子樹必須形成和比較。
  • 以鳶尾花數據集為例,用Python對決策樹進行分類
    同時決策樹也存在劣勢,容易出現過度擬合就是其中之一。本教程主要介紹了用於分類的決策樹,也稱為分類樹。此外,本教程還將涵蓋:· 分類樹的解剖結構(樹的深度、根節點、決策節點、葉節點/終端節點)。分類和回歸樹(CART)術語最早由利奧·布雷曼提出,用於指代可以用於分類或回歸預測建模問題的決策樹算法。本篇文章主要涵蓋分類樹。分類樹本質上,分類樹就是設計一系列問題來進行分類。下圖是在鳶尾花數據集(花種)上訓練的分類樹。
  • 決策樹
    (3)剪枝函數決策樹生成算法得到的樹對訓練數據的分類很準確,但對未知數據的分類卻沒那麼準確,容易過擬合;因為決策樹考慮的特徵太多,構建得太複雜。 所以我們需要對決策樹進行剪枝:從已生成的樹上裁掉一些子樹或葉節點,並將其根節點或父節點作為新的葉節點,以此簡化樹。剪枝算法很多,這裡引入一種簡單的:極小化決策樹整體的損失函數。
  • python分類-決策樹
    嘿嘿,這禮拜隨便研究了個入門的知識點,python決策樹。這次學會的只是一個入門級別的python決策樹案例,還是給大家分享下吧。
  • Python機器學習sklearn模塊——決策樹
    決策樹可以用來解決分類與回歸問題,是一種非線性模型;一課決策樹一般包含一個根節點、若干個內部節點和若干個葉節點;生產決策樹最關鍵的是如何選擇最優劃分屬性,我們希望分支節點所包含的樣本近可能的屬於同一類別,即節點的「純度」越來越高;而評估樣本集合純度的常用指標有信息增益、增益率和基尼指數;決策樹容易出現過擬合狀況,因為它們可以通過精確的描述每個訓練樣本的 特徵而構建出複雜的決策樹
  • 決策樹可視化-python
    決策樹相較於其他機器學習模型具有較好的解釋性,也容易將其結果進行可視化展示,python中sklearn.tree的export_graphviz函數可以將決策樹結果以doc或dot文件的形式輸出,然後將決策樹結果可視化,下面舉個簡單例子。
  • sklearn學習(三):決策樹
    一.決策樹原理概述樹模型        決策樹:
  • 【量化課堂】決策樹入門及Python應用
    如下圖, 他只會根據年齡或經驗區分數據,但當兩者有聯繫時,決策樹只會用多次切分擬合這一情況。>了解了決策樹的原理,那如何根據問題生成決策樹呢?>在決策樹學習過程中,因為隨著子集樣本越小,混亂程度必然下降,這意味著決策樹分叉總是向著過擬合的方向。
  • 機器學習系列(二)決策樹(Decision Tree)
    缺點:可能會產生過度匹配的問題(需要剪枝)適用數據類型:數值型和標稱型決策樹在對中間結點進行分裂的時候,是選擇最優分裂結果的屬性進行分裂,決策樹使用信息增益或者信息增益率作為選擇屬性的依據。信息增益表示劃分數據前後信息發生的變化,獲得信息增益最高的特徵就是最好的選擇。
  • 新知識 | 「過度擬合」
    拜讀萬維鋼的《高手:精英的見識和我們的時代》過程中,看到一個名詞:過度擬合。
  • 關於基於樹的建模的完整教程(從R&Python)
    它可以有兩種類型:分類變量決策樹:目標變量是分類變量的決策樹被稱為分類變量決策樹。示例: 在上述的學生問題當中,目標變量是「學生將打板球」,即YES或NO。連續變量決策樹:決策樹具有連續型的目標變量,則稱為連續變量決策樹。假設我們有一個問題來預測客戶是否會向保險公司支付續保費(是/否)。
  • ​機器學習 | 決策樹之回歸樹
    今天推出機器學習系列筆記第2期,本期分享內容為:機器學習之決策樹中的回歸樹。,用回歸樹來擬合正弦曲線,並添加一些噪聲來觀察回歸樹的表現。但請注意,sklearn中的決策樹模塊不支持對缺失值的處理。 使用樹的成本(比如說,在預測數據的時候)是用於訓練樹的數據點的數量的對數,相比於其他算法,這是 一個很低的成本。 能夠同時處理數字和分類數據,既可以做回歸又可以做分類。其他技術通常專門用於分析僅具有一種變量類型的數據集。
  • 機器學習入門之決策樹1
    每次遞歸調用都必須離基線條件更進一步,即 縮小問題規模。決策樹基本流程¶決策樹(Decision Tree)是一類常見的機器學習算法,其基本流程圖遵循簡單且直觀的 D&C策略。對照 D&C策略,我們發現下一次的問題總是基於屬性子集來問的。第一個問題包含3種屬性,第二個問題包含2種屬性,等等。由此可見,決策樹是基於樹結構來進行決策的。這種決策方式恰是人類在面臨決策問題時一種很自然的處理機制。並且我們會很容易理解每個決策步驟的結果(有很好的解釋性)。
  • 機器學習 | 決策樹之分類樹
    今天推出機器學習系列筆記第1期,本期分享內容為:機器學習之決策樹中的分類樹。(筆者使用的是Mac系統)決策樹(Decision Tree):是一種非參數的有監督學習算法,在已知各種情況發生概率的基礎上,通過構成決策樹來取淨現值的期望值大於等於零的概率,是直觀運用概率分析的圖解法,以解決分類和回歸問題。分為分類樹和回歸樹。
  • 詳解決策樹 C4.5 算法
    上圖給出了(二叉)決策樹的示例。決策樹具有以下特點:1、對於二叉決策樹而言,可以看作是if-then規則集合,由決策樹的根節點到葉子節點對應於一條分類規則;2、分類規則是互斥並且完備的,所謂互斥即每一條樣本記錄不會同時匹配上兩條分類規則,所謂完備即每條樣本記錄都在決策樹中都能匹配上一條規則。
  • 機器學習決策樹:sklearn分類和回歸
    作者:alg-flody編輯:Emily昨天的推送機器學習:對決策樹剪枝,分析了決策樹需要剪枝,今天再就這個話題,藉助 sklearn 進一步分析決策樹分類和回歸時過擬合發生後,該如何解決的問題。上周推送的機器學習:談談決策樹,介紹了利用邏輯回歸算法,二分類一個擁有2個特徵的數據集,模擬的結果如下所示:
  • Python隨機森林 - CodeProject
    這意味著隨機森林中包括多種決策樹,並將每個決策樹結果的平均值作為隨機森林的最終輸出。決策樹有一些缺點,比如訓練集的過擬合導至很高的差異性,不過這在隨機森林中已經可以通過Bagging(Bootstrap Aggregating)的幫助解決。因為隨機森林實際上是由多種不同的決策樹組成的,所以我們最好先了解一下決策樹算法,然後再學習隨機森林的相關知識。
  • 人工智慧算法問題——正則化解決神經網絡中的過度擬合
    過度擬合是一個很大的問題,尤其是在深度神經網絡中。如果你正在懷疑你的神經網絡過度擬合了。有很多方法可以確定過度擬合了數據,也許有一個高方差問題,或者繪製了一個訓練圖和測試精度圖,然後發現過度擬合了。在這種情況下,我們應該怎麼辦呢?
  • 【分類算法】基於 R 語言決策樹算法介紹及應用
    很多相關問題的算法複雜度較高,而且很難找到固有的規律,所以部分的機器學習研究是開發容易處理的近似算法。機器學習在數據挖掘、計算機視覺、自然語言處理、生物特徵識別、搜尋引擎、醫學診斷、檢測信用卡欺詐、證券市場分析、DNA 序列測序、語言與手寫識別、戰略遊戲與機器人運用等領域有著十分廣泛的應用。它無疑是當前數據分析領域的一個熱點內容。
  • 決策樹與隨機森林(4)—— 決策樹C5.0算法
    這是許多決策樹構建的一個過程,然後這些決策樹通過投票表決的方法為每個案例選擇最優的分類。Question 2: 什麼叫做 Adaboost ?簡單來說,對於一個數據集,我們通過某種算法建立了第一個分類器,若第一個分類器對樣本 x1, x2 的分類效果好,對 x3 的分類效果差,那麼第二個分類器通過一個神奇的公式,把對 x3 的權重增加,x1, x2的權重降低。為什麼呢?