Path: blob/master/site/pt-br/guide/keras/train_and_evaluate.ipynb
25118 views
Copyright 2020 The TensorFlow Authors.
Treinamento e avaliação com os métodos integrados
Configuração
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):
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.
Especificamos a configuração de treinamento (otimizador, perda, métricas):
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
.
O objeto history
retornado contém um registro dos valores de perda e valores de métrica durante o treinamento:
Avaliamos o modelo nos dados de teste via evaluate()
:
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()
:
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:
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.
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:
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 perdacall(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:
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:
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):
Você pode fazer o mesmo para registrar em log valores de métricas, usando add_metric()
:
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:
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()
.
Você pode usá-la num modelo com duas entradas (dados de entrada e alvos), compilados sem um argumento loss
, assim:
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.
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()
:
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).
Usando um dataset de validação
Você pode passar uma instância Dataset
como o validation_data
em fit()
:
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:
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ídoObjetos
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
emfit()
).
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:
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).
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
paraModel.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).
Aqui está um exemplo Dataset
correspondente:
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,)
).
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).
Em tempo de compilação, podemos especificar diferentes perdas para diferentes saídas, passando as funções de perda como uma lista:
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:
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:
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
:
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:
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.
Aqui está o caso de uso Dataset
: da mesma forma que fizemos para os arrays NumPy, o Dataset
deve retornar uma tupla de dicts.
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()
:
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:
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
:
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:
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:
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:
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:
Para obter mais informações, consulte a documentação do callback TensorBoard
.