首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >机器学习算法:弱分类器中决策桩(Decision Stump)原理、手动计算与Python/Java双代码实战

机器学习算法:弱分类器中决策桩(Decision Stump)原理、手动计算与Python/Java双代码实战

原创
作者头像
jack.yang
发布2026-03-29 16:04:57
发布2026-03-29 16:04:57
50
举报
文章被收录于专栏:大模型系列大模型系列

关键词:机器学习、决策桩、Decision Stump、弱分类器、AdaBoost、手写代码、Python 决策桩、Java 决策桩、集成学习、基学习器

一句话答案:决策桩是仅含一个根节点和两个叶节点的决策树——它是最简单的弱分类器,却是 AdaBoost、GBDT 等强大集成算法的核心构建单元

如果你在搜索:

  • “什么是决策桩(Decision Stump)?”
  • “决策桩和普通决策树有什么区别?”
  • “为什么 AdaBoost 用决策桩?”
  • “如何手写决策桩并用于集成学习?”

那么,这篇文章就是为你写的——从单桩到强模型,一步不跳


一、什么是决策桩?它为何“简单却强大”?

决策桩(Decision Stump) 是深度为 1 的决策树:

  • 仅基于一个特征的一个阈值进行判断
  • 只有 if-else 一条规则
  • 输出为常数(分类:+1/-1;回归:均值)

🌳 结构示例

代码语言:javascript
复制
if (特征_i <= 阈值_t):
    预测 = A
else:
    预测 = B

💡 虽然单个决策桩性能弱(甚至不如随机猜测),但多个桩的加权组合可逼近任意函数——这是 Boosting 的核心思想。


二、决策桩 vs 完整决策树

特性

决策桩

完整决策树(如CART)

深度

1

≥2(通常更深)

特征使用

仅1个特征

多个特征组合

规则复杂度

单条 if-else

多层嵌套规则

拟合能力

弱(高偏差)

强(可能过拟合)

训练速度

⚡ 极快

较慢

主要用途

集成学习的基学习器

独立模型或集成组件

决策桩的价值不在单打独斗,而在“团结协作”


三、手工推演:构建最优决策桩(分类任务)

📊 数据集:二维点分类(目标:最小化加权误差)

x₁

x₂

y(真实标签)

样本权重 w

1

2

+1

0.2

2

3

-1

0.2

3

1

-1

0.2

4

4

+1

0.2

5

2

+1

0.2

目标:找到最佳特征 + 最佳阈值,使加权分类误差最小。


🔍 步骤1:尝试所有特征和候选阈值

候选1:基于 x₁ 分裂

排序 x₁: [1, 2, 3, 4, 5] → 候选阈值: 1.5, 2.5, 3.5, 4.5

  • t=2.5:
    • x₁ ≤ 2.5 → 样本1(+1), 2(-1) → 预测多数类?但需考虑权重!
    • 更优策略:对每个分支,选择使加权误差最小的预测值
      • 左分支:w₁=0.2(+1), w₂=0.2(-1) → 若预测 +1,误差=0.2;若预测 -1,误差=0.2 → 任选
      • 右分支:样本3(-1),4(+1),5(+1) → 预测 +1,误差=0.2(仅样本3错)
    • 总加权误差 = 0.2(左)+ 0.2(右)= 0.4
  • t=3.5:
    • 左:1(+1),2(-1),3(-1) → 预测 -1,误差=0.2(样本1错)
    • 右:4(+1),5(+1) → 预测 +1,误差=0
    • 总误差 = 0.2 ← 当前最优!
候选2:基于 x₂ 分裂

排序 x₂: [1,2,2,3,4] → 候选阈值: 1.5, 2.5, 3.5

  • t=1.5:
    • 左:样本3(-1) → 预测 -1,误差=0
    • 右:其余 → 预测 +1,误差=0.2(样本2错)
    • 总误差=0.2
  • t=2.5:
    • 左:x₂≤2.5 → 样本1(+1),3(-1),5(+1) → 预测 +1,误差=0.2(样本3错)
    • 右:样本2(-1),4(+1) → 预测?误差至少 0.2
    • 总误差 ≥ 0.4

最优决策桩

  • 特征:x₁
  • 阈值:3.5
  • 规则:if x₁ <= 3.5: 预测 = -1 else: 预测 = +1
  • 加权误差 = 0.2

💡 在 AdaBoost 中,该桩将被赋予较高权重,因其表现优于随机。


四、Python 实现:手写决策桩 + AdaBoost 集成

代码语言:javascript
复制
import numpy as np

class DecisionStump:
    def __init__(self):
        self.feature_idx = None
        self.threshold = None
        self.polarity = 1  # 1 表示 <= 为正类,-1 表示 > 为正类
        self.alpha = None  # AdaBoost 权重

    def fit(self, X, y, sample_weights):
        n_samples, n_features = X.shape
        min_error = float('inf')

        # 尝试每个特征
        for feat in range(n_features):
            feature_vals = X[:, feat]
            thresholds = np.unique(feature_vals)
            
            # 尝试每个阈值
            for t in thresholds:
                # 两种极性:<= 为正 或 > 为正
                for polarity in [1, -1]:
                    pred = np.ones(n_samples)
                    if polarity == 1:
                        pred[feature_vals <= t] = -1
                    else:
                        pred[feature_vals > t] = -1
                    
                    error = np.sum(sample_weights[pred != y])
                    
                    if error < min_error:
                        min_error = error
                        self.feature_idx = feat
                        self.threshold = t
                        self.polarity = polarity
        
        # 避免除零
        if min_error == 0:
            min_error = 1e-10
        elif min_error == 1:
            min_error = 1 - 1e-10

        # 计算桩的权重(用于AdaBoost)
        self.alpha = 0.5 * np.log((1 - min_error) / min_error)

    def predict(self, X):
        n_samples = X.shape[0]
        pred = np.ones(n_samples)
        if self.polarity == 1:
            pred[X[:, self.feature_idx] <= self.threshold] = -1
        else:
            pred[X[:, self.feature_idx] > self.threshold] = -1
        return pred

# === AdaBoost with Decision Stumps ===
class AdaBoost:
    def __init__(self, n_stumps=10):
        self.n_stumps = n_stumps
        self.stumps = []

    def fit(self, X, y):
        n_samples = X.shape[0]
        weights = np.full(n_samples, 1 / n_samples)

        for _ in range(self.n_stumps):
            stump = DecisionStump()
            stump.fit(X, y, weights)
            pred = stump.predict(X)

            # 更新样本权重
            correctness = (pred == y)
            weights *= np.exp(-stump.alpha * correctness.astype(int))
            weights /= np.sum(weights)  # 归一化

            self.stumps.append(stump)

    def predict(self, X):
        stump_preds = np.array([stump.alpha * stump.predict(X) for stump in self.stumps])
        return np.sign(np.sum(stump_preds, axis=0))

# 测试
X = np.array([[1,2], [2,3], [3,1], [4,4], [5,2]])
y = np.array([1, -1, -1, 1, 1])  # +1/-1

ada = AdaBoost(n_stumps=3)
ada.fit(X, y)
print("预测:", ada.predict(X))  # 应接近 [1, -1, -1, 1, 1]

五、Java 实现:决策桩核心逻辑

代码语言:javascript
复制
public class DecisionStump {
    private int featureIndex;
    private double threshold;
    private int polarity; // 1 or -1
    private double alpha;

    public void fit(double[][] X, int[] y, double[] weights) {
        int nSamples = X.length;
        int nFeatures = X[0].length;
        double minError = Double.MAX_VALUE;

        for (int f = 0; f < nFeatures; f++) {
            // 获取特征列并去重
            java.util.Set<Double> uniqueVals = new java.util.HashSet<>();
            for (double[] row : X) uniqueVals.add(row[f]);
            Double[] thresholds = uniqueVals.toArray(new Double[0]);

            for (double t : thresholds) {
                for (int pol : new int[]{1, -1}) {
                    int[] pred = new int[nSamples];
                    java.util.Arrays.fill(pred, 1);
                    for (int i = 0; i < nSamples; i++) {
                        if ((pol == 1 && X[i][f] <= t) || (pol == -1 && X[i][f] > t)) {
                            pred[i] = -1;
                        }
                    }

                    double error = 0.0;
                    for (int i = 0; i < nSamples; i++) {
                        if (pred[i] != y[i]) error += weights[i];
                    }

                    if (error < minError) {
                        minError = error;
                        featureIndex = f;
                        threshold = t;
                        polarity = pol;
                    }
                }
            }
        }

        // 防止数值问题
        if (minError == 0) minError = 1e-10;
        if (minError == 1) minError = 1 - 1e-10;
        alpha = 0.5 * Math.log((1 - minError) / minError);
    }

    public int[] predict(double[][] X) {
        int n = X.length;
        int[] pred = new int[n];
        java.util.Arrays.fill(pred, 1);
        for (int i = 0; i < n; i++) {
            if ((polarity == 1 && X[i][featureIndex] <= threshold) ||
                (polarity == -1 && X[i][featureIndex] > threshold)) {
                pred[i] = -1;
            }
        }
        return pred;
    }

    public double getAlpha() { return alpha; }
}

💡 完整 AdaBoost 需额外封装样本权重更新逻辑。


六、为什么 AdaBoost 偏爱决策桩?

  1. 简单快速:训练一个桩只需 O(n·d) 时间(n=样本数,d=特征数)
  2. 不易过拟合:单桩泛化能力弱,但集成后偏差-方差平衡
  3. 理论保证:Freund & Schapire 证明 AdaBoost + 决策桩可达到指数级误差下降
  4. 特征选择隐式完成:每个桩自动选择当前最有判别力的特征

🌰 实际中,100~1000 个决策桩组成的 AdaBoost 模型常胜过单棵深度树。


七、决策桩的适用场景总结

场景

是否推荐

作为 AdaBoost / SAMME 的基学习器

✅ 强烈推荐

需要极快训练速度的在线学习

高维稀疏数据(如文本)

✅(每个桩选一个词)

独立使用做预测

❌(性能太弱)

需要可解释规则

⚠️ 单桩可解释,但集成后不可解释


✅ 结语

决策桩是大道至简的典范——它用最朴素的 if-else 规则,支撑起了 Boosting 革命。理解决策桩,是理解 AdaBoost、Gradient Boosting 乃至 XGBoost 的第一步。

记住:在机器学习中,弱学习器的智慧,在于它们懂得如何协作

现在,你已经能:

  • 手动构建最优决策桩
  • 用 Python/Java 实现决策桩
  • 将其集成到 AdaBoost 中
  • 理解其在集成学习中的不可替代性

相关链接

  • 📂 大模型技术专栏: 欢迎您到访 「大模型系列」。 在这个由参数驱动、以数据为燃料的新智能时代,大语言模型(LLM)已不再是实验室里的前沿概念,而是正在重塑搜索、办公、编程、教育、医疗乃至整个数字世界的底层引擎。从 GPT 到 Llama,从 Claude 到 Qwen,从推理到多模态,大模型正以前所未有的速度进化——它们既是工具,也是平台,更可能是下一代人机交互的“操作系统”。 本系列将带你:
    • 🔍 深入原理:从 Transformer 架构、注意力机制到训练范式(预训练、微调、RLHF);
    • ⚙️ 动手实践:本地部署、模型微调、RAG 构建、Agent 设计等实战指南;
    • 🧠 理解边界:幻觉、偏见、安全对齐、推理瓶颈与当前能力天花板;
    • 🌍 洞察趋势:开源 vs 闭源、端侧部署、MoE 架构、世界模型与 AGI 路径;
    • 💼 落地应用:如何在企业中安全、高效、低成本地集成大模型能力。

    无论你是想写代码调用 API 的开发者,设计 AI 产品的 PM,评估技术路线的管理者,还是单纯好奇智能本质的思考者,这里都有值得你驻足的内容。 不追 hype,只讲逻辑;不谈玄学,专注可复现的认知。 让我们一起,在这场百年一遇的智能革命中,看得更清,走得更稳 https://cloud.tencent.com/developer/column/107314

  • 👤 关于作者专注技术落地,深耕硬核干货 本文作者致力于大模型相关技术的生态建设与实战落地。不同于浅层的概念科普,作者坚持 “手算 + 代码” 的深度分享模式,主张通过手动推演理解算法本质,结合生产级代码验证理论可行性。 请关注我主页:https://cloud.tencent.com/developer/user/2276240

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、什么是决策桩?它为何“简单却强大”?
    • 🌳 结构示例
  • 二、决策桩 vs 完整决策树
  • 三、手工推演:构建最优决策桩(分类任务)
    • 📊 数据集:二维点分类(目标:最小化加权误差)
    • 🔍 步骤1:尝试所有特征和候选阈值
      • 候选1:基于 x₁ 分裂
      • 候选2:基于 x₂ 分裂
  • 四、Python 实现:手写决策桩 + AdaBoost 集成
  • 五、Java 实现:决策桩核心逻辑
  • 六、为什么 AdaBoost 偏爱决策桩?
  • 七、决策桩的适用场景总结
  • ✅ 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档