National Central University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try an AI Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

3

Local AI

--

Uedu Code

--

中央大學 AQI 78 24°C PM2.5 18
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 09

決策樹

用 numpy 寫:Gini 與分割,深度與過擬合
Interaction實作 30 minute(s)

Learning Objectives

  • 算出一群資料的 Gini 不純度,說明它代表什麼
  • 用 numpy 寫出找最佳分割的程式,並說明一棵樹怎麼遞迴長出來
  • 讀懂 scikit-learn 印出的決策樹,並說明決策樹為什麼不受特徵尺度影響
  • 從不同深度的訓練與測試準確率,判斷欠擬合與過擬合

問一連串是非題

上一課的 k 近鄰靠距離判斷,這一課換一條路。想像你要猜一位同學會不會及格,可能會這樣問:

小考平均 ≤ 59 嗎?
├─ 是 → 缺席次數 ≤ 2 嗎? …
└─ 否 → 大概會及格

這就是決策樹(decision tree):每個節點問一個「某個特徵是否小於等於某個門檻」的問題,資料依答案往左或往右走,走到最底下的葉節點就得到預測。

它的優點是人讀得懂:你可以把整棵樹印出來,一路說明為什麼得到這個預測。

要長出一棵樹,只要回答一個問題:這一群資料,問哪一題最好? 好的問題會把資料分成兩群,每一群裡的類別盡量單純。「單純的程度」要先有個數字才能比較,最常用的是 Gini 不純度:

Gini = 1 − (每個類別比例的平方加總)

全部是同一類時 Gini 是 0;兩類各半時是 0.5,這是兩個類別時的最大值。一次分割的好壞,就看分割後兩邊的 Gini(依筆數加權平均)比分割前減少了多少。

逐步看:算 Gini,找出最好的門檻

六位同學的讀書時數與是否及格。程式把每個候選門檻都試一次:左邊放小於等於門檻的,右邊放其他的,算 Gini 減少多少。候選門檻是相鄰兩個不同值的中點。

從一次分割長成一棵樹

找到最好的分割之後,資料分成左右兩群。接著對每一群重複同樣的事,直到:

  • 這一群只剩一種類別(Gini 是 0),或
  • 到達我們限制的深度(max_depth)。

這是遞迴(recursion)。每一步都只挑眼前最好的分割,不會回頭修改,所以長出來的不保證是全部可能的樹裡最好的一棵,但速度快,結果通常也夠好。

葉節點的預測就是這一群裡最多的類別。接下來的程式把這些步驟串起來,每一步都看得到。

逐步看:用 numpy 長一棵樹

四筆資料、兩個特徵,深度限制 2。注意右上角的呼叫堆疊:grow 會呼叫自己,每一層看到的 X、y 越來越少。

互動練習:自己切一棵樹

橫軸是小考平均、縱軸是缺席次數,背景顏色是目前這棵樹的預測。先自己選特徵與門檻切切看,再和程式找到的最佳分割比較。「自動長」可以看深度增加時準確率怎麼變。

改用 scikit-learn

實際工作不會自己寫,scikit-learn 的 DecisionTreeClassifier 做的是同一件事:預設 criterion="gini",max_depth=None(一直長到葉節點單純,或筆數少於 min_samples_split=2 為止)。

export_text 可以把樹印成文字。決策樹只比大小,不算距離,所以特徵乘以 1000 只會讓門檻跟著乘以 1000,切法不變,也就不需要標準化。這是它和第 8 課 k 近鄰很大的不同。

逐步看:scikit-learn 的決策樹

用 students.csv(程式產生的模擬資料,不是任何學校的真實資料)預測是否及格,限制深度 2,把樹印出來讀一讀。

切了之後預測沒變,為什麼還要切?

上面那棵樹右半邊的兩個葉節點都預測 class: 1,看起來像白切了一刀。

Gini 看的是比例,不是最後的預測。把一群資料切成一群比較混雜、一群幾乎單純,兩邊都還是多數及格,預測不變,但 Gini 確實下降,而且再往下切時可能就會分出不及格的那一群。所以 Gini 減少量和「準確率有沒有提高」不是同一件事。

逐步看:深度怎麼影響準確率

同一份資料,深度從 1 試到 12。看輸出的四欄:深度、葉節點數、訓練準確率、測試準確率;最後畫成折線圖。

控制樹的複雜度

沒有限制的決策樹可以把訓練資料背到 100%(只要沒有特徵完全相同、標籤卻不同的資料),它會為了一兩筆資料多開一個分支,這就是過擬合。常用的限制:

參數 意思 預設
max_depth 樹最多幾層 None(不限制)
min_samples_split 一個節點至少幾筆才可以再切 2
min_samples_leaf 每個葉節點至少幾筆 1

上面深度 6 的測試準確率是 0.798,比較高,但測試資料只有 94 筆,差一兩筆就差 1~2%,而且用測試資料挑深度本身就會讓這個數字偏樂觀。正確的挑法是交叉驗證(第 14 課)。

決策樹還有一個弱點:不穩定。 訓練資料稍微變動,第一刀可能就切在別的地方,整棵樹跟著不同。下一課的隨機森林正是利用這一點。

互動練習:看決策邊界的形狀

實心是訓練資料、空心是測試資料,背景顏色是模型的預測。決策樹的邊界長得和 k 近鄰很不一樣,切換看看。

實作:算一次分割的 Gini 減少量

完成 gini(y) 與 split_gain(x, y, t):split_gain 回傳用門檻 t 切特徵 x 之後,Gini 減少多少。

你的輸出
預期輸出

小測驗:決策樹

1.

下面程式會印出什麼?

import numpy as np
y = np.array([0, 0, 0, 1])
p = np.bincount(y) / len(y)
print(round(1 - (p ** 2).sum(), 3))
2.

六筆資料的類別是 [0, 0, 0, 1, 1, 1],某個門檻剛好把前三筆與後三筆分開。Gini 減少多少?

3.

把一個特徵的單位從公尺改成公分(數字乘以 100),決策樹會怎樣?

4.

DecisionTreeClassifier() 不設任何參數,訓練準確率通常接近 1.0。這代表什麼?

5.

一次分割之後,左右兩個葉節點都預測「及格」。這次分割的效果是?

第 1 次作答

重點整理

  • 決策樹用一連串「特徵 ≤ 門檻」的問題分類,葉節點預測該群最多的類別,可以印出來讀。
  • Gini = 1 − 比例平方的加總;每一步挑「Gini 減少最多」的分割,然後對左右兩群遞迴。
  • 決策樹只比大小,不受特徵尺度影響,不需要標準化;邊界由水平線與垂直線組成。
  • 越深越容易過擬合:訓練準確率可以到 1.0,測試不一定跟著上升;用 max_depth、min_samples_leaf 控制,並用交叉驗證挑(第 14 課)。
  • 一棵樹很不穩定。下一課把很多棵樹組合起來:隨機森林與梯度提升。
這一課完成了!
Next lesson:隨機森林與梯度提升
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。