使用 GPU 加速的 XGBoost 预测出租车费用

news2024/11/15 13:44:11

目录

XGBoost

GPU 加速的 XGBoost

用例数据集示例

将文件中的数据加载到 DataFrame

定义特征数组

保存模型

总结


前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家, 可以当故事来看,轻松学习。

大数据是用于改善城市生活的 10 大领域之一。通过分析城市内的位置信息和行为模式,相关人员可以优化交通、作出更明智的规划决策,并提升广告的智能性。例如,通过分析 GPS 汽车数据,相关人员可以根据城市的实时交通信息优化交通流量。电信公司可利用手机定位数据,通过识别和预测大都市地区的位置活动趋势和人口模式来提供分析洞见。而且,现已证明,将机器学习 (ML) 应用于地理位置数据对于确定电信、旅游、市场营销和制造业的行业模式和发展趋势大有帮助。

在本章中,我们将使用纽约出租车行程公开数据来检验出租车行程数据的回归分析,因为该数据与预测纽约市出租车费用较为相关。首先,我们将概述 XGBoost 算法,然后探讨用例。

XGBoost

XGBoost 即极端梯度提升,是可扩展的分布式梯度提升决策树 (GBDT) 机器学习库。XGBoost 提供并行树提升功能,是应用于回归、分类和排序问题的出色 ML 库。RAPIDS 团队与 Distributed Machine Learning Common (DMLC) XGBoost 组织建立了紧密的合作关系,而且 XGBoost 现已加入无缝嵌入式 GPU 加速特性,可显著加快模型训练速度并提高准确性,从而得出更精确的预测结果。

梯度提升决策树 (GBDT) 是类似于随机森林的决策树集成算法,两者的区别在于树的构建和组合方式。随机森林使用名为 Bagging 的技术,通过数据集的自助抽样样本并行构建完整的决策树。最终得到的预测结果是所有决策树预测结果的平均值。梯度提升决策树使用名为 Boosting 的技术对一组浅层决策树进行迭代训练,每次迭代都会使用为前一个样本中的记录所赋予的权重(并未正确预测),从而减少后继树的错误。最终得到的预测结果是所有决策树预测结果的加权平均值。Bagging 可大幅减少差异和过拟合,而 Boosting 则可大幅减少偏差和欠拟合。

XGBoost 是 GBDT 的变体。使用 GBDT 时,决策树可按顺序构建。使用 XGBoost 时,决策树为并行构建,遵循 level-wise 生长策略,扫描梯度值并使用这些部分和来评估训练集中每个可分割点的分割质量。 

GPU 加速的 XGBoost

GPU 加速的 XGBoost 算法利用并行前缀快速求和运算来扫描所有可能的分割,并通过并行基数排序对数据进行重新分区。此算法针对给定的提升迭代构建决策树,一次生成一层,并在 GPU 上同时处理整个数据集。

GPU 加速的 Spark XGBoost 提供以下关键特性:

  • 跨多个 GPU 的 ORC、CSV 和 Parquet 输入文件分区
    几乎任意数量或大小的受支持格式输入文件均可在不同的训练节点之间平均分配。
  • GPU 加速的训练
    XGBoost 训练时间通过训练数据的动态内存表示形式(基于数据集的稀疏性来充分优化存储特性)减少,而不是通过固定内存表示形式(基于不同训练实例中最大数量特性)减少。决策树使用梯度对构建,而梯度对可通过重复使用来节省内存,从而减少副本以提高性能。
  • 高效的 GPU 显存利用
    XGBoost 要求将数据装入显存,从而对使用单个 GPU 或分布式多 GPU 多节点训练的数据大小进行限制。现在,随着 GPU 显存利用率提高,用户可以训练的数据量已达到第一版的五倍。这是在不影响性能的前提下降低培训总成本的关键因素之一。

用例数据集示例

示例数据集为纽约出租车数据集,该数据集已经过清理和转换,可添加特征,如使用此 Spark ETL notebook 的 Haversine 距离。

在此场景中,我们将基于以下特征构建模型,进而预测出租车费用:

  • 标签 🡪 车费
  • 特征 🡪 {passenger count, trip distance, pickup longitude, pickup latitude, rate code, dropoff longitude, dropoff latitude, hour, day of week, is weekend}

将文件中的数据加载到 DataFrame

首先,我们导入 Spark XGBoost 的 GPU 版本和 CPU 版本所需的软件包:

import org.apache.spark.sql.functions._
import org.apache.spark.sql.types._
import org.apache.spark.sql._
import org.apache.spark.ml._
import org.apache.spark.ml.feature._
import org.apache.spark.ml.evaluation._
import org.apache.spark.sql.types._
import ml.dmlc.xgboost4j.scala.spark.{XGBoostRegressor, XGBoostRegressionModel}

使用 Spark XGBoost 的 GPU 版本时,您还需要进行以下导入操作:

import ml.dmlc.xgboost4j.scala.spark.rapids.{GpuDataReader, GpuDataset}

我们使用 Spark StructType 指定模式。

lazy val schema =
  StructType(Array(
    StructField("vendor_id", DoubleType),
    StructField("passenger_count", DoubleType),
    StructField("trip_distance", DoubleType),
    StructField("pickup_longitude", DoubleType),
    StructField("pickup_latitude", DoubleType),
    StructField("rate_code", DoubleType),
    StructField("store_and_fwd", DoubleType),
    StructField("dropoff_longitude", DoubleType),
    StructField("dropoff_latitude", DoubleType),
    StructField(labelName, DoubleType),
    StructField("hour", DoubleType),
    StructField("year", IntegerType),
    StructField("month", IntegerType),
    StructField("day", DoubleType),
    StructField("day_of_week", DoubleType),
    StructField("is_weekend", DoubleType)
  )) 

在以下代码中,我们创建 Spark 会话并设置训练和评估数据文件路径。(请注意:如果您使用 notebook,则不必创建 SparkSession。)

val trainPath = "/FileStore/tables/taxi_tsmall.csv"
val evalPath  = "/FileStore/tables/taxi_esmall.csv"
val spark = SparkSession.builder().appName("Taxi-GPU").getOrCreate

我们将 CSV 文件的数据加载到 Spark DataFrame 中,并指定要加载到 DataFrame 中的数据源和模式,具体如下所示。

val tdf = spark.read.option("inferSchema",
"false").option("header", true).schema(schema).csv(trainPath)
val edf = spark.read.option("inferSchema", "false").option("header",
true).schema(schema).csv(evalPath)

DataFrame show(5) 显示前 5 行:

tdf.select("trip_distance", "rate_code","fare_amount").show(5)
result:
+------------------+-------------+-----------+
|     trip_distance|    rate_code|fare_amount|
+------------------+-------------+-----------+
|              2.72|-6.77418915E8|       11.5|
|              0.94|-6.77418915E8|        5.5|
|              3.63|-6.77418915E8|       13.0|
|             11.86|-6.77418915E8|       33.5|
|              3.03|-6.77418915E8|       11.0|
+------------------+-------------+-----------+

函数 Describe 返回一个 DataFrame,其中包含描述性汇总统计信息,例如计数、均值、标准差以及每个数字列的最小值和最大值。

tdf.select("trip_distance", "rate_code","fare_amount").describe().show
+-------+------------------+--------------------+------------------+
|summary|     trip_distance|           rate_code|       fare_amount|
+-------+------------------+--------------------+------------------+
|  count|              7999|                7999|              7999|
|   mean| 3.278923615451919|-6.569284350812602E8|12.348543567945994|
| stddev|3.6320775770793547|1.6677419425906155E8|10.221929466939088|
|    min|               0.0|       -6.77418915E8|               2.5|
|    max|35.970000000000006|       1.957796822E9|             107.5|
+-------+------------------+--------------------+------------------+

以下散点图用于探讨车费与行程距离之间的相关性。

%sql
select trip_distance, fare_amount
from taxi

定义特征数组

需要将由 ML 算法使用的特征进行转换,并将其放入特征向量,这些向量是代表每个特征值的数字向量。如下所示,我们使用 VectorAssembler 转换器返回带有标签和向量特征列的新 DataFrame。 

// 特征列名称
val featureNames = Array("passenger_count","trip_distance", "pickup_longitude","pickup_latitude","rate_code","dropoff_longitude", "dropoff_latitude", "hour", "day_of_week","is_weekend")
// 创建转换器
object Vectorize {
  def apply(df: DataFrame, featureNames: Seq[String], labelName: String): DataFrame = {
    val toFloat = df.schema.map(f => col(f.name).cast(FloatType))
    new VectorAssembler()
      .setInputCols(featureNames.toArray)
      .setOutputCol("features")
      .transform(df.select(toFloat:_*))
      .select(col("features"), col(labelName))
  }
}
// transform 方法添加特征列
var trainSet = Vectorize(tdf, featureNames, labelName)
var evalSet = Vectorize(edf, featureNames, labelName)
trainSet.take(1)
result:
res8: Array[org.apache.spark.sql.Row] =
Array([[5.0,2.7200000286102295,-73.94813537597656,40.82982635498047,-6.77418944E8,-73.96965026855469,40.79747009277344,10.0,6.0,1.0],11.5])

使用 XGBoost GPU 版本时,不需使用 VectorAssembler。

使用 CPU 版本时,应将 num_workers 设置为 CPU 内核数,将 tree_method 设置为“hist”,并在 Vector Assembler 中将特征列设置为输出特征列。

lazy val paramMap = Map(
  "learning_rate" -> 0.05,
  "max_depth" -> 8,
  "subsample" -> 0.8,
  "gamma" -> 1,
  "num_round" -> 500
)
// 设置 xgboost 参数
val xgbParamFinal = paramMap ++ Map("tree_method" -> "hist", "num_workers" -> 12)
// 创建 xgboostregressor 估测器
val xgbRegressor = new XGBoostRegressor(xgbParamFinal)
      .setLabelCol(labelName)
      .setFeaturesCol("features")

使用 GPU 版本时,应将 num_workers 设置为 Spark 集群中具有 GPU 的计算机的数量,将 tree_method 设置为“gpu_hist”,并将特征列设置为包含特征名称的字符串数组。

val xgbParamFinal = paramMap ++ Map("tree_method" -> "gpu_hist",
"num_workers" -> 1)
// 创建估测器
val xgbRegressor = new XGBoostRegressor(xgbParamFinal)
  .setLabelCol(labelName)
  .setFeaturesCols(featureNames)

以下代码在训练数据集上使用 XGBoostRegressor 估测器拟合方法来训练并返回 XGBoostRegressor 模型。此外,我们还使用时间方法返回训练模型的时间,并使用此方法对采用 CPU 和 GPU 的训练时间进行比较。

object Benchmark {
  def time[R](phase: String)(block: => R): (R, Float) = {
    val t0 = System.currentTimeMillis
    val result = block // call-by-name
    val t1 = System.currentTimeMillis
    println("Elapsed time [" + phase + "]: " + 
    ((t1 - t0).toFloat / 1000) + "s")
    (result, (t1 - t0).toFloat / 1000)
  }
}
// 使用估测器来拟合(训练)模型
val (model, _) = Benchmark.time("train") {
  xgbRegressor.fit(trainSet)
}

尚未用于训练的评估数据集也可用于评估模型的性能。我们使用模型转换方法对测试数据进行预测。 

此模型将使用经训练的 XGBoost 模型进行估测,然后在返回的 DataFrame 的新预测列中返回车费预测结果。此处,我们再次使用基准测试时间方法来比较预测时间。 

val (prediction, _) = Benchmark.time("transform") {
  val ret = model.transform(evalSet).cache()
  ret.foreachPartition(_ => ())
  ret
}
prediction.select( labelName, "prediction").show(10)
Result:
+-----------+------------------+
|fare_amount|        prediction|
+-----------+------------------+
|        5.0| 4.749197959899902|
|       34.0|38.651187896728516|
|       10.0|11.101678848266602|
|       16.5| 17.23284912109375|
|        7.0| 8.149757385253906|
|        7.5|7.5153608322143555|
|        5.5| 7.248467922210693|
|        2.5|12.289423942565918|
|        9.5|10.893491744995117|
|       12.0| 12.06682014465332|
+-----------+------------------+

RegressionEvaluator 评估方法将计算预测列和标签列的均方根误差,即均方误差的平方根。 

val evaluator = new RegressionEvaluator().setLabelCol(labelName)
val (rmse, _) = Benchmark.time("evaluation") {
  evaluator.evaluate(prediction)
}
println(s"RMSE == $rmse")
Result:
Elapsed time [evaluation]: 0.356s
RMSE == 2.6105287283128353

保存模型

如下所示,可将模型保存到磁盘,以便以后使用。

model.write.overwrite().save(savepath)

保存模型可得到一个元数据的 JSON 文件和一个模型数据的 Parquet 文件。我们可以使用加载命令重新加载模型。原始模型和重新加载的模型相同。

val sameModel = XGBoostRegressionModel.load(savepath)

总结

在本章中,我们介绍了 XGBoost 的工作原理,以及如何将 XGBoost 回归和 Spark 结合使用来预测出租车费用。现在,您可以利用大型数据集,在 CPU 和 GPU 上运行此示例,以比较预测的时间和准确性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1996967.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小智纯前端js报表实战5-绝对坐标-横向扩展

绝对坐标-横向扩展 概述 绝对坐标-横向扩展:绝对坐标定位 层次坐标是实现复杂报表的一个重要功能。 在进行小智报表模板设计时,单元格尚未进行扩展,但是有些时候需要获取扩展后的单元格并进行计算。例如,A1单元格扩展成A1-D1&am…

VUE+Spring前后台传值的坑,后台接收的String参数在末尾会出现 “=”

一、问题 VUESpringBoot做增删改查时,前端使用axios.post发起请求,传输主键字符型参数 taskId 到后台,后台再进行删除处理。 实际过程中发现后台拿到的数据再末尾多了一个等号,但是通过console.log(taskId)前台打印参数是正常的…

巴洛克风格的现代演绎,戴上亚法银耳机,感受古典雕花与现代声学的碰撞

flipears耳机品牌以其独特的风格、精细的配置和卓越的音质在耳机市场中很受欢迎,像是我最近用过的一款Artha Argentum亚法银,就采用了纯银外壳,而且用料扎实,具有出众的声学表现,带来了更干净清澈的声底。内在配置方面…

[Linux] LVM挂载的硬盘重启就掉的问题解决

问题:系统重启后挂在逻辑卷的盘会掉(必现) 环境:SUSE Linux 11 SP4 原因:boot.lvm是关闭的 解决:boot.lvm设置开启 参考资料: linux下lvm状态Not avaliable问题排查及处理(常见Suse操作系统…

使用ubuntu串口数据收和发不一致问题

串口配置 使用virtual Serial Port Driver Pro模拟串口两个串口,com2和com3,使用默认配置;通过virtual box 串口映射功能,在Ubuntu里使用CuteCom打开com2接受和发送数据,在windows里使用com3发送和接收数据。 遇到问…

24/8/9算法笔记 随机森林

"极限森林"(Extremely Randomized Trees,简称ERT)是一种集成学习方法,它属于决策树的变体,通常被归类为随机森林(Random Forest)的一种。极限森林的核心思想是在构建决策树时引入极端…

空间推理验证码的介绍!

空间推理验证码 ​是一种验证码形式,‌旨在通过要求用户解决一些视觉或空间推理问题来区分计算机和人类用户。‌这种验证码形式要求用户通过完成一些视觉或空间推理任务来证明他们是真实的人类用户,‌而不是计算机程序。‌空间推理验证码通常涉及一些图…

智慧交通:将物联网与人工智能完美融合

智慧交通是当今社会面临的一个重要挑战,也是人们生活质量提高的一个重要方面。通过将物联网技术与人工智能相结合,我们能够实现智慧交通系统的全面升级和优化,为人们带来更加便捷、高效和安全的出行体验。 在智慧交通领域,物联网…

Java面试题--JVM大厂篇之从原理到实践:JVM 字节码优化秘籍

目录 引言: 正文: 1. JVM 字节码生成原理 2. 字节码优化的痛点 3. 字节码优化策略 3.1 方法内联(Method Inlining) 3.2 循环展开(Loop Unrolling) 3.3 常量折叠(Constant Folding&#…

线程池原理(二)关键源码剖析

更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 属性 & 构造方法 对于 ThreadPoolExecutor 有几个关键的属性,这里需要先大致了解: public class ThreadPoolExecutor extends AbstractExecutorService {// 控制变量-存放状态和…

什么是NLP分词(Tokenization)

在自然语言处理和机器学习的领域里,咱们得聊聊一个超基础的技巧——就是“分词”啦。这个技巧啊,就是把一长串的文字切分成小块,让机器能更容易地“消化”。这些小块,不管大小,单个的字符也好,整个的单词也…

汽车EDI:德国大众 EDI 项目案例

德国大众(Volkswagen)是成立于1937年的大型汽车制造商,总部位于德国沃尔夫斯堡。大众集团拥有众多知名汽车品牌,如奥迪、保时捷、宾利、兰博基尼、布加迪等,业务遍及全球。作为一个全球性企业,大众集团依赖…

时序预测|基于变分模态分解-时域卷积-双向长短期记忆-注意力机制多变量时间序列预测VMD-TCN-BiLSTM-Attention

时序预测|基于变分模态分解-时域卷积-双向长短期记忆-注意力机制多变量时间序列预测VMD-TCN-BiLSTM-Attention 文章目录 前言时序预测|基于变分模态分解-时域卷积-双向长短期记忆-注意力机制多变量时间序列预测VMD-TCN-BiLSTM-Attention 一、VMD-TCN-BiLSTM-Attention模型1. **…

SystemUI plugin 开发

一、前言 SystemUI结构复杂,模块数量众多,最重要的是SystemUI属于常驻进程是一个系统的门面,且不能自升级,如果定制功能对主项目做复杂的修改,首先会造成适配压力,如果对主框架不甚理解,有可能会造成很多隐藏的Bug,且不易修复,一旦崩溃对整个系统的影响很大,那么怎…

【从零开始一步步学习VSOA开发】VSOA数据流

VSOA数据流 概念 实际业务中常常存在既有实时命令通信,又有非实时的大数据通信,如文件、音视频传输服务等,如果使用常规的 RPC 或订阅/发布功能来实现,将实时命令和大数据传输混在一起,则会影响 RPC 通道响应的实时性…

C语言程序设计-[10] for语句循环结构

1、for语句循环结构定义 for语句循环结构的一般形式、流程图和执行过程如下: ​ 注1:计算表达式2是循环的判定表达式。与前面一样,这个表达式可以是任意的,只要有值就行,遵循非0即真的原则。 注2:一个循…

(源码)Springboot项目集成Activiti工作流,前端Vue,Bpmn.js

前言 activiti工作流引擎项目,企业erp、oa、hr、crm等企事业办公系统轻松落地,一套完整并且实际运用在多套项目中的案例,满足日常业务流程审批需求。 一、项目形式 springbootvueactiviti集成了activiti在线编辑器,流行的前后端…

【Python】nn.nn.CircularPad1、2、3d函数和nn.ConstantPad1、2、3d函数详解和示例

前言 在深度学习中,尤其是在处理图像、音频或其他多维数据时,数据填充(Padding)是一个常见的操作。填充不仅可以保持数据的空间维度,还能在卷积操作中避免信息丢失。PyTorch提供了多种填充方式,其中nn.Cir…

unity 本地使用Json(全套)

提示:文章有错误的地方,还望诸位大神不吝指教! 文章目录 前言一、Json是什么?二、创建Json文件1.在线编辑并转实体类(C#)2.Json文件 三、解析Json并使用四、报错:JsonError:JsonExce…

使用frp内网穿透将个人主机上的MySQL发布到公网上,再通过python管理MySQL

目录 1.frp内网穿透部署 1.frp服务器 1.开放端口 2.上传软件包 3.解压 4.配置文件 2.frp客户端 1.上传软件包 2.配置文件 3.启动测试 1.浏览器查看服务器上连接的客户端数量 2.启动测试 2.MySQL安装 3.python3的安装使用 4.python管理MySQL 1.pip 2.pandas 3.p…