Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/pt-br/guide/keras/train_and_evaluate.ipynb
25118 views
Kernel: Python 3
#@title Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.

Treinamento e avaliação com os métodos integrados

Configuração

import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers

Introdução

Este guia trata de modelos de treinamento, avaliação e previsão (inferência) ao usar APIs integradas para treinamento e validação (tais como Model.fit(), Model.evaluate() e Model.predict()).

Se você tiver interesse em utilizar fit() ao especificar sua própria função de passo de treinamento, consulte o guia Personalizando o que acontece no fit().

Se seu interesse for escrever seus próprios loops de treinamento e avaliação do zero, consulte o guia "escrevendo um loop de treinamento do zero".

Em geral, esteja você usando loops integrados ou escrevendo seus próprios loops, o treinamento e a avaliação do modelo funcionam estritamente da mesma maneira em todos os tipos de modelo Keras: modelos sequenciais, modelos construídos com a API Functional e modelos escritos do zero através da implementação da subclasse de um modelo.

Este guia não trata do treinamento distribuído, que é abordado em nosso guia para treinamento multi-GPU e distribuído.

Visão geral da API: um primeiro exemplo completo

Ao passar dados para os loops de treinamento integrados de um modelo, você deve ou usar arrays NumPy (se seus dados forem pequenos e caberem na memória), ou tf.data Dataset objects. Nos próximos parágrafos, usaremos o dataset MNIST como matrizes NumPy, a fim de demonstrar como usar otimizadores, perdas e métricas.

Vamos considerar o seguinte modelo (aqui, construímos com a API funcional, mas também poderia ser um modelo sequencial ou de subclasse):

inputs = keras.Input(shape=(784,), name="digits") x = layers.Dense(64, activation="relu", name="dense_1")(inputs) x = layers.Dense(64, activation="relu", name="dense_2")(x) outputs = layers.Dense(10, activation="softmax", name="predictions")(x) model = keras.Model(inputs=inputs, outputs=outputs)

Um típico workflow de ponta a ponta consistiria de:

  • Treinamento

  • Validação num conjunto de validação gerado a partir dos dados de treinamento originais

  • Avaliação nos dados de teste

Usaremos dados MNIST para este exemplo.

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data() # Preprocess the data (these are NumPy arrays) x_train = x_train.reshape(60000, 784).astype("float32") / 255 x_test = x_test.reshape(10000, 784).astype("float32") / 255 y_train = y_train.astype("float32") y_test = y_test.astype("float32") # Reserve 10,000 samples for validation x_val = x_train[-10000:] y_val = y_train[-10000:] x_train = x_train[:-10000] y_train = y_train[:-10000]

Especificamos a configuração de treinamento (otimizador, perda, métricas):

model.compile( optimizer=keras.optimizers.RMSprop(), # Optimizer # Loss function to minimize loss=keras.losses.SparseCategoricalCrossentropy(), # List of metrics to monitor metrics=[keras.metrics.SparseCategoricalAccuracy()], )

Chamamos fit(), que treinará o modelo dividindo os dados em "lotes" de tamanho batch_size e iterando repetidamente em todo o dataset por um determinado número de epochs.

print("Fit model on training data") history = model.fit( x_train, y_train, batch_size=64, epochs=2, # We pass some validation for # monitoring validation loss and metrics # at the end of each epoch validation_data=(x_val, y_val), )

O objeto history retornado contém um registro dos valores de perda e valores de métrica durante o treinamento:

history.history

Avaliamos o modelo nos dados de teste via evaluate():

# Evaluate the model on the test data using `evaluate` print("Evaluate on test data") results = model.evaluate(x_test, y_test, batch_size=128) print("test loss, test acc:", results) # Generate predictions (probabilities -- the output of the last layer) # on new data using `predict` print("Generate predictions for 3 samples") predictions = model.predict(x_test[:3]) print("predictions shape:", predictions.shape)

Agora, vamos revisar cada parte desse workflow em detalhes.

O método compile(): especificando uma perda, métricas e um otimizador

Para treinar um modelo com fit(), você precisa especificar uma função de perda, um otimizador e, opcionalmente, algumas métricas a serem monitoradas.

Você passa esses parâmetros para o modelo como argumentos do método compile():

model.compile( optimizer=keras.optimizers.RMSprop(learning_rate=1e-3), loss=keras.losses.SparseCategoricalCrossentropy(), metrics=[keras.metrics.SparseCategoricalAccuracy()], )

O argumento metrics deve ser uma lista: seu modelo pode ter qualquer quantidade de métricas.

Se seu modelo tiver múltiplas saídas, você poderá especificar diferentes perdas e métricas para cada saída e modular a contribuição de cada saída para a perda total do modelo. Você encontrará mais detalhes sobre isso na seção Passando dados para modelos multi-entrada, multi-saída.

Observe que, se você estiver satisfeito com as configurações padrão, em muitos casos, o otimizador, a perda e as métricas podem ser especificados via identificadores de string como um atalho:

model.compile( optimizer="rmsprop", loss="sparse_categorical_crossentropy", metrics=["sparse_categorical_accuracy"], )

Para reutilização posterior, vamos colocar nossa definição de modelo e passo de compilação dentro de funções; vamos chamá-las várias vezes em diferentes exemplos neste guia.

def get_uncompiled_model(): inputs = keras.Input(shape=(784,), name="digits") x = layers.Dense(64, activation="relu", name="dense_1")(inputs) x = layers.Dense(64, activation="relu", name="dense_2")(x) outputs = layers.Dense(10, activation="softmax", name="predictions")(x) model = keras.Model(inputs=inputs, outputs=outputs) return model def get_compiled_model(): model = get_uncompiled_model() model.compile( optimizer="rmsprop", loss="sparse_categorical_crossentropy", metrics=["sparse_categorical_accuracy"], ) return model

Muitos otimizadores, perdas e métricas integrados estão disponíveis na API

Em geral, você não terá que criar suas próprias perdas, métricas ou otimizadores do zero, porque o que você precisa provavelmente já faz parte da API do Keras:

Otimizadores:

  • SGD() (com ou sem momento)

  • RMSprop()

  • Adam()

  • etc.

Perdas:

  • MeanSquaredError()

  • KLDivergence()

  • CosineSimilarity()

  • etc.

Métricas:

  • AUC()

  • Precision()

  • Recall()

  • etc.

Perdas personalizadas

Se você precisar criar uma perda personalizada, o Keras oferece duas maneiras de fazer isso.

O primeiro método envolve a criação de uma função que aceita entradas y_true e y_pred. O exemplo a seguir mostra uma função de perda que calcula o erro quadrático médio entre os dados reais e as previsões:

def custom_mean_squared_error(y_true, y_pred): return tf.math.reduce_mean(tf.square(y_true - y_pred)) model = get_uncompiled_model() model.compile(optimizer=keras.optimizers.Adam(), loss=custom_mean_squared_error) # We need to one-hot encode the labels to use MSE y_train_one_hot = tf.one_hot(y_train, depth=10) model.fit(x_train, y_train_one_hot, batch_size=64, epochs=1)

Se você precisar de uma função de perda que aceite parâmetros além de y_true e y_pred, você pode criar uma subclasse da classe tf.keras.losses.Loss e implementar os dois métodos a seguir:

  • __init__(self): aceita parâmetros a serem passados durante a chamada de sua função de perda

  • call(self, y_true, y_pred): usa os alvos (y_true) e as previsões do modelo (y_pred) para calcular a perda do modelo

Vamos supor que você queira usar o erro quadrático médio, mas com um termo adicionado que desincentivará os valores de previsão distantes de 0,5 (presumimos que os alvos categóricos são one-hot encoded e assumem valores entre 0 e 1). Isto cria um incentivo para que o modelo não seja muito confiante, o que pode ajudar a reduzir o overfitting (não saberemos se funciona até tentarmos!).

Veja como você faria:

class CustomMSE(keras.losses.Loss): def __init__(self, regularization_factor=0.1, name="custom_mse"): super().__init__(name=name) self.regularization_factor = regularization_factor def call(self, y_true, y_pred): mse = tf.math.reduce_mean(tf.square(y_true - y_pred)) reg = tf.math.reduce_mean(tf.square(0.5 - y_pred)) return mse + reg * self.regularization_factor model = get_uncompiled_model() model.compile(optimizer=keras.optimizers.Adam(), loss=CustomMSE()) y_train_one_hot = tf.one_hot(y_train, depth=10) model.fit(x_train, y_train_one_hot, batch_size=64, epochs=1)

Métricas personalizadas

Se você precisar de uma métrica que não faz parte da API, poderá criar facilmente métricas personalizadas criando uma subclasse da classe tf.keras.metrics.Metric. Você precisará implementar 4 métodos:

  • __init__(self), no qual você criará variáveis ​​de estado para sua métrica.

  • update_state(self, y_true, y_pred, sample_weight=None), que usa os alvos y_true e as previsões do modelo y_pred para atualizar as variáveis ​​de estado.

  • result(self), que usa as variáveis ​​de estado para calcular os resultados finais.

  • reset_state(self), que reinicializa o estado da métrica.

A atualização de estado e a computação de resultados são mantidos separados (em update_state() e result(), respectivamente) porque, em alguns casos, a computação de resultados pode ser muito cara e só seria feita periodicamente.

Aqui está um exemplo simples mostrando como implementar uma métrica CategoricalTruePositives que conta quantas amostras foram classificadas corretamente como pertencentes a uma determinada classe:

class CategoricalTruePositives(keras.metrics.Metric): def __init__(self, name="categorical_true_positives", **kwargs): super(CategoricalTruePositives, self).__init__(name=name, **kwargs) self.true_positives = self.add_weight(name="ctp", initializer="zeros") def update_state(self, y_true, y_pred, sample_weight=None): y_pred = tf.reshape(tf.argmax(y_pred, axis=1), shape=(-1, 1)) values = tf.cast(y_true, "int32") == tf.cast(y_pred, "int32") values = tf.cast(values, "float32") if sample_weight is not None: sample_weight = tf.cast(sample_weight, "float32") values = tf.multiply(values, sample_weight) self.true_positives.assign_add(tf.reduce_sum(values)) def result(self): return self.true_positives def reset_state(self): # The state of the metric will be reset at the start of each epoch. self.true_positives.assign(0.0) model = get_uncompiled_model() model.compile( optimizer=keras.optimizers.RMSprop(learning_rate=1e-3), loss=keras.losses.SparseCategoricalCrossentropy(), metrics=[CategoricalTruePositives()], ) model.fit(x_train, y_train, batch_size=64, epochs=3)

Lidando com perdas e métricas que não se encaixam na assinatura padrão

A esmagadora maioria das perdas e métricas pode ser calculada a partir de y_true e y_pred, onde y_pred é uma saída do seu modelo, mas isto não é verdade para todas. Por exemplo, uma perda de regularização pode requerer apenas a ativação de uma camada (não há alvos neste caso), e esta ativação pode não ser uma saída do modelo.

Em tais casos, você pode chamar self.add_loss(loss_value) de dentro do método call de uma camada personalizada. As perdas adicionadas dessa maneira são adicionadas à perda "principal" durante o treinamento (a que é passada para compile()). Aqui está um exemplo simples que adiciona regularização de atividades (observe que a regularização de atividades é integrada em todas as camadas do Keras: esta camada serve apenas para fornecer um exemplo concreto):

class ActivityRegularizationLayer(layers.Layer): def call(self, inputs): self.add_loss(tf.reduce_sum(inputs) * 0.1) return inputs # Pass-through layer. inputs = keras.Input(shape=(784,), name="digits") x = layers.Dense(64, activation="relu", name="dense_1")(inputs) # Insert activity regularization as a layer x = ActivityRegularizationLayer()(x) x = layers.Dense(64, activation="relu", name="dense_2")(x) outputs = layers.Dense(10, name="predictions")(x) model = keras.Model(inputs=inputs, outputs=outputs) model.compile( optimizer=keras.optimizers.RMSprop(learning_rate=1e-3), loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True), ) # The displayed loss will be much higher than before # due to the regularization component. model.fit(x_train, y_train, batch_size=64, epochs=1)

Você pode fazer o mesmo para registrar em log valores de métricas, usando add_metric():

class MetricLoggingLayer(layers.Layer): def call(self, inputs): # The `aggregation` argument defines # how to aggregate the per-batch values # over each epoch: # in this case we simply average them. self.add_metric( keras.backend.std(inputs), name="std_of_activation", aggregation="mean" ) return inputs # Pass-through layer. inputs = keras.Input(shape=(784,), name="digits") x = layers.Dense(64, activation="relu", name="dense_1")(inputs) # Insert std logging as a layer. x = MetricLoggingLayer()(x) x = layers.Dense(64, activation="relu", name="dense_2")(x) outputs = layers.Dense(10, name="predictions")(x) model = keras.Model(inputs=inputs, outputs=outputs) model.compile( optimizer=keras.optimizers.RMSprop(learning_rate=1e-3), loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True), ) model.fit(x_train, y_train, batch_size=64, epochs=1)

Na API Functional , você também pode chamar model.add_loss(loss_tensor), ou model.add_metric(metric_tensor, name, aggregation).

Eis aqui um exemplo simples:

inputs = keras.Input(shape=(784,), name="digits") x1 = layers.Dense(64, activation="relu", name="dense_1")(inputs) x2 = layers.Dense(64, activation="relu", name="dense_2")(x1) outputs = layers.Dense(10, name="predictions")(x2) model = keras.Model(inputs=inputs, outputs=outputs) model.add_loss(tf.reduce_sum(x1) * 0.1) model.add_metric(keras.backend.std(x1), name="std_of_activation", aggregation="mean") model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True), ) model.fit(x_train, y_train, batch_size=64, epochs=1)

Observe que quando você passa perdas via add_loss(), torna-se possível chamar compile() sem uma função de perda, pois o modelo já possui uma perda a minimizar.

Considere a seguinte camada LogisticEndpoint: ela usa como entrada alvos e logits e rastreia uma perda de entropia cruzada via add_loss(). Ela também rastreia a exatidão da classificação via add_metric().

class LogisticEndpoint(keras.layers.Layer): def __init__(self, name=None): super(LogisticEndpoint, self).__init__(name=name) self.loss_fn = keras.losses.BinaryCrossentropy(from_logits=True) self.accuracy_fn = keras.metrics.BinaryAccuracy() def call(self, targets, logits, sample_weights=None): # Compute the training-time loss value and add it # to the layer using `self.add_loss()`. loss = self.loss_fn(targets, logits, sample_weights) self.add_loss(loss) # Log accuracy as a metric and add it # to the layer using `self.add_metric()`. acc = self.accuracy_fn(targets, logits, sample_weights) self.add_metric(acc, name="accuracy") # Return the inference-time prediction tensor (for `.predict()`). return tf.nn.softmax(logits)

Você pode usá-la num modelo com duas entradas (dados de entrada e alvos), compilados sem um argumento loss, assim:

import numpy as np inputs = keras.Input(shape=(3,), name="inputs") targets = keras.Input(shape=(10,), name="targets") logits = keras.layers.Dense(10)(inputs) predictions = LogisticEndpoint(name="predictions")(logits, targets) model = keras.Model(inputs=[inputs, targets], outputs=predictions) model.compile(optimizer="adam") # No loss argument! data = { "inputs": np.random.random((3, 3)), "targets": np.random.random((3, 10)), } model.fit(data)

Para obter mais informações sobre como treinar modelos multi-entradas, consulte a seção Passando dados para modelos multi-entradas e multi-saídas.

Separando automaticamente um conjunto de validação

No primeiro exemplo completo que você viu, usamos o argumento validation_data para passar uma tupla de matrizes NumPy (x_val, y_val) para o modelo para avaliar uma perda de validação e métricas de validação no final de cada época.

Aqui está outra alternativa: o argumento validation_split permite que você reserve automaticamente parte de seus dados de treinamento para validação. O valor do argumento representa a fração dos dados a serem reservados para validação, portanto, deve ser definido como um número maior que 0 e menor que 1. Por exemplo, validation_split=0.2 significa "usar 20% dos dados para validação" e validation_split=0.6 significa "usar 60% dos dados para validação".

A forma como a validação é calculada é tomando as últimas x% amostras das matrizes recebidas pela chamada fit(), antes de qualquer embaralhamento.

Observe que você só pode usar validation_split ao treinar com dados NumPy.

model = get_compiled_model() model.fit(x_train, y_train, batch_size=64, validation_split=0.2, epochs=1)

Treinamento e avaliação de datasets tf.data

Nos últimos parágrafos, você viu como lidar com perdas, métricas e otimizadores, e viu como usar os argumentos validation_data e validation_split em fit(), quando seus dados são passados ​​como arrays NumPy.

Vamos agora dar uma olhada no caso em que seus dados vêm na forma de um objeto tf.data.Dataset.

A API tf.data é um conjunto de utilitários no TensorFlow 2.0 para carregar e pré-processar dados de forma rápida e escalável.

Para obter um guia completo sobre como criar Datasets, consulte a documentação do tf.data.

Você pode passar uma instância Dataset diretamente para os métodos fit(), evaluate() e predict():

model = get_compiled_model() # First, let's create a training Dataset instance. # For the sake of our example, we'll use the same MNIST data as before. train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)) # Shuffle and slice the dataset. train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64) # Now we get a test dataset. test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test)) test_dataset = test_dataset.batch(64) # Since the dataset already takes care of batching, # we don't pass a `batch_size` argument. model.fit(train_dataset, epochs=3) # You can also evaluate or predict on a dataset. print("Evaluate") result = model.evaluate(test_dataset) dict(zip(model.metrics_names, result))

Observe que o Dataset é reinicializado no final de cada época, para que possa ser reutilizado na época seguinte.

Se você quiser executar o treinamento apenas num número específico de lotes deste dataset, pode passar o argumento steps_per_epoch, que especifica quantos passos de treinamento o modelo deve executar usando este dataset antes de passar para a época seguinte.

Se você fizer isso, o dataset não será reinicializado no final de cada época; em vez disso, simplesmente continuaremos buscando os próximos lotes. O dataset acabará ficando sem dados (a menos que seja um dataset com loop infinito).

model = get_compiled_model() # Prepare the training dataset train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)) train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64) # Only use the 100 batches per epoch (that's 64 * 100 samples) model.fit(train_dataset, epochs=3, steps_per_epoch=100)

Usando um dataset de validação

Você pode passar uma instância Dataset como o validation_data em fit():

model = get_compiled_model() # Prepare the training dataset train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)) train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64) # Prepare the validation dataset val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val)) val_dataset = val_dataset.batch(64) model.fit(train_dataset, epochs=1, validation_data=val_dataset)

No final de cada época, o modelo irá iterar sobre o dataset de validação e calcular a perda de validação e as métricas de validação.

Se você quiser executar a validação apenas num número específico de lotes desse conjunto de dados, pode passar o validation_steps, que especifica quantos passos de validação o modelo deve executar com o dataset de validação antes de interromper a validação e passar para a época seguinte:

model = get_compiled_model() # Prepare the training dataset train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)) train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64) # Prepare the validation dataset val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val)) val_dataset = val_dataset.batch(64) model.fit( train_dataset, epochs=1, # Only run validation using the first 10 batches of the dataset # using the `validation_steps` argument validation_data=val_dataset, validation_steps=10, )

Observe que o dataset de validação será reinicializado após cada uso (para que você sempre avalie nas mesmas amostras de época para época).

O argumento validation_split (gerando um conjunto de validação a partir dos dados de treinamento) não é suportado ao treinar a partir de objetos Dataset, pois esse recurso requer a capacidade de indexar as amostras dos datasets, o que geralmente não é possível com a API Dataset.

Outros formatos de entrada suportados

Além de arrays NumPy, tensores eager e Datasets TensorFlow, é possível treinar um modelo Keras usando dataframes Pandas ou geradores Python que geram lotes de dados e rótulos.

Em particular, a classe keras.utils.Sequence oferece uma interface simples para construir geradores de dados Python que reconhecem multiprocessamento e podem ser embaralhados.

Em geral, recomendamos que você use:

  • Dados de entrada NumPy se seus dados forem pequenos e caberem na memória

  • Objetos Dataset se você tiver grandes datasets e precisar fazer treinamento distribuído

  • Objetos Sequence se você tiver grandes datasets e precisar fazer muitos processamentos personalizados do lado do Python que não podem ser feitos no TensorFlow (por exemplo, se você depender de bibliotecas externas para carregamento ou pré-processamento de dados).

Usando um objeto keras.utils.Sequence como entrada

O keras.utils.Sequence é um utilitário a partir do qual você pode construir uma subclasse para obter um gerador Python com duas propriedades importantes:

  • Funciona bem com multiprocessamento.

  • Ele pode ser embaralhado (por exemplo, ao passar shuffle=True em fit()).

Uma Sequence deve implementar dois métodos:

  • __getitem__

  • __len__

O método __getitem__ deve retornar um lote completo. Se você deseja modificar seu dataset entre épocas, pode implementar on_epoch_end.

Eis aqui um exemplo rápido:

from skimage.io import imread from skimage.transform import resize import numpy as np # Here, `filenames` is list of path to the images # and `labels` are the associated labels. class CIFAR10Sequence(Sequence): def __init__(self, filenames, labels, batch_size): self.filenames, self.labels = filenames, labels self.batch_size = batch_size def __len__(self): return int(np.ceil(len(self.filenames) / float(self.batch_size))) def __getitem__(self, idx): batch_x = self.filenames[idx * self.batch_size:(idx + 1) * self.batch_size] batch_y = self.labels[idx * self.batch_size:(idx + 1) * self.batch_size] return np.array([ resize(imread(filename), (200, 200)) for filename in batch_x]), np.array(batch_y) sequence = CIFAR10Sequence(filenames, labels, batch_size) model.fit(sequence, epochs=10)

Usando ponderação de amostra e de classe

Com as configurações padrão, o peso de uma amostra é decidido por sua frequência no dataset. Existem dois métodos para ponderar os dados, independentemente da frequência da amostra:

  • Pesos de classe

  • Pesos de amostra

Pesos de classe

Isso é definido passando um dicionário para o argumento class_weight para Model.fit(). Este dicionário mapeia índices de classe ao peso que deve ser usado para amostras pertencentes a esta classe.

Isto pode ser usado para equilibrar classes sem reamostragem ou para treinar um modelo que dê mais importância a uma determinada classe.

Por exemplo, se a classe "0" tiver metade da representação da classe "1" em seus dados, você poderá usar Model.fit(..., class_weight={0: 1., 1: 0.5}).

Aqui está um exemplo NumPy onde usamos pesos de classe ou pesos de amostra para dar mais importância à classificação correta da classe #5 (que é o dígito "5" no conjunto de dados MNIST).

import numpy as np class_weight = { 0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0, # Set weight "2" for class "5", # making this class 2x more important 5: 2.0, 6: 1.0, 7: 1.0, 8: 1.0, 9: 1.0, } print("Fit with class weight") model = get_compiled_model() model.fit(x_train, y_train, class_weight=class_weight, batch_size=64, epochs=1)

Pesos de amostra

Para controle refinado, ou se você não estiver construindo um classificador, você pode usar "pesos de amostra".

  • Ao treinar com dados NumPy: Passe o argumento sample_weight para Model.fit().

  • Ao treinar com tf.data ou qualquer outro tipo de iterador: Forneça tuplas (input_batch, label_batch, sample_weight_batch).

Um array de "pesos de amostra" é um array de números que especifica quanto peso cada amostra em um lote deve ter no cálculo da perda total. É frequentemente usado em problemas de classificação desbalanceados (a ideia é dar mais peso a classes raramente vistas).

Quando os pesos utilizados são uns e zeros, o array pode ser utilizado como uma máscara para a função de perda (descartando totalmente a contribuição de determinadas amostras para a perda total).

sample_weight = np.ones(shape=(len(y_train),)) sample_weight[y_train == 5] = 2.0 print("Fit with sample weight") model = get_compiled_model() model.fit(x_train, y_train, sample_weight=sample_weight, batch_size=64, epochs=1)

Aqui está um exemplo Dataset correspondente:

sample_weight = np.ones(shape=(len(y_train),)) sample_weight[y_train == 5] = 2.0 # Create a Dataset that includes sample weights # (3rd element in the return tuple). train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train, sample_weight)) # Shuffle and slice the dataset. train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64) model = get_compiled_model() model.fit(train_dataset, epochs=1)

Passando dados para modelos com multi-entradas e multi-saídas

Nos exemplos anteriores, estávamos considerando um modelo com uma única entrada (um tensor de formato (764,)) e uma única saída (um tensor de previsão de forma (10,) ). Mas como ficam os modelos que possuem múltiplas entradas ou saídas?

Considere o modelo a seguir, que tem uma entrada de imagem de formato (32, 32, 3) (isto é (height, width, channels)) e uma entrada de série temporal de formato (None, 10) (isto é (timesteps, features)). Nosso modelo terá duas saídas calculadas a partir da combinação dessas entradas: uma "pontuação" (de formato (1,)) e uma distribuição de probabilidade em cinco classes (de formato (5,)).

image_input = keras.Input(shape=(32, 32, 3), name="img_input") timeseries_input = keras.Input(shape=(None, 10), name="ts_input") x1 = layers.Conv2D(3, 3)(image_input) x1 = layers.GlobalMaxPooling2D()(x1) x2 = layers.Conv1D(3, 3)(timeseries_input) x2 = layers.GlobalMaxPooling1D()(x2) x = layers.concatenate([x1, x2]) score_output = layers.Dense(1, name="score_output")(x) class_output = layers.Dense(5, name="class_output")(x) model = keras.Model( inputs=[image_input, timeseries_input], outputs=[score_output, class_output] )

Vamos plotar este modelo, para que você possa ver claramente o que estamos fazendo aqui (observe que os formatos mostrados no gráfico são formatos de lote, em vez de formatos por amostra).

keras.utils.plot_model(model, "multi_input_and_output_model.png", show_shapes=True)

Em tempo de compilação, podemos especificar diferentes perdas para diferentes saídas, passando as funções de perda como uma lista:

model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss=[keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy()], )

Se passássemos apenas uma única função de perda para o modelo, a mesma função de perda seria aplicada a todas as saídas (o que não é apropriado aqui).

Da mesma forma para as métricas:

model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss=[keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy()], metrics=[ [ keras.metrics.MeanAbsolutePercentageError(), keras.metrics.MeanAbsoluteError(), ], [keras.metrics.CategoricalAccuracy()], ], )

Já que demos nomes às nossas camadas de saída, também podemos especificar perdas e métricas por saída através de um dict:

model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss={ "score_output": keras.losses.MeanSquaredError(), "class_output": keras.losses.CategoricalCrossentropy(), }, metrics={ "score_output": [ keras.metrics.MeanAbsolutePercentageError(), keras.metrics.MeanAbsoluteError(), ], "class_output": [keras.metrics.CategoricalAccuracy()], }, )

Recomendamos o uso de nomes e dicts explícitos se você tiver mais de 2 saídas.

É possível atribuir diferentes pesos a diferentes perdas específicas de saída (por exemplo, pode-se desejar privilegiar a perda de "pontuação" em nosso exemplo, atribuindo em 2x a importância da perda de classe), usando o argumento loss_weights:

model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss={ "score_output": keras.losses.MeanSquaredError(), "class_output": keras.losses.CategoricalCrossentropy(), }, metrics={ "score_output": [ keras.metrics.MeanAbsolutePercentageError(), keras.metrics.MeanAbsoluteError(), ], "class_output": [keras.metrics.CategoricalAccuracy()], }, loss_weights={"score_output": 2.0, "class_output": 1.0}, )

Você também poderia optar por não calcular uma perda para determinadas saídas, se essas saídas forem destinadas à previsão, mas não ao treinamento:

# List loss version model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss=[None, keras.losses.CategoricalCrossentropy()], ) # Or dict loss version model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss={"class_output": keras.losses.CategoricalCrossentropy()}, )

Passar dados para um modelo multi-entrada ou multi-saída em fit() funciona de maneira semelhante à especificação de uma função de perda em compile: você pode passar listas de arrays NumPy (com mapeamento 1:1 para as saídas que receberam uma função de perda) ou dicts mapeando nomes de saída para arrays NumPy.

model.compile( optimizer=keras.optimizers.RMSprop(1e-3), loss=[keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy()], ) # Generate dummy NumPy data img_data = np.random.random_sample(size=(100, 32, 32, 3)) ts_data = np.random.random_sample(size=(100, 20, 10)) score_targets = np.random.random_sample(size=(100, 1)) class_targets = np.random.random_sample(size=(100, 5)) # Fit on lists model.fit([img_data, ts_data], [score_targets, class_targets], batch_size=32, epochs=1) # Alternatively, fit on dicts model.fit( {"img_input": img_data, "ts_input": ts_data}, {"score_output": score_targets, "class_output": class_targets}, batch_size=32, epochs=1, )

Aqui está o caso de uso Dataset: da mesma forma que fizemos para os arrays NumPy, o Dataset deve retornar uma tupla de dicts.

train_dataset = tf.data.Dataset.from_tensor_slices( ( {"img_input": img_data, "ts_input": ts_data}, {"score_output": score_targets, "class_output": class_targets}, ) ) train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64) model.fit(train_dataset, epochs=1)

Usando callbacks

Callbacks em Keras são objetos chamados em diferentes pontos durante o treinamento (no início de uma época, no final de um lote, no final de uma época, etc.). Eles podem ser usados ​​para implementar certos comportamentos, como:

  • Validação em diferentes pontos durante o treinamento (além da validação por época integrada)

  • Checkpointing do modelo em intervalos regulares ou quando ele excede um determinado limite de exatidão

  • Alterando a taxa de aprendizado do modelo quando o treinamento parecer estagnado

  • Fazendo o ajuste fino das camadas superiores quando o treinamento parecer estar estagnado

  • Enviando de notificações por e-mail ou mensagem instantânea quando o treinamento terminar ou quando um determinado limite de desempenho for excedido

  • etc.

Callbacks podem ser passados ​​como uma lista para sua chamada para fit():

model = get_compiled_model() callbacks = [ keras.callbacks.EarlyStopping( # Stop training when `val_loss` is no longer improving monitor="val_loss", # "no longer improving" being defined as "no better than 1e-2 less" min_delta=1e-2, # "no longer improving" being further defined as "for at least 2 epochs" patience=2, verbose=1, ) ] model.fit( x_train, y_train, epochs=20, batch_size=64, callbacks=callbacks, validation_split=0.2, )

Há muitos callbacks disponíveis

Existem muitos callbacks integrados ao Keras, como:

  • ModelCheckpoint : Salva o modelo periodicamente.

  • EarlyStopping : Interrompe o treinamento quando o treinamento não estiver mais melhorando as métricas de validação.

  • TensorBoard : escreve logs de modelo periodicamente que podem ser visualizados no TensorBoard (mais detalhes na seção "Visualização").

  • CSVLogger: transmite dados de perda e métricas para um arquivo CSV.

  • etc.

Consulte a documentação de callbacks para obter a lista completa.

Escrevendo seu próprio callback

Você pode criar um callback personalizado estendendo a classe base keras.callbacks.Callback. Um callback tem acesso ao modelo associado através da propriedade de classe self.model.

Não deixe de ler o guia completo para escrever callbacks personalizados.

Aqui está um exemplo simples de salvamento de uma lista de valores de perda por lote durante o treinamento:

class LossHistory(keras.callbacks.Callback): def on_train_begin(self, logs): self.per_batch_losses = [] def on_batch_end(self, batch, logs): self.per_batch_losses.append(logs.get("loss"))

Modelos de checkpoint

Quando você está treinando o modelo em datasets relativamente grandes, é crucial salvar os checkpoints do seu modelo em intervalos frequentes.

A maneira mais fácil de conseguir isso é com o callback ModelCheckpoint:

model = get_compiled_model() callbacks = [ keras.callbacks.ModelCheckpoint( # Path where to save the model # The two parameters below mean that we will overwrite # the current checkpoint if and only if # the `val_loss` score has improved. # The saved model name will include the current epoch. filepath="mymodel_{epoch}", save_best_only=True, # Only save a model if `val_loss` has improved. monitor="val_loss", verbose=1, ) ] model.fit( x_train, y_train, epochs=2, batch_size=64, callbacks=callbacks, validation_split=0.2 )

O callback ModelCheckpoint pode ser usado para implementar tolerância a falhas: a capacidade de reiniciar o treinamento a partir do último estado salvo do modelo caso o treinamento seja interrompido aleatoriamente. Aqui está um exemplo básico:

import os # Prepare a directory to store all the checkpoints. checkpoint_dir = "./ckpt" if not os.path.exists(checkpoint_dir): os.makedirs(checkpoint_dir) def make_or_restore_model(): # Either restore the latest model, or create a fresh one # if there is no checkpoint available. checkpoints = [checkpoint_dir + "/" + name for name in os.listdir(checkpoint_dir)] if checkpoints: latest_checkpoint = max(checkpoints, key=os.path.getctime) print("Restoring from", latest_checkpoint) return keras.models.load_model(latest_checkpoint) print("Creating a new model") return get_compiled_model() model = make_or_restore_model() callbacks = [ # This callback saves a SavedModel every 100 batches. # We include the training loss in the saved model name. keras.callbacks.ModelCheckpoint( filepath=checkpoint_dir + "/ckpt-loss={loss:.2f}", save_freq=100 ) ] model.fit(x_train, y_train, epochs=1, callbacks=callbacks)

Você também pode escrever seu próprio callback para salvar e restaurar modelos.

Para um guia completo sobre serialização e salvamento, consulte o guia para salvar e serializar modelos.

Usando cronogramas de taxa de aprendizado

Um padrão comum ao treinar modelos de aprendizado profundo é reduzir gradualmente o aprendizado à medida que o treinamento avança. Isso é geralmente conhecido como "decaimento da taxa de aprendizado".

O cronograma de decaimento do aprendizado pode ser estático (fixado antecipadamente, em função da época atual ou do índice do lote atual) ou dinâmico (respondendo ao comportamento atual do modelo, em particular, a perda de validação).

Passando um cronograma para um otimizador

Você pode usar um cronograma de decaimento de taxa de aprendizado estático facilmente, passando um objeto de cronograma como o argumento learning_rate no seu otimizador:

initial_learning_rate = 0.1 lr_schedule = keras.optimizers.schedules.ExponentialDecay( initial_learning_rate, decay_steps=100000, decay_rate=0.96, staircase=True ) optimizer = keras.optimizers.RMSprop(learning_rate=lr_schedule)

Vários cronogramas estão disponíveis: ExponentialDecay, PiecewiseConstantDecay, PolynomialDecay e InverseTimeDecay.

Usando callbacks para implementar um cronograma de taxa de aprendizado dinâmico

Um cronograma de taxa de aprendizado dinâmico (por exemplo, diminuindo a taxa de aprendizado quando a perda de validação não está mais melhorando) não pode ser alcançado com esses objetos de cronograma, pois o otimizador não tem acesso às métricas de validação.

No entanto, os callbacks têm acesso a todas as métricas, incluindo métricas de validação! Assim, você pode obter esse padrão usando um callback que modifica a taxa de aprendizado atual no otimizador. Na verdade, isto faz parte da API como o callback ReduceLROnPlateau.

Visualizando perda e métricas durante o treinamento

A melhor maneira de ficar de olho no seu modelo durante o treinamento é usar o TensorBoard, um aplicativo que roda no navegador, que pode ser executado localmente e que fornece:

  • Plotagens ao vivo da perda e métricas para treinamento e avaliação

  • (opcionalmente) Visualizações dos histogramas de suas ativações de camada

  • (opcionalmente) visualizações 3D dos espaços de incorporação aprendidos por suas camadas Embedding

Se você instalou o TensorFlow com pip, poderá iniciar o TensorBoard através da linha de comando:

tensorboard --logdir=/full_path_to_your_logs

Usando o callback TensorBoard

A maneira mais fácil de usar o TensorBoard com um modelo Keras e o método fit() é o callback TensorBoard.

No caso mais simples, apenas especifique onde deseja que o callback grave os logs e pronto:

keras.callbacks.TensorBoard( log_dir="/full_path_to_your_logs", histogram_freq=0, # How often to log histogram visualizations embeddings_freq=0, # How often to log embedding visualizations update_freq="epoch", ) # How often to write logs (default: once per epoch)

Para obter mais informações, consulte a documentação do callback TensorBoard .