web-dev-qa-db-fra.com

Comment enregistrer le modèle Scikit-Learn-Keras dans un fichier de persistance (pickle / hd5 / json / yaml)

J'ai le code suivant, en utilisant Keras Scikit-Learn Wrapper :

from keras.models import Sequential
from sklearn import datasets
from keras.layers import Dense
from sklearn.model_selection import train_test_split
from keras.wrappers.scikit_learn import KerasClassifier
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import cross_val_score
from sklearn import preprocessing
import pickle
import numpy as np
import json

def classifier(X, y):
    """
    Description of classifier
    """
    NOF_ROW, NOF_COL =  X.shape

    def create_model():
        # create model
        model = Sequential()
        model.add(Dense(12, input_dim=NOF_COL, init='uniform', activation='relu'))
        model.add(Dense(6, init='uniform', activation='relu'))
        model.add(Dense(1, init='uniform', activation='sigmoid'))
        # Compile model
        model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
        return model

    # evaluate using 10-fold cross validation
    seed = 7
    np.random.seed(seed)
    model = KerasClassifier(build_fn=create_model, nb_Epoch=150, batch_size=10, verbose=0)
    return model


def main():
    """
    Description of main
    """

    iris = datasets.load_iris()
    X, y = iris.data, iris.target
    X = preprocessing.scale(X)

    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=0)
    model_tt = classifier(X_train, y_train)
    model_tt.fit(X_train,y_train)

    #--------------------------------------------------
    # This fail
    #-------------------------------------------------- 
    filename = 'finalized_model.sav'
    pickle.dump(model_tt, open(filename, 'wb'))
    # load the model from disk
    loaded_model = pickle.load(open(filename, 'rb'))
    result = loaded_model.score(X_test, Y_test)
    print(result)

    #--------------------------------------------------
    # This also fail
    #--------------------------------------------------
    # from keras.models import load_model       
    # model_tt.save('test_model.h5')


    #--------------------------------------------------
    # This works OK 
    #-------------------------------------------------- 
    # print model_tt.score(X_test, y_test)
    # print model_tt.predict_proba(X_test)
    # print model_tt.predict(X_test)


# Output of predict_proba
# 2nd column is the probability that the prediction is 1
# this value is used as final score, which can be used
# with other method as comparison
# [   [ 0.25311464  0.74688536]
#     [ 0.84401423  0.15598579]
#     [ 0.96047372  0.03952631]
#     ...,
#     [ 0.25518912  0.74481088]
#     [ 0.91467732  0.08532269]
#     [ 0.25473493  0.74526507]]

# Output of predict
# [[1]
# [0]
# [0]
# ...,
# [1]
# [0]
# [1]]


if __name__ == '__main__':
    main()

Comme indiqué dans le code, il échoue à cette ligne:

pickle.dump(model_tt, open(filename, 'wb'))

Avec cette erreur:

pickle.PicklingError: Can't pickle <function create_model at 0x101c09320>: it's not found as __main__.create_model

Comment puis-je me déplacer?

20
neversaint

Edit 1: Réponse originale sur l'enregistrement du modèle

Avec HDF5:

# saving model
json_model = model_tt.model.to_json()
open('model_architecture.json', 'w').write(json_model)
# saving weights
model_tt.model.save_weights('model_weights.h5', overwrite=True)


# loading model
from keras.models import model_from_json

model = model_from_json(open('model_architecture.json').read())
model.load_weights('model_weights.h5')

# dont forget to compile your model
model.compile(loss='binary_crossentropy', optimizer='adam')

Edit 2: exemple de code complet avec jeu de données iris

# Train model and make predictions
import numpy
import pandas
from keras.models import Sequential, model_from_json
from keras.layers import Dense
from keras.utils import np_utils
from sklearn import datasets
from sklearn import preprocessing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder

# fix random seed for reproducibility
seed = 7
numpy.random.seed(seed)

# load dataset
iris = datasets.load_iris()
X, Y, labels = iris.data, iris.target, iris.target_names
X = preprocessing.scale(X)

# encode class values as integers
encoder = LabelEncoder()
encoder.fit(Y)
encoded_Y = encoder.transform(Y)

# convert integers to dummy variables (i.e. one hot encoded)
y = np_utils.to_categorical(encoded_Y)

def build_model():
    # create model
    model = Sequential()
    model.add(Dense(4, input_dim=4, init='normal', activation='relu'))
    model.add(Dense(3, init='normal', activation='sigmoid'))
    model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model

def save_model(model):
    # saving model
    json_model = model.to_json()
    open('model_architecture.json', 'w').write(json_model)
    # saving weights
    model.save_weights('model_weights.h5', overwrite=True)

def load_model():
    # loading model
    model = model_from_json(open('model_architecture.json').read())
    model.load_weights('model_weights.h5')
    model.compile(loss='categorical_crossentropy', optimizer='adam')
    return model


X_train, X_test, Y_train, Y_test = train_test_split(X, y, test_size=0.3, random_state=seed)

# build
model = build_model()
model.fit(X_train, Y_train, nb_Epoch=200, batch_size=5, verbose=0)

# save
save_model(model)

# load
model = load_model()

# predictions
predictions = model.predict_classes(X_test, verbose=0)
print(predictions)
# reverse encoding
for pred in predictions:
    print(labels[pred])

Veuillez noter que j'ai utilisé Keras uniquement, pas le wrapper. Cela ajoute seulement de la complexité à quelque chose de simple. De plus, le code est volontairement non pris en compte afin que vous puissiez avoir l'image complète.

De plus, vous avez dit que vous vouliez sortir 1 ou 0. Ce n'est pas possible dans cet ensemble de données car vous avez 3 dims et classes de sortie (Iris-setosa, Iris-versicolor, Iris-virginica). Si vous n'aviez que 2 classes, votre sortie dim et les classes seraient 0 ou 1 en utilisant la fonction de sortie sigmoïde.

14
Gaarv

Ajout juste à la réponse de gaarv - Si vous n'avez pas besoin de la séparation entre la structure du modèle (model.to_json()) et les poids (model.save_weights()), vous pouvez utiliser l'une des options suivantes:

  • Utilisez le keras.models.save_model et 'keras.models.load_model` qui stockent tout ensemble dans un fichier hdf5.
  • Utilisez pickle pour sérialiser l'objet Model (ou toute classe qui contient des références à lui) dans un fichier/réseau/peu importe.
    Malheureusement, Keras ne prend pas en charge les cornichons par défaut. Vous pouvez utiliser ma solution inégale qui ajoute cette fonctionnalité manquante. Le code de travail est ici: http://zachmoshe.com/2017/04/03/pickling-keras-models.html
7
Zach Moshe

Une autre excellente alternative consiste à utiliser callbacks lorsque vous fit votre modèle. Plus précisément le rappel ModelCheckpoint , comme ceci:

from keras.callbacks import ModelCheckpoint
#Create instance of ModelCheckpoint
chk = ModelCheckpoint("myModel.h5", monitor='val_loss', save_best_only=False)
#add that callback to the list of callbacks to pass
callbacks_list = [chk]
#create your model
model_tt = KerasClassifier(build_fn=create_model, nb_Epoch=150, batch_size=10)
#fit your model with your data. Pass the callback(s) here
model_tt.fit(X_train,y_train, callbacks=callbacks_list)

Cela permettra d'économiser votre formation chaque époque dans le myModel.h5 fichier. Cela offre de grands avantages, car vous pouvez arrêter votre entraînement lorsque vous le souhaitez (comme lorsque vous voyez qu'il a commencé à sur-équiper), tout en conservant la formation précédente.

Notez que cela enregistre à la fois la structure et les poids dans le même hdf5 (comme l'a montré Zach), vous pouvez donc charger votre modèle à l'aide de keras.models.load_model.

Si vous souhaitez enregistrer uniquement vos poids séparément, vous pouvez alors utiliser le save_weights_only=True argument lors de l'instanciation de votre ModelCheckpoint, vous permettant de charger votre modèle comme expliqué par Gaarv. Extraire des docs :

save_weights_only: si True, seuls les poids du modèle seront enregistrés (model.save_weights (filepath)), sinon le modèle complet est enregistré (model.save (chemin du fichier)).

4
DarkCygnus

La réponse acceptée est trop compliquée. Vous pouvez enregistrer et restaurer entièrement chaque aspect de votre modèle dans un .h5 fichier. Directement à partir de la Keras FAQ :

Vous pouvez utiliser model.save (filepath) pour enregistrer un modèle Keras dans un seul fichier HDF5 qui contiendra:

  • l'architecture du modèle, permettant de recréer le modèle
  • les poids du modèle
  • la configuration de la formation (perte, optimiseur)
  • l'état de l'optimiseur, permettant de reprendre l'entraînement exactement là où vous vous étiez arrêté.

Vous pouvez ensuite utiliser keras.models.load_model (filepath) pour rétablir votre modèle. load_model se chargera également de compiler le modèle en utilisant la configuration de formation enregistrée (sauf si le modèle n'a jamais été compilé en premier lieu).

Et le code correspondant:

from keras.models import load_model

model.save('my_model.h5')  # creates a HDF5 file 'my_model.h5'
del model  # deletes the existing model

# returns a compiled model
# identical to the previous one
model = load_model('my_model.h5')
1
BallpointBen