Skip to content

Implementazione di una rete neurale personalizzata su MNIST con Tensorflow 2.0?

Abbiamo bisogno del tuo aiuto per estendere le nostre recensioni di informatica.

Soluzione:

Mi sono chiesto da dove iniziare con la tua domanda multipla e ho deciso di farlo con un'affermazione:

Il tuo codice non dovrebbe assolutamente avere questo aspetto e non si avvicina alle attuali best practice di Tensorflow..

Mi dispiace, ma il debug passo per passo è una perdita di tempo per tutti e non gioverebbe a nessuno dei due.

Ora passiamo al terzo punto:

  1. C'è qualcos'altro nel mio codice che posso ottimizzare ulteriormente?
    come ad esempio l'uso del decoratore @tf.function di tensorflow 2.x, ecc.
    ecc.)

Sì, si può usare tensorflow2.0 e sembra che si stia fuggendo da queste (tf.function il decoratore non serve a nulla, lasciatelo per il momento).

Seguire le nuove linee guida allevierebbe anche i problemi relativi al 5° punto, cioè:

  1. Vorrei anche un aiuto per scrivere questo codice in modo più generalizzato, in modo che
    di poter facilmente implementare altre reti come le ConvNet (cioè Conv, MaxPool
    ecc.) basate su questo codice.

poiché è stato progettato specificamente per questo. Dopo una piccola introduzione, cercherò di farvi capire questi concetti in pochi passi:

1. Dividere il programma in parti logiche

Tensorflow ha fatto molti danni quando si tratta di leggibilità del codice; tutto in tf1.x era di solito schiacciato in un unico punto, globali seguiti da una definizione di funzione seguita da un'altra globale o forse dal caricamento dei dati, tutto sommato un casino. Non è colpa degli sviluppatori, perché il progetto del sistema incoraggiava queste azioni.

Ora, in tf2.0 il programmatore è incoraggiato a dividere il suo lavoro in modo simile alla struttura che si può vedere in pytorch, chainer e in altri framework più semplici da usare.

1.1 Caricamento dei dati

Eravate sulla buona strada con i dataset di Tensorflow, ma vi siete allontanati senza un motivo apparente.

Ecco il vostro codice con il commento di ciò che sta accadendo:

# You already have tf.data.Dataset objects after load
(x_train, y_train), (x_test, y_test) = tfds.load('mnist', split=['train', 'test'], 
                                                  batch_size=-1, as_supervised=True)

# But you are reshaping them in a strange manner...
x_train = tf.reshape(x_train, shape=(x_train.shape[0], 784))
x_test  = tf.reshape(x_test, shape=(x_test.shape[0], 784))

# And building from slices...
ds_train = tf.data.Dataset.from_tensor_slices((x_train, y_train))
# Unreadable rescaling (there are built-ins for that)

Si può facilmente generalizzare questa idea per qualsiasi set di dati, inserendolo in un modulo separato, ad esempio datasets.py:

import tensorflow as tf
import tensorflow_datasets as tfds

class ImageDatasetCreator:
    @classmethod
    # More portable and readable than dividing by 255
    def _convert_image_dtype(cls, dataset):
        return dataset.map(
            lambda image, label: (
                tf.image.convert_image_dtype(image, tf.float32),
                label,
            )
        )

    def __init__(self, name: str, batch: int, cache: bool = True, split=None):
        # Load dataset, every dataset has default train, test split
        dataset = tfds.load(name, as_supervised=True, split=split)
        # Convert to float range
        try:
            self.train = ImageDatasetCreator._convert_image_dtype(dataset["train"])
            self.test = ImageDatasetCreator._convert_image_dtype(dataset["test"])
        except KeyError as exception:
            raise ValueError(
                f"Dataset {name} does not have train and test, write your own custom dataset handler."
            ) from exception

        if cache:
            self.train = self.train.cache()  # speed things up considerably
            self.test = self.test.cache()

        self.batch: int = batch

    def get_train(self):
        return self.train.shuffle().batch(self.batch).repeat()

    def get_test(self):
        return self.test.batch(self.batch).repeat()

Quindi ora è possibile caricare più di mnist usando un semplice comando:

from datasets import ImageDatasetCreator

if __name__ == "__main__":
    dataloader = ImageDatasetCreator("mnist", batch=64, cache = True)
    train, test = dataloader.get_train(), dataloader.get_test()

E si può usare qualsiasi nome diverso da mnist per caricare i set di dati d'ora in poi.

Per favore, smettila di fare di tutto ciò che riguarda il deep learning un unico script, anche tu sei un programmatore..

1.2 Creazione del modello

Da quando tf2.0 si può procedere in due modi, a seconda della complessità dei modelli:

  • tensorflow.keras.models.Sequential - questo modo è stato mostrato da @Stewart_R, non c'è bisogno di ripetere i suoi punti. Si usa per i modelli più semplici (si dovrebbe usare questo con il feedforward).
  • Ereditare tensorflow.keras.Model e scrivendo un modello personalizzato. Questo dovrebbe essere usato quando si ha un qualche tipo di logica all'interno del modulo o è più complicato (cose come le reti ResNet, le reti multipath, ecc.). Tutto sommato è più leggibile e personalizzabile.

Il tuo Model ha cercato di assomigliare a qualcosa del genere, ma è andato di nuovo a rotoli; backprop non fa assolutamente parte del modello stesso, né lo è loss o accuracy, separarli in un altro modulo o funzione, sicuramente non è un membro!

Detto questo, codifichiamo la rete usando il secondo approccio (si dovrebbe mettere questo codice in model.py per brevità). Prima di questo, codificherò YourDense lo strato feedforward da zero, ereditando da tf.keras.Layers (questo potrebbe andare in layers.py ):

import tensorflow as tf

class YourDense(tf.keras.layers.Layer):
    def __init__(self, units):
        # It's Python 3, you don't have to specify super parents explicitly
        super().__init__()
        self.units = units

    # Use build to create variables, as shape can be inferred from previous layers
    # If you were to create layers in __init__, one would have to provide input_shape
    # (same as it occurs in PyTorch for example)
    def build(self, input_shape):
        # You could use different initializers here as well
        self.kernel = self.add_weight(
            shape=(input_shape[-1], self.units),
            initializer="random_normal",
            trainable=True,
        )
        # You could define bias in __init__ as well as it's not input dependent
        self.bias = self.add_weight(shape=(self.units,), initializer="random_normal")
        # Oh, trainable=True is default

    def call(self, inputs):
        # Use overloaded operators instead of tf.add, better readability
        return tf.matmul(inputs, self.kernel) + self.bias

Per quanto riguarda il tuo

  1. Come aggiungere un livello di dropout e di normalizzazione dei lotti in questa
    implementazione personalizzata? (cioè facendolo funzionare sia per il tempo di addestramento che per quello di prova)

Suppongo che vogliate creare un'implementazione personalizzata di questi livelli.
Altrimenti, si può semplicemente importare from tensorflow.keras.layers import Dropout e usarlo dove si vuole, come ha sottolineato @Leevo.
Dropout invertito con comportamento diverso durante train e test sotto:

class CustomDropout(layers.Layer):
    def __init__(self, rate, **kwargs):
        super().__init__(**kwargs)
        self.rate = rate

    def call(self, inputs, training=None):
        if training:
            # You could simply create binary mask and multiply here
            return tf.nn.dropout(inputs, rate=self.rate)
        # You would need to multiply by dropout rate if you were to do that
        return inputs

Strati presi da qui e modificati per adattarsi meglio allo scopo della presentazione.

Ora è possibile creare finalmente il modello (semplice doppio feedforward):

import tensorflow as tf

from layers import YourDense

class Model(tf.keras.Model):
    def __init__(self):
        super().__init__()
        # Use Sequential here for readability
        self.network = tf.keras.Sequential(
            [YourDense(100), tf.keras.layers.ReLU(), YourDense(10)]
        )

    def call(self, inputs):
        # You can use non-parametric layers inside call as well
        flattened = tf.keras.layers.Flatten()(inputs)
        return self.network(flattened)

Naturalmente, si dovrebbero usare i built-in il più possibile nelle implementazioni generali.

Questa struttura è abbastanza estensibile, quindi la generalizzazione a reti convoluzionali, reti di risonanza, reti di senet, ecc. dovrebbe essere fatta tramite questo modulo.. Si possono leggere ulteriori informazioni qui.

Penso che soddisfi il vostro 5° punto:

  1. Voglio anche un aiuto per scrivere questo codice in modo più generalizzato in modo che
    posso facilmente implementare altre reti come le ConvNets (cioè Conv, MaxPool
    ecc.) basate su questo codice.

Ultima cosa, potreste dover usare model.build(shape) per costruire il grafico del modello.

model.build((None, 28, 28, 1))

Questo sarebbe per MNIST 28x28x1 di MNIST, dove None sta per lotto.

1.3 Formazione

Ancora una volta, l'addestramento può essere effettuato in due modi distinti:

  • Keras standard model.fit(dataset) - utile per compiti semplici come la classificazione
  • tf.GradientTape - schemi di addestramento più complicati, l'esempio più importante è quello delle reti avversarie generative, in cui due modelli ottimizzano obiettivi ortogonali giocando a minmax.

Come sottolineato ancora una volta da @Leevo, se si vuole usare il secondo modo, non si potranno usare semplicemente i callback forniti da Keras, quindi consiglierei di attenersi alla prima opzione quando possibile.

In teoria, si potrebbero richiamare manualmente le funzioni di callback come on_batch_begin() e altre dove necessario, ma sarebbe macchinoso e non sono sicuro di come funzionerebbe.

Per quanto riguarda la prima opzione, si può usare tf.data.Dataset direttamente con fit. Eccolo presentato all'interno di un altro modulo (preferibilmente train.py):

def train(
    model: tf.keras.Model,
    path: str,
    train: tf.data.Dataset,
    epochs: int,
    steps_per_epoch: int,
    validation: tf.data.Dataset,
    steps_per_validation: int,
    stopping_epochs: int,
    optimizer=tf.optimizers.Adam(),
):
    model.compile(
        optimizer=optimizer,
        # I used logits as output from the last layer, hence this
        loss=tf.losses.SparseCategoricalCrossentropy(from_logits=True),
        metrics=[tf.metrics.SparseCategoricalAccuracy()],
    )

    model.fit(
        train,
        epochs=epochs,
        steps_per_epoch=steps_per_epoch,
        validation_data=validation,
        validation_steps=steps_per_validation,
        callbacks=[
            # Tensorboard logging
            tf.keras.callbacks.TensorBoard(
                pathlib.Path("logs")
                / pathlib.Path(datetime.datetime.now().strftime("%Y%m%d-%H%M%S")),
                histogram_freq=1,
            ),
            # Early stopping with best weights preserving
            tf.keras.callbacks.EarlyStopping(
                monitor="val_sparse_categorical_accuracy",
                patience=stopping_epochs,
                restore_best_weights=True,
            ),
        ],
    )
    model.save(path)

L'approccio più complicato è molto simile (quasi un copia e incolla) a PyTorch quindi, se avete familiarità con questi, non dovrebbe essere un problema.

Si possono trovare esempi in tutto il testo tf2.0 documentazione, ad esempio qui o qui.

2. Altre cose

2.1 Domande senza risposta

  1. C'è qualcos'altro nel codice che posso ottimizzare ulteriormente in questo codice?
    questo codice? Ad esempio (facendo uso del decoratore di funzioni @tf.function di tensorflow 2.x
    ecc.)

Sopra trasforma già il modello in grafici, quindi non credo che si possa trarre vantaggio dal chiamarlo in questo caso. E l'ottimizzazione prematura è la radice di tutti i mali, ricordate di misurare il vostro codice prima di farlo.

Si otterrebbe molto di più con una corretta memorizzazione nella cache dei dati (come descritto all'inizio di #1.1) e una buona pipeline piuttosto che con queste.

  1. Ho anche bisogno di un modo per estrarre tutti i pesi finali per tutti i livelli dopo l'addestramento
    dopo l'addestramento, in modo da poterli tracciare e controllare le loro distribuzioni. Per
    controllare problemi come la scomparsa o l'esplosione del gradiente.

Come sottolineato da @Leevo sopra,

weights = model.get_weights()

fornisce i pesi. Si possono trasformare in np.array e tracciare utilizzando seaborn, matplotlib, analizzare, controllare o qualsiasi altra cosa vogliate.

2.2 Mettere tutto insieme

Nel complesso, il vostro main.py (o entrypoint o qualcosa di simile) consisterebbe in questo (più o meno):

from dataset import ImageDatasetCreator
from model import Model
from train import train

# You could use argparse for things like batch, epochs etc.
if __name__ == "__main__":
    dataloader = ImageDatasetCreator("mnist", batch=64, cache=True)
    train, test = dataloader.get_train(), dataloader.get_test()
    model = Model()
    model.build((None, 28, 28, 1))
    train(
        model, train, path epochs, test, len(train) // batch, len(test) // batch, ...
    )  # provide necessary arguments appropriately
    # Do whatever you want with those
    weights = model.get_weights()

Ricordate che le funzioni di cui sopra non sono da copiare e devono essere considerate più come una linea guida. Se avete domande, chiamatemi pure.

3. Domande dai commenti

3.1 Come inizializzare i livelli personalizzati e incorporati

3.1.1 TLDR di ciò che si sta per leggere

  • Funzione di inizializzazione di Poisson personalizzata, ma richiede tre
    argomenti
  • tf.keras.initalization Le esigenze dell'API due (si veda l'ultimo punto della documentazione), quindi uno viene
    specificato tramite il metodo Python lambda all'interno del livello personalizzato che abbiamo scritto prima
  • Viene aggiunto un bias opzionale per il livello, che può essere disattivato con
    booleano

Perché è così inutilmente complicato? Per mostrare che in tf2.0 si può finalmente utilizzare la funzionalità di Python, senza più problemi di grafici, if invece di tf.cond ecc.

3.1.2 Dal TLDR all'implementazione

Gli inizializzatori di Keras possono essere trovati qui e quelli di Tensorflow qui.

Si prega di notare le incoerenze dell'API (lettere maiuscole come classi, lettere minuscole con trattino basso come funzioni), specialmente in tf2.0ma non è questo il punto.

Si possono usare passando una stringa (come si fa in YourDense sopra) o durante la creazione dell'oggetto.

Per consentire un'inizializzazione personalizzata nei propri livelli personalizzati, si può semplicemente aggiungere un parametro aggiuntivo al costruttore (tf.keras.Model La classe è ancora una classe Python e il suo parametro __init__ dovrebbe essere usato come quello di Python).

Prima di questo, vi mostrerò come creare un'inizializzazione personalizzata:

# Poisson custom initialization because why not.
def my_dumb_init(shape, lam, dtype=None):
    return tf.squeeze(tf.random.poisson(shape, lam, dtype=dtype))

Notate che la firma prende tre argomenti, mentre dovrebbe prendere (shape, dtype) solo. Tuttavia, si può "aggiustare" facilmente questo problema creando il proprio livello, come quello sottostante (esteso a YourLinear):

import typing

import tensorflow as tf

class YourDense(tf.keras.layers.Layer):
    # It's still Python, use it as Python, that's the point of tf.2.0
    @classmethod
    def register_initialization(cls, initializer):
        # Set defaults if init not provided by user
        if initializer is None:
            # let's make the signature proper for init in tf.keras
            return lambda shape, dtype: my_dumb_init(shape, 1, dtype)
        return initializer

    def __init__(
        self,
        units: int,
        bias: bool = True,
        # can be string or callable, some typing info added as well...
        kernel_initializer: typing.Union[str, typing.Callable] = None,
        bias_initializer: typing.Union[str, typing.Callable] = None,
    ):
        super().__init__()
        self.units: int = units
        self.kernel_initializer = YourDense.register_initialization(kernel_initializer)
        if bias:
            self.bias_initializer = YourDense.register_initialization(bias_initializer)
        else:
            self.bias_initializer = None

    def build(self, input_shape):
        # Simply pass your init here
        self.kernel = self.add_weight(
            shape=(input_shape[-1], self.units),
            initializer=self.kernel_initializer,
            trainable=True,
        )
        if self.bias_initializer is not None:
            self.bias = self.add_weight(
                shape=(self.units,), initializer=self.bias_initializer
            )
        else:
            self.bias = None

    def call(self, inputs):
        weights = tf.matmul(inputs, self.kernel)
        if self.bias is not None:
            return weights + self.bias

Ho aggiunto my_dumb_initialization come predefinito (se l'utente non ne fornisce uno) e ho reso opzionale il bias con bias . Si noti che è possibile utilizzare if liberamente, purché non dipenda dai dati. Se lo è (o dipende da tf.Tensor in qualche modo), si deve usare @tf.function che cambia il flusso di Python in quello di tensorflow (per esempio if a tf.cond).

Per maggiori informazioni sull'autografia, vedere qui, è molto facile da seguire.

Se si vogliono incorporare le modifiche all'inizializzatore di cui sopra nel proprio modello, occorre creare l'oggetto appropriato e il gioco è fatto.

... # Previous of code Model here
self.network = tf.keras.Sequential(
    [
        YourDense(100, bias=False, kernel_initializer="lecun_uniform"),
        tf.keras.layers.ReLU(),
        YourDense(10, bias_initializer=tf.initializers.Ones()),
    ]
)
... # and the same afterwards

Con il built-in tf.keras.layers.Dense si può fare lo stesso (i nomi degli argomenti sono diversi, ma l'idea è valida).

3.2 Differenziazione automatica con tf.GradientTape

3.2.1 Introduzione

Punto di tf.GradientTape è di consentire agli utenti il normale flusso di controllo Python e il calcolo del gradiente delle variabili rispetto a un'altra variabile.

Esempio preso da qui, ma spezzato in pezzi separati:

def f(x, y):
  output = 1.0
  for i in range(y):
    if i > 1 and i < 5:
      output = tf.multiply(output, x)
  return output

Una normale funzione Python con for e if dichiarazioni di controllo del flusso

def grad(x, y):
  with tf.GradientTape() as t:
    t.watch(x)
    out = f(x, y)
  return t.gradient(out, x)

Utilizzando il nastro gradiente è possibile registrare tutte le operazioni su Tensors (e anche i loro stati intermedi) e "riprodurlo" all'indietro (eseguire la differenziazione automatica all'indietro usando la regola del chaing).

Ogni Tensor entro tf.GradientTape() del gestore del contesto viene registrato automaticamente. Se qualche tensore è fuori dal campo di applicazione, usare watch() come si può vedere sopra.

Infine, il gradiente di output rispetto a x (l'input viene restituito).

3.2.2 Collegamento con l'apprendimento profondo

Quanto descritto in precedenza è backpropagation algoritmo. Per ogni nodo della rete (o meglio per ogni strato) vengono calcolati i gradienti rispetto alle uscite. Questi gradienti vengono poi utilizzati dai vari ottimizzatori per apportare correzioni e così si ripete.

Continuiamo e supponiamo di avere il vostro tf.keras.Model, istanza di ottimizzatore, tf.data.Dataset e la funzione di perdita già impostata.

È possibile definire un Trainer che eseguirà l'addestramento per noi. Leggere i commenti nel codice in caso di dubbi:

class Trainer:
    def __init__(self, model, optimizer, loss_function):
        self.model = model
        self.loss_function = loss_function
        self.optimizer = optimizer
        # You could pass custom metrics in constructor
        # and adjust train_step and test_step accordingly
        self.train_loss = tf.keras.metrics.Mean(name="train_loss")
        self.test_loss = tf.keras.metrics.Mean(name="train_loss")

    def train_step(self, x, y):
        # Setup tape
        with tf.GradientTape() as tape:
            # Get current predictions of network
            y_pred = self.model(x)
            # Calculate loss generated by predictions
            loss = self.loss_function(y, y_pred)
        # Get gradients of loss w.r.t. EVERY trainable variable (iterable returned)
        gradients = tape.gradient(loss, self.model.trainable_variables)
        # Change trainable variable values according to gradient by applying optimizer policy
        self.optimizer.apply_gradients(zip(gradients, self.model.trainable_variables))
        # Record loss of current step
        self.train_loss(loss)

    def train(self, dataset):
        # For N epochs iterate over dataset and perform train steps each time
        for x, y in dataset:
            self.train_step(x, y)

    def test_step(self, x, y):
        # Record test loss separately
        self.test_loss(self.loss_function(y, self.model(x)))

    def test(self, dataset):
        # Iterate over whole dataset
        for x, y in dataset:
            self.test_step(x, y)

    def __str__(self):
        # You need Python 3.7 with f-string support
        # Just return metrics
        return f"Loss: {self.train_loss.result()}, Test Loss: {self.test_loss.result()}"

Ora, si può usare questa classe nel proprio codice in modo molto semplice, come questo:

EPOCHS = 5

# model, optimizer, loss defined beforehand
trainer = Trainer(model, optimizer, loss)
for _ in range(EPOCHS):
    trainer.train(train_dataset) # Same for training and test datasets
    trainer.test(test_dataset)
    print(f"Epoch {epoch}: {trainer})")

La stampa indicherà la perdita di allenamento e di test per ogni epoca. È possibile mescolare l'allenamento e il test in qualsiasi modo (ad esempio, 5 epoche per l'allenamento e 1 per il test), si possono aggiungere metriche diverse, ecc.

Vedere qui se si desidera un approccio non orientato all'OOP (IMO meno leggibile, ma a ciascuno il suo).

Se c'è qualcosa che posso migliorare nel codice, fatemelo sapere.
anche.

Abbracciare l'API di alto livello per qualcosa di simile. Si può fare con poche righe di codice ed è molto più facile da debuggare, leggere e ragionare:

(x_train, y_train), (x_test, y_test) = tfds.load('mnist', split=['train', 'test'], 
                                                  batch_size=-1, as_supervised=True)

x_train = tf.cast(tf.reshape(x_train, shape=(x_train.shape[0], 784)), tf.float32)
x_test  = tf.cast(tf.reshape(x_test, shape=(x_test.shape[0], 784)), tf.float32)

model = tf.keras.models.Sequential([
  tf.keras.layers.Dense(512, activation='sigmoid'),
  tf.keras.layers.Dense(256, activation='sigmoid'),
  tf.keras.layers.Dense(10, activation='softmax')
])
model.fit(x_train, y_train, epochs=5)
model.evaluate(x_test, y_test)

Ho provato a scrivere un'implementazione personalizzata di una rete neurale di base con
due strati nascosti sul dataset MNIST usando tensorflow 2.0 beta, ma non sono sicuro di cosa sia andato storto.
non sono sicuro di cosa sia andato storto, ma la mia perdita di addestramento e l'accuratezza sembrano
bloccati rispettivamente a 1.5 e circa 85.

Dov'è la parte di addestramento? L'addestramento dei modelli di TF 2.0 o la sintassi di Keras o Esecuzione avida con tf.GradientTape(). Puoi incollare il codice con gli strati conv e densi e come lo hai addestrato?


Altre domande:

1) Come aggiungere un livello di dropout in questa implementazione personalizzata?
(facendolo funzionare sia in fase di addestramento che di test)

È possibile aggiungere un livello Dropout() con:

from tensorflow.keras.layers import Dropout

E poi lo si inserisce in un modello Sequential() semplicemente con:

Dropout(dprob)     # where dprob = dropout probability

2) Come aggiungere la normalizzazione batch in questo codice?

Come prima, con:

from tensorflow.keras.layers import BatchNormalization

La scelta di dove per inserire la batchnorm nel modello, beh, dipende da voi. Non esiste una regola empirica, vi consiglio di fare degli esperimenti. Con il metodo ML è sempre un processo di tentativi ed errori.


3) Come posso usare le callback in questo codice? Cioè (facendo uso di
EarlyStopping e ModelCheckpoint)

Se vi state allenando usando la sintassi di Keras, potete semplicemente usarla. Consultate questo tutorial molto approfondito su come utilizzarla. Sono sufficienti poche righe di codice.
Se si esegue un modello in Esecuzione avida è necessario implementare queste tecniche da soli, con il proprio codice. È più complesso, ma offre anche maggiore libertà nell'implementazione.


4) C'è qualcos'altro nel codice che posso ottimizzare ulteriormente?
questo codice? Ad esempio (facendo uso di tensorflow 2.x @tf.function decorator
ecc.)

Dipende. Se si utilizza la sintassi di Keras, non credo sia necessario aggiungere altro. Nel caso in cui stiate addestrando il modello in esecuzione Eager, allora vi suggerirei di usare l'opzione @tf.function su qualche funzione per accelerare un po'.
Potete vedere un esempio pratico di TF 2.0 su come utilizzare il decoratore in questo Quaderno.

Oltre a questo, vi suggerisco di giocare con tecniche di regolarizzazione come le inizializzazioni dei pesi, la perdita L1-L2, ecc.


5) Ho anche bisogno di un modo per estrarre tutti i pesi finali per tutti i livelli
dopo l'addestramento, in modo da poterli tracciare e verificare le loro distribuzioni. Per
verificare problemi come la scomparsa o l'esplosione del gradiente.

Una volta che il modello è stato addestrato, è possibile estrarre i suoi pesi con:

weights = model.get_weights()

o:

weights = model.trainable_weights

Se si desidera mantenere solo quelli addestrabili.


6) Vorrei anche un aiuto per scrivere questo codice in modo più generalizzato, in modo che
Posso facilmente implementare altre reti come la rete convoluzionale (ad es.
Conv, MaxPool ecc.) basate su questo codice.

È possibile racchiudere tutto il codice in una funzione, quindi . Alla fine di questo Quaderno ho fatto qualcosa del genere (è per una rete feed-forward, che è molto più semplice, ma è un inizio e potete modificare il codice in base alle vostre esigenze).

---

AGGIORNAMENTO:

Si prega di controllare la mia implementazione TensorFlow 2.0 di un classificatore CNN. Questo potrebbe essere un suggerimento utile: è addestrato su Moda MNIST che lo rende molto simile al vostro compito.

Qui puoi vedere i commenti e le valutazioni degli utenti

Non dimenticare di comunicare questa dichiarazione se ti è stata utile.



Utilizzate il nostro motore di ricerca

Ricerca
Generic filters

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.