Ładowanie wytrenowanego modelu Keras i kontynuuj szkolenie

102

Zastanawiałem się, czy da się zapisać częściowo wytrenowany model Keras i kontynuować trening po ponownym załadowaniu modelu.

Powodem tego jest to, że w przyszłości będę miał więcej danych treningowych i nie chcę ponownie trenować całego modelu.

Funkcje, których używam to:

#Partly train model
model.fit(first_training, first_classes, batch_size=32, nb_epoch=20)

#Save partly trained model
model.save('partly_trained.h5')

#Load partly trained model
from keras.models import load_model
model = load_model('partly_trained.h5')

#Continue training
model.fit(second_training, second_classes, batch_size=32, nb_epoch=20)

Edycja 1: dodano w pełni działający przykład

Przy pierwszym zestawie danych po 10 epokach utrata ostatniej epoki wyniesie 0,0748, a dokładność 0,9863.

Po zapisaniu, usunięciu i ponownym załadowaniu modelu utrata i dokładność modelu wytrenowanego na drugim zbiorze danych będzie wynosić odpowiednio 0,1711 i 0,9504.

Czy jest to spowodowane nowymi danymi szkoleniowymi, czy całkowicie ponownie przeszkolonym modelem?

"""
Model by: http://machinelearningmastery.com/
"""
# load (downloaded if needed) the MNIST dataset
import numpy
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.utils import np_utils
from keras.models import load_model
numpy.random.seed(7)

def baseline_model():
    model = Sequential()
    model.add(Dense(num_pixels, input_dim=num_pixels, init='normal', activation='relu'))
    model.add(Dense(num_classes, init='normal', activation='softmax'))
    model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model

if __name__ == '__main__':
    # load data
    (X_train, y_train), (X_test, y_test) = mnist.load_data()

    # flatten 28*28 images to a 784 vector for each image
    num_pixels = X_train.shape[1] * X_train.shape[2]
    X_train = X_train.reshape(X_train.shape[0], num_pixels).astype('float32')
    X_test = X_test.reshape(X_test.shape[0], num_pixels).astype('float32')
    # normalize inputs from 0-255 to 0-1
    X_train = X_train / 255
    X_test = X_test / 255
    # one hot encode outputs
    y_train = np_utils.to_categorical(y_train)
    y_test = np_utils.to_categorical(y_test)
    num_classes = y_test.shape[1]

    # build the model
    model = baseline_model()

    #Partly train model
    dataset1_x = X_train[:3000]
    dataset1_y = y_train[:3000]
    model.fit(dataset1_x, dataset1_y, nb_epoch=10, batch_size=200, verbose=2)

    # Final evaluation of the model
    scores = model.evaluate(X_test, y_test, verbose=0)
    print("Baseline Error: %.2f%%" % (100-scores[1]*100))

    #Save partly trained model
    model.save('partly_trained.h5')
    del model

    #Reload model
    model = load_model('partly_trained.h5')

    #Continue training
    dataset2_x = X_train[3000:]
    dataset2_y = y_train[3000:]
    model.fit(dataset2_x, dataset2_y, nb_epoch=10, batch_size=200, verbose=2)
    scores = model.evaluate(X_test, y_test, verbose=0)
    print("Baseline Error: %.2f%%" % (100-scores[1]*100))
Wilmar van Ommeren
źródło
3
Czy to przetestowałeś? Nie widzę powodów, żeby to nie zadziałało.
maz
Teraz widzę, że moja dokładność spada o około 10 procent po załadowaniu modelu (tylko w pierwszych epokach). Jeśli ponowne ładowanie działa, jest to oczywiście spowodowane nowymi danymi szkoleniowymi. Ale chcę się tylko upewnić, że tak jest.
Wilmar van Ommeren
7
Czy zapisujesz swój model bezpośrednio za pomocą model.save, czy używasz modelu punktu kontrolnego ( keras.io/callbacks/#example-model-checkpoints )? Jeśli używasz model.save, czy byłaby szansa, że ​​zapisujesz najnowszy model (tj. Ostatnią epokę) zamiast najlepszego (najniższy błąd)? Czy możesz podać rzeczywisty kod?
maz
Zapisuję mój najnowszy model, nie najlepszy (do tej pory nie wiedziałem, że to możliwe). Przygotuję kod
Wilmar van Ommeren
3
Czy nie mógłbyś więc przeładować tego i kontynuować trening na tych samych danych pociągu? Powinno to zapewnić, że ponowne ładowanie jest w porządku, jeśli wyniki byłyby porównywalne.
Marcin Możejko

Odpowiedzi:

36

Właściwie - model.savezapisuje wszystkie informacje potrzebne do ponownego uruchomienia szkolenia w Twoim przypadku. Jedyną rzeczą, która może zostać zepsuta przez ponowne załadowanie modelu, jest stan optymalizatora. Aby to sprawdzić - spróbuj saveponownie załadować model i wytrenować go na danych treningowych.

Marcin Możejko
źródło
1
@Marcin: czy używając keras save(), zapisuje najlepszy wynik (najmniejszą stratę) modelu, czy też ostatni wynik (ostatnią aktualizację) modelu? dzięki
Lion Lai
5
Ostatnia aktualizacja. Model wywołania zwrotnego punktu kontrolnego służy do zapisania najlepszego.
Holi
2
@Khaj Czy odnosisz się do tego keras.io/callbacks/#modelcheckpoint ? Wygląda na to, że domyślnie zapisuje ostatnią aktualizację (nie najlepszą); najlepszy jest zapisywany tylko wtedy, gdysave_best_only=True jest ustawiony jawnie.
flow2k
9

Większość z powyższych odpowiedzi dotyczyła ważnych punktów. Jeśli korzystasz z najnowszej wersji Tensorflow ( TF2.1lub nowszej), pomoże Ci następujący przykład. Modelowa część kodu pochodzi ze strony internetowej Tensorflow.

import tensorflow as tf
from tensorflow import keras
mnist = tf.keras.datasets.mnist

(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

def create_model():
  model = tf.keras.models.Sequential([
    tf.keras.layers.Flatten(input_shape=(28, 28)),
    tf.keras.layers.Dense(512, activation=tf.nn.relu),  
    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.Dense(10, activation=tf.nn.softmax)
    ])

  model.compile(optimizer='adam', loss='sparse_categorical_crossentropy',metrics=['accuracy'])
  return model

# Create a basic model instance
model=create_model()
model.fit(x_train, y_train, epochs = 10, validation_data = (x_test,y_test),verbose=1)

Prosimy o zapisanie modelu w formacie * .tf. Z mojego doświadczenia wynika, że ​​jeśli masz zdefiniowany jakikolwiek custom_loss, format * .h5 nie zapisze statusu optymalizatora, a zatem nie będzie służył twojemu celowi, jeśli chcesz przekwalifikować model z miejsca, w którym zostawiliśmy.

# saving the model in tensorflow format
model.save('./MyModel_tf',save_format='tf')


# loading the saved model
loaded_model = tf.keras.models.load_model('./MyModel_tf')

# retraining the model
loaded_model.fit(x_train, y_train, epochs = 10, validation_data = (x_test,y_test),verbose=1)

Takie podejście spowoduje ponowne uruchomienie szkolenia, które zostawiliśmy przed zapisaniem modelu. Jak wspomniano przez innych, jeśli chcesz zapisać wagi najlepszego modelu lub chcesz zapisać ciężary modelu każdej epoce trzeba użyć Keras callbacks funkcyjny (ModelCheckpoint) z opcjami takimi jak save_weights_only=True, save_freq='epoch'i save_best_only.

Aby uzyskać więcej informacji, sprawdź tutaj i inny przykład tutaj .

Vishnuvardhan Janapati
źródło
1
fajnie, wygląda to bardzo obiecująco - dzięki za informację. w tym przykładzie wydaje mi się, że ponownie trenujesz model na tych samych danych, które były używane do uczenia. jeśli tak, pomyślałbym, że właściwym podejściem byłoby załadowanie nowego podzbioru danych szkoleniowych w celu ponownego przeszkolenia (w celu odzwierciedlenia nowych informacji wprowadzanych do procesu)?
bibzzzz
1
@bibzzzz Zgadzam się z tobą. Bardzo dobry komentarz. Chciałem zademonstrować przekwalifikowanie na tych samych danych, aby poprawić wydajność. Streszczenie wyraźnie pokazuje poprawę wydajności, gdy została zatrzymana przed zapisaniem modelu. Całkowicie zgodziłbym się z Tobą na przekwalifikowanie się na innych danych i spróbuję później. Dzięki!
Vishnuvardhan Janapati
wspaniale - bardzo ładnie to zademonstrowałeś, dziękuję.
bibzzzz
8

Problem może polegać na tym, że używasz innego optymalizatora - lub innych argumentów do swojego optymalizatora. Po prostu miałem ten sam problem z niestandardowym, wstępnie wytrenowanym modelem, używając

reduce_lr = ReduceLROnPlateau(monitor='loss', factor=lr_reduction_factor,
                              patience=patience, min_lr=min_lr, verbose=1)

dla modelu pretrained, gdzie pierwotny współczynnik uczenia się zaczyna się od 0,0003 i podczas przedtreningowego jest redukowany do min_learning rate, który wynosi 0,000003

Właśnie skopiowałem tę linię do skryptu, który używa wstępnie wytrenowanego modelu i uzyskałem naprawdę złe dokładności. Dopóki nie zauważyłem, że ostatnim tempem uczenia się wstępnie trenowanego modelu był minimalny współczynnik uczenia, czyli 0,000003. A jeśli zacznę od tego współczynnika uczenia się, na początku otrzymam dokładnie takie same dokładności, jak dane wyjściowe wstępnie wytrenowanego modelu - co ma sens, ponieważ zaczyna się od wskaźnika uczenia się, który jest 100 razy większy niż ostatni współczynnik uczenia się używany w model spowoduje ogromne przeregulowanie GD, a tym samym znaczne zmniejszenie dokładności.

Wolfgang
źródło
2

Zauważ, że Keras czasami ma problemy z wczytanymi modelami, jak tutaj . Może to wyjaśniać przypadki, w których nie zaczynasz od tej samej wytrenowanej dokładności.

shahar_m
źródło
1

Wszystko powyżej pomaga, musisz wznowić od tego samego współczynnika uczenia () co LR, kiedy model i wagi zostały zapisane. Ustaw go bezpośrednio na optymalizatorze.

Należy pamiętać, że poprawa od tego miejsca nie jest gwarantowana, ponieważ model mógł osiągnąć lokalne minimum, które może być globalne. Nie ma sensu wznawiać modelu w celu wyszukania innego lokalnego minimum, chyba że zamierzasz zwiększyć tempo uczenia się w kontrolowany sposób i popchnąć model do możliwie lepszego minimum niedaleko.

flowgrad
źródło
Dlaczego? Czy nie mogę użyć mniejszego LR niż wcześniej?
lte__,
W rzeczywistości ciągłe szkolenie MOŻE doprowadzić Cię do lepszego modelu, jeśli otrzymasz więcej danych. Jest więc sens, aby wznowić model w celu wyszukania innego lokalnego minimum.
Corey Levinson