Scikit-Learn 1.4使用指南:通过scikit-learn计算 计算性能 Computational Performance

本文详细探讨了在生产环境中,Scikit-learn中的预测延迟和吞吐量优化关键因素,包括特征数量、数据表示、模型复杂度、内存管理以及模型压缩和重塑等技术。提供了一些实用的技巧和配置建议,帮助提高机器学习模型的性能效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


对于某些应用来说,估计器的性能(主要是预测时的延迟和吞吐量)至关重要。考虑训练吞吐量也可能是有趣的,但在生产环境中通常不太重要(因为通常是离线进行的)。

我们将在这里讨论一下在不同情境下可以期望从一些 scikit-learn 估计器中获得的数量级,并提供一些克服性能瓶颈的技巧和窍门。

预测延迟以预测所需的经过的时间来衡量(例如以微秒为单位)。延迟通常被视为一个分布,运维工程师通常关注该分布的某个百分位数的延迟(例如 90 百分位数)。

预测吞吐量定义为软件在给定时间内可以提供的预测数量(例如每秒预测数)。

性能优化的一个重要方面也是它可能会影响预测准确性。事实上,简单的模型(例如线性模型而不是非线性模型,或者参数更少的模型)通常运行更快,但并不总能考虑到与更复杂模型相同的数据属性。

8.2.1. 预测延迟

在使用/选择机器学习工具包时,最直接的关注之一就是在生产环境中可以进行预测的延迟。

影响预测延迟的主要因素有:

  1. 特征数量
  2. 输入数据表示和稀疏性
  3. 模型复杂度
  4. 特征提取

最后一个主要参数还是能否以批量或逐个模式进行预测。

8.2.1.1. 批量模式与原子模式

一般来说,批量预测(同时预测多个实例)在许多方面更高效(分支预测性、CPU 缓存、线性代数库优化等)。在这里,我们可以看到在特征较少的情况下,无论选择哪个估计器,批量模式总是更快的,对于其中一些估计器,速度甚至快了 1 到 2 个数量级:

原子预测延迟

批量预测延迟

要为您的情况对不同的估计器进行基准测试,您只需更改此示例中的 n_features 参数:预测延迟。这应该能够给您一个预测延迟的数量级估计。

8.2.1.2. 配置 Scikit-learn 以减少验证开销

Scikit-learn 对数据进行一些验证,这会增加每次调用 predict 和类似函数的开销。特别是,检查特征是否有限(不是 NaN 或无穷大)需要对数据进行完整的遍历。如果您确保数据是可接受的,可以通过在导入 scikit-learn 之前将环境变量 SKLEARN_ASSUME_FINITE 设置为非空字符串,或者在 Python 中使用 set_config 进行配置来取消检查有限性。如果您需要比这些全局设置更多的控制权,config_context 允许您在指定的上下文中设置此配置:

>>> import sklearn
>>> with sklearn.config_context(assume_finite=True):
...     pass  # 在这里进行学习/预测,减少验证

请注意,这将影响上下文中所有对 assert_all_finite 的使用。

8.2.1.3. 特征数量的影响

显然,当特征数量增加时,每个示例的内存消耗也会增加。实际上,对于具有特征的实例矩阵,空间复杂度为 。从计算的角度来看,这也意味着基本操作的数量(例如线性模型中的向量-矩阵乘法的乘法)也会增加。下面是预测延迟随特征数量变化的图表:

特征数量对延迟的影响

总体而言,您可以预期预测时间至少与特征数量线性增加(根据全局内存占用和估计器的不同,可能会发生非线性情况)。

8.2.1.4. 输入数据表示的影响

Scipy 提供了针对存储稀疏数据进行优化的稀疏矩阵数据结构。稀疏格式的主要特点是您不存储零值,因此如果您的数据是稀疏的,那么您可以使用更少的内存。稀疏(CSR 或 CSC)表示中的非零值只需要平均一个 32 位整数位置 + 64 位浮点值 + 矩阵中每行或每列的额外 32 位。在稀疏输入上使用密集(或稀疏)线性模型可以大大加快预测速度,因为只有非零值特征会影响点积和模型预测。因此,如果在 1e6 维空间中有 100 个非零值,您只需要进行 100 次乘法和加法运算,而不是 1e6 次。

然而,对于密集表示的计算,可以利用高度优化的向量操作和 BLAS 中的多线程,从而减少 CPU 缓存未命中。因此,为了使稀疏输入表示在具有多个 CPU 和优化的 BLAS 实现的计算机上比密集输入表示更快,稀疏度通常应该相当高(最多 10% 的非零值,具体取决于硬件)。

以下是测试输入稀疏性的示例代码:

def sparsity_ratio(X):
    return 1.0 - np.count_nonzero(X) / float(X.shape[0] * X.shape[1])
print("input sparsity ratio:", sparsity_ratio(X))

作为经验法则,如果稀疏比率大于 90%,则可能可以从稀疏格式中受益。有关如何构建(或将数据转换为)稀疏矩阵格式的更多信息,请查看 Scipy 的稀疏矩阵格式 文档。大多数情况下,CSRCSC 格式效果最好。

8.2.1.5. 模型复杂度的影响

一般来说,当模型复杂度增加时,预测能力和延迟应该会增加。增加预测能力通常是有意义的,但对于许多应用程序来说,最好不要太大幅度增加预测延迟。我们现在将为不同类型的监督模型讨论这个想法。

对于 sklearn.linear_model(例如 Lasso、ElasticNet、SGDClassifier/Regressor、Ridge 和 RidgeClassifier、PassiveAggressiveClassifier/Regressor、LinearSVC、LogisticRegression 等)来说,预测时应用的决策函数是相同的(点积),因此延迟应该是相等的。
en_model_complexity

对于具有非线性核的 sklearn.svm 算法系列,延迟与支持向量的数量相关(支持向量越少,速度越快)。在 SVC 或 SVR 模型中,延迟和吞吐量应该(渐近地)与支持向量的数量成线性关系。核函数也会影响延迟,因为它用于每个支持向量计算输入向量的投影。在下图中,NuSVRnu 参数被用来影响支持向量的数量。

nusvr_model_complexity

对于树的 sklearn.ensemble(例如 RandomForest、GBT、ExtraTrees 等),树的数量和深度起着最重要的作用。延迟和吞吐量应该与树的数量成线性关系。在这种情况下,我们直接使用 GradientBoostingRegressorn_estimators 参数。

gbt_model_complexity

无论如何,需要注意降低模型复杂性可能会损害准确性,如上所述。例如,非线性可分问题可以使用高速线性模型处理,但预测能力很可能会受到影响。

8.2.1.6. 特征提取延迟

大多数 scikit-learn 模型通常都很快,因为它们要么使用编译的 Cython 扩展,要么使用优化的计算库实现。另一方面,在许多实际应用中,特征提取过程(即将原始数据(如数据库行或网络数据包)转换为 numpy 数组)决定了整体预测时间。例如,在 Reuters 文本分类任务中,整个准备过程(读取和解析 SGML 文件,对文本进行标记化并将其哈希化为一个共同的向量空间)所花费的时间比实际预测代码多 100 到 500 倍,具体取决于所选择的模型。

prediction_time

因此,在整体延迟对于应用程序来说过慢时,建议仔细计时和分析特征提取代码,因为这可能是优化的好起点。

8.2.2. 预测吞吐量

在调整生产系统规模时,另一个重要的指标是吞吐量,即在给定时间内可以进行的预测数量。下面是一个基准测试,来自预测延迟示例,用于测量一些估计器在合成数据上的吞吐量:

throughput_benchmark

这些吞吐量是在单个进程上实现的。增加应用程序吞吐量的一种明显方法是生成额外的实例(通常在 Python 中是进程),它们共享相同的模型。也可以添加机器来分担负载。如何实现这一点的详细说明超出了本文档的范围。

8.2.3. 小贴士

8.2.3.1. 线性代数库

由于 scikit-learn 在很大程度上依赖于 Numpy/Scipy 和线性代数,因此有必要特别关注这些库的版本。基本上,您应该确保 Numpy 使用了优化的 BLAS / LAPACK 库进行构建。

并非所有模型都受益于优化的 BLAS 和 Lapack 实现。例如,基于(随机)决策树的模型通常不在其内部循环中使用 BLAS 调用,核 SVM(SVCSVRNuSVCNuSVR)也是如此。另一方面,使用 BLAS DGEMM 调用(通过 numpy.dot)实现的线性模型通常会从调优的 BLAS 实现中获益,并且比非优化的 BLAS 快几个数量级。

您可以使用以下命令显示您的 NumPy / SciPy / scikit-learn 安装所使用的 BLAS / LAPACK 实现:

python -c "import sklearn; sklearn.show_versions()"

优化的 BLAS / LAPACK 实现包括:

  • Atlas(需要在目标机器上重新构建以进行硬件特定的调优)
  • OpenBLAS
  • MKL
  • Apple Accelerate 和 vecLib 框架(仅限于 OSX)

更多信息可以在 NumPy 安装页面 和 Daniel Nouri 的博客文章中找到,其中提供了一些德比安装的逐步说明。

8.2.3.2. 限制工作内存

使用标准的 numpy 向量化操作实现的一些计算涉及使用大量临时内存。这可能会耗尽系统内存。在可以以固定内存块执行计算的情况下,我们会尝试这样做,并允许用户提示此工作内存的最大大小(默认为 1GB),使用 set_configconfig_context。以下示例将临时工作内存限制为 128 MiB:

>>> import sklearn
>>> with sklearn.config_context(working_memory=128):
...     pass  # 在此处执行分块工作

遵守此设置的分块操作示例是 pairwise_distances_chunked,它便于计算成对距离矩阵的逐行归约。

8.2.3.3. 模型压缩

目前,scikit-learn 中的模型压缩仅涉及线性模型。在这种情况下,我们希望控制模型的稀疏性(即模型向量中的非零坐标数)。通常,将模型稀疏性与稀疏输入数据表示相结合是一个很好的选择。

以下是演示使用 sparsify() 方法的示例代码:

clf = SGDRegressor(penalty='elasticnet', l1_ratio=0.25)
clf.fit(X_train, y_train).sparsify()
clf.predict(X_test)

在此示例中,我们更喜欢 elasticnet 惩罚,因为它通常是模型紧凑性和预测能力之间的一个很好的折中。可以进一步调整 l1_ratio 参数(与正则化强度 alpha 结合使用)来控制这种权衡。

8.2.3.4. 模型重塑

模型重塑是指选择仅使用部分可用特征来拟合模型。换句话说,如果模型在学习阶段丢弃了某些特征,我们可以从输入中将这些特征去除。这有几个好处。首先,它减少了模型本身的内存(因此也减少了时间)开销。它还允许在我们知道从先前运行中保留哪些特征后,从流水线中丢弃显式的特征选择组件。最后,它可以通过不收集和构建模型丢弃的特征来减少数据访问和特征提取层上游的处理时间和I/O使用。例如,如果原始数据来自数据库,这可以使得编写更简单更快速的查询成为可能,或者通过使查询返回更轻量级的记录来减少I/O使用。目前,在 scikit-learn 中需要手动执行重塑操作。对于稀疏输入(特别是 CSR 格式),通常只需不生成相关特征,将它们的列保留为空即可。

8.2.3.5. 链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数智笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值