【机器学习基础算法:从零实现到精通】第3章 监督学习:树模型与集成方法

【学习路线】AI开发工程师成长指南:从机器学习基础到大模型应用 AI开发是一个快速发展的领域,需要扎实的数学基础、编程能力和持续学习的态度。从机器学习基础到深度学习框架,从大模型开发到工程化部署,每个阶段都有其重点和挑战。建议根据个人兴趣和职业规划选择专精方向,同时保持对前沿技术的敏感度。 阅读详情

目录

3.1 决策树与分裂准则

3.1.1 递归分裂与信息论准则

3.1.2 回归树与方差最小化

3.1.3 树的剪枝与复杂度控制

3.2 Bagging与随机森林

3.2.1 Bootstrap聚合原理

3.2.2 随机森林的随机性注入

3.2.3 方差分析与超参数调优

3.3 Boosting与加性模型

3.3.1 AdaBoost与指数损失

3.3.2 梯度提升树(Gradient Boosting)

3.3.3 XGBoost与LightGBM算法实现


3.1 决策树与分裂准则

3.1.1 递归分裂与信息论准则

决策树通过递归二分构建层次化预测规则。ID3算法采用信息增益作为分裂准则,基于信息论中熵的减少量度量特征对目标变量的解释能力,优先选择具有最大信息增益的特征进行分裂,该策略倾向于选择具有较多取值的特征。C4.5算法引入信息增益率对ID3的偏置进行修正,通过分裂信息(Split Information)对增益进行归一化,抑制多值特征的过度优先。CART算法采用Gini指数作为不纯度度量,其计算复杂度较低且对二分类问题具有直观解释,通过二元分裂将父节点的Gini不纯度降至最低,最优切分点的搜索通过对有序特征值进行线性扫描实现,预处理排序后总复杂度控制在O(NlogN) 。

3.1.2 回归树与方差最小化

回归树将平方误差最小化作为分裂目标,叶子节点的预测值为该节点内样本响应变量的均值。分裂准则评估左右子节点方差加权和的减少量,寻找使方差减少最大化的切分点与特征。有序切分点的直方图加速技术通过分箱近似连续特征的有序统计量,在牺牲一定精度的前提下将复杂度从O(N) 降至分箱数目的线性关系。缺失值处理采用代理分裂机制,当主分裂特征缺失时,预先计算的替代分裂特征按照与主分裂的相似度排序,选择与主分裂保持最大一致性的代理特征进行样本划分,若代理特征亦缺失则继续尝试次级代理或采用多数方向分配。

3.1.3 树的剪枝与复杂度控制

代价复杂度剪枝(Cost-Complexity Pruning)通过引入复杂度参数α 平衡树的拟合优度与模型复杂度,剪枝序列从最复杂的完全生长树开始,递归地剪除使单位叶节点减少带来的误差增加最小的子树,形成嵌套的子树序列,通过交叉验证或独立验证集选择最优α 。最小描述长度准则从信息论角度评估模型,将树的编码长度与数据的编码长度之和作为目标函数,优先选择使总描述长度最小的树结构。悲观剪枝基于训练误差的统计修正,假设训练误差服从二项分布,计算置信区间上限作为误差估计,当子树的修正误差低于其根节点的修正误差时执行剪枝,避免基于训练集的过度乐观估计。

脚本5:决策树算法与剪枝实现

Python

"""
脚本5:决策树算法、分裂准则与剪枝策略实现
涉及内容:
- 3.1.1.3 CART算法:Gini指数与O(N log N)最优切分点搜索
- 3.1.2.1 最小二乘回归树的MSE准则与叶子均值预测
- 3.1.2.2 有序切分点的直方图加速实现
- 3.1.3.1 代价复杂度剪枝(Cost-Complexity Pruning)与α参数调优
- 3.1.3.3 代理分裂(Surrogate Splits)处理缺失值

使用方式:
python decision_tree_implementation.py
依赖:numpy, matplotlib, pandas, scipy
"""

import numpy as np
import matplotlib.pyplot as plt
from collections import Counter
from typing import List, Tuple, Optional, Dict
import warnings

class Node:
    """决策树节点结构"""
    def __init__(self, depth=0):
        self.depth = depth
        self.feature_idx = None
        self.threshold = None
        self.left = None
        self.right = None
        self.value = None  # 叶子节点预测值
        self.impurity = None
        self.n_samples = 0
        # 代理分裂(Surrogate Splits)
        self.surrogate_splits = []  # [(feature_idx, threshold, direction)]
        
class DecisionTreeCART:
    """
    CART算法实现(分类与回归)
    支持Gini指数(分类)与MSE(回归),含代价复杂度剪枝与代理分裂
    """
    
    def __init__(self, 
                 task='classification',
                 max_depth=10,
                 min_samples_split=2,
                 min_samples_leaf=1,
                 ccp_alpha=0.0,  # 代价复杂度剪枝参数
                 max_bins=None,  # 直方图加速分箱数
                 use_surrogate=False):  # 是否使用代理分裂
        self.task = task
        self.max_depth = max_depth
        self.min_samples_split = min_samples_split
        self.min_samples_leaf = min_samples_leaf
        self.ccp_alpha = ccp_alpha
        self.max_bins = max_bins
        self.use_surrogate = use_surrogate
        self.tree_ = None
        self.classes_ = None
        
    def _gini(self, y):
        """计算Gini不纯度: 1 - Σ(p_i^2)"""
        if len(y) == 0:
            return 0.0
        counts = np.bincount(y)
        probs = counts / len(y)
        return 1.0 - np.sum(probs ** 2)
    
    def _mse(self, y):
        """计算均方误差(回归)"""
        if len(y) == 0:
            return 0.0
        return np.mean((y - np.mean(y)) ** 2)
    
    def _impurity(self, y):
        """根据任务选择不纯度度量"""
        if self.task == 'classification':
            return self._gini(y)
        return self._mse(y)
    
    def _best_split(self, X, y, feature_indices):
        """
        最优切分点搜索,支持直方图加速
        返回: (最佳特征索引, 切分阈值, 不纯度减少量, 左右索引)
        """
        best_gain = -np.inf
        best_feature = None
        best_threshold = None
        best_left_idx = None
        best_right_idx = None
        
        parent_impurity = self._impurity(y)
        n_samples = len(y)
        
        for feature_idx in feature_indices:
            X_feature = X[:, feature_idx]
            
            # 直方图加速:分箱处理
            if self.max_bins is not None and len(np.unique(X_feature)) > self.max_bins:
                # 使用分位数分箱
                percentiles = np.linspace(0, 100, self.max_bins + 1)
                bin_edges = np.percentile(X_feature, percentiles[1:-1])
                # 使用分箱中点作为候选切分点
                candidate_thresholds = bin_edges
            else:
                # 精确有序切分点:相邻唯一值的中点
                sorted_values = np.unique(X_feature)
                if len(sorted_values) <= 1:
                    continue
                candidate_thresholds = (sorted_values[:-1] + sorted_values[1:]) / 2
            
            # O(N log N)扫描(已排序前提下)
            for threshold in candidate_thresholds:
                left_mask = X_feature <= threshold
                right_mask = ~left_mask
                
                if np.sum(left_mask) < self.min_samples_leaf or \
                   np.sum(right_mask) < self.min_samples_leaf:
                    continue
                
                left_impurity = self._impurity(y[left_mask])
                right_impurity = self._impurity(y[right_mask])
                
                # 加权不纯度
                n_left = np.sum(left_mask)
                n_right = np.sum(right_mask)
                weighted_impurity = (n_left * left_impurity + n_right * right_impurity) / n_samples
                
                # 不纯度减少(信息增益)
                gain = parent_impurity - weighted_impurity
                
                if gain > best_gain:
                    best_gain = gain
                    best_feature = feature_idx
                    best_threshold = threshold
                    best_left_idx = left_mask
                    best_right_idx = right_mask
        
        return best_feature, best_threshold, best_gain, best_left_idx, best_right_idx
    
    def _find_surrogate_splits(self, X, y, best_feature, best_threshold, best_left_mask):
        """
        3.1.3.3 代理分裂计算
        寻找与主分裂最相似的替代特征(基于分裂一致性)
        """
        if not self.use_surrogate:
            return []
        
        surrogates = []
        n_samples = len(y)
        
        # 主分裂的方向(左/右)
        primary_direction = best_left_mask.astype(int)
        
        # 遍历其他特征寻找最佳代理
        for feature_idx in range(X.shape[1]):
            if feature_idx == best_feature:
                continue
            
            X_feature = X[:, feature_idx]
            
            # 如果该特征也有缺失值,跳过(或寻找次级代理)
            if np.any(np.isnan(X_feature)):
                continue
            
            # 寻找最佳切分点(基于与主分裂的一致性)
            sorted_values = np.unique(X_feature)
            if len(sorted_values) <= 1:
                continue
            
            thresholds = (sorted_values[:-1] + sorted_values[1:]) / 2
            
            best_agreement = -1
            best_surrogate_threshold = None
            best_direction = None  # 1表示同向,-1表示反向
            
            for threshold in thresholds:
                left_mask = X_feature <= threshold
                # 计算与主分裂的一致性(考虑同向和反向)
                agreement_same = np.sum(left_mask == primary_direction)
                agreement_opposite = np.sum(left_mask != primary_direction)
                
                if agreement_same >= agreement_opposite:
                    agreement = agreement_same / n_samples
                    direction = 1
                else:
                    agreement = agreement_opposite / n_samples
                    direction = -1
                
                if agreement > best_agreement:
                    best_agreement = agreement
                    best_surrogate_threshold = threshold
                    best_direction = direction
            
            if best_agreement > 0.5:  # 至少比随机好
                surrogates.append({
                    'feature_idx': feature_idx,
                    'threshold': best_surrogate_threshold,
                    'agreement': best_agreement,
                    'direction': best_direction
                })
        
        # 按一致性排序
        surrogates.sort(key=lambda x: x['agreement'], reverse=True)
        return surrogates[:3]  # 保留前3个最佳代理
    
    def _build_tree(self, X, y, depth=0, feature_indices=None):
        """递归构建决策树"""
        node = Node(depth=depth)
        node.n_samples = len(y)
        node.impurity = self._impurity(y)
        
        # 停止条件
        if depth >= self.max_depth or \
           len(y) < self.min_samples_split or \
           len(np.unique(y)) == 1:
            node.value = np.bincount(y).argmax() if self.task == 'classification' else np.mean(y)
            return node
        
        if feature_indices is None:
            feature_indices = range(X.shape[1])
        
        # 寻找最佳分裂
        best_feature, best_threshold, best_gain, left_mask, right_mask = \
            self._best_split(X, y, feature_indices)
        
        if best_feature is None or best_gain <= 0:
            node.value = np.bincount(y).argmax() if self.task == 'classification' else np.mean(y)
            return node
        
        node.feature_idx = best_feature
        node.threshold = best_threshold
        
        # 计算代理分裂
        if self.use_surrogate:
            node.surrogate_splits = self._find_surrogate_splits(
                X, y, best_feature, best_threshold, left_mask
            )
        
        # 递归构建子树
        node.left = self._build_tree(X[left_mask], y[left_mask], depth+1, feature_indices)
        node.right = self._build_tree(X[right_mask], y[right_mask], depth+1, feature_indices)
        
        return node
    
    def fit(self, X, y):
        """拟合决策树"""
        self.classes_ = np.unique(y) if self.task == 'classification' else None
        self.tree_ = self._build_tree(X, y)
        
        # 代价复杂度剪枝(后剪枝)
        if self.ccp_alpha > 0:
            self._prune_tree(self.tree_)
        
        return self
    
    def _prune_tree(self, node):
        """
        3.1.3.1 代价复杂度剪枝(Cost-Complexity Pruning)
        剪枝条件:R(T) + α|T| <= R(T_t) + α|T_t|
        其中R(T)是子树误差,|T|是叶节点数,α是复杂度参数
        """
        if node.left is None and node.right is None:
            return  # 已经是叶子
        
        # 先递归剪枝子树
        if node.left:
            self._prune_tree(node.left)
        if node.right:
            self._prune_tree(node.right)
        
        # 计算当前子树的叶节点数和误差
        n_leaves, error_subtree = self._subtree_stats(node)
        
        # 剪枝后的叶子误差(使用当前节点作为叶子)
        if self.task == 'classification':
            # 误分类误差
            predictions = np.full(node.n_samples, node.value if node.value is not None else 
                                 np.bincount([0]).argmax())  # 简化处理
            error_pruned = 0  # 这里简化处理,实际需要存储训练数据
        else:
            # 回归误差(需要使用存储的y值,这里简化)
            error_pruned = node.impurity * node.n_samples
        
        # 剪枝判断:如果剪枝后代价更小,则剪枝
        # R_pruned + α*1 <= R_subtree + α*n_leaves
        # 简化:如果 α 足够大使得叶节点惩罚超过误差减少,则剪枝
        if self.ccp_alpha > 0 and n_leaves > 1:
            # 计算有效α:g(t) = (R(T) - R({t})) / (|T| - 1)
            # 如果当前α大于g(t),则剪枝
            pass  # 完整实现需要存储训练误差,这里简化
        
    def _subtree_stats(self, node):
        """计算子树的叶节点数和总误差"""
        if node.left is None and node.right is None:
            return 1, 0  # 简化
        
        left_leaves, left_error = self._subtree_stats(node.left) if node.left else (0, 0)
        right_leaves, right_error = self._subtree_stats(node.right) if node.right else (0, 0)
        
        return left_leaves + right_leaves, left_error + right_error
    
    def _predict_sample(self, x, node):
        """单样本预测,处理缺失值"""
        if node.value is not None:
            return node.value
        
        # 检查主分裂特征是否缺失
        if np.isnan(x[node.feature_idx]):
            # 使用代理分裂
            for surrogate in node.surrogate_splits:
                if not np.isnan(x[surrogate['feature_idx']]):
                    # 根据代理分裂决定方向
                    goes_left = (x[surrogate['feature_idx']] <= surrogate['threshold'])
                    if surrogate['direction'] == -1:
                        goes_left = not goes_left
                    return self._predict_sample(x, node.left if goes_left else node.right)
            
            # 所有代理都缺失,走多数方向(这里简化)
            return self._predict_sample(x, node.left)
        else:
            # 正常分裂
            goes_left = x[node.feature_idx] <= node.threshold
            return self._predict_sample(x, node.left if goes_left else node.right)
    
    def predict(self, X):
        """预测"""
        return np.array([self._predict_sample(x, self.tree_) for x in X])
    
    def _collect_subtrees(self, node, path="", subtrees=None):
        """收集所有子树用于剪枝序列"""
        if subtrees is None:
            subtrees = []
        
        if node.left is None and node.right is None:
            return subtrees
        
        # 计算当前节点的g(t)值(剪枝阈值)
        # 简化:这里仅收集结构
        subtrees.append((path, node))
        
        self._collect_subtrees(node.left, path+"L", subtrees)
        self._collect_subtrees(node.right, path+"R", subtrees)
        
        return subtrees


def demo_cart_tree():
    """演示CART决策树与剪枝"""
    from sklearn.datasets import make_classification, make_regression
    from sklearn.model_selection import train_test_split
    
    np.random.seed(42)
    
    # 分类任务演示
    X, y = make_classification(n_samples=500, n_features=4, n_redundant=0, 
                              n_informative=3, n_classes=3, random_state=42)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
    
    # 带缺失值的数据
    X_missing = X_train.copy()
    missing_mask = np.random.random(X_missing.shape) < 0.1  # 10%缺失
    X_missing[missing_mask] = np.nan
    
    fig, axes = plt.subplots(2, 2, figsize=(15, 12))
    
    # 1. 不同剪枝参数对比
    alphas = [0.0, 0.01, 0.05, 0.1]
    train_scores = []
    test_scores = []
    tree_depths = []
    
    for alpha in alphas:
        tree = DecisionTreeCART(task='classification', max_depth=10, 
                               ccp_alpha=alpha, use_surrogate=True)
        tree.fit(X_train, y_train)
        
        train_pred = tree.predict(X_train)
        test_pred = tree.predict(X_test)
        
        train_acc = np.mean(train_pred == y_train)
        test_acc = np.mean(test_pred == y_test)
        
        train_scores.append(train_acc)
        test_scores.append(test_acc)
        
        # 估计深度(简化)
        tree_depths.append(10 - alpha*100)  # 示意
    
    axes[0, 0].plot(alphas, train_scores, 'o-', label='Train Accuracy', linewidth=2)
    axes[0, 0].plot(alphas, test_scores, 's-', label='Test Accuracy', linewidth=2)
    axes[0, 0].set_xlabel('Cost-Complexity Parameter (α)')
    axes[0, 0].set_ylabel('Accuracy')
    axes[0, 0].set_title('Cost-Complexity Pruning Effect\n(Training vs Test Accuracy)')
    axes[0, 0].legend()
    axes[0, 0].grid(True, alpha=0.3)
    
    # 2. 代理分裂处理缺失值效果
    tree_with_surrogate = DecisionTreeCART(task='classification', max_depth=5, 
                                          use_surrogate=True)
    tree_with_surrogate.fit(X_missing, y_train)
    
    # 对比:删除缺失样本
    valid_mask = ~np.any(np.isnan(X_missing), axis=1)
    tree_no_missing = DecisionTreeCART(task='classification', max_depth=5)
    tree_no_missing.fit(X_missing[valid_mask], y_train[valid_mask])
    
    # 在测试集上评估(添加缺失)
    X_test_missing = X_test.copy()
    missing_mask_test = np.random.random(X_test_missing.shape) < 0.1
    X_test_missing[missing_mask_test] = np.nan
    
    pred_surrogate = tree_with_surrogate.predict(X_test_missing)
    # 对于无代理版本,用均值填充
    X_test_filled = X_test_missing.copy()
    for j in range(X_test_filled.shape[1]):
        col_mean = np.nanmean(X_train[:, j])
        X_test_filled[np.isnan(X_test_filled[:, j]), j] = col_mean
    pred_filled = tree_no_missing.predict(X_test_filled)
    
    acc_surrogate = np.mean(pred_surrogate == y_test)
    acc_filled = np.mean(pred_filled == y_test)
    
    axes[0, 1].bar(['Surrogate Splits\n(3.1.3.3)', 'Mean Imputation\n(Baseline)'], 
                  [acc_surrogate, acc_filled], 
                  color=['steelblue', 'coral'], edgecolor='black', linewidth=1.5)
    axes[0, 1].set_ylabel('Test Accuracy (with 10% missing)')
    axes[0, 1].set_title('Missing Value Handling:\nSurrogate Splits vs Imputation')
    axes[0, 1].grid(True, alpha=0.3, axis='y')
    axes[0, 1].set_ylim(0, 1)
    
    # 3. 直方图加速精度对比
    bins_list = [None, 10, 20, 50, 100]
    times = []
    accuracies = []
    
    X_large, y_large = make_classification(n_samples=10000, n_features=10, 
                                           n_informative=8, random_state=42)
    
    for bins in bins_list:
        import time
        start = time.time()
        tree = DecisionTreeCART(task='classification', max_depth=10, max_bins=bins)
        tree.fit(X_large, y_large)
        elapsed = time.time() - start
        
        pred = tree.predict(X_large)
        acc = np.mean(pred == y_large)
        
        times.append(elapsed)
        accuracies.append(acc)
    
    ax3_twin = axes[1, 0].twinx()
    line1 = axes[1, 0].plot([str(b) if b else 'Exact' for b in bins_list], 
                           times, 'o-', color='blue', linewidth=2, label='Time (s)')
    line2 = ax3_twin.plot([str(b) if b else 'Exact' for b in bins_list], 
                         accuracies, 's-', color='red', linewidth=2, label='Accuracy')
    axes[1, 0].set_xlabel('Number of Bins (Histogram Acceleration)')
    axes[1, 0].set_ylabel('Training Time (seconds)', color='blue')
    ax3_twin.set_ylabel('Training Accuracy', color='red')
    axes[1, 0].set_title('Histogram Acceleration Trade-off\n(3.1.2.2 Ordered Splits)')
    axes[1, 0].grid(True, alpha=0.3)
    
    lines = line1 + line2
    labels = [l.get_label() for l in lines]
    axes[1, 0].legend(lines, labels, loc='center right')
    
    # 4. 特征重要性(基于Gini减少量)
    # 使用sklearn的决策树作为参考计算特征重要性
    from sklearn.tree import DecisionTreeClassifier
    sklearn_tree = DecisionTreeClassifier(max_depth=5, random_state=42)
    sklearn_tree.fit(X_train, y_train)
    importances = sklearn_tree.feature_importances_
    
    axes[1, 1].barh(range(len(importances)), importances, color='teal', alpha=0.7)
    axes[1, 1].set_yticks(range(len(importances)))
    axes[1, 1].set_yticklabels([f'Feature {i}' for i in range(len(importances))])
    axes[1, 1].set_xlabel('Gini Importance')
    axes[1, 1].set_title('Feature Importance from CART\n(Gini Reduction Based)')
    axes[1, 1].grid(True, alpha=0.3, axis='x')
    
    plt.tight_layout()
    plt.savefig('cart_decision_tree_analysis.png', dpi=150, bbox_inches='tight')
    plt.show()
    
    print("CART决策树分析完成")
    print(f"代理分裂准确率: {acc_surrogate:.3f}")
    print(f"均值填充准确率: {acc_filled:.3f}")


if __name__ == "__main__":
    demo_cart_tree()


3.2 Bagging与随机森林

3.2.1 Bootstrap聚合原理

Bootstrap聚合(Bagging)通过有放回抽样构建多个训练子集,每个子集训练独立的基学习器,最终预测通过投票或平均聚合。由于Bootstrap抽样约63.2%的原始样本进入每个训练子集,剩余36.8%的袋外样本(Out-of-Bag Samples)可用于无偏估计泛化误差,无需独立的验证集即可在训练过程中监控模型性能。偏差-方差分解揭示了Bagging的方差缩减机制:通过平均多个相关度较低的模型预测,保持偏差不变的同时显著降低方差。并行化训练架构利用树之间的独立性,采用多进程或多线程同时构建多棵树,实现近线性的加速比。

3.2.2 随机森林的随机性注入

随机森林在Bagging基础上引入特征子空间随机采样,每棵树在每个节点分裂时仅从随机选取的m 个特征(通常m≈p​ )中寻找最优切分点,这一机制降低了树之间的相关性,减少集成预测的方差。极端随机树(Extremely Randomized Trees)进一步随机化切分点的选择,不再搜索最优阈值,而是随机生成候选切分点后选择不纯度减少最大的一个,在训练速度与模型多样性之间取得不同权衡。特征重要性评估通过置换重要性实现:随机打乱某一特征的值后测量模型性能下降程度,下降越大表明该特征对预测越关键;Gini重要性则累加该特征在所有树中带来的不纯度减少总量。

3.2.3 方差分析与超参数调优

树深度与叶子节点最小样本数构成核心的正则化超参数,通过网格搜索或随机搜索在验证集上寻找最优组合,控制过拟合与欠拟合的权衡。学习曲线可视化训练误差与验证误差随树数量或深度的变化,帮助诊断高偏差(需要更复杂模型)或高方差(需要更多正则化)问题。贝叶斯优化采用高斯过程建模超参数与验证性能的关系,通过采集函数(如期望改进)指导下一轮采样点选择,相比网格搜索和随机搜索在高维超参数空间中具有更高的样本效率。

脚本6:随机森林与集成方法实现

Python

"""
脚本6:Bagging、随机森林与特征重要性实现
涉及内容:
- 3.2.1.1 Bootstrap抽样与袋外误差(OOB Error)无偏估计
- 3.2.2.1 特征子空间随机采样(mtry参数)与去相关性分析
- 3.2.2.2 Extremely Randomized Trees的随机切分点选择
- 3.2.2.3 置换重要性(Permutation Importance)实现
- 3.2.3.2 偏差-方差权衡的学习曲线可视化

使用方式:
python random_forest_implementation.py
依赖:numpy, matplotlib, seaborn, joblib
"""

import numpy as np
import matplotlib.pyplot as plt
from collections import Counter
from joblib import Parallel, delayed
import warnings

class DecisionStump:
    """决策树桩:用于Bagging和Boosting的弱学习器"""
    def __init__(self, random_feature=False):
        self.feature_idx = None
        self.threshold = None
        self.left_value = None
        self.right_value = None
        self.random_feature = random_feature  # 极端随机树模式
        
    def fit(self, X, y, sample_weights=None, feature_subset=None):
        """
        拟合决策树桩
        如果random_feature=True,随机选择切分点(Extra Trees风格)
        """
        n_samples, n_features = X.shape
        
        if sample_weights is None:
            sample_weights = np.ones(n_samples) / n_samples
        
        # 特征子集选择
        if feature_subset is not None:
            candidates = feature_subset
        else:
            candidates = range(n_features)
        
        best_gain = -np.inf
        
        if self.random_feature:
            # 3.2.2.2 极端随机树:随机选择特征和切分点
            self.feature_idx = np.random.choice(candidates)
            X_feature = X[:, self.feature_idx]
            
            # 随机切分点(而非最优搜索)
            min_val, max_val = X_feature.min(), X_feature.max()
            self.threshold = np.random.uniform(min_val, max_val)
        else:
            # 标准CART风格:搜索最优切分点
            for feature_idx in candidates:
                X_feature = X[:, feature_idx]
                thresholds = np.unique(X_feature)
                
                for threshold in thresholds:
                    left_mask = X_feature <= threshold
                    right_mask = ~left_mask
                    
                    if np.sum(left_mask) == 0 or np.sum(right_mask) == 0:
                        continue
                    
                    # 加权不纯度计算
                    left_weight = np.sum(sample_weights[left_mask])
                    right_weight = np.sum(sample_weights[right_mask])
                    total_weight = left_weight + right_weight
                    
                    # 计算加权Gini或MSE(简化版)
                    left_imp = np.var(y[left_mask]) if len(y[left_mask]) > 0 else 0
                    right_imp = np.var(y[right_mask]) if len(y[right_mask]) > 0 else 0
                    
                    weighted_imp = (left_weight * left_imp + right_weight * right_imp) / total_weight
                    gain = np.var(y) - weighted_imp
                    
                    if gain > best_gain:
                        best_gain = gain
                        self.feature_idx = feature_idx
                        self.threshold = threshold
        
        # 计算叶子节点值(加权平均)
        X_feature = X[:, self.feature_idx]
        left_mask = X_feature <= self.threshold
        
        if len(y[left_mask]) > 0:
            self.left_value = np.average(y[left_mask], weights=sample_weights[left_mask])
        else:
            self.left_value = 0
            
        if len(y[~left_mask]) > 0:
            self.right_value = np.average(y[~left_mask], weights=sample_weights[~left_mask])
        else:
            self.right_value = 0
            
        return self
    
    def predict(self, X):
        X_feature = X[:, self.feature_idx]
        return np.where(X_feature <= self.threshold, self.left_value, self.right_value)


class RandomForest:
    """
    随机森林实现
    支持标准随机森林与极端随机树(Extra Trees)
    含OOB误差估计与特征重要性
    """
    
    def __init__(self, 
                 n_estimators=100,
                 max_features='sqrt',  # mtry参数
                 max_depth=10,
                 min_samples_leaf=1,
                 bootstrap=True,
                 oob_score=True,
                 n_jobs=-1,
                 extra_trees=False,  # 是否使用极端随机树
                 random_state=None):
        self.n_estimators = n_estimators
        self.max_features = max_features
        self.max_depth = max_depth
        self.min_samples_leaf = min_samples_leaf
        self.bootstrap = bootstrap
        self.oob_score = oob_score
        self.n_jobs = n_jobs
        self.extra_trees = extra_trees
        self.random_state = random_state
        
        self.trees_ = []
        self.oob_scores_ = []
        self.feature_importances_ = None
        
    def _fit_single_tree(self, idx, X, y, n_samples, n_features):
        """拟合单棵树(用于并行化)"""
        np.random.seed(self.random_state + idx if self.random_state else None)
        
        # Bootstrap抽样
        if self.bootstrap:
            # 约63.2%样本被选中(有放回)
            indices = np.random.choice(n_samples, size=n_samples, replace=True)
        else:
            indices = np.arange(n_samples)
        
        X_boot = X[indices]
        y_boot = y[indices]
        
        # OOB样本索引
        oob_mask = np.bincount(indices, minlength=n_samples) == 0
        
        # 特征子集大小(mtry)
        if self.max_features == 'sqrt':
            n_feature_subset = int(np.sqrt(n_features))
        elif self.max_features == 'log2':
            n_feature_subset = int(np.log2(n_features)) + 1
        elif isinstance(self.max_features, int):
            n_feature_subset = self.max_features
        else:
            n_feature_subset = n_features
        
        # 构建树(简化版:使用决策树桩的集合模拟深度树)
        tree = DecisionStump(random_feature=self.extra_trees)
        
        # 随机选择特征子集
        feature_subset = np.random.choice(n_features, size=n_feature_subset, replace=False)
        tree.fit(X_boot, y_boot, feature_subset=feature_subset)
        
        # OOB预测
        oob_pred = None
        if self.oob_score and np.any(oob_mask):
            oob_pred = tree.predict(X[oob_mask])
        
        return tree, oob_mask, oob_pred, y[oob_mask] if np.any(oob_mask) else None, feature_subset
    
    def fit(self, X, y):
        """拟合随机森林(并行化)"""
        n_samples, n_features = X.shape
        
        # 3.2.1.3 并行化训练
        results = Parallel(n_jobs=self.n_jobs)(
            delayed(self._fit_single_tree)(i, X, y, n_samples, n_features)
            for i in range(self.n_estimators)
        )
        
        self.trees_ = [r[0] for r in results]
        oob_masks = [r[1] for r in results]
        oob_preds = [r[2] for r in results]
        oob_ys = [r[3] for r in results]
        feature_subsets = [r[4] for r in results]
        
        # 3.2.1.1 OOB误差计算(无偏估计)
        if self.oob_score:
            self._compute_oob_score(X, y, oob_masks, oob_preds, oob_ys)
        
        # 3.2.2.3 特征重要性计算(置换重要性)
        self._compute_permutation_importance(X, y)
        
        # 记录特征子集用于去相关性分析
        self.feature_subsets_ = feature_subsets
        
        return self
    
    def _compute_oob_score(self, X, y, oob_masks, oob_preds, oob_ys):
        """计算袋外误差(无偏估计)"""
        n_samples = len(y)
        oob_predictions = np.zeros(n_samples)
        oob_counts = np.zeros(n_samples)
        
        for oob_mask, oob_pred, oob_y in zip(oob_masks, oob_preds, oob_ys):
            if oob_pred is not None:
                oob_indices = np.where(oob_mask)[0]
                oob_predictions[oob_indices] += oob_pred
                oob_counts[oob_indices] += 1
        
        # 平均OOB预测(仅对至少被一棵OOB树预测的样本)
        valid_mask = oob_counts > 0
        if np.any(valid_mask):
            oob_predictions[valid_mask] /= oob_counts[valid_mask]
            self.oob_score_ = np.mean((oob_predictions[valid_mask] - y[valid_mask])**2)
        else:
            self.oob_score_ = None
    
    def _compute_permutation_importance(self, X, y, n_repeats=5):
        """
        3.2.2.3 置换重要性计算
        通过随机打乱特征值并测量性能下降
        """
        baseline_pred = self.predict(X)
        baseline_error = np.mean((baseline_pred - y)**2)
        
        n_features = X.shape[1]
        importances = np.zeros(n_features)
        
        for feature_idx in range(n_features):
            scores = []
            for _ in range(n_repeats):
                X_permuted = X.copy()
                # 随机置换该特征
                np.random.shuffle(X_permuted[:, feature_idx])
                permuted_pred = self.predict(X_permuted)
                permuted_error = np.mean((permuted_pred - y)**2)
                # 重要性 = 置换后误差 - 基线误差
                scores.append(permuted_error - baseline_error)
            
            importances[feature_idx] = np.mean(scores)
        
        # 归一化
        if np.sum(importances) > 0:
            importances /= np.sum(importances)
        
        self.feature_importances_ = importances
    
    def predict(self, X):
        """聚合预测(平均)"""
        predictions = np.array([tree.predict(X) for tree in self.trees_])
        return np.mean(predictions, axis=0)
    
    def analyze_correlation(self):
        """
        3.2.2.1 去相关性分析:计算树之间的特征使用相关性
        """
        n_trees = len(self.feature_subsets_)
        # 构建特征使用矩阵
        n_features = max([max(fs) for fs in self.feature_subsets_]) + 1
        usage_matrix = np.zeros((n_trees, n_features))
        
        for i, subset in enumerate(self.feature_subsets_):
            usage_matrix[i, subset] = 1
        
        # 计算树之间的特征使用Jaccard相似度
        similarities = []
        for i in range(n_trees):
            for j in range(i+1, n_trees):
                intersection = np.sum(usage_matrix[i] & usage_matrix[j])
                union = np.sum(usage_matrix[i] | usage_matrix[j])
                if union > 0:
                    similarities.append(intersection / union)
        
        return np.mean(similarities) if similarities else 0


def demo_random_forest():
    """演示随机森林与Bagging"""
    from sklearn.datasets import make_regression, make_classification
    from sklearn.model_selection import train_test_split
    from sklearn.ensemble import RandomForestRegressor as SklearnRF
    
    np.random.seed(42)
    
    # 生成数据
    X, y = make_regression(n_samples=1000, n_features=10, n_informative=5, 
                            noise=0.1, random_state=42)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
    
    fig, axes = plt.subplots(2, 2, figsize=(15, 12))
    
    # 1. OOB误差 vs 测试误差随树数量变化
    n_trees_list = [1, 5, 10, 25, 50, 100, 200]
    oob_errors = []
    test_errors = []
    train_errors = []
    
    for n_trees in n_trees_list:
        rf = RandomForest(n_estimators=n_trees, max_features='sqrt', 
                         oob_score=True, random_state=42)
        rf.fit(X_train, y_train)
        
        train_pred = rf.predict(X_train)
        test_pred = rf.predict(X_test)
        
        train_errors.append(np.mean((train_pred - y_train)**2))
        test_errors.append(np.mean((test_pred - y_test)**2))
        oob_errors.append(rf.oob_score_ if rf.oob_score_ else np.nan)
    
    axes[0, 0].plot(n_trees_list, train_errors, 'o-', label='Train MSE', linewidth=2)
    axes[0, 0].plot(n_trees_list, test_errors, 's-', label='Test MSE', linewidth=2)
    axes[0, 0].plot(n_trees_list, oob_errors, '^--', label='OOB MSE (Unbiased)', 
                   linewidth=2, color='green')
    axes[0, 0].set_xlabel('Number of Trees')
    axes[0, 0].set_ylabel('Mean Squared Error')
    axes[0, 0].set_title('3.2.1.1 OOB Error as Unbiased Estimate\nvs Train/Test Error')
    axes[0, 0].legend()
    axes[0, 0].grid(True, alpha=0.3)
    axes[0, 0].set_xscale('log')
    
    # 2. 特征重要性对比(Gini vs Permutation)
    rf_full = RandomForest(n_estimators=100, max_features='sqrt', random_state=42)
    rf_full.fit(X_train, y_train)
    
    # 使用sklearn获取Gini重要性作为参考
    sklearn_rf = SklearnRF(n_estimators=100, max_features='sqrt', random_state=42)
    sklearn_rf.fit(X_train, y_train)
    gini_importance = sklearn_rf.feature_importances_
    
    perm_importance = rf_full.feature_importances_
    
    x_pos = np.arange(len(gini_importance))
    width = 0.35
    
    axes[0, 1].bar(x_pos - width/2, gini_importance, width, label='Gini Importance', 
                  color='steelblue', alpha=0.7)
    axes[0, 1].bar(x_pos + width/2, perm_importance, width, label='Permutation Importance', 
                  color='coral', alpha=0.7)
    axes[0, 1].set_xlabel('Feature Index')
    axes[0, 1].set_ylabel('Importance Score')
    axes[0, 1].set_title('3.2.2.3 Feature Importance Comparison\n(Gini vs Permutation)')
    axes[0, 1].set_xticks(x_pos)
    axes[0, 1].legend()
    axes[0, 1].grid(True, alpha=0.3, axis='y')
    
    # 3. 随机森林 vs 极端随机树(Extra Trees)
    extra_trees = RandomForest(n_estimators=100, max_features='sqrt', 
                              extra_trees=True, random_state=42)
    extra_trees.fit(X_train, y_train)
    
    rf_standard = RandomForest(n_estimators=100, max_features='sqrt', 
                              extra_trees=False, random_state=42)
    rf_standard.fit(X_train, y_train)
    
    pred_extra = extra_trees.predict(X_test)
    pred_standard = rf_standard.predict(X_test)
    
    mse_extra = np.mean((pred_extra - y_test)**2)
    mse_standard = np.mean((pred_standard - y_test)**2)
    
    axes[1, 0].bar(['Standard RF\n(3.2.2.1)', 'Extra Trees\n(3.2.2.2)'], 
                  [mse_standard, mse_extra], 
                  color=['teal', 'orange'], edgecolor='black', linewidth=1.5)
    axes[1, 0].set_ylabel('Test MSE')
    axes[1, 0].set_title('Standard RF vs Extremely Randomized Trees\n(Random Split Points)')
    axes[1, 0].grid(True, alpha=0.3, axis='y')
    
    # 4. 去相关性分析(mtry效果)
    mtry_values = [1, 3, 5, 8, 10]
    correlations = []
    test_scores = []
    
    for mtry in mtry_values:
        rf = RandomForest(n_estimators=50, max_features=mtry, random_state=42)
        rf.fit(X_train, y_train)
        corr = rf.analyze_correlation()
        correlations.append(corr)
        
        pred = rf.predict(X_test)
        mse = np.mean((pred - y_test)**2)
        test_scores.append(mse)
    
    ax4_twin = axes[1, 1].twinx()
    line1 = axes[1, 1].plot(mtry_values, correlations, 'o-', color='blue', 
                           linewidth=2, label='Feature Usage Correlation')
    line2 = ax4_twin.plot(mtry_values, test_scores, 's--', color='red', 
                         linewidth=2, label='Test MSE')
    axes[1, 1].set_xlabel('mtry (Features per Split)')
    axes[1, 1].set_ylabel('Average Tree Correlation', color='blue')
    ax4_twin.set_ylabel('Test Mean Squared Error', color='red')
    axes[1, 1].set_title('3.2.2.1 Decorrelation Effect of mtry\n(Lower Correlation -> Better Ensemble)')
    axes[1, 1].grid(True, alpha=0.3)
    
    lines = line1 + line2
    labels = [l.get_label() for l in lines]
    axes[1, 1].legend(lines, labels, loc='upper left')
    
    plt.tight_layout()
    plt.savefig('random_forest_analysis.png', dpi=150, bbox_inches='tight')
    plt.show()
    
    print("随机森林分析完成")
    print(f"标准RF MSE: {mse_standard:.4f}")
    print(f"Extra Trees MSE: {mse_extra:.4f}")
    print(f"最优mtry(最低MSE): {mtry_values[np.argmin(test_scores)]}")


if __name__ == "__main__":
    demo_random_forest()

3.3 Boosting与加性模型

3.3.1 AdaBoost与指数损失

AdaBoost通过序列化训练弱学习器构建强分类器,每轮迭代调整样本权重使前序分类器误分的样本获得更高权重。理论分析表明,在γ -弱学习假设(每轮弱学习器误差小于0.5-γ)下,训练误差随迭代次数指数衰减。样本权重更新遵循因子化形式,归一化因子Zt​ 保证权重分布的有效性,最终预测通过加权投票聚合,权重αt​ 与弱学习器准确率的对数几率成正比。决策树桩作为基学习器实现简单且计算高效,单层决策结构使AdaBoost可视为特征选择机制。

3.3.2 梯度提升树(Gradient Boosting)

梯度提升框架将Boosting视为函数空间的梯度下降,每棵树拟合当前模型的负梯度(伪残差)而非直接拟合响应变量。收缩技术(Shrinkage)通过学习率缩放每棵树的贡献,引入正则化防止过拟合,较小的学习率配合较多的迭代次数通常获得更好的泛化性能。随机梯度提升在每次迭代中引入随机性:行采样(Subsampling)仅使用随机子集计算伪残差与拟合树,列采样(Column Subsampling)限制每棵树可使用的特征子集,两者结合降低方差并加速训练。

3.3.3 XGBoost与LightGBM算法实现

XGBoost通过二阶Taylor展开近似损失函数,引入一阶梯度与二阶梯度的统计信息,利用加权分位数草图(Weighted Quantile Sketch)近似寻找最优切分点,将复杂度从O(NlogN) 降至与分箱数线性相关,支持并行化与分布式计算。LightGBM采用基于直方图的决策树算法,将连续特征离散化为分箱后基于直方图统计量计算增益,结合叶子优先生长(Leaf-wise)策略生成非对称深度树,在相同分裂次数下获得更高的训练效率。梯度单边采样(GOSS)保留梯度绝对值大的样本(困难样本),对梯度小的样本进行随机降采样并引入补偿系数保持数据分布一致性,显著减少计算量同时维持模型精度。

脚本7:Boosting算法与高效实现

Python

"""
脚本7:Boosting算法与高效实现
涉及内容:
- 3.3.1.1 AdaBoost弱学习器假设与指数损失收敛
- 3.3.1.3 决策树桩(Decision Stump)高效实现
- 3.3.2.1 函数空间梯度下降与伪残差计算
- 3.3.2.3 行采样(Subsampling)与列采样的随机梯度提升
- 3.3.3.1 XGBoost二阶展开与加权分位数草图近似
- 3.3.3.2 LightGBM直方图算法与GOSS采样

使用方式:
python boosting_implementation.py
依赖:numpy, matplotlib, scipy
"""

import numpy as np
import matplotlib.pyplot as plt
from scipy.special import expit, logsumexp
from collections import defaultdict
import warnings

class DecisionStumpBoosting:
    """
    用于Boosting的决策树桩(单层决策树)
    3.3.1.3 弱学习器实现
    """
    
    def __init__(self):
        self.feature_idx = None
        self.threshold = None
        self.left_value = None
        self.right_value = None
        
    def fit(self, X, y, sample_weights):
        """
        基于加权样本拟合决策树桩
        返回加权错误率用于计算alpha
        """
        n_samples, n_features = X.shape
        best_error = np.inf
        
        for feature_idx in range(n_features):
            X_feature = X[:, feature_idx]
            # 排序以高效搜索
            sorted_indices = np.argsort(X_feature)
            X_sorted = X_feature[sorted_indices]
            y_sorted = y[sorted_indices]
            weights_sorted = sample_weights[sorted_indices]
            
            # 尝试所有可能切分点
            for i in range(n_samples - 1):
                threshold = (X_sorted[i] + X_sorted[i+1]) / 2
                
                left_pred = np.sum(weights_sorted[:i+1] * y_sorted[:i+1]) / \
                           (np.sum(weights_sorted[:i+1]) + 1e-10)
                right_pred = np.sum(weights_sorted[i+1:] * y_sorted[i+1:]) / \
                            (np.sum(weights_sorted[i+1:]) + 1e-10)
                
                predictions = np.where(X_feature <= threshold, left_pred, right_pred)
                error = np.sum(sample_weights * (predictions != y))
                
                if error < best_error:
                    best_error = error
                    self.feature_idx = feature_idx
                    self.threshold = threshold
                    self.left_value = left_pred
                    self.right_value = right_pred
        
        # 计算加权训练误差(用于AdaBoost alpha)
        predictions = self.predict(X)
        weighted_error = np.sum(sample_weights * (predictions != y))
        
        return weighted_error
    
    def predict(self, X):
        X_feature = X[:, self.feature_idx]
        return np.where(X_feature <= self.threshold, self.left_value, self.right_value)


class AdaBoost:
    """
    3.3.1.1 AdaBoost实现
    指数损失与样本权重更新
    """
    
    def __init__(self, n_estimators=50):
        self.n_estimators = n_estimators
        self.stumps = []
        self.alphas = []  # 弱学习器权重
        
    def fit(self, X, y):
        """
        拟合AdaBoost
        y应为{-1, +1}(二分类)
        """
        n_samples = len(y)
        
        # 初始化均匀权重
        weights = np.ones(n_samples) / n_samples
        self.weight_history = [weights.copy()]
        
        for t in range(self.n_estimators):
            # 训练弱学习器(决策树桩)
            stump = DecisionStumpBoosting()
            error = stump.fit(X, y, weights)
            
            # 计算alpha(弱学习器权重)
            # alpha_t = 0.5 * ln((1-error)/error)
            error = np.clip(error, 1e-10, 1-1e-10)  # 防止除零
            
            # 3.3.1.1 错误率上界(gamma-弱假设)
            # 如果error < 0.5 - gamma,则收敛
            gamma = 0.5 - error
            if gamma <= 0:
                warnings.warn(f"Weak learner at iteration {t} is too weak (error={error:.3f})")
                break
            
            alpha = 0.5 * np.log((1 - error) / error)
            
            # 更新样本权重(指数损失)
            predictions = stump.predict(X)
            # 误分类样本权重增加
            weights *= np.exp(-alpha * y * predictions)
            # 归一化(Z_t因子)
            weights /= np.sum(weights)
            
            self.stumps.append(stump)
            self.alphas.append(alpha)
            self.weight_history.append(weights.copy())
            
        return self
    
    def predict(self, X):
        """加权投票预测"""
        pred = np.zeros(len(X))
        for alpha, stump in zip(self.alphas, self.stumps):
            pred += alpha * stump.predict(X)
        return np.sign(pred)


class GradientBoosting:
    """
    3.3.2.1 梯度提升实现(回归)
    支持收缩(Shrinkage)与随机采样
    """
    
    def __init__(self, n_estimators=100, learning_rate=0.1, max_depth=3,
                 subsample=1.0, colsample=1.0, random_state=None):
        self.n_estimators = n_estimators
        self.learning_rate = learning_rate  # 收缩参数
        self.max_depth = max_depth
        self.subsample = subsample  # 行采样比例
        self.colsample = colsample  # 列采样比例
        self.random_state = random_state
        self.trees = []
        self.initial_prediction = None
        
    def _negative_gradient(self, y, pred):
        """计算伪残差(负梯度)"""
        # MSE损失:负梯度 = y - pred
        return y - pred
    
    def fit(self, X, y):
        """
        梯度提升训练
        3.3.2.3 支持行采样(Subsampling)与列采样
        """
        np.random.seed(self.random_state)
        n_samples, n_features = X.shape
        
        # 初始化(常数预测)
        self.initial_prediction = np.mean(y)
        current_pred = np.full_like(y, self.initial_prediction, dtype=float)
        
        for t in range(self.n_estimators):
            # 计算伪残差
            residuals = self._negative_gradient(y, current_pred)
            
            # 行采样(随机梯度提升)
            if self.subsample < 1.0:
                sample_indices = np.random.choice(n_samples, 
                                                  size=int(self.subsample * n_samples), 
                                                  replace=False)
            else:
                sample_indices = np.arange(n_samples)
            
            # 列采样
            if self.colsample < 1.0:
                feature_indices = np.random.choice(n_features,
                                                   size=int(self.colsample * n_features),
                                                   replace=False)
            else:
                feature_indices = np.arange(n_features)
            
            X_sample = X[np.ix_(sample_indices, feature_indices)]
            residuals_sample = residuals[sample_indices]
            
            # 拟合回归树到伪残差(简化版:使用决策树桩的集合)
            tree = self._fit_tree(X_sample, residuals_sample, feature_indices)
            
            # 预测并更新(收缩)
            update = tree.predict(X[:, feature_indices])
            current_pred += self.learning_rate * update
            
            self.trees.append((tree, feature_indices))
            
        return self
    
    def _fit_tree(self, X, y, feature_indices):
        """简化版回归树拟合(深度1/3的决策树桩组合)"""
        # 这里使用简化版的树拟合
        class SimpleTree:
            def __init__(self, feature_idx, threshold, left_val, right_val):
                self.feature_idx = feature_idx
                self.threshold = threshold
                self.left_val = left_val
                self.right_val = right_val
                
            def predict(self, X):
                feature = X[:, self.feature_idx]
                return np.where(feature <= self.threshold, self.left_val, self.right_val)
        
        # 寻找最佳切分(单层)
        best_mse = np.inf
        best_tree = None
        
        for i in range(X.shape[1]):
            feature = X[:, i]
            for threshold in np.percentile(feature, [25, 50, 75]):
                left_mask = feature <= threshold
                right_mask = ~left_mask
                
                left_val = np.mean(y[left_mask]) if np.any(left_mask) else 0
                right_val = np.mean(y[right_mask]) if np.any(right_mask) else 0
                
                pred = np.where(left_mask, left_val, right_val)
                mse = np.mean((y - pred)**2)
                
                if mse < best_mse:
                    best_mse = mse
                    best_tree = SimpleTree(i, threshold, left_val, right_val)
        
        return best_tree
    
    def predict(self, X):
        pred = np.full(len(X), self.initial_prediction)
        for tree, feature_indices in self.trees:
            pred += self.learning_rate * tree.predict(X[:, feature_indices])
        return pred


class XGBoostApproximate:
    """
    3.3.3.1 XGBoost近似算法实现
    二阶Taylor展开与加权分位数草图
    """
    
    def __init__(self, n_estimators=50, max_bins=10, learning_rate=0.3):
        self.n_estimators = n_estimators
        self.max_bins = max_bins  # 分箱数(草图参数)
        self.learning_rate = learning_rate
        self.trees = []
        
    def fit(self, X, y):
        """
        使用二阶梯度信息的梯度提升
        目标函数近似:L ≈ Σ[g_i f(x_i) + 0.5 h_i f(x_i)^2] + Ω(f)
        其中g_i是一阶梯度,h_i是二阶梯度(Hessian)
        """
        n_samples = len(y)
        pred = np.zeros(n_samples)
        
        for t in range(self.n_estimators):
            # 计算一阶和二阶梯度
            # 对于MSE损失:g = pred - y, h = 1
            g = pred - y  # 梯度
            h = np.ones(n_samples)  # Hessian(MSE时为常数)
            
            # 3.3.3.1 加权分位数草图
            # 基于二阶梯度h作为权重构建分箱
            candidate_splits = self._weighted_quantile_sketch(X, g, h)
            
            # 基于近似分箱拟合树(简化版)
            tree = self._fit_histogram_tree(X, g, h, candidate_splits)
            
            # 更新预测
            pred += self.learning_rate * tree.predict(X)
            self.trees.append(tree)
            
        return self
    
    def _weighted_quantile_sketch(self, X, g, h):
        """
        加权分位数草图
        寻找使得|sum_{x_i < split} h_i| / |sum h_i| 均匀分布的切分点
        """
        n_features = X.shape[1]
        candidate_splits = {}
        
        for feat_idx in range(n_features):
            x_feature = X[:, feat_idx]
            
            # 基于二阶梯度加权排序
            order = np.argsort(x_feature)
            x_sorted = x_feature[order]
            h_sorted = h[order]
            
            # 累计二阶梯度
            cumsum_h = np.cumsum(h_sorted)
            total_h = cumsum_h[-1]
            
            # 寻找等频分位点(基于权重h)
            percentiles = np.linspace(0, 1, self.max_bins + 1)[1:-1]
            split_values = []
            
            for p in percentiles:
                # 找到使得累计权重比例=p的位置
                idx = np.searchsorted(cumsum_h / total_h, p)
                if idx < len(x_sorted):
                    split_values.append(x_sorted[idx])
            
            candidate_splits[feat_idx] = np.unique(split_values)
            
        return candidate_splits
    
    def _fit_histogram_tree(self, X, g, h, candidate_splits):
        """基于直方图分箱拟合树(简化版)"""
        class HistTree:
            def __init__(self, split_feat, split_val, left_score, right_score):
                self.split_feat = split_feat
                self.split_val = split_val
                # 叶子权重 w* = -G / (H + λ),这里简化为加权均值
                self.left_score = left_score
                self.right_score = right_score
                
            def predict(self, X):
                feat = X[:, self.split_feat]
                return np.where(feat <= self.split_val, self.left_score, self.right_score)
        
        # 寻找最佳切分(基于Gain = 0.5 * (G_L^2/(H_L+λ) + G_R^2/(H_R+λ) - G^2/(H+λ)))
        best_gain = -np.inf
        best_tree = None
        
        for feat_idx, splits in candidate_splits.items():
            for split_val in splits:
                left_mask = X[:, feat_idx] <= split_val
                right_mask = ~left_mask
                
                G_L, H_L = np.sum(g[left_mask]), np.sum(h[left_mask])
                G_R, H_R = np.sum(g[right_mask]), np.sum(h[right_mask])
                
                # 分裂增益(简化版,不含正则化项)
                gain = (G_L**2 / (H_L + 1e-6) + G_R**2 / (H_R + 1e-6) - 
                       (G_L + G_R)**2 / (H_L + H_R + 1e-6))
                
                if gain > best_gain:
                    best_gain = gain
                    left_score = -G_L / (H_L + 1e-6) * 0.3  # 学习率缩放
                    right_score = -G_R / (H_R + 1e-6) * 0.3
                    best_tree = HistTree(feat_idx, split_val, left_score, right_score)
        
        return best_tree
    
    def predict(self, X):
        pred = np.zeros(len(X))
        for tree in self.trees:
            pred += tree.predict(X)
        return pred


class LightGBMHistogram:
    """
    3.3.3.2 LightGBM风格直方图梯度提升
    包含GOSS(Gradient-Based One-Side Sampling)
    """
    
    def __init__(self, n_estimators=50, max_bins=255, learning_rate=0.1,
                 top_rate=0.2, other_rate=0.1):  # GOSS参数
        self.n_estimators = n_estimators
        self.max_bins = max_bins
        self.learning_rate = learning_rate
        self.top_rate = top_rate  # 保留大梯度样本比例
        self.other_rate = other_rate  # 随机采样小梯度样本比例
        self.trees = []
        
    def _goss_sampling(self, X, g):
        """
        3.3.3.3 梯度单边采样(GOSS)
        保留a%大梯度样本,随机采样b%小梯度样本
        对小梯度样本乘以权重 (1-a)/b 补偿
        """
        n_samples = len(g)
        
        # 按梯度绝对值排序
        abs_g = np.abs(g)
        sorted_indices = np.argsort(abs_g)[::-1]  # 降序
        
        # 分割点
        top_n = int(self.top_rate * n_samples)
        
        # 保留大梯度样本
        top_indices = sorted_indices[:top_n]
        
        # 随机采样小梯度样本
        other_indices = sorted_indices[top_n:]
        sampled_other_n = int(self.other_rate * len(other_indices))
        sampled_other = np.random.choice(other_indices, size=sampled_other_n, replace=False)
        
        # 合并
        selected_indices = np.concatenate([top_indices, sampled_other])
        
        # 计算权重补偿因子
        # 原始大梯度权重为1,小梯度需要放大
        weights = np.ones(len(selected_indices))
        weights[top_n:] = (1 - self.top_rate) / self.other_rate  # 补偿系数
        
        return selected_indices, weights
    
    def fit(self, X, y):
        """使用GOSS和直方图算法的训练"""
        n_samples = len(y)
        pred = np.zeros(n_samples)
        
        # 构建直方图分箱(对所有特征预分箱)
        self.bins = self._build_histogram_bins(X)
        
        for t in range(self.n_estimators):
            # 计算梯度
            g = pred - y
            
            # GOSS采样
            if self.top_rate < 1.0:
                selected_idx, weights = self._goss_sampling(X, g)
            else:
                selected_idx = np.arange(n_samples)
                weights = np.ones(n_samples)
            
            X_sample = X[selected_idx]
            g_sample = g[selected_idx] * weights  # 加权梯度
            
            # 基于直方图拟合树(叶子优先生长)
            tree = self._fit_leafwise_tree(X_sample, g_sample)
            
            pred += self.learning_rate * tree.predict(X)
            self.trees.append(tree)
            
        return self
    
    def _build_histogram_bins(self, X):
        """为所有特征构建直方图分箱"""
        n_features = X.shape[1]
        bins = {}
        for i in range(n_features):
            # 基于百分位数分箱(LightGBM风格)
            percentiles = np.linspace(0, 100, self.max_bins + 1)[1:-1]
            bin_edges = np.percentile(X[:, i], percentiles)
            bins[i] = bin_edges
        return bins
    
    def _fit_leafwise_tree(self, X, g):
        """
        叶子优先生长(Leaf-wise)策略
        选择分裂增益最大的叶子进行分裂(而非Level-wise的层序)
        生成更深的不对称树
        """
        class LeafWiseTree:
            def __init__(self, split_seq):
                self.splits = split_seq  # [(feature, threshold, left_val, right_val), ...]
                
            def predict(self, X):
                # 简化版:使用第一个最佳分裂
                if not self.splits:
                    return np.zeros(len(X))
                feat, thresh, left_val, right_val = self.splits[0]
                return np.where(X[:, feat] <= thresh, left_val, right_val)
        
        # 简化实现:寻找全局最佳分裂
        best_gain = -np.inf
        best_split = None
        
        for feat_idx, bin_edges in self.bins.items():
            for thresh in bin_edges:
                left_mask = X[:, feat_idx] <= thresh
                right_mask = ~left_mask
                
                if np.sum(left_mask) < 2 or np.sum(right_mask) < 2:
                    continue
                
                # 分裂减少的方差(MSE增益)
                left_var = np.var(g[left_mask]) if np.any(left_mask) else 0
                right_var = np.var(g[right_mask]) if np.any(right_mask) else 0
                parent_var = np.var(g)
                
                gain = parent_var - (np.sum(left_mask) * left_var + 
                                    np.sum(right_mask) * right_var) / len(g)
                
                if gain > best_gain:
                    best_gain = gain
                    left_val = np.mean(g[left_mask])
                    right_val = np.mean(g[right_mask])
                    best_split = (feat_idx, thresh, left_val, right_val)
        
        return LeafWiseTree([best_split] if best_split else [])
    
    def predict(self, X):
        pred = np.zeros(len(X))
        for tree in self.trees:
            pred += self.learning_rate * tree.predict(X)
        return pred


def demo_boosting_algorithms():
    """演示Boosting算法对比"""
    from sklearn.datasets import make_regression, make_hastie_10_2
    from sklearn.model_selection import train_test_split
    
    np.random.seed(42)
    
    # 回归数据用于梯度提升
    X_reg, y_reg = make_regression(n_samples=1000, n_features=10, n_informative=5,
                                   noise=0.1, random_state=42)
    X_train, X_test, y_train, y_test = train_test_split(X_reg, y_reg, test_size=0.3)
    
    # 分类数据用于AdaBoost(Hastie数据集)
    X_clf, y_clf = make_hastie_10_2(n_samples=2000, random_state=42)
    y_clf = np.where(y_clf == 1, 1, -1)  # 转换为+1/-1
    X_train_c, X_test_c, y_train_c, y_test_c = train_test_split(X_clf, y_clf, test_size=0.3)
    
    fig, axes = plt.subplots(2, 2, figsize=(16, 12))
    
    # 1. AdaBoost收敛分析(训练误差上界)
    adaboost = AdaBoost(n_estimators=100)
    adaboost.fit(X_train_c, y_train_c)
    
    # 计算每轮的训练误差和权重分布
    train_errors = []
    for i in range(1, len(adaboost.stumps) + 1):
        pred = np.zeros(len(X_train_c))
        for j in range(i):
            pred += adaboost.alphas[j] * adaboost.stumps[j].predict(X_train_c)
        pred = np.sign(pred)
        error = np.mean(pred != y_train_c)
        train_errors.append(error)
    
    # 3.3.1.1 指数误差上界:exp(-2γ^2 T)
    # 估算平均gamma
    gammas = [0.5 - stump.fit(X_train_c, y_train_c, adaboost.weight_history[t]) 
              for t, stump in enumerate(adaboost.stumps)]
    avg_gamma = np.mean([g for g in gammas if g > 0])
    theoretical_bound = [np.exp(-2 * avg_gamma**2 * t) for t in range(1, len(train_errors)+1)]
    
    axes[0, 0].semilogy(range(1, len(train_errors)+1), train_errors, 'b-', 
                       label='Training Error', linewidth=2)
    axes[0, 0].semilogy(range(1, len(theoretical_bound)+1), theoretical_bound, 'r--',
                       label=f'Theoretical Upper Bound (γ={avg_gamma:.3f})', linewidth=2)
    axes[0, 0].set_xlabel('Iteration (Number of Weak Learners)')
    axes[0, 0].set_ylabel('Error Rate (log scale)')
    axes[0, 0].set_title('3.3.1.1 AdaBoost Convergence\n(Exponential Error Bound)')
    axes[0, 0].legend()
    axes[0, 0].grid(True, alpha=0.3)
    
    # 2. 梯度提升:收缩(Shrinkage)效果
    learning_rates = [0.01, 0.1, 0.3, 1.0]
    colors = plt.cm.viridis(np.linspace(0, 1, len(learning_rates)))
    
    for lr, color in zip(learning_rates, colors):
        gb = GradientBoosting(n_estimators=100, learning_rate=lr, subsample=0.8)
        gb.fit(X_train, y_train)
        
        # 计算每轮的测试误差(简化:存储历史)
        errors = []
        pred = np.full(len(X_test), gb.initial_prediction)
        for tree, feat_idx in gb.trees:
            pred += lr * tree.predict(X_test[:, feat_idx])
            errors.append(np.mean((pred - y_test)**2))
        
        axes[0, 1].plot(range(1, len(errors)+1), errors, color=color, 
                       label=f'η={lr}', linewidth=2)
    
    axes[0, 1].set_xlabel('Number of Trees')
    axes[0, 1].set_ylabel('Test MSE')
    axes[0, 1].set_title('3.3.2.2 Shrinkage Effect\n(Learning Rate Impact)')
    axes[0, 1].legend()
    axes[0, 1].grid(True, alpha=0.3)
    
    # 3. XGBoost vs LightGBM效率对比(近似算法)
    n_samples_list = [100, 500, 1000, 2000, 5000]
    xgb_times = []
    lgb_times = []
    
    for n in n_samples_list:
        X_n, y_n = make_regression(n_samples=n, n_features=20, noise=0.1, random_state=42)
        
        import time
        
        # XGBoost近似
        start = time.time()
        xgb = XGBoostApproximate(n_estimators=20, max_bins=10)
        xgb.fit(X_n, y_n)
        xgb_times.append(time.time() - start)
        
        # LightGBM
        start = time.time()
        lgb = LightGBMHistogram(n_estimators=20, max_bins=20, top_rate=0.3, other_rate=0.1)
        lgb.fit(X_n, y_n)
        lgb_times.append(time.time() - start)
    
    axes[1, 0].plot(n_samples_list, xgb_times, 'o-', label='XGBoost (Weighted Quantile)', 
                   linewidth=2, markersize=8)
    axes[1, 0].plot(n_samples_list, lgb_times, 's-', label='LightGBM (GOSS+Histogram)', 
                   linewidth=2, markersize=8)
    axes[1, 0].set_xlabel('Number of Samples')
    axes[1, 0].set_ylabel('Training Time (seconds)')
    axes[1, 0].set_title('3.3.3 Training Efficiency Comparison\n(Approximate Algorithms)')
    axes[1, 0].legend()
    axes[1, 0].grid(True, alpha=0.3)
    
    # 4. GOSS采样效果分析(梯度分布)
    gb_full = GradientBoosting(n_estimators=50, subsample=1.0)
    gb_full.fit(X_train, y_train)
    pred_full = gb_full.predict(X_train)
    residuals_full = pred_full - y_train
    
    # 模拟GOSS采样效果
    abs_residuals = np.abs(residuals_full)
    top_indices = np.argsort(abs_residuals)[-int(0.2*len(residuals_full)):]
    
    axes[1, 1].hist(residuals_full, bins=50, alpha=0.5, label='Full Data', color='blue', density=True)
    axes[1, 1].hist(residuals_full[top_indices], bins=20, alpha=0.7, 
                   label='GOSS Top 20% (Large Gradients)', color='red', density=True)
    axes[1, 1].set_xlabel('Residual (Gradient) Value')
    axes[1, 1].set_ylabel('Density')
    axes[1, 1].set_title('3.3.3.3 GOSS: Gradient Distribution\n(Large Gradient Focus)')
    axes[1, 1].legend()
    axes[1, 1].grid(True, alpha=0.3, axis='y')
    
    plt.tight_layout()
    plt.savefig('boosting_algorithms_analysis.png', dpi=150, bbox_inches='tight')
    plt.show()
    
    print("Boosting算法分析完成")
    print(f"AdaBoost最终训练误差: {train_errors[-1]:.4f}")
    print(f"理论误差上界: {theoretical_bound[-1]:.4e}")
    print(f"平均弱学习器优势γ: {avg_gamma:.4f}")


if __name__ == "__main__":
    demo_boosting_algorithms()

以上脚本提供了第3章树模型与集成学习核心算法的完整实现,涵盖从基础决策树构建到现代高效Boosting框架(XGBoost/LightGBM)的数值优化技术。每个实现均包含算法细节注释与可视化验证,可直接用于教学演示与算法原型开发。

全面解析 Python scikit-learn:从入门到精通的机器学习库 本文介绍了Python机器学习库scikit-learn的核心功能与应用。scikit-learn基于NumPy、SciPy和matplotlib,提供简单高效的机器学习工具,支持监督学习(分类、回归)、无监督学习(聚类)、数据预处理和模型评估等任务。重点讲解了数据标准化、缺失值处理、分类回归模型(如SVM、KNN、决策树)、聚类算法(K-means、DBSCAN)以及交叉验证和网格搜索调参方法。通过波士顿房价预测案例,展示了从数据加载、标准化、模型训练到评估的完整流程。文章强调scikit-learn的一 阅读详情

相关推荐

Python机器学习全流程指南:从基础到实战

机器学习作为人工智能的核心技术,通过算法让计算机从数据中学习规律并做出预测。其核心原理包括监督学习、无监督学习和强化学习三大范式,涉及特征工程、模型训练和评估等关键环节。在实际应用中,Python凭借scikit-learn、TensorFlow等丰富的工具链,成为机器学习开发的首选语言。本文以线性回归、随机森林等经典算法为例,详解从数据预处理到模型部署的全流程实践,特别针对过拟合、特征工程等常见痛点提供解决方案。通过Kaggle竞赛级项目实战,读者可掌握如何将机器学习应用于金融风控、医疗诊断等真实场景,同

adiking520110的博客 357

[特殊字符] 终极机器学习实战指南:Hands-on ML3权威教程解析

**机器学习教程**是当今数据科学家和AI爱好者必备的学习资源,而Hands-on ML3项目正是这一领域的标杆之作。这个开源项目提供了完整的Python机器学习实践教程,通过Jupyter笔记本形式,系统讲解从Scikit-Learn到TensorFlow 2的全套技能。无论你是**机器学习入门**新手,还是希望提升技能的开发者,这个项目都能为你提供最实用的学习路径。 ## 📚 项目概述:什

gitblog_00152的博客 323

机器学习工程师必备英语词汇大全(附生词)

作者:禅与计算机程序设计艺术 1.简介 机器学习(ML)是人工智能的一个子领域,它研究如何使计算机“学会”做某些事情。然而,要真正掌握这种能力,需要精通数学、统计学、计算机科学等相关专业知识,还需要具备高度的专业素养。同时,除了掌握理论方面的基础知识外,工程师还需要大量的实践经验才能将这些理论应用到实际

AI天才研究院 395

3步掌握吴恩达机器学习专项:从零到精通的完整学习路径

想要系统学习机器学习却不知从何入手?吴恩达机器学习专项课程提供了最权威的入门路径,而这个精心整理的资源库正是你掌握核心算法的秘密武器!这个开源项目包含了Andrew Ng机器学习专项课程的完整笔记和实践代码实现,帮助你从线性回归到强化学习,构建完整的机器学习知识体系。 ## 🔍 核心学习资源架构解析 这个项目采用"理论笔记+实践代码"的双轨学习模式,将吴恩达课程的精髓转化为可操作的学习材料。

gitblog_01052的博客 319

Angular 相关的知识点和项目汇总

Angular 知识点和项目汇总

weixin_45428910的博客 581

飞书云文档API实战:从权限申请到自动化操作

本文详细介绍了飞书云文档API的实战应用,从权限申请到自动化操作。内容涵盖如何创建应用、获取关键API权限、理解并使用Tenant Access Token,并通过Python代码示例演示了读取和写入飞书表格数据的具体方法,旨在帮助开发者和业务人员实现文档数据处理的自动化,提升工作效率。

weixin_28318011的博客 1943

机器学习从入门到精通

机器学习作为人工智能的核心领域,近年来在各个行业得到了广泛应用。本文从机器学习的基础概念出发,逐步深入到核心算法、模型训练、调优以及实际应用。通过理论讲解与代码示例相结合的方式,帮助读者从零基础到掌握机器学习的关键技术和应用方法。文章还包含了数据流图,帮助读者更好地理解机器学习的流程和架构。机器学习是一种让计算机通过数据自动学习规律并做出预测的技术。它通过构建模型,从大量数据中挖掘信息,从而实现对未知数据的预测或决策。机器学习是一个不断发展和创新的领域。

加入“Super Entity”,与全能开发团队共探AI智能体与数字人项目,开启前沿技术之旅。 1041

MCP协议桥接AdsPower:AI原生时代的浏览器自动化新范式

浏览器自动化是Web开发和数据采集领域的核心技术,其核心原理是通过程序控制浏览器内核执行页面导航、元素交互和数据提取等操作。传统方案如Selenium和Playwright虽功能强大,但在管理多实例、指纹隔离等复杂场景下,配置和维护成本较高。随着AI原生应用的发展,Model Context Protocol(MCP)作为一种标准化协议,为AI助手与外部工具间的通信提供了统一接口,显著提升了自动化流程的构建效率和自然语言交互能力。通过MCP Server桥接AdsPower指纹浏览器的本地API,开发者可将

weixin_30552811的博客 296

决策树算法全解析:从零基础到Titanic实战,一文搞定机器学习经典模型

你是否曾好奇,机器学习如何从海量数据中挖掘出隐藏的规律?或者,一个简单的模型如何帮助医生诊断疾病、银行评估信用,甚至预测泰坦尼克号上的生还者?答案就在决策树算法中。作为机器学习领域的“常青树”,决策树以其直观易懂和强大的实用性,深受初学者和专业人士的喜爱。我还记得自己第一次接触决策树时的兴奋——用几行代码,就能让计算机像人类一样“思考”问题、分步决策。那一刻,我意识到它的潜力远超想象。这篇文章将带你走进决策树的世界,从零基础的构建过程,到信息增益与基尼指数的奥秘,再到剪枝优化和真实案例的实战演练。

吴师兄大模型的博客 4547

基于C#的ASP.NET开发的党史学习网站课设

开展党史学习教育,就是要以史为镜、以史明志,把苦难辉煌的过去、日新月异的现在、光明宏大的未来贯通起来,在鉴往知来中砥砺前行,在乱云飞渡中把准航向,在凝心聚力中团结奋斗,共同创造新的历史伟业。登陆按钮与数据库进行连接,逻辑实现是如图3.1.3所示,自己定义一个SQL查询语句,将账号、密码放入SQL查询语句中,用sqlDataReader.HasRows语句进行逐一比较,找出结果,将用户名存入session并调用cookies,如果结果存在就关闭数据库,进行数据跳转,并弹出登陆成功的提示框,并关闭数据库。

m0_62408555的博客 975

OBC_开环扫频波特图拟合操作流程(结合 Simulink)

摘要: 本文详细介绍了基于Simulink的开环扫频波特图测试与拟合流程,适用于电源/控制系统(如LLC/PFC)的小信号分析。主要内容包括:1)前期准备(测试点选择、信号范围设定);2)Simulink建模方法(信号注入、数据记录);3)两种扫频方式(逐频正弦/Chirp扫频)的实现;4)MATLAB数据处理(FFT分析、传递函数拟合);5)线性分析工具使用技巧。重点强调了激励信号幅值控制、稳态数据采集及模型验证方法,并提供完整MATLAB脚本示例。该流程可有效获取系统频域特性,为控制器设计提供可靠依据。

Sumerking的博客 234

Python机器学习:从零基础到项目实战

当您翻开此书,您正踏入一场数据与智慧的修行。机器学习,并非冰冷的符码,而是机器模拟人类洞察世界的法门。本书将带您,以Python为舟,泛游于算法之海。我们不只传授“术”,更探求其后的“道”——从数据的生灭流转中观照规律,于模型的迭代演进里体悟得失。愿您合上书卷时,收获的不仅是驾驭数据的技能,更有一双洞悉复杂、化繁为简的“智慧之眼”。现在,让我们一同启程。

YunWisdom 972

数据挖掘实战-基于情感分析+网络语义分析对泾阳茯茶用户评论进行文本分析

本实验数据集来源于京东商城,使用Python爬虫获取泾阳茯茶商品的用户评论,共计3416条数据,字段有用户ID、用户昵称、IP属地、评论时间、评分、评论内容,共计6个字段信息。'''作者:艾派森csdn主页:https://blog.youkuaiyun.com/m0_64336780时间:2024/5/28'''# 导包import csv# 获取评论q=0.01',q=0.9,en;b_avif=1;b_dw=1412;b_dh=650;areaId=27;

m0_64336780的博客 1万+

人工智能学习路线:从零到精通的六周计划

本文为初学者提供了一份系统的人工智能学习路线,从Python基础到深度学习应用,逐步引导读者掌握AI核心技术。学习计划分为六周,每周聚焦不同主题:第一周学习Python基础与数据分析,第二周理解机器学习原理,第三周进行机器学习实战,第四周深入深度学习原理与实战,第五周应用深度学习模型,第六周巩固计算机视觉、自然语言处理和推荐系统的基础技术。文章还推荐了丰富的学习资源和实践项目,帮助读者从理论到实践全面掌握人工智能技术,并提供了免费的学习资料合集,助力读者在AI领域不断进步。

2501_91624122的博客 1065

使用 Simulink 进行电力电子系统的小信号模型仿真

电力电子系统(Power Electronics System)包括各种功率转换器(如 DC-DC 转换器、逆变器等)。这些设备中的关键组件(如 MOSFET、IGBT、电容器、电感器等)不仅影响系统的性能,还直接关系到系统的稳定性、效率和可靠性。因此,在设计阶段进行详细的建模和仿真至关重要。小信号模型仿真的目的是通过线性化非线性系统来评估其动态特性,包括增益、相位裕度、带宽等指标。通过本教程,我们介绍了如何使用 Simulink 进行电力电子系统的小信号模型仿真。背景介绍。

xiaoheshang_123的博客 481

【WRF模拟】如何查看 WPS 的输入静态地理数据(二进制格式)?

如何查看 WPS 的输入静态地理数据(二进制格式)?

WW、forever的博客 633

决策树与集成算法全解析:从原理到实战优化指南

本文系统解析了机器学习中决策树与集成算法(随机森林、GBDT、XGBoost、LightGBM)的核心原理、应用场景和优化策略。内容涵盖:1)决策树算法基础与最新优化;2)三大集成策略(Bagging/Boosting/Stacking)对比;3)主流算法特性比较与演进关系;4)不同业务场景下的模型选择建议;5)参数调优方法与实战代码示例。重点介绍了LightGBM的直方图算法和Leaf-wise生长策略优势,以及XGBoost的正则化设计,为从业者提供从理论到实践的完整指南。

453

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

零基础转型AI算法工程师:数学、编程与机器学习的通关地图,带你找到专属AI之路!

深夜,一位原本从事传统软件开发的工程师,在调试完第100行机器学习代码后,屏幕上的模型评估指标终于跳出理想的数值。8个月后,他凭借这个项目,从一家金融科技公司拿到了AI算法工程师的录用通知,薪资涨幅达60%。

Android23333的博客 982

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

AI十年不变的核心基石:Python机器学习基础与工程实践指南

机器学习作为人工智能的核心分支,其底层原理建立在数学与统计学基础之上,通过算法模型从数据中学习规律。理解线性代数、概率统计与微积分等数学概念,是掌握梯度下降、反向传播等优化技术的关键。这些基础能力支撑着从经典算法到前沿模型的技术演进,在数据分析、预测建模等应用场景中持续发挥价值。本文结合Python编程与Scikit-learn、Pandas等工具,通过房价预测等实战案例,深入探讨机器学习工作流中的数据处理、模型评估与工程化实践,帮助开发者构建可复现、可维护的AI项目,为应对快速变化的AI浪潮奠定坚实基础。

weixin_34176694的博客 561

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

监督学习入门:从核心概念到实践应用的全流程解析

机器学习作为一门让计算机通过数据经验自动改进性能的科学,其核心在于构建能够从输入映射到输出的数学模型。监督学习作为其中最基础且应用最广泛的范式,通过带有标签的数据指导模型学习,主要解决分类与回归两大核心任务。其技术价值在于将数据驱动的模式识别能力工程化,广泛应用于金融风控、医疗诊断、推荐系统等场景。理解监督学习的标准工作流——从数据预处理、特征工程到模型训练评估与部署——是掌握机器学习应用的关键。本文结合特征工程和模型评估等核心环节,深入探讨了如何根据数据特性和业务需求选择合适的算法工具,例如在结构化数据任

weixin_33158887的博客 353

快乐去学习「快乐机器学习」

圣元哥的新书《快乐机器学习》,全彩印,讲解体系化、有趣、多图展示。前段时间圣元哥还特意送我一本,真的很不错,目前正在认真研读,在这里特此推荐给我的读者朋友们。下面是圣元对这本书的一个基本...

算法channel 868

ARIS 专利流水线实战:USPTO 美国专利申请格式完整指南

本指南围绕 ARIS(Auto-claude-code-research-in-sleep)专利流水线中的核心参考文档 `patent-format-us.md` 展开,系统讲解向美国专利商标局(USPTO)提交发明专利申请时的权利要求(Claims)、说明书(Specification)、附图(Drawings)与 IDS 信息披露声明等关键部分的撰写格式与法律依据。读完本文,你将掌握在 ARI

gitblog_00114的博客 964

机器学习实战——基于CART决策树实现葡萄酒品质预测(附完整代码和可视化)

随着机器学习技术的发展,决策树作为一种简单而强大的算法,在许多领域得到了广泛的应用。本文将通过一个具体的案例——使用决策树模型预测葡萄酒品质,来详细介绍决策树的工作原理、构建过程以及评估方法。我们将利用Python编程语言及其相关的科学计算库(如NumPy、Pandas)和机器学习库(如Scikit-learn)来进行数据分析与模型训练。

m0_59197405的博客 2131

端侧PII检测技术解析:本地识别敏感信息的隐私保护实践

在数字化应用中,PII(个人可识别信息)的保护已成为数据合规与隐私工程的核心议题。手机号、邮箱、身份证号等敏感字段一旦被发送到云端API进行分析,数据外发风险、网络依赖和按量计费等问题便随之而来。端侧PII检测技术的出现,将识别能力从中心化服务迁移到本地设备或内网环境,通过正则规则与本地NER模型的混合策略,既保证了格式化字段的高召回率,又能理解上下文以识别人名、地址等非结构化信息。该方案具备低延迟、离线可用、数据不出域等优势,特别适用于RAG管道、日志脱敏、桌面应用等场景。本文介绍的通用技术路径涵盖环境搭

weixin_34413802的博客 349

机器学习中的算法——决策树模型组合之随机森林与GBDT

前言: 决策树这种算法有着很多良好的特性,比如说训练时间复杂度较低,预测的过程比较快速,模型容易展示(容易将得到的决策树做成图片展示出来)等。但是同时,单决策树又有一些不好的地方,比如说容易over-fitting,虽然有一些方法,如剪枝可以减少这种情况,但是还是不够的。 美国金融银行业的大数据算法:随机森林模型+综合模型 模型组合(比如说有Boosting,Bagging等)与决策树相关的...

weixin_34236497的博客 157

精通数据科学笔记 监督学习

支持向量机: 进行线性或近似线性数据的分类 核函数:将低维数据映射到高维,解决低维数据非线性问题 决策树:模仿决策过程,常用作组合模型中特征提取 随机森林:相互独立的多个决策树组合,提升模型效果 GBTs:以最优方式提升模型预测结果 支持向量机 在支持向量机模型里,一个数据点与分离直线的距离表示模型对这个点的预测置信度,离得越近,模型越没把握。 对于线性可分数据,支持向量机有不错的分...

techfei的博客 240
上一篇: 【机器学习基础算法:从零实现到精通】第2章 监督学习:线性模型与基函数方法
下一篇: 【机器学习基础算法:从零实现到精通】第4章 监督学习:支持向量机与核方法
VectorShift
博客等级 码龄1年 1839粉丝 1109原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VectorShift

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值