CPU più veloce della GPU utilizzando xgb e XGBclassifier

Aug 17 2020

Mi scuso in anticipo perché sono un principiante. Sto provando i test GPU vs CPU con XGBoost usando xgb e XGBclassifier. I risultati sono i seguenti:

   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

Mi chiedo perché la CPU sembra funzionare alla pari se non meglio della GPU. Questa è la mia configurazione:

  • Pitone 3.6.1
  • Sistema operativo: Windows 10 a 64 bit
  • GPU: NVIDIA RTX 2070 Super 8gb vram (driver aggiornato all'ultima versione)
  • CUDA 10.1 installato
  • Processore i7 10700 2,9 Ghz
  • In esecuzione su Jupyter Notebook
  • Installata la build notturna di xgboost 1.2.0 tramite pip

** provato anche a utilizzare la versione di xgboost installata da una ruota binaria predefinita utilizzando pip: stesso problema

Ecco il codice di prova che sto usando (rilevato da qui ):

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))

Ho provato a incorporare una ricerca sulla griglia di Sklearn per vedere se avrei ottenuto velocità più elevate sulla GPU, ma alla fine è stata molto più lenta della 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}

Sto usando un set di dati con 75k osservazioni. Qualche idea sul perché non ottengo un aumento di velocità dall'utilizzo della GPU? Il set di dati è troppo piccolo per ottenere vantaggi dall'utilizzo della GPU?

Qualsiasi aiuto sarebbe molto apprezzato. Grazie mille!

Risposte

6 wundermahn Jan 11 2021 at 19:47

Domanda interessante. Come hai notato, ci sono alcuni esempi di ciò che sono stati notati su Github e sul funzionario xgboost site:

  • https://github.com/dmlc/xgboost/issues/2819
  • https://discuss.xgboost.ai/t/no-gpu-usage-when-using-gpu-hist/532

Ci sono anche altri che hanno postato domande simili:

  • Nessuna accelerazione utilizzando XGBClassifier con supporto GPU

Guardando la documentazione ufficialexgboost , c'è un'ampia sezione sul supporto GPU .

Ci sono alcune cose da controllare. La documentazione rileva che:

La costruzione dell'albero (addestramento) e la previsione possono essere accelerate con GPU compatibili con CUDA.

1. La tua GPU CUDA è abilitata?

Sì, lo è .

2. Stai utilizzando parametri che possono essere influenzati dall'utilizzo della GPU?

Tieni presente che solo alcuni parametri traggono vantaggio dall'utilizzo di una GPU. Quelli sono:

Sì, sei tu. La maggior parte di questi è inclusa nel set di iperparametri, il che è positivo.

{subsample, sampling_method, colsample_bytree, colsample_bylevel, max_bin, gamma, gpu_id, predictor, grow_policy, monotone_constraints, interaction_constraints, single_precision_histogram}

3. Stai configurando i parametri per utilizzare il supporto GPU?

Se guardi la pagina dei parametri di XGBoost , puoi trovare aree aggiuntive che possono aiutarti a migliorare i tuoi tempi. Ad esempio, updaterpuò essere impostato su grow_gpu_hist, which (nota, questo è discutibile dato che hai tree_methodimpostato, ma per le note):

grow_gpu_hist: Fai crescere l'albero con la GPU.

Nella parte inferiore della pagina dei parametri, ci sono parametri aggiuntivi per gpu_histabilitato, in particolare deterministic_histogram(nota, questo è discutibile poiché l'impostazione predefinita è True):

Crea l'istogramma sulla GPU in modo deterministico. La costruzione dell'istogramma non è deterministica a causa dell'aspetto non associativo della sommatoria in virgola mobile. Utilizziamo una routine di pre-arrotondamento per mitigare il problema, che potrebbe portare a una precisione leggermente inferiore. Imposta su false per disabilitarlo.

4. I dati

Ho condotto alcuni esperimenti interessanti con alcuni dati. Dato che non avevo accesso ai tuoi dati, ho usato sklearn's make_classification, che genera dati in modo piuttosto robusto .

Ho apportato alcune modifiche al tuo script ma non ho notato alcun cambiamento: ho cambiato gli iperparametri sugli esempi gpu vs cpu, l'ho eseguito 100 volte e ho ottenuto risultati medi, ecc. Ho ricordato che una volta ho utilizzato XGBoostle funzionalità GPU vs CPU per velocizzare alcune analisi, tuttavia stavo lavorando su un set di dati molto più grande.

Ho modificato leggermente il tuo script per utilizzare questi dati e ho anche iniziato a modificare il numero di samplese featuresnel set di dati (via n_samplese n_featuresparametri) per osservare gli effetti sul runtime. Sembra che una GPU migliorerà in modo significativo i tempi di addestramento per i dati ad alta dimensione , ma i dati di massa con molti campioni non vedono un enorme miglioramento. Vedi il mio script qui sotto:

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')

Ho eseguito questo modificando ciascuno (campioni, funzionalità) separatamente e insieme, sugli stessi set di dati. Vedi i risultati di seguito:

| 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   |

Nessun cambiamento

Conteggio delle caratteristiche in aumento lineare

Campioni linearmente crescenti

Campioni + funzionalità in aumento lineare

Quando ho iniziato a ricercare di più; questo ha senso. È noto che le GPU si adattano bene con dati dimensionali elevati e avrebbe senso vedere un miglioramento dei tempi di addestramento se i dati fossero dimensionali elevati . Vedere i seguenti esempi:

  • https://projecteuclid.org/download/pdfview_1/euclid.ss/1294167962
  • Clustering Kmeans più veloce su dati ad alta dimensione con supporto GPU
  • https://link.springer.com/article/10.1007/s11063-014-9383-4

Sebbene non possiamo dirlo con certezza senza l'accesso ai tuoi dati, sembrerebbe che le capacità hardware di una GPU consentano aumenti significativi delle prestazioni quando i tuoi dati lo supportano, e sembra che potrebbe non essere il caso data la dimensione e la forma dei dati che hai avere.