CPU mais rápida que GPU usando xgb e XGBclassifier
Peço desculpas antecipadamente, pois sou iniciante. Estou testando GPU vs CPU com XGBoost usando xgb e XGBclassifier. Os resultados são os seguintes:
passed time with xgb (gpu): 0.390s
passed time with XGBClassifier (gpu): 0.465s
passed time with xgb (cpu): 0.412s
passed time with XGBClassifier (cpu): 0.421s
Estou me perguntando por que a CPU parece ter um desempenho igual, se não melhor que a GPU. Esta é a minha configuração:
- Python 3.6.1
- SO: Windows 10 64 bits
- GPU: NVIDIA RTX 2070 Super 8gb vram (driver atualizado para a versão mais recente)
- CUDA 10.1 instalado
- Processador i7 10700 2.9GHz
- Executando no Jupyter Notebook
- Instalou a compilação noturna do xgboost 1.2.0 via pip
** também tentei usar a versão do xgboost instalada a partir de uma roda binária pré-construída usando pip: mesmo problema
Aqui está o código de teste que estou usando (retirado daqui ):
param = {'max_depth':5, 'objective':'binary:logistic', 'subsample':0.8,
'colsample_bytree':0.8, 'eta':0.5, 'min_child_weight':1,
'tree_method':'gpu_hist'
}
num_round = 100
dtrain = xgb.DMatrix(X_train2, y_train)
tic = time.time()
model = xgb.train(param, dtrain, num_round)
print('passed time with xgb (gpu): %.3fs'%(time.time()-tic))
xgb_param = {'max_depth':5, 'objective':'binary:logistic', 'subsample':0.8,
'colsample_bytree':0.8, 'learning_rate':0.5, 'min_child_weight':1,
'tree_method':'gpu_hist'}
model = xgb.XGBClassifier(**xgb_param)
tic = time.time()
model.fit(X_train2, y_train)
print('passed time with XGBClassifier (gpu): %.3fs'%(time.time()-tic))
param = {'max_depth':5, 'objective':'binary:logistic', 'subsample':0.8,
'colsample_bytree':0.8, 'eta':0.5, 'min_child_weight':1,
'tree_method':'hist'}
num_round = 100
dtrain = xgb.DMatrix(X_train2, y_train)
tic = time.time()
model = xgb.train(param, dtrain, num_round)
print('passed time with xgb (cpu): %.3fs'%(time.time()-tic))
xgb_param = {'max_depth':5, 'objective':'binary:logistic', 'subsample':0.8,
'colsample_bytree':0.8, 'learning_rate':0.5, 'min_child_weight':1,
'tree_method':'hist'}
model = xgb.XGBClassifier(**xgb_param)
tic = time.time()
model.fit(X_train2, y_train)
print('passed time with XGBClassifier (cpu): %.3fs'%(time.time()-tic))
Tentei incorporar uma pesquisa de grade do Sklearn para ver se obteria velocidades mais rápidas na GPU, mas acabou sendo muito mais lenta que a CPU:
passed time with XGBClassifier (gpu): 2457.510s
Best parameter (CV score=0.490):
{'xgbclass__alpha': 100, 'xgbclass__eta': 0.01, 'xgbclass__gamma': 0.2, 'xgbclass__max_depth': 5, 'xgbclass__n_estimators': 100}
passed time with XGBClassifier (cpu): 383.662s
Best parameter (CV score=0.487):
{'xgbclass__alpha': 100, 'xgbclass__eta': 0.1, 'xgbclass__gamma': 0.2, 'xgbclass__max_depth': 2, 'xgbclass__n_estimators': 20}
Estou usando um conjunto de dados com 75 mil observações. Alguma ideia de por que não estou obtendo uma aceleração usando a GPU? O conjunto de dados é muito pequeno para obter os ganhos do uso da GPU?
Qualquer ajuda seria muito apreciada. Muito obrigado!
Respostas
Pergunta interessante. Como você observou, existem alguns exemplos disso que foram observados no Github e no oficial xgboost site:
- https://github.com/dmlc/xgboost/issues/2819
- https://discuss.xgboost.ai/t/no-gpu-usage-when-using-gpu-hist/532
Há também outros que postaram perguntas semelhantes:
- Sem aceleração usando XGBClassifier com suporte a GPU
Olhando para a documentação oficialxgboost , há uma extensa seção sobre suporte a GPU .
Há algumas coisas para verificar. A documentação observa que:
A construção da árvore (treinamento) e a previsão podem ser aceleradas com GPUs compatíveis com CUDA.
1. Sua GPU CUDA está habilitada?
Sim, é .
2. Você está usando parâmetros que podem ser afetados pelo uso da GPU?
Lembre-se de que apenas alguns parâmetros se beneficiam do uso de uma GPU. Esses são:
Sim você é. A maioria deles está incluída em seu conjunto de hiperparâmetros, o que é bom.
{subsample, sampling_method, colsample_bytree, colsample_bylevel, max_bin, gamma, gpu_id, predictor, grow_policy, monotone_constraints, interaction_constraints, single_precision_histogram}
3. Você está configurando parâmetros para usar o suporte de GPU?
Se você consultar a página de parâmetros do XGBoost , poderá encontrar áreas adicionais que podem ajudar a melhorar seus tempos. Por exemplo, updater
pode ser definido como grow_gpu_hist
, que (observe, isso é discutível, pois você tree_method
definiu, mas para notas):
grow_gpu_hist: Cresce árvore com GPU.
Na parte inferior da página de parâmetros, há parâmetros adicionais para gpu_hist
ativado, especificamente deterministic_histogram
(observe, isso é discutível, pois o padrão é True
):
Construa o histograma na GPU de forma determinística. A construção do histograma não é determinística devido ao aspecto não associativo da soma de ponto flutuante. Empregamos uma rotina de pré-arredondamento para mitigar o problema, o que pode levar a uma precisão um pouco menor. Defina como false para desativá-lo.
4. Os dados
Fiz alguns experimentos interessantes com alguns dados. Como não tive acesso aos seus dados, utilizei sklearn
o de make_classification, que gera os dados de forma bastante robusta .
Fiz algumas alterações no seu script, mas não notei nenhuma alteração: alterei os hiperparâmetros nos exemplos gpu vs cpu, executei isso 100 vezes e obtive resultados médios, etc. Nada parecia se destacar para mim. Lembrei-me de que uma vez usei XGBoost
os recursos de GPU x CPU para acelerar algumas análises; no entanto, estava trabalhando em um conjunto de dados muito maior.
Editei ligeiramente seu script para usar esses dados e também comecei a alterar o número de samples
e features
no conjunto de dados (via n_samples
e n_features
parâmetros) para observar os efeitos no tempo de execução. Parece que uma GPU melhorará significativamente os tempos de treinamento para dados de alta dimensão , mas os dados em massa com muitas amostras não apresentam uma grande melhoria. Veja meu roteiro abaixo:
import xgboost as xgb, numpy, time
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
xgb_gpu = []
xgbclassifier_gpu = []
xgb_cpu = []
xgbclassifier_cpu = []
n_samples = 75000
n_features = 500
for i in range(len(10)):
n_samples += 10000
n_features += 300
# Make my own data since I do not have the data from the SO question
X_train2, y_train = make_classification(n_samples=n_samples, n_features=n_features*0.9, n_informative=n_features*0.1,
n_redundant=100, flip_y=0.10, random_state=8)
# Keep script from OP intact
param = {'max_depth':5, 'objective':'binary:logistic', 'subsample':0.8,
'colsample_bytree':0.8, 'eta':0.5, 'min_child_weight':1,
'tree_method':'gpu_hist', 'gpu_id': 0
}
num_round = 100
dtrain = xgb.DMatrix(X_train2, y_train)
tic = time.time()
model = xgb.train(param, dtrain, num_round)
print('passed time with xgb (gpu): %.3fs'%(time.time()-tic))
xgb_gpu.append(time.time()-tic)
xgb_param = {'max_depth':5, 'objective':'binary:logistic', 'subsample':0.8,
'colsample_bytree':0.8, 'learning_rate':0.5, 'min_child_weight':1,
'tree_method':'gpu_hist', 'gpu_id':0}
model = xgb.XGBClassifier(**xgb_param)
tic = time.time()
model.fit(X_train2, y_train)
print('passed time with XGBClassifier (gpu): %.3fs'%(time.time()-tic))
xgbclassifier_gpu.append(time.time()-tic)
param = {'max_depth':5, 'objective':'binary:logistic', 'subsample':0.8,
'colsample_bytree':0.8, 'eta':0.5, 'min_child_weight':1,
'tree_method':'hist'}
num_round = 100
dtrain = xgb.DMatrix(X_train2, y_train)
tic = time.time()
model = xgb.train(param, dtrain, num_round)
print('passed time with xgb (cpu): %.3fs'%(time.time()-tic))
xgb_cpu.append(time.time()-tic)
xgb_param = {'max_depth':5, 'objective':'binary:logistic', 'subsample':0.8,
'colsample_bytree':0.8, 'learning_rate':0.5, 'min_child_weight':1,
'tree_method':'hist'}
model = xgb.XGBClassifier(**xgb_param)
tic = time.time()
model.fit(X_train2, y_train)
print('passed time with XGBClassifier (cpu): %.3fs'%(time.time()-tic))
xgbclassifier_cpu.append(time.time()-tic)
import pandas as pd
df = pd.DataFrame({'XGB GPU': xgb_gpu, 'XGBClassifier GPU': xgbclassifier_gpu, 'XGB CPU': xgb_cpu, 'XGBClassifier CPU': xgbclassifier_cpu})
#df.to_csv('both_results.csv')
Eu executei isso alterando cada um (amostras, recursos) separadamente e juntos, nos mesmos conjuntos de dados. Veja os resultados abaixo:
| Interval | XGB GPU | XGBClassifier GPU | XGB CPU | XGBClassifier CPU | Metric |
|:--------:|:--------:|:-----------------:|:--------:|:-----------------:|:----------------:|
| 0 | 11.3801 | 12.00785 | 15.20124 | 15.48131 | Changed Features |
| 1 | 15.67674 | 16.85668 | 20.63819 | 22.12265 | Changed Features |
| 2 | 18.76029 | 20.39844 | 33.23108 | 32.29926 | Changed Features |
| 3 | 23.147 | 24.91953 | 47.65588 | 44.76052 | Changed Features |
| 4 | 27.42542 | 29.48186 | 50.76428 | 55.88155 | Changed Features |
| 5 | 30.78596 | 33.03594 | 71.4733 | 67.24275 | Changed Features |
| 6 | 35.03331 | 37.74951 | 77.68997 | 75.61216 | Changed Features |
| 7 | 39.13849 | 42.17049 | 82.95307 | 85.83364 | Changed Features |
| 8 | 42.55439 | 45.90751 | 92.33368 | 96.72809 | Changed Features |
| 9 | 46.89023 | 50.57919 | 105.8298 | 107.3893 | Changed Features |
| 0 | 7.013227 | 7.303488 | 6.998254 | 9.733574 | No Changes |
| 1 | 6.757523 | 7.302388 | 5.714839 | 6.805287 | No Changes |
| 2 | 6.753428 | 7.291906 | 5.899611 | 6.603533 | No Changes |
| 3 | 6.749848 | 7.293555 | 6.005773 | 6.486256 | No Changes |
| 4 | 6.755352 | 7.297607 | 5.982163 | 8.280619 | No Changes |
| 5 | 6.756498 | 7.335412 | 6.321188 | 7.900422 | No Changes |
| 6 | 6.792402 | 7.332112 | 6.17904 | 6.443676 | No Changes |
| 7 | 6.786584 | 7.311666 | 7.093638 | 7.811417 | No Changes |
| 8 | 6.7851 | 7.30604 | 5.574762 | 6.045969 | No Changes |
| 9 | 6.789152 | 7.309363 | 5.751018 | 6.213471 | No Changes |
| 0 | 7.696765 | 8.03615 | 6.175457 | 6.764809 | Changed Samples |
| 1 | 7.914885 | 8.646722 | 6.997217 | 7.598789 | Changed Samples |
| 2 | 8.489555 | 9.2526 | 6.899783 | 7.202334 | Changed Samples |
| 3 | 9.197605 | 10.02934 | 7.511708 | 7.724675 | Changed Samples |
| 4 | 9.73642 | 10.64056 | 7.918493 | 8.982463 | Changed Samples |
| 5 | 10.34522 | 11.31103 | 8.524865 | 9.403711 | Changed Samples |
| 6 | 10.94025 | 11.98357 | 8.697257 | 9.49277 | Changed Samples |
| 7 | 11.80717 | 12.93195 | 8.734307 | 10.79595 | Changed Samples |
| 8 | 12.18282 | 13.38646 | 9.175231 | 10.33532 | Changed Samples |
| 9 | 13.05499 | 14.33106 | 11.04398 | 10.50722 | Changed Samples |
| 0 | 12.43683 | 13.19787 | 12.80741 | 13.86206 | Changed Both |
| 1 | 18.59139 | 20.01569 | 25.61141 | 35.37391 | Changed Both |
| 2 | 24.37475 | 26.44214 | 40.86238 | 42.79259 | Changed Both |
| 3 | 31.96762 | 34.75215 | 68.869 | 59.97797 | Changed Both |
| 4 | 41.26578 | 44.70537 | 83.84672 | 94.62811 | Changed Both |
| 5 | 49.82583 | 54.06252 | 109.197 | 108.0314 | Changed Both |
| 6 | 59.36528 | 64.60577 | 131.1234 | 140.6352 | Changed Both |
| 7 | 71.44678 | 77.71752 | 156.1914 | 161.4897 | Changed Both |
| 8 | 81.79306 | 90.56132 | 196.0033 | 193.4111 | Changed Both |
| 9 | 94.71505 | 104.8044 | 215.0758 | 224.6175 | Changed Both |
Sem alteração

Contagem de recursos crescente linearmente

Aumentar Amostras Linearmente

Amostras + recursos linearmente crescentes

Quando comecei a pesquisar mais; isso faz sentido. Sabe-se que as GPUs escalam bem com dados de alta dimensão, e faria sentido que você visse uma melhoria no tempo de treinamento se seus dados fossem de alta dimensão . Veja os exemplos a seguir:
- https://projecteuclid.org/download/pdfview_1/euclid.ss/1294167962
- Agrupamento Kmeans mais rápido em dados de alta dimensão com suporte a GPU
- https://link.springer.com/article/10.1007/s11063-014-9383-4
Embora não possamos dizer com certeza sem acesso aos seus dados, parece que os recursos de hardware de uma GPU permitem aumentos significativos de desempenho quando seus dados o suportam, e parece que pode não ser o caso, dado o tamanho e a forma dos dados que você tenho.