NVIDIA cuOpt:GPU加速优化AI微服务详解

发布于:2025-03-29 ⋅ 阅读:(29) ⋅ 点赞:(0)

NVIDIA cuOpt:GPU加速优化AI微服务详解

引言

在当今数据驱动的世界中,优化问题无处不在,从物流配送路线规划到资源分配,从生产调度到投资组合优化。然而,随着问题规模的增长,传统的CPU求解器往往面临计算效率低下、求解时间长、精度不足等挑战。NVIDIA推出的cuOpt正是为了解决这些挑战而设计的GPU加速优化AI微服务。

本文将详细介绍NVIDIA cuOpt的功能特性、应用场景、安装部署方法以及使用示例,帮助读者全面了解这一强大工具,并能够将其应用到实际业务中。

1. NVIDIA cuOpt概述

1.1 什么是NVIDIA cuOpt

NVIDIA® cuOpt™是一种基于GPU加速的优化AI微服务,专门用于解决以下三类问题:

  • 混合整数线性规划(Mixed Integer Linear Programming, MILP)
  • 线性规划(Linear Programming, LP)
  • 车辆路径问题(Vehicle Routing Problems, VRP)

cuOpt能够为包含数百万变量和约束条件的大规模问题提供近实时的解决方案,可以轻松集成到现有求解器中,并能在混合和多云环境中无缝部署。

注意:线性规划(LP)和混合整数线性规划(MILP)目前是早期访问功能,仅向特定客户开放。

1.2 cuOpt的核心优势

在这里插入图片描述

NVIDIA cuOpt通过利用GPU的并行计算能力,为运筹学和物流优化提供了以下核心优势:

  1. 性能提升:传统的CPU求解器往往因为精度不足和长时间等待而令人沮丧。cuOpt利用GPU的并行计算能力,能够实现数量级的加速,大幅降低求解时间。

  2. 高精度解决方案:cuOpt提供先进的并行启发式算法,能够在合理的时间内为非常大的问题产生接近最优的解决方案。

  3. 灵活性与可扩展性:支持多种问题变体和约束条件,如车辆容量约束、交付时间窗口以及车辆驾驶员的轮班和休息时间等。

  4. 易于部署与集成:作为NVIDIA AI Enterprise的一部分,cuOpt提供了一种安全、高效的方式来快速生成世界级的优化解决方案。使用单个优化容器,您可以在云端、数据中心、工作站或PC上的NVIDIA GPU系统上在5分钟内部署AI微服务。

1.3 应用场景

在这里插入图片描述

NVIDIA cuOpt广泛应用于以下场景:

1.3.1 车辆路径问题(VRP)和取送问题(PDP)

这些问题源自于旅行商问题(Traveling Salesperson Problem, TSP),这是运筹学和计算机科学中研究最广泛的问题之一。

TSP提出的问题是:

给定一系列目的地和每对目的地之间的距离矩阵,访问每个目的地恰好一次并返回原始位置的最短可能路线是什么?

TSP在规划和物流中有多种应用,良好的解决方案可以在货物运输和交付中节省大量的行程时间和燃料成本。

VRP是TSP的泛化,用于解决车队为给定的客户集合提供服务的最优路线集合。而PDP则增加了两种不同类型的服务可能性,即取货或送货,而在VRP中所有客户都需要在客户位置执行相同的服务。

1.3.2 线性规划(LP)

线性规划是一种在给定一组线性等式和不等式约束条件下优化线性目标函数的技术。例如:

给定系统约束:

2x + 4y >= 230
3x + 2y < 190
x >= 0
y >= 0

最大化目标函数:

f(x) = 5x + 3y
1.3.3 混合整数线性规划(MILP)

混合整数线性规划是线性规划的一个版本,其中一些变量被约束为整数,而其他变量可以是非整数。例如:

给定系统约束:

2x + 4y >= 230
3x + 2y < 190
x >= 0 且 x 是整数
y >= 0 且 y 是浮点数

最大化目标函数:

f(x) = 5x + 3y

虽然这个问题看起来与前面的类似,但它是一个完全不同的问题类型,求解难度也大大增加。

2. NVIDIA cuOpt的工作原理

2.1 GPU释放大规模并行计算能力

凭借利用数千个并行核心的能力,GPU是加速大规模可并行问题的理想计算平台,这类问题需要并行计算数千或数百万个独立任务。这使得在运行这类问题的启发式算法时能够实现数量级的加速,从而降低运营成本并提高解决方案的准确性。

2.2 cuOpt的解决方案方法

2.2.1 路由问题解决方案

cuOpt首先生成初始解决方案群体,然后迭代改进该群体直到达到时间限制,并从群体中选择最佳解决方案。

由于暴力枚举所需的时间和计算资源,获取精确的最优解决方案在实际中是不现实的。然而,有一些经过充分研究的启发式算法,可以在合理的时间内为非常大的问题产生接近最优的解决方案,NVIDIA cuOpt专注于使用这些启发式算法。

2.2.2 线性规划解决方案

cuOpt LP求解器基于PDLP,这是一种用于大规模求解LP的新型一阶方法(FOM)。它实现了梯度下降,通过启发式增强,利用最新的NVIDIA GPU高效执行大规模并行操作。

2.2.3 混合整数线性规划解决方案

NVIDIA cuOpt生成启发式解决方案,并用尽用户给定的所有时间限制。对于MILP问题,cuOpt能够在有限的时间内找到高质量的可行解,这对于实际应用中的决策至关重要。

3. NVIDIA cuOpt系统要求

3.1 自托管容器

  • 需要安装 nvidia-docker2
  • GPU
    • VRP:需要 Ampere (A100) 或 Hopper (H100) 架构
    • LP/MIP:需要 Hopper (H100 SXM) 架构
  • 支持多个GPU。cuOpt为每个cuOpt求解器进程使用一个GPU
  • CPU - x86-64 或 arm >= 8核(推荐)
  • 内存 >= 16 GB(推荐)
  • 最小存储空间:20 GB(8 GB容器大小)
  • CUDA - 12.6
  • 计算能力 >= 9.x
  • 最低NVIDIA驱动版本:525.60.04
  • CUDA安装指南:linux和windows

注意:在Windows中,容器只能通过WSL2运行。

3.2 基于Kubernetes的自托管容器

cuOpt可以使用Helm chart部署在任何配置了支持NVIDIA GPU的计算节点的Kubernetes集群上。计算节点的系统要求与上述相同。集群可以是单节点的"一体式"集群,也可以是多节点集群。

3.3 自托管Thin Client

  • 操作系统 - Ubuntu
  • CPU - x86
  • Python - 3.10.x

3.4 托管服务

  • NVIDIA将提供对cuOpt服务的访问,用户只需满足thin client的系统要求

3.5 托管服务Thin Client

  • 操作系统 - Ubuntu
  • CPU - x86
  • Python - 3.10.x

4. NVIDIA cuOpt安装部署指南

4.1 获取cuOpt访问权限

4.1.1 GA(仅路由功能):
  • 获取NVIDIA AI Enterprise (NVAIE)订阅,以获取cuOpt容器在云中托管
  • 获得访问权限后,用户可以在NGC目录中找到cuOpt容器
4.1.2 EA - LP/MIP早期访问:
  • 请联系cuopt@nvidia.com并分享您对使用Routing/LP/MILP的兴趣
  • 获得访问权限后,用户可以在NGC目录中找到EA cuOpt容器

4.2 访问NGC

  1. 使用邀请登录NGC并选择适当的NGC组织
  2. 从设置中生成NGC API密钥。如果您尚未生成API密钥,可以通过转到个人资料中的Setup选项并选择Get API Key来生成。存储此密钥或下次生成新密钥

4.3 拉取cuOpt容器

  1. 运行容器的先决条件:请访问自托管系统要求了解更多信息

  2. 转到cuOpt的容器部分,复制最新镜像的拉取标签

    • 在"Select a tag"下拉菜单中,找到要运行的容器镜像版本
    • 点击"Copy Image Path"按钮复制容器镜像路径
  3. 使用NGC API密钥登录nvcr.io容器注册表:

    docker login nvcr.io
    Username: $oauthtoken
    Password: <my-api-key>
    

    注意:用户名是$oauthtoken,密码是您的API密钥

  4. 拉取cuOpt容器:

    # 将镜像标签保存在变量中以供下面使用
    export CUOPT_IMAGE="CONTAINER_TAG_COPIED_FROM_NGC"
    
    docker pull $CUOPT_IMAGE
    

4.4 运行cuOpt

注意:以下命令以分离模式运行cuOpt容器。要停止分离的容器,请使用docker stop命令。要以交互模式运行容器,请删除-d选项。

  1. 如果您使用Docker 19.03或更高版本,启动容器的典型命令是:

    docker run -it -d --gpus=1 --rm -p 5000:5000 $CUOPT_IMAGE
    
  2. 如果您使用Docker 19.02或更早版本,启动容器的典型命令是:

    nvidia-docker run -it -d --gpus=1 --rm -p 5000:5000 $CUOPT_IMAGE
    
  3. 默认情况下,容器在端口5000上运行,但可以使用环境变量CUOPT_SERVER_PORT更改:

    docker run -it -d --gpus=1 -e CUOPT_SERVER_PORT=8080 --rm -p 8080:8080 $CUOPT_IMAGE
    
  4. 此命令将启动容器,cuOpt API端点将可用于测试

  5. 如果您有多个GPU并希望选择特定GPU,请使用--gpus device=<GPU_ID>。可以使用命令nvidia-smi找到GPU_ID

  6. 服务器可以使用环境变量配置日志级别和其他选项,选项如下:

    • CUOPT_SERVER_PORT:服务器端口(默认5000)
    • CUOPT_SERVER_IP:服务器IP(默认0.0.0.0)
    • CUOPT_SERVER_LOG_LEVEL:选项有criticalerrorwarninginfodebug(默认为info)
    • CUOPT_LP_TIME_LIMIT_SEC:仅为LP/MILP求解器运行时间设置最大时间限制;如果未设置,这也将作为默认时间限制。否则,默认为None,求解器运行直到达到最优解(对于LP)。对于MILP,时间限制是强制的
    • CUOPT_LP_ITERATION_LIMIT:仅为LP/MILP求解器设置最大迭代时间限制;如果未设置,这也将作为默认值。否则,默认为None,求解器运行直到达到解决方案
    • CUOPT_DATA_DIR:用于可选地将cuOpt问题数据文件传递到端点的共享挂载路径,而不是通过网络发送数据(默认为None)
    • CUOPT_RESULT_DIR:用于可选地将cuOpt结果文件从端点传递的共享挂载路径,而不是通过网络发送数据(默认为None)
    • CUOPT_MAX_RESULT:当设置了CUOPT_RESULT_DIR时,从端点通过http返回的结果的最大大小(千字节)。设置为0以将所有结果写入CUOPT_RESULT_DIR(默认为250)
    • CUOPT_GPU_COUNT:配置每个GPU运行一个cuOpt求解器进程(多GPU配置),以便cuOpt服务可以同时解决多个问题。问题将分配给可用的空闲求解器进程。默认为1。此值上限为可用GPU的数量

    示例:

    docker run -it -d --gpus '"device=0,1"' -e CUDA_VISIBLE_DEVICES="0,1" -e CUOPT_SERVER_PORT=8080 -e CUOPT_GPU_COUNT=2 --rm -p 8080:8080 $CUOPT_IMAGE
    

4.5 测试容器

可以使用curl命令测试容器是否正常运行:

curl -X POST "http://localhost:5000/cuopt/health" -H "accept: application/json"

如果容器正常运行,应该返回类似以下的响应:

{
  "status": "ok"
}

4.6 使用Pip索引安装Thin Client

注意:自托管thin client需要Python 3.10。

  1. thin client使用户能够快速测试,但用户可以设计自己的客户端
  2. 当需要更新或安装thin client时,可以直接使用NVIDIA pip索引安装
  3. 要求:请访问system requirements for thin client for self-hosted了解更多信息

安装命令:

pip install --upgrade --extra-index-url https://pypi.nvidia.com cuopt-sh-client

4.7 使用Helm Chart在Kubernetes上安装cuOpt

请参考system requirements for self-hosted container on kubernetes了解Kubernetes集群要求和Kubernetes资源的链接。

  1. 在Helm中创建命名空间:

    kubectl create namespace <some name>
    export NAMESPACE="<some name>"
    
  2. 测试集群是否使用以下示例访问GPU(如果kubectl不可用,请使用适合您的Kubernetes的类似命令来运行此测试):

    kubectl run nvidia-smi --rm -t -i --restart=Never --image=nvidia/cuda:12.0.0-base-ubuntu22.04 --limits=nvidia.com/gpu=1 -- nvidia-smi
    
  3. 从NGC获取Helm Chart:

    • 登录NGC并导航到cuOpt Helm Chart
    • 下载Helm Chart并解压缩
    • 更新values.yaml文件中的配置
  4. 安装Helm Chart:

    helm install cuopt-release ./cuopt-helm-chart -n $NAMESPACE
    
  5. 验证安装:

    kubectl get pods -n $NAMESPACE
    
  6. 设置端口转发以访问服务:

    kubectl port-forward service/cuopt-service 5000:5000 -n $NAMESPACE
    
  7. 测试服务:

    curl -X POST "http://localhost:5000/cuopt/health" -H "accept: application/json"
    

5. NVIDIA cuOpt使用示例

5.1 线性规划示例

考虑以下线性规划问题:

给定系统约束:

2x + 4y >= 230
3x + 2y <= 190
x >= 0
y >= 0

最大化目标函数:

f(x) = 5x + 3y

您需要找到满足约束条件并最大化目标函数的x和y的实数值。

以下是使用cuOpt解决此问题的Python代码示例:

import numpy as np
import cuopt_mps_parser
import solver_settings

from data_model import DataModel
from solver_settings import SolverSettings

problem_data = {}

dm = DataModel()  # 创建数据模型对象
ss = SolverSettings()  # 创建求解器设置对象

## 设置约束矩阵

# 如果约束条件是:
# 2x + 4y >= 230
# 3x + 2y <= 190

# 约束以CSR格式表示。约束可以转换为CSR矩阵,如下所示:
offsets = np.array([0, 2, 4], dtype=np.int32)  # 偏移量表示约束的长度
indices = np.array([0, 1, 0, 1], dtype=np.int32)  # 索引表示变量
coefficients = np.array([2.0, 4.0, 3.0, 2.0], dtype=np.float64)  # 系数值

dm.set_csr_constraint_matrix(coefficients, indices, offsets)

## 设置约束边界

# 如果约束条件如下:
# 2x + 4y >= 230
# 3x + 2y <= 190

# 您需要定义所有约束的`upper_bounds`和`lower_bounds`,每个值表示每个约束相对于其索引的上限或下限。
upper_bounds = np.array([np.inf, 190], dtype=np.float64)  # 上界,无穷大表示无上限
lower_bounds = np.array([230, -np.inf], dtype=np.float64)  # 下界,负无穷大表示无下限

dm.set_constraint_lower_bounds(lower_bounds)
dm.set_constraint_upper_bounds(upper_bounds)

# `inf` - 无穷大和 `-inf` - 负无穷大在没有明确的上限或下限时使用。

## 设置变量边界

# 变量:
# 定义变量边界,类似于约束边界。
var_upper_bounds = np.array([np.inf, np.inf], dtype=np.float64)  # 变量上界
var_lower_bounds = np.array([0, 0], dtype=np.float64)  # 变量下界

dm.set_variable_lower_bounds(var_lower_bounds)
dm.set_variable_upper_bounds(var_upper_bounds)

## 设置目标数据

# 目标:
# 传递目标数据的系数,并设置是需要最大化还是最小化。
objective_coefficients = np.array([5, 3], dtype=np.float64)  # 目标函数系数

dm.set_objective_coefficients(objective_coefficients)
dm.set_maximize(True)  # 设置为最大化问题

## 设置变量名称

# 这是可选的,但它有助于用户导航结果。
dm.set_variable_names(np.array(["x", "y"]))  # 设置变量名称为x和y

## 设置求解器配置

# 求解器配置可以针对优化和运行时间进行微调。
ss.set_time_limit(1)  # 设置时间限制为1秒
ss.set_optimality_tolerance(0.0001)  # 设置最优性容差

## 解决问题

# 对于托管服务,可以按照托管服务的thin client示例触发cuOpt端点。
# 对于自托管,可以按照自托管的thin client示例触发cuOpt端点。

# 使用此数据并调用cuOpt端点,它将返回`x`和`y`的值。

# 以下示例使用本地托管服务器:
data = cuopt_mps_parser.toDict(dm)  # 将数据模型转换为字典
data["solver_config"] = solver_settings.toDict(ss)  # 添加求解器配置
del data["variable_types"]  # 删除变量类型(对于LP不需要)

import json
from cuopt_sh_client import CuOptServiceSelfHostClient

# 如果cuOpt不在localhost:5000上运行,请编辑ip和port参数
cuopt_service_client = CuOptServiceSelfHostClient(
    ip="localhost",
    port=5000,
    timeout_exception=False
)

repoll_tries = 50  # 重新轮询尝试次数

def repoll(solution, repoll_tries):
    # 如果求解器仍在忙于求解,作业将被分配一个请求ID,并以以下格式发送回响应
    # {"reqId": <REQUEST-ID>}。需要使用此<REQUEST-ID>重新轮询求解器以获取响应。

    if "reqId" in solution and "response" not in solution:
        req_id = solution["reqId"]
        for i in range(repoll_tries):
            solution = cuopt_service_client.repoll(req_id)
            if "response" in solution:
                return solution
        return solution
    return solution

# 调用LP求解器
solution = cuopt_service_client.solve_lp(data)
solution = repoll(solution, repoll_tries)

# 打印结果
print(json.dumps(solution, indent=2))

5.2 车辆路径问题示例

以下是一个使用成本/时间矩阵解决车辆路径问题的示例:

import json

json_data = {}

# 假设成本是行程时间的一半

# 汽车
car_cost_matrix = [
    [0, 5, 4, 3, 5],  # 从位置0到其他位置的成本
    [5, 0, 6, 4, 3],  # 从位置1到其他位置的成本
    [4, 8, 0, 4, 2],  # 从位置2到其他位置的成本
    [1, 4, 3, 0, 4],  # 从位置3到其他位置的成本
    [3, 3, 5, 6, 0],  # 从位置4到其他位置的成本
]

car_time_matrix = [
    [ 0, 10,  8,  6, 10],  # 从位置0到其他位置的时间
    [10,  0, 12,  8,  6],  # 从位置1到其他位置的时间
    [ 8, 16,  0,  8,  4],  # 从位置2到其他位置的时间
    [ 2,  8,  6,  0,  8],  # 从位置3到其他位置的时间
    [ 6,  6, 10, 12,  0],  # 从位置4到其他位置的时间
]

# 自行车

# 假设是汽车成本的0.3倍
bike_cost_matrix = [[val * 0.3 for val in row] for row in car_cost_matrix]

# 假设是汽车时间的0.5倍
bike_time_matrix = [[val * 0.5 for val in row] for row in car_time_matrix]

# 1代表汽车类型,2代表自行车类型

# 添加成本矩阵
json_data["cost_matrix_data"] = {
    "data": {
        1: car_cost_matrix,  # 汽车的成本矩阵
        2: bike_cost_matrix  # 自行车的成本矩阵
    }
}

# 添加时间矩阵
json_data["travel_time_matrix_data"] = {
    "data": {
        1: car_time_matrix,  # 汽车的时间矩阵
        2: bike_time_matrix  # 自行车的时间矩阵
    }
}

# 设置车队数据
# 提供车辆起点和终点位置以及车辆特性和容量
fleet_data = {
    "vehicle_locations": [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]],  # 车辆的起点和终点位置
    "vehicle_ids": ["Car-A", "Car-B", "Car-C", "Bike-A", "Bike-B"],  # 车辆ID
    "vehicle_types": [1, 1, 1, 2, 2],  # 车辆类型:1=汽车,2=自行车
    "capacities": [[85, 85, 85, 65, 70]],  # 每辆车的容量
    "vehicle_time_windows": [  # 车辆的时间窗口
        [0, 100],
        [0, 100],
        [0, 100],
        [0, 100],
        [0, 100]
    ],
    # 车辆可以在此时间窗口内根据提供的休息时间休息
    "vehicle_break_time_windows": [
        [
            [20, 25],
            [20, 25],
            [20, 25],
            [20, 25],
            [20, 25]
        ]
    ],
    "vehicle_break_durations": [[1, 1, 1, 1, 1]],  # 休息时间长度
    # 车辆在交付过程中可能产生的最大成本
    "vehicle_max_costs": [100, 100, 100, 100, 100],
    # 车辆可以工作的最长时间
    "vehicle_max_times": [120, 120, 120, 120, 120]
}

json_data["fleet_data"] = fleet_data

# 设置任务数据
# 提供有关任务位置、需求和时间窗口的详细信息
task_data = {
    "task_locations": [1, 2, 3, 4],  # 任务位置
    "demand": [[30, 40, 40, 30]],  # 每个任务的需求量
    "task_time_windows": [  # 任务的时间窗口
        [3, 20],
        [5, 30],
        [1, 20],
        [4, 40],
    ],
    "service_times": [3, 1, 8, 4],  # 每个任务的服务时间
    "prizes": [50, 50, 100, 50]  # 完成每个任务的奖励
}

json_data["task_data"] = task_data

# 设置求解器配置
# 较大的问题可能需要更多时间
solver_config = {
    "objectives": {  # 目标权重
        "cost": 1,  # 成本权重
        "travel_time": 1,  # 行程时间权重
        "prize": 2  # 奖励权重
    }
}

json_data["solver_config"] = solver_config

# 解决问题
from cuopt_sh_client import CuOptServiceSelfHostClient

# 如果cuOpt不在localhost:5000上运行,请编辑ip和port参数
cuopt_service_client = CuOptServiceSelfHostClient(
    ip="localhost",
    port=5000,
    timeout_exception=False
)

# 调用VRP求解器
solution = cuopt_service_client.solve_vrp(json_data)

# 处理结果
print(json.dumps(solution, indent=2))
5.2.1 处理非统一休息时间

对于非统一休息时间,可以为每辆车单独指定休息时间窗口和休息时间长度:

fleet_data = {
    # ... 其他车队数据 ...
    
    # 为每辆车指定不同的休息时间窗口
    "vehicle_break_time_windows": [
        [
            [20, 25],  # Car-A的休息时间窗口
            [30, 35],  # Car-B的休息时间窗口
            [40, 45],  # Car-C的休息时间窗口
            [15, 20],  # Bike-A的休息时间窗口
            [25, 30]   # Bike-B的休息时间窗口
        ]
    ],
    # 为每辆车指定不同的休息时间长度
    "vehicle_break_durations": [
        [
            1,  # Car-A的休息时间长度
            2,  # Car-B的休息时间长度
            1.5,  # Car-C的休息时间长度
            0.5,  # Bike-A的休息时间长度
            1   # Bike-B的休息时间长度
        ]
    ],
    
    # ... 其他车队数据 ...
}

5.3 混合整数线性规划示例

混合整数线性规划是线性规划的一个版本,其中一些变量被约束为整数,而其他变量可以是非整数。

给定系统约束:

2x + 4y >= 230
3x + 2y < 190
x >= 0 且 x 是整数
y >= 0 且 y 是浮点数

最大化目标函数:

f(x) = 5x + 3y

以下是使用cuOpt解决此问题的Python代码示例:

import numpy as np
import cuopt_mps_parser
import solver_settings

from data_model import DataModel
from solver_settings import SolverSettings

problem_data = {}

dm = DataModel()  # 创建数据模型对象
ss = SolverSettings()  # 创建求解器设置对象

## 设置约束矩阵(与LP相同)
offsets = np.array([0, 2, 4], dtype=np.int32)
indices = np.array([0, 1, 0, 1], dtype=np.int32)
coefficients = np.array([2.0, 4.0, 3.0, 2.0], dtype=np.float64)

dm.set_csr_constraint_matrix(coefficients, indices, offsets)

## 设置约束边界(与LP相同)
upper_bounds = np.array([np.inf, 190], dtype=np.float64)
lower_bounds = np.array([230, -np.inf], dtype=np.float64)

dm.set_constraint_lower_bounds(lower_bounds)
dm.set_constraint_upper_bounds(upper_bounds)

## 设置变量边界(与LP相同)
var_upper_bounds = np.array([np.inf, np.inf], dtype=np.float64)
var_lower_bounds = np.array([0, 0], dtype=np.float64)

dm.set_variable_lower_bounds(var_lower_bounds)
dm.set_variable_upper_bounds(var_upper_bounds)

## 设置变量类型(MILP特有)
# 对于MILP,需要指定哪些变量是整数,哪些是连续的
# 0表示连续变量,1表示整数变量
variable_types = np.array([1, 0], dtype=np.int32)  # x是整数,y是连续的
dm.set_variable_types(variable_types)

## 设置目标数据(与LP相同)
objective_coefficients = np.array([5, 3], dtype=np.float64)

dm.set_objective_coefficients(objective_coefficients)
dm.set_maximize(True)

## 设置变量名称(可选,与LP相同)
dm.set_variable_names(np.array(["x", "y"]))

## 设置求解器配置
# MILP通常需要更多的时间来求解
ss.set_time_limit(10)  # 设置时间限制为10秒
ss.set_optimality_tolerance(0.0001)  # 设置最优性容差

## 解决问题
data = cuopt_mps_parser.toDict(dm)
data["solver_config"] = solver_settings.toDict(ss)

import json
from cuopt_sh_client import CuOptServiceSelfHostClient

# 如果cuOpt不在localhost:5000上运行,请编辑ip和port参数
cuopt_service_client = CuOptServiceSelfHostClient(
    ip="localhost",
    port=5000,
    timeout_exception=False
)

repoll_tries = 50

def repoll(solution, repoll_tries):
    if "reqId" in solution and "response" not in solution:
        req_id = solution["reqId"]
        for i in range(repoll_tries):
            solution = cuopt_service_client.repoll(req_id)
            if "response" in solution:
                return solution
        return solution
    return solution

# 调用MILP求解器
solution = cuopt_service_client.solve_milp(data)
solution = repoll(solution, repoll_tries)

# 打印结果
print(json.dumps(solution, indent=2))

6. 最佳实践与性能优化

6.1 多GPU配置

cuOpt支持多GPU配置,每个GPU运行一个cuOpt求解器进程,这样cuOpt服务可以同时解决多个问题。问题将分配给可用的空闲求解器进程。

要配置多GPU,可以使用以下环境变量:

  • CUDA_VISIBLE_DEVICES:指定哪些GPU可见
  • CUOPT_GPU_COUNT:指定要使用的GPU数量

示例:

docker run -it -d --gpus '"device=0,1"' -e CUDA_VISIBLE_DEVICES="0,1" -e CUOPT_GPU_COUNT=2 --rm -p 8080:8080 $CUOPT_IMAGE

6.2 时间限制与求解质量

对于大型问题,特别是MILP问题,求解时间和解的质量之间存在权衡。增加时间限制通常会提高解的质量,但也会增加等待时间。

对于LP问题,cuOpt会一直运行直到找到最优解或达到时间限制。对于MILP问题,时间限制是必须的,cuOpt会在给定的时间内尽可能找到最好的解。

6.3 数据传输优化

对于大型问题,数据传输可能成为瓶颈。cuOpt提供了以下环境变量来优化数据传输:

  • CUOPT_DATA_DIR:用于将问题数据文件传递到端点的共享挂载路径
  • CUOPT_RESULT_DIR:用于将结果文件从端点传递的共享挂载路径
  • CUOPT_MAX_RESULT:从端点通过http返回的结果的最大大小

7. 总结与展望

NVIDIA cuOpt作为一种GPU加速的优化AI微服务,为解决大规模优化问题提供了强大的工具。通过利用GPU的并行计算能力,cuOpt能够在合理的时间内为包含数百万变量和约束条件的问题提供高质量的解决方案。

cuOpt支持多种优化问题类型,包括车辆路径问题、线性规划和混合整数线性规划,并提供了灵活的部署选项,可以在云端、数据中心、工作站或PC上的NVIDIA GPU系统上快速部署。

随着优化问题在各行各业的广泛应用,NVIDIA cuOpt将继续发挥重要作用,帮助企业提高运营效率,降低成本,并做出更好的决策。

参考资料

  1. NVIDIA cuOpt官方文档:https://docs.nvidia.com/cuopt/user-guide/latest/introduction.html
  2. NVIDIA AI Enterprise:https://www.nvidia.com/en-us/data-center/products/ai-enterprise/
  3. NVIDIA Developer Program:https://developer.nvidia.com/
  4. NVIDIA NGC:https://ngc.nvidia.com/