Programando com PyTorch: criando aplicativos de aprendizado profundo

imagemOlá Habitantes! Ian Poynter o ajudará a descobrir como configurar o PyTorch na nuvem, como criar arquiteturas neurais que tornam mais fácil trabalhar com imagens, som e texto. O livro cobre os conceitos essenciais para aprender a portar, depurar modelos e usar a biblioteca PyTorch. Você aprenderá a: - Implementar modelos de aprendizagem profunda - Usar PyTorch em projetos de grande escala - Aplicar transferência de aprendizagem - Usar modelos PyTorch torchaudio e convolucional para classificar dados de áudio - Aplicar as técnicas de PNL mais recentes usando um modelo treinado na Wikipedia - Depurar modelos PyTorch com TensorBoard e Flamegraph - implantar aplicativos PyTorch em contêineres “PyTorch é uma das bibliotecas de aprendizado profundo de crescimento mais rápido, rivalizando com o gigante TensorFlow do Google - quase no mesmo nível.





Classificação de imagem com PyTorch



Os livros didáticos de aprendizagem profunda estão repletos de terminologia profissional incompreensível. Tento reduzi-lo ao mínimo e sempre dou um exemplo que pode ser facilmente estendido à medida que você se acostuma a trabalhar com o PyTorch. Usamos esse exemplo ao longo do livro para demonstrar como depurar um modelo (Capítulo 7) ou implantá-lo na produção (Capítulo 8).



De agora em diante até o final do Capítulo 4, iremos compilar o classificador de imagens. Redes neurais são comumente usadas como classificadores de imagem; as redes oferecem uma imagem e fazem uma pergunta simples: "O que é isso?"



Vamos começar criando nosso aplicativo em PyTorch.



Problema de classificação



Aqui, criaremos um classificador simples que pode distinguir um peixe de um gato. Iremos iterar o processo de design e desenvolvimento de nosso modelo para torná-lo mais preciso.

Na fig. 2.1 e 2.2 representam um peixe e um gato em toda a sua glória. Não tenho certeza se o peixe tem nome, mas o nome do gato é Helvetica.



Vamos começar discutindo alguns dos problemas de classificação padrão.



imagem imagem



Dificuldades padrão



Como escrever um programa que pode diferenciar um peixe de um gato? Talvez você escreva um conjunto de regras descrevendo se um gato tem cauda ou que um peixe tem escamas e aplique essas regras à imagem para que o programa possa classificar a imagem. Mas isso exigirá tempo, esforço e habilidade. E se você encontrar um gato Manx? Embora seja claramente um gato, não tem cauda.



Essas regras ficam cada vez mais complexas quando você tenta descrever todos os cenários possíveis usando-as. Além disso, devo admitir que a programação visual é terrível para mim, então a idéia de ter que escrever manualmente o código para todas essas regras é assustadora.



Você precisa de uma função que retorne um gato ou peixe quando você inserir uma imagem. É difícil construir tal função simplesmente listando todos os critérios por completo. Mas o aprendizado profundo basicamente força o computador a fazer o trabalho árduo de criar todas essas regras sobre as quais acabamos de falar, desde que criemos a estrutura, forneçamos muitos dados à rede e informemos se ela deu a resposta certa. Isso é o que vamos fazer. Além disso, você aprenderá algumas técnicas básicas para usar o PyTorch.



Mas primeiro os dados



Primeiro, precisamos de dados. Quantos dados? Depende de vários fatores. Como você verá no Capítulo 4, a ideia de que qualquer técnica de aprendizado profundo requer grandes quantidades de dados para treinar uma rede neural não é necessariamente verdadeira. No entanto, agora vamos começar do zero, o que geralmente requer acesso a muitos dados. Muitas imagens de peixes e gatos são necessárias.



Pode-se gastar algum tempo baixando um monte de imagens de uma busca de imagens no Google, mas há uma maneira mais fácil: a coleção padrão de imagens usada para treinar redes neurais é ImageNet. Ele contém mais de 14 milhões de imagens e 20 mil categorias de imagens. Este é o padrão pelo qual todos os classificadores de imagem são comparados. Portanto, eu tiro as imagens de lá, embora você possa escolher outras opções se quiser.



Além dos dados, o PyTorch deve ter uma maneira de definir o que é um gato e o que é um peixe. É fácil para nós, mas é mais difícil para um computador (é por isso que criamos um programa!). Usamos rotulagem anexada aos dados e isso é chamado de aprendizagem supervisionada. (Se você não tiver acesso a nenhum dos rótulos, então adivinhou, o aprendizado de máquina não supervisionado é usado.)



Se usarmos dados do ImageNet, seus rótulos não serão úteis porque contêm muitas informações. Rotular um gato malhado ou uma truta para um computador não é o mesmo que um gato ou um peixe.



É necessário rotulá-los novamente. Como o ImageNet é uma vasta coleção de imagens, eu compilei a imagem e as URLs de marcação de peixes e gatos (https://oreil.ly/NbtEU).



Você pode executar o script download.py neste diretório e ele irá baixar as imagens dos urls e colocá-los nos locais de treinamento apropriados. A reclassificação é simples; o script armazena imagens de gatos no diretório train / cat e imagens de peixes no diretório train / fish. Se você não quiser usar um script para fazer o download, basta criar esses diretórios e colocar as imagens correspondentes nos lugares certos. Agora temos dados, mas precisamos convertê-los para um formato que o PyTorch possa entender.



PyTorch e carregadores de dados



Carregar e transformar dados em formatos prontos para treinamento costuma ser uma área da ciência de dados que consome muito tempo. A PyTorch desenvolveu requisitos de interação de dados estabelecidos que o tornam bastante simples, esteja você trabalhando com imagens, texto ou áudio.



As duas principais condições para trabalhar com dados são conjuntos de dados e carregadores de dados. Um conjunto de dados é uma classe Python que nos permite receber os dados que enviamos para a rede neural.



Um carregador de dados é o que transfere dados de um conjunto de dados para a rede. (Isso pode incluir informações como: Quantos processos de trabalho estão enviando dados para a rede? Quantas imagens estamos enviando ao mesmo tempo?)



Vamos dar uma olhada no conjunto de dados primeiro. Cada conjunto de dados, quer contenha imagens, áudio, texto, paisagens 3D, informações do mercado de ações ou qualquer outra coisa, pode interagir com o PyTorch, desde que atenda aos requisitos desta classe abstrata do Python:



class Dataset(object):
     def __getitem__(self, index):
          raise NotImplementedError

     def __len__(self):
          raise NotImplementedError


É muito simples: temos que usar um método que retorna o tamanho do nosso conjunto de dados (len), e um que pode extrair um elemento do conjunto de dados em um par (rótulo, tensor). Isso é chamado pelo carregador de dados, pois alimenta os dados para a rede neural para treinamento. Portanto, temos que escrever um corpo para o método getitem que pode pegar uma imagem, convertê-la em um tensor e colocá-la de volta e marcá-la para que PyTorch possa trabalhar com ela. Tudo está claro, mas obviamente esse cenário é bastante comum, então talvez o PyTorch torne a tarefa mais fácil?



Criar um conjunto de dados de treinamento



O pacote torchvision inclui uma classe ImageFolder que faz praticamente tudo, assumindo que nossas imagens estejam em uma estrutura onde cada diretório é um rótulo (por exemplo, todos os gatos estão em um diretório chamado cat). Aqui está o que você precisa para o exemplo de gato e peixe:



import torchvision
from torchvision import transforms

train_data_path = "./train/"
transforms = transforms.Compose([
      transforms.Resize(64),
      transforms.ToTensor(),
      transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                std=[0.229, 0.224, 0.225] )
      ])

train_data = torchvision.datasets.ImageFolder
(root=train_data_path,transform=transforms)


Algo mais é adicionado aqui porque o torchvision também permite especificar uma lista de transformações a serem aplicadas à imagem antes que ela entre na rede neural. A transformação padrão é pegar os dados da imagem e transformá-los em um tensor (o método trans forms.ToTensor () mostrado no código anterior), mas também faz algumas outras coisas que podem não ser tão óbvias.



Primeiro, as GPUs são construídas para realizar cálculos rápidos e de tamanho padrão. Mas provavelmente temos uma variedade de imagens em muitas resoluções. Para melhorar o desempenho do processamento, escalamos cada imagem de entrada para a mesma resolução de 64x64 usando a transformação Resize (64). Em seguida, convertemos as imagens em um tensor e finalmente normalizamos o tensor em torno de um conjunto específico de pontos de média e desvio padrão.



A normalização é importante porque espera-se que um grande número de multiplicações seja executado conforme a entrada passa pelas camadas da rede neural; manter os valores de entrada entre 0 e 1 evita grandes aumentos nos valores durante a fase de aprendizagem (conhecido como o problema do gradiente explosivo). Essa encarnação mágica é apenas a média e o desvio padrão do conjunto de dados ImageNet como um todo. Você pode calculá-lo especificamente para um subconjunto de peixes e gatos, mas esses valores são bastante confiáveis. (Se você estivesse trabalhando em um conjunto de dados completamente diferente, essa média e variância teriam que ser calculadas, embora muitos simplesmente usem constantes ImageNet e relatem resultados aceitáveis.)



As transformações composíveis também facilitam a execução de ações como rotação e deslocamento da imagem para aumento de dados, às quais retornaremos no Capítulo 4.



Neste exemplo, estamos redimensionando as imagens para 64 x 64. Fiz essa escolha aleatória para acelerar o cálculo em nossa primeira rede. A maioria das arquiteturas existentes, que você verá no Capítulo 3, usa 224x224 ou 299x299 para suas imagens de entrada. Geralmente, quanto maior o tamanho do arquivo de entrada, mais dados a rede pode aprender. O outro lado da moeda é que você geralmente pode colocar um lote menor de imagens na memória da GPU.


Existem muitas outras informações sobre conjuntos de dados, e isso não é tudo. Mas por que deveríamos saber mais do que precisamos se já sabemos sobre o conjunto de dados de treinamento?



Validação e conjuntos de dados de referência



Nosso conjunto de dados de treinamento está configurado, mas agora precisamos repetir as mesmas etapas com o conjunto de dados de validação. Qual é a diferença aqui? Uma das armadilhas do aprendizado profundo (e, na verdade, de todo aprendizado de máquina) é o overfitting: o modelo é muito bom em reconhecer no que foi treinado, mas não funciona em exemplos que não viu. A modelo vê a foto de um gato, e se todas as outras fotos de gatos não forem muito parecidas com esta, a modelo decide que não é um gato, embora o contrário seja óbvio. Para evitar que a rede neural se comporte assim, carregamos a amostra de controle em download.py, ou seja, em uma série de imagens de gatos e peixes que não estão no conjunto de dados de treinamento. No final de cada ciclo de treinamento (também conhecido como época), comparamos esse conjunto para garantir que a rede não esteja errada. Não se assuste, o código para esta verificação é incrivelmente simples:este é o mesmo código com vários nomes de variáveis ​​alterados:



val_data_path = "./val/"
val_data = torchvision.datasets.ImageFolder(root=val_data_path,
                                                                  transform=transforms)


Apenas usamos a cadeia de transformações em vez de defini-la novamente.



Além do conjunto de dados de validação, também precisamos criar um conjunto de dados de validação. É usado para testar o modelo após a conclusão de todo o treinamento:



test_data_path = "./test/"
test_data = torchvision.datasets.ImageFolder(root=test_data_path,
                                                                   transform=transforms)


À primeira vista, os diferentes tipos de conjuntos podem ser complexos e confusos, então eu montei uma tabela para indicar qual parte do treinamento usa cada conjunto (Tabela 2.1).



imagem


Agora podemos criar carregadores de dados com mais algumas linhas de código Python:



batch_size=64
train_data_loader = data.DataLoader(train_data, batch_size=batch_size)
val_data_loader = data.DataLoader(val_data, batch_size=batch_size)
test_data_loader = data.DataLoader(test_data, batch_size=batch_size)


Novo e digno de nota neste código é o comando batch_size. Ela diz quantas imagens irão passar pela rede antes de treiná-la e atualizá-la. Em teoria, poderíamos atribuir batch_size a uma série de imagens nos conjuntos de dados de teste e treinamento para que a rede veja cada imagem antes de atualizar. Na prática, isso geralmente não é feito porque pacotes menores (mais comumente conhecidos na literatura como minipacotes) requerem menos memória e não há necessidade de armazenar todas as informações sobre cada imagem no conjunto de dados, e um tamanho de pacote menor leva a um aprendizado mais rápido desde a rede atualizações muito mais rápidas. Para carregadores de dados PyTorch, batch_size é definido como 1. Você provavelmente desejará alterá-lo. Embora eu tenha escolhido 64, você pode experimentar para entenderquantos mini-pacotes podem ser usados ​​sem esgotar a memória da GPU. Experimente alguns parâmetros adicionais: por exemplo, você pode especificar como os conjuntos de dados são buscados, se o conjunto de dados inteiro é embaralhado toda vez que é executado e quantos fluxos de trabalho estão envolvidos para recuperar dados do conjunto de dados. Tudo isso pode ser encontrado emDocumentação do PyTorch .



Trata-se de passar dados para PyTorch, então vamos imaginar uma rede neural simples que começará a classificar nossas imagens.



Finalmente, uma rede neural!



Começaremos com a rede de aprendizado profundo mais simples - uma camada de entrada que funcionará com os tensores de entrada (nossas imagens); uma camada de saída do tamanho do número de nossas classes de saída (2); e uma camada oculta no meio. No primeiro exemplo, usaremos camadas totalmente vinculadas. Na fig. 2.3 mostra uma camada de entrada de três nós, uma

camada oculta de três nós e uma saída de dois nós.



Neste exemplo, cada nó em uma camada afeta um nó na próxima camada e cada conexão tem um peso que determina a intensidade do sinal desse nó para a próxima camada. (Esses são os pesos que serão atualizados quando treinarmos a rede, geralmente a partir da inicialização aleatória.) Quando a entrada passa pela rede, nós (ou PyTorch) podemos simplesmente multiplicar em matriz os pesos e vieses dessa camada pela entrada. Antes de passá-los para a próxima função, esse resultado entra na função de ativação, que é simplesmente uma forma de introduzir a não linearidade em nosso sistema.



imagem


Funções de ativação



A função de ativação parece complicada, mas a função de ativação mais comum que você pode encontrar agora é ReLU, ou unidade linear retificada. Novamente inteligente! Mas esta é apenas uma função que implementa max (0, x), então o resultado é 0 se a entrada for negativa, ou apenas a entrada (x) se x for positivo. É simples assim!



Outra função de ativação que você provavelmente encontrará é a função logística multivariada (softmax), que é um pouco mais complexa do ponto de vista matemático. Basicamente, ele gera um conjunto de valores de 0 a 1, que somam 1 (probabilidades!), E pondera os valores de forma a aumentar a diferença, ou seja, produz um resultado em um vetor que será maior que todos os outros. Freqüentemente, você o verá usado no final de uma rede de classificação para ter certeza de que a rede fará alguma previsão sobre qual classe ela pensa ser os dados de entrada.



Agora que temos todos esses blocos de construção, podemos começar a construir nossa primeira rede neural.



Criação de rede neural



Construir uma rede neural em PyTorch é semelhante a programar em Python. Herdamos de uma classe chamada torch.nn.Network e preenchemos os métodos __init__ e forward:



class SimpleNet(nn.Module):

def __init__(self):
     super(Net, self).__init__()
     self.fc1 = nn.Linear(12288, 84)
     self.fc2 = nn.Linear(84, 50)
     self.fc3 = nn.Linear(50,2)

def forward(self):
     x = x.view(-1, 12288)
     x = F.relu(self.fc1(x))
     x = F.relu(self.fc2(x))
     x = F.softmax(self.fc3(x))
     return x
simplenet = SimpleNet()


Novamente, isso não é difícil. Fazemos as configurações necessárias em init (), neste caso chamamos o construtor da superclasse e três camadas totalmente conectadas (chamadas Linear em PyTorch, elas são chamadas Densas em Keras). O método forward () descreve como os dados são transmitidos pela rede, tanto no treinamento quanto na predição (inferência). Primeiro, temos que transformar o tensor 3D (xey mais informações de cor de 3 canais - vermelho, verde, azul) na imagem - atenção! - em um tensor unidimensional para que possa ser passado para a primeira camada Linear, e fazemos isso usando view (). Portanto, aplicamos as camadas e as funções de ativação em ordem, retornando a saída do softmax para obter a previsão para esta imagem.



Os números nas camadas ocultas são arbitrários, exceto para a saída da última camada, que é 2, que corresponde às nossas duas classes - gato ou peixe. Requer que os dados em camadas diminuam à medida que encolhem na pilha. Se a camada vai, digamos, de 50 entradas para 100 saídas, então a rede pode aprender simplesmente passando 50 conexões para cinquenta de cem saídas e considerar seu trabalho concluído. Ao reduzir o tamanho da saída em relação à entrada, estamos forçando esta parte da rede a aprender a representatividade da entrada original com menos recursos, o que presumivelmente significa que a rede define algumas características distintivas das imagens: por exemplo, ela aprendeu a reconhecer uma barbatana ou cauda.



Temos uma previsão e podemos compará-la com a rotulagem real da imagem original para ver se estava correta. Mas ele precisa de alguma forma para permitir que o PyTorch quantifique não apenas a correção ou incorreção de uma previsão, mas também o quão correta ou incorreta ela é. A função de perda faz isso.



SOBRE O AUTOR



Ian Poynter (Ian Pointer) - Engenheiro de ciência de dados, especializado em soluções para aprendizado de máquina (incluindo métodos de ensino aprofundados) para vários clientes da Fortune 100. Atualmente, Yang trabalha na Lucidworks, que está envolvida no desenvolvimento de aplicativos avançados e PNL.



»Mais detalhes sobre o livro podem ser encontrados no site da Editora

» Índice

» Trecho



Para Habitantes desconto de 25% no cupom - PyTorch No ato



do pagamento da versão em papel do livro, é enviado um e-book para o e-mail.



All Articles