python - 增加 n_jobs 对 GridSearchCV 没有影响

标签 python multithreading scikit-learn knn

我设置了一个简单的实验来检查多核 CPU 在运行 sklearn GridSearchCVKNeighborsClassifier 时的重要性。我得到的结果让我感到惊讶,我想知道我是否误解了多核的好处,或者我没有做对。

完成 2-8 个作业的时间没有差异。怎么来的 ?我注意到 CPU 性能选项卡上的差异。当第一个单元格运行时,CPU 使用率约为 13%,最后一个单元格逐渐增加到 100%。我期待它能更快地完成。也许不是线性更快,也就是 8 个作业会比 4 个作业快 2 倍,但会快一点。

我是这样设置的:

我用的是jupyter-notebook,cell指的是jupyter-notebook cell。

我已经加载了 MNIST 并在 X_play 中使用了 0.05 测试大小 3000 位。

from sklearn.datasets import fetch_mldata
from sklearn.model_selection import train_test_split

mnist = fetch_mldata('MNIST original')

X, y = mnist["data"], mnist['target']

X_train, X_test, y_train, y_test = X[:60000], X[60000:], y[:60000], y[60000:]
_, X_play, _, y_play = train_test_split(X_train, y_train, test_size=0.05, random_state=42, stratify=y_train, shuffle=True)

在下一个单元格中,我设置了 KNNGridSearchCV

from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV

knn_clf = KNeighborsClassifier()
param_grid = [{'weights': ["uniform", "distance"], 'n_neighbors': [3, 4, 5]}]

然后我为 8 个 n_jobs 值做了 8 个单元格。我的 CPU 是 i7-4770,4 核 8 线程。

grid_search = GridSearchCV(knn_clf, param_grid, cv=3, verbose=3, n_jobs=N_JOB_1_TO_8)
grid_search.fit(X_play, y_play)

结果

Parallel(n_jobs=1)]: Done  18 out of  18 | elapsed:  2.0min finished
Parallel(n_jobs=2)]: Done  18 out of  18 | elapsed:  1.4min finished
Parallel(n_jobs=3)]: Done  18 out of  18 | elapsed:  1.3min finished
Parallel(n_jobs=4)]: Done  18 out of  18 | elapsed:  1.3min finished
Parallel(n_jobs=5)]: Done  18 out of  18 | elapsed:  1.4min finished
Parallel(n_jobs=6)]: Done  18 out of  18 | elapsed:  1.4min finished
Parallel(n_jobs=7)]: Done  18 out of  18 | elapsed:  1.4min finished
Parallel(n_jobs=8)]: Done  18 out of  18 | elapsed:  1.4min finished

第二次测试

随机森林分类器的使用要好得多。测试大小为 0.530000 图像。

from sklearn.ensemble import RandomForestClassifier

rf_clf = RandomForestClassifier()
param_grid = [{'n_estimators': [20, 30, 40, 50, 60], 'max_features': [100, 200, 300, 400, 500], 'criterion': ['gini', 'entropy']}]

Parallel(n_jobs=1)]: Done 150 out of 150 | elapsed: 110.9min finished
Parallel(n_jobs=2)]: Done 150 out of 150 | elapsed: 56.8min finished
Parallel(n_jobs=3)]: Done 150 out of 150 | elapsed: 39.3min finished
Parallel(n_jobs=4)]: Done 150 out of 150 | elapsed: 35.3min finished
Parallel(n_jobs=5)]: Done 150 out of 150 | elapsed: 36.0min finished
Parallel(n_jobs=6)]: Done 150 out of 150 | elapsed: 34.4min finished
Parallel(n_jobs=7)]: Done 150 out of 150 | elapsed: 32.1min finished
Parallel(n_jobs=8)]: Done 150 out of 150 | elapsed: 30.1min finished

最佳答案

以下是一些可能导致此行为的原因

  • 随着编号的增加在线程中,初始化和释放每个线程会产生明显的开销。我在我的 i7 7700HQ 上运行了你的代码,我看到每次不断增加的 n_job 都会出现以下行为
    • n_job=1n_job=2 时,每个线程的时间(GridSearchCV 对模型进行全面训练和测试的每个模型评估时间)为 2.9 秒(总体而言时间~2分钟)
    • n_job=3时,时间为3.4s(总时间1.4分钟)
    • n_job=4时,时间为3.8s(总时间58秒)
    • n_job=5时,时间为4.2s(总时间51秒)
    • n_job=6时,时间为4.2s(总时间~49秒)
    • n_job=7时,时间为4.2s(总时间~49秒)
    • n_job=8时,时间为4.2s(总时间~49秒)
  • 现在如您所见,每个线程的时间增加了,但总体时间似乎减少了(尽管在 n_job=4 之后,差异并不是完全线性的)并且保持在 n_jobs>=6 ` 这是因为初始化和释放线程会产生成本。参见 this github issuethis issue .

  • 此外,可能还有其他瓶颈,例如数据太大而无法同时广播到所有线程、线程抢占 RAM(或其他资源等)、如何将数据推送到每个线程线程等

  • 我建议您阅读 Ahmdal 定律,该定律指出通过公式给出的并行化可以实现的加速在理论上存在界限 enter image description here 图片来源:Ahmdal's Law : Wikipedia

  • 最后,这也可能是由于数据大小和您用于训练的模型的复杂性所致。

这里是 a blog post解释有关多线程的相同问题。

关于python - 增加 n_jobs 对 GridSearchCV 没有影响,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50993867/

相关文章:

python - OpenCV-使用多个IP摄像机进行运动捕捉

Python 在一行中用 strip 拆分两次

python - 如何在 Python openCV 中显示 16 位 4096 强度图像?

c# - 线程不会在应用程序关闭时中止

java - 在多个线程中调用相同的API

python - 我在 sklearn 中没有顺利得到多项式回归

python - scikit-learn 中用于交叉验证的自定义折叠

python - 从 flask 中的 url 获取 facebook 访问 token

c++ - 在生产者-消费者情况下使用条件变量

python - ValueError : Input contains NaN, 无穷大或值对于 dtype ('float64' 来说太大)使用来自 KNeighborsRegressor 的拟合