如何将Scikit-Learn-Keras模型保存到持久性文件中(pickle / hd5 / json / yaml)

时间:2016-11-03 07:34:56

标签: python scikit-learn persistence pickle keras

我有以下代码,使用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()

正如代码中所述,它在这一行失败了:

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

出现此错误:

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

我怎样才能绕过它?

5 个答案:

答案 0 :(得分:14)

编辑1 :有关保存模式的原始答案

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

编辑2 :使用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])

请注意,我只使用了Keras,而不是包装器。它只会增加一些简单的复杂性。此外,代码是volontary而不是因素,因此您可以全面了解。

另外,你说你要输出1或0.这个数据集是不可能的,因为你有3个输出dims和类(Iris-setosa,Iris-versicolor,Iris-virginica)。如果你只有2个类,那么你的输出变暗,使用sigmoid输出功能,类将为0或1。

答案 1 :(得分:5)

添加到gaarv的答案 - 如果您不需要模型结构(require(plotly) set.seed(1) scatter.list <- vector(mode = "list", 3) for(i in 1:3){ df <- data.frame(x = rnorm(100), y = rnorm(100), a = LETTERS[sample(26, 100, replace = T)] ) scatter.list[[i]] <- plot_ly(type = 'scatter', mode = 'markers', x = ~df$x, y = ~df$y, text = ~df$a, data= df, marker = list(color = 'darkred'), showlegend = FALSE) %>% layout(xaxis = list(title = xlab, zeroline = F), yaxis = list(title = ylab, zeroline = F)) } plotly::subplot(scatter.list, nrows=3) )和权重(model.to_json())之间的分隔,您可以使用以下之一:

  • 使用内置的model.save_weights()和'keras.models.load_model`将所有内容存储在hdf5文件中。
  • 使用pickle将Model对象(或任何包含对它的引用的类)序列化为file / network / whatever ..
    不幸的是,Keras默认不支持pickle。您可以使用 我的补丁解决方案,添加了这个缺少的功能。工作代码是 这里:http://zachmoshe.com/2017/04/03/pickling-keras-models.html

答案 2 :(得分:4)

另一个不错的选择是在fit模型时使用callbacks。特别是ModelCheckpoint回调,如下所示:

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)

这会将您的训练每个纪元保存到myModel.h5文件中。这提供了很大的好处,因为你可以在你想要的时候停止训练(就像你看到它已经开始过度训练一样),并且仍然保留以前的训练。

请注意,这会在同一个hdf5文件中保存结构和权重(如Zach所示),因此您可以使用keras.models.load_model加载模型。

如果您只想单独保存权重,则可以在实例化save_weights_only=True时使用ModelCheckpoint参数,以便按照Gaarv的说明加载模型。摘自docs

  

save_weights_only:如果为True,则只保存模型的权重(model.save_weights(filepath)),否则保存完整模型(model.save(filepath))。

答案 3 :(得分:1)

公认的答案太复杂了。您可以在.h5文件中完全保存和恢复模型的各个方面。直接来自Keras FAQ

  

您可以使用model.save(filepath)将Keras模型保存为单个   HDF5文件将包含:

     
      
  • 模型的体系结构,允许重新创建模型
  •   
  • 模型的权重
  •   
  • 培训配置(损失,优化程序)
  •   
  • 优化器的状态,允许从您上次中断的地方继续进行精确训练。
  •   
     

然后可以使用keras.models.load_model(filepath)重新实例化模型。 load_model还将负责使用保存的训练配置来编译模型(除非从未首先编译过模型)。

和相应的代码:

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

答案 4 :(得分:1)

如果您的keras包装模型在scikit管道中,则可以分别在管道中保存步骤。

import joblib
from sklearn.pipeline import Pipeline
from tensorflow import keras

# pass the create_cnn_model function into wrapper
cnn_model = keras.wrappers.scikit_learn.KerasClassifier(build_fn=create_cnn_model)

# create pipeline
cnn_model_pipeline_estimator = Pipeline([
    ('preprocessing_pipeline', pipeline_estimator),
    ('clf', cnn_model)
])

# train model
final_model = cnn_model_pipeline_estimator.fit(
X, y, clf__batch_size=32, clf__epochs=15)

# collect the preprocessing pipeline & model seperately
pipeline_estimator = final_model.named_steps['preprocessing_pipeline']
clf = final_model.named_steps['clf']

# store pipeline and model seperately
joblib.dump(pipeline_estimator, open('path/to/pipeline.pkl', 'wb'))
clf.model.save('path/to/model.h5')

# load pipeline and model
pipeline_estimator = joblib.load('path/to/pipeline.pxl')
model = keras.models.load_model('path/to/model.h5')

new_example = [[...]]

# transform new data with pipeline & use model for prediction
transformed_data = pipeline_estimator.transform(new_example)
prediction = model.predict(transformed_data)