Redes Neurônicas, Tipos e Programação Funcional

– http://colah.github.io/posts/2015-09-NN-Types-FP/
Postado em 3 de setembro de 2015

Um Campo Ad-Hoc

A aprendizagem profunda, apesar dos seus sucessos notáveis, é um campo jovem. Enquanto os modelos chamados de redes neurais artificiais foram estudados há décadas, grande parte desse trabalho parece apenas ligeiramente ligado aos resultados modernos.
Muitas vezes, os novos campos começam de forma muito ad hoc. Mais tarde, o campo maduro é compreendido de forma muito diferente do que foi entendido pelos primeiros praticantes. Por exemplo, na taxonomia, as pessoas agruparam plantas e animais há milhares de anos, mas a forma como entendemos o que estávamos a fazer mudou muito à luz da evolução e da biologia molecular. Na química, exploramos as reações químicas há muito tempo, mas o que nos entendemos mudou muito com a descoberta de elementos irredutíveis e, mais tarde, com modelos do átomo. Esses são exemplos grandiosos, mas a história da ciência e da matemática tem visto esse padrão uma e outra vez, em diferentes escalas.
Parece bastante provável que a aprendizagem profunda esteja neste estado ad-hoc.
Agora, a aprendizagem profunda é mantida unida por uma ferramenta extremamente bem-sucedida. Esta ferramenta não parece fundamental; é algo em que tropeçamos, com detalhes aparentemente arbitrários que mudam regularmente. Como um campo, ainda não temos uma visão unificadora ou compreensão compartilhada. Na verdade, o campo tem várias narrativas concorrentes!
Eu acho que é muito provável que, refletindo em 30 anos, veremos a aprendizagem profunda de forma muito diferente.

Aprendizagem profunda 30 anos no futuro
Se pensarmos que provavelmente veremos a aprendizagem profunda de forma muito diferente em 30 anos, isso sugere uma pergunta interessante: como vamos vê-la? Claro, ninguém pode realmente saber como entenderemos o campo. Mas é interessante especular.
Atualmente, três narrativas estão a competir para ser a forma como entendemos a aprendizagem profunda. Há a narrativa da neurociência, desenhando analogias com a biologia. Há a narrativa das representações, centrada nas transformações de dados e na múltipla hipótese. Finalmente, há uma narrativa probabilística, que interpreta as redes neurais como a descoberta de variáveis latentes. Essas narrativas não são mutuamente exclusivas, mas apresentam formas muito diferentes de pensar sobre o aprendizado profundo.
Este ensaio amplia a narrativa de representações para uma nova resposta: a aprendizagem profunda estuda uma conexão entre otimização e programação funcional.
Nesta visão, a narrativa de representações em aprendizagem profunda corresponde à teoria do tipo na programação funcional. Ela vê a aprendizagem profunda como a junção de dois campos que já sabemos que são incrivelmente ricos. O que achamos, parece tão bonito para mim, é tão natural, que o matemático em mim poderia acreditar que era algo fundamental sobre a realidade.
Esta é uma ideia extremamente especulativa. Não estou argumentando que é verdade. Desejo argumentar apenas que é plausível, que se poderia imaginar uma aprendizagem profunda evoluindo nessa direção. Para ser claro: principalmente estou a fazer um argumento estético, e não um argumento de fato. Desejo mostrar que esta é uma idéia natural e elegante, abrangendo o que chamamos de aprendizagem profunda.

Otimização e composição da função
A propriedade distintiva da aprendizagem profunda é que estuda redes neuronais profundas – redes neurais com muitas camadas. No decorrer de múltiplas camadas, esses modelos dobram progressivamente os dados, deturpando-o em uma forma onde é fácil resolver a tarefa.

Os detalhes dessas camadas mudam de vez em quando. O que permaneceu constante é que existe uma seqüência de camadas.
Cada camada é uma função, atuando na saída de uma camada anterior. Em geral, a rede é uma cadeia de funções compostas. Essa cadeia de funções compostas é otimizada para executar uma tarefa.
Todo modelo em aprendizagem profunda que eu tenho conhecimento envolve a otimização de funções compostas. Eu acredito que este é o coração do que estamos a estudar.

Representações são tipos
Com cada camada, as redes neurais transformam dados, moldando-os em uma forma que facilita a sua tarefa. Chamamos essas versões transformadas “representações” de dados.
Representações correspondem a tipos.
Nos mais cruéis, os tipos de ciência da computação são uma maneira de incorporar algum tipo de dados em n bits. Da mesma forma, as representações em aprendizagem profunda são uma maneira de incorporar um colector de dados em n dimensões.
Assim como duas funções só podem ser compostas juntas se os seus tipos concordarem, as duas camadas só podem ser compostas juntas quando as suas representações concordarem. Os dados na representação errada são absurdos a uma rede neural. Ao longo do treinamento, as camadas adjacentes negociam a representação em que se comunicarão; o desempenho da rede depende de dados estarem na representação que a rede espera.

Uma camada f1 seguida de uma camada f2. A representação de saída de f1 é a entrada de f2.
No caso de arquiteturas de rede neural muito simples, onde há apenas uma seqüência linear de camadas, isso não é muito interessante. A representação da saída de uma camada precisa corresponder à representação da entrada da próxima camada – então, o que? É um requisito trivial e chato.
Mas muitas redes neurais possuem arquiteturas mais complicadas, onde isso se torna uma restrição mais interessante. Para um exemplo muito simples, vamos imaginar uma rede neural com vários tipos de entradas similares, que executa múltiplas tarefas relacionadas. Talvez seja preciso imagens RGB e também imagens em escala de cinza. Talvez seja a olhar fotos de pessoas, e tentar prever idade e sexo. Como as semelhanças entre os tipos de insumos e entre os tipos de tarefas, pode ser útil fazer tudo isso em um modelo, de modo que os dados de treinamento os ajudem a todos. O resultado é o mapeamento de várias camadas de entrada em uma representação e o mapeamento de múltiplas saídas da mesma representação.


Talvez este exemplo pareça um pouco elaborado, mas o mapeamento de diferentes tipos de dados na mesma representação pode levar a coisas bem notáveis. Por exemplo, mapeando palavras de dois idiomas para uma representação, podemos encontrar palavras correspondentes que não sabíamos que fossem traduções quando começamos. E ao mapear imagens e palavras na mesma representação, podemos classificar imagens de classes que nunca vimos!
As representações e tipos podem ser vistos como os blocos de construção básicos para aprendizagem profunda e programação funcional, respectivamente. Uma das principais narrativas da aprendizagem profunda, a narrativa múltipla e representações, é inteiramente centrada nas redes neurais que dobram dados para novas representações. A conexão conhecida entre geometria, lógica, topologia e programação funcional sugere que as conexões entre representações e tipos podem ser de fundamental importância.
Aprendizagem profunda e programação funcional
Um dos principais pontos de vista das redes neurais modernas é a idéia de que muitas cópias de um neurônio podem ser usadas em uma rede neural.
Na programação, a abstração das funções é essencial. Em vez de escrever o mesmo código dezenas, centenas ou mesmo milhares de vezes, podemos escrevê-lo uma vez e usá-lo como precisamos. Não só isso reduz massivamente a quantidade de código que precisamos escrever e manter, acelerando o processo de desenvolvimento, mas também reduz o risco de introduzir bugs e cometeu os erros que fazemos mais fáceis de capturar.
O uso de múltiplas cópias de um neurônio em diferentes lugares é a rede neural equivalente ao uso de funções. Porque há menos para aprender, o modelo aprende mais rapidamente e aprende um modelo melhor. Esta técnica – o nome técnico para isso é “amarrar o peso” – é essencial para os resultados fenomenais que vimos recentemente através da aprendizagem profunda.
Claro, não é possível simplesmente colocar arbitrariamente cópias de neurônios em todo o lugar. Para que o modelo funcione, você precisa fazê-lo de maneira fundamentada, explorando alguma estrutura nos seus dados. Na prática, há muitos padrões que são amplamente utilizados, como camadas recorrentes e camadas convolutivas.
Esses padrões de rede neural são apenas funções de ordem superior – ou seja, funções que tomam funções como argumentos. Coisas assim foram estudadas extensivamente na programação funcional. Na verdade, muitos desses padrões de rede correspondem a funções extremamente comuns, como dobra. A única coisa incomum é que, em vez de receber funções normais como argumentos, eles recebem pedaços de rede neural2.
– Codificação de redes neurais recorrentes são apenas dobras. São freqüentemente usados para permitir que uma rede neural tome uma lista de comprimento variável como entrada, por exemplo, tomando uma frase como entrada.

Dobra = Codificação RNN
Haskell: foldl a s
– Gerar Redes Neurais Recorrentes são apenas desdobras. Costumam ser usados para permitir que uma rede neural produza uma lista de saídas, como palavras em uma frase.


Mapa de acumulação = RNN
Haskell: mapAccumR a s
– Redes Neurais Recursivas Bidirecionais são uma variante mais obscura, que eu menciono principalmente para o sabor. Em termos de programação funcional, são um mapa de acumulação esquerdo e direito compactado. São usados para fazer previsões sobre uma seqüência com contexto passado e futuro.

Mapa de acumulação de esquerda e direita zipada = RNN bidirecional
Haskell: zip (mapAccumR a s xs) (mapAccumL a` s` xs)
– As redes neurais convolutivas são um parente próximo do mapa. Um mapa normal aplica uma função a cada elemento. As redes neurais convolutivas também observam elementos vizinhos, aplicando uma função a uma pequena janela em torno de cada elemento.3

Mapa Windowed = Camada Convolucional
Haskell: zip com um xs (tail xs)
As redes nervosas convolucionais bidimensionais são particularmente notáveis. Eles estiveram por trás de sucessos recentes em visão por computador. (Mais sobre redes de conv.)

Rede de convolução bidimensional
– Redes Neurais Recursivas (“TreeNets”) são catamorfismos, uma generalização de dobras. Eles consomem uma estrutura de dados de baixo para cima. São usados principalmente para o processamento de linguagem natural, para permitir que as redes neurais operem em árvores analisadas.

Catamorfismo = TreeNet
Haskell: cata a
Os exemplos acima demonstram como os padrões comuns em redes neurais correspondem a programas funcionais muito naturais e simples.

Um novo tipo de programação
Esses padrões são blocos de construção que podem ser combinados em redes maiores. Como os blocos de construção, essas combinações são programas funcionais, com pedaços de redes neurais por todas as partes. O programa funcional fornece estrutura de alto nível, enquanto os pedaços são peças flexíveis que aprendem a executar a tarefa real dentro da estrutura fornecida pelo programa funcional.
Essas combinações de blocos de construção podem fazer realmente, coisas realmente notáveis. Gostaria de ver alguns exemplos.
– Sutskever, et al. (2014) executam a tradução de inglês para francês com o estado da arte ao combinar uma RNN de codificação e uma RNN de geração. Em termos de programação funcional, eles essencialmente se dobram sobre a frase inglesa (para trás) e então se desdobram para produzir a tradução francesa.

– Vinyals, et al. (2014) geram legendas de imagem com uma rede convolutiva e uma RNN geradora. Essencialmente, eles consomem a imagem com uma rede convolutiva e, em seguida, desdobram o vetor resultante em uma frase que a descreve.

(Vinyals, et al. (2014))

Esse tipo de modelos pode ser visto como uma espécie de programação funcional diferenciável.
Mas não é apenas uma coisa abstrata! Eles estão imbuídos com o sabor da programação funcional, mesmo que as pessoas não usem esse idioma. Quando ouço colegas falarem em um nível elevado sobre os seus modelos, tem um sentimento muito diferente do que as pessoas a falar sobre modelos mais clássicos. As pessoas falam sobre coisas de muitas maneiras diferentes, é claro – há muita variação na forma como as pessoas vêem uma aprendizagem profunda – mas muitas vezes há uma corrente secundária que parece muito semelhante às conversas de programação funcional.
Parece um novo tipo de programação, uma espécie de programação funcional diferenciável. Um escreve um programa funcional muito áspero, com essas peças flexíveis e aprendidas, e define o comportamento correto do programa com muitos dados. Em seguida, você aplica uma descida de gradiente ou algum outro algoritmo de otimização. O resultado é um programa capaz de fazer coisas notáveis que não temos ideia de como criar diretamente, como gerar legendas que descrevem imagens.
É a interseção natural da programação funcional e otimização, e acho que é lindo.

Conclusão
Eu acho essa idéia realmente bonita. Ao mesmo tempo, este é um artigo muito estranho e eu me sinto um pouco estranho postando-o. Estou a apresentar uma ideia especulativa com muito apoio, além do meu próprio entusiasmo. Honestamente, adotando a perspectiva mais objetiva que posso, espero que essa idéia seja errada, porque a maioria das idéias não testadas está errada. Mas pode estar certo, e eu acho que vale a pena falar.
Além disso, não sou a pessoa certa para explorar muitas direções que isso sugere – por exemplo, uma das coisas óbvias a fazer é analisar as redes neurais a partir de uma perspectiva da teoria do tipo homotopia, mas eu não tenho um fundo relevante. Esta é uma ideia que implora uma ampla discussão. Realmente parece que publicar é a coisa certa a fazer.
Finalmente, espero que este ensaio possa suscitar mais discussões e pensamentos sobre o que realmente é a aprendizagem profunda. Eu acho que há uma discussão importante à espera.
Além disso, qual é o ponto de escrever um blog se não posso especular? Felizmente, consegui equilibrar adequadamente a minha excitação com a minha incerteza.

Reconhecimentos
Em primeiro lugar, estou incrivelmente agradecido a Greg Corrado e Aaron Courville. Eles são os pesquisadores de aprendizagem profunda que conheço que mais empolga com essas idéias, e estou realmente agradecido pelo seu apoio intelectual.
Várias outras pessoas tiveram conversas muito amplas e úteis comigo. Sebastian Zany passou várias horas a falar sobre a teoria do tipo e as redes neurais comigo. Michael Nielsen deu um feedback minucioso sobre um rascunho deste ensaio. Chas Leichner pensou profundamente sobre essas idéias e foi muito encorajador. Um grande obrigado para os três!
Também agradeço os comentários de James Koppel, Luke Vilnis, Sam Bowman, Greg Brockman e Rob Gilson. Finalmente, falei com várias outras pessoas sobre essas idéias nos últimos meses – obrigado a todas elas!

Apêndice: Nomes funcionais de camadas comuns
Nome Profundo de Aprendizagem – Nome Funcional
Aprendizagem Vector – Constante
Incorporação de camada – Indexação de lista
Codificação RNN – Dobra
Gerando RNN – Desdobrável
General RNN – Mapa de acumulação
RNN bidirecional – Mapas de acumulação de esquerda/direita fechados
Conv Layer – “Mapa da Janela”
TreeNet – Catamorfismo
TreeNet inversa – Anamorfismo

1. Por exemplo, a camada sigmóide uma vez omnipresente foi substancialmente substituída por camadas ReLU.↩
2. Acho que é realmente surpreendente que esse tipo de modelos seja possível, e é por causa de um fato surpreendente. Muitas funções de ordem superior, com funções diferenciáveis como argumentos, produzem uma função que é diferenciável em quase todos os lugares. Além disso, dadas as derivadas das funções de argumento, você pode simplesmente usar a regra da cadeia para calcular a derivada da função resultante.
3. Esta operação também está intimamente relacionada às funções de estêncil/convolução, que são sua versão linear. Eles geralmente são implementados usando aqueles. No entanto, na pesquisa moderna da rede neural, onde “camadas de convolução MLP” estão-se a tornar mais populares, parece preferível pensar nisso como uma função arbitrária.

Sobre o autor

Sara Filipa







por: Sara Filipa

Posts recentes

Comentários

Arquivos

Categorias

Meta