PyTorch 101 para manequins como eu

Sangeet Moy Das Blocked Desbloquear Seguir Seguindo 5 de novembro de 2018

O que é o PyTorch?

É um pacote baseado em Python que serve como substituto dos arrays Numpy e fornece uma biblioteca flexível para a Plataforma de Desenvolvimento de Aprendizagem Dura. Quanto ao motivo pelo qual eu prefiro que o PyTorch sobre o TensorFLow possa ser aprendido através do post do blog do Fast AI para o motivo de mudar para o PyTorch. Ou simplesmente, a principal razão de acordo comigo é ter gráficos de computação dinâmicos , o que torna a depuração de redes neurais mais fácil para os usuários.

O que é uma matriz?

Uma matriz é uma grade de n × m (digamos, 4 × 4), onde podemos adicionar e subtrair matrizes do mesmo tamanho, multiplicar uma matriz por outra desde que os tamanhos sejam compatíveis (( n × m ) × ( m × p ) = n × p ) e multiplique uma matriz inteira por uma constante. Um vetor é uma matriz com apenas uma linha ou coluna, então há um monte de operações matemáticas que podemos fazer em qualquer matriz.

O que é um array Numpy em Python?

Numpy é a biblioteca central para computação científica em Python. Ele fornece um objeto de matriz multidimensional de alto desempenho e ferramentas para trabalhar com essas matrizes. Uma matriz numpy é uma grade de valores, todos do mesmo tipo, e é indexada por uma tupla de inteiros não negativos. O número de dimensões é a classificação da matriz; a forma de uma matriz é uma tupla de inteiros que fornece o tamanho da matriz ao longo de cada dimensão. Para saber mais sobre como funciona o Numpy, consulte este link .

Por que tensores e não numpy?

A razão pela qual usamos o Numpy no Aprendizado de Máquina é que ele é muito mais rápido que as listas do Python ao fazer operações de matriz. Por quê? Porque faz a maior parte do trabalho pesado em C. Mas, no caso de treinar redes neurais profundas, os arrays NumPy só levariam meses para treinar algumas das redes mais modernas. É aqui que entram os tensores . O PyTorch nos fornece uma estrutura de dados chamada Tensor , que é muito semelhante ao ND- array do NumPy . Mas, diferentemente do último, os tensores podem aproveitar os recursos de uma GPU para acelerar significativamente as operações de matriz.

Tensores são matrizes multidimensionais.

 tocha.Tensor (x, y) 

Isso criará um tensor X por Y dimensionado que tenha sido instanciado com valores aleatórios. Para criar um tensor de 7×5 com valores selecionados aleatoriamente de uma distribuição uniforme entre -1 e 1,

 tocha.Tensor (7, 5) .uniforme _ (- 1, 1) 

Os tensores têm um atributo de tamanho que pode ser chamado para verificar seu tamanho

 print (x.size ()) 

PyTorch suporta várias funções tensoras com diferentes sintaxes:

Considere Adição:

  • Adição Normal
 y = torch.rand (5, 3) 
imprimir (x + y)
  • Obtendo o resultado em um tensor
 resultado = tocha.Tensor (5, 3) 
torch.add (x, y, out = resultado)
  • Na linha
 y.add_ (x) 

As funções embutidas são indicadas por um sublinhado após o seu nome. Nota: Estes têm um tempo de execução mais rápido (com uma troca de complexidade de memória maior)

Todas as funções de Indexação, Transmissão e Remodelação da Numpy são suportadas

Nota: O PyTorch não suporta um salto negativo, portanto [:: – 1] resultará em erro

 print (x [:, 1]) 
 y = torch.randn (5, 10, 15) 
print (y.size ())
print (y.view (-1, 15) .size ())

Para mais detalhes sobre a diferença entre um array numpy e um Tensor, leia este post bem escrito no Medium .

O que são gráficos computacionais?

Um gráfico computacional é um gráfico direcionado, no qual os nós correspondem a operações ou variáveis . Variáveis podem alimentar seu valor em operações, e operações podem alimentar sua produção em outras operações. Desta forma, cada nó no gráfico define uma função das variáveis. O gráfico de computação é simplesmente uma estrutura de dados que permite aplicar com eficiência a regra da cadeia para calcular gradientes em todos os seus parâmetros. Suponha que seu modelo seja descrito assim:

 b = w1 * a 
c = w2 * a
d = (w3 * b) + (w4 * c)
L = f (d)

Se eu fosse realmente desenhar o gráfico de computação, provavelmente seria assim.

Agora , você deve observar que a figura acima não é inteiramente uma representação precisa de como o gráfico é representado sob o capô pelo PyTorch. No entanto, por enquanto, é o suficiente para direcionar nosso ponto de vista.

Por que devemos criar esse gráfico quando podemos executar sequencialmente as operações necessárias para calcular a saída?

Imagine o que aconteceria se você não precisasse apenas calcular a saída, mas também treinar a rede. Você terá que calcular os gradientes para todos os pesos rotulados por nós roxos. Isso exigiria que você descobrisse a regra da cadeia e atualizasse os pesos.

Aqui estão algumas coisas para notar. Primeiro, que as direções das setas estão agora invertidas no gráfico. Isso porque estamos retropropagando, e as setas marcam o fluxo de gradientes para trás.

Segundo, por causa desses exemplos, você pode pensar nos gradientes que escrevi como pesos de borda . Observe que esses gradientes não exigem que a regra da cadeia seja computada.

Agora, para calcular o gradiente de qualquer nó, digamos, L, com relação a qualquer outro nó, digamos c (dL / dc), tudo o que precisamos fazer é.

  1. Trace o caminho de L para c . Isso seria L ? d ? c.
  2. Multiplique todos os pesos da borda à medida que você percorre esse caminho. A quantidade que você acaba com é: ( dL / dd) * ( dd / dc) = (dL / dc)
  3. Se houver vários caminhos, adicione seus resultados. Por exemplo, no caso de dL / da, temos dois caminhos. L ? d ? c ? a e L ? d ? b ? a. Adicionamos suas contribuições para obter o gradiente de L wrta

[ ( dL / dd) * ( dd / dc) * ( dc / da)] + [ ( dL / dd) * ( dd / db) * ( db / da)]

Em princípio, pode-se começar em L e começar a percorrer o gráfico para trás, calculando gradientes para cada nó que aparecer ao longo do caminho.

Este vídeo do Dr. Andrew Ng. dá uma boa visão geral dos gráficos de computação.

O que são variáveis e autograd?

Autograd: diferenciação automática

O pacote de autograd fornece diferenciação automática para todas as operações em Tensores. É uma estrutura de definição por execução, o que significa que seu backprop é definido por como seu código é executado e que cada iteração pode ser diferente.

Variável

autograd.Variable é a classe central do pacote. Ele envolve um tensor e suporta quase todas as operações definidas nele. Depois de terminar seu cálculo, você pode chamar .backward() e ter todos os gradientes computados automaticamente.

Você pode acessar o tensor bruto através do atributo .data , enquanto o gradiente dessa variável é acumulado em .grad .

 x_data = [1.0, 2.0, 3.0] 
y_data = [2,0, 4,0, 6,0]

w = Variável (torch.Tensor ([1.0]), requires_grad = True )

Chamando a função Backward

 l = perda (x_val, y_val) 
l.backward ()

Você não pode acessar o atributo grad de variáveis não-folha. Sim, esse é o comportamento padrão. Você pode substituí-lo chamando. retenha_grad () na Variável logo após defini-la e então você poderá acessar seu atributo grad . Mas realmente, o que diabos está acontecendo sob os envoltórios.

Então, finalmente, por que o gráfico de computação dinâmica balança?

O PyTorch cria algo chamado Gráfico de Computação Dinâmica, o que significa que o gráfico é gerado na hora. Até que a função de redirecionamento de uma variável seja chamada, não existe nenhum nó para a variável (é grad_fn) no gráfico. O gráfico é criado como resultado da função forward de muitas Variáveis sendo invocadas. Somente então, os buffers são alocados para o gráfico e os valores intermediários (usados para calcular gradientes posteriormente). Quando você chama backward () , conforme os gradientes são calculados, esses buffers são essencialmente liberados e o gráfico é destruído. Você pode tentar chamar de volta () mais de uma vez em um gráfico, e você verá que o PyTorch vai lhe dar um erro. Isso ocorre porque o gráfico é destruído na primeira vez que backward () é chamado e, portanto, não há nenhum gráfico para chamar de volta na segunda vez.

Se você ligar para a frente novamente, um gráfico completamente novo é gerado. Com nova memória alocada para ele.

Por padrão, apenas os gradientes (atributo grad ) dos nós folha são salvos e os gradientes dos nós não-folha são destruídos. Mas esse comportamento pode ser alterado conforme descrito acima.

Isto está em contraste com os gráficos de computação estática , usados pelo TensorFlow, onde o gráfico é declarado antes de executar o programa. O paradigma de gráfico dinâmico permite que você faça alterações em sua arquitetura de rede durante o tempo de execução, pois um gráfico é criado somente quando uma parte do código é executada. Isso significa que um gráfico pode ser redefinido durante a vida útil de um programa. Isto, no entanto, não é possível com gráficos estáticos onde os gráficos são criados antes de executar o programa, e meramente executados posteriormente. Gráficos dinâmicos também facilitam a depuração, pois a origem do erro é facilmente rastreável.

Conclusão

Todo o objetivo de escrever este post de blog médio é ajudar-me a começar o desafio do Facebook PyTorch Scholarship para que eu, assim como outros colegas acadêmicos, possamos encontrar um guia para começar a desenvolver com o PyTorch. Uma vez que eu comece com o desafio eu estarei postando outra postagem no blog que essencialmente conteria minha jornada através deste lindo mundo de PyTorch.

Referências

  1. Noções básicas de PyTorch
  2. Entendendo a retropropagação
  3. Entendendo como funciona o autograd
  4. Entendendo a regra da cadeia
  5. Classes em Python Parte 1 e Parte 2
  6. Tutorial Oficial do PyTorch
  7. Recursos de gráfico dinâmico no TensorFlow

Texto original em inglês.