NVIDIA cuMLcuml.solvers模块深度解析:CD、QN、SGD 三大 GPU 线性求解器实战指南
【免费下载链接】cumlNVIDIA cuML: GPU-Accelerated Machine Learning项目地址: https://gitcode.com/GitHub_Trending/cu/cuml
本篇指南聚焦 NVIDIA cuML(GPU-Accelerated Machine Learning)中位于python/cuml/cuml/solvers/的优化求解器模块,以官方 API 文档 docs/source/api/cuml.solvers.rst 为骨架,系统讲解其暴露的三个核心类——坐标下降(CD)、拟牛顿法(QN)与随机梯度下降(SGD)的算法原理、全部超参数语义、输入输出约束与底层 C++ 调用链。读完本文,你将能根据数据规模、稀疏性与正则化需求,为回归、逻辑回归与 SVM 类任务正确选择并调优 cuML 求解器,并理解它们如何被LogisticRegression、ElasticNet、MBSGD等高层估计器复用。
模块概览:cuml.solvers暴露了什么
cuml.solvers是一个 Cython 封装的底层优化求解器集合,其 Python 导出非常简单直接,见 python/cuml/cuml/solvers/init.py:
from cuml.solvers.cd import CD from cuml.solvers.qn import QN from cuml.solvers.sgd import SGDAPI 文档 cuml.solvers.rst 通过automodule与autosummary指令,将CD、QN、SGD三个类的完整 docstring(参数、属性、示例)自动渲染成 API 参考页。除此之外,这三个类还会在顶层命名空间再次导出,见 python/cuml/cuml/init.py,因此你也可以直接from cuml import CD, QN, SGD。
模块目录结构如下:
| 文件 | 内容 |
|---|---|
| cd.pyx | CD类与fit_cd底层函数,坐标下降求解线性/岭/套索/弹性网 |
| qn.pyx | QN类与fit_qn底层函数,L-BFGS / OWL-QN |
| sgd.pyx | SGD类与fit_sgd底层函数,随机梯度下降 |
| cd_mg.pyx | 多 GPU(MNMG)版坐标下降 |
| qn.pxd | qn_params等 C 结构体的 Cython 声明 |
三个求解器共享同一套输入约定:接受 numpy 数组、cuPy 数组或 cuDF DataFrame/Series 作为输入(QN 额外支持 CSR 稀疏输入),内部统一转换为列主序(Fortran order)的 float32/float64 稠密矩阵后交给 C++ 层计算。
CD:沿坐标方向迭代的弹性网求解器
算法定位与目标函数
坐标下降(Coordinate Descent)是最常见的优化算法之一:每轮迭代固定其它坐标,只沿单个坐标方向做精确极小化,从而逐步逼近全局最小值。cuML 的CD目前只支持线性回归,并可作为岭回归(Ridge)、套索(Lasso)与弹性网(ElasticNet)惩罚的求解器,其最小化目标在 cpp/include/cuml/solvers/solver.hpp 的注释中给出:
f(coef) = 1/2 * || labels - input * coef ||^2 + 1/2 * alpha * (1 - l1_ratio) * ||coef||^2 + alpha * l1_ratio * ||coef||_1即平方损失 + L2 项(Ridge)+ L1 项(Lasso)的加权组合,l1_ratio正是弹性网的混合系数。
完整参数说明
CD类的全部超参数定义于 cd.pyx 的 docstring 与__init__中,默认值与语义如下:
| 参数 | 默认值 | 说明 |
|---|---|---|
loss | 'squared_loss' | 目前唯一支持的损失函数,预测阶段使用线性回归 |
alpha | 0.0001 | 正则化强度常数;alpha=0等价于普通最小二乘(可由LinearRegression求解) |
l1_ratio | 0.15 | 弹性网混合参数,取值范围0 <= l1_ratio <= 1;0时为纯 L2 惩罚,1时为纯 L1 惩罚,中间值为 L1/L2 组合 |
fit_intercept | True | 为True时模型自动修正 y 的全局均值;为False时要求数据已中心化 |
max_iter | 1000 | 训练时遍历整个数据集的轮数上限 |
tol | 1e-3 | 优化容差:当参数更新量小于tol时求解器停止 |
shuffle | True | 为True时每轮随机挑选一个系数更新,而非按特征顺序循环;文档指出在tol高于1e-4时通常能显著加速收敛 |
verbose | False | 日志级别,需取cuml.common.logger.level_*常量 |
output_type | None | 结果输出类型('input'/'cupy'/'numpy'/'cudf'/'pandas'),None时使用模块级cuml.global_settings.output_type |
拟合完成后可通过coef_、intercept_、n_iter_属性获取系数、截距与实际迭代轮数。
实战示例
以下示例取自CD的官方 docstring(cd.pyx),演示用 cuDF + cuPy 数据训练一个无正则化的坐标下降模型:
import cupy as cp import cudf from cuml.solvers import CD cd = CD(alpha=0.0) X = cudf.DataFrame() X['col1'] = cp.array([1, 1, 2, 2], dtype=cp.float32) X['col2'] = cp.array([1, 2, 2, 3], dtype=cp.float32) y = cudf.Series(cp.array([6.0, 8.0, 9.0, 11.0], dtype=cp.float32)) cd.fit(X, y) print(cd.coef_) # 0 1.001... 1 1.998... dtype: float32 print(cd.intercept_) # 3.00... X_new = cudf.DataFrame() X_new['col1'] = cp.array([3, 2], dtype=cp.float32) X_new['col2'] = cp.array([5, 5], dtype=cp.float32) preds = cd.predict(X_new) # 15.997... 14.995...底层调用链
CD.fit内部调用fit_cd(cd.pyx),该函数完成参数校验(loss必须是'squared_loss',alpha >= 0)、输入检查(dtype 限float32/float64、列主序、至少 2 个样本)后,在nogil块中调用 C++ 入口cdFit,其 float/double 两个重载声明于 solver.hpp,实现位于 cpp/src/solver/solver.cu。C++ 层cdFit会校验loss == 0(即SQRD_LOSS),并转发到模板实现cd.cuh中的cdFit<float/double>,预测阶段则走cdPredict(同样在nogil中执行并handle.sync()同步流)。
值得注意的是,高层估计器 python/cuml/cuml/linear_model/elastic_net.py 直接复用了fit_cd与fit_qn:其solver参数取'auto'时,稠密输入走'cd',稀疏输入走'qn'——这正是CD作为"求解器组件"被组合进完整估计器的典型模式。
QN:L-BFGS 与 OWL-QN 的统一入口
算法定位
拟牛顿法(Quasi-Newton)通过迭代逼近目标函数的极值点来优化代价函数。cuML 的QN类依据是否启用 L1 正则化,在两种经典算法间自动切换(见 qn.pyx 的 docstring):
- 存在 L1 正则化(
l1_strength > 0)时,运行Orthant-Wise Limited Memory Quasi-Newton(OWL-QN); - 否则运行Limited Memory BFGS(L-BFGS)。
两种方法分别引用 Nocedal & Wright《Numerical Optimization》(1999) 与 Andrew, Gao (ICML 2007) 的公开文献实现。OWL-QN 专门用于处理 L1 惩罚产生的非光滑最优点。
损失函数支持矩阵
QN支持的损失在 qn.pyx 中以SUPPORTED_LOSSES字典映射到 C 层枚举(见 cpp/include/cuml/linear_model/qn.h):
| Python 字符串 | 对应损失 | 适用任务 | 目标取值 |
|---|---|---|---|
'sigmoid'(默认) | QN_LOSS_LOGISTIC | 二分类逻辑回归 | {0, 1} |
'logistic' | QN_LOSS_LOGISTIC | 二分类逻辑回归(别名) | {0, 1} |
'softmax' | QN_LOSS_SOFTMAX | 多分类逻辑回归 | {0, 1, ...} |
'l2'/'normal' | QN_LOSS_SQUARED | L2 回归 | 实数 |
'l1' | QN_LOSS_ABS | L1 回归 | 实数 |
'svc_l1' | QN_LOSS_SVC_L1 | 线性 SVM(hinge) | {0, 1} |
'svc_l2' | QN_LOSS_SVC_L2 | 线性 SVM(squared-hinge) | {0, 1} |
'svr_l1' | QN_LOSS_SVR_L1 | 支持向量回归(epsilon-insensitive) | 实数 |
'svr_l2' | QN_LOSS_SVR_L2 | 支持向量回归(epsilon-insensitive 平方) | 实数 |
init_qn_params(qn.pyx)中还有两条自动推导规则值得注意:loss='logistic'在多分类场景(n_classes > 2)会被自动提升为'softmax';loss='softmax'要求类别数大于 2,而'logistic'/'svc_l1'/'svc_l2'要求恰好二分类,否则抛出ValueError。
完整参数说明
QN的超参数比CD更丰富,默认值见 qn.pyx:
| 参数 | 默认值 | 说明 |
|---|---|---|
loss | 'sigmoid' | 见上方损失表 |
fit_intercept | True | 是否拟合截距;False要求数据已中心化 |
l1_strength | 0.0 | L1 正则化强度;非零时启用 OWL-QN,否则 L-BFGS。是否除以样本数由penalty_normalized控制 |
l2_strength | 0.0 | L2 正则化强度,同样受penalty_normalized控制 |
max_iter | 1000 | 求解器最大迭代轮数 |
tol | 1e-4 | 梯度停止准则:norm(current_loss_grad) <= tol * max(current_loss, tol)时停止 |
delta | None | 损失变化停止准则:abs(current_loss - previous_loss) <= delta * max(current_loss, tol);None时取tol * 0.01,0时禁用该检查 |
linesearch_max_iter | 50 | 每轮外层迭代中线搜索(内循环)的最大次数 |
lbfgs_memory | 5 | L-BFGS 逆 Hessian 近似的秩,内存开销为O(lbfgs_memory * D) |
warm_start | False | 为True时复用上一次fit的解作为初始化 |
penalty_normalized | True | 为True时 L1/L2 参数除以样本数,用于对齐 sklearn 等其它实现的行为 |
verbose | False | 日志级别 |
output_type | None | 输出类型 |
拟合后的属性包括coef_(形状(n_classes, n_features))、intercept_、n_classes_、n_iter_与objective(最终目标函数值)。
与 sklearn / scipy 的兼容性注意
docstring(qn.pyx)明确指出了三处跨框架差异,是迁移调参时的关键知识:
tol的停止条件(norm(grad) <= tol * max(loss, tol))与 scipyL-BFGS-B的gtol条件(norm(projected_grad) <= gtol)略有不同;- sklearn 的
LogisticRegression使用损失之和,而 cuML 使用损失平均值,因此 sklearn 的损失通常约为 cuML 的sample_size倍——若希望 cuML 求解器不比 sklearn 提前停止,可把tol除以样本数; delta对应 scipy 的ftol(scipy 默认极小值2.2e-9且 sklearn 未暴露),该条件用于防止求解器在线搜索步长过小时"原地踏步/锯齿震荡";设delta = 0可确保 cuML 不比 sklearn 提前停止。
实战示例与底层调用链
官方示例(qn.pyx)演示二分类逻辑回归:
import cupy as cp from cuml.solvers import QN X = cp.array([[1, 1], [1, 2], [2, 2], [2, 3]]) y = cp.array([0, 0, 1, 1]) solver = QN(loss="sigmoid").fit(X, y) solver.predict(X) # array([0, 0, 1, 1], dtype=int32)调用链上,QN.fit→fit_qn(qn.pyx)→init_qn_params填充qn_params结构体(字段定义见 cpp/include/cuml/linear_model/qn.h,含loss、penalty_l1、penalty_l2、grad_tol、change_tol、max_iter、linesearch_max_iter、lbfgs_memory、verbose、fit_intercept、penalty_normalized)→ 依据输入是否为 CSR 稀疏矩阵,分别调用 C++ 模板qnFit(稠密)或qnFitSparse(稀疏,见 cpp/include/cuml/linear_model/glm.hpp)。此外warm_start=True时会把上一次的coef_与intercept_拼接后作为初始点传入。高层估计器 python/cuml/cuml/linear_model/logistic_regression.py 正是通过fit_qn实现 GPU 逻辑回归。
SGD:面向大规模数据的随机梯度下降
算法定位
随机梯度下降通过沿梯度方向迭代步进优化代价函数,非常适合精确解难以求得的大规模问题。cuML 的SGD当前可用于线性回归、岭回归与 SVM 模型(见 sgd.pyx)。与QN不同,SGD目前仅支持二分类——fit_sgd在检测到超过 2 个类别时会抛出ValueError(sgd.pyx)。
完整参数说明
SGD的超参数定义于 sgd.pyx,比CD多出批量、学习率策略等维度:
| 参数 | 默认值 | 说明 |
|---|---|---|
loss | 'squared_loss' | 'hinge'(线性 SVM)、'log'(逻辑回归)、'squared_loss'(线性回归) |
penalty | None | 正则化项:'l1'(Lasso)、'l2'(Ridge)、'elasticnet'(L1/L2 加权平均)、None(无惩罚,默认) |
alpha | 0.0001 | 正则化强度常数(校验要求alpha > 0) |
l1_ratio | 0.15 | 弹性网混合系数(penalty='elasticnet'时生效) |
fit_intercept | True | 是否拟合截距 |
epochs | 1000 | 遍历整个数据集的轮数 |
tol | 1e-3 | 停止准则:current_loss > previous_loss - tol时停止 |
shuffle | True | 每个 epoch 后是否打乱训练数据 |
eta0 | 0.001 | 初始学习率(校验要求eta0 > 0) |
power_t | 0.5 | invscaling学习率策略的指数 |
batch_size | 32 | 每个 batch 的样本数 |
learning_rate | 'constant' | 'constant'保持学习率不变;'adaptive'在训练损失/验证精度连续n_iter_no_change轮无改善时将学习率除以 5;'invscaling'使用指数衰减 |
n_iter_no_change | 5 | 触发adaptive学习率调整的"无改善轮数" |
verbose/output_type | False/None | 同前两者 |
枚举映射与底层调用链
sfit_sgd通过三个 Python 字典把字符串参数映射为整数代码(sgd.pyx):_LEARNING_RATES(constant→1、invscaling→2、adaptive→3)、_LOSSES(squared_loss→0、log→1、hinge→2)、_PENALTIES(None→0、l1→1、l2→2、elasticnet→3)。这些整数在 C++ 层 cpp/src/solver/solver.cu 中又被翻译回ML::lr_type、ML::loss_funct、ML::penalty枚举(定义于 cpp/include/cuml/solvers/params.hpp,其中lr_type还包含一个未被 Python 层暴露的OPTIMAL选项),随后调用模板实现sgdFit,并传入handle.get_stream().get()的 CUDA 流以支持异步执行。预测阶段对应sgdPredict与sgdPredictBinaryClass(声明见 solver.hpp)。
实战示例
官方示例(sgd.pyx)演示用小批量 SGD 拟合线性回归:
import numpy as np import cudf from cuml.solvers import SGD as cumlSGD X = cudf.DataFrame() X['col1'] = np.array([1, 1, 2, 2], dtype=np.float32) X['col2'] = np.array([1, 2, 2, 3], dtype=np.float32) y = cudf.Series(np.array([1, 1, 2, 2], dtype=np.float32)) pred_data = cudf.DataFrame() pred_data['col1'] = np.asarray([3, 2], dtype=np.float32) pred_data['col2'] = np.asarray([5, 5], dtype=np.float32) cu_sgd = cumlSGD(learning_rate='constant', eta0=0.005, epochs=2000, fit_intercept=True, batch_size=2, tol=0.0, penalty=None, loss='squared_loss') cu_sgd.fit(X, y) cu_pred = cu_sgd.predict(pred_data).to_numpy() print(" cuML intercept : ", cu_sgd.intercept_) # 0.00418... print(" cuML coef : ", cu_sgd.coef_) # 0 0.9841... 1 0.0097... print("cuML predictions : ", cu_pred) # [3.0055... 2.0214...]高层估计器 mbsgd_classifier.py 与 mbsgd_regressor.py 均直接复用fit_sgd,因此SGD实质上就是 cuML Mini-Batch SGD 分类器/回归器的内核。
三大求解器选型对比
| 维度 | CD | QN | SGD |
|---|---|---|---|
| 迭代策略 | 沿单坐标方向精确极小化 | 拟牛顿(近似二阶) | 随机梯度步进(一阶) |
| 底层算法 | 坐标下降 | L-BFGS / OWL-QN | Mini-batch SGD |
| 支持损失 | 仅squared_loss | 9 种(分类/回归/SVM/SVR) | 3 种(回归/逻辑/ hinge) |
| 支持惩罚 | 通过alpha+l1_ratio表达 L1/L2/弹性网 | 独立l1_strength/l2_strength | penalty+alpha+l1_ratio |
| 稀疏输入 | 不支持(列主序稠密) | 支持 CSR | 不支持 |
| 分类能力 | 无(纯回归) | 二分类 + 多分类(softmax) | 仅二分类 |
| 典型上层封装 | ElasticNet(稠密路径) | LogisticRegression、ElasticNet(稀疏路径) | MBSGDClassifier、MBSGDRegressor |
| 收敛停止准则 | 更新量 <tol | 梯度范数/损失变化双重准则 | 损失不再改善- tol |
选择建议(依据上述源码事实推断):追求稠密数据的精确回归解且需要弹性网惩罚,选CD;需要逻辑回归、多分类或 SVM/SVR 损失,或输入为 CSR 稀疏矩阵,选QN;数据规模极大、允许近似解且希望控制 batch 大小与学习率调度,选SGD。
测试与工程验证
求解器模块的正确性由仓库测试套件覆盖,主要验证文件包括:
- python/cuml/tests/test_qn.py:覆盖
QN的损失类型、收敛准则、与参考实现的数值一致性; - python/cuml/tests/test_sgd.py:覆盖
SGD的损失/惩罚/学习率组合与预测; - python/cuml/tests/test_elastic_net.py:验证
ElasticNet在'cd'/'qn'/'auto'求解器下的行为; - python/cuml/tests/test_linear_regression.py 与 python/cuml/tests/test_logistic_regression.py:间接回归上述求解器组件;
- python/cuml/tests/test_lars.py 等其它线性模型测试:验证求解器与 LARS、SGD 等其它算法的交叉一致性。
此外,多 GPU 场景可参考 python/cuml/cuml/dask/solvers/cd.py 与cd_mg.pyx的 Dask 封装,它们把单 GPU 的CD求解能力扩展到多节点环境。
小结
cuml.solvers是 cuML 线性模型体系的地基:CD、QN、SGD三个类分别对应坐标下降、拟牛顿与随机梯度三类经典优化器,在 Python 侧以一致的参数风格暴露,在 C++ 侧通过 solver.hpp(CD/SGD)与 glm.hpp(QN)提供 float/double 双精度模板实现。理解这三者的损失/惩罚/收敛准则差异,即可在上层LogisticRegression、ElasticNet、MBSGD估计器中做出有依据的调参决策,也能直接以底层 API 方式构建自定义的 GPU 线性模型。
【免费下载链接】cumlNVIDIA cuML: GPU-Accelerated Machine Learning项目地址: https://gitcode.com/GitHub_Trending/cu/cuml
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考