web-dev-qa-db-ja.com

Scikit-Learn-Kerasモデルを永続性ファイル(pickle / hd5 / json / yaml)に保存する方法

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

どうすればそれを回避できますか?

20
neversaint

編集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:アイリスデータセットを使用した完全なコード例

# 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のみを使用したことに注意してください。単純なものに複雑さを加えるだけです。また、コードは因数分解ではなく任意であるため、全体像を把握できます。

また、1または0を出力すると言いました。3つの出力ディムとクラス(Iris-setosa、Iris-versicolor、Iris-virginica)があるため、このデータセットでは不可能です。クラスが2つしかない場合、シグモイド出力関数を使用すると、出力は薄暗く、クラスは0または1になります。

14
Gaarv

Gaarvの答えに追加するだけです-モデル構造(model.to_json())と重み(model.save_weights())を分離する必要がない場合は、次のいずれかを使用できます。

  • 組み込みのkeras.models.save_modelと 'keras.models.load_model`はすべてをhdf5ファイルにまとめて保存します。
  • Pickleを使用して、Modelオブジェクト(またはそのオブジェクトへの参照を含むクラス)をファイル/ネットワーク/その他にシリアル化します。
    不幸なことに、Kerasはデフォルトでpickleをサポートしていません。この欠けている機能を追加するパッチ適用ソリューションを使用できます。作業コードはこちら: http://zachmoshe.com/2017/04/03/pickling-keras-models.html
7
Zach Moshe

別の優れた代替手段は、モデルを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を使用してモデルをロードできます。

ウェイトのみを個別に保存する場合は、ModelCheckpointをインスタンス化するときにsave_weights_only=True引数を使用して、Gaarvの説明に従ってモデルをロードできます。 docs からの抽出:

save_weights_only:Trueの場合、モデルの重みのみが保存され(model.save_weights(filepath))、それ以外の場合は完全なモデルが保存されます(model.save (ファイルパス))。

4
DarkCygnus

受け入れられた答えは複雑すぎます。モデルのあらゆる側面を.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')
1
BallpointBen