機器學習決策樹

一、何為決策樹

決策樹(Decision Tree)是一種分類和回歸方法,是基于各種情況發生的所需條件構成決策樹,以實現期望最大化的一種圖解法。由于這種決策分支畫成圖形很像一棵樹的枝干,故稱決策樹。它的運行機制非常通俗易懂,因此被譽為機器學習中,最“友好”的算法。下面通過一個簡單的例子來闡述它的執行流程。假設根據大量數據(含 3 個指標:天氣、溫度、風速)構建了一棵“可預測學校會不會舉辦運動會”的決策樹(如下圖所示)。

1、決策樹的組成

決策樹由結點和有向邊組成。結點有兩種類型:內部結點(圓)和葉結點(矩形)。其中,內部結點表示一個特征(屬性);葉結點表示一個類別。而有向邊則對應其所屬內部結點的可選項(屬性的取值范圍),有非常好的可解釋性。

2、決策樹的構建

構建要求:

  1. 具有較好的泛化能力;
  2. 在 1 的基礎上盡量不出現過擬合現象。

思路:

如果按照某個特征對數據進行劃分時,它能最大程度地將原本混亂的結果盡可能劃分為幾個有序的大類,則就應該先以這個特征為決策樹中的根結點。接著,不斷重復這一過程,直到整棵決策樹被構建完成為止。

二、熵

1、熵的作用

熵(Entropy)是表示隨機變量不確定性的度量。說簡單點就是物體內部的混亂程度。我們希望分類后的結果能使得整個樣本集在各自的類別中盡可能有序,即希望某個特征在被用于分類后,能最大程度地降低樣本數據的熵。

根據以上結果,可以很直觀地認為,決策樹 2 的分類效果優于決策樹 1 。決策樹 1 在通過特征 𝑥1 進行分類后,得到的分類結果依然混亂(甚至有熵增的情況),因此這個特征在現階段被認為是無效特征。

2、熵的定義

設 𝑋 是取值在有限范圍內的一個離散隨機變量,其概率密度為:

P(X=xi)=pi,pi=1,2,......,n

隨機變量 𝑋 的熵定義為:

當某個集合含有多個類別時,此時 𝑘 較大, 𝑝𝑖?的數量過多;且整體的 𝑝𝑖?都會因 𝑘 的過大而普遍較小,從而使得 𝐻(X) 的值過大。這正好符合“熵值越大,事物越混亂”的定義。

3、條件熵

對于每個特征,都可以算出“該特征各項取值對運動會舉辦與否”的影響(而衡量各特征誰最合適的準則,即是熵)。

定義條件熵 𝐻(𝑌 | 𝑋) :在給定 𝑋 的條件下 𝑌 的條件概率分布對 𝑋 的數學期望,即

pi?=P(X=xi?)?,?i=1,2,…,k?(表示指定集合中的元素類別) 。

三、劃分選擇

決策樹學習的關鍵在于:如何選擇最優劃分屬性。一般而言,隨著劃分過程的不斷進行,我們自然希望決策樹各分支結點所包含的樣本盡可能屬于同一類別,即結點的 “純度” (purity) 越來越高。下面介紹幾類較為主流的評選算法。

1、信息增益( ID3 算法選用的評估標準)

信息增益 𝑔(𝐷, 𝑋) :表示某特征 𝑋 使得數據集 𝐷 的不確定性減少程度,定義為集合 𝐷 的熵與在給定特征 𝑋 的條件下 𝐷 的條件熵 𝐻(𝐷 | 𝑋) 之差,即

信息增益表達了樣本數據在被分類后的專一性。因此,它可以作為選擇當前最優特征的一個指標,依據該指標從大到小依次作為選擇的特征。

2、信息增益率( C4.5 算法選用的評估標準)

?以信息增益作為劃分數據集的特征時,其偏向于選擇取值較多的特征(特征下的類別很多)。比如,當在學校舉辦運動會的歷史數據中加入一個新特征 “編號” 時,該特征將成為最優特征。因為給定 “編號” 就一定知道那天是否舉行過運動會,因此 “編號” 的信息增益很高。

但實際我們知道,“編號” 對于類別的劃分并沒有實際意義。故此,引入信息增益率。

信息增益率 𝑔𝑅(𝐷, 𝑋) 定義為其信息增益 𝑔(𝐷, 𝑋) 與數據集 𝐷 在特征 𝑋 上值的熵 𝐻𝑋(𝐷) 之比,即:

𝑘 是特征 𝑋 的取值類別個數。

信息增益率能明顯降低取值較多的特征偏好現象,從而更合理地評估各特征在劃分數據集時取得的效果。

3、基尼系數( CART 算法選用的評估標準)

它通過使用基尼系數來代替信息增益率,從而避免復雜的對數運算。基尼系數代表了模型的不純度,基尼系數越小,則不純度越低,特征越好。注:這一點和信息增益(率)恰好相反。

對于給定數據集 𝐷 ,假設有 𝑘 個類別,且第 𝑘 個類別的數量為 𝐶𝑘?,則該數據集的基尼系數為

如果數據集 D DD 根據特征 X XX 的取值將其劃分為 { D 1 , D 2 , … , D m },則在特征D的條件下劃分過后的基尼系數為:

四、實戰

data_loader.py

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_splitdef load_data():"""加載并預處理數據"""data = {'ID': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],'年齡段': ['青年', '青年', '青年', '青年', '青年', '中年', '中年', '中年', '中年', '中年', '老年', '老年','老年', '老年', '老年', '老年'],'有工作': ['否', '否', '是', '是', '否', '否', '否', '是', '否', '否', '否', '否', '是', '是', '否', '否'],'有自己的房子': ['否', '否', '否', '是', '否', '否', '否', '是', '是', '是', '是', '是', '否', '否', '否','否'],'信貸情況': ['一般', '好', '好', '一般', '一般', '一般', '好', '好', '非常好', '非常好', '非常好', '好', '好','非常好', '一般', '非常好'],'類別(是否給貸款)': ['否', '否', '是', '是', '否', '否', '否', '是', '是', '是', '是', '是', '是', '是', '否','否']}df = pd.DataFrame(data)X = df.drop(['ID', '類別(是否給貸款)'], axis=1)y = df['類別(是否給貸款)']# 劃分訓練集和測試集X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)return X_train, X_test, y_train, y_test, X.columns.tolist()

將原始貸款審批數據轉換為結構化格式,劃分訓練集和測試集,為后續決策樹建模提供可直接使用的輸入

具體分三步:

1) 用字典定義原始數據并轉為Pandas DataFrame;

2) 分離特征(年齡段、工作、房產、信貸)和標簽(是否貸款);

3) 按7:3比例隨機拆分訓練集和測試集(固定隨機種子確保可復現),最終返回處理好的特征矩陣、標簽向量及特征名稱列表。

?c45_tree.py

import numpy as np
from collections import Counterclass C45DecisionTree:def __init__(self, max_depth=None, min_samples_split=2):self.max_depth = max_depthself.min_samples_split = min_samples_splitself.tree = Noneself.label_encoders = Nonedef _entropy(self, y):"""計算熵"""counts = np.bincount(y)probabilities = counts / len(y)return -np.sum([p * np.log2(p) for p in probabilities if p > 0])def _information_gain(self, X, y, feature):"""計算信息增益"""total_entropy = self._entropy(y)values, counts = np.unique(X[feature], return_counts=True)weighted_entropy = 0for v, c in zip(values, counts):subset_y = y[X[feature] == v]weighted_entropy += (c / len(y)) * self._entropy(subset_y)return total_entropy - weighted_entropydef _split_info(self, X, feature):"""計算分裂信息"""_, counts = np.unique(X[feature], return_counts=True)probabilities = counts / len(X)return -np.sum([p * np.log2(p) for p in probabilities if p > 0])def _gain_ratio(self, X, y, feature):"""計算信息增益率"""gain = self._information_gain(X, y, feature)split = self._split_info(X, feature)return gain / split if split != 0 else 0def _choose_best_feature(self, X, y, features):"""選擇最佳分裂特征"""gain_ratios = [self._gain_ratio(X, y, f) for f in features]best_idx = np.argmax(gain_ratios)return features[best_idx]def _build_tree(self, X, y, features, depth=0):"""遞歸構建決策樹"""# 終止條件if (len(np.unique(y)) == 1 or(self.max_depth is not None and depth >= self.max_depth) orlen(y) < self.min_samples_split):return {'class': Counter(y).most_common(1)[0][0]}# 選擇最佳特征best_feature = self._choose_best_feature(X, y, features)tree = {'feature': best_feature, 'children': {}}# 遞歸構建子樹remaining_features = [f for f in features if f != best_feature]for value in np.unique(X[best_feature]):subset_X = X[X[best_feature] == value].drop(columns=[best_feature])subset_y = y[X[best_feature] == value]tree['children'][value] = self._build_tree(subset_X, subset_y, remaining_features, depth + 1)return treedef fit(self, X, y):"""訓練模型"""# 將分類特征轉換為數值(簡化實現)self.label_encoders = {}X_encoded = X.copy()for col in X.columns:le = {val: idx for idx, val in enumerate(X[col].unique())}X_encoded[col] = X[col].map(le)self.label_encoders[col] = ley_encoded = y.map({'否': 0, '是': 1})self.tree = self._build_tree(X_encoded, y_encoded, X.columns.tolist())def predict(self, X):"""預測"""X_encoded = X.copy()for col in X.columns:X_encoded[col] = X[col].map(self.label_encoders[col])predictions = []for _, row in X_encoded.iterrows():node = self.treewhile 'children' in node:feature = node['feature']value = row[feature]if value in node['children']:node = node['children'][value]else:breakpredictions.append(node.get('class', 0))return ['是' if p == 1 else '否' for p in predictions]

通過遞歸地選擇信息增益率最大的特征進行節點分裂,構建樹形決策結構

主要流程分為四步:

1) 預處理階段將分類特征編碼為數值;

2) 基于信息熵計算各特征的信息增益率,選擇最佳分裂特征;

3) 遞歸構建決策樹,直到滿足終止條件(純度100%、達到最大深度或樣本不足);

4) 預測時根據特征值沿樹結構向下遍歷至葉節點獲取分類結果。該實現完整包含了C4.5算法的關鍵特性:使用增益率避免特征取值數目帶來的偏差,支持預剪枝控制過擬合,并通過字典嵌套結構清晰表示樹的分支關系。

cart_tree.py

import numpy as np
from collections import Counterclass CARTDecisionTree:def __init__(self, max_depth=None, min_samples_split=2):self.max_depth = max_depthself.min_samples_split = min_samples_splitself.tree = Noneself.label_encoders = Nonedef _gini(self, y):"""計算基尼系數"""counts = np.bincount(y)probabilities = counts / len(y)return 1 - np.sum([p ** 2 for p in probabilities if p > 0])def _gini_index(self, X, y, feature):"""計算基尼指數,基于指定特征 X XX 進行劃分后,集合 𝐷 的不確定性"""values, counts = np.unique(X[feature], return_counts=True)weighted_gini = 0for v, c in zip(values, counts):subset_y = y[X[feature] == v]weighted_gini += (c / len(y)) * self._gini(subset_y)return weighted_ginidef _choose_best_feature(self, X, y, features):"""選擇最佳分裂特征"""gini_indices = [self._gini_index(X, y, f) for f in features]best_idx = np.argmin(gini_indices)return features[best_idx]def _build_tree(self, X, y, features, depth=0):"""遞歸構建決策樹"""# 終止條件if (len(np.unique(y)) == 1 or(self.max_depth is not None and depth >= self.max_depth) orlen(y) < self.min_samples_split):return {'class': Counter(y).most_common(1)[0][0]}# 選擇最佳特征best_feature = self._choose_best_feature(X, y, features)tree = {'feature': best_feature, 'children': {}}# 遞歸構建子樹remaining_features = [f for f in features if f != best_feature]for value in np.unique(X[best_feature]):subset_X = X[X[best_feature] == value].drop(columns=[best_feature])subset_y = y[X[best_feature] == value]tree['children'][value] = self._build_tree(subset_X, subset_y, remaining_features, depth + 1)return treedef fit(self, X, y):"""訓練模型"""# 將分類特征轉換為數值(簡化實現)self.label_encoders = {}X_encoded = X.copy()for col in X.columns:le = {val: idx for idx, val in enumerate(X[col].unique())}X_encoded[col] = X[col].map(le)self.label_encoders[col] = ley_encoded = y.map({'否': 0, '是': 1})self.tree = self._build_tree(X_encoded, y_encoded, X.columns.tolist())def predict(self, X):"""預測"""X_encoded = X.copy()for col in X.columns:X_encoded[col] = X[col].map(self.label_encoders[col])predictions = []for _, row in X_encoded.iterrows():node = self.treewhile 'children' in node:feature = node['feature']value = row[feature]if value in node['children']:node = node['children'][value]else:breakpredictions.append(node.get('class', 0))return ['是' if p == 1 else '否' for p in predictions]

通過遞歸地選擇基尼指數最小的特征進行節點分裂,構建二叉樹結構的決策模型

主要流程分為四個關鍵步驟:

1) 數據預處理階段將分類特征編碼為數值形式;

2) 計算各特征的基尼指數,選擇使數據不純度降低最多的特征作為分裂節點;

3) 遞歸構建決策樹,直到滿足終止條件(節點樣本純凈、達到最大深度或樣本數不足);

4) 預測時根據特征值匹配樹的分支結構,最終到達葉節點獲得分類結果。該實現完整體現了CART算法的核心特點:使用基尼指數作為分裂標準、支持預剪枝參數控制過擬合、采用字典嵌套結構存儲樹形關系,并能處理分類特征,最終輸出"是"/"否"的貸款審批決策。

main.py

import numpy as npfrom data_loader import load_data
from c45_tree import C45DecisionTree
from cart_tree import CARTDecisionTreedef accuracy(y_true, y_pred):return np.sum(y_true == y_pred) / len(y_true)def print_tree(node, feature_names, class_names, indent=""):"""打印決策樹結構"""if 'class' in node:print(indent + "預測:", class_names[node['class']])else:print(indent + "特征:", feature_names[node['feature']] if isinstance(node['feature'], int) else node['feature'])for value, child in node['children'].items():print(indent + f"--> 值: {value}")print_tree(child, feature_names, class_names, indent + "    ")if __name__ == "__main__":# 加載數據X_train, X_test, y_train, y_test, feature_names = load_data()class_names = ['否', '是']# 訓練和評估C4.5print("\n=== C4.5決策樹 ===")c45 = C45DecisionTree(max_depth=3)c45.fit(X_train, y_train)print("測試集準確率:", accuracy(y_test, c45.predict(X_test)))print("\n決策樹結構:")print_tree(c45.tree, feature_names, class_names)# 訓練和評估CARTprint("\n=== CART決策樹 ===")cart = CARTDecisionTree(max_depth=3)cart.fit(X_train, y_train)print("測試集準確率:", accuracy(y_test, cart.predict(X_test)))print("\n決策樹結構:")print_tree(cart.tree, feature_names, class_names)
  1. 數據準備階段:調用load_data()加載并劃分訓練集/測試集,同時獲取特征名稱和類別標簽。

  2. 模型訓練與評估:

    • 分別實例化C4.5和CART決策樹(限制最大深度為3)

    • 在訓練集上擬合模型

    • 在測試集上計算預測準確率

  3. 結果展示:遞歸打印決策樹結構,直觀顯示每個節點的分裂特征和分支條件,以及葉節點的預測結果。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/76841.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/76841.shtml
英文地址,請注明出處:http://en.pswp.cn/web/76841.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

香港服務器CPU對比:Intel E3與E5系列核心區別與使用場景

香港服務器的 CPU 配置(核心數與主頻)直接決定了其并發處理能力和數據運算效率&#xff0c;例如高頻多核處理器可顯著提升多線程任務響應速度。在實際業務場景中&#xff0c;不同負載需求對 CPU 架構的要求存在顯著差異——以 Intel E3 和 E5 系列為例&#xff0c;由于兩者在性…

【Rust 精進之路之第8篇-工具賦能】深入 Cargo:依賴管理、構建配置與工作空間 (Workspace)

系列: Rust 精進之路:構建可靠、高效軟件的底層邏輯 作者: 碼覺客 發布日期: 2025-04-20 引言:超越構建,Cargo 是 Rust 生態的引擎 在我們的 Rust 學習之旅初期(第二篇),我們已經與 Cargo 有過初步的接觸。我們學會了使用 cargo new 創建項目骨架,用 cargo build 編…

#systemverilog# 進程控制問題#(八)關于#0 問題的使用(三)

今天,我們繼續研究一下上一節討論的問題。其實,還有一個小問題,我們來探討一下。 `timescale 1ns/10psmodule tb_top(); reg clk; reg reset;initial begin reset = 0; #10 reset = 1; #15 reset = 0; #50 $finish; endinitial beginfor(int i = 0; i < 4 ; i++)fork #…

Linux:簡單自定義shell

1.實現原理 考慮下?這個與shell典型的互動&#xff1a; [rootlocalhost epoll]# ls client.cpp readme.md server.cpp utility.h [rootlocalhost epoll]# ps PID TTY TIME CMD 3451 pts/0 00:00:00 bash 3514 pts/0 00:00:00 ps ?下圖的時間軸來表?事件的發?次序。其中時…

PLSQL語法入門--PL/SQL 基礎詳解

PL/SQL 基礎詳解 PL/SQL&#xff08;Procedural Language for SQL&#xff09;是 Oracle 數據庫中的一種過程式語言&#xff0c;它擴展了 SQL 的功能&#xff0c;允許開發者編寫復雜的程序邏輯。 一、匿名塊 解釋 匿名塊是 PL/SQL 的基本執行單位&#xff0c;它是一段獨立的…

Oracle--用戶管理

前言&#xff1a;本博客僅作記錄學習使用&#xff0c;部分圖片出自網絡&#xff0c;如有侵犯您的權益&#xff0c;請聯系刪除 用戶管理在 Oracle 數據庫中至關重要。一個服務器通常只運行一個 Oracle 實例&#xff0c;而一個 Oracle 用戶代表一個用戶群&#xff0c;他們通過該用…

UOS+N 卡 + CUDA 環境下 X86 架構 DeepSeek 基于 vLLM 部署與 Dify 平臺搭建指南

一、文檔說明 本文檔是一份關于 DeepSeek 在X86架構下通vLLM工具部署的操作指南&#xff0c;主要面向需要在UOSN卡CUDA環境中部署DeepSeek的技術人員&#xff0c;旨在指導文檔使用者完成從 Python 環境升級、vLLM 庫安裝、模型部署到 Dify 平臺搭建的全流程操作。 二、安裝Pyt…

操作系統之shell實現(下)

&#x1f31f; 各位看官好&#xff0c;我是maomi_9526&#xff01; &#x1f30d; 種一棵樹最好是十年前&#xff0c;其次是現在&#xff01; &#x1f680; 今天來學習C語言的相關知識。 &#x1f44d; 如果覺得這篇文章有幫助&#xff0c;歡迎您一鍵三連&#xff0c;分享給更…

Spark,流量統計案例

提前創好一個文件夾分為四個類 FlowBean中的代碼內容為&#xff1a;package org.example.flow; import org.apache.hadoop.io.Writable; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; //hadoop 序列化 //三個屬性&#xff1a;手機…

下載油管視頻 - yt-dlp

文章目錄 1. yt-dlp與you-get介紹1.1 主要功能對比1.2 使用場景1.3 安裝 2. 基本命令介紹2.1 默認下載視頻2.2 指定畫質和格式規則2.3 下載播放列表2.4 備注 3. 參考資料 之前只使用you-get下載b站視頻&#xff0c;當時了解you-get也可下載油管視頻&#xff0c;但之前無此需求&…

基于javaweb的SSM+Maven教材管理系統設計與實現(源碼+文檔+部署講解)

技術范圍&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬蟲、數據可視化、小程序、安卓app、大數據、物聯網、機器學習等設計與開發。 主要內容&#xff1a;免費功能設計、開題報告、任務書、中期檢查PPT、系統功能實現、代碼編寫、論文編寫和輔導、論文…

VS2022+QT環境配置及基本操作

參考文章 2025最新&#xff01;Visual Studio 2022 QT6.7 環境配置全攻略&#xff1a;一鍵搞定安裝與亂碼問題&#xff0c;開發效率翻倍&#xff01;&#xff08;全網最詳細教程&#xff0c;手把手教你搭建完美開發環境&#xff01;&#xff09;_vs2022 qt-CSDN博客 下載QT …

使用percona-toolkit同步mysql表數據

背景 做了主備mysql的配置以后&#xff0c;可能因為切換過程造成不一致的情況&#xff0c;這個時候可以處理的方式是全量導入再導出&#xff0c;這個有個問題就是操作的數據太多了 我們只需要數據補全同步即可 mysql的同步是基于binlog的&#xff0c;如果沒有記錄的部分的數據…

MDG 實現后端主數據變更后快照自動刷新的相關設置

文章目錄 前言實現過程BGRFC期初配置&#xff08;可選&#xff09;設置 MDG快照 BGRFC維護BP出站功能模塊 監控 前言 眾所周知&#xff0c;在MDG變更請求創建的同時&#xff0c;所有reuse模型實體對應的快照snapshot數據都會記錄下來。隨后在CR中&#xff0c;用戶可以修改這些…

重裝系統 之 Dell戴爾服務器 PowerEdge R750xs + window server2012r2 || 2016

因要求需要給新服務器裝個 win server2012或者2016系統 XXX使用U盤制作PE系統U盤安裝系統不行&#xff0c;適合普通win8&#xff0c;win10&#xff0c;win11U盤制作PE系統U盤安裝win10系統教程U盤制作PE系統U盤安裝win10系統教程https://mp.weixin.qq.com/s/t0W8aNJaHPAU8T78nh…

基于Spring Security 6的OAuth2 系列之二十六 - 終章

之所以想寫這一系列&#xff0c;是因為之前工作過程中使用Spring Security OAuth2搭建了網關和授權服務器&#xff0c;但當時基于spring-boot 2.3.x&#xff0c;其默認的Spring Security是5.3.x。之后新項目升級到了spring-boot 3.3.0&#xff0c;結果一看Spring Security也升級…

一鍵配置多用戶VNC遠程桌面:自動化腳本詳解

在當今遠程工作盛行的時代,高效且安全地管理多用戶遠程桌面訪問變得至關重要。本文將介紹一個強大的自動化腳本,該腳本能夠快速創建用戶并配置VNC遠程桌面環境,大大簡化了系統管理員的工作。 一、背景介紹 在Linux系統中,手動配置VNC服務器通常需要執行多個步驟,包括創建…

IOT項目——雙軸追光系統

雙軸太陽能追光系統 - ESP32實現 系統概述 這個系統使用&#xff1a; ESP32開發板2個舵機&#xff08;水平方向和垂直方向&#xff09;4個光敏電阻&#xff08;用于檢測光照方向&#xff09;適當的電阻&#xff08;用于光敏電阻分壓&#xff09; 接線示意圖 --------------…

Maven集成模塊打包使用

文章目錄 1.問題思考&#xff08;如何對集成模塊進行打包&#xff09;2.問題解決 &#xff08;如何對集成模塊進行打包&#xff09;3.使用者使用該jar包(jar包安裝本地倉庫和使用) 1.問題思考&#xff08;如何對集成模塊進行打包&#xff09; 思考&#xff1a;假設有這么一個場…

OpenVINO教程(二):圖片目標檢測推理應用

YOLO模型物體檢測 下面是一個簡單的python程序,他的功能是使用yolo11n模型對coco_bike.jpg照片進行檢測,并顯示檢測結果 代碼步驟如下: coco_bike.jpg照片加載yolo模型使用模型進行detect推理顯示推理結果 下面是完整的代碼 from pathlib import Pathimport urllib.request…