1. Visão geral
Neste laboratório, você aprenderá a montar uma camada convolucional em um modelo de rede neural que reconhece flores. Desta vez, você vai criar o modelo do zero e usar o poder da TPU para treiná-lo em segundos e iterar o design.
Este laboratório inclui as explicações teóricas necessárias sobre redes neurais convolucionais e é um bom ponto de partida para desenvolvedores que estão aprendendo sobre aprendizado profundo.
Este laboratório é a Parte 3 da série "Keras on TPU". Você pode fazer isso na ordem a seguir ou de forma independente.
- Pipelines de dados com velocidade de TPU: tf.data.Dataset e TFRecords
- Seu primeiro modelo do Keras, com aprendizado de transferência
- [THIS LAB] Redes neurais convolucionais com Keras e TPUs
- Convnets modernas, squeezenet, xception, com Keras e TPUs
O que você vai aprender
- Para criar um classificador de imagens convolucional usando um modelo sequencial do Keras.
- Para treinar seu modelo Keras em TPU
- Para ajustar seu modelo com uma boa escolha de camadas convolucionais.
Feedback
Se você encontrar algo de errado nesse codelab, informe-nos. O feedback pode ser enviado pela página de problemas do GitHub [link do feedback].
2. Guia de início rápido do Google Colaboratory
Este laboratório usa o Google Colaboratory e não requer configuração. O Colaboratory é uma plataforma de cadernos on-line para fins educacionais. Ele oferece treinamento sem custo financeiro de CPU, GPU e TPU.
Abra este notebook de exemplo e percorra algumas células para se familiarizar com o Colaboratory.
Selecionar um back-end de TPU
No menu do Colab, selecione Ambiente de execução > Alterar tipo de ambiente de execução e selecione TPU. Neste laboratório de código, você vai usar uma TPU (Unidade de Processamento de Tensor) poderosa com suporte para treinamento com aceleração de hardware. A conexão com o ambiente de execução vai acontecer automaticamente na primeira execução. Você também pode usar o botão "Conectar" no canto superior direito.
Execução do notebook
Execute uma célula de cada vez clicando em uma célula e usando Shift-ENTER. Você também pode executar o notebook inteiro com Runtime > Run all.
Índice
Todos os notebooks têm um sumário. Para abri-lo, use a seta preta à esquerda.
Células ocultas
Algumas células mostrarão apenas o título. Esse é um recurso específico do Colab. Você pode clicar duas vezes neles para ver o código, mas ele geralmente não é muito interessante. Normalmente, são funções de suporte ou visualização. Você ainda precisa executar essas células para que as funções sejam definidas.
Authentication
O Colab pode acessar seus buckets particulares do Google Cloud Storage desde que você faça a autenticação com uma conta autorizada. O snippet de código acima acionará um processo de autenticação.
3. [INFO] O que são as Unidades de Processamento de Tensor (TPUs)?
Resumo
O código para treinar um modelo em TPU no Keras (e usar a GPU ou CPU se uma TPU não estiver disponível):
try: # detect TPUs
tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect()
strategy = tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
strategy = tf.distribute.MirroredStrategy() # for CPU/GPU or multi-GPU machines
# use TPUStrategy scope to define model
with strategy.scope():
model = tf.keras.Sequential( ... )
model.compile( ... )
# train model normally on a tf.data.Dataset
model.fit(training_dataset, epochs=EPOCHS, steps_per_epoch=...)
Vamos usar TPUs hoje para criar e otimizar um classificador de flores em velocidades interativas (minutos por execução de treinamento).
Por que usar TPUs?
As GPUs modernas são organizadas em torno de "núcleos programáveis", uma arquitetura muito flexível que permite lidar com diversas tarefas, como renderização 3D, aprendizado profundo, simulações físicas etc. As TPUs, por outro lado, combinam um processador vetorial clássico com uma unidade de multiplicação de matriz dedicada e se destacam em qualquer tarefa em que grandes multiplicações de matrizes dominem, como redes neurais.
Ilustração: uma camada densa de rede neural como uma multiplicação de matrizes, com um lote de oito imagens processadas pela rede neural de uma só vez. Execute a multiplicação de uma linha x coluna para verificar se ela está de fato fazendo uma soma ponderada de todos os valores de pixels de uma imagem. Também é possível representar camadas convolucionais como multiplicações de matrizes, embora isso seja um pouco mais complicado ( explicação aqui, na seção 1).
O hardware
MXU e VPU
O núcleo da TPU v2 é composto de uma unidade de multiplicação de matriz (MXU) que executa multiplicações de matriz e uma Unidade de processamento vetorial (VPU) para todas as outras tarefas, como ativações, softmax etc. A VPU lida com cálculos de float32 e int32. Por outro lado, a MXU opera em um formato de ponto flutuante de 16 a 32 bits de precisão mista.
Ponto flutuante de precisão mista e bfloat16
A MXU calcula multiplicações de matriz usando entradas bfloat16 e saídas float32. Acumulações intermediárias são realizadas com precisão float32.
O treinamento de redes neurais geralmente é resistente ao ruído introduzido por uma precisão de ponto flutuante reduzida. Há casos em que o ruído até ajuda a convergir o otimizador. A precisão de ponto flutuante de 16 bits é tradicionalmente usada para acelerar as computações, mas os formatos float16 e float32 têm intervalos muito diferentes. Reduzir a precisão de float32 para float16 geralmente resulta em fluxos excessivos e insuficientes. Existem soluções, mas geralmente é necessário fazer mais trabalho para que o float16 funcione.
É por isso que o Google introduziu o formato bfloat16 em TPUs. O bfloat16 é um float32 truncado com exatamente os mesmos bits e intervalo de expoente que o float32. Isso, somado ao fato de que as TPUs calculam multiplicações de matriz com precisão mista com entradas bfloat16, mas saídas float32, significa que, normalmente, nenhuma mudança de código é necessária para se beneficiar dos ganhos de desempenho da precisão reduzida.
Matriz systolic
O MXU implementa multiplicações de matrizes no hardware usando uma arquitetura chamada de "matriz sistólica", na qual os elementos de dados fluem por uma matriz de unidades de computação de hardware. (Em medicina, “sistólico” refere-se às contrações cardíacas e ao fluxo sanguíneo; aqui, ao fluxo de dados.)
O elemento básico de uma multiplicação de matrizes é um produto escalar entre uma linha de uma matriz e uma coluna da outra matriz (consulte a ilustração na parte de cima desta seção). No caso da multiplicação de matrizes Y=X*W, um elemento do resultado seria:
Y[2,0] = X[2,0]*W[0,0] + X[2,1]*W[1,0] + X[2,2]*W[2,0] + ... + X[2,n]*W[n,0]
Em uma GPU, é necessário programar esse produto escalar em um "núcleo" da GPU e executá-lo em todos os "núcleos" disponíveis em paralelo para tentar computar todos os valores da matriz resultante de uma só vez. Se a matriz resultante tiver 128 x 128, será necessário que 128 x 128 = 16K "cores" estejam disponíveis, o que normalmente não é possível. As maiores GPUs têm cerca de 4.000 núcleos. Por outro lado, uma TPU usa o mínimo de hardware para as unidades de computação na MXU: apenas acumuladores de multiplicação bfloat16 x bfloat16 => float32
, nada mais. Eles são tão pequenos que uma TPU pode implementar 16 K deles em um MXU de 128 x 128 e processar essa multiplicação de matrizes de uma vez.
Ilustração: a matriz sistólica da MXU. Os elementos de computação são acumuladores de multiplicação. Os valores de uma matriz são carregados nela (pontos vermelhos). Os valores da outra matriz fluem pela matriz (pontos cinza). As linhas verticais propagam os valores para cima. Linhas horizontais propagam somas parciais. O usuário deve verificar se, à medida que os dados fluem pela matriz, o resultado da multiplicação da matriz aparece no lado direito.
Além disso, enquanto os produtos escalares são calculados em uma MXU, as somas intermediárias simplesmente fluem entre unidades de computação adjacentes. Eles não precisam ser armazenados e recuperados da/da memória ou mesmo de um arquivo de registro. O resultado final é que a arquitetura de matriz sistólico da TPU tem uma vantagem significativa de densidade e potência, além de uma vantagem de velocidade não desprezível em relação a uma GPU, ao calcular multiplicações de matriz.
Cloud TPU
Ao solicitar uma "Cloud TPU v2" no Google Cloud Platform, você recebe uma máquina virtual (VM) com uma placa de TPU conectada a PCI. A placa de TPU tem quatro chips de TPU de dois núcleos. Cada núcleo de TPU possui uma unidade de processamento vetorial (VPU, na sigla em inglês) e uma unidade de multiplicação de matriz (MXU, na sigla em inglês) de 128 x 128. Essa "Cloud TPU" geralmente é conectada pela rede à VM que a solicitou. A imagem completa fica assim:
Ilustração: sua VM com um acelerador "Cloud TPU" conectado à rede. "O próprio Cloud TPU é composto de uma VM com uma placa de TPU conectada a PCI e quatro chips de TPU dual-core.
Pods TPU
Nos data centers do Google, as TPUs são conectadas a uma interconexão de computação de alto desempenho (HPC), que pode fazê-las parecer um acelerador muito grande. O Google os chama de pods, e eles podem abranger até 512 núcleos de TPU v2 ou 2.048 núcleos de TPU v3.
Ilustração: um pod da TPU v3. Placas e racks de TPU conectados por interconexão de HPC.
Durante o treinamento, os gradientes são trocados entre os núcleos da TPU usando o algoritmo de redução total. Confira uma boa explicação sobre a redução total aqui (link em inglês). O modelo que está sendo treinado pode aproveitar o hardware com o treinamento em lotes grandes.
Ilustração: sincronização de gradientes durante o treinamento usando o algoritmo de redução total na rede HPC de malha toroidal 2D do Google TPU.
O software
Treinamento de lote grande
O tamanho de lote ideal para TPUs é de 128 itens de dados por núcleo, mas o hardware já pode mostrar uma boa utilização com 8 itens de dados por núcleo. Lembre-se de que uma Cloud TPU tem 8 núcleos.
Neste codelab, vamos usar a API Keras. No Keras, o lote especificado é o tamanho global de lote para toda a TPU. Seus lotes serão divididos automaticamente em 8 e executados nos 8 núcleos do TPU.
Para mais dicas sobre desempenho, consulte o Guia de desempenho da TPU. Para tamanhos de lote muito grandes, pode ser necessário cuidado especial em alguns modelos. Consulte LARSOptimizer para mais detalhes.
Em segundo plano: XLA
Os programas do TensorFlow definem grafos de computação. Ela não executa o código Python diretamente, e sim o gráfico de computação definido pelo programa do TensorFlow. Em segundo plano, um compilador chamado XLA (Acelerador de álgebra linear) transforma o gráfico de nós de computação do Tensorflow em código de máquina da TPU. Ele também executa muitas otimizações avançadas no código e no layout da memória. A compilação acontece automaticamente conforme o trabalho é enviado para a TPU. Não é necessário incluir a XLA na cadeia de build de forma explícita.
Ilustração: para ser executado em uma TPU, o gráfico de computação definido pelo programa Tensorflow é primeiro convertido em uma representação do XLA (acelerador de álgebra linear) e depois compilado pelo XLA no código de máquina da TPU.
Como usar TPUs no Keras
As TPUs têm suporte na API Keras a partir do TensorFlow 2.1. O suporte a Keras funciona em TPUs e pods da TPU. Veja um exemplo que funciona em TPU, GPU(s) e CPU:
try: # detect TPUs
tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect()
strategy = tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
strategy = tf.distribute.MirroredStrategy() # for CPU/GPU or multi-GPU machines
# use TPUStrategy scope to define model
with strategy.scope():
model = tf.keras.Sequential( ... )
model.compile( ... )
# train model normally on a tf.data.Dataset
model.fit(training_dataset, epochs=EPOCHS, steps_per_epoch=...)
Neste snippet de código:
TPUClusterResolver().connect()
encontra a TPU na rede. Ele funciona sem parâmetros na maioria dos sistemas do Google Cloud (jobs do AI Platform, Colaboratory, Kubeflow, VMs de aprendizado profundo criados com o utilitário "gsutil up"). Esses sistemas sabem onde está a TPU graças a uma variável de ambiente TPU_NAME. Se você criar uma TPU manualmente, defina a variável env. TPU_NAME na VM da qual ela está sendo usada ou chameTPUClusterResolver
com parâmetros explícitos:TPUClusterResolver(tp_uname, zone, project)
TPUStrategy
é a parte que implementa a distribuição e o algoritmo de sincronização de gradiente "all-reduce".- A estratégia é aplicada por um escopo. O modelo precisa ser definido dentro do scope() da estratégia.
- A função
tpu_model.fit
espera um objeto tf.data.Dataset para entrada no treinamento da TPU.
Tarefas comuns de portabilidade de TPU
- Embora haja muitas maneiras de carregar dados em um modelo do TensorFlow, para TPUs, o uso da API
tf.data.Dataset
é necessário. - As TPUs são muito rápidas, e a ingestão de dados geralmente se torna um gargalo quando elas são executadas. Existem ferramentas que podem ser usadas para detectar gargalos de dados e outras dicas de desempenho no Guia de desempenho da TPU.
- os números int8 ou int16 são tratados como int32. A TPU não tem hardware inteiro que opera em menos de 32 bits.
- Algumas operações do Tensorflow não são compatíveis. Confira a lista. A boa notícia é que essa limitação se aplica apenas ao código de treinamento, ou seja, a passagem para frente e para trás pelo modelo. Ainda é possível usar todas as operações do TensorFlow no pipeline de entrada de dados, já que elas serão executadas na CPU.
tf.py_func
não é compatível com a TPU.
4. [INFORMAÇÃO] Introdução ao classificador de rede neural
Resumindo
Se você já conhece todos os termos em negrito no próximo parágrafo, avance para o próximo exercício. Se você está começando no aprendizado profundo, dê as boas-vindas e continue lendo.
Para modelos criados como uma sequência de camadas, o Keras oferece a API Sequential. Por exemplo, um classificador de imagens que usa três camadas densas pode ser escrito no Keras como:
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=[192, 192, 3]),
tf.keras.layers.Dense(500, activation="relu"),
tf.keras.layers.Dense(50, activation="relu"),
tf.keras.layers.Dense(5, activation='softmax') # classifying into 5 classes
])
# this configures the training of the model. Keras calls it "compiling" the model.
model.compile(
optimizer='adam',
loss= 'categorical_crossentropy',
metrics=['accuracy']) # % of correct answers
# train the model
model.fit(dataset, ... )
Rede neural densa
Essa é a rede neural mais simples para classificar imagens. Ele é feito de "neurônios" organizados em camadas. A primeira camada processa os dados de entrada e alimenta as saídas em outras camadas. É chamado de "denso" porque cada neurônio está conectado a todos os neurônios da camada anterior.
É possível alimentar uma imagem nessa rede, achatando os valores RGB de todos os pixels em um vetor longo e usando-o como entradas. Não é a melhor técnica para reconhecimento de imagem, mas vamos melhorá-la mais tarde.
Neurônios, ativações, RELU
Um "neurônio" calcula uma soma ponderada de todas as entradas, adiciona um valor chamado "viés" e alimenta o resultado por meio de uma chamada "função de ativação". Os pesos e o viés são desconhecidos no início. Elas serão inicializadas aleatoriamente e "aprendedas" treinando a rede neural com muitos dados conhecidos.
A função de ativação mais conhecida é chamada RELU, que significa "Unidade linear retificada". É uma função muito simples, como você pode ver no gráfico acima.
Ativação Softmax
A rede acima termina com uma camada de cinco neurônios porque estamos classificando flores em cinco categorias (rosa, tulipa, dente-de-leão, margarida, girassol). Os neurônios nas camadas intermediárias são ativados usando a função de ativação RELU clássica. Na última camada, queremos calcular números entre 0 e 1 que representam a probabilidade de essa flor ser uma rosa, uma tulipa e assim por diante. Para isso, vamos usar uma função de ativação chamada "softmax".
A aplicação de softmax a um vetor é feita tomando a exponencial de cada elemento e normalizando-o, normalmente usando a norma L1 (soma dos valores absolutos) para que os valores somem 1 e possam ser interpretados como probabilidades.
Perda de entropia cruzada
Agora que nossa rede neural produz previsões com base nas imagens de entrada, precisamos medir a qualidade delas, ou seja, a distância entre o que a rede nos informa e as respostas corretas, que geralmente são chamadas de "rótulos". Lembre-se de que temos rótulos corretos para todas as imagens no conjunto de dados.
Qualquer distância funciona, mas, para problemas de classificação, a chamada "distância de entropia cruzada" é a mais eficaz. Vamos chamar isso de função de erro ou "perda":
Gradiente descendente
"Treinar" a rede neural significa, na verdade, usar imagens e rótulos de treinamento para ajustar pesos e vieses, a fim de minimizar a função de perda de entropia cruzada. Confira como funciona.
A entropia cruzada é uma função de pesos, vieses, pixels da imagem de treinamento e a classe conhecida dela.
Se calcularmos as derivadas parciais da entropia cruzada em relação a todos os pesos e vieses, vamos conseguir um "gradiente", calculado para uma determinada imagem, rótulo e valor atual de pesos e vieses. Lembre-se de que podemos ter milhões de pesos e vieses, então calcular o gradiente parece muito trabalho. Felizmente, o TensorFlow faz isso por nós. A propriedade matemática de um gradiente é que ele aponta "para cima". Como queremos chegar onde a entropia cruzada é baixa, vamos na direção oposta. Atualizamos os pesos e as tendências por uma fração do gradiente. Em seguida, fazemos a mesma coisa várias vezes usando os próximos lotes de imagens e rótulos de treinamento, em um ciclo de treinamento. Com sorte, isso converge para um lugar em que a entropia cruzada é mínima, embora nada garanta que esse mínimo seja único.
Minilotes e momentum
Você pode calcular seu gradiente em apenas uma imagem de exemplo e atualizar os pesos e vieses imediatamente, mas fazer isso em um lote de, por exemplo, 128 imagens gera um gradiente que representa melhor as restrições impostas por diferentes imagens de exemplo e, portanto, é provável que converja para a solução mais rapidamente. O tamanho do minilote é um parâmetro ajustável.
Essa técnica, às vezes chamada de "gradiente descendente estocástico" tem outro benefício mais pragmático: trabalhar com lotes também significa trabalhar com matrizes maiores, que geralmente são mais fáceis de otimizar em GPUs e TPUs.
A convergência ainda pode ser um pouco caótica e pode até parar se o vetor de gradiente for todo zero. Isso significa que encontramos um mínimo? Nem sempre. Um componente de gradiente pode ser zero em um mínimo ou um máximo. Com um vetor de gradiente com milhões de elementos, se todos forem zeros, a probabilidade de cada zero corresponder a um mínimo e nenhum deles a um ponto máximo será muito pequena. Em um espaço de muitas dimensões, os pontos de sela são muito comuns e não queremos parar neles.
Ilustração: um suporte para sentar. O gradiente é 0, mas não é um mínimo em todas as direções. (Atribuição da imagem Wikimedia: por Nicoguaro - Próprio trabalho, CC BY 3.0)
A solução é adicionar um pouco de impulso ao algoritmo de otimização para que ele possa ultrapassar os pontos de obstáculos sem parar.
Glossário
Lote ou minilote: o treinamento é sempre realizado em lotes de dados e rótulos de treinamento. Isso ajuda o algoritmo a convergir. A dimensão "batch" é geralmente a primeira dimensão dos tensores de dados. Por exemplo, um tensor de forma [100, 192, 192, 3] contém 100 imagens de 192x192 pixels com três valores por pixel (RGB).
perda de entropia cruzada: uma função de perda especial usada com frequência em classificadores.
camada densa: uma camada de neurônios em que cada um está conectado a todos os neurônios da camada anterior.
Atributos: as entradas de uma rede neural às vezes são chamadas de "atributos". A arte de descobrir quais partes de um conjunto de dados (ou combinações de partes) alimentar em uma rede neural para obter boas previsões é chamada de "engenharia de recursos".
Rótulos: outro nome para "classes" ou respostas corretas em um problema de classificação supervisionada
Taxa de aprendizado: fração do gradiente em que os pesos e vieses são atualizados em cada iteração do ciclo de treinamento.
logits: as saídas de uma camada de neurônios antes da aplicação da função de ativação são chamadas de "logits". O termo vem da "função logística", também conhecida como "função sigmoide", que costumava ser a função de ativação mais popular. "Saídas de neurônios antes da função logística" foi encurtada para "logits".
loss: a função de erro que compara as saídas da rede neural com as respostas corretas
neuron: calcula a soma ponderada das entradas, adiciona uma polarização e alimenta o resultado por meio de uma função de ativação.
codificação one-hot: a classe 3 de 5 é codificada como um vetor de cinco elementos, todos os zeros, exceto o terceiro, que é 1.
ReLU: unidade linear retificada. Uma função de ativação conhecida para os neurônios.
Sigmóide: outra função de ativação que costumava ser popular e ainda é útil em casos especiais.
softmax: uma função de ativação especial que atua em um vetor, aumenta a diferença entre o maior componente e todos os outros, além de normalizar o vetor para ter uma soma de 1, de modo que possa ser interpretado como um vetor de probabilidades. É usada como a última etapa em classificadores.
tensor: um "tensor" é como uma matriz, mas com um número arbitrário de dimensões. Um tensor unidimensional é um vetor. Um tensor de duas dimensões é uma matriz. E então você pode ter tensores com 3, 4, 5 ou mais dimensões.
5. [NOVA INFORMAÇÕES] Redes neurais convolucionais
Resumo
Se você já conhece todos os termos em negrito no próximo parágrafo, avance para o próximo exercício. Se você está começando a trabalhar com redes neurais convolucionais, continue lendo.
Ilustração: filtrar uma imagem com dois filtros sucessivos feitos de 4 x 4 x 3=48 pesos aprendidos cada.
Confira como uma rede neural convolucional simples fica no Keras:
model = tf.keras.Sequential([
# input: images of size 192x192x3 pixels (the three stands for RGB channels)
tf.keras.layers.Conv2D(kernel_size=3, filters=24, padding='same', activation='relu', input_shape=[192, 192, 3]),
tf.keras.layers.Conv2D(kernel_size=3, filters=24, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=12, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=6, padding='same', activation='relu'),
tf.keras.layers.Flatten(),
# classifying into 5 categories
tf.keras.layers.Dense(5, activation='softmax')
])
model.compile(
optimizer='adam',
loss= 'categorical_crossentropy',
metrics=['accuracy'])
Introdução às redes neurais convolucionais
Em uma camada de uma rede convolucional, um "neurônio" faz uma soma ponderada dos pixels logo acima dele, em uma pequena região da imagem. Ela adiciona um viés e alimenta a soma por meio de uma função de ativação, assim como um neurônio em uma camada densa regular faria. Essa operação é então repetida em toda a imagem usando os mesmos pesos. Lembre-se de que, em camadas densas, cada neurônio tem os próprios pesos. Aqui, um único "patch" de pesos desliza pela imagem em ambas as direções (uma "convolução"). A saída tem tantos valores quanto pixels na imagem, mas é necessário um pouco de padding nas bordas. É uma operação de filtragem, usando um filtro de 4x4x3=48 pesos.
No entanto, 48 pesos não serão suficientes. Para adicionar mais graus de liberdade, repetimos a mesma operação com um novo conjunto de pesos. Isso produz um novo conjunto de saídas de filtro. Vamos chamar isso de "canal" de saídas por analogia com os canais R, G e B na imagem de entrada.
Os dois (ou mais) conjuntos de pesos podem ser resumidos como um tensor adicionando uma nova dimensão. Isso nos dá a forma genérica do tensor de pesos para uma camada convolucional. Como o número de canais de entrada e saída são parâmetros, podemos começar a empilhar e encadear camadas convolucionais.
Ilustração: uma rede neural convolucional transforma "cubos" de dados em outros "cubos" de dados.
Convoluções com passo, max pooling
Ao realizar as convoluções com um passo de 2 ou 3, também é possível reduzir o cubo de dados resultante nas dimensões horizontais. Há duas maneiras comuns de fazer isso:
- Convolução em desvio: um filtro deslizante como acima, mas com um passo >1
- Pool máximo: uma janela deslizante que aplica a operação MAX (normalmente em patches 2x2, repetidos a cada 2 pixels)
Ilustração: deslizar a janela de computação em 3 pixels resulta em menos valores de saída. As convoluções com passo ou a pooling máxima (máxima em uma janela 2x2 deslizante com um passo de 2) são uma maneira de reduzir o cubo de dados nas dimensões horizontais.
Classificador convolucional
Por fim, anexamos uma cabeça de classificação achatando o último cubo de dados e alimentando-o por uma camada densa e ativada por softmax. Um classificador convolucional típico pode ter a seguinte aparência:
Ilustração: um classificador de imagens usando camadas convolucionais e softmax. Ele usa filtros 3x3 e 1x1. As camadas do maxpool usam o máximo de grupos de pontos de dados 2x2. O cabeçalho de classificação é implementado com uma camada densa com ativação softmax.
No Keras
A pilha convolucional ilustrada acima pode ser escrita em Keras assim:
model = tf.keras.Sequential([
# input: images of size 192x192x3 pixels (the three stands for RGB channels)
tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu', input_shape=[192, 192, 3]),
tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=16, padding='same', activation='relu'),
tf.keras.layers.Conv2D(kernel_size=1, filters=8, padding='same', activation='relu'),
tf.keras.layers.Flatten(),
# classifying into 5 categories
tf.keras.layers.Dense(5, activation='softmax')
])
model.compile(
optimizer='adam',
loss= 'categorical_crossentropy',
metrics=['accuracy'])
6. Sua rede neural convolucional personalizada
Prático
Vamos criar e treinar uma rede neural convolucional do zero. Usar uma TPU nos permite iterar muito rapidamente. Abra o notebook a seguir, execute as células (Shift-ENTER) e siga as instruções sempre que encontrar o rótulo "WORK REQUIRED".
Keras_Flowers_TPU (playground).ipynb
O objetivo é superar a precisão de 75% do modelo de aprendizado de transferência. Esse modelo tinha uma vantagem: ele foi pré-treinado em um conjunto de dados com milhões de imagens, mas aqui temos apenas 3.670 imagens. Você pode pelo menos igualar?
Informações adicionais
Quantas camadas, qual o tamanho?
Selecionar tamanhos de camada é mais uma arte do que uma ciência. Você precisa encontrar o equilíbrio entre ter poucos e muitos parâmetros (ponderações e vieses). Com poucos pesos, a rede neural não pode representar a complexidade das formas das flores. Com muitos, pode haver uma tendência de "superajuste", ou seja, a rede se especializa nas imagens de treinamento e não consegue generalizar. Com muitos parâmetros, o treinamento do modelo também será lento. No Keras, a função model.summary()
mostra a estrutura e a contagem de parâmetros do modelo:
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 192, 192, 16) 448
_________________________________________________________________
conv2d_1 (Conv2D) (None, 192, 192, 30) 4350
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 96, 96, 30) 0
_________________________________________________________________
conv2d_2 (Conv2D) (None, 96, 96, 60) 16260
_________________________________________________________________
...
_________________________________________________________________
global_average_pooling2d (Gl (None, 130) 0
_________________________________________________________________
dense (Dense) (None, 90) 11790
_________________________________________________________________
dense_1 (Dense) (None, 5) 455
=================================================================
Total params: 300,033
Trainable params: 300,033
Non-trainable params: 0
_________________________________________________________________
Algumas dicas:
- Ter várias camadas é o que torna as redes neurais "profundas". Para esse problema simples de reconhecimento de flores, 5 a 10 camadas são suficientes.
- Use filtros pequenos. Normalmente, filtros 3x3 são bons em qualquer lugar.
- Os filtros 1x1 também podem ser usados e são baratos. Eles não "filtram" nada, mas calculam combinações lineares de canais. Use filtros reais no futuro. Saiba mais sobre "convoluções 1x1" na próxima seção.
- Para um problema de classificação como esse, faça downsample frequentemente com camadas de max-pooling (ou convoluções com salto maior que 1). Você não se importa onde a flor está, apenas que ela é uma rosa ou um dente-de-leão. Portanto, perder informações x e y não é importante, e filtrar áreas menores é mais barato.
- O número de filtros geralmente se torna semelhante ao número de classes no final da rede. Por quê? Consulte o truque "agrupamento médio global" abaixo. Se você classificar em centenas de classes, aumente a contagem de filtros progressivamente em camadas consecutivas. Para o conjunto de dados de flores com cinco classes, filtrar com apenas cinco filtros não seria suficiente. Você pode usar a mesma contagem de filtros na maioria das camadas, por exemplo, 32 e diminuir até o final.
- As camadas densas finais são caras. Elas podem ter mais pesos do que todas as camadas convolucionais combinadas. Por exemplo, mesmo com uma saída muito razoável do último cubo de dados de 24x24x10 pontos de dados, uma camada densa de 100 neurônios custaria 24x24x10x100=576.000 pesos! Tente ser cuidadoso ou teste o pooling de média global (veja abaixo).
Pooling médio global
Em vez de usar uma camada densa cara no final de uma rede neural convolucional, você pode dividir o "cubo" de dados recebidos em tantas partes quantos classes tiver, calcular a média dos valores e alimentá-los com uma função de ativação softmax. Essa maneira de criar o cabeçalho de classificação não custa peso. No Keras, a sintaxe é tf.keras.layers.GlobalAveragePooling2D().
Solução
Este é o notebook da solução. Você pode usá-la se tiver dificuldades.
Keras_Flowers_TPU (solution).ipynb
O que vimos
- 🤔 Brincou com camadas convolucionais
- 🤓 Experimentei o agrupamento máximo, os passos, o agrupamento médio global etc.
- 😀 iterou rapidamente em um modelo real na TPU.
Leia esta lista de verificação.
7. Parabéns!
Você criou sua primeira rede neural convolucional moderna e a treinou com mais de 80% de precisão, iterando a arquitetura em apenas alguns minutos, graças às TPUs. Prossiga para o próximo laboratório para saber mais sobre arquiteturas convolucionais modernas:
- Pipelines de dados com velocidade de TPU: tf.data.Dataset e TFRecords
- Seu primeiro modelo do Keras, com aprendizado de transferência
- [THIS LAB] Redes neurais convolucionais com Keras e TPUs
- Convnets modernas, squeezenet, xception, com Keras e TPUs
TPUs na prática
As TPUs e GPUs estão disponíveis na Cloud AI Platform:
- Em VMs de aprendizado profundo
- nos Notebooks do AI Platform
- Nos jobs do AI Platform Training
Por fim, adoramos receber feedback. Avise nossa equipe se você encontrar algo errado no laboratório ou achar que ele precisa ser melhorado. O feedback pode ser enviado pela página de problemas do GitHub [link do feedback].
|