One place for hosting & domains

      Neural

      Como enganar uma rede neural no Python 3


      O autor selecionou a Dev Color para receber uma doação como parte do programa Write for DOnations.

      Será que uma rede neural para classificação de animais pode ser enganada? Enganar um classificador de animais pode gerar poucas consequências, mas e se nosso autenticador facial pudesse ser enganado? Ou o software do nosso protótipo de carro autônomo? Felizmente,existem legiões de engenheiros e pesquisas entre um modelo visual computacional protótipo e modelos de qualidade de produção em nossos dispositivos móveis ou carros. Ainda assim, esses riscos têm implicações significativas e é importante que sejam considerados pelos profissionais de machine learning.

      Neste tutorial, você irá tentar “iludir” ou enganar um classificador de animais. Ao longo do tutorial, você irá usar o OpenCV, uma biblioteca de visão computacional e o PyTorch, uma biblioteca de deep learning. Os seguintes tópicos serão abordados no campo associado do adversarial machine learning (machine learning contraditório):

      • Crie um exemplo contraditório direcionado. Escolha uma imagem, digamos, de um cachorro. Escolha uma classe alvo, digamos, um gato. Seu objetivo é enganar a rede neural para acreditar que o cão retratado é um gato.
      • Crie uma defesa contraditória. Em resumo, proteja sua rede neural contra essas imagens suspeitas, sem saber qual é o truque.

      Ao final do tutorial, você terá uma ferramenta para enganar redes neurais e um entendimento sobre como se defender contra os truques.

      Pré-requisitos

      Para concluir este tutorial, você precisará do seguinte:

      Passo 1 — Criando o projeto e instalando as dependências

      Vamos criar um espaço de trabalho para este projeto e instalar as dependências que você irá precisar. Você irá chamar seu espaço de trabalho AdversarialML:

      Navegue até o diretório AdversarialML:

      Crie um diretório para manter todos os seus recursos:

      • mkdir ~/AdversarialML/assets

      A seguir, crie um novo ambiente virtual para o projeto:

      • python3 -m venv adversarialml

      Ative seu ambiente:

      • source adversarialml/bin/activate

      Em seguida, instale o PyTorch, um framework de deep learning para Python que você usará neste tutorial.

      No macOS, instale o Pytorch com o seguinte comando:

      • python -m pip install torch==1.2.0 torchvision==0.4.0

      No Linux e Windows, utilize os seguintes comandos para uma compilação CPU-only:

      • pip install torch==1.2.0+cpu torchvision==0.4.0+cpu -f https://download.pytorch.org/whl/torch_stable.html
      • pip install torchvision

      Agora, instale binários pré-empacotados para o OpenCV e numpy, que são bibliotecas para visão computacional e álgebra linear, respectivamente. O OpenCV oferece utilitários como rotações de imagem, e o numpy oferece utilitários de álgebra linear, como inversão de matriz:

      • python -m pip install opencv-python==3.4.3.18 numpy==1.14.5

      Em distribuições Linux, você precisará instalar a libSM.so:

      • sudo apt-get install libsm6 libxext6 libxrender-dev

      Com as dependências instaladas, vamos executar um classificador de animais chamado ResNet18, que descrevemos a seguir.

      Passo 2 — Executando um classificador de animais pré-treinado

      A biblioteca torchvision, que é a biblioteca oficial de visão computacional para o PyTorch, contém versões pré-treinadas de redes neurais de visão computacional comumente usadas. Essas redes neurais são todas treinadas no ImageNet 2012, um conjunto de dados que consiste em 1,2 milhões de imagens de treinamento com 1000 classes. Essas classes incluem veículos, lugares e, acima de tudo, animais. Neste passo, você irá executar uma dessas redes neurais pré-treinadas chamada ResNet18. Chamaremos a rede neural ResNet18 treinada no ImageNet de “classificador de animais”.

      O que é o ResNet18? O ResNet18 é a menor rede neural em uma família de redes neurais chamada redes neurais residuais, desenvolvida pela MSR (He et al.). Em resumo, ele descobriu que uma rede neural (denotada como uma função f, com entrada x, e saída f(x)) teria melhor desempenho com uma “conexão residual” x + f(x). Essa conexão residual é usada prolificamente em redes neurais no estado da arte, mesmo hoje. Por exemplo, FBNetV2, FBNetV3.

      Baixe esta imagem de um cachorro com o seguinte comando:

      • wget -O assets/dog.jpg https://www.xpresservers.com/wp-content/uploads/2020/06/How-To-Trick-a-Neural-Network-in-Python-3.png

      Imagem de um corgi correndo perto de uma lagoa

      Então, baixe um arquivo JSON para converter o resultado da rede neural em um nome de classe humanamente legível:

      • wget -O assets/imagenet_idx_to_label.json https://raw.githubusercontent.com/do-community/tricking-neural-networks/master/utils/imagenet_idx_to_label.json

      Em seguida, crie um script para executar seu modelo pré-treinado na imagem do cão. Crie um novo arquivo chamado step_2_pretrained.py:

      • nano step_2_pretrained.py

      Primeiro, adicione o código padrão Python importando os pacotes necessários e declarando uma função main (principal):

      step_2_pretrained.py

      from PIL import Image
      import json
      import torchvision.models as models
      import torchvision.transforms as transforms
      import torch
      import sys
      
      def main():
          pass
      
      if __name__ == '__main__':
          main()
      

      Em seguida, carregue o mapeamento a partir do resultado da rede neural para nomes de classe humanamente legíveis. Adicione isto diretamente após suas declarações de importação e antes de sua função main:

      step_2_pretrained.py

      . . .
      def get_idx_to_label():
          with open("assets/imagenet_idx_to_label.json") as f:
              return json.load(f)
      . . .
      

      Crie uma função de transformação de imagem que irá garantir que sua imagem de entrada tenha as dimensões corretas e que seja normalizada corretamente. Adicione a seguinte função diretamente após a última:

      step_2_pretrained.py

      . . .
      def get_image_transform():
          transform = transforms.Compose([
            transforms.Resize(224),
            transforms.CenterCrop(224),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                 std=[0.229, 0.224, 0.225])
          ])
          return transform
      . . .
      

      Em get_image_transform, você define um número de transformações diferentes para aplicar às imagens que são passadas para sua rede neural:

      • transforms.Resize(224): redimensiona o lado menor da imagem para 224. Por exemplo, se a imagem tiver 448 x 672, esta operação reduziria a resolução dela para 224 x 336.
      • transforms.CenterCrop(224): faz um recorte do centro da imagem, de tamanho 224 x 224.
      • transforms.ToTensor(): Converte a imagem em um tensor do PyTorch. Todos os modelos PyTorch exigem os tensores do PyTorch como entrada.
      • transforms.Normalize(mean=..., std=...): Normaliza sua entrada primeiro subtraindo a média, então dividindo pelo desvio padrão. Isso é descrito mais precisamente na documentação do torchvision.

      Adicione um utilitário para prever a classe animal, dada a imagem. Este método usa ambos os utilitários anteriores para realizar a classificação de animais:

      step_2_pretrained.py

      . . .
      def predict(image):
          model = models.resnet18(pretrained=True)
          model.eval()
      
          out = model(image)
      
          _, pred = torch.max(out, 1)  
          idx_to_label = get_idx_to_label()  
          cls = idx_to_label[str(int(pred))]  
          return cls
      . . .
      

      Aqui a função de predict (prever) classifica a imagem fornecida usando uma rede neural pré-treinada:

      • models.resnet18(pretrained=True): Carrega uma rede neural pré-treinada chamada ResNet18.
      • model.eval(): modifica o modelo em vigor para ser executar no modo ‘avaliação’. O único outro modo é o modo ‘treinamento’, mas o modo de treinamento não é necessário, pois você não está treinando o modelo (ou seja, atualizando os parâmetros do modelo) neste tutorial.
      • out = model(image): Executa a rede neural na imagem transformada fornecida.
      • _, pred = torch.max(out, 1): A rede neural gera uma probabilidade para cada classe possível. Esse passo computa o índice da classe com a maior probabilidade. Por exemplo, se out = [0.4, 0.1, 0.2], então pred = 0.
      • idx_to_label = get_idx_to_label(): Obtém um mapeamento do índice de classes para nomes de classe humanamente legíveis. Por exemplo, o mapeamento poderia ser {0: cat, 1: dog, 2: fish}.
      • cls = idx_to_label[str(int(pred))]: Converte o índice de classe previsto em um nome de classe. Os exemplos fornecidos nos dois últimos tópicos iriam gerar cls = idx_to_label[0] = 'cat'.

      Em seguida, adicione um utilitário para carregar imagens após a última função:

      step_2_pretrained.py

      . . .
      def load_image():
          assert len(sys.argv) > 1, 'Need to pass path to image'
          image = Image.open(sys.argv[1])
      
          transform = get_image_transform()
          image = transform(image)[None]
          return image
      . . .
      

      Isso irá carregar uma imagem a partir do caminho fornecido no primeiro argumento para o script. transform(image)[None] aplica a sequência de transformações de imagem definida nas linhas anteriores.

      Por fim, preencha sua função main da seguinte forma, para carregar sua imagem e classificar o animal na imagem:

      step_2_pretrained.py

      def main():
          x = load_image()
          print(f'Prediction: {predict(x)}')
      

      Verifique se seu arquivo corresponde ao nosso script do final do passo 2 em step_2_pretrained.py no GitHub. Salve e saia do seu script. Em seguida, execute o classificador de animais:

      • python step_2_pretrained.py assets/dog.jpg

      Isso irá produzir o seguinte resultado, mostrando que seu classificador de animais funciona como esperado:

      Output

      Prediction: Pembroke, Pembroke Welsh corgi

      Isso conclui que há uma inferência em execução com seu modelo pré-treinado. Em seguida, você verá um exemplo contraditório em ação enganando uma rede neural com diferenças imperceptíveis na imagem.

      Passo 3 — Tentando um exemplo contraditório

      Agora, você irá sintetizar um exemplo contraditório e testar a rede neural nesse exemplo. Para este tutorial, você irá compilar exemplos contraditórios da forma x + r, onde x é a imagem original e r é alguma “perturbação”. Eventualmente, você irá criar a perturbação r por conta própria, mas, neste passo, irá baixar uma que já criamos para você. Comece baixando a perturbação r:

      • wget -O assets/adversarial_r.npy https://github.com/do-community/tricking-neural-networks/blob/master/outputs/adversarial_r.npy?raw=true

      Agora, crie uma composição da figura com a perturbação. Crie um novo arquivo chamado step_3_adversarial.py:

      • nano step_3_adversarial.py

      Neste arquivo, você irá realizar o seguinte processo de três etapas, para produzir um exemplo contraditório:

      1. Transformar uma imagem
      2. Aplicar a perturbação r
      3. Fazer a transformação inversa da imagem perturbada

      No final do passo 3, você terá uma imagem contraditória. Primeiro, importe os pacotes necessários e declare uma função main:

      step_3_adversarial.py

      from PIL import Image
      import torchvision.transforms as transforms
      import torch
      import numpy as np
      import os
      import sys
      
      from step_2_pretrained import get_idx_to_label, get_image_transform, predict, load_image
      
      
      def main():
          pass
      
      
      if __name__ == '__main__':
          main()
      

      Em seguida, crie uma “transformação de imagem” que inverte a transformação de imagem anterior. Coloque isto após suas importações, antes da função main:

      step_3_adversarial.py

      . . .
      def get_inverse_transform():
          return transforms.Normalize(
              mean=[-0.485/0.229, -0.456/0.224, -0.406/0.255],  # INVERSE normalize images, according to https://pytorch.org/docs/stable/torchvision/models.html
              std=[1/0.229, 1/0.224, 1/0.255])
      . . .
      

      Assim como antes, a operação transforms.Normalize subtrai a média e divide o valor pelo desvio padrão (ou seja, para a imagem original x, y = transforms.Normalize(mean=u, std=o) = (x - u) / o). Você aplica um pouco de álgebra e define uma nova operação que reverte essa função normalizadora (transforms.Normalize(mean=-u/o, std=1/o) = (y - -u/o) / 1/o = (y + u/o) o = yo + u = x).

      Como parte da transformação inversa, adicione um método que transforma um tensor do PyTorch de volta em uma imagem PIL. Adicione isto após a última função:

      step_3_adversarial.py

      . . .
      def tensor_to_image(tensor):
          x = tensor.data.numpy().transpose(1, 2, 0) * 255.  
          x = np.clip(x, 0, 255)
          return Image.fromarray(x.astype(np.uint8))
      . . .
      
      • O tensor.data.numpy() converte o tensor do PyTorch em uma matriz do NumPy. .transpose(1, 2, 0) reorganiza (channels, width, height) em (height, width, channels). Essa matriz do NumPy está aproximadamente no intervalo (0, 1). Por fim, multiplique isso por 255 para garantir que a imagem esteja agora na faixa (0, 255).
      • O np.clip garante que todos os valores na imagem estejam entre (0, 255).
      • O x.astype(np.uint8) garante que todos os valores de imagem sejam inteiros. Por fim, o Image.fromarray(...) cria um objeto de imagem PIL a partir da matriz do NumPy.

      Em seguida, use esses utilitários para criar o exemplo contraditório da seguinte forma:

      step_3_adversarial.py

      . . .
      def get_adversarial_example(x, r):
          y = x + r
          y = get_inverse_transform()(y[0])
          image = tensor_to_image(y)
          return image
      . . .
      

      Essa função gera o exemplo contraditório como descrito no início da seção:

      1. y = x + r. Pega sua perturbação r e a adiciona à imagem original x.
      2. get_inverse_transform: Obtém e aplica a transformação reversa de imagem que você definiu várias linhas atrás.
      3. tensor_to_image: Por fim, converte o tensor do PyTorch de volta para um objeto de imagem.

      Em último lugar, modifique sua função main para carregar a imagem, carregar a perturbação contraditória r, aplicar a perturbação, salvar o exemplo contraditório no disco e executar uma previsão no exemplo contraditório:

      step_3_adversarial.py

      def main():
          x = load_image()
          r = torch.Tensor(np.load('assets/adversarial_r.npy'))
      
          # save perturbed image
          os.makedirs('outputs', exist_ok=True)
          adversarial = get_adversarial_example(x, r)
          adversarial.save('outputs/adversarial.png')
      
          # check prediction is new class
          print(f'Old prediction: {predict(x)}')
          print(f'New prediction: {predict(x + r)}')
      

      Seu arquivo finalizado deve corresponder ao step_3_adversarial.py no GitHub. Salve o arquivo, saia do editor e inicie seu script com:

      • python step_3_adversarial.py assets/dog.jpg

      Você verá este resultado:

      Output

      Old prediction: Pembroke, Pembroke Welsh corgi New prediction: goldfish, Carassius auratus

      Agora, você criou um exemplo contraditório: enganou a rede neural para pensar que um corgi é um peixe dourado. No próximo passo, você irá efetivamente criar a perturbação r que você usou aqui.

      Passo 4 — Compreendendo um exemplo contraditório

      Para uma cartilha sobre classificação, consulte “How to Build an Emotion-Based Dog Filter”.

      Dando um passo para trás, lembre-se que seu modelo de classificação gera uma probabilidade para cada classe. Durante a inferência, o modelo prevê a classe que tenha a maior probabilidade. Durante o treinamento, você atualiza os parâmetros t do modelo para maximizar a probabilidade da classe correta y, dado os seus dados x.

      argmax_y P(y|x,t)
      

      No entanto, para gerar exemplos contraditórios, agora seu objetivo é outro. Em vez de encontrar uma classe, seu objetivo agora é encontrar uma nova imagem, x. Escolha qualquer classe que não seja a correta. Chamemos essa nova classe de w. Seu novo objetivo é maximizar a probabilidade da classe errada.

      argmax_x P(w|x)
      

      Observe que os pesos t da rede neural foram deixados de fora da expressão acima. Isso ocorre porque agora assumem o papel da contradição: outra pessoa treinou e implantou um modelo. Você só pode criar entradas contraditórias e não é permitido modificar o modelo implantado. Para gerar o exemplo contraditório x, é possível executar um “treinamento”, exceto que, em vez de atualizar os pesos da rede neural, você atualiza a imagem de entrada com o novo objetivo.

      Como um lembrete, para este tutorial, você supõe que o exemplo contraditório é uma transformação afim de x. Em outras palavras, seu exemplo contraditório assume a forma x + r para alguns r. No próximo passo, você irá escrever um script para gerar este r.

      Passo 5 — Criando um exemplo contraditório

      Neste passo, você irá aprender uma perturbação r, para que seu corgi seja classificado erroneamente como um peixe dourado. Crie um novo arquivo chamado step_5_perturb.py:

      Importe os pacotes necessários e declare uma função main:

      step_5_perturb.py

      from torch.autograd import Variable
      import torchvision.models as models
      import torch.nn as nn
      import torch.optim as optim
      import numpy as np
      import torch
      import os
      
      from step_2_pretrained import get_idx_to_label, get_image_transform, predict, load_image
      from step_3_adversarial import get_adversarial_example
      
      
      def main():
          pass
      
      
      if __name__ == '__main__':
          main()
      

      Logo após suas importações e antes da função main, defina duas constantes:

      step_5_perturb.py

      . . .
      TARGET_LABEL = 1
      EPSILON = 10 / 255.
      . . .
      

      A primeira constante, TARGET_LABEL, é a classe que será utilizada para classificar erroneamente o corgi. Neste caso, o índice 1 corresponde a “goldfish”(peixe dourado). A segunda constante do EPSILON é a quantidade máxima de perturbação permitida para cada valor da imagem. Este limite é introduzido para que a imagem seja alterada de maneira imperceptível.

      Após suas duas constantes, adicione uma função auxiliar para definir uma rede neural e o parâmetro de perturbação r:

      step_5_perturb.py

      . . .
      def get_model():
          net = models.resnet18(pretrained=True).eval()
          r = nn.Parameter(data=torch.zeros(1, 3, 224, 224), requires_grad=True)
          return net, r
      . . .
      
      • O model.resnet18(pré-trained=True) carrega uma rede neural pré-treinada chamada ResNet18, como antes. Também como antes, você define o modelo para o modo de avaliação usando .eval.
      • O nn.Parameter(...) define uma nova perturbação r, com o tamanho da imagem de entrada. A imagem de entrada também tem o tamanho (1, 3, 224, 224). O argumento de palavra-chave requires_grad=True garante que você possa atualizar essa perturbação r em linhas posteriores neste arquivo.

      Em seguida, comece a modificar sua função main. Comece carregando o modelo net, carregando as entradas x e definindo a etiqueta label:

      step_5_perturb.py

      . . .
      def main():
          print(f'Target class: {get_idx_to_label()[str(TARGET_LABEL)]}')
          net, r = get_model()
          x = load_image()
          labels = Variable(torch.Tensor([TARGET_LABEL])).long()
        . . .
      

      Em seguida, defina tanto o critério quanto o otimizador em sua função main. O primeiro diz ao PyTorch qual é o objetivo – ou seja, qual perda deve ser minimizada. O último diz ao PyTorch como treinar seu parâmetro r:

      step_5_perturb.py

      . . .
          criterion = nn.CrossEntropyLoss()
          optimizer = optim.SGD([r], lr=0.1, momentum=0.1)
      . . .
      

      Diretamente a seguir, adicione o loop de treinamento principal para seu parâmetro r:

      step_5_perturb.py

      . . .
          for i in range(30):
              r.data.clamp_(-EPSILON, EPSILON)
              optimizer.zero_grad()
      
              outputs = net(x + r)
              loss = criterion(outputs, labels)
              loss.backward()
              optimizer.step()
      
              _, pred = torch.max(outputs, 1)
              if i % 5 == 0:
                  print(f'Loss: {loss.item():.2f} / Class: {get_idx_to_label()[str(int(pred))]}')
      . . .
      

      Em cada iteração deste loop de treinamento, você:

      • r.data.clamp_(...): certifica-se que o parâmetro r é pequeno, dentro do EPSILON de 0.
      • optimizer.zero_grad(): limpa quaisquer gradientes que você computou na iteração anterior.
      • model(x + r): executa uma inferência na imagem modificada x + r.
      • Computa a loss (perda).
      • Computa o gradiente loss.backward.
      • Dá um passo de descendência de gradiente optimizer.step.
      • Computa a previsão pred.
      • Por fim, reporta a perda e a classe prevista print(...).

      Em seguida, salve a perturbação final r:

      step_5_perturb.py

      def main():
          . . .
          for i in range(30):
              . . .
          . . .
          np.save('outputs/adversarial_r.npy', r.data.numpy())
      

      Logo a seguir, ainda na função main, salve a imagem perturbada:

      step_5_perturb.py

      . . .
          os.makedirs('outputs', exist_ok=True)
          adversarial = get_adversarial_example(x, r)
      

      Por fim, execute uma previsão tanto na imagem original quanto no exemplo contraditório:

      step_5_perturb.py

          print(f'Old prediction: {predict(x)}')
          print(f'New prediction: {predict(x + r)}')
      

      Verifique novamente se seu script corresponde ao step_5_perturb.py no GitHub. Salve, saia e execute o script:

      • python step_5_perturb.py assets/dog.jpg

      Seu script irá gerar o seguinte resultado.

      Output

      Target class: goldfish, Carassius auratus Loss: 17.03 / Class: Pembroke, Pembroke Welsh corgi Loss: 8.19 / Class: Pembroke, Pembroke Welsh corgi Loss: 5.56 / Class: Pembroke, Pembroke Welsh corgi Loss: 3.53 / Class: Pembroke, Pembroke Welsh corgi Loss: 1.99 / Class: Pembroke, Pembroke Welsh corgi Loss: 1.00 / Class: goldfish, Carassius auratus Old prediction: Pembroke, Pembroke Welsh corgi New prediction: goldfish, Carassius auratus

      As duas últimas linhas indicam que agora você completou a construção de um exemplo contraditório do zero. Sua rede neural agora classifica uma imagem perfeitamente razoável de um corgi como um peixe dourado.

      Agora, você mostrou que as redes neurais podem ser enganadas com facilidade—mais que isso, a falta de robustez nos exemplos contraditórios tem consequências significativas. Uma pergunta que surge naturalmente é: Como se combate exemplos contraditórios? Muitas pesquisas foram realizadas por várias organizações, incluindo a OpenAI. Na próxima seção, você irá executar uma defesa para impedir este exemplo contraditório.

      Passo 6 — Defendendo-se contra exemplos contraditórios

      Neste passo, você irá implementar uma defesa contra exemplos contraditórios. A ideia é a seguinte: agora você é o proprietário do classificador de animais que está sendo implantado para a produção. Você não sabe quais exemplos contraditórios podem ser gerados, mas pode modificar a imagem ou o modelo para proteger-se contra os ataques.

      Antes de se defender, você deve ver por si mesmo como a manipulação de imagem é imperceptível. Abra as duas imagens a seguir:

      1. assets/dog.jpg
      2. outputs/adversarial.png

      Aqui estão as duas lado a lado. Sua imagem original terá uma taxa de proporção diferente. Consegue dizer qual delas é o exemplo contraditório?

      (esquerda) Corgi como peixe dourado, contraditório, (direita)Corgi como ele mesmo, não contraditório

      Observe que a nova imagem parece ser idêntica à original. Na realidade, a imagem da esquerda é sua imagem contraditória. Para ter certeza disso, faça o download da imagem e execute seu script de avaliação:

      • wget -O assets/adversarial.png https://github.com/alvinwan/fooling-neural-network/blob/master/outputs/adversarial.png?raw=true
      • python step_2_pretrained.py assets/adversarial.png

      Isso irá mostrar como resultado a classe de peixe dourado, para provar sua natureza contraditória:

      Output

      Prediction: goldfish, Carassius auratus

      Você irá por em prática uma defesa bastante ingênua, mas eficaz: comprima a imagem gravando-a em um formato JPEG com perdas. Abra o prompt interativo do Python:

      Em seguida, carregue a imagem contraditória como PNG e a salve de volta como JPEG.

      • from PIL import Image
      • image = Image.open('assets/adversarial.png')
      • image.save('outputs/adversarial.jpg')

      Digite CTRL + D para sair do prompt interativo do Python. Depois disso, realize uma inferência com seu modelo no exemplo contraditório comprimido:

      • python step_2_pretrained.py outputs/adversarial.jpg

      Isso gera uma classe corgi, mostrando a eficácia de sua defesa ingênua.

      Output

      Prediction: Pembroke, Pembroke Welsh corgi

      Agora, você completou sua primeira defesa contraditória. Observe que essa defesa não requer saber como o exemplo contraditório foi gerado. Isso é o que torna uma defesa eficaz. Há ainda muitas outras formas de defesa, muitas das quais envolvem um maior treinamento da rede neural. No entanto, esses procedimentos de treinamento são um tema próprio e estão fora do âmbito deste tutorial. Com isso, isso conclui seu guia sobre o machine learning contraditório.

      Conclusão

      Para compreender as implicações do seu trabalho neste tutorial, revisite as duas imagens lado a lado – o exemplo original e o contraditório.

      (esquerda) Corgi como peixe dourado, contraditório, (direita)Corgi como ele mesmo, não contraditório

      Apesar do fato de ambas as imagens serem idênticas ao olho humano, a primeira foi manipulada para enganar seu modelo. As duas imagens exibem claramente um corgi, mas o modelo está totalmente convencido de que o segundo modelo contém um peixe dourado. Isso deve gerar-lhe preocupação. Enquanto finaliza este tutorial, tenha em mente a fragilidade do seu modelo. Apenas aplicando uma simples transformação, você foi capaz de enganá-lo. Esses são perigos reais e plausíveis que escapam aos olhos, até mesmo de uma pesquisa de ponta. Pesquisas que vão além da segurança em machine learning são igualmente suscetíveis a essas falhas e, como um profissional, cabe a você aplicar o machine learning com segurança. Para mais leituras, confira os seguintes links:

      Para mais conteúdo e tutoriais de machine learning, visite nossa página do tópico de Machine Learning.



      Source link

      How To Trick a Neural Network in Python 3


      The author selected Dev Color to receive a donation as part of the Write for DOnations program.

      Could a neural network for animal classification be fooled? Fooling an animal classifier may have few consequences, but what if our face authenticator could be fooled? Or our self-driving car prototype’s software? Fortunately, legions of engineers and research stand between a prototype computer-vision model and production-quality models on our mobile devices or cars. Still, these risks have significant implications and are important to consider as a machine-learning practitioner.

      In this tutorial, you will try “fooling” or tricking an animal classifier. As you work through the tutorial, you’ll use OpenCV, a computer-vision library, and PyTorch, a deep learning library. You will cover the following topics in the associated field of adversarial machine learning:

      • Create a targeted adversarial example. Pick an image, say, of a dog. Pick a target class, say, a cat. Your goal is to trick the neural network into believing the pictured dog is a cat.
      • Create an adversarial defense. In short, protect your neural network against these tricky images, without knowing what the trick is.

      By the end of the tutorial, you will have a tool for tricking neural networks and an understanding of how to defend against tricks.

      Prerequisites

      To complete this tutorial, you will need the following:

      Step 1 — Creating Your Project and Installing Dependencies

      Let’s create a workspace for this project and install the dependencies you’ll need. You’ll call your workspace AdversarialML:

      Navigate to the AdversarialML directory:

      Make a directory to hold all your assets:

      • mkdir ~/AdversarialML/assets

      Then create a new virtual environment for the project:

      • python3 -m venv adversarialml

      Activate your environment:

      • source adversarialml/bin/activate

      Then install PyTorch, a deep-learning framework for Python that you’ll use in this tutorial.

      On macOS, install Pytorch with the following command:

      • python -m pip install torch==1.2.0 torchvision==0.4.0

      On Linux and Windows, use the following commands for a CPU-only build:

      • pip install torch==1.2.0+cpu torchvision==0.4.0+cpu -f https://download.pytorch.org/whl/torch_stable.html
      • pip install torchvision

      Now install prepackaged binaries for OpenCV and numpy, which are libraries for computer vision and linear algebra, respectively. OpenCV offers utilities such as image rotations, and numpy offers linear algebra utilities such as a matrix inversion:

      • python -m pip install opencv-python==3.4.3.18 numpy==1.14.5

      On Linux distributions, you will need to install libSM.so:

      • sudo apt-get install libsm6 libxext6 libxrender-dev

      With the dependencies installed, let’s run an animal classifier called ResNet18, which we describe next.

      Step 2 — Running a Pretrained Animal Classifier

      The torchvision library, the official computer vision library for PyTorch, contains pretrained versions of commonly used computer vision neural networks. These neural networks are all trained on ImageNet 2012, a dataset of 1.2 million training images with 1000 classes. These classes include vehicles, places, and most importantly, animals. In this step, you will run one of these pretrained neural networks, called ResNet18. We will refer to ResNet18 trained on ImageNet as an “animal classifier”.

      What is ResNet18? ResNet18 is the smallest neural network in a family of neural networks called residual neural networks, developed by MSR (He et al.). In short, He found that a neural network (denoted as a function f, with input x, and output f(x)) would perform better with a “residual connection” x + f(x). This residual connection is used prolifically in state-of-the-art neural networks, even today. For example, FBNetV2, FBNetV3.

      Download this image of a dog with the following command:

      • wget -O assets/dog.jpg https://www.xpresservers.com/wp-content/uploads/2020/06/How-To-Trick-a-Neural-Network-in-Python-3.png

      Image of corgi running near pond

      Then, download a JSON file to convert neural network output to a human-readable class name:

      • wget -O assets/imagenet_idx_to_label.json https://raw.githubusercontent.com/do-community/tricking-neural-networks/master/utils/imagenet_idx_to_label.json

      Next, create a script to run your pretrained model on the dog image. Create a new file called step_2_pretrained.py:

      • nano step_2_pretrained.py

      First, add the Python boilerplate by importing the necessary packages and declaring a main function:

      step_2_pretrained.py

      from PIL import Image
      import json
      import torchvision.models as models
      import torchvision.transforms as transforms
      import torch
      import sys
      
      def main():
          pass
      
      if __name__ == '__main__':
          main()
      

      Next, load the mapping from neural network output to human-readable class names. Add this directly after your import statements and before your main function:

      step_2_pretrained.py

      . . .
      def get_idx_to_label():
          with open("assets/imagenet_idx_to_label.json") as f:
              return json.load(f)
      . . .
      

      Create an image transformation function that will ensure your input image firstly has the correct dimensions, and secondly is normalized correctly. Add the following function directly after the last:

      step_2_pretrained.py

      . . .
      def get_image_transform():
          transform = transforms.Compose([
            transforms.Resize(224),
            transforms.CenterCrop(224),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                 std=[0.229, 0.224, 0.225])
          ])
          return transform
      . . .
      

      In get_image_transform, you define a number of different transformations to apply to the images that are passed to your neural network:

      • transforms.Resize(224): Resizes the smaller side of the image to 224. For example, if your image is 448 x 672, this operation would downsample the image to 224 x 336.
      • transforms.CenterCrop(224): Takes a crop from the center of the image, of size 224 x 224.
      • transforms.ToTensor(): Converts the image into a PyTorch tensor. All PyTorch models require PyTorch tensors as input.
      • transforms.Normalize(mean=..., std=...): Standardizes your input by subtracting the mean, then dividing by the standard deviation. This is described more precisely in the torchvision documentation.

      Add a utility to predict the animal class, given the image. This method uses both the previous utilities to perform animal classification:

      step_2_pretrained.py

      . . .
      def predict(image):
          model = models.resnet18(pretrained=True)
          model.eval()
      
          out = model(image)
      
          _, pred = torch.max(out, 1)  
          idx_to_label = get_idx_to_label()  
          cls = idx_to_label[str(int(pred))]  
          return cls
      . . .
      

      Here the predict function classifies the provided image using a pretrained neural network:

      • models.resnet18(pretrained=True): Loads a pretrained neural network called ResNet18.
      • model.eval(): Modifies the model in-place to run in ‘evaluation’ mode. The only other mode is ‘training’ mode, but training mode isn’t needed, as you aren’t training the model (that is, updating the model’s parameters) in this tutorial.
      • out = model(image): Runs the neural network on the provided, transformed image.
      • _, pred = torch.max(out, 1): The neural network outputs one probability for each possible class. This step computes the index of the class with the highest probability. For example, if out = [0.4, 0.1, 0.2], then pred = 0.
      • idx_to_label = get_idx_to_label(): Obtains a mapping from class index to human-readable class names. For example, the mapping could be {0: cat, 1: dog, 2: fish}.
      • cls = idx_to_label[str(int(pred))]: Convert the predicted class index to a class name. The examples provided in the last two bullet points would yield cls = idx_to_label[0] = 'cat'.

      Next, following the last function, add a utility to load images:

      step_2_pretrained.py

      . . .
      def load_image():
          assert len(sys.argv) > 1, 'Need to pass path to image'
          image = Image.open(sys.argv[1])
      
          transform = get_image_transform()
          image = transform(image)[None]
          return image
      . . .
      

      This will load an image from the path provided in the first argument to the script. transform(image)[None] applies the sequence of image transformations defined in the previous lines.

      Finally, populate your main function with the following, to load your image and classify the animal in the image:

      step_2_pretrained.py

      def main():
          x = load_image()
          print(f'Prediction: {predict(x)}')
      

      Double check that your file matches our final step 2 script at step_2_pretrained.py on GitHub. Save and exit your script, and run the animal classifier:

      • python step_2_pretrained.py assets/dog.jpg

      This will produce the following output, showing your animal classifier works as expected:

      Output

      Prediction: Pembroke, Pembroke Welsh corgi

      That concludes running inference with your pretrained model. Next, you will see an adversarial example in action by tricking a neural network with impercetible differences in the image.

      Step 3 — Trying an Adversarial Example

      Now, you will synthesize an adversarial example, and test the neural network on that example. For this tutorial, you will build adversarial examples of the form x + r, where x is the original image and r is some “perturbation”. You will eventually create the perturbation r yourself, but in this step, you will download one we created for you beforehand. Start by downloading the perturbation r:

      • wget -O assets/adversarial_r.npy https://github.com/do-community/tricking-neural-networks/blob/master/outputs/adversarial_r.npy?raw=true

      Now composite the picture with the perturbation. Create a new file called step_3_adversarial.py:

      • nano step_3_adversarial.py

      In this file, you will perform the following three-step process, to produce an adversarial example:

      1. Transform an image
      2. Apply the perturbation r
      3. Inverse transform the perturbed image

      At the end of step 3, you will have an adversarial image. First, import the necessary packages and declare a main function:

      step_3_adversarial.py

      from PIL import Image
      import torchvision.transforms as transforms
      import torch
      import numpy as np
      import os
      import sys
      
      from step_2_pretrained import get_idx_to_label, get_image_transform, predict, load_image
      
      
      def main():
          pass
      
      
      if __name__ == '__main__':
          main()
      

      Next, create an “image transformation” that inverts the earlier image transformation. Place this after your imports, before the main function:

      step_3_adversarial.py

      . . .
      def get_inverse_transform():
          return transforms.Normalize(
              mean=[-0.485/0.229, -0.456/0.224, -0.406/0.255],  # INVERSE normalize images, according to https://pytorch.org/docs/stable/torchvision/models.html
              std=[1/0.229, 1/0.224, 1/0.255])
      . . .
      

      As before, the transforms.Normalize operation subtracts the mean and divides by the standard deviation (that is, for the original image x, y = transforms.Normalize(mean=u, std=o) = (x - u) / o). You do some algebra and define a new operation that reverses this normalize function (transforms.Normalize(mean=-u/o, std=1/o) = (y - -u/o) / 1/o = (y + u/o) o = yo + u = x).

      As part of the inverse transformation, add a method that transforms a PyTorch tensor back to a PIL image. Add this following the last function:

      step_3_adversarial.py

      . . .
      def tensor_to_image(tensor):
          x = tensor.data.numpy().transpose(1, 2, 0) * 255.  
          x = np.clip(x, 0, 255)
          return Image.fromarray(x.astype(np.uint8))
      . . .
      
      • tensor.data.numpy() converts the PyTorch tensor into a NumPy array. .transpose(1, 2, 0) rearranges (channels, width, height) into (height, width, channels). This NumPy array is approximately in the range (0, 1). Finally, multiply by 255 to ensure the image is now in the range (0, 255).
      • np.clip ensures that all values in the image are between (0, 255).
      • x.astype(np.uint8) ensures all image values are integers. Finally, Image.fromarray(...) creates a PIL image object from the NumPy array.

      Then, use these utilities to create the adversarial example with the following:

      step_3_adversarial.py

      . . .
      def get_adversarial_example(x, r):
          y = x + r
          y = get_inverse_transform()(y[0])
          image = tensor_to_image(y)
          return image
      . . .
      

      This function generates the adversarial example as described at the start of the section:

      1. y = x + r. Take your perturbation r and add it to the original image x.
      2. get_inverse_transform: Obtain and apply the reverse image transformation you defined several lines earlier.
      3. tensor_to_image: Finally, convert the PyTorch tensor back to an image object.

      Finally, modify your main function to load the image, load the adversarial perturbation r, apply the perturbation, save the adversarial example to disk, and run prediction on the adversarial example:

      step_3_adversarial.py

      def main():
          x = load_image()
          r = torch.Tensor(np.load('assets/adversarial_r.npy'))
      
          # save perturbed image
          os.makedirs('outputs', exist_ok=True)
          adversarial = get_adversarial_example(x, r)
          adversarial.save('outputs/adversarial.png')
      
          # check prediction is new class
          print(f'Old prediction: {predict(x)}')
          print(f'New prediction: {predict(x + r)}')
      

      Your completed file should match step_3_adversarial.py on GitHub. Save the file, exit the editor, and launch your script with:

      • python step_3_adversarial.py assets/dog.jpg

      You’ll see this output:

      Output

      Old prediction: Pembroke, Pembroke Welsh corgi New prediction: goldfish, Carassius auratus

      You’ve now created an adversarial example: tricking the neural network into thinking a corgi is a goldfish. In the next step, you will actually create the perturbation r that you used here.

      Step 4 — Understanding an Adversarial Example

      For a primer on classification, see “How to Build an Emotion-Based Dog Filter”.

      Taking a step back, recall that your classification model outputs a probability for each class. During inference, the model predicts the class with the highest probability. During training, you update the model parameters t to maximize the probability of the correct class y, given your data x.

      argmax_y P(y|x,t)
      

      However, to generate adversarial examples, you now modify your goal. Instead of finding a class, your goal is now to find a new image, x. Take any class other than the correct one. Let us call this new class w. Your new objective is to maximize the probability of the wrong class.

      argmax_x P(w|x)
      

      Note that the neural network weights t are missing from the above expression. This is because you now assume the role of the adversary: Someone else has trained and deployed a model. You are only allowed to create adversarial inputs and are not allowed to modify the deployed model. To generate the adversarial example x, you can run “training”, except instead of updating the neural network weights, you update the input image with the new objective.

      As a reminder, for this tutorial, you assume that the adversarial example is an affine transformation of x. In other words, your adversarial example takes the form x + r for some r. In the next step, you will write a script to generate this r.

      Step 5 — Creating an Adversarial Example

      In this step, you will learn a perturbation r, so that your corgi is misclassified as a goldfish. Create a new file called step_5_perturb.py:

      Import the necessary packages and declare a main function:

      step_5_perturb.py

      from torch.autograd import Variable
      import torchvision.models as models
      import torch.nn as nn
      import torch.optim as optim
      import numpy as np
      import torch
      import os
      
      from step_2_pretrained import get_idx_to_label, get_image_transform, predict, load_image
      from step_3_adversarial import get_adversarial_example
      
      
      def main():
          pass
      
      
      if __name__ == '__main__':
          main()
      

      Directly following your imports and before the main function, define two constants:

      step_5_perturb.py

      . . .
      TARGET_LABEL = 1
      EPSILON = 10 / 255.
      . . .
      

      The first constant TARGET_LABEL is the class to misclassify the corgi as. In this case, index 1 corresponds to “goldfish”. The second constant EPSILON is the maximum amount of perturbation allowed for each image value. This limit is introduced so that the image is imperceptibly altered.

      Following your two constants, add a helper function to define a neural network and the perturbation parameter r:

      step_5_perturb.py

      . . .
      def get_model():
          net = models.resnet18(pretrained=True).eval()
          r = nn.Parameter(data=torch.zeros(1, 3, 224, 224), requires_grad=True)
          return net, r
      . . .
      
      • model.resnet18(pretrained=True) loads a pretrained neural network called ResNet18, like before. Also like before, you set the model to evaluation mode using .eval.
      • nn.Parameter(...) defines a new perturbation r, the size of the input image. The input image is also of size (1, 3, 224, 224). The requires_grad=True keyword argument ensures that you can update this perturbation r in later lines, in this file.

      Next, begin modifying your main function. Start by loading the model net, loading the inputs x, and defining the label label:

      step_5_perturb.py

      . . .
      def main():
          print(f'Target class: {get_idx_to_label()[str(TARGET_LABEL)]}')
          net, r = get_model()
          x = load_image()
          labels = Variable(torch.Tensor([TARGET_LABEL])).long()
        . . .
      

      Next, define both the criterion and the optimizer in your main function. The former tells PyTorch what the objective is—that is, what loss to minimize. The latter tells PyTorch how to train your parameter r:

      step_5_perturb.py

      . . .
          criterion = nn.CrossEntropyLoss()
          optimizer = optim.SGD([r], lr=0.1, momentum=0.1)
      . . .
      

      Directly following, add the main training loop for your parameter r:

      step_5_perturb.py

      . . .
          for i in range(30):
              r.data.clamp_(-EPSILON, EPSILON)
              optimizer.zero_grad()
      
              outputs = net(x + r)
              loss = criterion(outputs, labels)
              loss.backward()
              optimizer.step()
      
              _, pred = torch.max(outputs, 1)
              if i % 5 == 0:
                  print(f'Loss: {loss.item():.2f} / Class: {get_idx_to_label()[str(int(pred))]}')
      . . .
      

      On each iteration of this training loop, you:

      • r.data.clamp_(...): Ensure the parameter r is small, within EPSILON of 0.
      • optimizer.zero_grad(): Clear any gradients you computed in the previous iteration.
      • model(x + r): Run inference on the modified image x + r.
      • Compute the loss.
      • Compute the gradient loss.backward.
      • Take a gradient descent step optimizer.step.
      • Compute the prediction pred.
      • Finally, report the loss and predicted class print(...).

      Next, save the final perturbation r:

      step_5_perturb.py

      def main():
          . . .
          for i in range(30):
              . . .
          . . .
          np.save('outputs/adversarial_r.npy', r.data.numpy())
      

      Directly following, still in the main function, save the perturbed image:

      step_5_perturb.py

      . . .
          os.makedirs('outputs', exist_ok=True)
          adversarial = get_adversarial_example(x, r)
      

      Finally, run prediction on both the original image and the adversarial example:

      step_5_perturb.py

          print(f'Old prediction: {predict(x)}')
          print(f'New prediction: {predict(x + r)}')
      

      Double check your script matches step_5_perturb.py on GitHub. Save, exit, and run the script:

      • python step_5_perturb.py assets/dog.jpg

      Your script will output the following.

      Output

      Target class: goldfish, Carassius auratus Loss: 17.03 / Class: Pembroke, Pembroke Welsh corgi Loss: 8.19 / Class: Pembroke, Pembroke Welsh corgi Loss: 5.56 / Class: Pembroke, Pembroke Welsh corgi Loss: 3.53 / Class: Pembroke, Pembroke Welsh corgi Loss: 1.99 / Class: Pembroke, Pembroke Welsh corgi Loss: 1.00 / Class: goldfish, Carassius auratus Old prediction: Pembroke, Pembroke Welsh corgi New prediction: goldfish, Carassius auratus

      The last two lines indicate you have now completed construction of an adversarial example from scratch. Your neural network now classifies a perfectly reasonable corgi image as a goldfish.

      You’ve now shown that neural networks can be fooled easily—what’s more, the lack of robustness to adversarial examples has significant consequences. A natural next question is this: How can you combat adversarial examples? A good amount of research has been conducted by various organizations, including OpenAI. In the next section, you’ll run a defense to thwart this adversarial example.

      Step 6 — Defending Against Adversarial Examples

      In this step, you will implement a defense against adversarial examples. The idea is the following: You are now the owner of the animal classifier being deployed to production. You don’t know what adversarial examples may be generated, but you can modify the image or the model to protect against attacks.

      Before you defend, you should see for yourself how imperceptible the image manipulation is. Open both of the following images:

      1. assets/dog.jpg
      2. outputs/adversarial.png

      Here, you show both side by side. Your original image will have a different aspect ratio. Can you tell which is the adversarial example?

      (left) Corgi as goldfish, adversarial, (right)Corgi as itself, not adversarial

      Notice that the new image looks identical to the original. As it turns out, the left image is your adversarial image. To be certain, download the image and run your evaluation script:

      • wget -O assets/adversarial.png https://github.com/alvinwan/fooling-neural-network/blob/master/outputs/adversarial.png?raw=true
      • python step_2_pretrained.py assets/adversarial.png

      This will output the goldfish class, to prove its adversarial nature:

      Output

      Prediction: goldfish, Carassius auratus

      You will run a fairly naive, but effective, defense: Compress the image by writing to a lossy JPEG format. Open the Python interactive prompt:

      Then, load the adversarial image as PNG, and save it back as a JPEG.

      • from PIL import Image
      • image = Image.open('assets/adversarial.png')
      • image.save('outputs/adversarial.jpg')

      Type CTRL + D to leave the Python interactive prompt. Next, run inference with your model on the compressed adversarial example:

      • python step_2_pretrained.py outputs/adversarial.jpg

      This will now output the corgi class, proving the efficacy of your naive defense.

      Output

      Prediction: Pembroke, Pembroke Welsh corgi

      You’ve now completed your very first adversarial defense. Note that this defense does not require knowing how the adversarial example was generated. This is what makes an effective defense. There are also many other forms of defense, many of which involve retraining the neural network. However, these retraining procedures are a topic of their own and beyond the scope of this tutorial. With that, this concludes your guide into adversarial machine learning.

      Conclusion

      To understand the implications of your work in this tutorial, revisit the two images side-by-side—the original and the adversarial example.

      (left) Corgi as goldfish, adversarial, (right)Corgi as itself, not adversarial

      Despite the fact that both images look identical to the human eye, the first has been manipulated to fool your model. Both images clearly feature a corgi, and yet the model is entirely confident that the second model contains a goldfish. This should concern you and, as you wrap up this tutorial, keep in mind the fragility of your model. Just by applying a simple transformation, you can fool it. These are real, plausible dangers that evade even cutting-edge research. Research beyond machine-learning security is just as susceptible to these flaws, and, as a practitioner, it is up to you to apply machine learning safely. For more readings, check out the following links:

      For more machine learning content and tutorials, you can visit our Machine Learning Topic page.



      Source link

      Como Construir uma Rede Neural para Reconhecer Dígitos Manuscritos com o TensorFlow


      Introdução

      Redes neurais são usadas como um método de deep learning ou aprendizado profundo, um dos vários subcampos da inteligência artificial. Elas foram propostas pela primeira vez há cerca de 70 anos como uma tentativa de simular a maneira como o cérebro humano funciona, embora de uma forma muito mais simplificada. Os “neurônios” individuais são conectados em camadas, com pesos designados para determinar como o neurônio responde quando os sinais são propagados pela rede. Anteriormente, as redes neurais eram limitadas no número de neurônios que elas eram capazes de simular e, portanto, a complexidade do aprendizado que podiam alcançar. Mas nos últimos anos, devido aos avanços no desenvolvimento de hardware, pudemos construir redes muito profundas e treiná-las em enormes datasets ou conjuntos de dados para obter avanços na inteligência de máquinas.

      Essas inovações permitiram que as máquinas correspondessem e excedessem as capacidades dos humanos em realizar certas tarefas. Uma dessas tarefas é o reconhecimento de objetos. Embora as máquinas tenham sido historicamente incapazes de corresponder à visão humana, avanços recentes em deep learning tornaram possível construir redes neurais capazes de reconhecer objetos, rostos, textos e até mesmo emoções.

      Neste tutorial, você implementará uma pequena subseção de reconhecimento de objeto recognition—digit. Utilizando o TensorFlow, uma biblioteca Python open-source desenvolvida pelos laboratórios do Google Brain para pesquisa em deep learning, você pegará imagens desenhadas à mão dos números de 0 a 9 e construirá e treinará uma rede neural para reconhecer e prever o rótulo correto para o dígito exibido.

      Embora você não precise de experiência prévia em deep learning prático ou de uso do TensorFlow para acompanhar este tutorial, vamos assumir alguma familiaridade com termos e conceitos de machine learning, como treinamento e testes, recursos e rótulos, otimização e avaliação. Você pode aprender mais sobre esses conceitos em Uma Introdução ao Machine Learning.

      Pré-requisitos

      Para completar esse tutorial, você vai precisar de:

      Passo 1 — Configurando o Projeto

      Antes de desenvolver o programa de reconhecimento, você precisará instalar algumas dependências e criar um espaço de trabalho para armazenar seus arquivos.

      Usaremos um ambiente virtual do Python 3 para gerenciar as dependências do nosso projeto. Crie um novo diretório para o seu projeto e navegue até o novo diretório:

      • mkdir tensorflow-demo
      • cd tensorflow-demo

      Execute os seguintes comandos para configurar o ambiente virtual para este tutorial:

      • python3 -m venv tensorflow-demo
      • source tensorflow-demo/bin/activate

      Em seguida, instale as bibliotecas que você usará neste tutorial. Usaremos versões específicas dessas bibliotecas criando um arquivo requirements.txt no diretório do projeto, que especifica o requisito e a versão que precisamos. Crie o arquivo requirements.txt:

      Abra o arquivo em seu editor de textos e adicione as seguintes linhas para especificar as bibliotecas Image, NumPy, e TensorFlow e suas versões:

      requirements.txt

      image==1.5.20
      numpy==1.14.3
      tensorflow==1.4.0
      

      Salve o arquivo e saia do editor. Em seguida instale estas bibliotecas com o seguinte comando:

      • pip install -r requirements.txt

      Com as dependências instaladas, podemos começar a trabalhar no nosso projeto.

      Passo 2 — Importando o Dataset MNIST

      O dataset que estaremos utilizando neste tutorial é chamado de dataset MNIST, e ele é um clássico na comunidade de machine learning. Este dataset é composto de imagens de dígitos manuscritos, com 28x28 pixels de tamanho. Aqui estão alguns exemplos dos dígitos incluídos no dataset:

      Vamos criar um programa Python para trabalhar com este dataset. Usaremos um arquivo para todo o nosso trabalho neste tutorial. Crie um novo arquivo chamado main.py:

      Agora abra este arquivo no editor de textos de sua preferência e adicione esta linha de código ao arquivo para importar a biblioteca do TensorFlow:

      main.py

      import tensorflow as tf
      

      Adicione as seguintes linhas de código ao seu arquivo para importar o dataset MNIST e armazenar os dados da imagem na variável mnist:

      main.py

      from tensorflow.examples.tutorials.mnist import input_data
      mnist = input_data.read_data_sets("MNIST_data/", one_hot=True) # y labels are oh-encoded
      

      Ao ler os dados, estamos usando one-hot-encoding para representar os rótulos (o dígito real desenhado, por exemplo "3") das imagens. O one-hot-encoding utiliza um vetor de valores binários para representar valores numéricos ou categóricos. Como nossos rótulos são para os dígitos de 0 a 9, o vetor contém dez valores, um para cada dígito possível. Um desses valores é definido como 1, para representar o dígito nesse índice do vetor, e o restante é difinido como 0. Por exemplo, o dígito 3 é representado usando o vetor [0, 0, 0, 1, 0, 0, 0, 0, 0, 0]. Como o valor no índice 3 está armazenado como 1, o vetor representa o dígito 3.

      Para representar as imagens, os 28x28 pixels são achatados em um vetor 1D com 784 pixels de tamanho. Cada um dos 784 pixels que compõem a imagem é armazenado como um valor entre 0 e 255. Isso determina a escala de cinza do pixel, pois nossas imagens são apresentadas apenas em preto e branco. Portanto, um pixel preto é representado por 255 e um pixel branco por 0, com os vários tons de cinza em algum lugar entre eles.

      Podemos usar a variável mnist para descobrir o tamanho do dataset que acabamos de importar. Observando os num_examples para cada um dos três subconjuntos, podemos determinar que o dataset foi dividido em 55.000 imagens para treinamento, 5000 para validação e 10.000 para teste. Adicione as seguintes linhas ao seu arquivo:

      main.py

      
      n_train = mnist.train.num_examples # 55,000
      n_validation = mnist.validation.num_examples # 5000
      n_test = mnist.test.num_examples # 10,000
      

      Agora que temos nossos dados importados, é hora de pensar sobre a rede neural.

      Passo 3 — Definindo a Arquitetura da Rede Neural

      A arquitetura da rede neural refere-se a elementos como o número de camadas na rede, o número de unidades em cada camada e como as unidades são conectadas entre as camadas. Como as redes neurais são vagamente inspiradas no funcionamento do cérebro humano, aqui o termo unidade é usado para representar o que seria biologicamente um neurônio. Assim como os neurônios transmitem sinais pelo cérebro, as unidades tomam alguns valores das unidades anteriores como entrada, realizam uma computação e, em seguida, transmitem o novo valor como saída para outras unidades. Essas unidades são colocadas em camadas para formar a rede, iniciando no mínimo com uma camada para entrada de valores e uma camada para valores de saída. O termo hidden layer ou camada oculta é usado para todas as camadas entre as camadas de entrada e saída, ou seja, aquelas "ocultas" do mundo real.

      Arquiteturas diferentes podem produzir resultados drasticamente diferentes, já que o desempenho pode ser pensado como uma função da arquitetura entre outras coisas, como os parâmetros, os dados e a duração do treinamento.

      Adicione as seguintes linhas de código ao seu arquivo para armazenar o número de unidades por camada nas variáveis globais. Isso nos permite alterar a arquitetura de rede em um único lugar e, no final do tutorial, você pode testar por si mesmo como diferentes números de camadas e unidades afetarão os resultados de nosso modelo:

      main.py

      
      n_input = 784   # input layer (28x28 pixels)
      n_hidden1 = 512 # 1st hidden layer
      n_hidden2 = 256 # 2nd hidden layer
      n_hidden3 = 128 # 3rd hidden layer
      n_output = 10   # output layer (0-9 digits)
      

      O diagrama a seguir mostra uma visualização da arquitetura que projetamos, com cada camada totalmente conectada às camadas adjacentes:

      O termo "deep neural network" ou rede neural profunda se relaciona com o número de camadas ocultas, com "superficial" geralmente significando apenas uma camada oculta e "profunda", referindo-se a várias camadas ocultas. Fornecidos dados de treinamento suficientes, uma rede neural superficial com um número suficiente de unidades deve teoricamente ser capaz de representar qualquer função que uma rede neural profunda possa. Mas é mais eficiente computacionalmente usar uma rede neural profunda menor para realizar a mesma tarefa que exigiria uma rede superficial com exponencialmente mais unidades ocultas. Redes neurais superficiais também freqüentemente encontram overfitting, onde a rede essencialmente memoriza os dados de treinamento que viu e não é capaz de generalizar o conhecimento para novos dados. É por isso que as redes neurais profundas são mais comumente usadas: as várias camadas entre os dados brutos de entrada e o rótulo de saída permitem que a rede aprenda recursos em vários níveis de abstração, tornando a própria rede mais capaz de generalizar.

      Outros elementos da rede neural que precisam ser definidos aqui são os hiperparâmetros. Ao contrário dos parâmetros que serão atualizados durante o treinamento, esses valores são definidos inicialmente e permanecem constantes durante todo o processo. No seu arquivo, defina as seguintes variáveis e valores:

      main.py

      
      learning_rate = 1e-4
      n_iterations = 1000
      batch_size = 128
      dropout = 0.5
      

      A taxa de aprendizado, learningrate, representa o quanto os parâmetros serão ajustados em cada etapa do processo de aprendizado. Esses ajustes são um componente-chave do treinamento: depois de cada passagem pela rede, ajustamos os pesos ligeiramente para tentar reduzir a perda. Taxas de aprendizado maiores podem convergir mais rapidamente, mas também têm o potencial de ultrapassar os valores ideais à medida que são atualizados. O número de iterações, niterations, refere-se a quantas vezes passamos pela etapa de treinamento e o tamanho do lote ou batch_size se refere a quantos exemplos de treinamento estamos usando em cada etapa. A variável dropout representa um limiar no qual eliminamos algumas unidades aleatoriamente. Estaremos usando dropout em nossa última camada oculta para dar a cada unidade 50% de chance de ser eliminada em cada etapa de treinamento. Isso ajuda a evitar o overfitting.

      Agora já definimos a arquitetura de nossa rede neural e os hiperparâmetros que impactam o processo de aprendizagem. O próximo passo é construir a rede como um gráfico do TensorFlow.

      Passo 4 — Construindo o Gráfico do TensorFlow

      Para construir nossa rede, vamos configurará-la como um gráfico computacional para o TensorFlow executar. O conceito central do TensorFlow é o tensor, uma estrutura de dados semelhante a uma matriz ou lista inicializada, manipulada à medida que passa pelo gráfico e atualizada através do processo de aprendizado.

      Começaremos definindo três tensores como placeholders ou marcadores de posição, que são tensores nos quais alimentaremos os valores posteriormente. Adicione o seguinte ao seu arquivo:

      main.py

      
      X = tf.placeholder("float", [None, n_input])
      Y = tf.placeholder("float", [None, n_output])
      keep_prob = tf.placeholder(tf.float32) ^
      

      O único parâmetro que precisa ser especificado em sua declaração é o tamanho dos dados os quais estaremos alimentando. Para X usamos um formato [None, 784], onde None representa qualquer quantidade, pois estaremos alimentando em um número indefinido de imagens de 784 pixels. O formato de Y é [None, 10] pois iremos usá-lo para um número indefinido de saídas de rótulo, com 10 classes possíveis. O tensor keep_prob é usado para controlar a taxa de dropout, e nós o inicializamos como um placeholder ao invés de uma variável imutável porque queremos usar o mesmo tensor tanto para treinamento (quando dropout é definido para 0.5) quanto para testes (quando dropout é definido como 1.0).

      Os parâmetros que a rede atualizará no processo de treinamento são os valores weight e bias, portanto, precisamos definir um valor inicial em vez de um placeholder vazio. Esses valores são essencialmente onde a rede faz seu aprendizado, pois são utilizados nas funções de ativação dos neurônios, representando a força das conexões entre as unidades.

      Como os valores são otimizados durante o treinamento, podemos defini-los para zero por enquanto. Mas o valor inicial realmente tem um impacto significativo na precisão final do modelo. Usaremos valores aleatórios de uma distribuição normal truncada para os pesos. Queremos que eles estejam próximos de zero, para que possam se ajustar em uma direção positiva ou negativa, e um pouco diferente, para que gerem erros diferentes. Isso garantirá que o modelo aprenda algo útil. Adicione estas linhas:

      main.py

      
      weights = {
          'w1': tf.Variable(tf.truncated_normal([n_input, n_hidden1], stddev=0.1)),
          'w2': tf.Variable(tf.truncated_normal([n_hidden1, n_hidden2], stddev=0.1)),
          'w3': tf.Variable(tf.truncated_normal([n_hidden2, n_hidden3], stddev=0.1)),
          'out': tf.Variable(tf.truncated_normal([n_hidden3, n_output], stddev=0.1)),
      }
      

      Para o bias ou tendência, usamos um pequeno valor constante para garantir que os tensores se ativem nos estágios iniciais e, portanto, contribuam para a propagação. Os pesos e tensores de bias são armazenados em objetos de dicionário para facilitar o acesso. Adicione este código ao seu arquivo para definir cada bias:

      main.py

      
      biases = {
          'b1': tf.Variable(tf.constant(0.1, shape=[n_hidden1])),
          'b2': tf.Variable(tf.constant(0.1, shape=[n_hidden2])),
          'b3': tf.Variable(tf.constant(0.1, shape=[n_hidden3])),
          'out': tf.Variable(tf.constant(0.1, shape=[n_output]))
      }
      

      Em seguida, configure as camadas da rede definindo as operações que manipularão os tensores. Adicione estas linhas ao seu arquivo:

      main.py

      
      layer_1 = tf.add(tf.matmul(X, weights['w1']), biases['b1'])
      layer_2 = tf.add(tf.matmul(layer_1, weights['w2']), biases['b2'])
      layer_3 = tf.add(tf.matmul(layer_2, weights['w3']), biases['b3'])
      layer_drop = tf.nn.dropout(layer_3, keep_prob)
      output_layer = tf.matmul(layer_3, weights['out']) + biases['out']
      

      Cada camada oculta executará a multiplicação da matriz nas saídas da camada anterior e os pesos da camada atual e adicionará o bias a esses valores. Na última camada oculta, aplicaremos uma operação de eliminação usando nosso valor keep_prob de 0.5.

      O passo final na construção do gráfico é definir a função de perda que queremos otimizar. Uma escolha popular da função de perda nos programas do TensorFlow é a cross-entropy ou entropia cruzada, também conhecida como log-loss, que quantifica a diferença entre duas distribuições de probabilidade (as predições e os rótulos). Uma classificação perfeita resultaria em uma entropia cruzada de 0, com a perda completamente minimizada.

      Também precisamos escolher o algoritmo de otimização que será usado para minimizar a função de perda. Um processo denominado otimização gradiente descendente é um método comum para encontrar o mínimo (local) de uma função, tomando etapas iterativas ao longo do gradiente em uma direção negativa (descendente). Existem várias opções de algoritmos de otimização de gradiente descendente já implementados no TensorFlow, e neste tutorial vamos usar o otimizador Adam. Isso se estende à otimização de gradiente descendente usando o momento para acelerar o processo através do cálculo de uma média exponencialmente ponderada dos gradientes e usando isso nos ajustes. Adicione o seguinte código ao seu arquivo:

      main.py

      
      cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=Y, logits=output_layer))
      train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
      

      Agora acabamos de definir a rede e a construímos com o TensorFlow. O próximo passo é alimentar os dados através do gráfico para treiná-los e, em seguida, testar se realmente aprendeu alguma coisa.

      Passo 5 — Treinando e Testando

      O processo de treinamento envolve alimentar o dataset de treinamento através do gráfico e otimizar a função de perda. Toda vez que a rede itera um lote de mais imagens de treinamento, ela atualiza os parâmetros para reduzir a perda, a fim de prever com mais precisão os dígitos exibidos. O processo de teste envolve a execução do nosso dataset de teste através do gráfico treinado e o acompanhamento do número de imagens que são corretamente previstas, para que possamos calcular a precisão.

      Antes de iniciar o processo de treinamento, definiremos nosso método de avaliação da precisão para que possamos imprimi-lo em mini-lotes de dados enquanto treinamos. Estas declarações impressas nos permitem verificar que, da primeira iteração até a última, a perda diminui e a precisão aumenta; elas também nos permitem rastrear se executamos ou não repetições suficientes para alcançar um resultado consistente e ideal:

      main.py

      correct_pred = tf.equal(tf.argmax(output_layer, 1), tf.argmax(Y, 1))
      accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
      

      Em correct_pred, usamos a função arg_max para comparar quais imagens estão sendo previstas corretamente observando output_layer (predições) e Y (labels), e usamos a função equal para retornar isso como uma lista de Booleanos. Podemos, então, converter essa lista em floats e calcular a média para obter uma pontuação total da precisão.

      Agora estamos prontos para inicializar uma sessão para executar o gráfico. Nesta sessão, vamos alimentar a rede com nossos exemplos de treinamento e, uma vez treinados, alimentamos o mesmo gráfico com novos exemplos de teste para determinar a precisão do modelo. Adicione as seguintes linhas de código ao seu arquivo:

      main.py

      
      init = tf.global_variables_initializer()
      sess = tf.Session()
      sess.run(init)
      

      A essência do processo de treinamento em deep learning é otimizar a função de perda. Aqui, pretendemos minimizar a diferença entre os rótulos previstos das imagens e os rótulos verdadeiros das imagens. O processo envolve quatro etapas que são repetidas para um número definido de iterações:

      Em cada etapa de treinamento, os parâmetros são ligeiramente ajustados para tentar reduzir a perda para a próxima etapa. À medida que o aprendizado avança, devemos ver uma redução na perda e, eventualmente, podemos parar de treinar e usar a rede como um modelo para testar nossos novos dados.

      Adicione este código ao arquivo:

      main.py

      
      # train on mini batches
      for i in range(n_iterations):
          batch_x, batch_y = mnist.train.next_batch(batch_size)
          sess.run(train_step, feed_dict={X: batch_x, Y: batch_y, keep_prob:dropout})
      
          # print loss and accuracy (per minibatch)
          if i%100==0:
              minibatch_loss, minibatch_accuracy = sess.run([cross_entropy, accuracy], feed_dict={X: batch_x, Y: batch_y, keep_prob:1.0})
              print("Iteration", str(i), "t| Loss =", str(minibatch_loss), "t| Accuracy =", str(minibatch_accuracy))
      

      Após 100 iterações de cada etapa de treinamento em que alimentamos um mini-lote de imagens através da rede, imprimimos a perda e a precisão desse lote. Observe que não devemos esperar uma perda decrescente e uma precisão crescente aqui, pois os valores são por lote, não para o modelo inteiro. Usamos mini-lotes de imagens em vez de alimentá-las individualmente para acelerar o processo de treinamento e permitir que a rede veja vários exemplos diferentes antes de atualizar os parâmetros.

      Quando o treinamento estiver concluído, podemos executar a sessão nas imagens de teste. Desta vez estamos usando uma taxa de dropout keep_prob de 1.0 para garantir que todas as unidades estejam ativas no processo de teste.

      Adicione este código ao arquivo:

      main.py

      
      test_accuracy = sess.run(accuracy, feed_dict={X: mnist.test.images, Y: mnist.test.labels, keep_prob:1.0})
      print("nAccuracy on test set:", test_accuracy)
      

      Agora é hora de executar nosso programa e ver com que precisão nossa rede neural pode reconhecer esses dígitos manuscritos. Salve o arquivo main.py e execute o seguinte comando no terminal para executar o script:

      Você verá uma saída semelhante à seguinte, embora os resultados individuais de perda e precisão possam variar um pouco:

      Output

      Iteration 0 | Loss = 3.67079 | Accuracy = 0.140625 Iteration 100 | Loss = 0.492122 | Accuracy = 0.84375 Iteration 200 | Loss = 0.421595 | Accuracy = 0.882812 Iteration 300 | Loss = 0.307726 | Accuracy = 0.921875 Iteration 400 | Loss = 0.392948 | Accuracy = 0.882812 Iteration 500 | Loss = 0.371461 | Accuracy = 0.90625 Iteration 600 | Loss = 0.378425 | Accuracy = 0.882812 Iteration 700 | Loss = 0.338605 | Accuracy = 0.914062 Iteration 800 | Loss = 0.379697 | Accuracy = 0.875 Iteration 900 | Loss = 0.444303 | Accuracy = 0.90625 Accuracy on test set: 0.9206

      Para tentar melhorar a precisão do nosso modelo, ou para saber mais sobre o impacto dos hiperparâmetros de ajuste, podemos testar o efeito de alterar a taxa de aprendizado, o limite de dropout, o tamanho do lote e o número de iterações. Também podemos alterar o número de unidades em nossas camadas ocultas e alterar a quantidade das próprias camadas ocultas, para ver como diferentes arquiteturas aumentam ou diminuem a precisão do modelo.

      Para demonstrar que a rede está realmente reconhecendo as imagens desenhadas à mão, vamos testá-la em uma única imagem nossa.

      Primeiro, faça o download dessa amostra de imagem de teste ou abra um editor gráfico e crie sua própria imagem de 28x28 pixels de um dígito.

      Abra o arquivo main.py no seu editor e adicione as seguintes linhas de código ao topo do arquivo para importar duas bibliotecas necessárias para a manipulação de imagens.

      main.py

      
      import numpy as np
      from PIL import Image
      ...
      

      Em seguida, no final do arquivo, adicione a seguinte linha de código para carregar a imagem de teste do dígito manuscrito:

      main.py

      
      img = np.invert(Image.open("test_img.png").convert('L')).ravel()
      

      A função open da bibliotecaImage carrega a imagem de teste como um array 4D contendo os três canais de cores RGB e a transparência Alpha. Esta não é a mesma representação que usamos anteriormente ao ler o dataset com o TensorFlow, portanto, precisamos fazer algum trabalho extra para corresponder ao formato.

      Primeiro, usamos a função convert com o parâmetro L para reduzir a representação 4D RGBA para um canal de cor em escala de cinza. Aarmazenamos isso como um array numpy e o invertemos usando np.invert, porque a matriz atual representa o preto como 0 e o branco como 255, porém, precisamos do oposto. Finalmente, chamamos ravel para achatar o array.

      Agora que os dados da imagem estão estruturados corretamente, podemos executar uma sessão da mesma forma que anteriormente, mas desta vez apenas alimentando uma imagem única para teste. Adicione o seguinte código ao seu arquivo para testar a imagem e imprimir o rótulo de saída.

      [labe main.py]
      prediction = sess.run(tf.argmax(output_layer,1), feed_dict={X: [img]})
      print ("Prediction for test image:", np.squeeze(prediction))
      

      A função np.squeeze é chamada na predição para retornar o único inteiro da matriz (ou seja, para ir de [2] para 2). A saída resultante demonstra que a rede reconheceu essa imagem como o dígito 2.

      Output

      Prediction for test image: 2

      Você pode tentar testar a rede com imagens mais complexas - dígitos que se parecem com outros dígitos, por exemplo, ou dígitos que foram mal desenhados ou desenhados incorretamente - para ver como ela se sai.

      Conclusão

      Neste tutorial você treinou com sucesso uma rede neural para classificar o dataset MNIST com cerca de 92% de precisão e testou em uma imagem sua. O estado da arte em pesquisa atual alcança cerca de 99% neste mesmo problema, usando arquiteturas de rede mais complexas envolvendo camadas convolucionais. Elas usam a estrutura 2D da imagem para melhor representar o conteúdo, ao contrário do nosso método que achata todos os pixels em um vetor de 784 unidades. Você pode ler mais sobre esse tópico no website do TensorFlow, e ver os documentos de pesquisa detalhando os resultados mais precisos no wesite do MNIST.

      Agora que você sabe como construir e treinar uma rede neural, pode tentar usar essa implementação em seus próprios dados ou testá-la em outros datasets populares, como o Google StreetView House Numbers, ou o dataset CIFAR-10 para um reconhecimento de imagem mais genérico.

      Por Ellie Birbeck



      Source link