Opas mukautettujen TensorFlow/Keras-soittojen kirjoittamiseen

esittely

Oletetaan, että haluat Keras-mallisi käyttäytyvän tietyllä tavalla harjoittelun, arvioinnin tai ennustamisen aikana. Saatat esimerkiksi haluta tallentaa mallisi joka harjoittelujaksolla. Yksi tapa tehdä tämä on takaisinsoittojen käyttö.

Yleensä takaisinkutsut ovat toimintoja, joita kutsutaan, kun jokin tapahtuma tapahtuu, ja ne välitetään argumentteina muille funktioille. Keran tapauksessa ne ovat työkalu mallin käyttäytymisen mukauttamiseen – olipa kyse sitten harjoittelusta, arvioinnista tai päättelystä. Jotkut sovellukset ovat kirjaamista, mallin pysyvyyttä, varhaista pysäyttämistä tai oppimisnopeuden muuttamista. Tämä tehdään välittämällä takaisinsoittoluettelo argumenteiksi keras.Model.fit(),keras.Model.evaluate() or keras.Model.predict().

Joitakin yleisiä takaisinsoittojen käyttötapauksia ovat oppimisnopeuden muuttaminen, kirjaaminen, seuranta ja koulutuksen varhainen lopettaminen. Kerasissa on useita sisäänrakennettuja yksityiskohtaisia ​​takaisinsoittoja
dokumentaatiossa
.

Jotkin tarkemmat sovellukset saattavat kuitenkin vaatia mukautetun takaisinsoiton. Esimerkiksi, Oppimisnopeuden lämmittely kosinivaimennuksella pitoajan jälkeen ei ole tällä hetkellä sisäänrakennettu, mutta sitä käytetään laajasti ja se on otettu käyttöön ajastimena.

Takaisinsoittoluokka ja sen menetelmät

Kerasilla on erityinen takaisinsoittoluokka, keras.callbacks.Callback, menetelmillä, joita voidaan kutsua koulutuksen, testauksen ja päättelyn aikana globaalilla, erä- tai aikakausitasolla. Jotta luoda mukautettuja takaisinsoittoja, meidän on luotava alaluokka ja ohitettava nämä menetelmät.

- keras.callbacks.Callback luokassa on kolmenlaisia ​​menetelmiä:

  • globaalit menetelmät: kutsutaan alussa tai lopussa fit(), evaluate() ja predict().
  • erätason menetelmät: kutsutaan erän käsittelyn alussa tai lopussa.
  • epoch-tason menetelmät: kutsutaan harjoituserän alussa tai lopussa.

Huomautus: Jokaisella menetelmällä on pääsy saneluun, jota kutsutaan logs. Avaimet ja arvot logs ovat kontekstuaalisia – ne riippuvat tapahtumasta, joka kutsuu menetelmää. Lisäksi meillä on pääsy malliin kunkin menetelmän kautta self.model määrite.

Katsotaanpa kolmea mukautettua takaisinsoittoesimerkkiä – yksi koulutusta, yksi arviointia ja yksi ennustamista varten. Jokainen tulostaa jokaisessa vaiheessa, mitä mallimme tekee ja mitä lokeja meillä on pääsy. Tämä auttaa ymmärtämään, mitä mukautetuilla takaisinkutsuilla voi tehdä kussakin vaiheessa.

Aloitetaan määrittelemällä lelumalli:

import tensorflow as tf
from tensorflow import keras
import numpy as np

model = keras.Sequential()
model.add(keras.layers.Dense(10, input_dim = 1, activation='relu'))
model.add(keras.layers.Dense(10, activation='relu'))
model.add(keras.layers.Dense(1))
model.compile(
    optimizer=keras.optimizers.RMSprop(learning_rate=0.1),
    loss = "mean_squared_error",
    metrics = ["mean_absolute_error"]
)

x = np.random.uniform(low = 0, high = 10, size = 1000)
y = x**2
x_train, x_test = (x[:900],x[900:])
y_train, y_test = (y[:900],y[900:])

Mukautetun koulutuksen takaisinsoitto

Ensimmäinen takaisinsoittomme on soitettava harjoituksen aikana. Otetaan alaluokka Callback luokka:

class TrainingCallback(keras.callbacks.Callback):
    def __init__(self):
        self.tabulation = {"train":"", 'batch': " "*8, 'epoch':" "*4}
    def on_train_begin(self, logs=None):
        tab = self.tabulation['train']
        print(f"{tab}Training!")
        print(f"{tab}available logs: {logs}")

    def on_train_batch_begin(self, batch, logs=None):
        tab = self.tabulation['batch']
        print(f"{tab}Batch {batch}")
        print(f"{tab}available logs: {logs}")

    def on_train_batch_end(self, batch, logs=None):
        tab = self.tabulation['batch']
        print(f"{tab}End of Batch {batch}")
        print(f"{tab}available logs: {logs}")

    def on_epoch_begin(self, epoch, logs=None):
        tab = self.tabulation['epoch']
        print(f"{tab}Epoch {epoch} of training")
        print(f"{tab}available logs: {logs}")

    def on_epoch_end(self, epoch, logs=None):
        tab = self.tabulation['epoch']
        print(f"{tab}End of Epoch {epoch} of training")
        print(f"{tab}available logs: {logs}")

    def on_train_end(self, logs=None):
        tab = self.tabulation['train']
        print(f"{tab}Finishing training!")
        print(f"{tab}available logs: {logs}")

Jos jotakin näistä menetelmistä ei ohiteta – oletustoiminta jatkuu entiseen tapaan. Esimerkissämme tulostetaan vain käytettävissä olevat lokit ja taso, jolla takaisinsoittoa käytetään, asianmukaisella sisennyksellä.

Katsotaanpa ulostuloja:

model.fit(
    x_train,
    y_train,
    batch_size=500,
    epochs=2,
    verbose=0,
    callbacks=[TrainingCallback()],
)
Training!
available logs: {}
    Epoch 0 of training
    available logs: {}
        Batch 0
        available logs: {}
        End of Batch 0
        available logs: {'loss': 2172.373291015625, 'mean_absolute_error': 34.79669952392578}
        Batch 1
        available logs: {}
        End of Batch 1
        available logs: {'loss': 2030.1309814453125, 'mean_absolute_error': 33.30256271362305}
    End of Epoch 0 of training
    available logs: {'loss': 2030.1309814453125, 'mean_absolute_error': 33.30256271362305}
    Epoch 1 of training
    available logs: {}
        Batch 0
        available logs: {}
        End of Batch 0
        available logs: {'loss': 1746.2772216796875, 'mean_absolute_error': 30.268001556396484}
        Batch 1
        available logs: {}
        End of Batch 1
        available logs: {'loss': 1467.36376953125, 'mean_absolute_error': 27.10252571105957}
    End of Epoch 1 of training
    available logs: {'loss': 1467.36376953125, 'mean_absolute_error': 27.10252571105957}
Finishing training!
available logs: {'loss': 1467.36376953125, 'mean_absolute_error': 27.10252571105957}


Huomaa, että voimme seurata jokaisessa vaiheessa, mitä malli tekee ja mihin mittareihin meillä on pääsy. Jokaisen erän ja aikakauden lopussa meillä on pääsy näytteen sisäiseen häviöfunktioon ja mallimme mittareihin.

Mukautetun arvioinnin takaisinsoitto

Nyt soitetaan Model.evaluate() menetelmä. Näemme, että erän lopussa meillä on pääsy menetysfunktioon ja senhetkisiin mittareihin, ja arvioinnin lopussa meillä on pääsy kokonaishäviöön ja mittareihin:

class TestingCallback(keras.callbacks.Callback):
    def __init__(self):
          self.tabulation = {"test":"", 'batch': " "*8}
      
    def on_test_begin(self, logs=None):
        tab = self.tabulation['test']
        print(f'{tab}Evaluating!')
        print(f'{tab}available logs: {logs}')

    def on_test_end(self, logs=None):
        tab = self.tabulation['test']
        print(f'{tab}Finishing evaluation!')
        print(f'{tab}available logs: {logs}')

    def on_test_batch_begin(self, batch, logs=None):
        tab = self.tabulation['batch']
        print(f"{tab}Batch {batch}")
        print(f"{tab}available logs: {logs}")

    def on_test_batch_end(self, batch, logs=None):
        tab = self.tabulation['batch']
        print(f"{tab}End of batch {batch}")
        print(f"{tab}available logs: {logs}")
res = model.evaluate(
    x_test, y_test, batch_size=100, verbose=0, callbacks=[TestingCallback()]
)
Evaluating!
available logs: {}
        Batch 0
        available logs: {}
        End of batch 0
        available logs: {'loss': 382.2723083496094, 'mean_absolute_error': 14.069927215576172}
Finishing evaluation!
available logs: {'loss': 382.2723083496094, 'mean_absolute_error': 14.069927215576172}

Mukautettu ennakoiva takaisinsoitto

Lopuksi soitetaan Model.predict() menetelmä. Huomaa, että jokaisen erän lopussa meillä on pääsy mallimme ennustettuihin ulostuloihin:

class PredictionCallback(keras.callbacks.Callback):
    def __init__(self):
        self.tabulation = {"prediction":"", 'batch': " "*8}

    def on_predict_begin(self, logs=None):
        tab = self.tabulation['prediction']
        print(f"{tab}Predicting!")
        print(f"{tab}available logs: {logs}")

    def on_predict_end(self, logs=None):
        tab = self.tabulation['prediction']
        print(f"{tab}End of Prediction!")
        print(f"{tab}available logs: {logs}")

    def on_predict_batch_begin(self, batch, logs=None):
        tab = self.tabulation['batch']
        print(f"{tab}batch {batch}")
        print(f"{tab}available logs: {logs}")

    def on_predict_batch_end(self, batch, logs=None):
        tab = self.tabulation['batch']
        print(f"{tab}End of batch {batch}")
        print(f"{tab}available logs:n {logs}")
res = model.predict(x_test[:10],
                    verbose = 0, 
                    callbacks=[PredictionCallback()])

Tutustu käytännönläheiseen, käytännölliseen Gitin oppimisoppaaseemme, jossa on parhaat käytännöt, alan hyväksymät standardit ja mukana tuleva huijauslehti. Lopeta Git-komentojen googlailu ja oikeastaan oppia se!

Predicting!
available logs: {}
        batch 0
        available logs: {}
        End of batch 0
        available logs:
 {'outputs': array([[ 7.743822],
       [27.748264],
       [33.082104],
       [26.530678],
       [27.939169],
       [18.414223],
       [42.610645],
       [36.69335 ],
       [13.096557],
       [37.120853]], dtype=float32)}
End of Prediction!
available logs: {}

Näiden avulla voit mukauttaa käyttäytymistä, määrittää seurantaa tai muuten muuttaa koulutus-, arviointi- tai päättelyprosesseja. Vaihtoehto alilähetykselle on käyttää LambdaCallback.

LambaCallbackin käyttö

Yksi Kerasin sisäänrakennetuista takaisinkutsuista on LambdaCallback luokkaa. Tämä takaisinsoitto hyväksyy toiminnon, joka määrittää, miten se käyttäytyy ja mitä se tekee! Tietyssä mielessä sen avulla voit käyttää mitä tahansa mielivaltaista toimintoa takaisinsoittona, jolloin voit luoda mukautettuja takaisinsoittoja.

Luokassa on valinnaiset parametrit:
-on_epoch_begin

  • on_epoch_end
  • on_batch_begin
  • on_batch_end
  • on_train_begin
  • on_train_end

Jokainen parametri hyväksyy toiminto jota kutsutaan vastaavassa mallitapahtumassa. Esimerkki: soitetaan takaisin sähköpostin lähettämiseksi, kun malli on lopettanut harjoittelun:

import smtplib
from email.message import EmailMessage

def send_email(logs): 
    msg = EmailMessage()
    content = f"""The model has finished training."""
    for key, value in logs.items():
      content = content + f"n{key}:{value:.2f}"
    msg.set_content(content)
    msg['Subject'] = f'Training report'
    msg['From'] = '[email protected]'
    msg['To'] = 'receiver-email'

    s = smtplib.SMTP('smtp.gmail.com', 587)
    s.starttls()
    s.login("[email protected]", "your-gmail-app-password")
    s.send_message(msg)
    s.quit()

lambda_send_email = lambda logs : send_email(logs)

email_callback = keras.callbacks.LambdaCallback(on_train_end = lambda_send_email)

model.fit(
    x_train,
    y_train,
    batch_size=100,
    epochs=1,
    verbose=0,
    callbacks=[email_callback],
)

Voit tehdä mukautetun takaisinsoittomme käyttämällä LambdaCallback, meidän on vain toteutettava funktio, jota haluamme kutsua, kääri se muotoon a lambda toiminto ja välitä se
LambdaCallback luokka parametrina.

Takaisinsoitto mallikoulutuksen visualisointiin

Tässä osiossa annamme esimerkin mukautetusta takaisinkutsusta, joka saa animaation mallimme suorituskyvystä paranemaan harjoittelun aikana. Tätä varten tallennamme lokien arvot jokaisen erän loppuun. Sitten harjoitussilmukan lopussa luomme animaation käyttämällä matplotlib.

Visualisoinnin parantamiseksi häviö ja mittarit piirretään log-asteikossa:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
from IPython import display

class TrainingAnimationCallback(keras.callbacks.Callback):
    def __init__(self, duration = 40, fps = 1000/25):
        self.duration = duration
        self.fps = fps
        self.logs_history = []

    def set_plot(self):   
        self.figure = plt.figure()
        
        plt.xticks(
            range(0,self.params['steps']*self.params['epochs'], self.params['steps']),
            range(0,self.params['epochs']))
        plt.xlabel('Epoch')
        plt.ylabel('Loss & Metrics ($Log_{10}$ scale)')

        self.plot = {}
        for metric in self.model.metrics_names:
          self.plot[metric], = plt.plot([],[], label = metric)
          
        max_y = [max(log.values()) for log in self.logs_history]
        
        self.title = plt.title(f'batches:0')
        plt.xlim(0,len(self.logs_history)) 
        plt.ylim(0,max(max_y))

           
        plt.legend(loc='upper right')
  
    def animation_function(self,frame):
        batch = frame % self.params['steps']
        self.title.set_text(f'batch:{batch}')
        x = list(range(frame))
        
        for metric in self.model.metrics_names:
            y = [log[metric] for log in self.logs_history[:frame]]
            self.plot[metric].set_data(x,y)
        
    def on_train_batch_end(self, batch, logs=None):
        logarithm_transform = lambda item: (item[0], np.log(item[1]))
        logs = dict(map(logarithm_transform,logs.items()))
        self.logs_history.append(logs)
       
    def on_train_end(self, logs=None):
        self.set_plot()
        num_frames = int(self.duration*self.fps)
        num_batches = self.params['steps']*self.params['epochs']
        selected_batches = range(0, num_batches , num_batches//num_frames )
        interval = 1000*(1/self.fps)
        anim_created = FuncAnimation(self.figure, 
                                     self.animation_function,
                                     frames=selected_batches,
                                     interval=interval)
        video = anim_created.to_html5_video()
        
        html = display.HTML(video)
        display.display(html)
        plt.close()

Käytämme samaa mallia kuin ennenkin, mutta lisää koulutusnäytteitä:

import tensorflow as tf
from tensorflow import keras
import numpy as np

model = keras.Sequential()
model.add(keras.layers.Dense(10, input_dim = 1, activation='relu'))
model.add(keras.layers.Dense(10, activation='relu'))
model.add(keras.layers.Dense(1))
model.compile(
    optimizer=keras.optimizers.RMSprop(learning_rate=0.1),
    loss = "mean_squared_error",
    metrics = ["mean_absolute_error"]
)

def create_sample(sample_size, train_test_proportion = 0.9):
    x = np.random.uniform(low = 0, high = 10, size = sample_size)
    y = x**2
    train_test_split = int(sample_size*train_test_proportion)
    x_train, x_test = (x[:train_test_split],x[train_test_split:])
    y_train, y_test = (y[:train_test_split],y[train_test_split:])
    return (x_train,x_test,y_train,y_test)

x_train,x_test,y_train,y_test = create_sample(35200)


model.fit(
    x_train,
    y_train,
    batch_size=32,
    epochs=2,
    verbose=0,
    callbacks=[TrainingAnimationCallback()],
)

Tuotteemme on animaatio mittareista ja häviöfunktiosta, kun ne muuttuvat koulutusprosessin aikana:

Selaimesi ei tue HTML-videoita.

Yhteenveto

Tässä oppaassa olemme tutustuneet mukautettujen takaisinsoittojen käyttöön Kerasissa.
Mukautettujen takaisinsoittojen toteuttamiseen on kaksi vaihtoehtoa – alaluokituksen kautta keras.callbacks.Callback luokkaa tai käyttämällä keras.callbacks.LambdaCallback luokka.

Olemme nähneet yhden käytännön esimerkin käytöstä LambdaCallbacksähköpostin lähettämiseen koulutussilmukan lopussa ja yksi esimerkki alaluokituksesta Callback luokka, joka luo animaation harjoitussilmukasta.

Althoug Kerasissa on monia sisäänrakennettuja takaisinsoittoja, joten mukautetun takaisinkutsun toteuttaminen voi olla hyödyllistä tarkemmissa sovelluksissa.

Aikaleima:

Lisää aiheesta Stackabus