web-dev-qa-db-fra.com

Comment puis-je mettre en œuvre une formation incrémentielle pour xgboost?

Le problème est que mes données de train n'ont pas pu être placées dans RAM en raison de la taille des données de train. J'ai donc besoin d'une méthode qui construit d'abord un arbre sur un ensemble de données de train complet, calcule les résidus, crée un autre arbre, etc. Évidemment, si j'appelle model = xgb.train(param, batch_dtrain, 2) dans une boucle, cela ne changera rien, car dans ce cas, le modèle entier est reconstruit pour chaque lot.

18
Marat Zakirov

Avertissement: je suis également nouveau sur xgboost, mais je pense avoir compris cela.

Essayez de sauvegarder votre modèle après vous être entraîné sur le premier lot. Ensuite, lors d'exécutions successives, indiquez à la méthode xgb.train le chemin d'accès au fichier du modèle enregistré. 

Voici une petite expérience que j'ai courue pour me convaincre que cela fonctionne:

Tout d’abord, divisez l’ensemble de données de Boston en ensembles d’entraînement et de test . Ensuite, divisez l’ensemble d’entraînement en moitiés. modèles avec la seconde moitié; un modèle aura le paramètre supplémentaire xgb_model. Si le fait de passer par le paramètre supplémentaire ne faisait pas une différence, alors nous nous attendrions à ce que leurs scores soient similaires… Mais, heureusement, le nouveau modèle semble bien mieux performer que le premier.

import xgboost as xgb
from sklearn.cross_validation import train_test_split as ttsplit
from sklearn.datasets import load_boston
from sklearn.metrics import mean_squared_error as mse

X = load_boston()['data']
y = load_boston()['target']

# split data into training and testing sets
# then split training set in half
X_train, X_test, y_train, y_test = ttsplit(X, y, test_size=0.1, random_state=0)
X_train_1, X_train_2, y_train_1, y_train_2 = ttsplit(X_train, 
                                                     y_train, 
                                                     test_size=0.5,
                                                     random_state=0)

xg_train_1 = xgb.DMatrix(X_train_1, label=y_train_1)
xg_train_2 = xgb.DMatrix(X_train_2, label=y_train_2)
xg_test = xgb.DMatrix(X_test, label=y_test)

params = {'objective': 'reg:linear', 'verbose': False}
model_1 = xgb.train(params, xg_train_1, 30)
model_1.save_model('model_1.model')

# ================= train two versions of the model =====================#
model_2_v1 = xgb.train(params, xg_train_2, 30)
model_2_v2 = xgb.train(params, xg_train_2, 30, xgb_model='model_1.model')

print(mse(model_1.predict(xg_test), y_test))     # benchmark
print(mse(model_2_v1.predict(xg_test), y_test))  # "before"
print(mse(model_2_v2.predict(xg_test), y_test))  # "after"

# 23.0475232194
# 39.6776876084
# 27.2053239482

Faites-moi savoir si quelque chose n'est pas clair!

référence: https://github.com/dmlc/xgboost/blob/master/python-package/xgboost/training.py

23
Alain

Il existe maintenant (version 0.6?) Un paramètre process_update qui pourrait aider. Voici une expérience avec elle:

import pandas as pd
import xgboost as xgb
from sklearn.model_selection import ShuffleSplit
from sklearn.datasets import load_boston
from sklearn.metrics import mean_squared_error as mse

boston = load_boston()
features = boston.feature_names
X = boston.data
y = boston.target

X=pd.DataFrame(X,columns=features)
y = pd.Series(y,index=X.index)

# split data into training and testing sets
rs = ShuffleSplit(test_size=0.3, n_splits=1, random_state=0)
for train_idx,test_idx in rs.split(X):  # this looks silly
    pass

train_split = round(len(train_idx) / 2)
train1_idx = train_idx[:train_split]
train2_idx = train_idx[train_split:]
X_train = X.loc[train_idx]
X_train_1 = X.loc[train1_idx]
X_train_2 = X.loc[train2_idx]
X_test = X.loc[test_idx]
y_train = y.loc[train_idx]
y_train_1 = y.loc[train1_idx]
y_train_2 = y.loc[train2_idx]
y_test = y.loc[test_idx]

xg_train_0 = xgb.DMatrix(X_train, label=y_train)
xg_train_1 = xgb.DMatrix(X_train_1, label=y_train_1)
xg_train_2 = xgb.DMatrix(X_train_2, label=y_train_2)
xg_test = xgb.DMatrix(X_test, label=y_test)

params = {'objective': 'reg:linear', 'verbose': False}
model_0 = xgb.train(params, xg_train_0, 30)
model_1 = xgb.train(params, xg_train_1, 30)
model_1.save_model('model_1.model')
model_2_v1 = xgb.train(params, xg_train_2, 30)
model_2_v2 = xgb.train(params, xg_train_2, 30, xgb_model=model_1)

params.update({'process_type': 'update',
               'updater'     : 'refresh',
               'refresh_leaf': True})
model_2_v2_update = xgb.train(params, xg_train_2, 30, xgb_model=model_1)

print('full train\t',mse(model_0.predict(xg_test), y_test)) # benchmark
print('model 1 \t',mse(model_1.predict(xg_test), y_test))  
print('model 2 \t',mse(model_2_v1.predict(xg_test), y_test))  # "before"
print('model 1+2\t',mse(model_2_v2.predict(xg_test), y_test))  # "after"
print('model 1+update2\t',mse(model_2_v2_update.predict(xg_test), y_test))  # "after"

Sortie:

full train   17.8364309709
model 1      24.2542132108
model 2      25.6967017352
model 1+2    22.8846455135
model 1+update2  14.2816257268
11
paulperry

J'ai créé un cahier Gist of jupyter pour démontrer que le modèle xgboost peut être formé progressivement. J'ai utilisé le jeu de données Boston pour former le modèle. J'ai fait 3 expériences - apprentissage un coup, apprentissage itératif un coup, apprentissage incrémental itératif. Lors de l’entraînement incrémental, j’ai transmis les données de Boston au modèle par lots de taille 50.

En résumé, vous devrez parcourir plusieurs fois les données pour que le modèle converge vers la précision obtenue par un seul apprentissage (toutes les données).

Voici le code correspondant à l'apprentissage itératif incrémental avec xgboost.

batch_size = 50
iterations = 25
model = None
for i in range(iterations):
    for start in range(0, len(x_tr), batch_size):
        model = xgb.train({
            'learning_rate': 0.007,
            'update':'refresh',
            'process_type': 'update',
            'refresh_leaf': True,
            #'reg_lambda': 3,  # L2
            'reg_alpha': 3,  # L1
            'silent': False,
        }, dtrain=xgb.DMatrix(x_tr[start:start+batch_size], y_tr[start:start+batch_size]), xgb_model=model)

        y_pr = model.predict(xgb.DMatrix(x_te))
        #print('    MSE itr@{}: {}'.format(int(start/batch_size), sklearn.metrics.mean_squared_error(y_te, y_pr)))
    print('MSE itr@{}: {}'.format(i, sklearn.metrics.mean_squared_error(y_te, y_pr)))

y_pr = model.predict(xgb.DMatrix(x_te))
print('MSE at the end: {}'.format(sklearn.metrics.mean_squared_error(y_te, y_pr)))

Version XGBoost: 0.6

3
Shubham Chaudhary

il semble que vous n'ayez besoin de rien d'autre que d'appeler à nouveau votre xgb.train(....) mais de fournir le résultat du modèle du lot précédent:

# python
params = {} # your params here
ith_batch = 0
n_batches = 100
model = None
while ith_batch < n_batches:
    d_train = getBatchData(ith_batch)
    model = xgb.train(params, d_train, xgb_model=model)
    ith_batch += 1

ceci est basé sur https://xgboost.readthedocs.io/en/latest/python/python_api.html enter image description here

1
Mobigital

Si votre problème concerne la taille du jeu de données et que vous n'avez pas vraiment besoin d'apprentissage incrémentiel (vous ne traitez pas avec une application de diffusion en continu, par exemple), vous devriez vérifier Spark ou Flink. 

Ces deux frameworks peuvent s’entraîner sur de très grands ensembles de données avec une petite mémoire vive, exploitant ainsi la mémoire disque. Les deux frameworks traitent des problèmes de mémoire en interne. Bien que Flink l'ait résolu en premier, Spark a rattrapé son retard dans les dernières versions.

Jeter un coup d'œil à:

Au code de paulperry, Si vous changez une ligne de "train_split = round (len (train_idx)/2)" en "train_split = len (train_idx) - 50". le modèle 1 + update2 sera modifié de 14,2816257268 à 45,60806270012028. Et beaucoup de "feuille = 0" résultent en un fichier de vidage.

Le modèle mis à jour n'est pas bon lorsque le jeu d'échantillons de mise à jour est relativement petit . Pour binaire: logistique, le modèle mis à jour est inutilisable lorsque le jeu d'échantillons de mise à jour ne comporte qu'une classe.

0
Tao Cheng