梯度提升Gradient Boosting

总览#

Gradient Boosting 梯度提升,是一种强大的 ML 技术,用于回归和分类问题。

弱学习算法通常更易发现、更易训练。Boosting 系列算法的基本思想是将弱基础模型组合为一个强大的集成。

Gradient Boosting 的基本思想是:不断重复生成弱学习器,每次生成弱学习器的目标是拟合先前累加模型的损失函数的负梯度, 使组合上该弱学习器后的累积模型 损失往负梯度的方向减少。

Gradient Boosting 概述#

约定以下符号:

  • 输入 x 输出 y
  • 模型 Fm(x)
  • 弱模型 fm(x)
  • 损失函数 L(F^m(x),y)

由于 Gradient Boosting 会依次训练出 m 个模型,所以用下标 m 区分它们。

在 Gradient Boosting 中,我们对 损失函数相对于模型输出的导数 感兴趣:

g(x)=L(F^(x),y)F^(x)

为了训练出下一个弱模型 fm(x),需要拟合模型 F^m1(x) 输出的负梯度:

f^m=arg minf1Ni=1N(g(x)f(xi))2

很容易令人联想到均方误差 L(x,y)=1n(xiyi)2。可以说,获得弱模型就是使用 MSE 损失函数拟合负梯度 g(x)

借助弱模型 fm(x) 更新 F^m1(x),获得下一个模型 F^m(x)。其中 γ 是学习率(一般在 0.01 或 0.001):

F^m(x)=F^m1(x)+γf^m(x)

如此,一直重复获得 fm(x) 和更新 Fm1(x) 的步骤,便可逐渐获得强大模型。

第一个弱模型#

第一个弱模型通常只是一个常数 c。找到一个使得损失 L 最小的常数:

f^0=arg minc1Ni=1NL(c,yi)

对于 MSE 损失,这个 c 就是 y 的均值。

Gradient Boosting 的步骤#

有了以上的认识,应当能理解 Gradient Boosting 算法流程:

  1. 初始化一个常数模型
  2. 拟合模型的负梯度,获得一个弱模型
  3. 用弱模型更新模型
  4. 回到 step.2

实现#

以下一个示例,从输入 x 预测 sigmamu

可见使用到了 DecisionTreeRegressor。这个学习器易训练,很适合堆叠。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeRegressor
from sklearn.linear_model import LinearRegression
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
import torch
from torch.distributions.normal import Normal
from torch.autograd import Variable
from typing import List, Optional

class GaussianGradientBoosting:
    def __init__(self,
                 learning_rate: float = 0.025,
                 max_depth: int = 1,
                 n_estimators: int =100):
        self.learning_rate: float = learning_rate
        self.max_depth: int = max_depth
        self.n_estimators: int = n_estimators
        self.init_mu: Optional[float] = None
        self.mu_trees: List[DecisionTreeRegressor] = []
        
        self.init_sigma: Optional[float] = None
        self.sigma_trees: List[DecisionTreeRegressor] = []
        
        self.is_trained: bool = False
    def predict(self, X: np.array) -> np.array:
        assert self.is_trained
        mus = self._predict_mus(X).reshape(-1,1)
        log_sigmas = np.exp(self._predict_log_sigmas(X).reshape(-1,1))
        return np.concatenate([mus, log_sigmas], 1) 
    def _predict_raw(self, X: np.array) -> np.array:
        assert self.is_trained
        mus = self._predict_mus(X).reshape(-1,1)
        log_sigmas = self._predict_log_sigmas(X).reshape(-1,1)
        return np.concatenate([mus, log_sigmas], 1) 
    
    def fit(self, X: np.array, y: np.array) -> None:
        self._fit_initial(y)
        self.is_trained = True
        for _ in range(self.n_estimators):
            y_pred = self._predict_raw(X)
            gradients = self._get_gradients(y, y_pred)
            mu_tree = DecisionTreeRegressor(max_depth=self.max_depth)
            mu_tree.fit(X, gradients[:,0])
            self.mu_trees.append(mu_tree)
            sigma_tree = DecisionTreeRegressor(max_depth=self.max_depth)
            sigma_tree.fit(X, gradients[:,1])
            self.sigma_trees.append(sigma_tree)
    def _fit_initial(self, y: np.array) -> None:
        assert not self.is_trained
        
        self.init_mu = np.mean(y)
        self.init_sigma = np.log(np.std(y))
    def _get_gradients(self, y: np.array, y_pred: np.array) -> np.array:
        y_torch = torch.tensor(y).float()
        y_pred_torch = Variable(torch.tensor(y_pred).float(), requires_grad=True)
        normal_dist = Normal(y_pred_torch[:,0], torch.exp(y_pred_torch[:,1])).log_prob(y_torch).sum()
        normal_dist.backward()
        return y_pred_torch.grad.numpy()
    def _predict_mus(self, X: np.array) -> np.array:
        output = np.zeros(len(X))
        output += self.init_mu
        for tree in self.mu_trees:
            output += self.learning_rate * tree.predict(X)
        return output
    def _predict_log_sigmas(self, X: np.array) -> np.array:
        output = np.zeros(len(X))
        output += self.init_sigma
        for tree in self.sigma_trees:
            output += self.learning_rate * tree.predict(X)
        return output

参考来源#

作者:chirp

出处:https://www.cnblogs.com/chirp/p/18188119

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   倒地  阅读(241)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示