Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/pt-br/guide/keras/preprocessing_layers.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.

Trabalhando com camadas de pré-processamento

Pré-processamento com o Keras

A API de camadas de pré-processamento do Keras permite que desenvolvedores criem pipelines de processamento de entrada nativos do Keras. Esses pipelines de processamento de entrada podem ser usados ​​como código de pré-processamento independente em outros workflows (que não usam Keras), combinados diretamente com modelos Keras e exportados como parte de um Keras SavedModel.

Com as camadas de pré-processamento do Keras, você pode construir e exportar modelos que são realmente modelos de ponta a ponta: modelos que aceitam imagens brutas ou dados estruturados brutos como entrada; modelos que lidam com normalização de recursos ou indexação de valores de recursos por conta própria.

Pré-processamentos disponíveis

Pré-processamento de texto

  • tf.keras.layers.TextVectorization: transforma strings brutas em uma representação codificada que pode ser lida por uma camada Embedding ou camada Dense.

Pré-processamento de recursos numéricos

  • tf.keras.layers.Normalization: executa uma normalização dos recursos de entrada.

  • tf.keras.layers.Discretization: transforma recursos numéricos contínuos em recursos categóricos inteiros.

Pré-processamento de recursos categóricos

  • tf.keras.layers.CategoryEncoding: transforma recursos categóricos inteiros em representações one-hot, multi-hot ou contagens densas.

  • tf.keras.layers.Hashing: realiza hashing de recursos categóricos, também conhecido como "truque de hash".

  • tf.keras.layers.StringLookup: transforma valores categóricos de string em uma representação codificada que pode ser lida por uma camada Embedding ou camada Dense.

  • tf.keras.layers.IntegerLookup: transforma valores categóricos inteiros em uma representação codificada que pode ser lida por uma camada Embedding ou camada Dense.

Pré-processamento de imagens

Estas camadas servem para padronizar as entradas de um modelo de imagem.

  • tf.keras.layers.Resizing: redimensiona um lote de imagens para um tamanho-alvo.

  • tf.keras.layers.Rescaling: redimensiona e desloca os valores de um lote de imagem (por exemplo, das entradas no intervalo [0, 255] para entradas no intervalo [0, 1].

  • tf.keras.layers.CenterCrop: retorna um corte central de um lote de imagens.

Ampliação de dados de imagem

Essas camadas aplicam transformações de ampliação aleatória a um lote de imagens. Elas só são ativas durante o treinamento.

  • tf.keras.layers.RandomCrop

  • tf.keras.layers.RandomFlip

  • tf.keras.layers.RandomTranslation

  • tf.keras.layers.RandomRotation

  • tf.keras.layers.RandomZoom

  • tf.keras.layers.RandomHeight

  • tf.keras.layers.RandomWidth

  • tf.keras.layers.RandomContrast

O método adapt()

Algumas camadas de pré-processamento têm um estado interno que pode ser computado com base numa amostra dos dados de treinamento. A lista de camadas de pré-processamento stateful é:

  • TextVectorization: mantém um mapeamento entre tokens de string e índices inteiros

  • StringLookup e IntegerLookup: mantêm um mapeamento entre valores de entrada e índices inteiros.

  • Normalization: mantém a média e o desvio padrão dos recursos.

  • Discretization: mantém informações sobre limites de buckets de valores.

Crucialmente, essas camadas não são treináveis. Seu estado não é definido durante o treinamento; ele precisa ser definido antes do treinamento, ou inicializados a partir de uma constante pré-computada ou "adaptando-os" aos dados.

Você define o estado de uma camada de pré-processamento expondo-a a dados de treinamento através do método adapt():

import numpy as np import tensorflow as tf from tensorflow.keras import layers data = np.array([[0.1, 0.2, 0.3], [0.8, 0.9, 1.0], [1.5, 1.6, 1.7],]) layer = layers.Normalization() layer.adapt(data) normalized_data = layer(data) print("Features mean: %.2f" % (normalized_data.numpy().mean())) print("Features std: %.2f" % (normalized_data.numpy().std()))

O método adapt() usa um array Numpy ou um objeto tf.data.Dataset. No caso de StringLookup e TextVectorization, você também pode passar uma lista de strings:

data = [ "ξεῖν᾽, ἦ τοι μὲν ὄνειροι ἀμήχανοι ἀκριτόμυθοι", "γίγνοντ᾽, οὐδέ τι πάντα τελείεται ἀνθρώποισι.", "δοιαὶ γάρ τε πύλαι ἀμενηνῶν εἰσὶν ὀνείρων:", "αἱ μὲν γὰρ κεράεσσι τετεύχαται, αἱ δ᾽ ἐλέφαντι:", "τῶν οἳ μέν κ᾽ ἔλθωσι διὰ πριστοῦ ἐλέφαντος,", "οἵ ῥ᾽ ἐλεφαίρονται, ἔπε᾽ ἀκράαντα φέροντες:", "οἱ δὲ διὰ ξεστῶν κεράων ἔλθωσι θύραζε,", "οἵ ῥ᾽ ἔτυμα κραίνουσι, βροτῶν ὅτε κέν τις ἴδηται.", ] layer = layers.TextVectorization() layer.adapt(data) vectorized_text = layer(data) print(vectorized_text)

Além disso, as camadas adaptáveis ​​sempre apresentam uma alternativa para definir diretamente o estado via argumentos do construtor ou atribuição de peso. Se os valores de estado pretendidos forem conhecidos no momento da construção da camada ou forem calculados fora da chamada adapt(), eles poderão ser definidos sem depender da computação interna da camada. Por exemplo, se já existirem arquivos de vocabulário externos para as camadas TextVectorization, StringLookup ou IntegerLookup, eles podem ser carregados diretamente nas tabelas de pesquisa passando um caminho para o arquivo de vocabulário nos argumentos do construtor da camada.

Aqui está um exemplo onde instanciamos uma camada StringLookup com vocabulário pré-computado:

vocab = ["a", "b", "c", "d"] data = tf.constant([["a", "c", "d"], ["d", "z", "b"]]) layer = layers.StringLookup(vocabulary=vocab) vectorized_data = layer(data) print(vectorized_data)

Pré-processamento de dados antes do modelo ou dentro do modelo

Há duas maneiras de usar camadas de pré-processamento:

Opção 1: Torná-las parte do modelo, assim:

inputs = keras.Input(shape=input_shape) x = preprocessing_layer(inputs) outputs = rest_of_the_model(x) model = keras.Model(inputs, outputs)

Com esta opção, o pré-processamento acontecerá no dispositivo, de forma sincronizada com o restante da execução do modelo, o que significa que ele se beneficiará da aceleração da GPU. Se você estiver treinando em GPU, esta é a melhor opção para a camada Normalization e para todas as camadas de pré-processamento de imagem e ampliação de dados.

Opção 2: aplicá-las ao seu tf.data.Dataset, para obter um dataset que gere lotes de dados pré-processados, como este:

dataset = dataset.map(lambda x, y: (preprocessing_layer(x), y))

Com esta opção, seu pré-processamento acontecerá na CPU, de forma assíncrona, e será armazenado em buffer antes de entrar no modelo. Além disso, se você chamar dataset.prefetch(tf.data.AUTOTUNE) em seu dataset, o pré-processamento acontecerá de forma eficiente em paralelo com o treinamento:

dataset = dataset.map(lambda x, y: (preprocessing_layer(x), y)) dataset = dataset.prefetch(tf.data.AUTOTUNE) model.fit(dataset, ...)

Esta é a melhor alternativa para TextVectorization e todas as camadas de pré-processamento de dados estruturados. Também pode ser uma boa opção se você estiver treinando na CPU e usa camadas de pré-processamento de imagem.

Ao executar em TPU, você sempre deve colocar as camadas de pré-processamento no pipeline tf.data (com exceção de Normalization e Rescaling, que funcionam bem em TPU e são frequentemente usadas ​​porque a primeira camada é um modelo de imagem).

Vantagens de fazer o pré-processamento dentro do modelo na hora da inferência

Mesmo que você escolha a opção 2, poderá desejar exportar posteriormente um modelo de ponta a ponta, de inferência apenas, que incluirá as camadas de pré-processamento. A principal vantagem de se fazer isso é que torna seu modelo portátil e ajuda a reduzir o desvio de treinamento/serviço.

Quando todo o pré-processamento de dados faz parte do modelo, outras pessoas podem carregar e usar seu modelo sem precisar saber como cada recurso deve ser codificado e normalizado. Seu modelo de inferência será capaz de processar imagens brutas ou dados estruturados brutos e não exigirá que os usuários do modelo estejam cientes dos detalhes de, por exemplo, o esquema de tokenização usado para texto, o esquema de indexação usado para recursos categóricos, se os valores de pixel da imagem são normalizados para [-1, +1] ou para [0, 1], etc. Isto é muito poderoso se você estiver exportando seu modelo para outro ambiente de execução, como TensorFlow.js: você não precisará reimplementar seu pipeline de pré-processamento em JavaScript.

Se você inicialmente colocar suas camadas de pré-processamento no seu pipeline tf.data, poderá exportar um modelo de inferência que empacote o pré-processamento. Basta instanciar um novo modelo que encadeia suas camadas de pré-processamento e seu modelo de treinamento:

inputs = keras.Input(shape=input_shape) x = preprocessing_layer(inputs) outputs = training_model(x) inference_model = keras.Model(inputs, outputs)

Receitas rápidas

Ampliação de dados de imagem

Observe que as camadas de ampliação de dados de imagem são ativas apenas durante o treinamento (semelhante à camada Dropout ).

from tensorflow import keras from tensorflow.keras import layers # Create a data augmentation stage with horizontal flipping, rotations, zooms data_augmentation = keras.Sequential( [ layers.RandomFlip("horizontal"), layers.RandomRotation(0.1), layers.RandomZoom(0.1), ] ) # Load some data (x_train, y_train), _ = keras.datasets.cifar10.load_data() input_shape = x_train.shape[1:] classes = 10 # Create a tf.data pipeline of augmented images (and their labels) train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)) train_dataset = train_dataset.batch(16).map(lambda x, y: (data_augmentation(x), y)) # Create a model and train it on the augmented image data inputs = keras.Input(shape=input_shape) x = layers.Rescaling(1.0 / 255)(inputs) # Rescale inputs outputs = keras.applications.ResNet50( # Add the rest of the model weights=None, input_shape=input_shape, classes=classes )(x) model = keras.Model(inputs, outputs) model.compile(optimizer="rmsprop", loss="sparse_categorical_crossentropy") model.fit(train_dataset, steps_per_epoch=5)

Você pode ver uma configuração semelhante em ação no exemplo classificação de imagem do zero.

Normalizando recursos numéricos

# Load some data (x_train, y_train), _ = keras.datasets.cifar10.load_data() x_train = x_train.reshape((len(x_train), -1)) input_shape = x_train.shape[1:] classes = 10 # Create a Normalization layer and set its internal state using the training data normalizer = layers.Normalization() normalizer.adapt(x_train) # Create a model that include the normalization layer inputs = keras.Input(shape=input_shape) x = normalizer(inputs) outputs = layers.Dense(classes, activation="softmax")(x) model = keras.Model(inputs, outputs) # Train the model model.compile(optimizer="adam", loss="sparse_categorical_crossentropy") model.fit(x_train, y_train)

Codificando recursos categóricos de string via one-hot encoding

# Define some toy data data = tf.constant([["a"], ["b"], ["c"], ["b"], ["c"], ["a"]]) # Use StringLookup to build an index of the feature values and encode output. lookup = layers.StringLookup(output_mode="one_hot") lookup.adapt(data) # Convert new test data (which includes unknown feature values) test_data = tf.constant([["a"], ["b"], ["c"], ["d"], ["e"], [""]]) encoded_data = lookup(test_data) print(encoded_data)

Observe que, aqui, o índice 0 é reservado para valores fora do vocabulário (valores que não foram vistos durante adapt()).

Você pode ver o StringLookup em ação no exemplo classificação de dados estruturados do zero.

Codificando recursos categóricos inteiros via one-hot encoding

# Define some toy data data = tf.constant([[10], [20], [20], [10], [30], [0]]) # Use IntegerLookup to build an index of the feature values and encode output. lookup = layers.IntegerLookup(output_mode="one_hot") lookup.adapt(data) # Convert new test data (which includes unknown feature values) test_data = tf.constant([[10], [10], [20], [50], [60], [0]]) encoded_data = lookup(test_data) print(encoded_data)

Observe que o índice 0 é reservado para valores ausentes (que você deve especificar como o valor 0) e o índice 1 é reservado para valores fora do vocabulário (valores que não foram vistos durante adapt()). Isto pode ser configurado com os argumentos mask_token e oov_token do construtor IntegerLookup.

Você pode ver o IntegerLookup em ação no exemplo classificação de dados estruturados do zero.

Aplicando o truque de hash a um recurso categórico inteiro

Se você tiver um recurso categórico que pode assumir muitos valores diferentes (na ordem de 10e3 ou superior), onde cada valor aparece apenas algumas vezes nos dados, torna-se impraticável e ineficaz indexar e fazer one-hot encoding dos valores do recurso. Em vez disso, pode ser uma boa ideia aplicar o "truque de hash": fazer hash dos valores para um vetor de tamanho fixo. Isto mantém o tamanho do espaço de recurso gerenciável e remove a necessidade de indexação explícita.

# Sample data: 10,000 random integers with values between 0 and 100,000 data = np.random.randint(0, 100000, size=(10000, 1)) # Use the Hashing layer to hash the values to the range [0, 64] hasher = layers.Hashing(num_bins=64, salt=1337) # Use the CategoryEncoding layer to multi-hot encode the hashed values encoder = layers.CategoryEncoding(num_tokens=64, output_mode="multi_hot") encoded_data = encoder(hasher(data)) print(encoded_data.shape)

Codificando texto como uma sequência de índices de token

É assim que você deve pré-processar o texto a ser passado para uma camada Embedding.

# Define some text data to adapt the layer adapt_data = tf.constant( [ "The Brain is wider than the Sky", "For put them side by side", "The one the other will contain", "With ease and You beside", ] ) # Create a TextVectorization layer text_vectorizer = layers.TextVectorization(output_mode="int") # Index the vocabulary via `adapt()` text_vectorizer.adapt(adapt_data) # Try out the layer print( "Encoded text:\n", text_vectorizer(["The Brain is deeper than the sea"]).numpy(), ) # Create a simple model inputs = keras.Input(shape=(None,), dtype="int64") x = layers.Embedding(input_dim=text_vectorizer.vocabulary_size(), output_dim=16)(inputs) x = layers.GRU(8)(x) outputs = layers.Dense(1)(x) model = keras.Model(inputs, outputs) # Create a labeled dataset (which includes unknown tokens) train_dataset = tf.data.Dataset.from_tensor_slices( (["The Brain is deeper than the sea", "for if they are held Blue to Blue"], [1, 0]) ) # Preprocess the string inputs, turning them into int sequences train_dataset = train_dataset.batch(2).map(lambda x, y: (text_vectorizer(x), y)) # Train the model on the int sequences print("\nTraining model...") model.compile(optimizer="rmsprop", loss="mse") model.fit(train_dataset) # For inference, you can export a model that accepts strings as input inputs = keras.Input(shape=(1,), dtype="string") x = text_vectorizer(inputs) outputs = model(x) end_to_end_model = keras.Model(inputs, outputs) # Call the end-to-end model on test data (which includes unknown tokens) print("\nCalling end-to-end model on test string...") test_data = tf.constant(["The one the other will absorb"]) test_output = end_to_end_model(test_data) print("Model output:", test_output)

Você pode ver a camada TextVectorization em ação, combinada com um modo Embedding, no exemplo classificação de texto do zero.

Observe que, ao treinar tal modelo, para melhor desempenho, você deve sempre usar a camada TextVectorization como parte do pipeline de entrada.

Codificando texto como uma matriz densa de ngrams com multi-hot encoding

É assim que você deve pré-processar o texto a ser passado para uma camada Dense.

# Define some text data to adapt the layer adapt_data = tf.constant( [ "The Brain is wider than the Sky", "For put them side by side", "The one the other will contain", "With ease and You beside", ] ) # Instantiate TextVectorization with "multi_hot" output_mode # and ngrams=2 (index all bigrams) text_vectorizer = layers.TextVectorization(output_mode="multi_hot", ngrams=2) # Index the bigrams via `adapt()` text_vectorizer.adapt(adapt_data) # Try out the layer print( "Encoded text:\n", text_vectorizer(["The Brain is deeper than the sea"]).numpy(), ) # Create a simple model inputs = keras.Input(shape=(text_vectorizer.vocabulary_size(),)) outputs = layers.Dense(1)(inputs) model = keras.Model(inputs, outputs) # Create a labeled dataset (which includes unknown tokens) train_dataset = tf.data.Dataset.from_tensor_slices( (["The Brain is deeper than the sea", "for if they are held Blue to Blue"], [1, 0]) ) # Preprocess the string inputs, turning them into int sequences train_dataset = train_dataset.batch(2).map(lambda x, y: (text_vectorizer(x), y)) # Train the model on the int sequences print("\nTraining model...") model.compile(optimizer="rmsprop", loss="mse") model.fit(train_dataset) # For inference, you can export a model that accepts strings as input inputs = keras.Input(shape=(1,), dtype="string") x = text_vectorizer(inputs) outputs = model(x) end_to_end_model = keras.Model(inputs, outputs) # Call the end-to-end model on test data (which includes unknown tokens) print("\nCalling end-to-end model on test string...") test_data = tf.constant(["The one the other will absorb"]) test_output = end_to_end_model(test_data) print("Model output:", test_output)

Codificando texto como uma matriz densa de ngrams com ponderação TF-IDF

Esta é uma forma alternativa de pré-processar o texto antes de passá-lo para uma camada Dense.

# Define some text data to adapt the layer adapt_data = tf.constant( [ "The Brain is wider than the Sky", "For put them side by side", "The one the other will contain", "With ease and You beside", ] ) # Instantiate TextVectorization with "tf-idf" output_mode # (multi-hot with TF-IDF weighting) and ngrams=2 (index all bigrams) text_vectorizer = layers.TextVectorization(output_mode="tf-idf", ngrams=2) # Index the bigrams and learn the TF-IDF weights via `adapt()` with tf.device("CPU"): # A bug that prevents this from running on GPU for now. text_vectorizer.adapt(adapt_data) # Try out the layer print( "Encoded text:\n", text_vectorizer(["The Brain is deeper than the sea"]).numpy(), ) # Create a simple model inputs = keras.Input(shape=(text_vectorizer.vocabulary_size(),)) outputs = layers.Dense(1)(inputs) model = keras.Model(inputs, outputs) # Create a labeled dataset (which includes unknown tokens) train_dataset = tf.data.Dataset.from_tensor_slices( (["The Brain is deeper than the sea", "for if they are held Blue to Blue"], [1, 0]) ) # Preprocess the string inputs, turning them into int sequences train_dataset = train_dataset.batch(2).map(lambda x, y: (text_vectorizer(x), y)) # Train the model on the int sequences print("\nTraining model...") model.compile(optimizer="rmsprop", loss="mse") model.fit(train_dataset) # For inference, you can export a model that accepts strings as input inputs = keras.Input(shape=(1,), dtype="string") x = text_vectorizer(inputs) outputs = model(x) end_to_end_model = keras.Model(inputs, outputs) # Call the end-to-end model on test data (which includes unknown tokens) print("\nCalling end-to-end model on test string...") test_data = tf.constant(["The one the other will absorb"]) test_output = end_to_end_model(test_data) print("Model output:", test_output)

Detalhes importantes

Trabalhando com camadas de pesquisa com vocabulários muito grandes

Você talvez se encontre trabalhando com um vocabulário muito grande numa TextVectorization, numa camada StringLookup ou numa camada IntegerLookup. Normalmente, um vocabulário maior que 500 MB já seria considerado "muito grande".

Em tais casos, para melhor desempenho, evite usar adapt(). Em vez disso, pré-compute seu vocabulário com antecedência (você pode usar o Apache Beam ou o TF Transform para isso) e armazene-o em um arquivo. Em seguida, carregue o vocabulário na camada no momento da construção, passando o caminho do arquivo como o argumento vocabulary.

Usando camadas de pesquisa em um pod de TPU ou com ParameterServerStrategy.

Há um bug importante que causa a degradação do desempenho ao usar uma camada TextVectorization, StringLookup ou IntegerLookup durante o treinamento em um pod de TPU ou em várias máquinas via ParameterServerStrategy. Isso está agendado para ser corrigido no TensorFlow 2.7.