跳至主要內容

    Random Forest:很多棵樹怎麼一起做判斷?

    從bootstrap與特徵抽樣,理解平均、相關誤差與OOB評估

    sunny 桑尼· · 17 MIN READ · 113 次瀏覽

    Random Forest:很多棵樹怎麼一起做判斷?
    本文目錄

    決策樹的規則容易閱讀,但換一批訓練資料,第一刀就可能不同。Random Forest的出發點是:讓多棵有差異的樹一起預測,降低對某一棵樹的依賴。

    「很多棵」還不夠。如果把完全相同的樹複製100次,平均後仍是原來的答案。森林需要在每棵樹的品質與樹之間的差異取得平衡,常見做法是bootstrap抽樣,加上每個節點重新抽候選特徵。

    這篇會把兩種抽樣、預測聚合與OOB拆開,再跑一個Iris分類範例。你會看到訓練、OOB與保留測試分數各自回答什麼,也能辨識「更多樹一定更好」這種過度簡化。

    先看單樹與森林的預測區域

    圖1|左為來源設定的橢圓ground truth,中間單棵剪枝樹,右為Random Forest。軸向分區可以用很多小區塊近似彎曲區域;這是來源教學示例,不是本站測試或所有資料都會改善的保證。來源:Google,Random forests,CC BY 4.0,原圖未改。

    接著只看森林裡三棵樹各自的結果:

    圖2|三棵樹各自的邊界都有不規則部分;來源透過bagging與特徵抽樣讓它們產生差異。它們是同一教學森林的前三棵成員,不是三個訓練階段,也不是下一棵在修正上一棵的殘差。來源同圖1,原圖未改。

    森林在訓練時,各樹的工作可以相對獨立進行。這和下一篇Boosting逐輪針對目前模型的不足加入新模型不同。

    第一步:每棵樹拿到不同的bootstrap樣本

    傳統bootstrap做法,是從n筆訓練資料「有放回」抽n次。抽過的樣本仍可再抽,因此某些樣本重複,某些完全沒被這棵樹看到。

    用A到F六個樣本,自訂三次抽樣:

    成員抽到的六次結果沒抽到的樣本
    樹1A、A、B、D、E、FC
    樹2B、C、C、D、D、FA、E
    樹3A、B、B、C、E、ED、F

    這些是為了方便追蹤而固定的教學抽樣,不是特別選來展示準確率,也不是每次都恰好漏同樣多樣本。重複的樣本在該棵樹中會有較大影響,並沒有產生新的獨立觀測。

    一筆固定樣本在一次抽樣沒中的機率是1−1/n;連續n次都沒中:

    P(某樣本未被抽到) = (1 − 1/n)ⁿ
    

    n=6時約0.334898。n很大時趨近e⁻¹≈0.367879,所以常見的「大約63.2%不同樣本被看到」是大樣本近似,不是每棵樹固定拿到這個比例。

    有些工具也可用不放回抽樣或較小的抽樣量;那時要按實際設定解讀,不能沿用同一個公式。

    第二步:每個節點只先比較部分特徵

    若某個特徵特別強,每棵樹即使看了不同樣本,也可能都先用它切,產生相似的錯誤。Random Forest通常在每個節點,隨機選部分候選特徵,再從候選中找較好的切分。

    圖3|來源共有f1–f5;藍色是該節點受測的候選,白色未受測,紅框為最後選用者。根節點與左子節點各測三個候選,右子節點只測兩個;不是整棵樹永遠只能用同一組特徵。來源同圖1,原圖未改。

    減少候選特徵通常增加樹之間的差異,但也可能讓單棵樹漏掉有用關係。候選數太少、特徵很多卻只有少數有效時,就可能傷害預測;不是越隨機越好。

    樹的深度、每葉最少樣本與抽樣量也都會影響取捨。森林可以使用較深的樹,但不等於所有任務都該完全不設限制。

    第三步:分類與迴歸怎麼聚合

    迴歸森林常平均各樹的數值預測。分類則要看實作:可以用硬類別多數決,也可以先平均各樹的機率型輸出,再選分數最高的類別。

    例如,假設三棵樹對同一個二元新輸入給出正類分數0.9、0.4、0.4。每棵先以0.5轉類別,會得到1、0、0,多數決為0;但平均分數約0.566667,結果為1。這是自訂聚合例,沒有擬合資料,也不代表這些分數已校準。

    本文實作使用的scikit-learn RandomForestClassifier,採平均各樹的類別機率型輸出後選類別。看到「投票」二字時,要先確認指的是硬標籤還是平均分布。迴歸平均、分類平均後的數字也不自動帶有可靠的不確定性區間。

    平均為何能減少部分不穩定

    在簡化數學情境下,假設B個估計的變異都為σ²,任兩個之間的相關係數都為ρ,平均後變異為:

    Var(平均) = σ²[ρ + (1−ρ)/B]
    

    若ρ=0,變異隨1/B下降;若ρ=1,平均相同的估計沒有幫助。ρ介於兩者時,增加成員只能降低其中一部分,無法把共同錯誤消掉。

    這是等變異、等相關下的解釋式,不是宣稱真實森林每對樹都有同一ρ,也不是分類準確率的公式。共同偏差、無用特徵、錯誤標籤與資料洩漏,不會因多訓練幾棵樹而自動消失。

    圖4|來源重新生成50組訓練資料,每組50筆,並在1000個共同測試位置評估;每組的Bagging另作bootstrap抽樣,噪音標準差0.1;左為單樹,右為Bagging(Tree)。上方的預測束變窄,下方綠色variance項減少,是這組回歸示例的觀察。這張用的是BaggingRegressor,未加入森林的逐節點特徵抽樣,不能冒稱RandomForest實驗。來源:scikit-learn developers,Single estimator versus bagging: bias-variance decomposition,BSD 3-Clause,原圖未改。

    這組來源中bias²略增加、variance下降更多,總誤差降低。它適合解釋平均高變異模型的動機,不適合用來保證任何資料都會得到同樣差值。

    OOB:只讓沒看過該樣本的樹來評它

    前面表格裡,每棵樹沒抽到的樣本稱out-of-bag。對某一筆訓練樣本,收集「沒有把它放進bootstrap訓練資料」的樹,用這些樹的聚合預測和它的標籤比較,就得到OOB評估的基本做法。

    圖5|來源用300筆分塊畫示意:上方樹A拿201–300作OOB,下方樹B拿101–200作OOB。這不表示實際抽樣必須按連續編號分塊,也不表示每棵樹恰好漏1/3。其他樹與其他樣本有各自的抽樣紀錄。來源:Google,Decision Forests glossary,CC BY 4.0,原圖未改。

    OOB可以在不另外切一份驗證集的情況下提供部分評估資訊,但有條件:樣本結構需適合這種抽樣,而且要有足夠樹,讓每個樣本有足夠的OOB預測。它不自動模擬未來時間、陌生使用者或不同群組。

    若一直用OOB調很多設定,也會適應這份評估;若前處理或特徵選擇先看過全體訓練標籤,OOB不會自動替你重做整條流程。因此,重要選擇仍可用符合任務的交叉驗證,並保留最後的測試資料。

    圖6|來源500筆、25維、15個資訊特徵的合成二元分類;比較sqrt、log2、None三種max_features,樹數15到150。縱軸為1−oob_score,不是測試集錯誤;曲線有波動,不能保證每增加一棵都降低。來源:scikit-learn developers,OOB Errors for Random Forests,BSD 3-Clause,原圖未改。

    更多樹通常走向穩定,不是品質保證

    圖7|原圖縱軸寫Accuracy (out-of-bag),不能改稱獨立test accuracy。這條來源曲線約在0.865附近趨於平臺,仍有細微起伏;它沒有證明每一步都單調改善,也沒有保證所有超參數都不會過擬合。來源:Google,Random forests,CC BY 4.0,原圖未改。

    固定其他設定,增加樹數常讓有限成員造成的隨機波動減小;但它不會修好原本不合適的森林或資料。樹多了,模型大小、訓練與預測成本也會增加,應在品質穩定後檢查是否值得繼續加。

    判斷過擬合要看適當保留資料,而不是只看到訓練準確率高就判定失敗,也不能看到「森林」二字就宣告永不過擬合。

    先用Python核對抽樣與聚合

    這段只用標準函式庫,重算本文固定的三次抽樣、六樣本OOB機率與自訂平均分數,不會擬合任何模型。

    names = list("ABCDEF")
    draws = [[0, 0, 1, 3, 4, 5], [1, 2, 2, 3, 3, 5], [0, 1, 1, 2, 4, 4]]
    for i, indices in enumerate(draws, 1):
        missing = [name for j, name in enumerate(names) if j not in indices]
        print("tree", i, "OOB:", missing)
    print("P(unseen), n=6:", round((1 - 1 / 6) ** 6, 6))
    probabilities = [0.9, 0.4, 0.4]
    mean_probability = sum(probabilities) / len(probabilities)
    hard_label = int(sum(p >= 0.5 for p in probabilities) > len(probabilities) / 2)
    print("hard vote:", hard_label, "mean score:", round(mean_probability, 6),
          "mean-score label:", int(mean_probability >= 0.5))
    

    再跑一個真的森林:Iris小型分類

    以下另做真實工具操作。實測環境為WSL Linux、Python 3.10.12、NumPy 1.26.3、scikit-learn 1.5.1;這是重現本文結果的版本組合,不宣稱是目前最新版本。可先沿Python最小工具箱建立Python 3.10環境,再安裝:

    python -m pip install "numpy==1.26.3" "scikit-learn==1.5.1"
    

    程式使用工具內附Iris資料,不需另抓檔案。先固定保留20%測試,再在剩餘120筆上訓練;設定在執行前固定,沒有看測試結果後繼續調參。這組數值欄位直接給樹使用,不涉及需在全資料估計的縮放。

    import sklearn
    from sklearn.datasets import load_iris
    from sklearn.ensemble import RandomForestClassifier
    from sklearn.metrics import accuracy_score, confusion_matrix
    from sklearn.model_selection import train_test_split
    
    X, y = load_iris(return_X_y=True)
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, stratify=y, random_state=42)
    model = RandomForestClassifier(
        n_estimators=200, max_features="sqrt", min_samples_leaf=2,
        bootstrap=True, oob_score=True, random_state=42, n_jobs=1)
    model.fit(X_train, y_train)
    predictions = model.predict(X_test)
    print("sklearn:", sklearn.__version__)
    print("train/test:", len(y_train), len(y_test))
    print("train accuracy:", round(model.score(X_train, y_train), 6))
    print("OOB accuracy:", round(model.oob_score_, 6))
    print("test accuracy:", round(accuracy_score(y_test, predictions), 6))
    print("confusion matrix (rows=true, columns=predicted):")
    print(confusion_matrix(y_test, predictions, labels=[0, 1, 2]))
    

    本環境實際得到:

    項目本次實測

    訓練筆數/測試筆數
    120/30
    訓練accuracy
    0.983333
    OOB accuracy
    0.95
    保留測試accuracy
    0.966667

    混淆矩陣的列是真實類別、欄是預測類別,順序皆為0、1、2:

    [[10  0  0]
     [ 0  9  1]
     [ 0  0 10]]
    

    這次類別1的十筆,有一筆被判成類別2。30筆測試上差一筆就相差約3.33個百分點,不能把0.966667當作普遍穩定成效,更不能直接套用到其他資料。這個小例只證明流程可執行,且三種評估確實不同;不是對所有分類器的公平比較。

    若要改參數,請把選擇留在訓練/開發資料,最後測試集只用於事先決定的驗收。類別不平衡時,也要回看Precision、Recall與混淆矩陣,不要只盯accuracy。

    Random Forest提供的是把多個有差異的樹合起來的方法。當你能清楚說明「每棵看了什麼、每次切分比較什麼、最後如何聚合、用哪批資料評估」,才算理解這座森林。

    來源與圖片授權

    本文的三次固定抽樣、分數聚合例與Iris實跑為本站新增。七張原圖的完整來源實驗未由本站重跑,不把來源曲線當本站測試結果。

    BSD 3-Clause License
    
    Copyright (c) 2007-2026 The scikit-learn developers.
    All rights reserved.
    
    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
    
    * Redistributions of source code must retain the above copyright notice, this
      list of conditions and the following disclaimer.
    
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
    
    * Neither the name of the copyright holder nor the names of its
      contributors may be used to endorse or promote products derived from
      this software without specific prior written permission.
    
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    

    封面將Google圖1完整等比縮放加白邊,依CC BY 4.0使用。手機圖1–2把原三面板依左到右順序改為上下排列;手機圖4依序為單樹預測、Bagging預測、共同預測圖例、單樹誤差分解、Bagging誤差分解、共同誤差圖例,保留曲線、刻度與圖例。Google改圖依CC BY 4.0,scikit-learn改圖依BSD 3-Clause使用;只重排原圖,未重畫數值。

    留言0

    登入後就能留言。

    登入
    sunny 桑尼
    sunny 桑尼

    AI 資料科學 · 產品經理 · 蒙奇 D 桑尼

    專注於 AI 工具應用與資料科學教育,致力於讓每個人都能用 AI 提升工作效率。曾任職於科技業,現為全職教育創作者。

    讀更多文章 →想自己動手試?領免費 Starter Kit →