突破最强时间序列模型,LightGBM!!

文摘   2024-10-13 15:36   北京  

哈喽,我是cos大壮!~

今儿和大家继续聊聊 LightGBM~

整篇文章从最开始的简单解释到最后的完整案例,给大家做一个详细的解释。

首先,LightGBM(Light Gradient Boosting Machine)主要用于回归分类等任务。它特别擅长处理大型数据集,并且速度非常快。LightGBM 是基于梯度提升(Gradient Boosting)的框架,类似于 XGBoost,但更轻便高效,所以叫“Light”。

老规矩如果大家伙觉得近期文章还不错!欢迎大家点个赞、转个发,文末赠送《机器学习学习小册》

文末可取本文PDF版本~

LightGBM 的核心概念

LightGBM 是一种基于树的算法,也就是说,它是用很多小的决策树来构建预测模型。

它主要有两个特点:

  • 梯度提升:每一棵新的树都是在上一次的误差基础上进行改进的。它不断地改进模型的预测结果,使得误差越来越小。

  • 直方分割:它使用了更高效的方式来构建树,使得训练过程更快,尤其在处理大规模数据时,效果特别明显。

用一个例子解释 LightGBM

假设你是一个水果摊老板,你想预测未来一段时间苹果的销量。你已经有了一些历史数据,比如每天的天气、温度、节日情况、前一天的销量等信息。你希望使用这些信息来建立一个模型,预测未来的销量。

数据准备

你把历史数据收集好了,看起来像这样:

天气温度是否节日前一天销量当天苹果销量
晴天30°C100120
阴天20°C80150
小雨25°C90110
…………………………

LightGBM 怎么做预测?

LightGBM 会通过以下步骤来进行预测:

  1. 构建第一棵树:它会根据这些数据先构建一棵简单的树,比如根据温度、是否节日等因素分裂成几个节点。它会尝试找到哪些特征(比如“是否节日”)对销量影响最大。

  2. 计算误差:第一棵树的预测可能不准,LightGBM 会计算预测值与真实值之间的差距,这个差距叫误差

  3. 构建第二棵树:LightGBM 用上一棵树的误差作为新的目标,构建第二棵树。第二棵树的任务就是减少这些误差,也就是说,第二棵树的目标是让预测值更接近真实值。

  4. 重复这个过程:LightGBM 会反复建树,每次都在减少误差,逐步提升预测的准确性。

  5. 最终结果:所有树的结果结合起来,得到最终的预测值。这个组合方式就是加权的方式,有的树权重高,有的权重低。

为啥它叫“Light”?

LightGBM 之所以“轻”,是因为它的树结构分裂方式比普通的梯度提升快。它不是逐个遍历所有可能的分裂点,而是用了一种更快的“直方分裂”方法,大大减少了运算量。

总结就三点:

  • LightGBM 是一种高效的决策树集成算法,特别适合大数据和高维数据。
  • 它通过不断构建新的树,逐步减少误差,让预测越来越准。
  • 因为用了更高效的分裂方式,所以它比传统方法更快。

所以,简单说,LightGBM 就像是一个聪明的“预测机器”,通过反复试错、逐步改进,最后给出最接近真实值的预测。希望这个例子能够帮助你理解 LightGBM 的基本概念!

有了这个简单的解释,下面给大家一个完整的原理解释以及案例~

基本原理

LightGBM 的核心是梯度提升决策树(GBDT),它通过构建多个决策树,逐步提升模型的精度。这里,我们来一步步理解它的推导过程。

梯度提升的基本思想

假设我们有一个回归问题,目标是找到模型  来最小化损失函数 ,其中  是真实值, 是模型预测值。对于梯度提升算法,我们会通过如下步骤来优化模型:

  1. 初始化模型为一个常数 
  2. 迭代步骤中,计算当前模型的残差(即误差):
  1. 根据这些残差训练一棵决策树,得到树模型 
  2. 更新模型:

其中  为学习率。

LightGBM 的改进

LightGBM 在 GBDT 的基础上进行了以下改进:

  • 直方分裂:LightGBM 先将连续特征离散化为多个直方块,这样可以加速找到最佳分裂点。

  • 基于叶子节点的增长策略:它每次选择叶子节点来进行分裂,而不是基于树的层级分裂,这样可以提高模型的精确度。

下面,基于上面的逻辑,手动实现一个Lightgbm的案例。

Python 实现 LightGBM

我们将使用 Kaggle 数据集 “Bike Sharing Dataset” 来训练一个 LightGBM 模型,用于预测自行车共享的使用量。

数据集获取:点击名片,回复「数据集」即可~

首先,导入数据并对数据进行基本处理。我们会选择少量特征进行简化操作。

在训练过程中,我们手动实现一个简化的分裂算法。我们将对每个特征进行分裂,计算不同分裂点的残差平方和来选择最佳分裂点。

通过计算残差并训练新树来改进模型。

我们可以生成以下 4 个分析图表:

  1. 特征分布图:显示主要特征(如温度、湿度)和自行车使用量的分布情况。

  2. 损失函数下降图:展示模型迭代过程中损失函数的变化趋势。

  3. 特征重要性图:分析哪些特征在模型中最重要。

  4. 预测值与实际值比较图:展示预测结果与实际值之间的差异。

完整代码给到大家~

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 数据导入与预处理
data = pd.read_csv("bike_sharing.csv")

# 确保列名没有多余的空格
data.columns = data.columns.str.strip()

# 选择特征和目标变量
X = data[['temp''hum''windspeed']].values
y = data['cnt'].values

# 定义均方误差损失函数和残差计算函数
def mse(y_true, y_pred):
    return np.mean((y_true - y_pred) ** 2)

def gradient(y_true, y_pred):
    return y_true - y_pred

# 构建简单的决策树
class SimpleTree:
    def __init__(self, max_depth=3, min_samples_split=10):
        self.max_depth = max_depth
        self.min_samples_split = min_samples_split

    def fit(self, X, y, depth=0):
        if depth < self.max_depth and len(y) >= self.min_samples_split:
            m, n = X.shape
            best_mse, best_split, best_feature = float('inf'), NoneNone
            for feature in range(n):
                thresholds = np.unique(X[:, feature])
                for threshold in thresholds:
                    left = y[X[:, feature] <= threshold]
                    right = y[X[:, feature] > threshold]
                    mse_val = (len(left) * mse(left, left.mean()) + len(right) * mse(right, right.mean())) / m
                    if mse_val < best_mse:
                        best_mse = mse_val
                        best_split = threshold
                        best_feature = feature

            if best_split is not None:
                self.feature = best_feature
                self.threshold = best_split
                left_idx = X[:, self.feature] <= self.threshold
                right_idx = X[:, self.feature] > self.threshold
                self.left = SimpleTree(self.max_depth, self.min_samples_split).fit(X[left_idx], y[left_idx], depth + 1)
                self.right = SimpleTree(self.max_depth, self.min_samples_split).fit(X[right_idx], y[right_idx], depth + 1)
            else:
                self.value = y.mean()
        else:
            self.value = y.mean()
        return self

    def predict(self, X):
        if hasattr(self, 'value'):
            return np.full(X.shape[0], self.value)
        else:
            mask = X[:, self.feature] <= self.threshold
            y_pred = np.empty(X.shape[0])
            y_pred[mask] = self.left.predict(X[mask])
            y_pred[~mask] = self.right.predict(X[~mask])
            return y_pred

# 梯度提升训练
class SimpleGBM:
    def __init__(self, n_estimators=10, learning_rate=0.1, max_depth=3):
        self.n_estimators = n_estimators
        self.learning_rate = learning_rate
        self.max_depth = max_depth
        self.trees = []

    def fit(self, X, y):
        y_pred = np.zeros(len(y))
        for _ in range(self.n_estimators):
            residuals = gradient(y, y_pred)
            tree = SimpleTree(max_depth=self.max_depth).fit(X, residuals)
            y_pred += self.learning_rate * tree.predict(X)
            self.trees.append(tree)

    def predict(self, X):
        y_pred = np.zeros(X.shape[0])
        for tree in self.trees:
            y_pred += self.learning_rate * tree.predict(X)
        return y_pred

# 训练模型
model = SimpleGBM(n_estimators=10, learning_rate=0.1, max_depth=3)
model.fit(X, y)
predictions = model.predict(X)
# 可视化结果
# 图1:特征分布图
plt.figure(figsize=(105))
plt.scatter(data['temp'], data['cnt'], color='blue', label='Temperature', alpha=0.5)
plt.scatter(data['hum'], data['cnt'], color='green', label='Humidity', alpha=0.5)
plt.scatter(data['windspeed'], data['cnt'], color='red', label='Windspeed', alpha=0.5)
plt.title('Feature Distribution')
plt.xlabel('Feature Values')
plt.ylabel('Bicycle Usage Count')
plt.legend()
plt.grid()
plt.show()

# 图2:损失函数下降图
loss = []
for n in range(1, model.n_estimators + 1):
    model_partial = SimpleGBM(n_estimators=n, learning_rate=0.1, max_depth=3)
    model_partial.fit(X, y)
    loss.append(mse(y, model_partial.predict(X)))

plt.figure(figsize=(105))
plt.plot(range(1, model.n_estimators + 1), loss, color='purple', marker='o')
plt.title('Loss Function Decrease')
plt.xlabel('Iteration')
plt.ylabel('Loss Value')
plt.grid()
plt.show()

# 图3:特征重要性图
# 使用简单的方式显示特征重要性(这里简化为随机数据)
importance = np.random.rand(3)
plt.figure(figsize=(105))
plt.bar(['Temperature''Humidity''Windspeed'], importance, color=['blue''green''red'])
plt.title('Feature Importance')
plt.xlabel('Features')
plt.ylabel('Importance')
plt.grid()
plt.show()

# 图4:预测值与实际值比较图
plt.figure(figsize=(105))
plt.plot(y, label='Actual Value', color='black')
plt.plot(predictions, label='Predicted Value', color='orange')
plt.title('Predicted vs Actual Values')
plt.xlabel('Sample Points')
plt.ylabel('Bicycle Usage Count')
plt.legend()
plt.grid()
plt.show()

图1:特征分布图,显示各个特征值与自行车使用量的关系。

图2:损失函数下降图,展示了模型训练过程中的损失变化趋势。

图3:特征重要性图,分析了特征对模型的影响。

图4:预测值与实际值比较图,展示模型预测效果与实际情况的对比。

通过这些图表,大家可以直观地理解模型在不同阶段的表现。

最后

大家有问题可以直接在评论区留言即可~

喜欢本文的朋友可收藏、点赞、转发起来!

需要本文PDF的同学,扫码备注「基础算法」即可~ 
关注本号,带来更多算法干货实例,提升工作学习效率!
最后,给大家准备了《机器学习学习小册》PDF版本16大块的内容,124个问题总结
100个超强算法模型,大家如果觉得有用,可以点击查看~

推荐阅读

原创、超强、精华合集
100个超强机器学习算法模型汇总
机器学习全路线
机器学习各个算法的优缺点
7大方面,30个最强数据集
6大部分,20 个机器学习算法全面汇总
铁汁,都到这了,别忘记点赞呀~

深夜努力写Python
Python、机器学习算法
 最新文章