O que são e como usá-los



O que são e como usá-los

Pré-processamento de dados: o que você faz com os dados antes de alimentá-los no modelo.
— Uma definição simples que, na prática, deixa muitas questões em aberto. Onde, exatamente, o pré-processamento deve parar e o modelo começar? Etapas como normalização ou várias transformações numéricas fazem parte do modelo ou do pré-processamento? E quanto ao aumento de dados? Em suma, a linha entre o que é pré-processamento e o que é modelagem sempre pareceu, nas bordas, um tanto fluida.

Nesta situação, o advento keras camadas de pré-processamento alteram uma imagem há muito acquainted.

Em termos concretos, com kerasduas alternativas tenderam a prevalecer: uma, fazer as coisas antecipadamente, em R; e dois, construir um tfdatasets gasoduto. O primeiro foi aplicado sempre que precisávamos de dados completos para extrair alguma informação resumida. Por exemplo, ao normalizar para uma média zero e um desvio padrão de um. Mas muitas vezes, isso significava que tínhamos que alternar entre versões normalizadas e não normalizadas em vários pontos do fluxo de trabalho. O tfdatasets a abordagem, por outro lado, period elegante; no entanto, pode ser necessário escrever muitos textos de baixo nível tensorflow código.

Camadas de pré-processamento, disponíveis a partir de keras versão 2.6.1, elimina a necessidade de operações R iniciais e integra-se perfeitamente com tfdatasets. Mas isso não é tudo que existe para eles. Neste publish, queremos destacar quatro aspectos essenciais:

  1. As camadas de pré-processamento reduzem significativamente o esforço de codificação. Você poderia codifique você mesmo essas operações; mas não ter que fazer isso economiza tempo, favorece o código modular e ajuda a evitar erros.
  2. Camadas de pré-processamento – um subconjunto delas, para ser mais preciso – podem produzir informações resumidas antes do treinamento propriamente dito e fazer uso de um estado salvo quando chamado posteriormente.
  3. Camadas de pré-processamento podem acelerar o treinamento.
  4. As camadas de pré-processamento são, ou podem ser feitas, parte do modelo, eliminando assim a necessidade de implementar procedimentos de pré-processamento independentes no ambiente de implantação.

Após uma breve introdução, expandiremos cada um desses pontos. Concluímos com dois exemplos completos (envolvendo imagens e textorespectivamente) que ilustram bem esses quatro aspectos.

Pré-processamento de camadas em poucas palavras

Como outros keras camadas, aquelas de que estamos falando aqui, todas começam com layer_e pode ser instanciado independentemente do modelo e do pipeline de dados. Aqui, criamos uma camada que irá girar as imagens aleatoriamente durante o treinamento, em até 45 graus em ambas as direções:

library(keras)
aug_layer <- layer_random_rotation(issue = 0.125)

Assim que tivermos essa camada, podemos testá-la imediatamente em alguma imagem fictícia.

tf.Tensor(
((1. 0. 0. 0. 0.)
 (0. 1. 0. 0. 0.)
 (0. 0. 1. 0. 0.)
 (0. 0. 0. 1. 0.)
 (0. 0. 0. 0. 1.)), form=(5, 5), dtype=float32)

“Testar a camada” agora significa literalmente chamando-o como uma função:

tf.Tensor(
((0.         0.         0.         0.         0.        )
 (0.44459596 0.32453176 0.05410459 0.         0.        )
 (0.15844001 0.4371609  1.         0.4371609  0.15844001)
 (0.         0.         0.05410453 0.3245318  0.44459593)
 (0.         0.         0.         0.         0.        )), form=(5, 5), dtype=float32)

Uma vez instanciada, uma camada pode ser usada de duas maneiras. Em primeiro lugar, como parte do pipeline de entrada.

Em pseudocódigo:

# pseudocode
library(tfdatasets)
 
train_ds <- ... # outline dataset
preprocessing_layer <- ... # instantiate layer

train_ds <- train_ds %>%
  dataset_map(perform(x, y) checklist(preprocessing_layer(x), y))

Em segundo lugar, a forma que parece mais pure, para uma camada: como uma camada dentro do modelo. Esquematicamente:

# pseudocode
enter <- layer_input(form = input_shape)

output <- enter %>%
  preprocessing_layer() %>%
  rest_of_the_model()

mannequin <- keras_model(enter, output)

Na verdade, esta última parece tão óbvia que você pode estar se perguntando: por que permitir uma tfdatasetsalternativa integrada? Expandiremos isso em breve, quando falarmos sobre desempenho.

Com estado camadas – que são especiais o suficiente para merecer seu seção própria – também podem ser usados ​​de ambas as maneiras, mas exigem uma etapa adicional. Mais sobre isso abaixo.

Como as camadas de pré-processamento facilitam a vida

Existem camadas dedicadas para uma infinidade de tarefas de transformação de dados. Podemos subsumi-los em duas categorias amplas: engenharia de recursos e aumento de dados.

Engenharia de recursos

A necessidade de engenharia de recursos pode surgir com todos os tipos de dados. Com imagens, normalmente não usamos esse termo para as operações “pedestres” que são necessárias para um modelo processá-las: redimensionamento, corte e assim por diante. Ainda assim, existem pressupostos ocultos em cada uma destas operações, pelo que nos sentimos justificados na nossa categorização. Seja como for, as camadas deste grupo incluem layer_resizing(), layer_rescaling()e layer_center_crop().

Com o texto, a única funcionalidade sem a qual não poderíamos prescindir é a vetorização. layer_text_vectorization() cuida disso para nós. Encontraremos essa camada na próxima seção, bem como na segundo exemplo de código completo.

Agora, vamos ao que normalmente é visto como o domínio da engenharia de recursos: dados numéricos e categóricos (poderíamos dizer: “planilha”).

Primeiro, os dados numéricos muitas vezes precisam ser normalizados para que as redes neurais tenham um bom desempenho – para conseguir isso, use layer_normalization(). Ou talvez haja uma razão pela qual gostaríamos de colocar valores contínuos em categorias discretas. Isso seria uma tarefa para layer_discretization().

Em segundo lugar, os dados categóricos vêm em vários formatos (strings, inteiros…), e há sempre algo isso precisa ser feito para processá-los de maneira significativa. Freqüentemente, você desejará incorporá-los em um espaço de dimensão superior, usando layer_embedding(). Agora, as camadas incorporadas esperam que suas entradas sejam números inteiros; para ser mais preciso: números inteiros consecutivos. Aqui, as camadas a serem procuradas são layer_integer_lookup() e layer_string_lookup(): Eles converterão números inteiros aleatórios (strings, respectivamente) em valores inteiros consecutivos. Num cenário diferente, pode haver demasiadas categorias para permitir a extracção de informações úteis. Nesses casos, use layer_hashing() para armazenar os dados. E finalmente, há layer_category_encoding() para produzir as representações clássicas one-hot ou multi-hot.

Aumento de dados

Na segunda categoria, encontramos camadas que executam (configurável) operações aleatórias em imagens. Para citar apenas alguns deles: layer_random_crop(), layer_random_translation(), layer_random_rotation() … Eles são convenientes não apenas porque implementam a funcionalidade de baixo nível necessária; quando integrados a um modelo, eles também reconhecem o fluxo de trabalho: quaisquer operações aleatórias serão executadas apenas durante o treinamento.

Agora que temos uma ideia do que essas camadas fazem por nós, vamos nos concentrar no caso específico das camadas que preservam o estado.

Camadas de pré-processamento que mantêm o estado

Uma camada que perturba imagens aleatoriamente não precisa saber nada sobre os dados. Só precisa seguir uma regra: com probabilidade (p)fazer (x). Uma camada que deveria vetorizar texto, por outro lado, precisa ter uma tabela de pesquisa, combinando cadeias de caracteres com números inteiros. O mesmo vale para uma camada que mapeia inteiros contingentes para um conjunto ordenado. E em ambos os casos, a tabela de consulta precisa ser construída antecipadamente.

Com camadas com estado, esse acúmulo de informações é acionado pela chamada adapt() em uma instância de camada recém-criada. Por exemplo, aqui instanciamos e “condicionamos” uma camada que mapeia strings para inteiros consecutivos:

colours <- c("cyan", "turquoise", "celeste");

layer <- layer_string_lookup()
layer %>% adapt(colours)

Podemos verificar o que está na tabela de pesquisa:

(1) "(UNK)"     "turquoise" "cyan"      "celeste"  

Então, chamar a camada codificará os argumentos:

layer(c("azure", "cyan"))
tf.Tensor((0 2), form=(2,), dtype=int64)

layer_string_lookup() funciona em cadeias de caracteres individuais e, conseqüentemente, é a transformação adequada para recursos categóricos com valor de cadeia. Para codificar frases inteiras (ou parágrafos, ou qualquer pedaço de texto), você usaria layer_text_vectorization() em vez de. Veremos como isso funciona em nosso segundo exemplo de ponta a ponta.

Usando camadas de pré-processamento para desempenho

Acima, dissemos que as camadas de pré-processamento poderiam ser usadas de duas maneiras: como parte do modelo ou como parte do pipeline de entrada de dados. Se estes são camadaspor que permitir a segunda maneira?

O principal motivo é o desempenho. As GPUs são ótimas em operações matriciais regulares, como aquelas envolvidas na manipulação de imagens e transformações de dados numéricos de formato uniforme. Portanto, se você tiver uma GPU para treinar, é preferível ter camadas de processamento de imagem, ou camadas como layer_normalization()faça parte do modelo (que é executado totalmente em GPU).

Por outro lado, operações envolvendo texto, como layer_text_vectorization()são melhor executados na CPU. O mesmo vale se nenhuma GPU estiver disponível para treinamento. Nesses casos, você moveria as camadas para o pipeline de entrada e se esforçaria para se beneficiar do processamento paralelo – na CPU. Por exemplo:

# pseudocode

preprocessing_layer <- ... # instantiate layer

dataset <- dataset %>%
  dataset_map(~checklist(text_vectorizer(.x), .y),
              num_parallel_calls = tf$information$AUTOTUNE) %>%
  dataset_prefetch()
mannequin %>% match(dataset)

Da mesma forma, nos exemplos completos abaixo, você verá o aumento de dados de imagem acontecendo como parte do modelo e a vetorização de texto, como parte do pipeline de entrada.

Exportando um modelo completo com pré-processamento

Digamos que para treinar seu modelo, você descobriu que o tfdatasets caminho foi o melhor. Agora, você o implanta em um servidor que não possui o R instalado. Parece que também é necessário implementar o pré-processamento em alguma outra tecnologia disponível. Como alternativa, você teria que contar com o envio de dados já pré-processados ​​pelos usuários.

Felizmente, há outra coisa que você pode fazer. Crie um novo modelo especificamente para inferência, assim:

# pseudocode

enter <- layer_input(form = input_shape)

output <- enter %>%
  preprocessing_layer(enter) %>%
  training_model()

inference_model <- keras_model(enter, output)

Esta técnica faz uso do API funcional para criar um novo modelo que antecede a camada de pré-processamento ao modelo unique sem pré-processamento.

Tendo nos concentrado em algumas coisas especialmente “boas de saber”, concluímos agora com os exemplos prometidos.

Exemplo 1: aumento de dados de imagem

Nosso primeiro exemplo demonstra o aumento de dados de imagem. Três tipos de transformações são agrupados, fazendo com que se destaquem claramente na definição geral do modelo. Este grupo de camadas estará ativo apenas durante o treinamento.

library(keras)
library(tfdatasets)

# Load CIFAR-10 information that include keras
c(c(x_train, y_train), ...) %<-% dataset_cifar10()
input_shape <- dim(x_train)(-1) # drop batch dim
courses <- 10

# Create a tf_dataset pipeline 
train_dataset <- tensor_slices_dataset(checklist(x_train, y_train)) %>%
  dataset_batch(16) 

# Use a (non-trained) ResNet structure
resnet <- application_resnet50(weights = NULL,
                               input_shape = input_shape,
                               courses = courses)

# Create an information augmentation stage with horizontal flipping, rotations, zooms
data_augmentation <-
  keras_model_sequential() %>%
  layer_random_flip("horizontal") %>%
  layer_random_rotation(0.1) %>%
  layer_random_zoom(0.1)

enter <- layer_input(form = input_shape)

# Outline and run the mannequin
output <- enter %>%
  layer_rescaling(1 / 255) %>%   # rescale inputs
  data_augmentation() %>%
  resnet()

mannequin <- keras_model(enter, output) %>%
  compile(optimizer = "rmsprop", loss = "sparse_categorical_crossentropy") %>%
  match(train_dataset, steps_per_epoch = 5)

Exemplo 2: vetorização de texto

No processamento de linguagem pure, muitas vezes usamos camadas de incorporação para apresentar as camadas “burras de carga” (recorrentes, convolucionais, autoatencionais, o que quer que seja) com a entrada contínua e dimensionada de maneira ultimate de que precisam. Incorporar camadas espera que os tokens sejam codificados como números inteiros e transformar texto em números inteiros é o que layer_text_vectorization() faz.

Nosso segundo exemplo demonstra o fluxo de trabalho: você faz com que a camada aprenda o vocabulário antecipadamente e depois o chame como parte do pipeline de pré-processamento. Assim que o treinamento terminar, criamos um modelo “tudo incluído” para implantação.

library(tensorflow)
library(tfdatasets)
library(keras)

# Instance information
textual content <- as_tensor(c(
  "From every in response to his skill, to every in response to his wants!",
  "Act that you just use humanity, whether or not in your individual individual or within the individual of another, all the time similtaneously an finish, by no means merely as a method.",
  "Purpose is, and ought solely to be the slave of the passions, and might by no means fake to another workplace than to serve and obey them."
))

# Create and adapt layer
text_vectorizer <- layer_text_vectorization(output_mode="int")
text_vectorizer %>% adapt(textual content)

# Verify
as.array(text_vectorizer("To every in response to his wants"))

# Create a easy classification mannequin
enter <- layer_input(form(NULL), dtype="int64")

output <- enter %>%
  layer_embedding(input_dim = text_vectorizer$vocabulary_size(),
                  output_dim = 16) %>%
  layer_gru(8) %>%
  layer_dense(1, activation = "sigmoid")

mannequin <- keras_model(enter, output)

# Create a labeled dataset (which incorporates unknown tokens)
train_dataset <- tensor_slices_dataset(checklist(
    c("From every in response to his skill", "There's nothing larger than cause."),
    c(1L, 0L)
))

# Preprocess the string inputs
train_dataset <- train_dataset %>%
  dataset_batch(2) %>%
  dataset_map(~checklist(text_vectorizer(.x), .y),
              num_parallel_calls = tf$information$AUTOTUNE)

# Practice the mannequin
mannequin %>%
  compile(optimizer = "adam", loss = "binary_crossentropy") %>%
  match(train_dataset)

# export inference mannequin that accepts strings as enter
enter <- layer_input(form = 1, dtype="string")
output <- enter %>%
  text_vectorizer() %>%
  mannequin()

end_to_end_model <- keras_model(enter, output)

# Check inference mannequin
test_data <- as_tensor(c(
  "To every in response to his wants!",
  "Purpose is, and ought solely to be the slave of the passions."
))
test_output <- end_to_end_model(test_data)
as.array(test_output)

Conclusão

Com este publish, nosso objetivo foi chamar a atenção para keras‘ novas camadas de pré-processamento e mostrar como – e por que – elas são úteis. Muitos outros casos de uso podem ser encontrados no vinheta.

Obrigado por ler!

Foto de Henning Borgersen sobre Remover respingo

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *