开源技术 * IBM 微讲堂:Kubeflow 系列(观看回放 | 下载讲义) 了解详情

使用 Watson Machine Learning Accelerator 超参数优化来加快零售价格预测

本文已纳入学习路径:Watson Machine Learning Accelerator 入门系列。

主题 类型
Watson Machine Learning Accelerator 简介 文章
加速深度学习和机器学习 文章 + Notebook
Watson Machine Learning Accelerator 中的 Elastic Distributed Training 文章 + Notebook
使用 Watson Machine Learning Accelerator 超参数优化来加快零售价格预测 教程
推动更高的 GPU 利用率和吞吐量 教程
使用 Watson Machine Learning Accelerator 对图像进行分类 文章 + Notebook

随着越来越多的行业领导者了解到数据和机器学习模型可以给他们的业务带来价值,各行各业已经开始逐步采用人工智能 (AI)。AI 可以给许多经济部门带来优势,其中包括:通过过程自动化来降低运营成本、通过提升生产效率和用户体验来增加收入、改善合规性并增强了安全性。

特别是,在零售行业中使用 AI,可以在优化、自动化和规模化方面提供优势。现如今,零售商都是使用数据来了解客户并改进现有产品,从而让他们的产品能够在竞争中脱颖而出。他们还可以更好地了解购物行为数据,预测客户的需求和个人偏好,利用个性化的报价和体验作出响应,从而增强促销效果并提高销售量。

每个零售商都需要完成的一项主要工作是优化价格,例如,确定特定商品的合适报价。借助 AI,他们便可以根据多种因素对各种商品进行优化。使用 AI 模型,可以根据季节性数据以及库存水平、竞争产品和价格的实时输入来确定每件商品的最佳价格。AI 还可以向零售商展示不同定价策略可能产生的结果,以便他们可以确立最佳促销优惠,吸引更多客户,从而提高销售量。

要实现这些潜在优势的关键是要设计和部署适当的机器学习模型,以最高的准确性预测每件商品的最佳价格。众所周知,梯度提升机 (GBM) 现在是最强大的机器学习模型之一,它可以为涉及表格化数据集的大多数任务提供最高泛化准确性。因此,本教程将重点介绍 GBM 机器学习模型,因为它是价格优化任务的理想之选。我们将使用 Mercari 价格优化竞争方案(Kaggle 提供的一个公共数据集)。然后,我们将使用大家喜爱的 GBM 模型 XGBoost。要使用 XGBoost 实现理想的泛化准确性,我们需要执行超参数调整 (HPT),即尝试不同的超参数集并选择在验证数据集中提供最佳准确性的超参数集。为此,我们将使用 Watson Machine Learning Accelerator 套件,此套件既是资源编排器,也是任务调度程序,它可以在一系列节点和 GPU 之间无缝分配 HPT 任务。

在本教程中,了解 Watson Machine Learning Accelerator 的易用性以及在执行分布式机器学习作业时的高资源效率,并了解 HPT 流程的强大功能,此流程建立的 XGBoost 模型在预测未见过的数据时也能提供较高的泛化准确性。

准备要用于提交 HPO 作业的集群:创建 conda 环境(在所有节点上)

要准备 POWER 集群:

  • 创建一个 conda 环境,并以 egoadmin 用户身份安装预先构建的 XGBoost 库。

      conda create --name dli-xgboost python=3.6 py-xgboost-gpu
    

要准备 x86 集群:

  • 编译 XGBoost。

      git clone --recursive https://github.com/dmlc/xgboost
      cd xgboost/
      mkdir build
      cd build
      PATH=/opt/wmla-ig/anacondapowerai161-1/anaconda/pkgs/cudatoolkit-dev-10.1.168-513.g7069ee4/bin:$PATH
      cmake3 ..-DUSE_CUDA=ON
      make -j
    
  • 创建一个 conda 环境,并在其中安装 XGBoost。

      conda create --name dli-xgboost --yes pip python=3.7
      conda activate dli-xgboost
      conda install numpy scikit-learn scipy
      python setup.py install
      source deactivate
    

创建 XGBoost BYOF 插件(仅在管理节点上)

要创建插件:

export DLI_EGO_TOP=/opt/ibm/spectrumcomputing
  1. 检查 DL_NFS_PATH 的值。

     $ cat $DLI_EGO_TOP/dli/conf/dlpd/dlpd.conf | grep DL_NFS_PATH
         "DL_NFS_PATH": "/dlishared",
    
     export DL_NFS_PATH=/dlishared
    
  2. 创建一个名为 Xgboost.conf 的文件并在其中包含以下内容。

     {
    
         "desc" :
         [{" ": "XGboost.Currently in development phase."},
          {" ": "Examples:"},
          {" ": "$ python dlicmd.py --exec-start XGboost <connection-options> --ig <ig> --model-main XGboost_Main.py"}
         ],
    
         "deployMode Desc": "Optional",
         "deployMode": "cluster",
    
         "appName Desc" : "This is required",
         "appName": "dlicmdXGboost",
    
         "numWorkers Desc": "Optional number of workers",
         "numWorkers": 1,
    
         "maxWorkers Desc": "User can't specify more than this number",
         "maxWorkers": 1,
    
         "maxGPUPerWorker Desc": "User can't specify more than this number",
         "maxGPUPerWorker": 10,
    
         "egoSlotRequiredTimeout Desc": "Optional",
         "egoSlotRequiredTimeout": 120,
    
         "workerMemory Desc" : "Optional",
         "workerMemory": "2G",
    
         "frameworkCmdGenerator Desc": "",
         "frameworkCmdGenerator": "XGboostCmdGen.py"
     }
    
  3. 将该文件移至以下目录。

     sudo mv XGboost.conf $DLI_EGO_TOP/dli/conf/dlpd/dl_plugins
    
  4. 创建一个名为 Xgboost_wrapper.sh` 的文件并在其中包含以下内容。

     #!/bin/sh
     source activate dli-xgboost
     python $@
    
  5. 创建一个名为 XGboostCmdGen.py 的文件并在其中包含以下内容。

     #!/usr/bin/env python2
     import os.path, sys
     from os import environ
    
     """
     """
    
     def main():
    
        cmd = ""
    
        if "DLI_SHARED_FS" in os.environ:
           print (environ.get('DLI_SHARED_FS'))
           cmd = environ.get('DLI_SHARED_FS') + "/tools/spark_tf_launcher/launcher.py"
        else:
           print("Error: environment variable DLI_SHARED_FS must be defined")
           sys.exit()
    
        if "APP_NAME" in os.environ:
           cmd = cmd + " --sparkAppName=" + environ.get('APP_NAME')
        else:
           print("Error: environment variable APP_NAME must be defined")
           sys.exit()
    
        if "MODEL" in os.environ:
           cmd = cmd + " --model=" + environ.get('MODEL')
        else:
           print("Error: environment variable MODEL must be defined")
           sys.exit()
    
        if "REDIS_HOST" in os.environ:
           cmd = cmd + " --redis_host=" + environ.get('REDIS_HOST')
        else:
           print("Error: environment variable REDIS_HOST must be defined")
           sys.exit()
    
        if "REDIS_PORT" in os.environ:
           cmd = cmd + " --redis_port=" + environ.get('REDIS_PORT')
        else:
           print("Error: environment variable REDIS_PORT must be defined")
           sys.exit()
    
        if "GPU_PER_WORKER" in os.environ:
           cmd = cmd + " --devices=" + environ.get('GPU_PER_WORKER')
        else:
           print("Error: environment variable GPU_PER_WORKER must be defined")
           sys.exit()
    
        cmd = cmd + " --work_dir=" + os.path.dirname(environ.get('MODEL'))
        cmd = cmd + " --app_type=executable"
    
        cmd = cmd + " --model=" + environ.get('DLI_SHARED_FS') + "/tools/dl_plugins/XGboost_wrapper.sh --"
        cmd = cmd + " " + environ.get('MODEL')
    
        # adding user args
        for i in range(1, len(sys.argv)):
           cmd += " " + sys.argv[i]
    
        # Expected result in json
        print('{"CMD" : "%s"}' % cmd)
    
     if __name__ == "__main__":
        sys.exit(main())
    
  6. 移动这些文件并使其成为可执行文件。

     sudo mv XGboost_wrapper.sh $DL_NFS_PATH/tools/dl_plugins
     sudo mv XGboostCmdGen.py $DL_NFS_PATH/tools/dl_plugins
     sudo chmod +x $DL_NFS_PATH/tools/dl_plugins/XGboost_wrapper.sh
     sudo chmod +x $DL_NFS_PATH/tools/dl_plugins/XGboostCmdGen.py
    

下载并准备数据集

mkdir $DL_NFS_PATH/data sets/higgs
cd $DL_NFS_PATH/datasets/higgs
wget --no-check-certificate https://archive.ics.uci.edu/ml/machine-learning-databases/00280/HIGGS.csv.gz
gunzip HIGGS.csv.gz
mkdir train
mkdir val
mkdir test
  1. 在当前文件夹中创建以下 Python 脚本 (preprocess.py)。

     import pandas as pd
     from sklearn.model_selection import train_test_split
     import xgboost as xgb
    
     df = pd.read_csv("HIGGS.csv", header=None)
    
     data = df.values
    
     y = data[:,0]
     X = data[:,1:]
    
     X_tmp, X_test, y_tmp, y_test = train_test_split(X,y, random_state=42)
     X_train, X_val, y_train, y_val = train_test_split(X_tmp,y_tmp, random_state=42)
    
     print("Number of features: %d" % (X_train.shape[1]))
     print("Number of training  examples: %d" % (X_train.shape[0]))
     print("Number of validation examples: %d" % (X_val.shape[0]))
     print("Number of test       examples: %d" % (X_test.shape[0]))
    
     dx_train = xgb.DMatrix(X_train, y_train)
     dx_val   = xgb.DMatrix(X_val, y_val)
     dx_test  = xgb.DMatrix(X_test, y_test)
    
     dx_train.save_binary("train/HIGGS_train.dmatrix")
     dx_val.save_binary("val/HIGGS_val.dmatrix")
     dx_test.save_binary("test/HIGGS_test.dmatrix")
    
  2. 执行预处理脚本以生成数据文件。

     conda activate dli-xgboost
     conda install pandas
     python preprocess.py
    

    您会看到以下输出:

     Number of features: 28
     Number of training   examples: 6187500
     Number of validation examples: 2062500
     Number of test       examples: 2750000
    
  3. 检查 DLI_DATA_FS 的值。

     $ cat $DLI_EGO_TOP/dli/conf/dlpd/dlpd.conf | grep DLI_DATA_FS
         "DLI_DATA_FS": "/dlidata/",
    
  4. 将训练和验证数据集复制到 'DLI_DATA_FS'

     $ pwd
     /dlidata/dataset/price_prediction
     $ ll -lt
     -rw-rw-r-- 1 egoadmin egoadmin 210025976 Nov  7 14:38 pp_val.dmatrix
     -rw-rw-r-- 1 egoadmin egoadmin 630075452 Nov  7 14:38 pp_train.dmatrix
    

使用默认参数运行 XGBoost

  1. 创建 train_xgb_default.py 文件。

     from sklearn.metrics import roc_auc_score
     import xgboost as xgb
     import argparse
    
     CLI=argparse.ArgumentParser()
     CLI.add_argument("--trainFile", type=str, default="")
     CLI.add_argument("--valFile", type=str, default="")
     CLI.add_argument("--testFile", type=str, default="")
     args = CLI.parse_args()
    
     # Set params
     params = {
       'tree_method': 'gpu_hist',
       'max_bin': 64,
       'objective': 'binary:logistic',
     }
    
     # Load data
     dtrain = xgb.DMatrix(args.trainFile)
     ddev = xgb.DMatrix(args.valFile)
     dtest = xgb.DMatrix(args.testFile)
    
     # Get labels
     y_train = dtrain.get_label()
     y_dev = ddev.get_label()
     y_test = dtest.get_label()
    
     # Train
     gbm = xgb.train(params, dtrain)
    
     # Inference
     p1_train = gbm.predict(dtrain)
     p1_dev  = gbm.predict(ddev)
     p1_test  = gbm.predict(dtest)
    
     # Evaluate
     auc_train = roc_auc_score(y_train, p1_train)
     auc_dev = roc_auc_score(y_dev, p1_dev)
     auc_test = roc_auc_score(y_test, p1_test)
    
     print("auc_test: %f, auc_val: %f, auc_test: %f" % (auc_train, auc_dev, auc_test))
    
  2. 使用默认参数运行模型(与以前一样使用 dli-xgboost 环境)。

(dli-xgboost)# python train_xgb_default.py --trainFile  /dlidata/dataset/price_prediction/pp_train.dmatrix --testFile /dlidata/dataset/price_prediction/pp_val.dmatrix
[04:16:57] 833433x93 matrix with 77509269 entries loaded from /dlidata/dataset/price_prediction/pp_train.dmatrix
[04:16:57] 277812x93 matrix with 25836516 entries loaded from /dlidata/dataset/price_prediction/pp_val.dmatrix
mse_test: 1231.55

使用 Watson Machine Learning Accelerator 超参数优化来调优 XGboost

我们来看看使用 Watson Machine Learning Accelerator 超参数优化是否可以做得更好。

  1. 通过运行 runbook 的步骤 1 – 4 来安装和配置 Watson Machine Learning Accelerator。

  2. 下载 05_tuning_xgboost_with_hpo.ipynb notebook,然后使用您的首选工具来打开该 notebook。

  3. 利用该文件夹下载模型:xgb-model/main.py

  4. 更新 notebook 的第一个单元:

     - hostname
     - username, password
     - protocol (http or https)
     - http or https port
     - sigName
     - Dataset location
    
  5. 更新 notebook 的第二个单元:

     - maxJobNum:  total number of tuning jobs to be running
     - maxParalleJobNum:  total number of tuning jobs running in parallel, which is equivalent to total number of GPUs available in the cluster
    

在此 notebook 中,我们将调优 XGBoost 模型的五个参数。运行 notebook 以启动并行模型调优作业。

  1. 运行第四个单元以监视作业进度。将返回建议的最佳参数集(具有最佳指标)。

     ....
     Hpo task Admin-hpo-83966261958354 state RUNNING progress 56%
     Hpo task Admin-hpo-83966261958354 completes with state FINISHED
     {
         "best": {
             "appId": "Admin-84189701779622-1370733872",
             "driverId": "driver-20191108160851-0342-bacfbcb3-ed76-4f70-92f5-65062f92d1cb",
             "endTime": "2019-11-08 16:11:07",
             "hyperParams": [
                 {
                     "dataType": "double",
                     "fixedVal": "0.9597590292372464",
                     "name": "learning_rate",
                     "userDefined": false
                 },
                 {
                     "dataType": "int",
                     "fixedVal": "565",
                     "name": "num_rounds",
                     "userDefined": false
                 },
                 {
                     "dataType": "int",
                     "fixedVal": "13",
                     "name": "max_depth",
                     "userDefined": false
                 },
                 {
                     "dataType": "double",
                     "fixedVal": "1584.7191653582931",
                     "name": "lambda",
                     "userDefined": false
                 },
                 {
                     "dataType": "double",
                     "fixedVal": "0.47",
                     "name": "colsample_bytree",
                     "userDefined": false
                 }
             ],
             "id": 6,
             "maxiteration": 0,
             "metricVal": 1036.0960693359375,
             "startTime": "2019-11-08 16:08:51",
             "state": "FINISHED"
         },
         ......
    
  2. 使用从调优作业返回的参数来创建 train_xgb_tuned.py 文件。

     import xgboost as xgb
     import argparse
     import numpy as np
     from sklearn.metrics import mean_squared_error
    
     CLI=argparse.ArgumentParser()
     CLI.add_argument("--trainFile", type=str, default="")
     CLI.add_argument("--testFile", type=str, default="")
     args = CLI.parse_args()
    
     # Set params as found by WML-A HPO
     params = {
       'tree_method': 'gpu_hist',
       'learning_rate': 0.9597590292372464,
       'num_rounds': 565,
       'max_depth': 13,
       'lambda': 1584.7191653582931,
       'colsample_bytree': 0.47
     }
    
     # Load training and test data
     dtrain = xgb.DMatrix(args.trainFile)
     dtest = xgb.DMatrix(args.testFile)
    
     # Training
     gbm = xgb.train(params, dtrain, params['num_rounds'])
    
     # Evaluate
     true_price = np.expm1(dtest.get_label())
     pred_price = np.expm1(gbm.predict(dtest))
     mse_test = mean_squared_error(true_price, pred_price)
    
     # Output
     print("mse_test: %.2f" % (mse_test))
    

使用已调优的参数运行 XGBoost

通过以下代码,使用已调优的参数运行 XGBoost。

(dli-xgboost)# python train_xgb_tuned.py --trainFile  /dlidata/dataset/price_prediction/pp_train.dmatrix --testFile /dlidata/dataset/price_prediction/pp_val.dmatrix
[04:23:26] 833433x93 matrix with 77509269 entries loaded from /dlidata/dataset/price_prediction/pp_train.dmatrix
[4:23:26] 277812x93 matrix with 25836516 entries loaded from /dlidata/dataset/price_prediction/pp_val.dmatrix
mse_test: 1036.10

结束语

均方误差 (MSE) 用于衡量误差平方的平均值,即估计值与实际值之间的平均平方差。MSE 值越小,近似误差就越小,泛化准确性就越好。在我们的实验中,使用 Watson Machine Learning Accelerator 超参数优化调整参数的 MSE 值是 1036.10,与 MSE 值 1231.55 的默认参数相比,它提供了较高的泛化准确性。

在本教程中,我们展示了 Watson Machine Learning Accelerator 的易用性以及在自动执行并行超参数调整作业时的效率,并说明了它如何以更精确的 XGBoost 模型泛化精度提供更准确的零售价格预测。

本文翻译自:Expedite retail price prediction with Watson Machine Learning Accelerator hyperparameter optimization(2020-10-21)