Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
François Chollet
direito autoral
Para informações on-line e pedidos deste e de outros livros da Manning,
visite www.manning.com . O editor oferece descontos neste livro quando solicitado em
quantidade. Para mais informações por favor entre em contato
20 Baldwin Road
Email: orders@manning.com
Muitas das designações usadas pelos fabricantes e vendedores para distinguir seus produtos são
reivindicadas como marcas registradas. Quando essas designações aparecem no livro e a
Manning Publications estava ciente de uma reivindicação de marca comercial, as designações
foram impressas em maiúsculas iniciais ou em maiúsculas.
20 Baldwin Road
ISBN 9781617294433
1 2 3 4 5 6 7 8 9 10 - EBM - 22 21 20 19 18 17
Índice Breve
direito autoral
Índice Breve
Índice
Prefácio
Agradecimentos
Sobre o autor
Sobre a capa
Capítulo 9. Conclusões
Índice
Lista de Figuras
Lista de mesas
Lista de Listagens
Prefácio
Se você pegou este livro, provavelmente está ciente do progresso extraordinário que o
aprendizado profundo representou para o campo da inteligência artificial no passado
recente. Em apenas cinco anos, passamos de reconhecimento de imagem e transcrição de fala
quase inutilizáveis a desempenho sobre-humano nessas tarefas.
As conseqüências desse progresso súbito se estendem a quase todos os setores. Mas, para
começar a implantar a tecnologia de aprendizagem profunda em todos os problemas que ela
poderia resolver, precisamos torná-la acessível ao maior número de pessoas possível, incluindo
não especialistas - pessoas que não são pesquisadores nem estudantes de pós-graduação. Para
que o aprendizado profundo atinja todo o seu potencial, precisamos democratizá-lo
radicalmente.
O livro que você está segurando é outro passo no caminho para tornar o aprendizado profundo
disponível para o maior número de pessoas possível. Keras sempre precisou de um curso
complementar para abordar simultaneamente os fundamentos da aprendizagem profunda, os
padrões de uso do Keras e as melhores práticas de aprendizado profundo. Este livro é o meu
melhor esforço para produzir esse curso. Escrevi isso com foco em tornar os conceitos por trás
do aprendizado profundo e sua implementação tão acessíveis quanto possível. Fazer isso não
exigiu que eu emburrecesse nada - acredito fortemente que não há idéias difíceis no aprendizado
profundo. Espero que você ache este livro valioso e que lhe permita começar a criar aplicativos
inteligentes e resolver os problemas que são importantes para você.
Agradecimentos
Gostaria de agradecer à comunidade de Keras por tornar este livro possível. A Keras cresceu
para ter centenas de colaboradores de código aberto e mais de 200.000 usuários. Suas
contribuições e comentários transformaram Keras no que é hoje.
Eu também gostaria de agradecer ao Google por apoiar o projeto Keras. Foi fantástico ver o
Keras adotado como API de alto nível do TensorFlow. Uma integração suave entre o Keras e o
TensorFlow beneficia muito os usuários do TensorFlow e os usuários do Keras e torna a
aprendizagem profunda acessível à maioria.
Quero agradecer às pessoas da Manning que tornaram este livro possível: a editora Marjan Bace
e todos os integrantes das equipes de produção e editoriais, incluindo Christina Taylor, Janet
Vail, Tiffany Taylor, Katie Tennant, Dottie Marsico e muitos outros que trabalharam nos
bastidores. .
Muito obrigado aos revisores técnicos chefiados por Aleksandar Dragosavljević - Diego Acuña
Rozas, Geoff Barto, David Blumenthal-Barby, Abel Brown, Clark Dorman, Clark Gaylord,
Thomas Heiman, Wilson Mar, Sumit Pal, Vladimir Pasman, Gustavo Patino, Peter Rabinovitch,
Alvin Raj, Claudio Rodriguez, Srdjan Santic, Richard Tobias, Martin Verzilli, William E.
Wheeler e Daniel Williams - e os colaboradores do fórum. Suas contribuições incluíam erros
técnicos, erros na terminologia, erros de digitação e sugestões de tópicos. Cada um passou pelo
processo de revisão e cada feedback implementado pelos tópicos do fórum moldou e moldou o
manuscrito.
No lado técnico, um agradecimento especial a Jerry Gaines, que serviu como editor técnico do
livro; e Alex Ott e Richard Tobias, que serviram como revisores técnicos do livro. Eles são os
melhores editores técnicos que eu poderia ter esperado.
Finalmente, gostaria de expressar minha gratidão à minha esposa Maria por ser extremamente
favorável ao desenvolvimento de Keras e à escrita deste livro.
Este livro oferece uma exploração prática e prática de aprendizado profundo. Evita a notação
matemática, preferindo explicar conceitos quantitativos através de trechos de código e construir
uma intuição prática sobre as idéias centrais de aprendizado de máquina e aprendizado
profundo.
Você aprenderá com mais de 30 exemplos de código que incluem comentários detalhados,
recomendações práticas e explicações simples de alto nível de tudo o que você precisa saber para
começar a usar o aprendizado profundo para resolver problemas concretos.
Depois de ler este livro, você terá uma sólida compreensão do que é o aprendizado profundo,
quando é aplicável e quais são suas limitações. Você estará familiarizado com o fluxo de trabalho
padrão para abordar e resolver problemas de aprendizado de máquina, e saberá como resolver
problemas comumente encontrados. Você poderá usar o Keras para resolver problemas reais
que vão desde a visão do computador até o processamento de linguagem natural: classificação
de imagens, previsão de timeseries, análise de sentimentos, geração de imagens e textos e muito
mais.
Este livro foi escrito para pessoas com experiência em programação em Python que desejam
iniciar aprendizado de máquina e aprendizado profundo. Mas este livro também pode ser
valioso para muitos tipos diferentes de leitores:
Se você é um cientista de dados familiarizado com o aprendizado de máquina, este livro
fornecerá uma introdução sólida e prática ao aprendizado profundo, o subcampo de
aprendizado de máquina que mais cresce e é mais significativo.
Se você é um especialista em aprendizado profundo que está procurando começar com o
framework Keras, você verá que este livro é o melhor curso de Keras disponível.
Se você é um estudante de pós-graduação que estuda aprendizagem profunda em um
ambiente formal, você verá que este livro é um complemento prático à sua educação,
ajudando você a criar intuição em torno do comportamento de redes neurais profundas
e a familiarizá-lo com as melhores práticas.
Mesmo pessoas tecnicamente conscientes que não codificam regularmente acharão este livro
útil como uma introdução aos conceitos básicos e avançados de aprendizagem profunda.
Para usar Keras, você precisará de uma proficiência em Python razoável. Além disso, a
familiaridade com a biblioteca Numpy será útil, embora não seja necessária. Você não precisa de
experiência anterior com aprendizado de máquina ou aprendizado profundo: este livro cobre
desde o início todos os fundamentos necessários. Você não precisa de um histórico de
matemática avançado - a matemática do ensino médio deve ser suficiente para seguir adiante.
ROTEIRO
Este livro está estruturado em duas partes. Se você não tem experiência anterior com
aprendizado de máquina, eu recomendo fortemente que você complete a parte 1 antes de
abordar a parte 2 . Começaremos com exemplos simples e, à medida que o livro avança, nos
aproximamos cada vez mais das técnicas mais avançadas.
O Capítulo 5 examina uma série de exemplos práticos de visão computacional, com foco
na classificação de imagens.
O Capítulo 6 oferece prática com técnicas para processamento de dados de sequência,
como texto e timeseries.
O Capítulo 7 introduz técnicas avançadas para a construção de modelos de aprendizagem
profunda de última geração.
O capítulo 8 explica modelos gerativos: modelos de aprendizagem profunda capazes de
criar imagens e texto, com resultados surpreendentemente artísticos.
O Capítulo 9 é dedicado a consolidar o que você aprendeu ao longo do livro, além de
abrir perspectivas sobre as limitações da aprendizagem profunda e explorar seu
provável futuro.
Todos os exemplos de código deste livro usam o framework de aprendizagem profunda Keras
( https://keras.io ), que é de código aberto e gratuito para download. Você precisará de acesso a
uma máquina UNIX; É possível usar o Windows também, mas não recomendo. O Apêndice
A orienta você pela configuração completa.
Também recomendo que você tenha uma GPU NVIDIA recente em sua máquina, como um
TITAN X. Isso não é necessário, mas tornará sua experiência melhor, permitindo que você
execute os exemplos de código várias vezes mais rápido. Consulte a seção 3.3 para obter mais
informações sobre a configuração de uma estação de trabalho de aprendizagem profunda.
Se você não tiver acesso a uma estação de trabalho local com uma GPU NVIDIA recente, poderá
usar um ambiente de nuvem. Em particular, você pode usar as instâncias do Google Cloud
(como uma instância n1-padrão-8 com um complemento NVIDIA Tesla K80) ou instâncias de
GPU do Amazon Web Services (AWS) (como uma instância p2.xlarge). O Apêndice B apresenta
em detalhes um possível fluxo de trabalho em nuvem que executa uma instância do AWS via
notebooks Jupyter, acessível em seu navegador.
CÓDIGO FONTE
Todos os exemplos de código neste livro estão disponíveis para download como notebooks
Jupyter no site do livro, www.manning.com/books/deep-learning-with-python , e no GitHub
em https://github.com/fchollet/deep- aprendizagem-com-python-notebooks .
FÓRUM DO LIVRO
A compra do Deep Learning with Python inclui acesso gratuito a um fórum privado da Manning
Publications, no qual você pode fazer comentários sobre o livro, fazer perguntas técnicas e
receber ajuda do autor e de outros usuários. Para acessar o fórum,
acesse https://forums.manning.com/forums/deep-learning-with-python . Você também pode aprender
mais sobre os fóruns de Manning e as regras de conduta
em https://forums.manning.com/forums/about .
François Chollet trabalha em deep learning no Google em Mountain View, CA. Ele é o criador da
biblioteca de aprendizado profundo de Keras, além de colaborador da estrutura de aprendizado
de máquina do TensorFlow. Ele também faz pesquisas de aprendizado profundo, com foco na
visão computacional e na aplicação do aprendizado de máquina ao raciocínio formal. Seus
trabalhos foram publicados nas principais conferências da área, incluindo a Conferência sobre
Visão Computacional e Reconhecimento de Padrões (CVPR), a Conferência e Workshop sobre
Sistemas de Processamento de Informações Neurais (NIPS), a Conferência Internacional sobre
Representações de Aprendizagem (ICLR) e outros. .
Sobre a capa
A figura na capa do Deep Learning with Python é legendada em “Hábito da Dama Persa em
1568”. A ilustração foi tirada de Thomas Jefferys, “ Uma Coleção dos Vestidos de Diferentes
Nações, Antigas e Modernas (quatro volumes), Londres, publicada entre 1757 e 1772. A página
de rosto declara que são gravuras de placas de cobre coloridas à mão, aumentadas com goma
arábica.
Thomas Jefferys (1719–1771) foi chamado de “Geógrafo do Rei George III”. Ele era um
cartógrafo inglês que era o principal fornecedor de mapas de sua época. Ele gravou e imprimiu
mapas para o governo e outros órgãos oficiais e produziu uma ampla gama de mapas e atlas
comerciais, especialmente da América do Norte. Seu trabalho como cartógrafo despertou o
interesse pela alfândega local das terras que ele pesquisou e mapeou, que estão brilhantemente
expostas nesta coleção. O fascínio com terras distantes e viagens a lazer eram fenômenos
relativamente novos no final do século XVIII, e coleções como essa eram populares,
introduzindo tanto o turista quanto o viajante de poltrona aos habitantes de outros países.
Numa época em que é difícil distinguir um livro de computador de outro, Manning celebra a
inventividade e a iniciativa do negócio de computadores com capas de livros baseadas na rica
diversidade da vida regional de dois séculos atrás, trazida de volta à vida pelas fotos de Jefferys.
Nos últimos anos, a inteligência artificial (IA) tem sido objeto de intensa agitação da
mídia. Aprendizado de máquina, aprendizado profundo e IA surgem em inúmeros artigos,
muitas vezes fora das publicações voltadas para a tecnologia. É prometido um futuro de chatbots
inteligentes, carros autônomos e assistentes virtuais - um futuro às vezes pintado em uma luz
sombria e outras vezes como utópico, onde os trabalhos humanos serão escassos e a maior parte
da atividade econômica será manipulada por robôs ou AI Agentes Para um praticante futuro ou
atual de aprendizado de máquina, é importante ser capaz de reconhecer o sinal no ruído, de
modo que você possa identificar desenvolvimentos que mudam o mundo a partir de
comunicados de imprensa exagerados. Nosso futuro está em jogo, e é um futuro em que você
tem um papel ativo a desempenhar: depois de ler este livro, você será um daqueles que
desenvolvem os agentes de IA. Então, vamos abordar estas questões: O que o aprendizado
profundo alcançou até agora? Quão significativo é isso? Para onde estamos indo em
seguida? Você deve acreditar no hype?
Primeiro, precisamos definir claramente o que estamos falando quando mencionamos a IA. O
que são inteligência artificial, aprendizado de máquina e aprendizado profundo (veja a figura
1.1 )? Como eles se relacionam?
Embora a IA simbólica tenha se mostrado adequada para resolver problemas lógicos bem
definidos, como jogar xadrez, tornou-se intratável descobrir regras explícitas para resolver
problemas mais complexos e difusos, como classificação de imagem, reconhecimento de fala e
tradução de linguagem. Uma nova abordagem surgiu para tomar o lugar da IA
simbólica: aprendizado de máquina .
Esta observação foi mais tarde citada pelo pioneiro da IA Alan Turing como “objeção de Lady
Lovelace” em seu importante trabalho de 1950, “Computing Machinery and Intelligence” [ 1 ], que
introduziu o teste de Turing e os principais conceitos que viriam a formar a IA. Turing estava
citando Ada Lovelace enquanto ponderava se os computadores de uso geral poderiam ser
capazes de aprender e originalidade, e chegou à conclusão de que eles poderiam.
AM Turing, "Computing Machinery and Intelligence", mente 59, não. 236 (1950): 433-460.
Esta questão abre a porta para um novo paradigma de programação. Na programação clássica, o
paradigma da IA simbólica, as regras de entrada de seres humanos (um programa) e os dados a
serem processados de acordo com essas regras, e as respostas de saída (ver figura 1.2 ). Com o
aprendizado de máquina, os dados de entrada dos seres humanos, bem como as respostas
esperadas dos dados, e fora as regras. Essas regras podem ser aplicadas a novos dados para
produzir respostas originais.
Figura 1.2 Aprendizado de máquina: um novo paradigma de programação
Embora o aprendizado de máquina tenha começado a florescer apenas nos anos 90, ele se
tornou rapidamente o subcampo de IA mais popular e mais bem-sucedido, uma tendência
impulsionada pela disponibilidade de hardware mais rápido e conjuntos de dados maiores. O
aprendizado de máquina está estreitamente relacionado à estatística matemática, mas difere das
estatísticas de várias maneiras importantes. Ao contrário das estatísticas, o aprendizado de
máquina tende a lidar com conjuntos de dados grandes e complexos (como um conjunto de
dados de milhões de imagens, cada um consistindo em dezenas de milhares de pixels) para os
quais análises estatísticas clássicas, como análises Bayesianas, seriam impraticáveis. Como
resultado, o aprendizado de máquina, e especialmente o aprendizado profundo, exibe
relativamente pouca teoria matemática - talvez muito pouco - e é orientado para a engenharia.
Vamos tornar isso concreto. Considere um eixo x, um eixo y e alguns pontos representados por
suas coordenadas no sistema (x, y), como mostrado na figura 1.3 .
Como você pode ver, temos alguns pontos brancos e alguns pontos pretos. Digamos que
queremos desenvolver um algoritmo que possa pegar as coordenadas (x, y) de um ponto e
produzir se o ponto provavelmente é preto ou branco. Nesse caso,
O que precisamos aqui é de uma nova representação de nossos dados que separa de maneira
clara os pontos brancos dos pontos pretos. Uma transformação que poderíamos usar, entre
muitas outras possibilidades, seria uma mudança coordenada, ilustrada na figura 1.4 .
Neste novo sistema de coordenadas, pode-se dizer que as coordenadas de nossos pontos são
uma nova representação de nossos dados. E é bom! Com essa representação, o problema de
classificação preto / branco pode ser expresso como uma regra simples: “Pontos pretos são tais
que x> 0” ou “Pontos brancos são tais que x <0.” Essa nova representação basicamente resolve o
problema de classificação.
Neste caso, definimos a mudança de coordenadas manualmente. Mas se, em vez disso,
tentássemos procurar sistematicamente por diferentes possíveis mudanças de coordenadas e
usássemos como feedback a porcentagem de pontos sendo classificados corretamente, então
estaríamos fazendo aprendizado de máquina. Aprendizagem , no contexto da aprendizagem de
máquina, descreve um processo de pesquisa automática para melhores representações.
Então, é isso que o aprendizado de máquina é tecnicamente: procurar por representações úteis
de alguns dados de entrada, dentro de um espaço predefinido de possibilidades, usando a
orientação de um sinal de feedback. Essa ideia simples permite resolver uma gama
extremamente ampla de tarefas intelectuais, do reconhecimento de fala à condução autônoma
de automóveis.
Agora que você entende o que entendemos por aprendizado , vamos dar uma olhada no que
torna o aprendizado profundo(Deep Learning) especial.
No aprendizado profundo, essas representações em camadas são (quase sempre) aprendidas por
meio de modelos denominados redes neurais , estruturados em camadas literais empilhadas
umas sobre as outras. O termo rede neural é uma referência à neurobiologia, mas, embora
alguns dos conceitos centrais da aprendizagem profunda tenham sido desenvolvidos, em parte,
pela inspiração de nossa compreensão do cérebro, os modelos de aprendizagem
profunda não sãomodelos do cérebro. Não há evidências de que o cérebro implemente algo
como os mecanismos de aprendizagem usados em modelos modernos de aprendizagem
profunda. Você pode se deparar com artigos de ciência pop proclamando que o aprendizado
profundo funciona como o cérebro ou foi modelado a partir do cérebro, mas esse não é o
caso. Seria confuso e contraproducente para os recém-chegados ao campo pensar na
aprendizagem profunda como sendo de alguma forma relacionada à neurobiologia; você não
precisa dessa mortalha da mística e do mistério de “como nossa mente”, e você pode esquecer
qualquer coisa que tenha lido sobre ligações hipotéticas entre a aprendizagem profunda e a
biologia. Para nossos propósitos, a aprendizagem profunda é uma estrutura matemática para
aprender representações a partir de dados.
Como você pode ver na figura 1.6 , a rede transforma a imagem do dígito em representações cada
vez mais diferentes da imagem original e cada vez mais informativa sobre o resultado final. Você
pode pensar em uma rede profunda como uma operação de destilação de informações em vários
estágios, em que as informações passam por filtros sucessivos e são cada vez
mais purificadas (ou seja, úteis em relação a algumas tarefas).
Figura 1.6 Representações profundas aprendidas por um modelo de classificação por dígitos
Para controlar algo, primeiro você precisa ser capaz de observá-lo. Para controlar a saída de uma
rede neural, você precisa ser capaz de medir até que ponto essa saída é do que você
esperava. Esse é o trabalho da função de perda da rede, também chamada de função objetivo . A
função loss leva as previsões da rede e do verdadeiro alvo (o que você queria que a rede produza)
e calcula uma pontuação de distância, capturando o quão bem a rede tem feito neste exemplo
específico (veja a figura 1.8 ).
Figura 1.9. A pontuação da perda é usada como um sinal de feedback para ajustar os pesos.
Inicialmente, os pesos da rede recebem valores aleatórios, portanto, a rede implementa apenas
uma série de transformações aleatórias. Naturalmente, sua saída está longe do ideal, e a
pontuação da perda é, portanto, muito alta. Mas com todos os exemplos dos processos de rede,
os pesos são ajustados um pouco na direção correta e a pontuação da perda diminui. Este é
o loop de treinamento , que, repetido um número suficiente de vezes (normalmente dezenas de
iterações em milhares de exemplos), produz valores de peso que minimizam a função de
perda. Uma rede com uma perda mínima é aquela em que as saídas são tão próximas quanto
possível dos alvos: uma rede treinada. Mais uma vez, é um mecanismo simples que, uma vez
escalado, acaba parecendo mágico.
Ainda estamos explorando toda a extensão do que o aprendizado profundo pode fazer. Nós
começamos a aplicá-lo a uma ampla variedade de problemas, fora da percepção da máquina e
do entendimento da linguagem natural, como o raciocínio formal. Se for bem sucedido, isso
pode anunciar uma era em que o aprendizado profundo ajuda os seres humanos na ciência, no
desenvolvimento de software e muito mais.
Isso aconteceu antes. Duas vezes no passado, a IA passou por um ciclo de intenso otimismo,
seguido por desapontamento e ceticismo, com uma carência de financiamento como
resultado. Começou com a IA simbólica nos anos 60. Naqueles primeiros dias, projeções sobre
IA estavam voando alto. Um dos pioneiros e proponentes mais conhecidos da abordagem
simbólica da IA foi Marvin Minsky, que afirmou em 1967: “Dentro de uma geração ... o
problema de criar 'inteligência artificial' será substancialmente resolvido.” Três anos depois, em
1970 , ele fez uma predição mais precisamente quantificada: “De três a oito anos teremos uma
máquina com a inteligência geral de um ser humano médio.” Em 2016, Essa conquista ainda
parece estar longe no futuro - até agora, não temos como prever quanto tempo levará -, mas nos
anos 60 e início dos anos 70, vários especialistas acreditavam que ela estava bem próxima
(assim como muitas pessoas hoje). Alguns anos depois, quando essas altas expectativas não se
concretizaram, pesquisadores e fundos do governo se afastaram do campo, marcando o início da
primeiraAI inverno (uma referência a um inverno nuclear, porque isso foi logo após o auge da
Guerra Fria).
Não seria o último. Na década de 1980, uma nova abordagem da inteligência artificial
simbólica, sistemas especialistas , começou a ganhar força entre as grandes empresas. Algumas
histórias de sucesso iniciais desencadearam uma onda de investimentos, com corporações em
todo o mundo iniciando seus próprios departamentos internos de IA para desenvolver sistemas
especialistas. Por volta de 1985, as empresas gastavam mais de US $ 1 bilhão por ano com a
tecnologia; mas no início da década de 1990, esses sistemas mostraram-se caros para serem
mantidos, difíceis de escalar e limitados em escopo, e os juros diminuíram. Assim começou o
segundo inverno da IA.
1.1.8. A promessa da IA
Embora possamos ter expectativas irrealistas de curto prazo para a IA, a imagem de longo prazo
está brilhando. Estamos apenas começando a aplicar a aprendizagem profunda a muitos
problemas importantes para os quais ela pode se mostrar transformadora, desde diagnósticos
médicos a assistentes digitais. A pesquisa da IA avançou surpreendentemente rapidamente nos
últimos cinco anos, em grande parte devido a um nível de financiamento nunca antes visto na
curta história da IA, mas até agora relativamente pouco desse progresso entrou nos produtos e
processos que formam o nosso mundo. A maioria das descobertas de pesquisa de aprendizagem
profunda ainda não foi aplicada, ou pelo menos não aplicada a toda a gama de problemas que
podem resolver em todas as indústrias. Seu médico ainda não usa AI, e nem o seu
contador. Você provavelmente não usa tecnologias de IA no seu dia-a-dia. É claro, você pode
fazer perguntas simples ao smartphone e obter respostas razoáveis, obter recomendações de
produtos bastante úteis na Amazon.com e pesquisar "aniversário" no Google Fotos e encontrar
instantaneamente essas fotos da festa de aniversário da sua filha mês. Isso está muito longe de
onde tais tecnologias costumavam ficar. Mas essas ferramentas ainda são apenas acessórios
para nossas vidas diárias. A AI ainda está em transição para ser fundamental na maneira como
trabalhamos, pensamos e vivemos. e você pode pesquisar por "aniversário" no Google Fotos e
encontrar instantaneamente as fotos da festa de aniversário da sua filha no mês passado. Isso
está muito longe de onde tais tecnologias costumavam ficar. Mas essas ferramentas ainda são
apenas acessórios para nossas vidas diárias. A AI ainda está em transição para ser fundamental
na maneira como trabalhamos, pensamos e vivemos. e você pode pesquisar por "aniversário" no
Google Fotos e encontrar instantaneamente as fotos da festa de aniversário da sua filha no mês
passado. Isso está muito longe de onde tais tecnologias costumavam ficar. Mas essas
ferramentas ainda são apenas acessórios para nossas vidas diárias. A AI ainda está em transição
para ser fundamental na maneira como trabalhamos, pensamos e vivemos.
Neste momento, pode parecer difícil acreditar que a IA possa ter um grande impacto no nosso
mundo, porque ainda não está amplamente implementada - por exemplo, em 1995, teria sido
difícil acreditar no impacto futuro da Internet. . Naquela época, a maioria das pessoas não via
como a internet era relevante para elas e como isso mudaria suas vidas. O mesmo é verdade
para a aprendizagem profunda e a IA hoje. Mas não se engane: a IA está chegando. Em um
futuro não tão distante, a IA será sua assistente, até mesmo sua amiga; Ele responderá suas
perguntas, ajudará a educar seus filhos e cuidará de sua saúde. Ele entregará seus mantimentos
à sua porta e levará você do ponto A ao ponto B. Será sua interface para um mundo cada vez
mais complexo e com muita informação. E ainda mais importante
Não acredite no hype de curto prazo, mas acredite na visão de longo prazo. Pode demorar um
pouco para que a IA seja implantada em seu verdadeiro potencial - um potencial cuja extensão
total ninguém ainda ousou sonhar -, mas a IA está chegando e transformará nosso mundo de
uma forma fantástica.
Uma discussão detalhada das abordagens clássicas de aprendizado de máquina está fora do
escopo deste livro, mas vamos examiná-las brevemente e descrever o contexto histórico no qual
elas foram desenvolvidas. Isso nos permitirá colocar a aprendizagem profunda no contexto mais
amplo da aprendizagem de máquina e entender melhor de onde vem a aprendizagem profunda e
por que ela é importante.
1.2.1. Modelagem probabilística
A modelagem probabilística é a aplicação dos princípios da estatística à análise de dados. Foi
uma das primeiras formas de aprendizado de máquina, e ainda é amplamente usada até
hoje. Um dos algoritmos mais conhecidos nesta categoria é o algoritmo Naive Bayes.
Um modelo estreitamente relacionado é a regressão logística (logreg for short), que às vezes é
considerada o “hello world” da moderna aprendizagem de máquina. Não se deixe enganar pelo
nome - logreg é um algoritmo de classificação, e não um algoritmo de regressão. Muito parecido
com Naive Bayes, logreg antecede a computação por um longo tempo, mas ainda é útil até hoje,
graças à sua natureza simples e versátil. Geralmente, é a primeira coisa que um cientista de
dados tentará em um conjunto de dados para ter uma ideia da tarefa de classificação em
questão.
A primeira aplicação prática bem-sucedida de redes neurais veio em 1989 da Bell Labs, quando
Yann LeCun combinou as idéias anteriores de redes neurais convolucionais e retropropagação, e
aplicou-as ao problema de classificar dígitos manuscritos. A rede resultante, apelidada
de LeNet , foi usada pelo Serviço Postal dos Estados Unidos na década de 1990 para automatizar
a leitura de códigos postais em envelopes de correio.
Vladimir Vapnik e Corinna Cortes, “Redes de Suporte-Vetor”, Machine Learning 20, no. 3 (1995): 273-297.
3
Vladimir Vapnik e Alexey Chervonenkis, “Uma Nota sobre Uma Classe de Perceptrons”, Automação e Controle Remoto 25 (1964).
SVMs visam resolver problemas de classificação, encontrando bons limites de decisão (ver figura
1.10 ) entre dois conjuntos de pontos pertencentes a duas categorias diferentes. Um limite de
decisão pode ser considerado uma linha ou superfície separando seus dados de treinamento em
dois espaços correspondentes a duas categorias. Para classificar novos pontos de dados, você só
precisa verificar de que lado do limite de decisão eles se encaixam.
1. Os dados são mapeados para uma nova representação de alta dimensão onde o limite de
decisão pode ser expresso como um hiperplano (se os dados forem bidimensionais,
como na figura 1.10 , um hiperplano seria uma linha reta).
2. Um bom limite de decisão (um hiperplano de separação) é calculado tentando
maximizar a distância entre o hiperplano e os pontos de dados mais próximos de cada
classe, uma etapa chamada maximizar a margem . Isso permite que o limite seja
generalizado para novas amostras fora do conjunto de dados de treinamento.
Mas os SVMs mostraram-se difíceis de escalar para grandes conjuntos de dados e não
forneceram bons resultados para problemas perceptivos, como a classificação de imagens. Como
um SVM é um método superficial, a aplicação de um SVM a problemas de percepção requer
primeiro a extração de representações úteis manualmente (uma etapa chamada engenharia de
recursos ), que é difícil e frágil.
1.2.4. Árvores de decisão, florestas aleatórias e máquinas de aumento
de gradiente
As árvores de decisão são estruturas semelhantes a fluxogramas que permitem classificar
pontos de dados de entrada ou prever valores de saída de acordo com entradas (consulte a figura
1.11 ). Eles são fáceis de visualizar e interpretar. As árvores de decisões aprendidas a partir de
dados começaram a receber interesse de pesquisa significativo nos anos 2000 e, em 2010, elas
eram frequentemente preferidas aos métodos de kernel.
Figura 1.11. Uma árvore de decisão: os parâmetros aprendidos são as questões sobre os dados. Uma pergunta poderia ser,
por exemplo, "O coeficiente 2 nos dados é maior que 3,5?"
Desde 2012, redes neurais convolucionais profundas ( convnets) tornou-se o algoritmo para
todas as tarefas de visão computacional; mais geralmente, eles trabalham em todas as tarefas
perceptivas. Nas principais conferências sobre visão computacional em 2015 e 2016, foi quase
impossível encontrar apresentações que não envolvessem as connas de alguma forma. Ao
mesmo tempo, o aprendizado profundo também encontrou aplicativos em muitos outros tipos
de problemas, como o processamento de linguagem natural. Ele substituiu completamente as
SVMs e as árvores de decisão em uma ampla gama de aplicações. Por exemplo, durante vários
anos, a Organização Europeia para Pesquisa Nuclear, CERN, usou métodos baseados em árvores
de decisão para análise de dados de partículas do detector ATLAS no Large Hadron Collider
(LHC);
Você pode perguntar: se o ponto crucial da questão é ter várias camadas sucessivas de
representações, os métodos superficiais poderiam ser aplicados repetidamente para emular os
efeitos da aprendizagem profunda? Na prática, há retornos que diminuem rapidamente para
aplicações sucessivas de métodos de aprendizado superficial, porque a primeira camada de
representação ideal em um modelo de três camadas não é a primeira camada ideal em um
modelo de camada única ou de camada dupla . O que é transformador na aprendizagem
profunda é que ela permite que um modelo aprenda todas as camadas de
representação conjuntamente , ao mesmo tempo, em vez de em sucessão ( avidamentecomo é
chamado). Com o recurso de aprendizagem conjunta, sempre que o modelo ajusta um de seus
recursos internos, todos os outros recursos que dependem dele se adaptam automaticamente à
mudança, sem a necessidade de intervenção humana. Tudo é supervisionado por um único sinal
de feedback: cada mudança no modelo atende ao objetivo final. Isso é muito mais poderoso do
que empilhar avidamente modelos superficiais, porque permite que representações abstratas e
complexas sejam aprendidas, dividindo-as em longas séries de espaços intermediários
(camadas); Cada espaço é apenas uma simples transformação da anterior.
Essas são as duas características essenciais de como a aprendizagem profunda aprende com os
dados: o modo incremental, camada por camada, no qual as representações cada vez mais
complexas são desenvolvidas , e o fato dessas representações incrementais intermediárias
serem aprendidas em conjunto , sendo cada camada atualizada para seguir ambos as
necessidades representacionais da camada acima e as necessidades da camada abaixo. Juntas,
essas duas propriedades tornaram o aprendizado profundo muito mais bem-sucedido do que as
abordagens anteriores de aprendizado de máquina.
Em 2016 e 2017, a Kaggle foi dominada por duas abordagens: máquinas de aumento de
gradiente e aprendizado profundo. Especificamente, o aumento de gradiente é usado para
problemas em que dados estruturados estão disponíveis, enquanto a aprendizagem profunda é
usada para problemas de percepção, como a classificação de imagens. Praticantes do primeiro
quase sempre usam a excelente biblioteca XGBoost, que oferece suporte para os dois idiomas
mais populares da ciência de dados: Python e R. Enquanto isso, a maioria dos participantes do
Kaggle usando deep learning usam a biblioteca Keras, devido à sua facilidade de uso. ,
flexibilidade e suporte do Python.
Essas são as duas técnicas com as quais você deve estar mais familiarizado para ter sucesso no
aprendizado de máquina aplicado hoje: máquinas de aumento de gradiente, para problemas de
aprendizado superficial; e aprendizado profundo, para problemas perceptivos. Em termos
técnicos, isso significa que você precisa estar familiarizado com XGBoost e Keras - as duas
bibliotecas que atualmente dominam as competições de Kaggle. Com este livro na mão, você já
está um grande passo mais perto.
As duas principais idéias de aprendizagem profunda para visão computacional - redes neurais
convolucionais e retropropagação - já eram bem compreendidas em 1989. O algoritmo LSTM
(Long Short-Term Memory), fundamental para a aprendizagem profunda de timeseries, foi
desenvolvido em 1997 e mal mudou desde então. Então, por que o aprendizado profundo só
decolou depois de 2012? O que mudou nessas duas décadas?
Hardware
Conjuntos de dados e benchmarks
Avanços algorítmicos
Como o campo é guiado por descobertas experimentais e não por teoria, avanços algorítmicos só
se tornam possíveis quando dados e hardware apropriados estão disponíveis para experimentar
novas ideias (ou escalar idéias antigas, como é frequentemente o caso). Aprendizado de
máquina não é matemática ou física, onde grandes avanços podem ser feitos com uma caneta e
um pedaço de papel. É uma ciência de engenharia.
Os gargalos reais durante os anos 1990 e 2000 foram dados e hardware. Mas eis o que
aconteceu durante esse tempo: a internet decolou e os chips gráficos de alto desempenho foram
desenvolvidos para as necessidades do mercado de jogos.
1.3.1. Hardware
Entre 1990 e 2010, os CPUs prontos para uso ficaram mais rápidos por um fator de
aproximadamente 5.000. Como resultado, hoje em dia é possível executar pequenos modelos de
aprendizagem profunda em seu laptop, enquanto isso seria intratável há 25 anos.
Veja “Redes Neurais Convolucionais Flexíveis e de Alto Desempenho para Classificação de Imagens”, Anais da 22ª Conferência
Veja “Classificação ImageNet com Redes Neurais Convolucionais Profundas”, Avanços em Sistemas de Processamento de Informação
O que aconteceu é que o mercado de jogos subsidiou a supercomputação para a próxima geração
de aplicativos de inteligência artificial. Às vezes, grandes coisas começam como jogos. Hoje, a
NVIDIA TITAN X, uma GPU de jogos que custou US $ 1.000 no final de 2015, pode fornecer um
pico de 6,6 TFLOPS em precisão única: 6,6 trilhõesfloat32operações por segundo. Isso é cerca
de 350 vezes mais do que você pode sair de um laptop moderno. Em um TITAN X, leva apenas
alguns dias para treinar um modelo ImageNet do tipo que teria vencido a competição ILSVRC
há alguns anos. Enquanto isso, grandes empresas treinam modelos de aprendizagem profunda
em grupos de centenas de GPUs de um tipo desenvolvido especificamente para as necessidades
de aprendizado profundo, como o NVIDIA Tesla K80. O grande poder computacional desses
clusters é algo que nunca teria sido possível sem as GPUs modernas.
Além do mais, a indústria de aprendizagem profunda está começando a ir além das GPUs e está
investindo em chips cada vez mais especializados e eficientes para o aprendizado profundo. Em
2016, em sua convenção anual de E / S, o Google revelou seu projeto de unidade de
processamento de tensor (TPU): um novo design de chip desenvolvido para operar redes
neurais profundas, que é 10 vezes mais rápido e muito mais eficiente em termos energéticos que
GPUs de última geração.
1.3.2. Dados
A IA às vezes é anunciada como a nova revolução industrial. Se a aprendizagem profunda é a
máquina a vapor dessa revolução, então os dados são o seu carvão: a matéria-prima que
alimenta nossas máquinas inteligentes, sem a qual nada seria possível. Quando se trata de
dados, além do progresso exponencial no hardware de armazenamento nos últimos 20 anos
(seguindo a lei de Moore), o jogo foi a ascensão da Internet, tornando viável a coleta e
distribuição de grandes conjuntos de dados para aprendizado de máquina. . Hoje, grandes
empresas trabalham com conjuntos de dados de imagens, conjuntos de dados de vídeo e
conjuntos de dados em linguagem natural que não poderiam ter sido coletados sem a
Internet. As tags de imagem geradas pelo usuário no Flickr, por exemplo, têm sido um tesouro
de dados para a visão computacional. Então, são vídeos do YouTube.
Como Kaggle vem demonstrando desde 2010, as competições públicas são uma excelente
maneira de motivar os pesquisadores e engenheiros a se esforçarem. Ter benchmarks comuns
que os pesquisadores competem para vencer ajudou muito o recente aumento do aprendizado
profundo.
1.3.3. Algoritmos
Além de hardware e dados, até o final dos anos 2000, estávamos perdendo uma maneira
confiável de treinar redes neurais muito profundas. Como resultado, as redes neurais ainda
eram bastante rasas,usando apenas uma ou duas camadas de representações; assim, eles não
foram capazes de brilhar contra métodos rasos mais refinados, como SVMs e florestas
aleatórias. A questão principal era a propagação de gradientes através de pilhas profundas de
camadas. O sinal de feedback usado para treinar redes neurais desapareceria à medida que o
número de camadas aumentasse.
Isso mudou por volta de 2009–2010 com o advento de várias melhorias algorítmicas simples,
mas importantes, que permitiram uma melhor propagação de gradiente:
Por fim, em 2014, 2015 e 2016, formas ainda mais avançadas de ajudar na propagação de
gradientes foram descobertas, como a normalização de lotes, conexões residuais e convoluções
separadas por profundidade. Hoje podemos treinar a partir de modelos que são milhares de
camadas de profundidade.
Em 2011, pouco antes de a aprendizagem profunda ter sido o centro das atenções, o
investimento total em capital de risco em IA foi de cerca de US $ 19 milhões, que foi quase
inteiramente para aplicações práticas de abordagens de aprendizado de máquina rasa. Em 2014,
havia chegado a impressionantes US $ 394 milhões. Dezenas de startups lançadas nesses três
anos, tentando capitalizar o hype de aprendizado profundo. Enquanto isso, grandes empresas de
tecnologia como Google, Facebook, Baidu e Microsoft investiram em departamentos de pesquisa
interna em quantias que provavelmente superariam o fluxo de dinheiro de capital de
risco. Apenas alguns números surgiram: em 2013, o Google adquiriu a DeepMind, startup de
aprendizado profundo, por US $ 500 milhões, a maior aquisição de uma empresa de IA da
história. Em 2014, O Baidu iniciou um centro de pesquisa de aprendizagem profunda no Vale do
Silício, investindo US $ 300 milhões no projeto. A startup de hardware de aprendizagem
profunda Nervana Systems foi adquirida pela Intel em 2016 por mais de US $ 400 milhões.
O aprendizado profundo tem várias propriedades que justificam seu status como uma revolução
da IA, e está aqui para ficar. Podemos não estar usando redes neurais daqui a duas décadas, mas
o que usarmos herdará diretamente da aprendizagem profunda moderna e de seus principais
conceitos. Essas propriedades importantes podem ser classificadas em três categorias:
O aprendizado profundo só está no centro das atenções há alguns anos, e ainda não
estabelecemos o escopo completo do que ele pode fazer. A cada mês que passa, aprendemos
sobre novos casos de uso e melhorias de engenharia que levantam limitações
anteriores. Seguindo uma revolução científica, o progresso geralmente segue uma curva
sigmóide: começa com um período de progresso rápido, que gradualmente se estabiliza à
medida que os pesquisadores atingem limitações difíceis, e então outras melhorias se tornam
incrementais. A aprendizagem profunda em 2017 parece estar na primeira metade desse
sigmóide, com muito mais progresso nos próximos anos.
Para adicionar algum contexto para tensores e gradiente de descida, vamos começar o capítulo
com um exemplo prático de uma rede neural. Então, examinaremos cada novo conceito que foi
introduzido, ponto por ponto. Tenha em mente que esses conceitos serão essenciais para você
entender os exemplos práticos que virão nos capítulos seguintes!
Depois de ler este capítulo, você terá uma compreensão intuitiva de como as redes neurais
funcionam e poderá seguir para os aplicativos práticos - que serão iniciados no capítulo 3 .
Vejamos um exemplo concreto de uma rede neural que usa a biblioteca Python Keras para
aprender a classificar dígitos manuscritos. A menos que você já tenha experiência com Keras ou
bibliotecas similares, você não entenderá tudo sobre esse primeiro exemplo
imediatamente. Você provavelmente ainda nem instalou o Keras; isso é bom. No próximo
capítulo, vamos revisar cada elemento no exemplo e explicá-los em detalhes. Então não se
preocupe se alguns passos parecerem arbitrários ou parecerem mágicos para você! Temos que
começar de algum lugar.
O problema que estamos tentando resolver aqui é classificar imagens em escala de cinza de
dígitos manuscritos (28 × 28 pixels) em suas 10 categorias (0 a 9). Usaremos o conjunto de
dados MNIST, um clássico da comunidade de aprendizado de máquina, que tem estado em
torno de quase o mesmo tempo que o próprio campo e foi intensamente estudado. É um
conjunto de 60.000 imagens de treinamento, além de 10.000 imagens de teste, montadas pelo
Instituto Nacional de Padrões e Tecnologia (o NIST no MNIST) na década de 1980. Você pode
pensar em “resolver” o MNIST como o “Hello World” de aprendizado profundo - é o que você faz
para verificar se seus algoritmos estão funcionando como esperado. À medida que você se tornar
um praticante de aprendizado de máquina, verá o MNIST aparecer repetidas vezes, em artigos
científicos, publicações em blogs e assim por diante. Você pode ver algumas amostras MNIST
emfigura 2.1 .
Você não precisa tentar reproduzir este exemplo em sua máquina agora. Se você desejar,
primeiro precisará configurar o Keras, que é abordado na seção 3.3 .
>>> train_images.shape
60000
>>> train_labels
>>> test_images.shape
10000
>>> test_labels
O fluxo de trabalho será o seguinte: Primeiro, alimentaremos a rede neural com os dados de
treinamento train_imagese train_labels. A rede aprenderá então a associar imagens e
rótulos. Por fim, solicitaremos que a rede produza previsões test_imagese verificaremos se
essas previsões correspondem aos rótulos test_labels.
Vamos construir a rede - mais uma vez, lembre-se de que você não deve entender tudo sobre
esse exemplo ainda.
rede = models.Sequential ()
Aqui, nossa rede consiste em uma sequência de duas Densecamadas, que são camadas
neurais densamente conectadas (também chamadas totalmente conectadas ). A segunda (e
última) camada é uma camada softmax de 10 vias , o que significa que ela retornará uma matriz
de 10 pontuações de probabilidade (somando 1). Cada pontuação será a probabilidade de a
imagem do dígito atual pertencer a uma das nossas classes de 10 dígitos.
Para tornar a rede pronta para o treinamento, precisamos escolher mais três coisas, como parte
da etapa de compilação :
Uma função de perda - como a rede poderá medir seu desempenho nos dados de
treinamento e, assim, como será capaz de se orientar na direção certa.
Um otimizador - O mecanismo pelo qual a rede se atualizará com base nos dados que
ela vê e na sua função de perda.
Métricas a serem monitoradas durante o treinamento e os testes - Aqui, nos
preocuparemos apenas com a precisão (a fração das imagens que foram classificadas
corretamente).
O objetivo exato da função de perda e o otimizador serão esclarecidos nos próximos dois
capítulos.
perda = 'categorical_crossentropy',
métricas = ['precisão']]
Agora estamos prontos para treinar a rede, que em Keras é feita por meio de uma chamada
ao fitmétodo da rede - ajustamos o modelo aos dados de treinamento:
Época 1/5
Duas quantidades são exibidas durante o treinamento: a perda da rede sobre os dados de
treinamento e a precisão da rede sobre os dados de treinamento.
Nós alcançamos rapidamente uma precisão de 0,989 (98,9%) nos dados de treinamento. Agora
vamos verificar se o modelo também funciona bem no conjunto de testes:
test_acc: 0,9785
Isso conclui nosso primeiro exemplo - você acabou de ver como você pode construir e treinar
uma rede neural para classificar dígitos manuscritos em menos de 20 linhas de código
Python. No próximo capítulo, vou entrar em detalhes sobre cada peça em movimento que
acabamos de visualizar e esclarecer o que está acontecendo nos bastidores. Você aprenderá
sobre tensores, os objetos de armazenamento de dados indo para a rede; operações de tensor, de
que camadas são feitas; e gradiente descendente, que permite que sua rede aprenda com seus
exemplos de treinamento.
Em seu núcleo, um tensor é um recipiente para dados - quase sempre dados numéricos. Então, é
um contêiner para números. Você pode já estar familiarizado com matrizes, que são tensores
2D: tensores são uma generalização de matrizes para um número arbitrário de dimensões
(observe que, no contexto de tensores, uma dimensão é freqüentemente chamada de eixo ).
>>> x
matriz (12)
>>> x.ndim
>>> x
>>> x.ndim
Este vetor tem cinco entradas e, portanto, é chamado de vetor de 5 dimensões . Não confunda
um vetor 5D com um tensor 5D! Um vetor 5D possui apenas um eixo e possui cinco dimensões
ao longo de seu eixo, enquanto um tensor 5D possui cinco eixos (e pode ter qualquer número de
dimensões ao longo de cada eixo). A dimensionalidade pode denotar o número de entradas ao
longo de um eixo específico (como no caso do nosso vetor 5D) ou o número de eixos em um
tensor (como um tensor 5D), que pode ser confuso às vezes. Neste último caso, é tecnicamente
mais correto falar sobre um tensor de rank 5 (o rank de um tensor é o número de eixos), mas a
notação ambígua 5D tensor é comum independentemente.
>>> x.ndim
As entradas do primeiro eixo são chamadas de linhas e as entradas do segundo eixo são
chamadas de colunas . No exemplo anterior, [5, 78, 2, 34, 0]é a primeira linha de xe [5,
6, 7]é a primeira coluna.
>>> x.ndim
Empacotando tensores 3D em uma matriz, você pode criar um tensor 4D e assim por diante. No
aprendizado profundo, você geralmente manipula tensores que são 0D a 4D, embora você possa
ir até 5D se processar dados de vídeo.
Número de eixos (rank) - Por exemplo, um tensor 3D tem três eixos e uma matriz
possui dois eixos. Isso também é chamado de tensorndimem bibliotecas Python, como o
Numpy.
Forma - Esta é uma tupla de inteiros que descreve quantas dimensões o tensor tem ao
longo de cada eixo. Por exemplo, o exemplo da matriz anterior tem forma(3, 5)e o
exemplo de tensor 3D tem forma(3, 3, 5). Um vetor tem uma forma com um único
elemento, como, por exemplo(5,), um escalar tem uma forma vazia().
Tipo de dados (geralmente chamado dtypeem bibliotecas Python) - este é o tipo de
dados contidos no tensor; por exemplo, o tipo de um tensor poderia
ser float32, uint8, float64, e assim por diante. Em raras ocasiões, você pode ver
um chartensor. Observe que os tensores de string não existem no Numpy (ou na
maioria das outras bibliotecas), porque os tensores residem em segmentos de memória
pré-alocados e contíguos: e strings, com tamanho variável, impediriam o uso dessa
implementação.
Para tornar isso mais concreto, vamos olhar para os dados que processamos no exemplo
MNIST. Primeiro, nós carregamos o conjunto de dados MNIST:
uint8
Então, o que temos aqui é um tensor 3D de inteiros de 8 bits. Mais precisamente, é uma matriz
de 60.000 matrizes de 28 × 8 inteiros. Cada uma dessas matrizes é uma imagem em tons de
cinza, com coeficientes entre 0 e 255.
Vamos mostrar o quarto dígito neste tensor 3D, usando a biblioteca Matplotlib (parte do pacote
científico padrão do Python); veja a figura 2.2 .
plt.show ()
O exemplo a seguir seleciona os dígitos de # 10 a # 100 (# 100 não está incluído) e os coloca em
uma matriz de forma (90, 28, 28):
>>> my_slice.shape
>>> my_slice.shape
Em geral, você pode selecionar entre dois índices ao longo de cada eixo do tensor. Por exemplo,
para selecionar 14 × 14 pixels no canto inferior direito de todas as imagens, faça o seguinte:
Também é possível usar índices negativos. Assim como os índices negativos nas listas do
Python, eles indicam uma posição relativa ao final do eixo atual. Para cortar as imagens em
patches de 14 × 14 pixels centralizados no meio, faça o seguinte:
Além disso, os modelos de aprendizagem profunda não processam um conjunto de dados inteiro
de uma só vez; em vez disso, eles dividem os dados em pequenos lotes. Concretamente, aqui está
um lote de nossos dígitos MNIST, com tamanho de lote de 128:
E o nth lote:
Ao considerar esse tensor de lote, o primeiro eixo (eixo 0) é chamado de eixo de lote ou
de lote . Este é um termo que você encontrará frequentemente ao usar o Keras e outras
bibliotecas de aprendizado profundo.
O eixo do tempo é sempre o segundo eixo (eixo do índice 1), por convenção. Vamos ver alguns
exemplos:
Assim como qualquer programa de computador pode ser reduzido a um pequeno conjunto de
operações binárias em entradas binárias (AND, OR, NOR e assim por diante), todas as
transformações aprendidas por redes neurais profundas podem ser reduzidas a um punhado
de operações tensorasaplicadas a tensores. de dados numéricos. Por exemplo, é possível
adicionar tensores, multiplicar tensores e assim por diante.
Em nosso exemplo inicial, estávamos construindo nossa rede empilhando Densecamadas umas
sobre as outras. Uma instância da camada Keras é assim:
Essa camada pode ser interpretada como uma função, que toma como entrada um tensor 2D e
retorna outro tensor 2D - uma nova representação para o tensor de entrada. Especificamente, a
função é a seguinte (onde Wé um tensor 2D e bé um vetor, ambos os atributos da camada):
Vamos descompactar isso. Nós temos três operações de tensor aqui: um produto de ponto
( dot) entre o tensor de entrada e um tensor chamado W; uma adição ( +) entre o tensor 2D
resultante e um vetor b; e, finalmente, uma reluoperação. relu(x)é max(x, 0).
Nota
Embora esta seção lide inteiramente com expressões de álgebra linear, você não encontrará
nenhuma notação matemática aqui. Descobri que os conceitos matemáticos podem ser mais
facilmente dominados por programadores sem histórico matemático, se forem expressos como
trechos curtos do Python, em vez de equações matemáticas. Então, vamos usar o código Numpy
por toda parte.
return x
1 x é um tensor 2D numpy.
2 Evite substituir o tensor de entrada.
x = x.copy () 2
x [i, j] + = y [i, j]
return x
No mesmo princípio, você pode fazer multiplicação, subtração e assim por diante.
Na prática, ao lidar com arrays Numpy, essas operações estão disponíveis como funções Numpy
internas bem otimizadas, que delegam o trabalho pesado a uma implementação de
Subprogramas de Álgebra Linear Básica (BLAS) se você tiver uma instalada (o que deve). BLAS
são rotinas de manipulação de tensor de baixo nível, altamente paralelas e eficientes que são
tipicamente implementadas em Fortran ou C.
Então, no Numpy, você pode fazer a seguinte operação element-wise, e será muito rápido:
z = x + y 1
1 adição elementar
2 Relé elementar
2.3.2. Radiodifusão
Nossa implementação ingênua anterior naive_addsuporta apenas a adição de tensores 2D com
formas idênticas. Mas na Densecamada introduzida anteriormente, adicionamos um tensor 2D
com um vetor. O que acontece com a adição quando as formas dos dois tensores que estão sendo
adicionados diferem?
Quando possível, e se não houver ambigüidade, o menor tensor será transmitido para
corresponder à forma do maior tensor. Broadcasting consiste em duas etapas:
Vamos dar uma olhada em um exemplo concreto. Considere Xcom forma (32, 10)e ycom
forma (10,). Primeiro, adicionamos um primeiro eixo vazio a ycuja forma se torna (1,
10). Então, nós repetimos y32 vezes ao lado deste novo eixo, de modo que acabamos com um
tensor Ycom forma(32, 10), Y[i, :] == ypara onde iin range(0, 32). Neste ponto,
podemos continuar a adicionar Xe Y, porque eles têm a mesma forma.
Em termos de implementação, nenhum novo tensor 2D é criado, porque isso seria terrivelmente
ineficiente. A operação de repetição é inteiramente virtual: acontece no nível algorítmico e não
no nível da memória. Mas pensar no vetor sendo repetido 10 vezes ao lado de um novo eixo é
um modelo mental útil. Aqui está como uma implementação ingênua seria:
x = x.copy () 3
x [i, j] + = y [j]
return x
1 x é um tensor 2D numpy.
2 y é um vetor Numpy.
3 Evite substituir o tensor de entrada.
Com a transmissão, você geralmente pode aplicar operações com elementos de dois tensores se
um tensor tiver forma (a, b, ... n, n + 1, ... m)e o outro tiver forma (n, n + 1,
... m). A transmissão irá então acontecer automaticamente para eixos aatravés n - 1.
z = np.maximum (x, y) 3
z = np.dot (x, y)
z = x. y
Matematicamente, o que a operação de ponto faz? Vamos começar com o produto escalar de
dois vetores xe y. É calculado da seguinte forma:
z = 0.
z + = x [i] * y [i]
retorno z
Você deve ter notado que o produto escalar entre dois vetores é escalar e que somente vetores
com o mesmo número de elementos são compatíveis para um produto escalar.
Você também pode pegar o produto de ponto entre uma matriz xe um vetor y, que retorna um
vetor onde os coeficientes são os produtos de ponto entre ye as linhas de x. Você implementa da
seguinte maneira:
retorno z
Você também pode reutilizar o código que escrevemos anteriormente, o que destaca a relação
entre um produto vetor de matriz e um produto vetorial:
retorno z
Note que, assim que um dos dois tensores tem um valor ndimmaior que 1, dotnão é mais
simétrico, o que significa que dot(x, y)não é o mesmo que dot(y, x).
row_x = x [i,:]
column_y = y [:, j]
retorno z
Para entender a compatibilidade das formas de pontos, é útil visualizar os tensores de entrada e
saída, alinhando-os conforme mostrado na figura 2.5 .
x, ye zsão retratados como retângulos (caixas literais de coeficientes). Como as linhas e xas
colunas de ydevem ter o mesmo tamanho, segue-se que a largura do xmostocoincidir com a
altura de y. Se você desenvolver novos algoritmos de aprendizado de máquina, provavelmente
estará desenhando esses diagramas com frequência.
Mais genericamente, você pode pegar o produto escalar entre tensores de dimensões mais altas,
seguindo as mesmas regras para compatibilidade de formas conforme descrito anteriormente
para o caso 2D:
(a, b, c, d). (d,) -> (a, b, c)
Remodelar um tensor significa reorganizar suas linhas e colunas para corresponder a uma
forma de destino. Naturalmente, o tensor reconfigurado tem o mesmo número total de
coeficientes que o tensor inicial. A reformulação é melhor compreendida através de exemplos
simples:
[2., 3.]
[4., 5.]])
(3, 2)
>>> x
array ([[0],
[1.]
[2.]
[3.]
[4.]
[5.]])
>>> x
(20, 300)
A = [0,5, 1]
É um ponto em um espaço 2D (veja a figura 2.6 ). É comum imaginar um vetor como uma flecha
ligando a origem ao ponto, como mostra a figura 2.7 .
Em 3D, a seguinte imagem mental pode ser útil. Imagine duas folhas de papel colorido: uma
vermelha e outra azul. Coloque um em cima do outro. Agora, amassem juntos em uma pequena
bola. Essa bolinha de papel amassada são os dados de entrada, e cada folha de papel é uma
classe de dados em um problema de classificação. O que uma rede neural (ou qualquer outro
modelo de aprendizado de máquina) pretende fazer é descobrir uma transformação da bola de
papel que a descompactaria, de modo a tornar as duas classes novamente separáveis. Com o
aprendizado profundo, isso seria implementado como uma série de transformações simples do
espaço 3D, como aquelas que você poderia aplicar na esfera de papel com os dedos, um
movimento de cada vez.
Como você viu na seção anterior, cada camada neural do nosso primeiro exemplo de rede
transforma seus dados de entrada da seguinte forma:
Nesta expressão, We bsão tensores que são atributos da camada. Eles são chamados
de pesos ou parâmetros treináveis da camada ( kernele os biasatributos,
respectivamente). Esses pesos contêm as informações aprendidas pela rede da exposição aos
dados de treinamento.
Inicialmente, essas matrizes de peso são preenchidas com pequenos valores aleatórios (uma
etapa chamada inicialização aleatória ). É claro que não há razão para esperar
que relu(dot(W, input) + b), quando We baleatoriamente, surjam representações
úteis. As representações resultantes não têm sentido, mas são um ponto de partida. O que vem a
seguir é ajustar gradualmente esses pesos, com base em um sinal de feedback. Esse ajuste
gradual, também chamado de treinamento , é basicamente o aprendizado da aprendizagem de
máquina.
Isso acontece dentro do que é chamado de loop de treinamento , que funciona da seguinte
maneira. Repita essas etapas em um loop, o quanto for necessário:
Você acabará tendo uma rede que tem uma perda muito baixa em seus dados de treinamento:
uma baixa incompatibilidade entre as previsões y_prede os alvos esperados y. A rede aprendeu
a mapear suas entradas para corrigir os alvos. De longe, pode parecer mágica, mas quando você
reduz a passos elementares, acaba por ser simples.
A etapa 1 parece bastante fácil - apenas código de E / S. Os passos 2 e 3 são apenas a aplicação
de um punhado de operações tensoriais, para que você possa implementar essas etapas apenas
com base no que aprendeu na seção anterior. A parte difícil é o passo 4: atualizar os pesos da
rede. Dado um coeficiente de peso individual na rede, como você pode calcular se o coeficiente
deve ser aumentado ou diminuído e quanto?
Uma solução ingênua seria congelar todos os pesos na rede, exceto o coeficiente escalar que está
sendo considerado, e tentar valores diferentes para esse coeficiente. Digamos que o valor inicial
do coeficiente seja 0,3. Após o envio direto de um lote de dados, a perda da rede no lote é de
0,5. Se você alterar o valor do coeficiente para 0.35 e executar novamente o passo para frente, a
perda aumentará para 0.6. Mas se você diminuir o coeficiente para 0,25, a perda cai para
0,4. Nesse caso, parece que atualizar o coeficiente em -0,05contribuiria para minimizar a
perda. Isso teria que ser repetido para todos os coeficientes da rede.
Mas tal abordagem seria horrivelmente ineficiente, porque você precisaria calcular dois passes
para frente (que são caros) para cada coeficiente individual (dos quais existem muitos,
geralmente milhares e às vezes até milhões). Uma abordagem muito melhor é aproveitar o fato
de que todas as operações usadas na rede são diferenciáveis e calcular o gradiente da perda em
relação aos coeficientes da rede. Você pode então mover os coeficientes na direção oposta do
gradiente, diminuindo assim a perda.
Se você já sabe o que significa diferenciável e o que é um gradiente , você pode pular para
a seção 2.4.3 . Caso contrário, as duas seções a seguir ajudarão você a entender esses conceitos.
f (x + epsilon_x) = y + epsilon_y
Além disso, como a função é suave (sua curva não possui ângulos abruptos),
quando epsilon_xé pequena o suficiente, em torno de um determinado ponto p, é possível
aproximar-se fcomo uma função linear de inclinação a, de modo que ela epsilon_yse torna a
* epsilon_x:
f (x + epsilon_x) = y + a * epsilon_x
Obviamente, esta aproximação linear é válida apenas quando xestá perto o suficiente p.
A inclinação aé chamado o derivado de fno p. Se afor negativo, isso significa que uma pequena
mudança xao redor presultará em uma diminuição f(x)(como mostrado na figura 2.10 ); e
se afor positivo, uma pequena alteração xresultará em um aumento de f(x). Além disso, o
valor absoluto de a(a magnitude da derivada) indica a rapidez com que esse aumento ou
diminuição ocorrerá.
Para cada função diferenciável f(x)( diferenciável significa “pode ser derivado”: por exemplo,
funções suaves e contínuas podem ser derivadas), existe uma função derivada f'(x)que mapeia
valores xpara a inclinação da aproximação linear local fnaquelespontos. Por exemplo, a
derivada de cos(x)é -sin(x), a derivada de f(x) = a * xis f'(x) = ae assim por diante.
Se você está tentando atualizar xpor um fator epsilon_xpara minimizar f(x), e você sabe o
derivado de f, então seu trabalho está feito: a derivada descreve completamente
como f(x)evolui à medida que você muda x. Se você quiser reduzir o valor de f(x), você só
precisa se mover xum pouco na direção oposta da derivada.
2.4.2. Derivada de uma operação tensorial: o gradiente
Um gradiente é a derivada de uma operação tensorial. É a generalização do conceito de
derivativos para funções de entradas multidimensionais: isto é, para funções que tomam
tensores como entradas.
Considere um vetor de entrada x, uma matriz W, um destino ye uma função de perda loss. Você
pode usar Wpara calcular um candidato de destino y_prede calcular a perda ou
incompatibilidade entre o candidato de destino y_prede o destino y:
Se as entradas de dados xe yestiverem congeladas, isso pode ser interpretado como uma função
mapeando valores Wpara valores de perda:
loss_value = f (W)
Vamos dizer que o valor atual de Wé W0. Então a derivada do fponto W0é um
tensor gradient(f)(W0)com a mesma forma W, onde cada coeficiente gradient(f)
(W0)[i, j]indica a direção e a magnitude da mudança loss_valueobservada durante a
modificação W0[i, j]. Esse tensor gradient(f)(W0)é o gradiente da função f(W) =
loss_valueem W0.
Você viu anteriormente que a derivada de uma função f(x)de um coeficiente único pode ser
interpretada como a inclinação da curva de f. Do mesmo modo, gradient(f)(W0)pode ser
interpretado como o tensor de descrever a curvatura de f(W)torno W0.
Por esta razão, da mesma maneira que, para uma função f(x), você pode reduzir o valor
de f(x)mover xum pouco na direção oposta da derivada, com uma função f(W)de um tensor,
você pode reduzir f(W)movendo-se Wna direção oposta de o gradiente: por exemplo, W1 = W0
- step * gradient(f)(W0)(onde stepé um pequeno fator de escala). Isso significa ir
contra a curvatura, que intuitivamente deve colocá-lo mais baixo na curva. Note que o fator de
escala stepé necessário porque gradient(f)(W0)apenas aproxima a curvatura quando você
está perto W0, então você não quer se afastar muito W0.
Aplicado a uma rede neural, isso significa encontrar analiticamente a combinação de valores de
peso que produza a menor função de perda possível. Isso pode ser feito resolvendo a
equação gradient(f)(W) = 0para W. Esta é uma equação polinomial de N variáveis,
onde N é o número de coeficientes na rede. Embora seja possível resolver tal equação para N = 2
ou N = 3, fazê-lo é intratável para redes neurais reais, onde o número de parâmetros nunca é
menor que alguns milhares e pode ser várias dezenas de milhões.
Em vez disso, você pode usar o algoritmo de quatro etapas descrito no início desta seção:
modifique os parâmetros pouco a pouco com base no valor de perda atual em um lote aleatório
de dados. Como você está lidando com uma função diferenciável, é possível calcular seu
gradiente, o que oferece uma maneira eficiente de implementar a etapa 4. Se você atualizar os
pesos na direção oposta do gradiente, a perda será um pouco menor a cada vez:
1. Desenhe um lote de amostras de treinamento xe alvos correspondentes y.
2. Execute a rede xpara obter previsões y_pred.
3. Calcule a perda da rede no lote, uma medida da incompatibilidade entre y_prede y.
4. Calcule o gradiente da perda em relação aos parâmetros da rede (um passo para trás ).
5. Mova os parâmetros um pouco na direção oposta do gradiente - por exemplo W = step
* gradient- reduzindo a perda no lote um pouco.
Figura 2.11. SGD abaixo de uma curva de perda 1D (um parâmetro que pode ser aprendido)
Como você pode ver, intuitivamente é importante escolher um valor razoável para
o stepfator. Se for muito pequeno, a descida da curva levará muitas iterações e poderá ficar
presa em um mínimo local. Se stepfor muito grande, suas atualizações podem levar você a
locais completamente aleatórios na curva.
Observe que uma variante do algoritmo mini-batch SGD seria desenhar uma única amostra e
destino em cada iteração, em vez de desenhar um lote de dados. Isso seria verdadeiro SGD (em
oposição ao mini-lote SGD). Alternativamente, indo ao extremo oposto, você poderia executar
todos os passos em todos os dados disponíveis, o que é chamado de lote SGD . Cada atualização
seria mais precisa, mas muito mais cara. O compromisso eficiente entre esses dois extremos é
usar mini-lotes de tamanho razoável.
Embora a figura 2.11 ilustre gradiente descendente em um espaço de parâmetro 1D, na prática
você usará gradiente descendente em espaços altamente dimensionais: cada coeficiente de peso
em uma rede neural é uma dimensão livre no espaço, e pode haver dezenas de milhares ou até
milhões deles. Para ajudá-lo a construir a intuição sobre superfícies de perda, você também
pode visualizar a descida de gradiente ao longo de uma superfície de perda 2D, como mostrado
na figura 2.12. Mas você não pode visualizar como o processo real de treinamento de uma rede
neural se parece - você não pode representar um espaço de 1.000.000 dimensões de uma
maneira que faça sentido para os seres humanos. Como tal, é bom ter em mente que as intuições
que você desenvolve através dessas representações de baixa dimensão nem sempre são precisas
na prática. Isso tem sido historicamente uma fonte de problemas no mundo da pesquisa em
aprendizagem profunda.
Figura 2.12. Gradiente descendo uma superfície de perda 2D (dois parâmetros aprendíveis)
Além disso, existem várias variantes do SGD que diferem levando em consideração as
atualizações de peso anteriores ao calcular a próxima atualização de peso, em vez de apenas
observar o valor atual dos gradientes. Há, por exemplo, o SGD com impulso, assim como o
Adagrad, o RMSProp e vários outros. Essas variantes são conhecidas como métodos de
otimização ou otimizadores . Em particular, o conceito de momento , que é usado em muitas
dessas variantes, merece sua atenção. Momentum aborda dois problemas com SGD: velocidade
de convergência e mínimos locais. Considere a figura 2.13 , que mostra a curva de uma perda
como uma função de um parâmetro de rede.
Como você pode ver, em torno de um determinado valor de parâmetro, há um mínimo local :
em torno desse ponto, mover para a esquerda resultaria em aumento da perda, mas também se
moveria para a direita. Se o parâmetro em consideração estivesse sendo otimizado via SGD com
uma pequena taxa de aprendizado, então o processo de otimização ficaria preso no mínimo local
em vez de chegar ao mínimo global.
Você pode evitar esses problemas usando o momentum, que inspira-se na física. Uma imagem
mental útil aqui é pensar no processo de otimização como uma pequena bola rolando pela curva
de perda. Se tiver força suficiente, a bola não ficará presa em uma ravina e terminará no mínimo
global. O momento é implementado movendo a bola em cada etapa com base não apenas no
valor de inclinação atual (aceleração de corrente), mas também na velocidade atual (resultante
da aceleração passada). Na prática, isso significa atualizar o parâmetro com wbase não apenas
no valor do gradiente atual, mas também na atualização do parâmetro anterior, como nesta
implementação ingênua:
past_velocity = 0.
momento = 0,1 1
past_velocity = velocidade
update_parameter (w)
Cálculo diz-nos que uma tal cadeia de funções pode ser derivado usando a seguinte identidade,
chamada regra da cadeia : f(g(x)) = f'(g(x)) * g'(x). A aplicação da regra da cadeia
ao cálculo dos valores de gradiente de uma rede neural dá origem a um algoritmo
chamadoRetropropagação (também às vezes chamado de diferenciação no modo reverso ). A
retropropagação começa com o valor da perda final e trabalha de volta das camadas superiores
para as camadas inferiores, aplicando a regra da cadeia para calcular a contribuição de cada
parâmetro no valor da perda.
Hoje em dia, e por muitos anos, as pessoas implementarão redes em estruturas modernas
capazes de diferenciação simbólica , como o TensorFlow. Isto significa que, dada uma cadeia de
operações com uma derivada conhecida, eles podem calcular uma função gradientepara a cadeia
(aplicando a regra da cadeia) que mapeia valores de parâmetros de rede para valores de
gradiente. Quando você tem acesso a essa função, a passagem para trás é reduzida para uma
chamada para essa função de gradiente. Graças à diferenciação simbólica, você nunca precisará
implementar o algoritmo Backpropagation manualmente. Por esse motivo, não
desperdiçaremos seu tempo e seu foco em derivar a formulação exata do algoritmo
Backpropagation nessas páginas. Tudo o que você precisa é de um bom entendimento de como
funciona a otimização baseada em gradiente.
Você chegou ao final deste capítulo e agora você deve ter uma compreensão geral do que está
acontecendo nos bastidores de uma rede neural. Vamos voltar ao primeiro exemplo e revisar
cada peça à luz do que você aprendeu nas três seções anteriores.
Agora você entende que as imagens de entrada são armazenadas em tensores de Numpy, que
são aqui formatados como float32tensores de forma (60000, 784)(dados de treinamento)
e (10000, 784)(dados de teste), respectivamente.
rede = models.Sequential ()
Agora você entende que essa rede consiste em uma cadeia de duas Densecamadas, que cada
camada aplica algumas operações de tensor simples aos dados de entrada e que essas operações
envolvem tensores de peso. Os tensores de peso, que são atributos das camadas, são onde
o conhecimento da rede persiste.
perda = 'categorical_crossentropy',
métricas = ['precisão']]
Agora você entende que categorical_crossentropyé a função de perda que é usada como
um sinal de feedback para aprender os tensores de peso e que a fase de treinamento tentará
minimizar. Você também sabe que essa redução da perda acontece via gradiente estocástico em
mini-lote. As regras exatas que regem um uso específico do gradiente descendente são definidas
pelo rmspropotimizador passado como o primeiro argumento.
Agora você entende o que acontece quando você liga fit: a rede começará a iterar nos dados de
treinamento em mini-lotes de 128 amostras, 5 vezes (cada iteração em todos os dados de
treinamento é chamada de época ). Em cada iteração, a rede calculará os gradientes dos pesos
em relação à perda no lote e atualizará os pesos de acordo. Após estas 5 épocas, a rede terá
realizado 2.345 atualizações de gradiente (469 por época), e a perda da rede será
suficientemente baixa para que a rede seja capaz de classificar dígitos manuscritos com alta
precisão.
Neste ponto, você já sabe muito do que há para saber sobre redes neurais.
Resumo do capítulo
Aprender significa encontrar uma combinação de parâmetros do modelo que minimize
uma função de perda para um determinado conjunto de amostras de dados de
treinamento e seus destinos correspondentes.
A aprendizagem acontece desenhando lotes aleatórios de amostras de dados e seus
alvos, e calculando o gradiente dos parâmetros de rede em relação à perda no lote. Os
parâmetros da rede são então movidos um pouco (a magnitude do movimento é
definida pela taxa de aprendizado) na direção oposta do gradiente.
Todo o processo de aprendizado é possibilitado pelo fato de que as redes neurais são
cadeias de operações de tensores diferenciáveis e, portanto, é possível aplicar a regra de
derivação da cadeia para encontrar a função de gradiente mapeando os parâmetros
atuais e o lote atual de dados para um valor de gradiente.
Dois conceitos-chave que você verá com frequência em capítulos futuros
são perda e otimizadores . Essas são as duas coisas que você precisa definir antes de
começar a alimentar os dados em uma rede.
A perda é a quantidade que você tentará minimizar durante o treinamento, por isso
deve representar uma medida de sucesso para a tarefa que você está tentando resolver.
O otimizador especifica a maneira exata na qual o gradiente da perda será usado para
atualizar parâmetros: por exemplo, pode ser o otimizador RMSProp, o SGD com
impulso e assim por diante.
Este capítulo foi projetado para você começar a usar redes neurais para resolver problemas
reais. Você consolidará o conhecimento adquirido em nosso primeiro exemplo prático
no capítulo 2 e aplicará o que aprendeu a três novos problemas que abrangem os três casos de
uso mais comuns de redes neurais: classificação binária, classificação multiclasse e escalar.
regressão.
Neste capítulo, vamos dar uma olhada mais de perto nos principais componentes das redes
neurais que introduzimos no capítulo 2 : camadas, redes, funções objetivas e
otimizadores. Vamos dar uma rápida introdução a Keras, a biblioteca de aprendizado profundo
do Python que usaremos ao longo do livro. Você configurará uma estação de trabalho de
aprendizado profundo, com suporte a TensorFlow, Keras e GPU. Vamos mergulhar em três
exemplos introdutórios de como usar redes neurais para resolver problemas reais:
No final deste capítulo, você poderá usar redes neurais para resolver problemas simples de
máquinas, como classificação e regressão sobre dados vetoriais. Você então estará pronto para
começar a construir uma compreensão baseada em teoria, mais baseada em princípios, no
aprendizado de máquina no capítulo 4 .
3.1. ANATOMIA DE UMA REDE NEURAL
Como você viu nos capítulos anteriores, o treinamento de uma rede neural gira em torno dos
seguintes objetos:
Você pode visualizar sua interação conforme ilustrado na figura 3.1 : a rede, composta de
camadas que estão encadeadas, mapeia os dados de entrada para as previsões. A função de
perda compara essas previsões com as metas, produzindo um valor de perda: uma medida de
quão bem as previsões da rede correspondem ao esperado. O otimizador usa esse valor de perda
para atualizar os pesos da rede.
Vamos dar uma olhada mais de perto em camadas, redes, funções de perda e otimizadores.
Diferentes camadas são apropriadas para diferentes formatos de tensores e diferentes tipos de
processamento de dados. Por exemplo, dados vetoriais simples, armazenados em tensores 2D de
forma (samples, features), são freqüentemente processados por camadas densamente
conectadas , também chamadas de camadas totalmente conectadas ou densas (a Denseclasse
em Keras). Os dados de sequência, armazenados em tensores 3D de forma (samples,
timesteps, features), são tipicamente processados por camadas recorrentes , como
uma LSTMcamada. Os dados de imagem, armazenados em tensores 4D, são geralmente
processados por camadas de convolução 2D ( Conv2D).
Você pode pensar em camadas como os blocos LEGO de aprendizagem profunda, uma metáfora
explicitada por estruturas como Keras. A criação de modelos de aprendizagem profunda em
Keras é feita agrupando camadas compatíveis para formar pipelines de transformação de dados
úteis. A noção de compatibilidade de camada aqui se refere especificamente ao fato de que toda
camada aceitará apenas tensores de entrada de uma certa forma e retornará tensores de saída de
uma certa forma. Considere o seguinte exemplo:
Estamos criando uma camada que aceitará apenas como tensores 2D de entrada, onde a
primeira dimensão é 784 (o eixo 0, a dimensão do lote, não é especificado e, portanto, qualquer
valor seria aceito). Essa camada retornará um tensor em que a primeira dimensão foi
transformada em 32.
Assim, esta camada só pode ser conectada a uma camada a jusante que espera vetores de 32
dimensões como sua entrada. Ao usar o Keras, você não precisa se preocupar com a
compatibilidade, porque as camadas que você adiciona aos seus modelos são construídas
dinamicamente para corresponder à forma da camada de entrada. Por exemplo, suponha que
você escreva o seguinte:
model = models.Sequential ()
A segunda camada não recebeu um argumento de forma de entrada - em vez disso, ele inferiu
automaticamente sua forma de entrada como sendo a forma de saída da camada que veio antes.
Mas à medida que você avança, você estará exposto a uma variedade muito maior de topologias
de rede. Alguns comuns incluem o seguinte:
A topologia de uma rede define um espaço de hipótese . Você pode lembrar que no capítulo
1 definimos aprendizado de máquina como “procurando representações úteis de alguns dados
de entrada, dentro de um espaço predefinido de possibilidades, usando a orientação de um sinal
de feedback”. Ao escolher uma topologia de rede, você restringe seu espaço de
possibilidades (espaço de hipóteses) para uma série específica de operações de tensor,
mapeando dados dedados de saída. O que você estará procurando é um bom conjunto de valores
para os tensores de peso envolvidos nessas operações tensoras.
Escolher a arquitetura de rede correta é mais uma arte do que uma ciência; e embora existam
algumas práticas e princípios que você possa confiar, somente a prática pode ajudá-lo a se
tornar um arquiteto de rede neural adequado. Os próximos capítulos ensinarão a você princípios
explícitos para a construção de redes neurais e o ajudarão a desenvolver a intuição sobre o que
funciona ou não para problemas específicos.
Uma rede neural que possui múltiplas saídas pode ter múltiplas funções de perda (uma por
saída). Mas o processo gradiente-descendente deve ser baseado em um único valor de perda
escalar; Assim, para redes multiloss, todas as perdas são combinadas (via média) em uma única
grandeza escalar.
Escolher a função objetiva correta para o problema certo é extremamente importante: sua rede
aceitará qualquer atalho possível para minimizar a perda; Portanto, se o objetivo não se
correlacionar totalmente com o sucesso da tarefa em questão, sua rede acabará fazendo coisas
que você pode não querer. Imagine uma AI estúpida e onipotente treinada via SGD, com essa
função objetiva mal escolhida: “maximizar o bem-estar médio de todos os seres humanos vivos.”
Para tornar seu trabalho mais fácil, essa IA pode escolher matar todos os humanos, exceto
alguns, e se concentrar o bem-estar dos restantes - porque o bem-estar médio não é afetado pela
quantidade de humanos que restam. Isso pode não ser o que você pretendia!
Ao longo deste livro, os exemplos de código usam Keras ( https://keras.io ). Keras é uma estrutura
de aprendizado profundo para Python que fornece uma maneira conveniente de definir e treinar
quase qualquer tipo de modelo de aprendizado profundo. Keras foi inicialmente desenvolvido
para pesquisadores, com o objetivo de permitir a experimentação rápida.
Ele permite que o mesmo código seja executado perfeitamente na CPU ou na GPU.
Tem uma API amigável que facilita a prototipagem rápida de modelos de aprendizagem
profunda.
Ele tem suporte embutido para redes convolucionais (para visão computacional), redes
recorrentes (para processamento sequencial) e qualquer combinação de ambos.
Ele suporta arquiteturas de rede arbitrárias: modelos com múltiplas entradas ou
múltiplas saídas, compartilhamento de camadas, compartilhamento de modelos e assim
por diante. Isso significa que Keras é apropriado para construir essencialmente
qualquer modelo de aprendizagem profunda, de uma rede adversária generativa a uma
máquina de Turing neural.
Keras é distribuído sob a licença MIT permissiva, o que significa que pode ser usado livremente
em projetos comerciais. É compatível com qualquer versão do Python de 2.7 a 3.6 (a partir de
meados de 2017).
A Keras tem mais de 200.000 usuários, desde pesquisadores acadêmicos e engenheiros, tanto
de startups quanto de grandes empresas, até estudantes de pós-graduação e amadores. Keras é
usado no Google, Netflix, Uber, CERN, Yelp, Square e centenas de startups trabalhando em uma
ampla gama de problemas. Keras também é uma estrutura popular no Kaggle, o site de
competição de aprendizado de máquina, onde quase todas as competições recentes de
aprendizado profundo foram vencidas usando modelos Keras.
Figura 3.2. Interesse da pesquisa na web do Google por diferentes estruturas de aprendizagem profunda ao longo do tempo
TensorFlow, CNTK e Theano são algumas das principais plataformas de aprendizado profundo
hoje. Theano ( http://deeplearning.net/software/theano ) é desenvolvido pelo laboratório MILA
da Université de Montréal , o TensorFlow ( www.tensorflow.org ) é desenvolvido pelo Google e
pelo CNTK ( https://github.com/Microsoft / CNTK) é desenvolvido pela Microsoft. Qualquer pedaço
de código que você escreve com Keras pode ser executado com qualquer um desses backends
sem ter que alterar nada no código: você pode alternar facilmente entre os dois durante o
desenvolvimento, o que geralmente é útil - por exemplo, se um desses backends provar ser mais
rápido para uma tarefa específica. Recomendamos usar o backend do TensorFlow como padrão
para a maioria de suas necessidades de aprendizado profundo, porque ele é o mais amplamente
adotado, escalável e pronto para produção.
Via TensorFlow (ou Theano, ou CNTK), o Keras é capaz de rodar sem problemas em CPUs e
GPUs. Quando executado na CPU, o próprio TensorFlow está agrupando uma biblioteca de
baixo nível para operações de tensor chamada Eigen ( http://eigen.tuxfamily.org ). Na GPU, o
Tensor-Flow encapsula uma biblioteca de operações de aprendizagem profunda bem otimizadas
chamada biblioteca de rede neural profunda NVIDIA CUDA (cuDNN).
model = models.Sequential ()
Com a API funcional, você está manipulando os tensores de dados que o modelo processa e
aplicando camadas a esse tensor como se fossem funções.
Nota
Um guia detalhado sobre o que você pode fazer com a API funcional pode ser encontrado
no capítulo 7 . Até o capítulo 7 , usaremos a Sequentialclasse apenas em nossos exemplos de
código.
Depois que sua arquitetura de modelo for definida, não importa se você usou
um Sequentialmodelo ou a API funcional. Todas as etapas a seguir são as mesmas.
perda = 'mse',
métricas = ['precisão']]
Nos próximos capítulos, você criará uma intuição sólida sobre que tipo de arquiteturas de rede
funciona para diferentes tipos de problemas, como escolher a configuração de aprendizado
correta e como ajustar um modelo até obter os resultados desejados. Examinaremos três
exemplos básicos nas seções 3.4 , 3.5 e 3.6 : um exemplo de classificação de duas classes, um
exemplo de classificação de muitas classes e um exemplo de regressão.
3.3. CONFIGURANDO UMA ESTAÇÃO DE TRABALHO DE APRENDIZAGEM
PROFUNDA
Se você está executando localmente ou na nuvem, é melhor usar uma estação de trabalho
Unix. Embora seja tecnicamente possível usar o Keras no Windows (todos os três back-ends do
Keras suportam o Windows), nós não o recomendamos. Nas instruções de instalação no apêndice
A , vamos considerar uma máquina Ubuntu. Se você é um usuário do Windows, a solução mais
simples para fazer tudo funcionar é configurar uma inicialização dupla do Ubuntu na sua
máquina. Pode parecer um incômodo, mas usar o Ubuntu economizará muito tempo e
problemas a longo prazo.
Note-se que, a fim de usar Keras, você precisa instalar TensorFlow ou CNTK ou Theano (ou
todos eles, se você quer ser capaz de alternar entre os três backends). Neste livro, vamos nos
concentrar no TensorFlow, com algumas instruções sobre o Theano. Nós não vamos cobrir o
CNTK.
Recomendamos usar os notebooks Jupyter para começar a usar o Keras, embora isso não seja
um requisito: você também pode executar scripts Python autônomos ou executar código de
dentro de um IDE como o PyCharm. Todos os exemplos de código deste livro estão disponíveis
como cadernos de código aberto; você pode baixá-los no site do livro
em www.manning.com/books/deep-learning-with-python.
Vamos dar uma olhada em alguns dos compromissos envolvidos na escolha de uma opção sobre
a outra.
Mas se você é um usuário pesado de aprendizado profundo, essa configuração não é sustentável
a longo prazo - ou mesmo por mais de algumas semanas. As instâncias do EC2 são caras: o tipo
de instância recomendado no apêndice B (a p2.xlargeinstância, que não fornecerá muita
energia) custa US $ 0,90 por hora a partir de meados de 2017. Enquanto isso, uma sólida GPU
de classe de consumidor custará entre US $ 1.000 e US $ 1.500 - um preço que tem sido
bastante estável ao longo do tempo, mesmo com as especificações das GPUs melhorando. Se
você é sério sobre aprendizagem profunda, você deve configurar uma estação de trabalho local
com uma ou mais GPUs.
Em suma, o EC2 é uma ótima maneira de começar. Você poderia seguir os exemplos de código
deste livro inteiramente em uma instância da GPU do EC2. Mas se você for um usuário
experiente em aprendizado profundo, adquira suas próprias GPUs.
A partir desta seção, vamos supor que você tenha acesso a uma máquina com o Keras e suas
dependências instaladas - de preferência com suporte a GPU. Certifique-se de terminar este
passo antes de prosseguir. Siga os guias passo a passo nos apêndices e procure on-line se
precisar de mais ajuda. Não há escassez de tutoriais sobre como instalar o Keras e as
dependências comuns de aprendizagem profunda.
Por que usar treinamentos e conjuntos de testes separados? Porque você nunca deve testar um
modelo de aprendizado de máquina nos mesmos dados que você usou para treiná-lo! Só porque
um modelo tem um bom desempenho em seus dados de treinamento, não significa que ele
funcionará bem em dados que nunca viu; e o que importa é o desempenho de seu modelo em
novos dados (porque você já conhece os rótulos de seus dados de treinamento - obviamente,
você não precisa do seu modelo para prevê-los). Por exemplo, é possível que seu modelo possa
simplesmente memorizar um mapeamento entre suas amostras de treinamento e seus alvos, o
que seria inútil para a tarefa de prever alvos para dados que o modelo nunca viu
antes. Analisaremos esse ponto com muito mais detalhes no próximo capítulo.
Assim como o conjunto de dados MNIST, o conjunto de dados do IMDB é fornecido com o
Keras. Já foi pré-processado: as revisões (seqüências de palavras) foram transformadas em
sequências de inteiros, onde cada inteiro representa uma palavra específica em um dicionário.
O código a seguir carregará o conjunto de dados (quando você executá-lo pela primeira vez,
cerca de 80 MB de dados serão baixados para sua máquina).
num_words = 10000)
As variáveis train_datae test_datasão listas de revisões; cada revisão é uma lista de índices
de palavras (codificando uma sequência de palavras). train_labelse test_labelssão listas
de 0s e 1s, onde 0 significa negativo e 1 significa positivo :
Como você está se restringindo às 10.000 palavras mais frequentes, nenhum índice de palavras
excederá 10.000:
Para chutes, veja como você pode decodificar rapidamente um desses comentários de volta para
as palavras em inglês:
word_index = imdb.get_word_index () 1
reverse_word_index = dict (
Pad suas listas para que todos tenham o mesmo comprimento, transformá-los em um
tensor inteiro de forma (samples, word_indices)e, em seguida, usar como
primeira camada em sua rede uma camada capaz de lidar com tais tensores inteiros
(a Embeddingcamada, que abordaremos em detalhes mais adiante no livro).
Um hot codificar suas listas para transformá-los em vetores de 0s e 1s. Isso significaria,
por exemplo, transformar a sequência [3, 5]em um vetor de 10.000 dimensões que
seria todos os 0s, exceto os índices 3 e 5, que seriam 1s. Então você poderia usar como a
primeira camada em sua rede uma Densecamada, capaz de manipular dados vetoriais
de ponto flutuante.
Vamos com a última solução para vetorizar os dados, o que você fará manualmente para
máxima clareza.
retornar resultados
Agora os dados estão prontos para serem alimentados em uma rede neural.
O argumento sendo passado para cada Densecamada (16) é o número de unidades ocultas da
camada. Uma unidade oculta é uma dimensão no espaço de representação da camada. Você
pode se lembrar do capítulo 2 que cada Densecamada com uma reluativação implementa a
seguinte cadeia de operações de tensor:
Ter 16 unidades ocultas significa que a matriz de peso Wterá forma (input_dimension, 16):
o produto de ponto com Wprojetará os dados de entrada em um espaço de representação de 16
dimensões (e, em seguida, você adicionará o vetor de polarização be aplicará
a reluoperação). Você pode entender intuitivamente a dimensionalidade de seu espaço de
representação como “quanta liberdade você está permitindo que a rede tenha ao aprender
representações internas.” Ter unidades mais ocultas (um espaço de representação mais alta)
permite que sua rede aprenda representações mais complexas , mas torna a rede mais
computacionalmente cara e pode levar ao aprendizado de padrões indesejados (padrões que
melhorarão o desempenho nos dados de treinamento, mas não nos dados de teste).
Há duas decisões de arquitetura importantes a serem feitas sobre essa pilha de Densecamadas:
No capítulo 4 , você aprenderá princípios formais para guiá-lo a fazer essas escolhas. Por
enquanto, você terá que confiar em mim com a seguinte opção de arquitetura:
A figura 3.6 mostra como é a rede. E aqui está a implementação Keras, semelhante ao exemplo
MNIST que você viu anteriormente.
Para ter acesso a um espaço de hipóteses muito mais rico que se beneficiaria de representações
profundas, você precisa de uma não-linearidade ou função de ativação. relué a função de
ativação mais popular na aprendizagem profunda, mas há muitos outros candidatos, que
contam com nomes semelhante estranho: prelu, elue assim por diante.
Finalmente, você precisa escolher uma função de perda e um otimizador. Como você está
enfrentando um problema de classificação binária e a saída da sua rede é uma probabilidade
(você termina sua rede com uma camada de unidade única com uma ativação sigmóide), é
melhor usarbinary_crossentropyperda. Não é a única opção viável: você poderia usar, por
exemplo mean_squared_error. Mas crossentropy é geralmente a melhor escolha quando
você está lidando com modelos que geram probabilidades. Crossentropy é uma quantidade do
campo da Teoria da Informação que mede a distância entre as distribuições de probabilidade
ou, neste caso, entre a distribuição da verdade e suas predições.
perda = 'binary_crossentropy',
métricas = ['precisão']]
Você está passando o seu otimizador, função de perda, e métricas como cordas, que é possível
porque rmsprop, binary_crossentropye accuracysão empacotados como parte de
Keras. Às vezes você pode querer configurar os parâmetros do seu otimizador ou passar uma
função de perda personalizada ou função métrica. O primeiro pode ser feito passando uma
instância de classe do otimizador como optimizerargumento, conforme mostrado na listagem
3.5 ; o último pode ser feito passando objetos de função como losse / ou metricsargumentos,
como mostrado na listagem 3.6 .
Listagem 3.5. Configurando o otimizador
perda = 'binary_crossentropy',
métricas = ['precisão']]
loss = perdas.binary_crossentropy,
métricas = [metrics.binary_accuracy])
Agora você vai treinar o modelo por 20 épocas (20 iterações sobre todas as amostras
no x_traine y_traintensores), em mini-lotes de 512 amostras. Ao mesmo tempo, você
monitorará a perda e a precisão nas 10.000 amostras que você separou. Você faz isso passando
os dados de validação como o validation_dataargumento.
perda = 'binary_crossentropy',
metrics = ['acc'])
partial_y_train,
épocas = 20,
batch_size = 512,
>>> history_dict.keys ()
O dicionário contém quatro entradas: uma por métrica que estava sendo monitorada durante o
treinamento e durante a validação. Nas duas listas a seguir, vamos usar o Matplotlib para plotar
as perdas de treinamento e validação lado a lado (ver figura 3.7 ), bem como a precisão do
treinamento e validação (ver figura 3.8 ). Observe que seus próprios resultados podem variar um
pouco devido a uma inicialização aleatória diferente da sua rede.
history_dict = history.history
plt.xlabel ('Épocas')
plt.ylabel ('Loss')
plt.legend ()
plt.show ()
plt.clf () 1
acc_values = history_dict ['acc']
plt.xlabel ('Épocas')
plt.ylabel ('Loss')
plt.legend ()
plt.show ()
1 Limpa a figura
Como você pode ver, a perda de treinamento diminui a cada época, e a precisão do treinamento
aumenta a cada época. Isso é o que você esperaria ao executar a otimização de gradiente-descida
- a quantidade que você está tentando minimizar deve ser menor a cada iteração. Mas esse não é
o caso da perda e precisão da validação: elas parecem atingir o pico na quarta época. Este é um
exemplo do que alertamos anteriormente: um modelo que tenha um desempenho melhor nos
dados de treinamento não é necessariamente um modelo que funcionará melhor em dados
nunca antes vistos. Em termos precisos, o que você está vendo é overfitting: após a segunda
época, você está super otimizando os dados de treinamento e acaba aprendendo representações
específicas dos dados de treinamento e não generaliza os dados fora do conjunto de
treinamento.
Neste caso, para evitar overfitting, você pode parar de treinar após três épocas. Em geral, você
pode usar uma variedade de técnicas para atenuar o overfitting, que abordaremos no capítulo 4 .
Vamos treinar uma nova rede a partir do zero por quatro épocas e depois avaliá-la nos dados de
teste.
model = models.Sequential ()
perda = 'binary_crossentropy',
métricas = ['precisão']]
model.fit (x_train, y_train, epochs = 4, batch_size = 512)
>>> resultados
[0.2929924130630493, 0.88327999999999995]
Essa abordagem bastante ingênua alcança uma precisão de 88%. Com abordagens de última
geração, você deve conseguir aproximar-se de 95%.
3.4.5. Usando uma rede treinada para gerar previsões sobre novos
dados
Depois de ter treinado uma rede, você vai querer usá-lo em um ambiente prático. Você pode
gerar a probabilidade de as análises serem positivas usando o predictmétodo:
array ([[0.98006207]
[0.99758697]
[0,99975556]
...
[0,82167041]
[0,02885115]
Como você pode ver, a rede está confiante para algumas amostras (0,99 ou mais, ou 0,01 ou
menos), mas menos confiante para outras (0,6, 0,4).
Você usou duas camadas ocultas. Tente usar uma ou três camadas ocultas e veja como
isso afeta a validação e a precisão do teste.
Tente usar camadas com mais unidades ocultas ou menos unidades ocultas: 32
unidades, 64 unidades e assim por diante.
Tente usar a msefunção de perda em vez de binary_crossentropy.
Tente usar a tanhativação (uma ativação que era popular nos primeiros dias das redes
neurais) em vez de relu.
3.4.7. Empacotando
Veja o que você deve tirar deste exemplo:
Você normalmente precisa fazer um bom preprocessamento em seus dados brutos para
poder alimentá-los - como tensores - em uma rede neural. Seqüências de palavras
podem ser codificadas como vetores binários, mas também existem outras opções de
codificação.
Pilhas de Densecamadas com reluativações podem resolver uma ampla gama de
problemas (incluindo a classificação de sentimentos), e você provavelmente as usará
com frequência.
Em um problema de classificação binária (duas classes de saída), sua rede deve
terminar com uma Densecamada com uma unidade e uma sigmoidativação: a saída
de sua rede deve ser um escalar entre 0 e 1, codificando uma probabilidade.
Com essa saída sigmoide escalar em um problema de classificação binária, a função de
perda que você deve usar é binary_crossentropy.
O rmspropotimizador é geralmente uma escolha boa o suficiente, seja qual for o seu
problema. Essa é uma coisa a menos para você se preocupar.
À medida que melhoram seus dados de treinamento, as redes neurais eventualmente
começam a se ajustar demais e acabam obtendo resultados cada vez piores em dados
que nunca viram antes. Certifique-se de sempre monitorar o desempenho nos dados
que estão fora do conjunto de treinamento.
Na seção anterior, você viu como classificar as entradas de vetor em duas classes mutuamente
exclusivas usando uma rede neural densamente conectada. Mas o que acontece quando você
tem mais de duas classes?
Nesta seção, você construirá uma rede para classificar as notícias da Reuters em 46 tópicos
mutuamente exclusivos. Porque você tem muitas classes, esse problema é uma instância
de classificação multiclasse ; e como cada ponto de dados deve ser classificado em apenas uma
categoria, o problema é mais especificamente uma instância de classificação de rótulo único e
multiclasse . Se cada ponto de dados pudesse pertencer a várias categorias (neste caso, tópicos),
você estaria enfrentando um problema de classificação multiclasse e multicamada .
Como o IMDB e MNIST, o conjunto de dados da Reuters vem embalado como parte de
Keras. Vamos dar uma olhada.
num_words = 10000)
8982
2246
Assim como nas resenhas do IMDB, cada exemplo é uma lista de inteiros (índices de palavras):
[1, 245, 273, 207, 156, 53, 74, 160, 26, 14, 46, 296, 26, 39, 74, 2979,
3554, 14, 46, 4689, 4329, 86, 61, 3499, 4795, 14, 61, 451, 4329, 17, 12]
Veja como você pode decodificá-lo de volta às palavras, caso esteja curioso.
word_index = reuters.get_word_index ()
train_data [0]])
1
retornar resultados
x_train = vectorize_sequences (train_data) 1
Para vetorizar os rótulos, há duas possibilidades: você pode converter a lista de rótulos como um
tensor inteiro ou usar uma codificação simples. Codificação One-hot é um formato amplamente
utilizado para dados categóricos, também chamado de codificação categórica . Para uma
explicação mais detalhada da codificação de um-quente, veja a seção 6.1 . Nesse caso, a
codificação de um dos tags a quente consiste na incorporação de cada rótulo como um vetor all-
zero com um 1 no lugar do índice de rótulo. Aqui está um exemplo:
retornar resultados
Observe que há uma maneira interna de fazer isso em Keras, que você já viu em ação no
exemplo MNIST:
Em uma pilha de Densecamadas como essa que você está usando, cada camada só pode acessar
as informações presentes na saída da camada anterior. Se uma camada soltar algumas
informações relevantes para o problema de classificação, essas informações nunca poderão ser
recuperadas por camadas posteriores: cada camada pode se tornar um gargalo de
informações. No exemplo anterior, você usou camadas intermediárias de 16 dimensões, mas um
espaço de 16 dimensões pode ser muito limitado para aprender a separar 46 classes diferentes:
essas camadas pequenas podem atuar como gargalos de informações, descartando
permanentemente informações relevantes.
Por esse motivo, você usará camadas maiores. Vamos com 64 unidades.
model = models.Sequential ()
Há duas outras coisas que você deve observar sobre essa arquitetura:
Você termina a rede com uma Densecamada de tamanho 46. Isso significa que para
cada amostra de entrada, a rede produzirá um vetor de 46 dimensões. Cada entrada
neste vetor (cada dimensão) codificará uma classe de saída diferente.
A última camada usa uma softmaxativação. Você viu esse padrão no exemplo
MNIST. Isso significa que a rede produzirá uma distribuição de probabilidade sobre as
46 classes de saída diferentes - para cada amostra de entrada, a rede produzirá um vetor
de saída de 46 dimensões, onde output[i]está a probabilidade de que a amostra
pertença à classe i. As 46 pontuações somam 1.
A melhor função de perda para usar neste caso é categorical_crossentropy. Ele mede a
distância entre duas distribuições de probabilidade: aqui, entre a distribuição de probabilidade
produzida pela rede e a distribuição real dos rótulos. Minimizando a distância entre essas duas
distribuições, você treina a rede para produzir algo o mais próximo possível dos rótulos
verdadeiros.
perda = 'categorical_crossentropy',
métricas = ['precisão']]
partial_y_train,
épocas = 20,
batch_size = 512,
E finalmente, vamos mostrar suas curvas de perda e precisão (veja as figuras 3.9 e 3.10 ).
plt.xlabel ('Épocas')
plt.ylabel ('Loss')
plt.legend ()
plt.show ()
plt.clf () 1
plt.xlabel ('Épocas')
plt.ylabel ('Loss')
plt.legend ()
plt.show ()
1 Limpa a figura
A rede começa a se sobrepor depois de nove épocas. Vamos treinar uma nova rede a partir do
zero por nove épocas e depois avaliá-la no conjunto de testes.
Listagem 3.21. Reciclagem de um modelo a partir do zero
model = models.Sequential ()
perda = 'categorical_crossentropy',
métricas = ['precisão']]
model.fit (partial_x_train,
partial_y_train,
épocas = 9,
batch_size = 512,
>>> resultados
[0.9565213431445807, 0.79697239536954589]
Essa abordagem atinge uma precisão de ~ 80%. Com um problema de classificação binária
equilibrada, a precisão alcançada por um classificador puramente aleatório seria de 50%. Mas
neste caso é mais próximo de 19%, então os resultados parecem muito bons, pelo menos quando
comparados a uma linha de base aleatória:
0,18655387355298308
(46)
1,0
A única coisa que essa abordagem mudaria é a escolha da função de perda. A função de perda
utilizadas na lista 3,21 , categorical_crossentropy, espera que os rótulos de seguir uma
codificação categórica. Com rótulos inteiros, você deve
usar sparse_categorical_crossentropy:
loss = 'sparse_categorical_crossentropy',
metrics = ['acc'])
model = models.Sequential ()
perda = 'categorical_crossentropy',
métricas = ['precisão']]
model.fit (partial_x_train,
partial_y_train,
épocas = 20,
batch_size = 128,
A rede agora atinge a precisão de validação de ~ 71%, uma queda absoluta de 8%. Esta queda é
principalmente devido ao fato de que você está tentando comprimir muita informação
(informação suficiente para recuperar os hiperplanos de separação de 46 classes) em um espaço
intermediário que é muito baixa-dimensional. A rede é capaz de encaixar a maioria das
informações necessárias nessas representações em oito dimensões, mas não em todas elas.
3.5.9. Empacotando
Veja o que você deve tirar deste exemplo:
Se você está tentando classificar pontos de dados entre N aulas, sua rede deve terminar
com uma Densecamada de tamanho N .
Em um problema de classificação multiclasse de rótulo único, sua rede deve terminar
com uma softmaxativação para que ela libere uma distribuição de probabilidade sobre
as N classes de saída.
A crossentropy categórica é quase sempre a função de perda que você deve usar para
esses problemas. Ele minimiza a distância entre as distribuições de probabilidade
produzidas pela rede e a distribuição real dos destinos.
Existem duas maneiras de lidar com rótulos na classificação multiclasse:
Codificando os rótulos por meio de codificação categórica (também conhecida
como codificação de um-quente) e
usando categorical_crossentropycomo uma função de perda
Codificando os rótulos como números inteiros e usando
a sparse_categorical_-crossentropyfunção de perda
Se você precisar classificar os dados em um grande número de categorias, evite a criação de
gargalos na rede devido às camadas intermediárias muito pequenas.
3.6. PREVISÃO DE PREÇOS DA HABITAÇÃO: UM EXEMPLO DE REGRESSÃO
Nota
boston_housing.load_data ()
>>> train_data.shape
(404, 13)
>>> test_data.shape
(102, 13)
Como você pode ver, você tem 404 amostras de treinamento e 102 amostras de teste, cada uma
com 13 características numéricas, como taxa de criminalidade per capita, número médio de
quartos por habitação, acessibilidade a rodovias e assim por diante.
Os alvos são os valores medianos dos lares ocupados pelos proprietários, em milhares de
dólares:
>>> train_targets
[15.2, 42.3, 50. ... 19.4, 19.4, 29.1]
Os preços são tipicamente entre US $ 10.000 e US $ 50.000. Se isso soa barato, lembre-se que
isso foi em meados da década de 1970, e esses preços não são ajustados pela inflação.
train_data - = mean
train_data / = std
test_data - = mean
test_data / = std
Observe que as quantidades usadas para normalizar os dados de teste são calculadas usando os
dados de treinamento. Você nunca deve usar em seu fluxo de trabalho qualquer quantidade
computada nos dados de teste, mesmo para algo tão simples quanto a normalização de dados.
model = models.Sequential () 1
modelo de retorno
1 Como você precisará instanciar o mesmo modelo várias vezes, use uma
função para construí-lo.
A rede termina com uma única unidade e nenhuma ativação (será uma camada linear). Essa é
uma configuração típica para a regressão escalar (uma regressão na qual você está tentando
prever um único valor contínuo). A aplicação de uma função de ativação restringiria o intervalo
que a saída pode levar; por exemplo, se você aplicou uma sigmoidfunção de ativação na última
camada, a rede só poderia aprender a prever valores entre 0 e 1. Aqui, porque a última camada é
puramente linear, a rede fica livre para aprender a prever valores em qualquer intervalo.
Note que você compila a rede com a msefunção loss - erro quadrado médio , o quadrado da
diferença entre as predições e os alvos. Esta é uma função de perda amplamente utilizada para
problemas de regressão.
Você também está monitorando uma nova métrica durante o treinamento: erro absoluto
médio(MAE). É o valor absoluto da diferença entre as previsões e os alvos. Por exemplo, um
MAE de 0,5 sobre esse problema significaria que suas previsões estão em US $ 500 em média.
A melhor prática em tais situações é usar a validação cruzada K-fold (veja a figura
3.11 ). Consiste em dividir os dados disponíveis em partições K (normalmente K = 4 ou 5),
instanciar K modelos idênticos e treinar cada um em partições K - 1 enquanto avalia a partição
restante. O escore de validação para o modelo utilizado é então a média dos escores de
validação K obtidos. Em termos de código, isso é simples.
k = 4
num_epochs = 100
all_scores = []
partial_train_data = np.concatenate (
2
[train_data [: i * num_val_samples],
eixo = 0)
partial_train_targets = np.concatenate (
[train_targets [: i * num_val_samples],
eixo = 0)
model = build_model ()
3
all_scores.append (val_mae)
>>> all_scores
2.9947904173572462
Vamos tentar treinar a rede um pouco mais: 500 épocas. Para manter um registro de quão bem
o modelo faz em cada época, você modificará o loop de treinamento para salvar o log de
pontuação de validação por época.
num_epochs = 500
all_mae_histories = []
partial_train_data = np.concatenate (
2
[train_data [: i * num_val_samples],
eixo = 0)
partial_train_targets = np.concatenate (
[train_targets [: i * num_val_samples],
eixo = 0)
model = build_model ()
3
all_mae_histories.append (mae_history)
Você pode então calcular a média das pontuações MAE por época para todas as dobras.
average_mae_history = [
plt.xlabel ('Épocas')
plt.show ()
Pode ser um pouco difícil ver o gráfico, devido a problemas de dimensionamento e variação
relativamente alta. Vamos fazer o seguinte:
Omita os primeiros 10 pontos de dados, que estão em uma escala diferente do restante
da curva.
Substitua cada ponto por uma média móvel exponencial dos pontos anteriores, para
obter uma curva suave.
Figura 3.13. Validação MAE por época, excluindo os primeiros 10 pontos de dados
smoothed_points = []
if smoothed_points:
outro:
smoothed_points.append (ponto)
return smoothed_points
plt.xlabel ('Épocas')
plt.ylabel ('Validação MAE')
plt.show ()
De acordo com este enredo, a validação do MAE deixa de melhorar significativamente após 80
épocas. Passado esse ponto, você começa overfitting.
Quando terminar de ajustar outros parâmetros do modelo (além do número de épocas, você
também pode ajustar o tamanho das camadas ocultas), é possível treinar um modelo de
produção final em todos os dados de treinamento, com os melhores parâmetros e, em seguida,
observe seu desempenho nos dados de teste.
model = build_model () 1
>>> test_mae_score
2,5532484335057877
3.6.5. Empacotando
Veja o que você deve tirar deste exemplo:
A regressão é feita usando diferentes funções de perda do que as que usamos para
classificação. O erro quadrático médio (MSE) é uma função de perda comumente usada
para regressão.
Da mesma forma, as métricas de avaliação a serem usadas para a regressão diferem
daquelas usadas para classificação; naturalmente, o conceito de precisão não se aplica à
regressão. Uma métrica de regressão comum é o erro absoluto médio (MAE).
Quando os recursos nos dados de entrada possuem valores em intervalos diferentes,
cada recurso deve ser dimensionado de forma independente como uma etapa de pré-
processamento.
Quando há poucos dados disponíveis, usar a validação de dobra em K é uma ótima
maneira de avaliar um modelo de maneira confiável.
Quando há poucos dados de treinamento disponíveis, é preferível usar uma pequena
rede com poucas camadas ocultas (normalmente apenas uma ou duas), para evitar um
overfitting severo.
Resumo do capítulo
Agora você pode manipular os tipos mais comuns de tarefas de aprendizado de máquina
em dados vetoriais: classificação binária, classificação multiclasse e regressão
escalar. As seções “Conclusão” no início do capítulo resumem os pontos importantes
que você aprendeu sobre esses tipos de tarefas.
Você geralmente precisa pré-processar os dados brutos antes de alimentá-los em uma
rede neural.
Quando seus dados tiverem recursos com intervalos diferentes, dimensione cada
recurso de forma independente como parte do pré-processamento.
À medida que o treinamento progride, as redes neurais eventualmente começam a se
sobrepor e obtêm resultados piores em dados nunca antes vistos.
Se você não tiver muitos dados de treinamento, use uma pequena rede com apenas uma
ou duas camadas ocultas, para evitar um overfitting severo.
Se seus dados estiverem divididos em várias categorias, você poderá causar gargalos de
informações se tornar as camadas intermediárias muito pequenas.
A regressão usa diferentes funções de perda e diferentes métricas de avaliação do que a
classificação.
Quando você está trabalhando com poucos dados, a validação do K-fold pode ajudar a
avaliar seu modelo de maneira confiável.
Depois dos três exemplos práticos no capítulo 3 , você deve estar começando a se familiarizar
com a maneira de abordar problemas de classificação e regressão usando redes neurais, e você
testemunhou o problema central do aprendizado de máquina: overfitting. Este capítulo irá
formalizar parte de sua nova intuição em uma sólida estrutura conceitual para atacar e resolver
problemas de aprendizagem profunda. Vamos consolidar todos esses conceitos - avaliação de
modelos, pré-processamento de dados e engenharia de recursos, e combater o ajuste excessivo -
em um fluxo de trabalho detalhado de sete etapas para lidar com qualquer tarefa de aprendizado
de máquina.
Nos nossos exemplos anteriores, você se familiarizou com três tipos específicos de problemas de
aprendizado de máquina: classificação binária, classificação multiclasse e regressão
escalar. Todos os três são exemplos de aprendizado supervisionado , em que o objetivo é
aprender a relação entre as entradas de treinamento e as metas de treinamento.
Por exemplo, os autoencodificadores são uma instância bem conhecida de aprendizado auto-
supervisionado, onde os alvos gerados são a entrada, não modificada. Da mesma forma, tentar
prever o próximo quadro em um vídeo, dados quadros anteriores ou a próxima palavra em um
texto, dadas as palavras anteriores, são exemplos de aprendizado auto-supervisionado
(aprendizado temporariamente supervisionado)., neste caso: a supervisão vem de dados de
entrada futuros). Observe que a distinção entre aprendizado supervisionado, auto-
supervisionado e não supervisionado pode ser confusa às vezes - essas categorias são mais de
um contínuo sem fronteiras sólidas. A aprendizagem auto-supervisionada pode ser
reinterpretada como aprendizagem supervisionada ou não supervisionada, dependendo de se
você prestar atenção ao mecanismo de aprendizagem ou ao contexto de sua aplicação.
Nota
Atualmente, o aprendizado por reforço é principalmente uma área de pesquisa e ainda não teve
significativos sucessos práticos além dos jogos. Com o tempo, no entanto, esperamos ver o
aprendizado de reforço assumir uma gama cada vez maior de aplicativos do mundo real: carros
autônomos, robótica, gerenciamento de recursos, educação e assim por diante. É uma ideia cuja
hora chegou ou virá em breve.
Classificação e regressão envolvem muitos termos especializados. Você se deparou com alguns
deles em exemplos anteriores, e você verá mais deles em capítulos futuros. Eles possuem
definições precisas específicas de aprendizado de máquina, e você deve estar familiarizado com
elas:
Você pode perguntar, por que não ter dois conjuntos: um conjunto de treinamento e um
conjunto de testes? Você treinaria nos dados de treinamento e avaliaria os dados de teste. Muito
mais simples!
A razão é que desenvolver um modelo sempre envolve ajustar sua configuração: por exemplo,
escolher o número de camadas ou o tamanho das camadas (chamadas de hiper-parâmetros do
modelo, para distingui-las dos parâmetros , que são os pesos da rede) . Você faz esse ajuste
usando como sinal de feedback o desempenho do modelo nos dados de validação. Em essência,
esse ajuste é uma forma de aprendizado : uma busca por uma boa configuração em algum
espaço de parâmetros. Como resultado, o ajuste da configuração do modelo com base em seu
desempenho no conjunto de validação pode resultar rapidamente em overfitting para o
conjunto de validação , mesmo que seu modelo nunca seja diretamente treinado nele.
Central para esse fenômeno é a noção de vazamentos de informações . Toda vez que você ajusta
um hiperparâmetro de seu modelo com base no desempenho do modelo no conjunto de
validação, algumas informações sobre os dados de validação vazam no modelo. Se você fizer isso
apenas uma vez, para um parâmetro, muito poucos bits de informações vazarão e seu conjunto
de validação permanecerá confiável para avaliar o modelo. Mas se você repetir isso várias vezes -
executando uma experiência, avaliando o conjunto de validação e modificando seu modelo como
resultado -, você vazará uma quantidade cada vez mais significativa de informações sobre o
conjunto de validação no modelo.
No final do dia, você vai acabar com um modelo que funciona artificialmente bem nos dados de
validação, porque é para isso que você o otimizou. Você se preocupa com o desempenho em
dados completamente novos, não nos dados de validação, portanto, é necessário usar um
conjunto de dados completamente diferente, nunca visto antes, para avaliar o modelo: o
conjunto de dados de teste. Seu modelo não deveria ter acesso a nenhuma informação sobre o
conjunto de testes, mesmo indiretamente.Se alguma coisa sobre o modelo foi ajustada com base
no desempenho do conjunto de testes, então sua medida de generalização será falha.
Dividir seus dados em treinamento, validação e conjuntos de testes pode parecer simples, mas
há algumas maneiras avançadas de fazer isso que podem ser úteis quando há poucos dados
disponíveis. Vamos revisar três receitas de avaliação clássicas: validação simples, validação K-
fold e validação de K-fold iterada com embaralhamento.
Separe alguma fração de seus dados como seu conjunto de testes. Treine os dados restantes e
avalie no conjunto de testes. Como você viu nas seções anteriores, a fim de evitar vazamentos de
informações, você não deve ajustar seu modelo com base no conjunto de testes e,
portanto, também deve reservar um conjunto de validação.
num_validation_samples = 10000
np.random.shuffle (data) 1
model = get_model () 4
model.train (training_data) 4
validation_data])) 5
Este é o protocolo de avaliação mais simples e sofre de uma falha: se houver poucos dados
disponíveis, seus conjuntos de testes e validação podem conter poucas amostras para
representar estatisticamente os dados disponíveis. Isso é fácil de reconhecer: se diferentes
rodadas aleatórias aleatórias dos dados antes da divisão resultarem em medidas muito
diferentes de desempenho do modelo, então você está tendo esse problema. A validação de
dobra em K e a validação de dobra em iteração repetida são duas maneiras de resolver isso,
conforme discutido a seguir.
Validação de K-fold
Com essa abordagem, você divide seus dados em partições K de tamanho igual. Para cada
partição i, treine um modelo nas partições restantes do K - 1 e avalie - o na partição i. Sua
pontuação final é, então, as médias dos K pontuações obtidas. Esse método é útil quando o
desempenho do seu modelo mostra uma variação significativa com base na divisão do teste de
trem. Como validação de hold-out, esse método não o isenta de usar um conjunto de validação
distinto para a calibração do modelo.
Esquematicamente, a validação cruzada K-fold se parece com a figura 4.2 . A Listagem 4.2 mostra
uma implementação simples.
k = 4
num_validation_samples = len (data) // k
np.random.shuffle (data)
validation_scores = []
model = get_model () 3
model.train (training_data)
validation_scores.append (validation_score)
model = get_model () 5
model.train (data) 5
Este é para situações em que você tem relativamente poucos dados disponíveis e você precisa
avaliar seu modelo com a maior precisão possível. Eu acho que é extremamente útil em
competições de Kaggle. Consiste em aplicar várias vezes a validação de dobra em K,
embaralhando os dados toda vez antes de dividir K maneiras. A pontuação final é a média das
pontuações obtidas em cada execução de validação do K-fold. Note que você acaba treinando e
avaliando modelos P × K (onde P é o número de iterações que você usa), o que pode ser muito
caro.
Além da avaliação do modelo, uma questão importante que devemos abordar antes de nos
aprofundarmos no desenvolvimento do modelo é a seguinte: como preparar os dados e os alvos
de entrada antes de alimentá-los em uma rede neural? Muitas técnicas de pré-processamento de
dados e engenharia de recursos são específicas de domínio (por exemplo, específicas para dados
de texto ou dados de imagem); cobriremos os capítulos a seguir quando os encontrarmos em
exemplos práticos. Por enquanto, analisaremos os princípios básicos comuns a todos os
domínios de dados.
Vectorização
Todas as entradas e alvos em uma rede neural devem ser tensores de dados de ponto flutuante
(ou, em casos específicos, tensores de inteiros). Quaisquer que sejam os dados que você precise
processar - som, imagens, texto - você precisa primeiro se transformar em tensores, um passo
chamado vetorização de dados . Por exemplo, nos dois exemplos de classificação de texto
anteriores, começamos a partir do texto representado como listas de inteiros (representando
sequências de palavras), e usamos uma codificação a quente para transformá-las em um tensor
de float32dados. Nos exemplos de classificação de dígitos e previsão de preços de casas, os
dados já vinham em forma vetorizada, então você pode pular essa etapa.
Normalização de valor
No exemplo de classificação por dígitos, você começou a partir de dados de imagem codificados
como inteiros no intervalo de 0 a 255, codificando valores em escala de cinza. Antes de inserir
esses dados em sua rede, você tinha que convertê-los em float32e dividir por 255, para que
você acabasse com valores de ponto flutuante no intervalo de 0 a 1. Da mesma forma, ao prever
os preços da habitação, você começou a partir de recursos que levaram a uma variedade de
intervalos - alguns recursos tinham pequenos valores de ponto flutuante, outros tinham valores
inteiros bastante grandes. Antes de alimentar esses dados em sua rede, você tinha que
normalizar cada recurso de forma independente, de modo que tivesse um desvio padrão de 1 e
uma média de 0.
Em geral, não é seguro inserir dados de rede neural que utilizem valores relativamente grandes
(por exemplo, inteiros de vários dígitos, que são muito maiores do que os valores iniciais obtidos
pelos pesos de uma rede) ou dados heterogêneos (por exemplo, exemplo, dados em que um
recurso está no intervalo de 0 a 1 e outro no intervalo de 100 a 200). Isso pode acionar grandes
atualizações de gradiente que impedirão a convergência da rede. Para facilitar o aprendizado da
sua rede, seus dados devem ter as seguintes características:
Além disso, a seguinte prática de normalização mais rigorosa é comum e pode ajudar, embora
nem sempre seja necessário (por exemplo, você não fez isso no exemplo de classificação por
dígitos):
x - = x.mean (eixo = 0) 1
x / = x.std (eixo = 0)
Às vezes, você pode ter valores ausentes em seus dados. Por exemplo, no exemplo do preço
interno, a primeira característica (a coluna do índice 0 nos dados) era a taxa de criminalidade
per capita. E se esse recurso não estivesse disponível para todas as amostras? Você teria, então,
valores ausentes nos dados de treinamento ou teste.
Em geral, com redes neurais, é seguro inserir valores omissos como 0, com a condição de que 0
já não é um valor significativo. A rede aprenderá com a exposição aos dados que o valor 0
significa dados perdidos e começará a ignorar o valor.
Observe que, se você está esperando valores ausentes nos dados de teste, mas a rede foi treinada
em dados sem nenhum valor ausente, a rede não terá aprendido a ignorar os valores
ausentes! Nessa situação, você deve gerar artificialmente amostras de treinamento com entradas
ausentes: copie algumas amostras de treinamento várias vezes e descarte alguns dos recursos
que você espera estarem ausentes nos dados de teste.
Se você optar por usar os pixels brutos da imagem como dados de entrada, você terá um
problema difícil de aprendizado de máquina em mãos. Você precisará de uma rede neural
convolucional para resolvê-la, e terá que gastar bastante recursos computacionais para treinar a
rede.
Mas se você já entende o problema em um nível alto (você entende como os humanos lêem o
tempo em um relógio), então você pode criar recursos de entrada muito melhores para um
algoritmo de aprendizado de máquina: por exemplo, é fácil escrever Script de linha em Python
para seguir os pixels pretos dos ponteiros do relógio e gerar as coordenadas (x, y) da ponta de
cada mão. Em seguida, um algoritmo simples de aprendizado de máquina pode aprender a
associar essas coordenadas à hora apropriada do dia.
Você pode ir ainda mais longe: faça uma alteração de coordenadas e expresse as coordenadas (x,
y) como coordenadas polares em relação ao centro da imagem. Sua entrada se tornará o
ângulo thetade cada ponteiro do relógio. Neste ponto, seus recursos estão tornando o
problema tão fácil que nenhum aprendizado de máquina é necessário; uma simples operação de
arredondamento e busca de dicionário são suficientes para recuperar a hora aproximada do dia.
Para impedir que um modelo aprenda padrões errôneos ou irrelevantes encontrados nos dados
de treinamento, a melhor solução é obter mais dados de treinamento . Um modelo treinado em
mais dados naturalmente generalizará melhor. Quando isso não for possível, a próxima melhor
solução é modular a quantidade de informações que seu modelo pode armazenar ou adicionar
restrições sobre as informações que podem ser armazenadas. Se uma rede só puder se dar ao
luxo de memorizar um pequeno número de padrões, o processo de otimização forçará o foco nos
padrões mais proeminentes, que têm uma chance melhor de generalizar bem.
Por outro lado, se a rede tiver recursos limitados de memorização, ela não conseguirá aprender
esse mapeamento com tanta facilidade; assim, para minimizar sua perda, ele terá que recorrer a
representações comprimidas de aprendizagem que tenham poder preditivo em relação aos alvos
- precisamente o tipo de representação em que estamos interessados. Ao mesmo tempo, tenha
em mente que você deve usar modelos que têm parâmetros suficientes que não são adequados:
seu modelo não deve passar fome por recursos de memorização. Existe um compromisso a ser
encontrado entre muita capacidade e capacidade insuficiente .
Infelizmente, não existe uma fórmula mágica para determinar o número correto de camadas ou
o tamanho certo para cada camada. Você deve avaliar uma matriz de arquiteturas diferentes (no
seu conjunto de validação, não em seu conjunto de testes, é claro) para encontrar o tamanho
correto do modelo para seus dados. O fluxo de trabalho geral para encontrar um tamanho de
modelo apropriado é começar com relativamente poucas camadas e parâmetros e aumentar o
tamanho das camadas ou adicionar novas camadas até que você veja retornos decrescentes em
relação à perda de validação.
Vamos tentar isso na rede de classificação de revisão de filmes. A rede original é mostrada a
seguir.
model = models.Sequential ()
model = models.Sequential ()
A Figura 4.4 mostra uma comparação das perdas de validação da rede original e da rede
menor. Os pontos são os valores de perda de validação da rede menor, e os cruzamentos são a
rede inicial (lembre-se, uma perda de validação menor sinaliza um modelo melhor).
Figura 4.4. Efeito da capacidade do modelo na perda de validação: experimentando um modelo menor
Como você pode ver, a rede menor inicia o overfitting mais tarde do que a rede de referência
(depois de seis épocas em vez de quatro), e seu desempenho degrada mais lentamente quando
começa a sobreaproveitamento.
Agora, por diversão, vamos adicionar a este benchmark uma rede que tem muito mais
capacidade - muito mais do que o problema exige.
model = models.Sequential ()
A Figura 4.5 mostra como as maiores tarifas de rede são comparadas à rede de referência. Os
pontos são os valores de perda de validação da rede maior e os cruzamentos são a rede inicial.
Figura 4.5. Efeito da capacidade do modelo na perda de validação: experimentando um modelo maior
A rede maior começa a sobreaproveitar quase imediatamente, depois de apenas uma época, e se
adapta muito mais severamente. Sua perda de validação também é mais ruidosa.
Enquanto isso, a figura 4.6 mostra as perdas de treinamento para as duas redes. Como você pode
ver, a rede maior recebe sua perda de treinamento perto de zero muito rapidamente. Quanto
mais capacidade a rede tiver, mais rapidamente ela poderá modelar os dados de treinamento
(resultando em uma baixa perda de treinamento), mas mais suscetível será o superajuste
(resultando em uma grande diferença entre o treinamento e a perda de validação).
Figura 4.6 Efeito da capacidade do modelo na perda de treino: experimentando um modelo maior
model = models.Sequential ()
ativação = 'relu'))
A Figura 4.7 mostra o impacto da penalidade de regularização de L2. Como você pode ver, o
modelo com regularização L2 (pontos) tornou-se muito mais resistente ao overfitting do que o
modelo de referência (cruzamentos), embora ambos os modelos tenham o mesmo número de
parâmetros.
Figura 4.7. Efeito da regularização do peso L2 na perda de validação
Como alternativa à regularização L2, você pode usar um dos seguintes reguladores de peso
Keras.
regularizadores.l1 (0,001) 1
1 regularização de L1
2 Regularização simultânea de L1 e L2
No tempo de teste, reduzimos a saída pela taxa de desistência. Aqui, nós aumentamos em 0,5
(porque anteriormente descartamos metade das unidades):
layer_output * = 0,5 1
1 no tempo de teste
Observe que esse processo pode ser implementado executando as duas operações no momento
do treinamento e deixando a saída inalterada no momento do teste, o que geralmente é o modo
como é implementado na prática (consulte a figura 4.8 ):
layer_output / = 0.5 2
1 no tempo de treinamento
2 Observe que estamos ampliando bastante o dimensionamento neste caso.
Figura 4.8. Dropout aplicado a uma matriz de ativação no momento do treinamento, com o reescalonamento acontecendo
durante o treinamento. No tempo de teste, a matriz de ativação permanece inalterada.
Essa técnica pode parecer estranha e arbitrária. Por que isso ajudaria a reduzir o
overfitting? Hinton diz que foi inspirado, entre outras coisas, por um mecanismo de prevenção
de fraude usado pelos bancos. Em suas próprias palavras, “fui ao meu banco. Os caixas
continuaram mudando e perguntei a um deles por quê. Ele disse que não sabia, mas eles se
mudaram muito. Achei que deveria ser porque exigiria a cooperação entre os funcionários para
fraudar com sucesso o banco. Isso me fez perceber que remover aleatoriamente um subconjunto
diferente de neurônios em cada exemplo evitaria conspirações e, assim, reduziria o overfitting
”. [ 1 ] A ideia central é que introduzir ruído nos valores de saída de uma camada pode quebrar
padrões de acaso que não são significante (o que Hinton se refere comoconspirações ), que a
rede começará a memorizar se não houver ruído.
Veja o tópico do Reddit “AMA: Nós somos a equipe do Google Brain. Adoraríamos responder suas perguntas sobre aprendizado de máquina
”, http://mng.bz/XrsS .
Em Keras, você pode introduzir o dropout em uma rede através da Dropoutcamada, que é
aplicada à saída da camada logo antes dela:
Vamos adicionar duas Dropoutcamadas na rede do IMDB para ver o quão bem elas estão em
reduzir o overfitting.
Listagem 4.8. Adicionando o dropout à rede do IMDB
model = models.Sequential ()
A figura 4.9 mostra um gráfico dos resultados. Mais uma vez, isso é uma clara melhoria em
relação à rede de referência.
Para recapitular, estas são as formas mais comuns de evitar overfitting em redes neurais:
Nesta seção, apresentaremos um plano universal que você pode usar para atacar e resolver
qualquer problema de aprendizado de máquina. O projeto une os conceitos sobre os quais você
aprendeu neste capítulo: definição de problemas, avaliação, engenharia de recursos e combate
ao overfitting.
Você não pode passar para o próximo estágio até saber quais são suas entradas e saídas e quais
dados serão usados. Esteja ciente das hipóteses que você faz neste estágio:
Você supõe que suas saídas podem ser previstas de acordo com suas entradas.
Você supõe que seus dados disponíveis são suficientemente informativos para aprender
a relação entre entradas e saídas.
Até que você tenha um modelo de trabalho, estas são meras hipóteses, esperando para serem
validadas ou invalidadas. Nem todos os problemas podem ser resolvidos; só porque você
montou exemplos de entradas X e metas Y não significa que X contém informações suficientes
para prever Y. Por exemplo, se você está tentando prever os movimentos de uma ação no
mercado de ações, dado seu recente histórico de preços, é improvável que você tenha sucesso,
porque o histórico de preços não contém muita informação preditiva.
Uma classe de problemas insolúveis dos quais você deve estar ciente são problemas não
estacionários. Suponha que você esteja tentando criar um mecanismo de recomendação para
roupas, esteja treinando em um mês de dados (agosto) e queira começar a gerar recomendações
no inverno. Uma grande questão é que os tipos de roupas que as pessoas compram mudam de
estação para estação: a compra de roupas é um fenômeno não-estacionário na escala de alguns
meses. O que você está tentando modelar mudanças ao longo do tempo. Nesse caso, o
movimento correto é treinar constantemente seu modelo em dados do passado recente, ou
reunir dados em uma escala de tempo em que o problema é estacionário. Para um problema
cíclico como a compra de roupas, dados de alguns anos serão suficientes para capturar a
variação sazonal - mas lembre-se de fazer da época do ano uma entrada do seu modelo!
Tenha em mente que o aprendizado de máquina só pode ser usado para memorizar padrões que
estão presentes em seus dados de treinamento. Você só pode reconhecer o que viu antes. Usar o
aprendizado de máquina treinado em dados passados para prever o futuro é supor que o futuro
se comportará como o passado. Isso geralmente não é o caso.
Para problemas de classificação balanceada, em que todas as classes são igualmente prováveis, a
precisão e a área sob a curva característica de operação do receptor (ROC AUC) são métricas
comuns. Para problemas com desequilíbrio de classe, você pode usar precisão e
recuperação. Para problemas de classificação ou classificação multilabel, você pode usar a
precisão média média. E não é incomum ter que definir sua própria métrica personalizada para
medir o sucesso. Para ter uma noção da diversidade de métricas de sucesso de aprendizado de
máquina e como elas se relacionam com diferentes domínios de problemas, é útil navegar nas
competições de ciência de dados no Kaggle ( https://kaggle.com ); eles exibem uma ampla gama
de problemas e métricas de avaliação.
4.5.3. Decidindo sobre um protocolo de avaliação
Uma vez que você saiba o que você está procurando, você deve estabelecer como você medirá
seu progresso atual. Analisamos anteriormente três protocolos de avaliação comuns:
Basta escolher um desses. Na maioria dos casos, o primeiro funcionará bem o suficiente.
Como você viu anteriormente, seus dados devem ser formatados como tensores.
Os valores obtidos por esses tensores devem normalmente ser redimensionados para
valores pequenos: por exemplo, no intervalo [-1, 1] ou [0, 1].
Se recursos diferentes usarem valores em intervalos diferentes (dados heterogêneos), os
dados deverão ser normalizados.
Você pode querer fazer alguns recursos de engenharia, especialmente para problemas
com pequenos dados.
Quando seus tensores de dados de entrada e dados de destino estiverem prontos, você poderá
começar a treinar modelos.
4.5.5. Desenvolvendo um modelo que faz melhor que uma linha de base
Seu objetivo nesse estágio é obter poder estatístico : isto é, desenvolver um modelo pequeno que
seja capaz de vencer uma linha de base idiota. No exemplo de classificação de dígitos MNIST,
qualquer coisa que obtiver uma precisão maior que 0,1 pode ser considerada como tendo poder
estatístico; no exemplo do IMDB, é qualquer coisa com uma precisão maior que 0,5.
Note que nem sempre é possível obter poder estatístico. Se você não conseguir superar uma
linha de base aleatória depois de tentar várias arquiteturas razoáveis, pode ser que a resposta à
pergunta que você está fazendo não esteja presente nos dados de entrada. Lembre-se que você
faz duas hipóteses:
Você supõe que suas saídas podem ser previstas de acordo com suas entradas.
Você supõe que os dados disponíveis são suficientemente informativos para aprender a
relação entre entradas e saídas.
Pode ser que essas hipóteses sejam falsas e, nesse caso, você deve voltar à prancheta.
Supondo que as coisas vão bem, você precisa fazer três opções-chave para construir seu
primeiro modelo de trabalho:
Ativação da última camada - Isso estabelece restrições úteis na saída da rede. Por
exemplo, o exemplo de classificação do IMDB usadosigmoidna última camada; o
exemplo de regressão não usou nenhuma ativação da última camada; e assim por
diante.
Função de perda - isso deve corresponder ao tipo de problema que você está
tentando resolver. Por exemplo, o exemplo do IMDB usadobinary_crossentropy, o
exemplo de regressão usadomsee assim por diante.
Configuração de otimização - qual otimizador você usará? Qual será a sua taxa de
aprendizado? Na maioria dos casos, é seguro acompanharrmspropsua taxa de
aprendizado padrão.
Em relação à escolha de uma função de perda, observe que nem sempre é possível otimizar
diretamente a métrica que mede o sucesso de um problema. Às vezes não há uma maneira fácil
de transformar uma métrica em uma função de perda; funções de perda, afinal, precisam ser
computáveis, dado apenas um mini lote de dados (idealmente, uma função de perda deve ser
computável para um único ponto de dados) e deve ser diferenciável (caso contrário, não é
possível usar retropropagação para treinar sua rede). Por exemplo, a métrica de classificação
amplamente utilizada ROC AUC não pode ser diretamente otimizada. Portanto, em tarefas de
classificação, é comum otimizar para uma métrica proxy de ROC AUC, como crossentropy. Em
geral, você pode esperar que quanto menor a crossentropy, mais alta será a ROC AUC.
A Tabela 4.1 pode ajudá-lo a escolher uma ativação da última camada e uma função de perda
para alguns tipos de problemas comuns.
Tabela 4.1. Escolhendo a função correta de ativação e perda da última camada para o seu modelo
Para descobrir o tamanho do modelo que você precisará, você deve desenvolver um modelo que
seja overfits. Isso é bastante fácil:
1. Adicione camadas.
2. Faça as camadas maiores.
3. Treinar para mais épocas.
A próxima etapa é começar a regularizar e ajustar o modelo, para chegar o mais próximo
possível do modelo ideal que nem os modelos nem os superpesados.
Adicione o dropout.
Experimente diferentes arquiteturas: adicione ou remova camadas.
Adicione a regularização L1 e / ou L2.
Experimente diferentes hiperparâmetros (como o número de unidades por camada ou a
taxa de aprendizado do otimizador) para encontrar a configuração ideal.
Como opção, faça uma iteração na engenharia de recursos: adicione novos recursos ou
remova recursos que não parecem ser informativos.
Esteja ciente do seguinte: toda vez que você usa o feedback de seu processo de validação para
ajustar seu modelo, você vaza informações sobre o processo de validação no modelo. Repetida
apenas algumas vezes, isso é inócuo; mas feito sistematicamente ao longo de várias iterações, ele
acabará fazendo com que seu modelo se sobreponha ao processo de validação (mesmo que
nenhum modelo seja diretamente treinado em qualquer um dos dados de validação). Isso torna
o processo de avaliação menos confiável.
Depois de desenvolver uma configuração de modelo satisfatória, você pode treinar seu modelo
de produção final em todos os dados disponíveis (treinamento e validação) e avaliá-lo uma
última vez no conjunto de testes. Se o desempenho no conjunto de testes for significativamente
pior que o desempenho medido nos dados de validação, isso pode significar que o procedimento
de validação não era confiável, ou que você começou a ajustar demais os dados de validação
enquanto ajustava os parâmetros do modelo. Nesse caso, convém alternar para um protocolo de
avaliação mais confiável (como a validação de dobra repetida).
Resumo do capítulo
Defina o problema em questão e os dados sobre os quais você treinará. Colete esses
dados ou anote-os com rótulos, se necessário.
Escolha como você medirá o sucesso em seu problema. Quais métricas você monitorará
nos seus dados de validação?
Determine seu protocolo de avaliação: validação de retenção? Validação de K-fold? Qual
parte dos dados você deve usar para validação?
Desenvolva um primeiro modelo que faça melhor do que uma linha de base básica: um
modelo com poder estatístico.
Desenvolva um modelo que overfits.
Regularize seu modelo e ajuste seus hiperparâmetros, com base no desempenho nos
dados de validação. Muitas pesquisas sobre aprendizado de máquina tendem a se
concentrar apenas nesta etapa - mas mantenham o quadro geral em mente.
Parte 2. Aprendizagem profunda na prática
Capítulos 5 - 9 vai ajudar você a ganhar intuição prática sobre como resolver problemas do
mundo real, usando o aprendizado profundo, e irá familiarizá-lo com as melhores práticas
profundas-learning essenciais. A maioria dos exemplos de código no livro está concentrada
neste segundo semestre.
Este capítulo apresenta redes neurais convolucionais, também conhecidas como " convnets" ,
um tipo de modelo de aprendizagem profunda quase universalmente usado em aplicações de
visão computacional. Você aprenderá a aplicar convnets a problemas de classificação de
imagens - em particular aqueles que envolvem conjuntos de dados de treinamento pequenos,
que são o caso de uso mais comum se você não for uma grande empresa de tecnologia.
Estamos prestes a mergulhar na teoria do que são as conversinhas e por que elas tiveram tanto
sucesso em tarefas de visão computacional. Mas primeiro, vamos dar uma olhada prática em um
exemplo simples de convnet. Ele usa uma convnet para classificar dígitos MNIST, uma tarefa
que realizamos no capítulo 2 usando uma rede densamente conectada (a precisão do nosso teste
era de 97,8%). Mesmo que a convnet seja básica, sua precisão vai sair da água do modelo
densamente conectado do capítulo 2 .
As linhas de código a seguir mostram como é uma convnet básica. É uma pilha
de Conv2De MaxPooling2Dcamadas. Você verá em um minuto exatamente o que eles fazem.
model = models.Sequential ()
>>> model.summary ()
________________________________________________________________
================================================== ==============
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
================================================== ==============
O próximo passo é alimentar o último tensor de saída (de forma (3, 3, 64)) em uma rede de
classificadores densamente conectada, como aqueles com os quais você já está familiarizado:
uma pilha de Densecamadas. Esses classificadores processam vetores, que são 1D, enquanto a
saída de corrente é um tensor 3D. Primeiro temos que achatar as saídas 3D para 1D e depois
adicionar algumas Densecamadas na parte superior.
>>> model.summary ()
================================================== ==============
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
================================================== ==============
Como você pode ver, as (3, 3, 64)saídas são achatadas em vetores de forma (576,)antes de
passar por duas Densecamadas.
Agora, vamos treinar a convnet nos dígitos MNIST. Vamos reutilizar muito do código do
exemplo MNIST no capítulo 2 .
perda = 'categorical_crossentropy',
métricas = ['precisão']]
>>> test_acc
0,99080000000000001
Enquanto a rede densamente conectada do capítulo 2 teve uma precisão de teste de 97,8%, a
convnet básica tem uma precisão de teste de 99,3%: diminuímos a taxa de erro em 68%
(relativa). Não é ruim!
Mas por que esta convnet simples funciona tão bem, em comparação com um modelo
densamente conectado? Para responder a isso, vamos mergulhar no que
as camadas Conv2De MaxPooling2Dfazem.
As convoluções operam sobre tensores 3D, chamados de mapas de características , com dois
eixos espaciais ( altura e largura ), bem como um eixo de profundidade (também chamado
de eixo dos canais ). Para uma imagem RGB, a dimensão do eixo de profundidade é 3, porque a
imagem tem três canais de cores: vermelho, verde e azul. Para uma imagem em preto e branco,
como os dígitos MNIST, a profundidade é 1 (níveis de cinza). A operação de convolução extrai os
patches de seu mapa de recursos de entrada e aplica a mesma transformação a todos esses
patches, produzindo um mapa de recursos de saída. Este mapa de recursos de saída ainda é um
tensor 3D: tem largura e altura. Sua profundidade pode ser arbitrária, porque a profundidade de
saída é um parâmetro da camada, e acanais diferentes nesse eixo de profundidade não mais
representam cores específicas como na entrada RGB; em vez disso, eles representam filtros . Os
filtros codificam aspectos específicos dos dados de entrada: em um nível alto, um único filtro
pode codificar o conceito “presença de uma face na entrada”, por exemplo.
Observe que a largura e a altura da saída podem diferir da largura e da altura da entrada. Eles
podem diferir por dois motivos:
Efeitos de borda, que podem ser neutralizados pelo preenchimento do mapa de recursos
de entrada
O uso de strides , que eu vou definir em um segundo
Considere um mapa de recursos 5 × 5 (total de 25 tiles). Existem apenas 9 blocos em torno dos
quais você pode centralizar uma janela 3 × 3, formando uma grade 3 × 3 (veja a figura
5.5 ). Assim, o mapa de recursos de saída será 3 × 3. Ele encolhe um pouco: exatamente dois
blocos ao lado de cada dimensão, neste caso. Você pode ver este efeito de borda em ação no
exemplo anterior: você começa com 28 × 28 entradas, que se tornam 26 × 26 após a primeira
camada de convolução.
Figura 5.5 Locais válidos de 3 × 3 patches em um mapa de recursos de entrada de 5 × 5
Se você deseja obter um mapa de recursos de saída com as mesmas dimensões espaciais da
entrada, use o preenchimento . O preenchimento consiste em adicionar um número apropriado
de linhas e colunas em cada lado do mapa de recursos de entrada, de modo a possibilitar a
instalação de janelas de convolução central em torno de cada bloco de entrada. Para uma janela
3 × 3, você adiciona uma coluna à direita, uma coluna à esquerda, uma linha na parte superior e
uma linha na parte inferior. Para uma janela 5 × 5, você adiciona duas linhas (veja a figura 5.6 ).
O outro fator que pode influenciar o tamanho da saída é a noção de passadas . A descrição da
convolução até agora assumiu que os blocos centrais das janelas de convolução são todos
contíguos. Mas a distância entre duas janelas sucessivas é um parâmetro da convolução,
chamado seu stride , cujo padrão é 1. É possível ter convoluções distribuídas : convoluções com
um passo maior que 1. Na figura 5.7 , é possível ver as correções extraídas por um Convolução 3
× 3 com passada 2 sobre uma entrada de 5 × 5 (sem preenchimento).
Figura 5.7. Patches de convolução 3 × 3 com 2 × 2 passos
Usando stride 2 significa que a largura e a altura do mapa de recursos são reduzidos em um
fator de 2 (além de quaisquer alterações induzidas pelos efeitos de borda). Convoluções
circulares raramente são usadas na prática, embora possam ser úteis para alguns tipos de
modelos; é bom estar familiarizado com o conceito.
O agrupamento máximo consiste em extrair janelas dos mapas de recursos de entrada e gerar o
valor máximo de cada canal. É conceitualmente semelhante à convolução, exceto que, em vez de
transformar os patches locais por meio de uma transformação linear aprendida (o kernel de
convolução), eles são transformados por meio de uma maxoperação tensorial codificada . Uma
grande diferença da convolução é que o pool máximo geralmente é feito com janelas 2 × 2 e
stride 2, para reduzir a resolução dos mapas de recursos por um fator de 2. Por outro lado, a
convolução é tipicamente feita com janelas 3 × 3 e não stride (passo 1).
Por que reduzir o recurso de mapas dessa maneira? Por que não remover as camadas de pool
máximo e manter os mapas de recursos razoavelmente grandes em todo o caminho? Vamos ver
essa opção. A base convolucional do modelo ficaria assim:
model_no_max_pool = models.Sequential ()
>>> model_no_max_pool.summary ()
================================================== ==============
________________________________________________________________
________________________________________________________________
================================================== ==============
Observe que o pool máximo não é a única maneira de obter essa redução de resolução. Como
você já sabe, você também pode usar strides na camada de convolução anterior. E você podeuse
pool médio em vez de pool máximo, em que cada patch de entrada local é transformado,
tomando o valor médio de cada canal sobre o patch, em vez do valor máximo. Mas o pool
máximo tende a funcionar melhor do que essas soluções alternativas. Em suma, a razão é que os
recursos tendem a codificar a presença espacial de algum padrão ou conceito sobre os diferentes
blocos do mapa de recursos (portanto, o termo mapa de recursos ), e é mais informativo
observar a presença máxima de diferentes recursos do que na sua presença média. Portanto, a
estratégia de subamostragem mais razoável é primeiro produzir mapas densos de recursos (por
meio de convoluções não-carregadas) e então observar a ativação máxima dos recursos em
pequenos trechos, em vez de olhar para janelas mais esparsas das entradas (por meio de
convoluções escalonadas) ou calcular a média de entrada correções, o que pode causar a perda
ou a diluição de informações de presença de recursos.
Nesse ponto, você deve compreender os fundamentos das redes de convecção - mapas de
recursos, convolução e pool máximo - e saber como construir uma pequena convnet para
resolver um problema de brinquedo, como a classificação de dígitos MNIST. Agora vamos
passar para aplicativos mais úteis e práticos.
Ter que treinar um modelo de classificação de imagens usando muito poucos dados é uma
situação comum, que você provavelmente encontrará na prática se já fez a visão computacional
em um contexto profissional. Um número “pequeno” de amostras pode significar de algumas
centenas a algumas dezenas de milhares de imagens. Como exemplo prático, nos
concentraremos na classificação de imagens como cães ou gatos, em um conjunto de dados
contendo 4.000 fotos de gatos e cães (2.000 gatos, 2.000 cães). Usaremos 2.000 fotos para
treinamento - 1.000 para validação e 1.000 para testes.
Nesta seção, analisaremos uma estratégia básica para resolver esse problema: treinar um novo
modelo a partir do zero usando os poucos dados que você tem. Você começará ingenuamente
treinando uma pequena convenção nas 2.000 amostras de treinamento, sem qualquer
regularização, para definir uma linha de base para o que pode ser alcançado. Isso levará você a
uma precisão de classificação de 71%. Nesse ponto, a questão principal será overfitting. Em
seguida, introduziremos o aumento de dados , uma técnica poderosa para atenuar o overfitting
na visão computacional. Usando o aumento de dados, você aprimora a rede para alcançar uma
precisão de 82%.
Na próxima seção, analisaremos duas outras técnicas essenciais para aplicar o aprendizado
profundo a conjuntos de dados pequenos: extração de recursos com uma rede pré-
treinada (que atingirá uma precisão de 90% a 96%) e ajuste fino de uma rede pré-planejada (
isto te levará a uma precisão final de 97%). Juntas, essas três estratégias - treinar um pequeno
modelo a partir do zero, fazer extração de recurso usando um modelo pré-treinado e ajustar um
modelo pré-treinado - constituirão sua futura caixa de ferramentas para resolver o problema de
classificação de imagens com pequenos conjuntos de dados.
Além disso, os modelos de aprendizagem profunda são altamente reutilizáveis por natureza:
você pode usar um modelo de classificação de imagem ou de fala para texto treinado em um
conjunto de dados em grande escala e reutilizá-lo em um problema significativamente diferente
com pequenas alterações. Especificamente, no caso da visão computacional, muitos modelos
pré-treinados (geralmente treinados no conjunto de dados Image-Net) estão agora disponíveis
publicamente para download e podem ser usados para inicializar modelos poderosos de visão
com poucos dados. Isso é o que você fará na próxima seção. Vamos começar por colocar as mãos
nos dados.
As imagens são JPEGs em cores de resolução média. A Figura 5.8 mostra alguns exemplos.
Figura 5.8. Amostras do conjunto de dados Dogs vs. Cats. Os tamanhos não foram modificados: as amostras são heterogêneas
em tamanho, aparência e assim por diante.
Sem surpresa, a competição de cães e gatos Kaggle em 2013 foi vencida pelos participantes que
usaram as convnets. As melhores entradas alcançaram até 95% de precisão. Neste exemplo, você
ficará bastante próximo a essa precisão (na próxima seção), embora você treine seus modelos
com menos de 10% dos dados disponíveis para os concorrentes.
Este conjunto de dados contém 25.000 imagens de cães e gatos (12.500 de cada classe) e é 543
MB (compactado). Depois de baixá-lo e descompactá-lo, você criará um novo conjunto de dados
contendo três subconjuntos: um conjunto de treinamento com 1.000 amostras de cada classe,
um conjunto de validação com 500 amostras de cada classe e um conjunto de teste com 500
amostras de cada classe.
os.mkdir (base_dir)
os.mkdir (train_dir)
os.mkdir (validation_dir)
os.mkdir (test_dir)
os.mkdir (train_cats_dir) 4
os.mkdir (train_dogs_dir) 5
os.mkdir (validation_cats_dir) 6
os.mkdir (validation_dogs_dir) 7
os.mkdir (test_cats_dir) 8
os.mkdir (test_dogs_dir) 9
fnames = ['cat. {} .jpg'.format (i) para i no intervalo (1000)]
10
Como verificação de integridade, vamos contar quantas fotos estão em cada divisão de
treinamento (trem / validação / teste):
Então você realmente tem 2.000 imagens de treinamento, 1.000 imagens de validação e 1.000
imagens de teste. Cada divisão contém o mesmo número de amostras de cada classe: trata-se de
um problema de classificação binária equilibrado, o que significa que a precisão da classificação
será uma medida apropriada de sucesso.
Mas como você está lidando com imagens maiores e um problema mais complexo, você
aumentará a sua rede de acordo: ele terá mais um estágio Conv2D+ MaxPooling2D. Isso serve
tanto para aumentar a capacidade da rede quanto para reduzir ainda mais o tamanho dos mapas
de recursos, para que eles não sejam excessivamente grandes quando você alcança
a Flattencamada. Aqui, porque você começa a partir de entradas de tamanho 150 × 150 (uma
escolha um pouco arbitrária), você acaba com mapas de recursos de tamanho 7 × 7 antes
da Flattencamada.
Nota
A profundidade dos mapas de recursos aumenta progressivamente na rede (de 32 para 128),
enquanto o tamanho dos mapas de recursos diminui (de 148 × 148 para 7 × 7). Este é um padrão
que você verá em quase todas as convnets.
Como você está atacando um problema de classificação binária, você terminará a rede com uma
única unidade (uma Densecamada de tamanho 1) e uma sigmoidativação. Esta unidade
codificará a probabilidade de que a rede esteja olhando para uma classe ou outra.
Listagem 5.5. Instanciando uma pequena convnet para classificação de cães vs. gatos
Vejamos como as dimensões dos mapas de recursos mudam a cada camada sucessiva:
>>> model.summary ()
================================================== ==============
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
================================================== ==============
Para a etapa de compilação, você irá com o RMSpropotimizador, como de costume. Como você
terminou a rede com uma única unidade sigmoid, você usará crossentropy binária como a perda
(como lembrete, confira a tabela 4.1 para uma folha de cheats sobre qual função de perda usar
em várias situações).
metrics = ['acc'])
Pode parecer um pouco assustador, mas felizmente Keras tem utilitários para cuidar desses
passos automaticamente. Keras tem um módulo com ferramentas auxiliares de processamento
de imagem, localizado em keras.preprocessing.image. Em particular, ele contém a
classe ImageDataGenerator, que permite configurar rapidamente geradores Python que
podem transformar automaticamente os arquivos de imagem no disco em lotes de tensores pré-
processados. Isso é o que você vai usar aqui.
Listagem 5.7. Usando ImageDataGeneratorpara ler imagens de diretórios
train_generator = train_datagen.flow_from_directory (
train_dir, 2
batch_size = 20,
class_mode = 'binary') 4
validation_generator = test_datagen.flow_from_directory (
validation_dir,
batch_size = 20,
class_mode = 'binary')
Um gerador Python é um objeto que age como um iterador: é um objeto que você pode usar
com o for... inoperador. Geradores são construídos usando o yieldoperador.
i = 0
enquanto verdadeiro:
i + = 1
rendimento i
se item> 4:
pausa
Imprime isto:
1 2 3 4 5
Vejamos a saída de um desses geradores: ele gera lotes de 150 x 150 imagens RGB (forma (20,
150, 150, 3)) e rótulos binários (forma (20,)). São 20amostras em cada lote (o tamanho do
lote). Observe que o gerador gera esses lotes indefinidamente: faz um loop indefinidamente
sobre as imagens na pasta de destino. Por esse motivo, você precisa breakdo loop de iteração
em algum momento:
>>> break
Vamos ajustar o modelo aos dados usando o gerador. Você faz isso usando
o fit_generatormétodo, o equivalente fita geradores de dados como este. Ele espera como
seu primeiro argumento um gerador Python que produzirá lotes de entradas e destinos
indefinidamente, como este. Como os dados estão sendo gerados indefinidamente, o modelo
Keras precisa saber quantas amostras extrair do gerador antes de declarar uma época. Este é o
papel do steps_per_epochargumento: depois de ter tirado steps_per_epochlotes do
gerador - isto é, depois de ter corrido para steps_per_epochdegraus de gradiente
descendente - o processo de adaptação irá para a próxima época. Nesse caso, os lotes são 20
amostras, portanto, serão necessários 100 lotes até você ver sua meta de 2.000 amostras.
history = model.fit_generator (
train_generator,
steps_per_epoch = 100,
épocas = 30,
validation_data = validation_generator,
validation_steps = 50)
É uma boa prática sempre salvar seus modelos após o treinamento.
model.save ('cats_and_dogs_small_1.h5')
Vamos traçar a perda e a precisão do modelo sobre os dados de treinamento e validação durante
o treinamento (ver figuras 5.9 e 5.10 ).
plt.legend ()
plt.figure ()
plt.legend ()
plt.show ()
Como você tem relativamente poucas amostras de treinamento (2.000), o overfitting será sua
preocupação número um. Você já sabe sobre várias técnicas que podem ajudar a atenuar o
overfitting, como o abandono e a perda de peso (regularização de L2). Agora vamos trabalhar
com um novo, específico para visão computacional e usado quase que universalmente ao
processar imagens com modelos de aprendizagem profunda: aumento de dados .
datagen = ImageDataGenerator (
rotation_range = 40,
width_shift_range = 0,2,
height_shift_range = 0,2,
shear_range = 0,2,
zoom_range = 0,2,
horizontal_flip = True,
Estas são apenas algumas das opções disponíveis (para mais informações, consulte a
documentação do Keras). Vamos rapidamente passar por cima deste código:
x = image.img_to_array (img) 4
i = 0 6
para lote em datagen.flow (x, batch_size = 1): 6
pt.figura (i) 6
i + = 1 6
se i % 4 == 0: 6
quebra 6
plt.show ()
Se você treinar uma nova rede usando essa configuração de aumento de dados, a rede nunca
verá a mesma entrada duas vezes. Mas as entradas que ele vê ainda estão pesadamente inter-
relacionadas, porque elas vêm de um pequeno número de imagens originais - você não pode
produzir novas informações, você pode apenas remixar informações existentes. Como tal, isso
pode não ser suficiente para se livrar completamente do overfitting. Para combater ainda mais o
overfitting, você também adicionará uma Dropoutcamada ao seu modelo, logo antes do
classificador densamente conectado.
model = models.Sequential ()
metrics = ['acc'])
train_datagen = ImageDataGenerator (
reescala = 1. / 255,
rotation_range = 40,
width_shift_range = 0,2,
height_shift_range = 0,2,
shear_range = 0,2,
zoom_range = 0,2,
horizontal_flip = True,)
train_generator = train_datagen.flow_from_directory (
train_dir, 2
batch_size = 32,
class_mode = 'binary') 4
validation_generator = test_datagen.flow_from_directory (
validation_dir,
batch_size = 32,
class_mode = 'binary')
history = model.fit_generator (
train_generator,
steps_per_epoch = 100,
épocas = 100,
validation_data = validation_generator,
validation_steps = 50)
model.save ('cats_and_dogs_small_2.h5')
E vamos plotar os resultados novamente: veja as figuras 5.12 e 5.13 . Graças ao aumento e ao
abandono de dados, você não está mais com overfitting: as curvas de treinamento estão
acompanhando de perto as curvas de validação. Agora você alcança uma precisão de 82%, uma
melhoria relativa de 15% em relação ao modelo não regularizado.
Nesse caso, vamos considerar uma grande convnet treinada no conjunto de dados do ImageNet
(1,4 milhão de imagens rotuladas e 1.000 classes diferentes). O ImageNet contém muitas classes
de animais, incluindo diferentes espécies de gatos e cães, e você pode esperar ter um bom
desempenho no problema de classificação entre cães e gatos.
Você usará a arquitetura VGG16, desenvolvida por Karen Simonyan e Andrew Zisserman em
2014; é uma arquitetura convnet simples e amplamente usada para o ImageNet. [ 1 ] Embora seja
um modelo antigo, longe do atual estado da arte e um pouco mais pesado do que muitos outros
modelos recentes, eu o escolhi porque sua arquitetura é semelhante ao que você já conhece e é
fácil de entender sem introduzir qualquer novos conceitos. Esse pode ser seu primeiro encontro
com um desses nomes de modelos bonitinhos - VGG, ResNet, Inception, Inception-ResNet,
Xception e assim por diante; você vai se acostumar com eles, porque eles vão aparecer com
frequência se você continuar fazendo aprendizado profundo para visão computacional.
1
Karen Simonyan e Andrew Zisserman, “Redes Convolucionais Muito Profundas para Reconhecimento de Imagem em Larga Escala”, arXiv
(2014), https://arxiv.org/abs/1409.1556 .
Existem duas maneiras de usar uma rede pré-estratificada: extração de recursos e ajuste
fino . Nós vamos cobrir os dois. Vamos começar com a extração de recursos.
Como você viu anteriormente, os convnets usados para classificação de imagens compreendem
duas partes: eles começam com uma série de camadas de agrupamento e convolução e
terminam com um classificador densamente conectado. A primeira parte é chamada de base
convolucional do modelo. No caso de convnets, a extração de características consiste em tomar
a base convolucional de umrede previamente treinada, executando os novos dados através dela e
treinando um novo classificador em cima da saída (veja a figura 5.14 ).
Por que apenas reutilizar a base convolucional? Você poderia reutilizar o classificador
densamente conectado também? Em geral, isso deve ser evitado. A razão é que as
representações aprendidas pela base convolucional tendem a ser mais genéricas e, portanto,
mais reutilizáveis: os mapas de características de uma convnet são mapas de presença de
conceitos genéricos sobre uma imagem, que provavelmente serão úteis independentemente da
visão computacional. problema na mão. Mas as representações aprendidas pelo classificador
serão necessariamente específicas para o conjunto de classes nas quais o modelo foi treinado -
elas conterão apenas informações sobre a probabilidade de presença dessa ou daquela classe em
toda a imagem. Além disso, as representações encontradas em camadas densamente conectadas
não contêm mais nenhuma informação sobreonde os objetos estão localizados na imagem de
entrada: essas camadas eliminam a noção de espaço, enquanto a localização do objeto ainda é
descrita pelos mapas de características convolucionais. Para problemas em que a localização do
objeto é importante, os recursos densamente conectados são em grande parte inúteis.
Nesse caso, como o conjunto de classes ImageNet contém várias classes de cães e gatos, é
provável que seja benéfico reutilizar as informações contidas nas camadas densamente
conectadas do modelo original. Mas vamos escolher não, para cobrir o caso mais geral em que o
conjunto de classes do novo problema não se sobrepõe ao conjunto de classes do modelo
original. Vamos colocar isso em prática usando a base convolucional da rede VGG16, treinada no
ImageNet, para extrair recursos interessantes de imagens de gatos e cachorros e depois treinar
um classificador de cães contra gatos sobre esses recursos.
O modelo VGG16, entre outros, vem pré-empacotado com Keras. Você pode importá-lo
do keras.applicationsmódulo. Aqui está a lista de modelos de classificação de imagens
(todos pré-concebidos no conjunto de dados do ImageNet) que estão disponíveis como parte
de keras.applications:
Xception
Inception V3
ResNet50
VGG16
VGG19
MobileNet
include_top = False,
================================================== ==============
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
block4_pool (MaxPooling2D) (Nenhum, 9, 9, 512) 0
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
================================================== ==============
O mapa de recursos final tem forma (4, 4, 512). Esse é o recurso em cima do qual você vai
colocar um classificador densamente conectado.
Executando a base convolucional sobre seu conjunto de dados, registrando sua saída em
um array Numpy no disco e, em seguida, usando esses dados como entrada para um
classificador autônomo e densamente semelhante àquele que você viu na parte 1 deste
livro. Essa solução é rápida e barata de ser executada, porque requer apenas a execução
da base convolucional uma vez para cada imagem de entrada, e a base convolucional é,
de longe, a parte mais cara do pipeline. Mas pela mesma razão, essa técnica não
permitirá que você use o aumento de dados.
Estendendo o modelo você tem ( conv_base) adicionando Densecamadas no topo, e
executando a coisa toda para terminar nos dados de entrada. Isso permitirá que você
use o aumento de dados, porque toda imagem de entrada passa pela base convolucional
toda vez que é vista pelo modelo. Mas pela mesma razão, esta técnica é muito mais cara
que a primeira.
Nós vamos cobrir as duas técnicas. Vamos percorrer o código necessário para configurar o
primeiro: registrando a saída de conv_baseseus dados e usando essas saídas como entradas
para um novo modelo.
importar os
batch_size = 20
gerador = datagen.flow_from_directory (
diretório,
batch_size = batch_size,
class_mode = 'binary')
i = 0
i + = 1
se eu * batch_size> = sample_count:
quebrar
1
Neste ponto, você pode definir seu classificador densamente conectado (observe o uso de
dropout para regularização) e treiná-lo nos dados e rótulos que você acabou de gravar.
model = models.Sequential ()
perda = 'binary_crossentropy',
metrics = ['acc'])
épocas = 30,
batch_size = 20,
validation_data = (validation_features,
validation_labels))
O treinamento é muito rápido, porque você só precisa lidar com duas Densecamadas - uma
época leva menos de um segundo, mesmo na CPU.
Vejamos as curvas de perda e precisão durante o treinamento (veja as figuras 5.15 e 5.16 ).
Figura 5.15. Precisão de treinamento e validação para extração simples de recursos
plt.legend ()
plt.figure ()
plt.legend ()
plt.show ()
Você alcança uma precisão de validação de cerca de 90% - muito melhor do que alcançou na
seção anterior, com o modelo pequeno treinado do zero. Mas os gráficos também indicam que
você está se sobrecarregando quase desde o início - apesar de usar o abandono com uma taxa
razoavelmente grande. Isso porque essa técnica não usa o aumento de dados, o que é essencial
para evitar o overfitting com pequenos conjuntos de imagens.
Agora, vamos rever a segunda técnica que mencionei para fazer a extração de recursos, que é
muito mais lenta e mais cara, mas que permite usar o aumento de dados durante o treinamento:
estendendo o conv_basemodelo e executando-o de ponta a ponta nas entradas.
Nota
Essa técnica é tão cara que você só deve tentar se tiver acesso a uma GPU - ela é absolutamente
intratável na CPU. Se você não pode executar o seu código na GPU, então a técnica anterior é o
caminho a percorrer.
model.add (conv_base)
>>> model.summary ()
================================================== ==============
________________________________________________________________
________________________________________________________________
________________________________________________________________
================================================== ==============
Como você pode ver, a base convolucional do VGG16 tem 14.714.688 parâmetros, o que é muito
grande. O classificador que você está adicionando no topo tem 2 milhões de parâmetros.
Em Keras, você congela uma rede configurando seu trainableatributo para False:
Com essa configuração, somente os pesos das duas Densecamadas que você adicionou serão
treinados. Isso é um total de quatro tensores de peso: dois por camada (a principal matriz de
peso e o vetor de polarização). Observe que, para que essas alterações sejam efetivadas, você
deve primeiro compilar o modelo. Se você modificar a treinabilidade de peso após a compilação,
recompile o modelo ou essas alterações serão ignoradas.
Agora você pode começar a treinar seu modelo com a mesma configuração de aumento de dados
usada no exemplo anterior.
Listagem 5.21. Treinar o modelo de ponta a ponta com uma base convolucional congelada
train_datagen = ImageDataGenerator (
reescala = 1. / 255,
rotation_range = 40,
width_shift_range = 0,2,
height_shift_range = 0,2,
shear_range = 0,2,
zoom_range = 0,2,
horizontal_flip = True,
train_generator = train_datagen.flow_from_directory (
train_dir, 2
batch_size = 20,
class_mode = 'binary') 4
validation_generator = test_datagen.flow_from_directory (
validation_dir,
target_size = (150, 150)
batch_size = 20,
class_mode = 'binary')
metrics = ['acc'])
history = model.fit_generator (
train_generator,
steps_per_epoch = 100,
épocas = 30,
validation_data = validation_generator,
validation_steps = 50)
Vamos plotar os resultados novamente (veja as figuras 5.17 e 5.18 ). Como você pode ver, você
alcança uma precisão de validação de cerca de 96%. Isso é muito melhor do que você conseguiu
com o pequeno convnet treinado a partir do zero.
Figura 5.17. Precisão de treinamento e validação para extração de recursos com aumento de dados
Figura 5.18. Perda de treinamento e validação para extração de recursos com aumento de dados
5.3.2. Afinação
Outra técnica amplamente utilizada para reutilização de modelos, complementar à extração de
características, é o ajuste fino (consulte a figura 5.19 ). O ajuste fino consiste no
descongelamento de algumas das camadas superiores de uma base de modelo congelada usada
para a extração de recursos e no treinamento conjunto da parte recém-adicionada do modelo
(neste caso, o classificador totalmente conectado) e dessas camadas superiores. Isso é
chamado de ajuste fino porque ajusta ligeiramente as representações mais abstratas do modelo
que está sendo reutilizado, a fim de torná-las mais relevantes para o problema em questão.
Figura 5.19. Afinar o último bloco convolucional da rede VGG16
Afirmei anteriormente que é necessário congelar a base de convolução do VGG16 para poder
treinar um classificador inicializado aleatoriamente no topo. Pela mesma razão, só é possível
ajustar as camadas superiores da base convolucional, uma vez que o classificador no topo já
tenha sido treinado. Se o classificador ainda não estiver treinado, o sinal de erro propagado pela
rede durante o treinamento será muito grande, e as representações aprendidas anteriormente
pelas camadas sendo ajustadas serão destruídas. Assim, as etapas para o ajuste fino de uma rede
são as seguintes:
Você já concluiu as três primeiras etapas ao fazer a extração de recursos. Vamos prosseguir com
o passo 4: você irá descongelar o seu conv_basee então congelar camadas individuais dentro
dele.
>>> conv_base.summary ()
================================================== ==============
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
block3_conv3 (Convolution2D) (nenhum, 37, 37, 256) 590080
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
================================================== ==============
Você vai afinar os últimos três camadas convolucionais, o que significa que todas as camadas
até block4_pooldeve ser congelado, e as
camadas block5_conv1, block5_conv2e block5_conv3deve ser treinável.
Por que não ajustar mais camadas? Por que não ajustar toda a base convolucional? Você
poderia. Mas você precisa considerar o seguinte:
conv_base.trainable = True
set_trainable = False
se layer.name == 'block5_conv1':
set_trainable = True
se set_trainable:
layer.trainable = True
outro:
layer.trainable = False
Agora você pode começar a sintonizar a rede. Você fará isso com o otimizador RMSProp, usando
uma taxa de aprendizado muito baixa. A razão para usar uma baixa taxa de aprendizado é que
você quer limitar a magnitude das modificações que você faz nas representações das três
camadas que você está ajustando. Atualizações que são muito grandes podem prejudicar essas
representações.
metrics = ['acc'])
history = model.fit_generator (
train_generator,
steps_per_epoch = 100,
épocas = 100,
validation_data = validation_generator,
validation_steps = 50)
Vamos plotar os resultados usando o mesmo código de plotagem de antes (veja as figuras
5.20 e 5.21 ).
Figura 5.20. Treinamento e validação de precisão para o ajuste fino
Essas curvas parecem barulhentas. Para torná-los mais legíveis, você pode suavizá-los,
substituindo cada perda e precisão por médias móveis exponenciais dessas quantidades. Aqui
está uma função de utilidade trivial para fazer isso (veja as figuras 5.22 e 5.23 ).
Figura 5.22. Curvas suavizadas para precisão de treinamento e validação para ajuste fino
Figura 5.23. Curvas suavizadas para perda de treinamento e validação para ajuste fino
smoothed_points = []
if smoothed_points:
outro:
smoothed_points.append (ponto)
return smoothed_points
plt.plot (épocas,
plt.plot (épocas,
plt.legend ()
plt.figure ()
plt.plot (épocas,
plt.plot (épocas,
plt.legend ()
plt.show ()
A curva de precisão de validação parece muito mais limpa. Você está vendo uma boa melhoria
absoluta de 1% na precisão, de cerca de 96% para acima de 97%.
Note que a curva de perda não mostra nenhuma melhora real (na verdade, está se
deteriorando). Você pode se perguntar, como a precisão pode permanecer estável ou melhorar
se a perda não estiver diminuindo? A resposta é simples: o que você exibe é uma média de
valores de perdas pontuais; mas o que importa para a precisão é a distribuição dos valores de
perda, não sua média, porque a precisão é o resultado de um limiar binário da probabilidade de
classe prevista pelo modelo. O modelo ainda pode estar melhorando, mesmo que isso não esteja
refletido na perda média.
Agora você pode finalmente avaliar este modelo nos dados de teste:
test_generator = test_datagen.flow_from_directory (
test_dir,
batch_size = 20,
class_mode = 'binary')
test_loss, test_acc = model.evaluate_generator (test_generator, etapas = 50)
Aqui você obtém uma precisão de teste de 97%. Na competição original de Kaggle em torno
deste conjunto de dados, este teria sido um dos principais resultados. Mas usando técnicas
modernas de aprendizagem profunda, você conseguiu alcançar esse resultado usando apenas
uma pequena fração dos dados de treinamento disponíveis (cerca de 10%). Existe uma enorme
diferença entre poder treinar 20.000 amostras em comparação com 2.000 amostras!
5.3.3. Empacotando
Aqui está o que você deve tirar dos exercícios nas duas últimas seções:
Agora você tem um conjunto sólido de ferramentas para lidar com problemas de classificação de
imagens - em particular, com pequenos conjuntos de dados.
Costuma-se dizer que os modelos de aprendizagem profunda são “caixas pretas”: representações
de aprendizado que são difíceis de extrair e apresentar de uma forma legível por
humanos. Embora isso seja parcialmente verdadeiro para certos tipos de modelos de
aprendizagem profunda, definitivamente não é verdade para os convnets. As representações
aprendidas por convnets são altamente receptivas à visualização, em grande parte porque
são representações de conceitos visuais . Desde 2013, uma ampla gama de técnicas foi
desenvolvida para visualizar e interpretar essas representações. Não pesquisaremos todos eles,
mas abordaremos três dos mais acessíveis e úteis:
Para o primeiro método - visualização de ativação - você usará a pequena convnet que você
treinou do zero no problema de classificação cães versus gatos na seção 5.2 . Para os próximos
dois métodos, você usará o modelo VGG16 apresentado na seção 5.3 .
________________________________________________________________
================================================== ==============
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
________________________________________________________________
================================================== ==============
Em seguida, você receberá uma imagem de entrada - uma foto de um gato, não parte das
imagens em que a rede foi treinada.
img_path =
'/Users/fchollet/Downloads/cats_and_dogs_small/test/cats/cat.1700.jpg'
img_tensor / = 255. 2
imprimir (img_tensor.shape)
plt.show ()
Para extrair os mapas de recursos que você deseja examinar, você criará um modelo Keras que
utiliza lotes de imagens como entrada e gera as ativações de todas as camadas de convolução e
pooling. Para fazer isso, você usará a classe Keras Model. Um modelo é instanciado usando dois
argumentos: um tensor de entrada (ou lista de tensores de entrada) e um tensor de saída (ou
lista de tensores de saída). A classe resultante é um modelo Keras, assim como
os Sequentialmodelos com os quais você está familiarizado, mapeando as entradas
especificadas para as saídas especificadas. O que diferencia a Modelclasse é que ela permite
modelos com múltiplas saídas, ao contrário Sequential. Para mais informações sobre
a Modelclasse, consulte a seção 7.1 .
Listagem 5.27. Instanciando um modelo a partir de um tensor de entrada e uma lista de tensores de saída
Quando alimentado com uma entrada de imagem, este modelo retorna os valores das ativações
de camada no modelo original. Esta é a primeira vez que você encontrou um modelo de
múltiplas saídas neste livro: até agora, os modelos que você viu tiveram exatamente uma
entrada e uma saída. No caso geral, um modelo pode ter qualquer número de entradas e
saídas. Este tem uma entrada e oito saídas: uma saída por ativação de camada.
1 Retorna uma lista de cinco matrizes Numpy: uma ativação de matriz por
camada
Por exemplo, esta é a ativação da primeira camada de convolução para a entrada de imagem de
gato:
É um mapa de recursos de 148 × 148 com 32 canais. Vamos tentar desenhar o quarto canal da
ativação da primeira camada do modelo original (veja a figura 5.25 ).
Figura 5.25. Quarto canal da ativação da primeira camada na imagem do gato de teste
Este canal parece codificar um detector de borda diagonal. Vamos tentar o sétimo canal (veja
a figura 5.26 ) - mas note que seus próprios canais podem variar, porque os filtros específicos
aprendidos pelas camadas de convolução não são deterministas.
Figura 5.26. Sétimo canal da ativação da primeira camada na foto do gato de teste
Este parece um detector de "ponto verde brilhante", útil para codificar os olhos de gato. Neste
ponto, vamos plotar uma visualização completa de todas as ativações na rede (veja a figura
5.27 ). Você irá extrair e traçar cada canal em cada um dos oito mapas de ativação, e você irá
empilhar os resultados em um grande tensor de imagem, com canais empilhados lado a lado.
Figura 5.27. Cada canal de ativação de cada camada na imagem do gato de teste
Listagem 5.31. Visualizando todos os canais em todas as ativações intermediárias
layer_names = [] 1
layer_names.append (layer.name) 1
images_per_row = 16
:,:
channel_image - = channel_image.mean () 7
channel_image / = channel_image.std ()
channel_image * = 64
channel_image + = 128
escala = 1. / tamanho
plt.title (layer_name)
plt.grid (falso)
1 Nomes das camadas, para que você possa tê-las como parte do seu enredo
2 Exibe os mapas de recursos
3 Número de recursos no mapa de recursos
4 O mapa de recursos tem forma (1, tamanho, tamanho, n_features).
5 Telhas os canais de ativação nesta matriz
6 telhas cada filtro em uma grade horizontal grande
7 Pós-processa o recurso para torná-lo visualmente palatável
8 Exibe a grade
A primeira camada atua como uma coleção de vários detectores de borda. Nesse estágio,
as ativações retêm quase todas as informações presentes no quadro inicial.
À medida que você sobe, as ativações tornam-se cada vez mais abstratas e menos
visualmente interpretáveis. Eles começam a codificar conceitos de nível mais alto, como
“orelha de gato” e “olho de gato”. Apresentações mais altas têm cada vez menos
informações sobre o conteúdo visual da imagem e cada vez mais informações
relacionadas à classe da imagem.
A dispersão das ativações aumenta com a profundidade da camada: na primeira
camada, todos os filtros são ativados pela imagem de entrada; mas nas camadas
seguintes, mais e mais filtros estão em branco. Isso significa que o padrão codificado
pelo filtro não é encontrado na imagem de entrada.
Isso é análogo ao modo como humanos e animais percebem o mundo: depois de observar uma
cena por alguns segundos, um humano pode lembrar quais objetos abstratos estavam presentes
nele (bicicleta, árvore), mas não consegue se lembrar da aparência específica desses objetos. Na
verdade, se você tentou desenhar uma bicicleta genérica da memória, é provável que não
consiga acertar remotamente, mesmo que tenha visto milhares de bicicletas durante a sua vida
(veja, por exemplo, a figura 5.28 ). Experimente agora mesmo: esse efeito é absolutamente
real. Seu cérebro aprendeu a abstrair completamente sua entrada visual - para transformá-lo em
conceitos visuais de alto nível enquanto filtra detalhes visuais irrelevantes - tornando
tremendamente difícil lembrar como as coisas ao seu redor parecem.
Figura 5.28. Esquerda: tenta tirar uma bicicleta da memória. Direita: como deve ser uma bicicleta esquemática.
O processo é simples: você criará uma função de perda que maximiza o valor de um
determinado filtro em uma determinada camada de convolução e, em seguida, usará a descida
de gradiente estocástica para ajustar os valores da imagem de entrada para maximizar esse valor
de ativação . Por exemplo, aqui está uma perda para a ativação do filtro 0 na
camada block3_conv1da rede VGG16, pré-configurada no ImageNet.
include_top = False)
layer_name = 'block3_conv1'
filter_index = 0
Para implementar gradiente descendente, você precisará do gradiente dessa perda em relação à
entrada do modelo. Para fazer isso, você usará a gradientsfunção empacotada com
o backendmódulo de Keras.
Listagem 5.33. Obtendo o gradiente da perda em relação à entrada
Um truque não óbvio a ser usado para ajudar o processo gradiente-descendente a ocorrer sem
problemas é normalizar o tensor do gradiente dividindo-o pela sua norma L2 (a raiz quadrada
da média do quadrado dos valores no tensor). Isso garante que a magnitude das atualizações
feitas na imagem de entrada esteja sempre dentro do mesmo intervalo.
Agora você precisa de uma maneira de calcular o valor do tensor de perda e do tensor de
gradiente, dada uma imagem de entrada. Você pode definir uma função de backend Keras para
fazer isso: iterateé uma função que usa um tensor Numpy (como uma lista de tensores de
tamanho 1) e retorna uma lista de dois tensores de Numpy: o valor de perda e o valor do
gradiente.
Listagem 5.35. Obtendo valores de saída numpy dados valores de entrada numpy
Neste ponto, você pode definir um loop de Python para fazer uma descida de gradiente
estocástica.
passo = 1. 2 3
Listagem 5.37. Função de utilidade para converter um tensor em uma imagem válida
x - = x.mean () 1
x / = (x.std () + 1e-5) 1
x * = 0,1 1
x + = 0,5 2
x = np.clip (x, 0, 1) 2
x * = 255 3
return x 3
Agora você tem todas as peças. Vamos colocá-los juntos em uma função Python que recebe
como entrada um nome de camada e um índice de filtro, e retorna um tensor de imagem válido
representando o padrão que maximiza a ativação do filtro especificado.
Listagem 5.39. Gerando uma grade de todos os padrões de resposta do filtro em uma camada
layer_name = 'block1_conv1'
tamanho = 64
margem = 5
plt.imshow (resultados)
6
Essas visualizações de filtro informam muito sobre como as camadas convnet veem o mundo:
cada camada em uma convnet aprende uma coleção de filtros de modo que suas entradas
possam ser expressas como uma combinação dos filtros. Isso é semelhante a como a
transformada de Fourier decompõe os sinais em um banco de funções cosseno. Os filtros nesses
bancos de filtros convnet se tornam cada vez mais complexos e refinados à medida que você
sobe no modelo:
Os filtros da primeira camada no modelo ( block1_conv1) codificam bordas e cores
direcionais simples (ou bordas coloridas, em alguns casos).
Os filtros de block2_conv1codificar texturas simples feitas de combinações de arestas
e cores.
Os filtros nas camadas superiores começam a assemelhar-se a texturas encontradas em
imagens naturais: penas, olhos, folhas e assim por diante.
Considere a imagem de dois elefantes africanos mostrados na figura 5.34 (sob uma licença
Creative Commons), possivelmente uma mãe e seu filhote, passeando pela savana. Vamos
converter essa imagem em algo que o modelo VGG16 possa ler: o modelo foi treinado em
imagens de tamanho 224 × 244, pré-processadas de acordo com algumas regras que são
empacotadas na função de
utilidade keras.applications.vgg16.preprocess_input. Portanto, você precisa
carregar a imagem, redimensioná-la para 224 × 224, convertê-la em
um float32tensor Numpy e aplicar essas regras de pré-processamento.
img_path = '/Users/fchollet/Downloads/creative_commons_elephant.jpg' 1
x = image.img_to_array (img) 3
x = preprocess_input (x) 5
386
Para visualizar quais partes da imagem são mais parecidas com elefantes africanos, vamos
configurar o processo Grad-CAM.
Para fins de visualização, você também normalizará o mapa de calor entre 0 e 1. O resultado é
mostrado na figura 5.35 .
Figura 5.35. Calor de ativação de classe de elefante africano sobre a imagem de teste
Finalmente, você usará o OpenCV para gerar uma imagem que sobrepõe a imagem original no
mapa de calor que você acabou de obter (veja a figura 5.36 ).
Figura 5.36. Sobrepondo o mapa de calor de ativação de classe na imagem original
importar cv2
Por que a rede achou que essa imagem continha um elefante africano?
Onde está o elefante africano localizado na foto?
Resumo do capítulo
Este capítulo explora modelos de aprendizagem profunda que podem processar textos
(entendidos como seqüências de palavras ou sequências de caracteres), séries de tempo e dados
de sequência em geral. Os dois algoritmos de aprendizagem profunda fundamentais para o
processamento de sequências são as redes neurais recorrentes e as conversões 1D , a versão
unidimensional das convnets 2D que abordamos nos capítulos anteriores. Vamos discutir ambas
as abordagens neste capítulo.
O texto é uma das formas mais difundidas de dados de sequência. Pode ser entendido como uma
sequência de caracteres ou uma sequência de palavras, mas é mais comum trabalhar no nível
das palavras. Os modelos de processamento de sequências de aprendizado profundo
introduzidos nas seções a seguir podem usar texto para produzir uma forma básica de
compreensão de linguagem natural, suficiente para aplicativos que incluem classificação de
documentos, análise de sentimentos, identificação de autores e até perguntas e respostas (QA)
(em um contexto restrito). É claro, tenha em mente ao longo deste capítulo que nenhum desses
modelos de aprendizagem profunda realmente entende o texto em um sentido humano; em vez
disso, esses modelos podem mapear a estrutura estatística da linguagem escrita, o que é
suficiente para resolver muitas tarefas textuais simples.
Como todas as outras redes neurais, os modelos de aprendizagem profunda não aceitam como
texto bruto de entrada: eles só funcionam com tensores numéricos. Vectorizing text é o processo
de transformar texto em tensores numéricos. Isso pode ser feito de várias maneiras:
Coletivamente, as diferentes unidades em que você pode dividir o texto (palavras, caracteres ou
n-gramas) são chamadas de tokens , e dividir o texto em tais tokens é chamado
de tokenização . Todos os processos de vetorização de vetores consistem em aplicar algum
esquema de tokenização e, em seguida, associar vetores numéricos aos tokens gerados. Esses
vetores, compactados em tensores de seqüência, são alimentados em redes neurais
profundas. Existem várias maneiras de associar um vetor a um token. Nesta seção, apresentarei
dois principais: codificação de tokens de um ponto a quente e incorporação de
token (normalmente usado exclusivamente para palavras e denominado incorporação de
palavras). O restante desta seção explica essas técnicas e mostra como usá-las para passar do
texto bruto para um tensor Numpy que você pode enviar para uma rede Keras.
Figura 6.1. Do texto aos tokens aos vetores
Word n-grams são grupos de N (ou menos) palavras consecutivas que você pode extrair de uma
frase. O mesmo conceito também pode ser aplicado a caracteres em vez de palavras.
Aqui está um exemplo simples. Considere a frase “O gato sentou-se no tapete”. Ele pode ser
decomposto no seguinte conjunto de 2 gramas:
Como o bag-of-words não é um método de tokenização que preserva a ordem (os tokens gerados
são entendidos como um conjunto, não uma sequência e a estrutura geral das sentenças é
perdida), ele tende a ser usado em processamento de linguagem superficial modelos, em vez de
modelos de aprendizagem profunda. A extração de n-grams é uma forma de engenharia de
recursos, e o aprendizado profundo acaba com esse tipo de abordagem rígida e quebradiça,
substituindo-a por aprendizado de recurso hierárquico. As convnets unidimensionais e as redes
neurais recorrentes, apresentadas mais adiante neste capítulo, são capazes de aprender
representações para grupos de palavras e caracteres sem serem explicitamente informados
sobre a existência de tais grupos, observando sequências contínuas de palavras ou
caracteres. Por esta razão, Não vamos cobrir mais n-gramas neste livro. Mas lembre-se de que
eles são uma ferramenta de engenharia de recursos poderosa e inevitável ao usar modelos de
processamento de texto simples e superficiais, como a regressão logística e florestas aleatórias.
6.1.1. Uma quente codificação de palavras e caracteres
Uma codificação quente é a maneira mais comum e básica de transformar um token em um
vetor. Você viu isso em ação nos exemplos iniciais do IMDB e Reuters no capítulo 3 (feito com
palavras, nesse caso). Ele consiste em associar um índice inteiro única com cada palavra e, em
seguida, transformar este índice inteiro i num vector binário de tamanho N (o tamanho do
vocabulário); o vetor é todo zeros, exceto pela i ésima entrada, que é 1.
Naturalmente, uma codificação a quente também pode ser feita no nível do personagem. Para
indubitavelmente levar para casa o que é uma codificação quente e como implementá-la,
as listagens 6.1e 6.2 mostram dois exemplos de brinquedo: um para palavras e outro para
caracteres.
samples = ['O gato sentou no tatame.', 'O cachorro comeu meu dever de casa.']
1
token_index = {} 2
max_length = 10 5
comprimento máximo,
1 Dados iniciais: uma entrada por amostra (neste exemplo, uma amostra é
uma sentença, mas poderia ser um documento inteiro)
2 Cria um índice de todos os tokens nos dados
3 Tokeniza as amostras pelo método de divisão. Na vida real, você também
tira pontos e caracteres especiais das amostras.
4 Atribui um índice exclusivo para cada palavra única. Observe que você
não atribui o índice 0 a nada.
5 Vectoriza as amostras. Você só considerará as primeiras palavras
max_length em cada amostra.
6 Aqui é onde você armazena os resultados.
string de importação
samples = ['O gato sentou no tatame.', 'O cachorro comeu meu dever de casa.']
caracteres = string.printable 1
max_length = 50
Observe que o Keras possui utilitários integrados para fazer uma codificação de texto quente no
nível de palavra ou nível de caractere, a partir de dados de texto bruto. Você deve usar esses
utilitários, pois eles cuidam de vários recursos importantes, como descartar caracteres especiais
de strings e levar em conta apenas as N palavras mais comuns em seu conjunto de dados (uma
restrição comum, para evitar lidar com espaços vetoriais de entrada muito grandes ).
samples = ['O gato sentou no tatame.', 'O cachorro comeu meu dever de casa.']
tokenizer.fit_on_texts (amostras) 2
word_index = tokenizer.word_index 5
Uma variante da codificação one-hot é o chamado truque de hash quente , que você pode usar
quando o número de tokens exclusivos no seu vocabulário for muito grande para manipular
explicitamente. Em vez de atribuir explicitamente um índice a cada palavra e manter uma
referência desses índices em um dicionário, você pode dividir as palavras em vetores de
tamanho fixo. Isso geralmente é feito com uma função de hash muito leve. A principal vantagem
deste método é que ele acaba com a manutenção de um índice de palavras explícito, que
economiza memória e permite a codificação online dos dados (você pode gerar vetores de token
imediatamente, antes de ver todos os dados disponíveis). A única desvantagem dessa
abordagem é que ela é suscetível a colisões de hash: duas palavras diferentes podem acabar com
o mesmo hash, e subseqüentemente qualquer modelo de aprendizado de máquina olhando para
esses hashes não será capaz de dizer a diferença entre essas palavras. A probabilidade de
colisões de hash diminui quando a dimensionalidade do espaço de hash é muito maior do que o
número total de tokens exclusivos sendo hash.
Listagem 6.4. Codificação one-hot no nível da palavra com truque de hashing (exemplo de brinquedo)
samples = ['O gato sentou no tatame.', 'O cachorro comeu meu dever de casa.']
dimensionalidade = 1000 1
max_length = 10
Figura 6.2. Enquanto as representações de palavras obtidas a partir de uma codificação ou hash quente são esparsas, de alta
dimensão e codificadas, as incorporações de palavras são densas, relativamente pouco dimensionais e aprendidas com os
dados.
A maneira mais simples de associar um vetor denso a uma palavra é escolher o vetor
aleatoriamente. O problema com essa abordagem é que o espaço de incorporação resultante não
tem estrutura: por exemplo, as palavras precisas e exatas podem ter inclusões completamente
diferentes, embora sejam intercambiáveis na maioria das sentenças. É difícil para uma rede
neural profunda dar sentido a um espaço de incorporação tão ruidoso e não estruturado.
Para obter um pouco mais abstrato, as relações geométricas entre os vetores de palavras devem
refletir as relações semânticas entre essas palavras. Os embeddings do Word destinam-se a
mapear a linguagem humana para um espaço geométrico. Por exemplo, em um espaço de
incorporação razoável, você esperaria que os sinônimos fossem incorporados em vetores de
palavras semelhantes; e, em geral, você esperaria que a distância geométrica (como a distância
L2) entre quaisquer dois vetores de palavras se relacionasse com a distância semântica entre as
palavras associadas (palavras significando que coisas diferentes são embutidas em pontos
distantes umas das outras, enquanto palavras relacionadas são mais perto). Além da distância,
você pode querer direções específicas no espaço de incorporação para ser significativo. Para
tornar isso mais claro, vamos dar uma olhada em um exemplo concreto.
Existe algum espaço de incorporação de palavras ideal que mapeie perfeitamente a linguagem
humana e possa ser usado para qualquer tarefa de processamento de linguagem
natural? Possivelmente, mas ainda temos que computar qualquer coisa do tipo. Além disso, não
existe linguagem humana- Existem muitas línguas diferentes e elas não são isomórficas, porque
uma linguagem é o reflexo de uma cultura específica e de um contexto específico. Mas, de forma
mais pragmática, o que faz um bom espaço para a palavra depende muito de sua tarefa: o espaço
perfeito para incorporar palavras a um modelo de análise de opinião de filme em inglês pode
parecer diferente do espaço ideal para uma linguagem jurídica em inglês. - modelo de
classificação de documentos, porque a importância de certos relacionamentos semânticos varia
de tarefa para tarefa.
Portanto, é razoável aprender um novo espaço de incorporação a cada nova tarefa. Felizmente, a
retropropagação torna isso fácil, e o Keras torna isso ainda mais fácil. É sobre aprender os pesos
de uma camada: a Embeddingcamada.
A Embeddingcamada é melhor entendida como um dicionário que mapeia índices inteiros (que
representam palavras específicas) para vetores densos. Ele recebe inteiros como entrada,
procura esses inteiros em um dicionário interno e retorna os vetores associados. É efetivamente
uma pesquisa de dicionário (veja a figura 6.4 ).
Quando você instancia uma Embeddingcamada, seus pesos (seu dicionário interno de vetores
de token) são inicialmente aleatórios, assim como com qualquer outra camada. Durante o
treinamento, esses vetores de palavras são gradualmente ajustados via retropropagação,
estruturando o espaço em algo que o modelo a jusante pode explorar. Uma vez totalmente
treinado, o espaço de incorporação mostrará muita estrutura - um tipo de estrutura
especializada para o problema específico para o qual você está treinando seu modelo.
Vamos aplicar essa ideia à tarefa de previsão do sentimento de revisão de filme do IMDB com a
qual você já está familiarizado. Primeiro, você preparará rapidamente os dados. Você restringirá
as resenhas de filmes às 10 mil palavras mais comuns (como fez na primeira vez que trabalhou
com esse conjunto de dados) e cortará as resenhas depois de apenas 20 palavras. A rede
iráaprenda embedings de 8 dimensões para cada uma das 10.000 palavras, gire as sequências
inteiras de entrada (tensor inteiro 2D) em sequências embutidas (tensor de flutuação 3D),
achatar o tensor para 2D e treinar uma única Densecamada no topo para classificação.
Listagem 6.6. Carregando os dados do IMDB para uso com uma Embeddingcamada
max_features = 10000 1
maxlen = 20 2
(x_train, y_train), (x_test, y_test) = imdb.load_data (
num_words = max_features) 3
model = Sequential ()
model.summary ()
épocas = 10,
batch_size = 32,
validation_split = 0,2)
Você chega a uma precisão de validação de ~ 76%, o que é muito bom, considerando que você
está olhando apenas as primeiras 20 palavras em cada revisão. Mas observe que apenas achatar
as sequências incorporadas e treinar uma única Densecamada no topo leva a um modelo que
trata cada palavra na sequência de entrada separadamente, sem considerar as relações entre
palavras e a estrutura da sentença (por exemplo, este modelo provavelmente trataria ambos
filme é uma bomba ”e“ este filme é a bomba ”como sendo críticas negativas). EstáÉ muito
melhor adicionar camadas recorrentes ou camadas convolucionais 1D sobre as sequências
incorporadas para aprender recursos que levam em conta cada seqüência como um todo. É nisso
que vamos nos concentrar nas próximas seções.
Às vezes, você tem tão poucos dados de treinamento disponíveis que você não pode usar seus
dados sozinho para aprender uma incorporação apropriada de seu vocabulário específica da
tarefa. O que fazes, então?
Em vez de aprender as incorporações de palavras juntamente com o problema que você deseja
resolver, é possível carregar vetores de incorporação a partir de um espaço de incorporação pré-
computado que você sabe que é altamente estruturado e exibe propriedades úteis - que
capturam aspectos genéricos da estrutura da linguagem. O raciocínio por trás do uso de
incorporação de palavras pré-formatadas no processamento de linguagem natural é semelhante
ao uso de convnets pré-formatados na classificação de imagens: você não tem dados suficientes
disponíveis para aprender recursos realmente poderosos, mas espera os recursos de que precisa
ser razoavelmente genérico - ou seja, recursos visuais comuns ou recursos semânticos. Nesse
caso, faz sentido reutilizar os recursos aprendidos em um problema diferente.
Existem vários bancos de dados pré-computados de incorporação de palavras que você pode
baixar e usar em uma Embeddingcamada Keras . Word2vec é um deles. Outro popular é
chamado de Vetores Globais para Representação de Palavras
(GloVe, https://nlp.stanford.edu/projects/glove ), que foi desenvolvido por pesquisadores de
Stanford em 2014. Esta técnica de incorporação é baseada na fatoração de uma matriz de co-
estatísticas de ocorrências. Seus desenvolvedores disponibilizaram integrações pré-computadas
para milhões de tokens ingleses, obtidos a partir de dados da Wikipedia e dados de
rastreamento comuns.
Vejamos como você pode começar a usar os envios do GloVe em um modelo Keras. O mesmo
método é válido para incorporações do Word2vec ou qualquer outro banco de dados de
incorporação de palavras. Você também usará este exemplo para atualizar as técnicas de
tokenização de texto introduzidas há alguns parágrafos: você começará a partir do texto não
processado e trabalhará para cima.
6.1.3. Juntando tudo: do texto bruto à incorporação de palavras
Você usará um modelo semelhante ao que acabamos de ler: embutir sentenças em sequências de
vetores, achatá-las e treinar uma Densecamada no topo. Mas você vai fazerEntão, usando
embeddings de palavras pré-rotuladas; e em vez de usar os dados do IMDB pretokenized
embalados em Keras, você vai começar do zero, baixando os dados do texto original.
Agora, vamos coletar as avaliações individuais de treinamento em uma lista de strings, uma
string por revisão. Você também coletará os rótulos de revisão (positivo / negativo) em
uma labelslista.
importar os
labels = []
textos = []
f.close ()
if label_type == 'neg':
labels.append (0)
outro:
labels.append (1)
Vamos vetorizar o texto e preparar uma divisão de treinamento e validação, usando os conceitos
introduzidos anteriormente nesta seção. Como a incorporação de palavras pré-concebidas é
particularmente útil em problemas em que há poucos dados de treinamento disponíveis (caso
contrário, é provável que as integrações específicas da tarefa os superem), adicionaremos a
seguinte alteração: restringir os dados de treinamento às primeiras 200 amostras. Então, você
aprenderá a classificar as resenhas de filmes depois de ver apenas 200 exemplos.
maxlen = 100 1
training_samples = 200 2
validation_samples = 10000 3
max_words = 10000 4
tokenizer.fit_on_texts (textos)
word_index = tokenizer.word_index
np.random.shuffle (índices)
Vamos analisar o arquivo descompactado (um arquivo .txt) para construir um índice que
mapeia palavras (como strings) para sua representação vetorial (como vetores numéricos).
glove_dir = '/Users/fchollet/Downloads/glove.6B'
embeddings_index = {}
para linha em f:
valores = line.split ()
f.close ()
Em seguida, você construirá uma matriz de incorporação que poderá ser carregada em
uma Embeddingcamada. Deve ser uma matriz de forma (max_words, embedding_dim),
em que cada entrada i contém o embedding_dimvector -dimensional para a palavra de
índice i no índice palavra de referência (construído durante tokenization). Observe que o índice
0 não deve representar qualquer palavra ou token - é um marcador de posição.
Listagem 6.11. Preparando a matriz de incorporação de palavras do GloVe
embedding_dim = 100
se eu <max_words:
Definindo um modelo
model = Sequential ()
model.summary ()
A Embeddingcamada tem uma matriz de peso única: uma matriz flutuante 2D em que cada
entrada i é o vetor de palavras que se pretende associar ao índice i . Simples o
suficiente. Carregue a matriz GloVe que você preparou na Embeddingcamada, a primeira
camada no modelo.
Além disso, você congelará a Embeddingcamada (defina seu trainableatributo como False),
seguindo a mesma lógica com a qual já está familiarizado no contexto de recursos convnet pré-
tratados: quando partes de um modelo são pré-tratadas (como sua Embeddingcamada) e partes
são inicializadas aleatoriamente (como seu classificador), as partes pré-tratadas não devem ser
atualizadas durante o treinamento, para evitar esquecer o que elas já sabem. O grandeas
atualizações de gradiente acionadas pelas camadas inicializadas aleatoriamente seriam
prejudiciais aos recursos já aprendidos.
perda = 'binary_crossentropy',
metrics = ['acc'])
épocas = 10,
batch_size = 32,
model.save_weights ('pre_trained_glove_model.h5')
Agora, plote o desempenho do modelo ao longo do tempo (veja figuras 6.5 e 6.6 ).
Figura 6.5. Perda de treinamento e validação ao usar embeddings de palavras pré-rotuladas
Figura 6.6. Precisão de treinamento e validação ao usar embeddings de palavras pré-rotuladas
plt.legend ()
plt.figure ()
plt.legend ()
plt.show ()
Observe que sua milhagem pode variar: como você tem poucas amostras de treinamento, o
desempenho depende muito de exatamente as 200 amostras escolhidas - e você as escolhe
aleatoriamente. Se isto funcionar mal para você, tente escolher um conjunto aleatório diferente
de 200 amostras, para o bem do exercício (na vida real, você não consegue escolher seus dados
de treinamento).
Você também pode treinar o mesmo modelo sem carregar a palavra pré-encadeada e sem
congelar a camada de incorporação. Nesse caso, você aprenderá uma incorporação específica de
tarefas dos tokens de entrada, que geralmente é mais poderosa do que os envoltórios de palavras
pré-roteados quando muitos dados estão disponíveis. Mas neste caso, você tem apenas 200
amostras de treinamento. Vamos tentar (veja as figuras 6.7 e 6.8 ).
Figura 6.7. Perda de treinamento e validação sem usar embeddings de palavras pré-rotuladas
Figura 6.8. Precisão de treinamento e validação sem usar embeddings de palavras pré-rotuladas
model = Sequential ()
model.summary ()
perda = 'binary_crossentropy',
metrics = ['acc'])
épocas = 10,
batch_size = 32,
A precisão de validação fica parada nos 50s baixos. Portanto, neste caso, a incorporação de
palavras pré-formatadas supera os embeddings aprendidos em conjunto. Se você aumentar o
número de amostras de treinamento, isso rapidamente deixará de ser o caso - experimente-o
como um exercício.
Finalmente, vamos avaliar o modelo nos dados de teste. Primeiro, você precisa tokenizar os
dados de teste.
labels = []
textos = []
f.close ()
if label_type == 'neg':
labels.append (0)
outro:
labels.append (1)
model.load_weights ('pre_trained_glove_model.h5')
Você obtém uma precisão de teste chocante de 56%. Trabalhar com apenas algumas amostras de
treinamento é difícil!
6.1.4. Empacotando
Agora você pode fazer o seguinte:
Transforme o texto bruto em algo que uma rede neural pode processar
Use a Embeddingcamada em um modelo Keras para aprender sobre incorporações de
token específicas da tarefa
Use encadeamentos pré-formatados para obter um impulso extra em pequenos
problemas de processamento de linguagem natural
Uma característica importante de todas as redes neurais que você viu até agora, como redes e
convnets densamente conectadas, é que elas não têm memória. Cada entrada mostrada a eles é
processada independentemente, sem estado mantido entre entradas. Com essas redes, para
processar uma seqüência ou uma série temporal de pontos de dados, você precisa mostrar a
sequência inteira para a rede de uma vez: transformá-la em um único ponto de dados. Por
exemplo, isso é o que você fez no exemplo do IMDB: uma revisão completa do filme foi
transformada em um único vetor grande e processada de uma só vez. Essas redes são chamadas
de redes feedforward .
Em contraste, quando você está lendo a sentença atual, você está processando palavra por
palavra - ou melhor, reveste os olhos com sacadas oculares - enquanto mantém memórias do
que veio antes; isso lhe dá uma representação fluida do significado transmitido por essa
sentença. A inteligência biológica processa as informações de forma incremental, mantendo um
modelo interno do que está processando, construído a partir de informações passadas e
constantemente atualizado à medida que novas informações chegam.
Uma rede neural recorrente (RNN) adota o mesmo princípio, ainda que em uma versão
extremamente simplificada: processa seqüências, iterando os elementos de sequência e
mantendo um estadocontendo informações relativas ao que foi visto até o momento. Na
verdade, um RNN é um tipo de rede neural que possui um loop interno (consulte a figura
6.9 ). O estado da RNN é redefinido entre o processamento de duas seqüências independentes
diferentes (como duas revisões diferentes do IMDB), portanto, você considera uma sequência
como um único ponto de dados: uma única entrada na rede. O que muda é que esse ponto de
dados não é mais processado em uma única etapa; em vez disso, a rede faz um loop interno
sobre os elementos da sequência.
Para tornar claras essas noções de loop e state , vamos implementar o forward forward de um
brinquedo RNN em Numpy. Este RNN toma como entrada uma seqüência de vetores, que você
codificará como um tensor 2D de tamanho (timesteps, input_features). Faz um loop
sobre timesteps, e em cada timestep, considera seu estado atual em te a entrada em t(de
shape (input_features,), e combina-os para obter a saída em t. Você então definirá o
estado para a próxima etapa para ser a saída anterior. Para o primeiro timestep, a saída anterior
não está definida, portanto, não há estado atual, portanto, você inicializará o estado como um
vetor all-zero chamado de estado inicial da rede.
state_t = 0 1
state_t = output_t 3
1 O estado em t
2 Itera sobre os elementos da sequência
3 A saída anterior se torna o estado da próxima iteração.
Pode mesmo carne para fora a função f: a transformação da entrada e uma saída em estado vai
ser parametrizado por duas matrizes, We U, e um vector de polarização. É semelhante à
transformação operada por uma camada densamente conectada em uma rede feedforward.
state_t = 0
state_t = output_t
Para tornar essas noções absolutamente inequívocas, vamos escrever uma implementação
ingênua do Numpy do passe para frente do RNN simples.
timesteps = 100 1
input_features = 32 2
output_features = 64 3
b = np.random.random ((output_features,)) 6
successive_outputs = []
successive_outputs.append (output_t) 9
state_t = output_t 10
Como todas as camadas recorrentes em Keras, SimpleRNNpode ser executado em dois modos
diferentes: pode retornar as sequências completas de saídas sucessivas para cada timestep (um
tensor 3D de forma (batch_size, timesteps, output_features)) ou somente a última
saída para cada seqüência de entrada (um tensor 2D de forma (batch_size,
output_features)). Esses dois modos são controlados
pelo return_sequencesargumento do construtor. Vamos ver um exemplo que
usa SimpleRNNe retorna apenas a saída no último momento:
>>> model.summary ()
________________________________________________________________
================================================== ==============
________________________________________________________________
================================================== ==============
>>> model.summary ()
________________________________________________________________
================================================== ==============
________________________________________________________________
================================================== ==============
Às vezes é útil empilhar várias camadas recorrentes, uma após a outra, para aumentar o poder
de representação de uma rede. Em tal configuração, você precisa obter todas as camadas
intermediárias para retornar a sequência completa de saídas:
>>> model.summary ()
________________________________________________________________
================================================== ==============
________________________________________________________________
simplernn_12 (SimpleRNN) (nenhum, nenhum, 32) 2080
________________________________________________________________
________________________________________________________________
________________________________________________________________
================================================== ==============
max_features = 10000 1
maxlen = 500 2
batch_size = 32
num_words = max_features)
model = Sequential ()
épocas = 10,
batch_size = 128,
validation_split = 0,2)
plt.legend ()
plt.figure ()
plt.show ()
Como lembrete, no capítulo 3 , a primeira abordagem ingênua a este conjunto de dados levou
você a uma precisão de teste de 88%. Infelizmente, essa pequena rede recorrente não apresenta
um bom desempenho em comparação com essa linha de base (apenas 85% de precisão de
validação). Parte do problema é que suas entradas consideram apenas as primeiras 500
palavras, em vez de sequências completas - portanto, o RNN tem acesso a menos informações
do que o modelo de linha de base anterior. O restante do problema é que SimpleRNNnão é bom
no processamento de sequências longas, como texto. Outros tipos de camadas recorrentes têm
um desempenho muito melhor. Vamos ver algumas camadas mais avançadas.
Veja, por exemplo, Yoshua Bengio, Patrice Simard e Paolo Frasconi, “Aprender Dependências de Longo Prazo com Descida de Gradiente é
Sepp Hochreiter e Jürgen Schmidhuber, “Long Short-Term Memory”, Neural Computation 9, no. 8 (1997).
Essa camada é uma variante da SimpleRNNcamada que você já conhece; Ele adiciona uma
maneira de transportar informações através de muitos timesteps. Imagine uma correia
transportadora correndo paralela à seqüência que você está processando. As informações da
sequência podem saltar para a correia transportadora em qualquer ponto, ser transportadas
para um intervalo de tempo mais recente e pular, intactas, quando você precisar. Isso é
essencialmente o que o LSTM faz: ele salva informações para mais tarde, evitando que os sinais
antigos desapareçam gradualmente durante o processamento.
Para entender isso em detalhes, vamos começar a partir da SimpleRNNcélula (veja a figura
6.13 ). Como você terá muitas matrizes de ponderação, indexe as matrizes We Una célula com a
letra o( Woe Uo) para saída .
Figura 6.13. O ponto de partida de uma lstmcamada: umsimplernn
Vamos adicionar a esta imagem um fluxo de dados adicional que transporta informações através
de timesteps. Chame seus valores em diferentes timesteps Ct, onde C significa carry . Esta
informação terá o seguinte impacto na célula: será combinada com a conexão de entrada e a
conexão recorrente (através de uma transformação densa: um produto de ponto com uma
matriz de peso seguido por uma adição de polarização e a aplicação de uma função de ativação)
e isso afetará o estado sendo enviado para o próximo timestep (via uma função de ativação e
uma operação de multiplicação). Conceitualmente, o fluxo de dados carry é uma maneira de
modular a próxima saída e o próximo estado (veja a figura 6.14 ). Simples até agora.
Agora, a sutileza: a maneira como o próximo valor do fluxo de dados carry é calculado. Envolve
três transformações distintas. Todos os três têm a forma de uma SimpleRNNcélula:
Mas todas as três transformações têm suas próprias matrizes de peso, que você índice com as
letras i, f, e k. Aqui está o que você tem até agora (pode parecer um pouco arbitrário, mas tenha
paciência comigo).
output_t = ativação (ponto (state_t, Uo) + ponto (input_t, Wo) + ponto (C_t,
Vo) + bo)
i_t = ativação (ponto (estado_t, Ui) + ponto (entrada_t, Wi) + bi)
Pode obter o novo estado carry (o próximo c_t), combinando i_t, f_te k_t.
Adicione isto como mostrado na figura 6.15 . E é isso. Não é tão complicado - apenas um pouco
complexo.
Se você quiser ser filosófico, pode interpretar o que cada uma dessas operações deve fazer. Por
exemplo, você pode dizer que multiplicar c_te f_té uma maneira de esquecer deliberadamente
informações irrelevantes no fluxo de dados de transporte. Enquanto isso, i_te k_tfornecer
informações sobre o presente, atualizando a faixa de transporte com novas informações. Mas no
final do dia, essas interpretações não significam muito, porque o que essas
operações realmentefazer é determinado pelo conteúdo dos pesos parametrizando-os; e os
pesos são aprendidos de uma maneira completa, recomeçando a cada rodada de treinamento,
tornando impossível creditar essa ou aquela operação com uma finalidade específica. A
especificação de uma célula RNN (como acabamos de descrever) determina seu espaço de
hipótese - o espaço no qual você procurará uma boa configuração de modelo durante o
treinamento - mas não determina o que a célula faz; isso é até os pesos das células. A mesma
célula com diferentes pesos pode estar fazendo coisas muito diferentes. Assim, a combinação de
operações que compõem uma célula RNN é melhor interpretada como um conjunto
de restrições em sua pesquisa, não como um design no sentido de engenharia.
Para um pesquisador, parece que a escolha de tais restrições - a questão de como implementar
células RNN - é melhor deixar para algoritmos de otimização (como algoritmos genéticos ou
processos de aprendizado por reforço) do que para engenheiros humanos. E no futuro, é assim
que vamos construir redes. Em resumo: você não precisa entender nada sobre a arquitetura
específica de uma LSTMcélula; como um ser humano, não deveria ser seu trabalho entendê-
lo. Basta ter em mente o que a LSTMcélula deve fazer: permitir que informações passadas sejam
reinjetadas mais tarde, combatendo assim o problema do gradiente de desaparecimento.
model = Sequential ()
perda = 'binary_crossentropy',
metrics = ['acc'])
épocas = 10,
batch_size = 128,
validation_split = 0,2)
Desta vez, você atinge até 89% de precisão de validação. Nada mal: certamente muito melhor
que a SimpleRNNrede - em grande parte porque o LSTM sofre muito menos com o problema do
gradiente de desaparecimento - e um pouco melhor do que a abordagem totalmente conectada
do capítulo 3 , embora você esteja olhando menos dados do que estava no capítulo 3 . Você
está truncando sequências depois de 500 timesteps, enquanto no capítulo 3 , você estava
considerando sequências completas.
Mas esse resultado não é inovador para essa abordagem computacionalmente intensiva. Por que
o desempenho do LSTM não é melhor? Uma razão é que você não fez nenhum esforço para
ajustar hiperparâmetros, como a dimensionalidade de incorporação ou a dimensionalidade de
saída do LSTM. Outro pode ser falta de regularização. Mas honestamente, a principal razão é
que analisar a estrutura global e de longo prazo das revisões (o que a LSTM é boa) não é útil
para um problema de análise de sentimentos. Esse problema básico é bem resolvido
observando-se quais palavras ocorrem em cada revisão e com que frequência. Isso é o que a
primeira abordagem totalmente conectada olhou. Mas existem problemas de processamento de
linguagem natural muito mais difíceis por aí, onde a força do LSTM se tornará aparente: em
particular,
6.2.4. Empacotando
Agora você entende o seguinte:
A seguir, analisaremos vários recursos mais avançados de RNNs, que podem ajudar você a
aproveitar ao máximo seus modelos de sequência de aprendizado profundo.
Nesta seção, revisaremos três técnicas avançadas para melhorar o desempenho e o poder de
generalização de redes neurais recorrentes. No final da seção, você saberá mais sobre o que há
para saber sobre o uso de redes recorrentes com o Keras. Vamos demonstrar todos os três
conceitos sobre um problema de previsão de temperatura, onde você tem acesso a uma série
temporal de pontos de dados provenientes de sensores instalados no telhado de um edifício,
como temperatura, pressão do ar e umidade, que você usa para prever qual a temperatura será
24 horas após o último ponto de dados. Este é um problema bastante desafiador que exemplifica
muitas dificuldades comuns encontradas quando se trabalha com timeseries.
Nós vamos cobrir as seguintes técnicas:
cd ~ / Downloads
mkdir jena_climate
cd jena_climate
wget https://s3.amazonaws.com/keras-datasets/jena_climate_2009_2016.csv.zip
descompacte jena_climate_2009_2016.csv.zip
importar os
f = aberto (fname)
data = f.read ()
f.close ()
imprimir (cabeçalho)
Isso gera uma contagem de 420.551 linhas de dados (cada linha é um timestep: um registro de
uma data e 14 valores relacionados ao clima), bem como o seguinte cabeçalho:
["Data hora",
"p (mbar)",
"T (degC)",
"Tpot (K)",
"Tdew (degC)",
"rh (%)",
"VPmax (mbar)",
"VPact (mbar)",
"VPdef (mbar)",
"sh (g / kg)",
"rho (g / m ** 3)",
"wv (m / s)",
"max. wv (m / s)",
"wd (deg)"]
Por exemplo, aqui está o gráfico da temperatura (em graus Celsius) ao longo do tempo (veja
a figura 6.18 ). Nesta trama, você pode ver claramente a periodicidade anual da temperatura.
Aqui está um gráfico mais estreito dos primeiros 10 dias de dados de temperatura (ver figura
6.19 ). Como os dados são registrados a cada 10 minutos, você obtém 144 pontos de dados por
dia.
Figura 6.19. Temperatura nos primeiros 10 dias do conjunto de dados (° C)
Neste gráfico, você pode ver a periodicidade diária, especialmente evidente nos últimos 4
dias. Observe também que esse período de 10 dias deve ser proveniente de um mês de inverno
bastante frio.
Se você estivesse tentando prever a temperatura média no mês seguinte, dados alguns meses de
dados anteriores, o problema seria fácil, devido à periodicidade confiável dos dados em escala
anual. Mas olhando para os dados em uma escala de dias, a temperatura parece muito mais
caótica. É esta timeseries previsível em uma escala diária? Vamos descobrir.
Pré-processe os dados para um formato que uma rede neural possa ingerir. Isso é fácil:
os dados já são numéricos, então você não precisa fazer nenhuma vetorização. Mas cada
timeseries nos dados está em uma escala diferente (por exemplo, a temperatura é
tipicamente entre -20 e +30, mas a pressão atmosférica, medida em mbar, é em torno
de 1.000). Você irá normalizar cada série de tempo de forma independente, para que
todos eles tomem pequenos valores em uma escala similar.
Escreva um gerador Python que use a matriz atual de dados flutuantes e forneça lotes de
dados do passado recente, junto com uma temperatura-alvo no futuro. Como as
amostras no conjunto de dados são altamente redundantes (a amostra N e a
amostra N + 1 terão a maioria de seus timesteps em comum), seria um desperdício
atribuir explicitamente cada amostra. Em vez disso, você gerará as amostras
rapidamente usando os dados originais.
Você pré-processará os dados subtraindo a média de cada timeseries e dividindo pelo desvio
padrão. Você usará os primeiros 200.000 timesteps como dados de treinamento, portanto
calcule a média e o desvio padrão apenas nessa fração dos dados.
float_data - = mean
float_data / = std
A Listagem 6.33 mostra o gerador de dados que você usará. Ela produz uma tupla (samples,
targets), onde samplesestá um lote de dados de entrada e targetsé a matriz
correspondente de temperaturas alvo. Leva os seguintes argumentos:
data- A matriz original de dados de ponto flutuante, que você normalizou na listagem
6.32 .
lookback—Quantos timesteps retornam os dados de entrada devem ir.
delay- Quantos timesteps no futuro o alvo deveria ser.
min_indexe max_index—Indices na datamatriz que delimitam quais
timesteps serão extraídos. Isso é útil para manter um segmento dos dados para
validação e outro para teste.
shuffle—Para misturar as amostras ou desenhá-las em ordem cronológica.
batch_size—O número de amostras por lote.
step—O período, em tempo, no qual você amostra dados. Você configurará para 6 para
desenhar um ponto de dados a cada hora.
i = min_index + lookback
enquanto 1:
se shuffle:
linhas = np.random.randint (
outro:
if i + batch_size> = max_index:
i = min_index + lookback
lookback // step,
data.shape [-1]))
Agora, vamos usar a generatorfunção abstrata para instanciar três geradores: um para
treinamento, um para validação e um para teste. Cada um examinará diferentes segmentos
temporais dos dados originais: o gerador de treinamento analisa os primeiros 200.000
timesteps, o gerador de validação examina os 100.000 a seguir e o gerador de teste examina o
restante.
lookback = 1440
etapa = 6
atraso = 144
batch_size = 128
lookback = lookback
atraso = atraso
min_index = 0,
max_index = 200000,
shuffle = Verdadeiro
passo = passo
batch_size = batch_size)
lookback = lookback
atraso = atraso
min_index = 200001,
max_index = 300000,
passo = passo
batch_size = batch_size)
lookback = lookback
atraso = atraso
min_index = 300001,
max_index = None,
passo = passo
batch_size = batch_size)
Neste caso, a série de tempos de temperatura pode ser seguramente assumida como contínua
(as temperaturas de amanhã provavelmente estarão próximas das temperaturas de hoje), bem
como periódicas com um período diário. Assim, uma abordagem de senso comum é sempre
prever que a temperatura daqui a 24 horas será igual à temperatura no momento. Vamos avaliar
essa abordagem usando a métrica de erro absoluto médio (MAE):
batch_maes = []
batch_maes.append (mae)
evaluate_naive_method ()
Isso produz um MAE de 0,29. Como os dados de temperatura foram normalizados para serem
centralizados em 0 e têm um desvio padrão de 1, esse número não é imediatamente
interpretável. Isso se traduz em um erro absoluto médio de 0,29 × temperature_stdgraus
Celsius: 2,57 ° C.
Esse é um erro absoluto médio razoavelmente grande. Agora o jogo é usar seu conhecimento de
aprendizagem profunda para fazer melhor.
A listagem a seguir mostra um modelo totalmente conectado que inicia com o achatamento dos
dados e, em seguida, o executa por meio de duas Densecamadas. Observe a falta de função de
ativação na última Densecamada, o que é típico de um problema de regressão. Você usa o MAE
como a perda. Como você avalia exatamente os mesmos dados e com a mesma métrica que fez
com a abordagem de senso comum, os resultados serão diretamente comparáveis.
model = Sequential ()
model.add (layers.Flatten (input_shape = (lookback // passo, float_data.shape
[-1])))
steps_per_epoch = 500,
épocas = 20,
validation_data = val_gen,
validation_steps = val_steps)
Vamos exibir as curvas de perda para validação e treinamento (veja a figura 6.20 ).
Figura 6.20. Perda de treinamento e validação na tarefa de previsão de temperatura do Jena com uma rede simples e
densamente conectada
plt.figure ()
plt.plot (epochs, loss, 'bo', label = 'perda de treino')
plt.legend ()
plt.show ()
Algumas das perdas de validação estão próximas da linha de base de não-aprendizado, mas não
de forma confiável. Isso mostra o mérito de ter essa linha de base em primeiro lugar: não é fácil
superá-la. Seu senso comum contém muitas informações valiosas que um modelo de
aprendizado de máquina não tem acesso.
Você pode se perguntar, se existe um modelo simples e de bom desempenho para ir dos dados
até os alvos (a linha de base do senso comum), por que o modelo que você está treinando não o
encontra e melhora? Porque esta solução simples não é o que sua configuração de treinamento
está procurando. O espaço dos modelos em que você está procurando uma solução - ou seja, o
espaço da sua hipótese - é o espaço de todas as redes possíveis de duas camadas com a
configuração que você definiu. Essas redes já são bastante complicadas. Quando você está
procurando por umCom um espaço de modelos complicados, a linha de base simples e de bom
desempenho pode ser desaprendida, mesmo que seja tecnicamente parte do espaço de
hipóteses. Essa é uma limitação bastante significativa do aprendizado de máquina em geral: a
menos que o algoritmo de aprendizado seja codificado para procurar um tipo específico de
modelo simples, o aprendizado de parâmetro pode, às vezes, falhar em encontrar uma solução
simples para um problema simples.
Junyoung Chung et al., “Avaliação Empírica de Redes Neurais Recorrentes Conectadas na Modelagem de Seqüências”, Conferência sobre
model = Sequential ()
steps_per_epoch = 500,
épocas = 20,
validation_data = val_gen,
validation_steps = val_steps)
A Figura 6.21 mostra os resultados. Muito melhor! Você pode superar significativamente a
linha de base de senso comum, demonstrando o valor do aprendizado de máquina, bem como a
superioridade das redes recorrentes em comparação com redes densas de achatamento de
seqüência nesse tipo de tarefa.
Figura 6.21. Perda de treinamento e validação na tarefa de previsão de temperatura de Jena com uma GRU
2016, http://mlg.eng.cam.ac.uk/yarin/blog_2248.html .
Yarin Gal fez sua pesquisa usando Keras e ajudou a construir esse mecanismo diretamente nas
camadas recorrentes de Keras. Cada camada recorrente em Keras possui dois argumentos
relacionados a dropout:, dropoutum float especificando a taxa de dropout para unidades de
entrada da camada, e recurrent_dropoutespecificando a taxa de dropout das unidades
recorrentes. Vamos adicionar dropout e dropout recorrente à GRUcamada e ver como isso afeta o
overfitting. Como as redes que estão sendo regularizadas com o dropout sempre demoram mais
para convergir totalmente, você treinará a rede para o dobro de épocas.
Listagem 6.40. Treinamento e avaliação de um modelo baseado em GRU regularizado por desistência
model = Sequential ()
abandono = 0,2,
recurrent_dropout = 0,2,
épocas = 40,
validation_data = val_gen,
validation_steps = val_steps)
A Figura 6.22 mostra os resultados. Sucesso! Você não está mais exagerando durante as
primeiras 30 épocas. Mas embora você tenha pontuações de avaliação mais estáveis, suas
melhores pontuações não são muito menores do que eram anteriormente.
Figura 6.22. Perda de treinamento e validação na tarefa de previsão de temperatura de Jena com um GRU regularizado por
abandono
Para empilhar camadas recorrentes umas em cima das outras em Keras, todas as camadas
intermediárias devem retornar sua sequência completa de saídas (um tensor 3D) em vez de sua
saída no último intervalo de tempo. Isso é feito especificando return_sequences=True.
Listagem 6.41. Treinamento e avaliação de um modelo GRU empilhado regularizado por desistência
abandono = 0,1,
recurrent_dropout = 0,5,
return_sequences = Verdadeiro,
abandono = 0,1,
recurrent_dropout = 0,5))
steps_per_epoch = 500,
épocas = 40,
validation_data = val_gen,
validation_steps = val_steps)
A Figura 6.23 mostra os resultados. Você pode ver que a camada adicionada melhora os
resultados um pouco, embora não significativamente. Você pode tirar duas conclusões:
Como você ainda não está super adaptando muito mal, você pode aumentar com
segurança o tamanho de suas camadas em uma busca pela melhoria da perda de
validação. Isto tem um custo computacional não desprezível, no entanto.
Adicionar uma camada não ajudou por um fator significativo, portanto, você pode estar
vendo retornos decrescentes de aumentar a capacidade da rede neste momento.
Figura 6.23. Perda de treinamento e validação na tarefa de previsão de temperatura Jena com uma rede GRU empilhada
Notavelmente, o fato de que as camadas RNN nesta seção processaram sequências em ordem
cronológica (datas mais antigas primeiro) pode ter sido uma decisão arbitrária. Pelo menos, é
uma decisão que não fizemos nenhuma tentativa de questionar até agora. Os RNNs poderiam
ter funcionado bem o suficiente se processassem seqüências de entrada em ordem
anticronológica, por exemplo (newest timepeps first)? Vamos tentar isso na prática e ver o que
acontece. Tudo o que você precisa fazer é gravar uma variante do gerador de dados onde as
seqüências de entrada são revertidas ao longo da dimensão de tempo (substitua a última linha
por yield samples[:, ::-1, :], targets). Treinando a mesma GRUrede de camada
única usada no primeiro experimento desta seção, você obtém os resultados mostrados
na figura 6.24 .
Figura 6.24. Perda de treinamento e validação na tarefa de previsão de temperatura de Jena com uma GRU treinada em
sequências invertidas
A GRU de ordem reversa tem um desempenho muito baixo até mesmo na linha de base de senso
comum, indicando que, nesse caso, o processamento cronológico é importante para o sucesso de
sua abordagem. Isso faz todo o sentido: o subjacenteGRUA camada normalmente será melhor
para lembrar o passado recente do que o passado distante e, naturalmente, os pontos de dados
climáticos mais recentes são mais preditivos do que os pontos de dados mais antigos para o
problema (é isso que torna a linha de base de senso comum bastante forte). Assim, a versão
cronológica da camada é obrigada a superar a versão de ordem inversa. É importante ressaltar
que isso não é verdade para muitos outros problemas, incluindo a linguagem natural:
intuitivamente, a importância de uma palavra para entender uma frase geralmente não depende
de sua posição na sentença. Vamos tentar o mesmo truque no exemplo do LSTM IMDB
da seção 6.2 .
max_features = 10000 1
maxlen = 500 2
num_words = max_features) 3
model = Sequential ()
perda = 'binary_crossentropy',
metrics = ['acc'])
épocas = 10,
batch_size = 128,
validation_split = 0,2)
Você obtém desempenho quase idêntico ao da ordem cronológica LSTM. Notavelmente, em tal
conjunto de dados de texto, inverteu-ordem de processamento funciona tão bem como o
processamento cronológica, confirmando a hipótese de que, embora a ordem das
palavras faz questão em linguagem compreensão, que pedir que você usa não é crucial. É
importante ressaltar que um RNN treinado em sequências invertidas aprenderá diferentes
representações do que um treinado nas sequências originais, da mesma forma que você teria
modelos mentais diferentes se o tempo fluísse para trás no mundo real - se você vivesse uma
vida onde morresse no primeiro dia e nasceram no seu último dia. Em aprendizado de máquina,
representações diferentes, mas úteisSempre vale a pena explorar, e quanto mais eles diferem,
melhor: eles oferecem um novo ângulo a partir do qual analisar seus dados, capturando aspectos
dos dados que foram perdidos por outras abordagens e, portanto, podem ajudar a melhorar o
desempenho em uma tarefa. Essa é a intuição por trás do conjunto , um conceito que
exploraremos no capítulo 7 .
Um RNN bidirecional explora essa ideia para melhorar o desempenho de RNNs de ordem
cronológica. Ele examina sua sequência de entrada nos dois sentidos (consulte a figura 6.25 ),
obtendo representações potencialmente mais ricas e capturando padrões que podem ter sido
perdidos somente pela versão de ordem cronológica.
Figura 6.25. Como funciona uma camada RNN bidirecional
Para instanciar um RNN bidirecional em Keras, você usa a Bidirectionalcamada, que toma
como primeiro argumento uma instância de camada recorrente. Bidirectionalcria uma
segunda instância separada dessa camada recorrente e usa uma instância para processar as
seqüências de entrada em ordem cronológica e a outra instância para processar as seqüências de
entrada em ordem inversa. Vamos tentar na tarefa de análise de sentimentos do IMDB.
model = Sequential ()
épocas = 10,
batch_size = 128,
validation_split = 0,2)
Ele executa um pouco melhor que o normal que LSTMvocê tentou na seção anterior, alcançando
mais de 89% de precisão na validação. Também parece sobrecarregar mais rapidamente, o que
não é surpreendente porque uma camada bidirecional tem duas vezes mais parâmetros que um
cronológico LSTM. Com alguma regularização, a abordagem bidirecional provavelmente seria
um bom desempenho nessa tarefa.
model = Sequential ()
model.add (layers.Bidirectional (
steps_per_epoch = 500,
épocas = 40,
validation_data = val_gen,
validation_steps = val_steps)
Isso funciona tão bem quanto a GRUcamada normal . É fácil entender por quê: toda a capacidade
preditiva deve vir da metade cronológica da rede, porque a metade anticronológica é conhecida
por estar gravemente abaixo do desempenho nessa tarefa (novamente, porque o passado recente
é muito mais importante do que o passado distante neste caso). ).
Como sempre, o aprendizado profundo é mais uma arte do que uma ciência. Podemos fornecer
diretrizes que sugiram o que provavelmente funcionará ou não em determinado problema, mas,
em última análise, todo problema é único; você terá que avaliar empiricamente estratégias
diferentes. Atualmente, não há uma teoria que lhe diga com antecedência o que você deve fazer
para solucionar um problema de maneira ideal. Você deve iterar.
6.3.10. Empacotando
Veja o que você deve tirar desta seção:
Como você aprendeu no capítulo 4 , ao abordar um novo problema, é bom primeiro
estabelecer linhas de base de bom senso para sua métrica escolhida. Se você não tem
uma base para vencer, você não pode dizer se você está fazendo um progresso real.
Tente modelos simples antes dos caros, para justificar a despesa adicional. Às vezes, um
modelo simples se tornará sua melhor opção.
Quando você tem dados em que a ordenação temporal é importante, as redes
recorrentes são um ótimo ajuste e superam facilmente os modelos que primeiro nivelam
os dados temporais.
Para usar o dropout com redes recorrentes, você deve usar uma máscara de dropout de
constante de tempo e máscara de dropout recorrente. Estes são construídos em
camadas recorrentes Keras, então tudo que você tem a fazer é usar
os argumentos dropoute recurrent_dropoutde camadas recorrentes.
Os RNNs empilhados fornecem mais poder representacional do que uma única camada
RNN. Eles também são muito mais caros e, portanto, nem sempre valem a
pena. Embora eles ofereçam ganhos claros em problemas complexos (como a tradução
automática), eles nem sempre são relevantes para problemas menores e mais simples.
Os RNNs bidirecionais, que analisam uma sequência nos dois sentidos, são úteis em
problemas de processamento de linguagem natural. Mas eles não são fortes em dados
de sequência, onde o passado recente é muito mais informativo do que o início da
sequência.
Nota
Há dois conceitos importantes que não abordaremos em detalhes aqui: atenção recorrente e
mascaramento de sequência. Ambos tendem a ser especialmente relevantes para o
processamento de linguagem natural e não são particularmente aplicáveis ao problema de
previsão de temperatura. Vamos deixá-los para estudo futuro fora deste livro.
Alguns leitores são obrigados a tomar as técnicas que introduzimos aqui e testá-las no problema
da previsão do preço futuro dos títulos no mercado de ações (ou taxas de câmbio, e assim por
diante). Os mercados têm características estatísticas muito diferentes dos fenômenos naturais,
como padrões climáticos. Tentar usar o aprendizado de máquina para vencer mercados, quando
você só tem acesso a dados disponíveis publicamente, é um esforço difícil, e é provável que você
perca tempo e recursos sem nada para mostrar.
Lembre-se sempre de que, quando se trata de mercados, o desempenho passado não é um bom
indicador de retornos futuros - olhar no espelho retrovisor é uma maneira ruim de dirigir. O
aprendizado de máquina, por outro lado, é aplicável a conjuntos de dados em que o
passado é um bom preditor do futuro.
No capítulo 5 , você aprendeu sobre redes neurais convolucionais (convnets) e como elas
funcionam particularmente bem em problemas de visão computacional, devido à sua capacidade
de operar de forma convolucional , extraindo recursos de correções de entrada locais e
permitindo modularidade de representação e eficiência de dados. As mesmas propriedades que
fazem as redes se destacarem na visão computacional também as tornam altamente relevantes
para o processamento de seqüências. O tempo pode ser tratado como uma dimensão espacial,
como a altura ou a largura de uma imagem 2D.
Tais capas 1D podem ser competitivas com RNNs em certos problemas de processamento de
sequência, geralmente a um custo computacional consideravelmente mais
barato. Recentemente, os modelos 1D, normalmente usados com núcleos dilatados, foram
usados com grande sucesso para geração de áudio e tradução automática. Além desses sucessos
específicos, há muito tempo se sabe que pequenas convnets 1D podem oferecer uma alternativa
rápida aos RNNs para tarefas simples, como classificação de texto e previsão de timeseries.
Figura 6.26. Como funciona a convolução 1D: cada timestep de saída é obtido de um patch temporal na sequência de entrada.
Essas camadas de convolução 1D podem reconhecer padrões locais em uma sequência. Como a
mesma transformação de entrada é executada em cada patch, um padrão aprendido em uma
determinada posição em uma sentença pode ser reconhecido posteriormente em uma posição
diferente, tornando invariante de tradução de convés 1D (para traduções temporais). Por
exemplo, uma seqüência de processamento de caracteres de convecção 1D usando janelas de
convolução de tamanho 5 deve ser capaz de aprender palavras ou fragmentos de tamanho 5 ou
menos, e deve ser capaz de reconhecer essas palavras em qualquer contexto em uma sequência
de entrada. Uma convã 1D de nível de personagem é, portanto, capaz de aprender sobre a
morfologia da palavra.
Vamos construir uma simples convecção 1D de duas camadas e aplicá-la à tarefa de classificação
de sentimentos do IMDB com a qual você já está familiarizado. Como lembrete, este é o código
para obter e pré-processar os dados.
max_features = 10000
max_len = 500
As capas 1D são estruturadas da mesma forma que suas contrapartes 2D, que você usou
no capítulo 5 : elas consistem em uma pilha Conv1De MaxPooling1Dcamadas, terminando
em uma camada de pool global ou uma Flattencamada, que transformam as saídas 3D em
saídas 2D, permitindo você adicionar uma ou mais Densecamadas ao modelo para classificação
ou regressão.
Uma diferença, no entanto, é o fato de que você pode se dar ao luxo de usar janelas de
convolução maiores com os modelos 1D. Com uma camada de convolução 2D, uma janela de
convolução 3 × 3 contém 3 × 3 = 9 vetores de recursos; mas com uma camada de convolução 1D,
uma janela de convolução de tamanho 3 contém apenas 3 vetores de recursos. Você pode assim
facilmente ter janelas de convolução 1D de tamanho 7 ou 9.
Listagem 6.46. Treinamento e avaliação de uma simples convenção 1D nos dados do IMDB
model = Sequential ()
model.summary ()
perda = 'binary_crossentropy',
metrics = ['acc'])
épocas = 10,
batch_size = 128,
validation_split = 0,2)
Figura 6.28. Treinamento e validação de validação no IMDB com uma simples convulsão 1D
Listagem 6.47. Treinar e avaliar uma simples convecção 1D nos dados de Jena
model = Sequential ()
steps_per_epoch = 500,
épocas = 20,
validation_data = val_gen,
validation_steps = val_steps)
A validação do MAE permanece nos 0,40s: você não pode nem superar a linha de base do senso
comum usando a pequena convnet. Novamente, isso acontece porque o convnet procura
padrões em qualquer ponto da entrada das séries de tempo e não tem conhecimento da posição
temporal de um padrão que vê (no início, no final e assim por diante). Como os pontos de dados
mais recentes devem ser interpretados de maneira diferente dos pontos de dados mais antigos,
no caso desse problema de previsão específico, a convnet falha ao produzir resultados
significativos. Essa limitação de convnets não é um problema com os dados do IMDB, porque os
padrões de palavras-chave associados a um sentimento positivo ou negativo são informativos
independentemente de onde eles são encontrados nas sentenças de entrada.
Uma estratégia para combinar a velocidade e a leveza das conversas com a sensibilidade à
ordem dos RNNs é usar uma convnet 1D como uma etapa de pré-processamento antes de uma
RNN (consulte a figura 6.30 ). Isso é especialmente benéfico quando você está lidando com
sequências que são tão longas que elas não podem ser processadas de maneira realista com
RNNs, como sequências com milhares de etapas. A convnet transformará a longa seqüência de
entrada em seqüências muito mais curtas (com resolução reduzida) de recursos de nível
superior. Essa seqüência de recursos extraídos se torna a entrada para a parte RNN da rede.
Figura 6.30. Combinando uma convnet 1D e um RNN para processar seqüências longas
Essa técnica não é vista com freqüência em trabalhos de pesquisa e aplicações práticas,
possivelmente porque não é bem conhecida. É eficaz e deveria ser mais comum. Vamos tentar
no conjunto de dados de previsão de temperatura. Como essa estratégia permite manipular
seqüências muito mais longas, é possível observar dados de mais tempo (aumentando
o lookbackparâmetro do gerador de dados) ou ver as séries de tempo de alta resolução
(diminuindo o stepparâmetro do gerador). Aqui, um pouco arbitrariamente, você usará
um stepque é metade do tamanho, resultando em uma série de tempo duas vezes mais longa,
onde oos dados de temperatura são amostrados a uma taxa de 1 ponto por 30 minutos. O
exemplo reutiliza a generatorfunção definida anteriormente.
Listagem 6.48. Preparando geradores de dados de alta resolução para o conjunto de dados do Jena
etapa = 3 1
lookback = 720 2
atraso = 144 2
lookback = lookback
atraso = atraso
min_index = 0,
max_index = 200000,
shuffle = Verdadeiro
passo = passo)
lookback = lookback
atraso = atraso
min_index = 200001,
max_index = 300000,
passo = passo)
lookback = lookback
atraso = atraso
min_index = 300001,
max_index = None,
passo = passo)
Este é o modelo, começando com duas Conv1Dcamadas e seguindo com uma GRUcamada. A
Figura 6.31mostra os resultados.
Figura 6.31. Perda de treinamento e validação na tarefa de previsão de temperatura de Jena com uma convecção 1D seguida
por uma gru
model = Sequential ()
model.summary ()
steps_per_epoch = 500,
épocas = 20,
validation_data = val_gen,
validation_steps = val_steps)
A julgar pela perda de validação, essa configuração não é tão boa quanto a
regularização GRUsozinha, mas é significativamente mais rápida. Ele analisa o dobro de dados, o
que, nesse caso, não parece ser muito útil, mas pode ser importante para outros conjuntos de
dados.
6.4.5. Empacotando
Veja o que você deve tirar desta seção:
Da mesma forma que as réplicas 2D funcionam bem para processar padrões visuais no
espaço 2D, as réplicas 1D têm um bom desempenho no processamento de padrões
temporais. Eles oferecem uma alternativa mais rápida para os RNNs em alguns
problemas, em particular tarefas de processamento de linguagem natural.
Tipicamente, as conversas 1D são estruturadas de maneira muito semelhante a seus
equivalentes 2D do mundo da visão computacional: elas consistem em pilhas
de Conv1Dcamadas e Max-Pooling1Dcamadas, terminando em uma operação de
agrupamento global ou operação de achatamento.
Como os RNNs são extremamente caros para processar sequências muito longas, mas as
convntas 1D são baratas, pode ser uma boa ideia usar uma convnet 1D como uma etapa
de pré-processamento antes de uma RNN, encurtando a sequência e extraindo
representações úteis para o RNN processar.
Resumo do capítulo
Neste capítulo, você aprendeu as seguintes técnicas, que são amplamente aplicáveis a
qualquer conjunto de dados de dados de sequência, do texto às séries de tempo:
Como tokenizar texto
Que palavra é a incorporação e como usá-las
Quais são as redes recorrentes e como usá-las
Como empilhar camadas RNN e usar RNNs bidirecionais para construir
modelos de processamento de seqüência mais poderosos
Como usar convnets 1D para processamento sequencial
Como combinar convnets 1D e RNNs para processar seqüências longas
Você pode usar RNNs para regressão de timeseries (“predizendo o futuro”), classificação de
timeseries, detecção de anomalias em timeseries e rotulagem de seqüências (como identificação
de nomes ou datas em sentenças).
Da mesma forma, você pode usar as convés 1D para tradução automática (modelos
convolucionais seqüência a seqüência, como SliceNet [ a ] ), classificação de documentos e
correção ortográfica.
uma
Veja https://arxiv.org/abs/1706.03059 .
Se a ordem global é importante em seus dados de sequência, é preferível usar uma rede
recorrente para processá-la. Este é tipicamente o caso de timeseries, onde o passado recente é
provavelmente mais informativo do que o passado distante.
Se a ordenação global não for fundamentalmente significativa , então as torneiras 1D
funcionarão pelo menos tão bem e serão mais baratas. Este é frequentemente o caso dos dados
de texto, em que uma palavra-chave encontrada no início de uma frase é tão significativa quanto
uma palavra-chave encontrada no final.
Capítulo 6. Aprendizagem profunda para texto e
sequências
Este capítulo cobre
Este capítulo explora modelos de aprendizagem profunda que podem processar textos
(entendidos como seqüências de palavras ou sequências de caracteres), séries de tempo e dados
de sequência em geral. Os dois algoritmos de aprendizagem profunda fundamentais para o
processamento de sequências são as redes neurais recorrentes e as conversões 1D , a versão
unidimensional das convnets 2D que abordamos nos capítulos anteriores. Vamos discutir ambas
as abordagens neste capítulo.
O texto é uma das formas mais difundidas de dados de sequência. Pode ser entendido como uma
sequência de caracteres ou uma sequência de palavras, mas é mais comum trabalhar no nível
das palavras. Os modelos de processamento de sequências de aprendizado profundo
introduzidos nas seções a seguir podem usar texto para produzir uma forma básica de
compreensão de linguagem natural, suficiente para aplicativos que incluem classificação de
documentos, análise de sentimentos, identificação de autores e até perguntas e respostas (QA)
(em um contexto restrito). É claro, tenha em mente ao longo deste capítulo que nenhum desses
modelos de aprendizagem profunda realmente entende o texto em um sentido humano; em vez
disso, esses modelos podem mapear a estrutura estatística da linguagem escrita, o que é
suficiente para resolver muitas tarefas textuais simples.
Como todas as outras redes neurais, os modelos de aprendizagem profunda não aceitam como
texto bruto de entrada: eles só funcionam com tensores numéricos. Vectorizing text é o processo
de transformar texto em tensores numéricos. Isso pode ser feito de várias maneiras:
Word n-grams são grupos de N (ou menos) palavras consecutivas que você pode extrair de uma
frase. O mesmo conceito também pode ser aplicado a caracteres em vez de palavras.
Aqui está um exemplo simples. Considere a frase “O gato sentou-se no tapete”. Ele pode ser
decomposto no seguinte conjunto de 2 gramas:
Como o bag-of-words não é um método de tokenização que preserva a ordem (os tokens gerados
são entendidos como um conjunto, não uma sequência e a estrutura geral das sentenças é
perdida), ele tende a ser usado em processamento de linguagem superficial modelos, em vez de
modelos de aprendizagem profunda. A extração de n-grams é uma forma de engenharia de
recursos, e o aprendizado profundo acaba com esse tipo de abordagem rígida e quebradiça,
substituindo-a por aprendizado de recurso hierárquico. As convnets unidimensionais e as redes
neurais recorrentes, apresentadas mais adiante neste capítulo, são capazes de aprender
representações para grupos de palavras e caracteres sem serem explicitamente informados
sobre a existência de tais grupos, observando sequências contínuas de palavras ou
caracteres. Por esta razão, Não vamos cobrir mais n-gramas neste livro. Mas lembre-se de que
eles são uma ferramenta de engenharia de recursos poderosa e inevitável ao usar modelos de
processamento de texto simples e superficiais, como a regressão logística e florestas aleatórias.
Naturalmente, uma codificação a quente também pode ser feita no nível do personagem. Para
indubitavelmente levar para casa o que é uma codificação quente e como implementá-la,
as listagens 6.1e 6.2 mostram dois exemplos de brinquedo: um para palavras e outro para
caracteres.
samples = ['O gato sentou no tatame.', 'O cachorro comeu meu dever de casa.']
1
token_index = {} 2
max_length = 10 5
comprimento máximo,
1 Dados iniciais: uma entrada por amostra (neste exemplo, uma amostra é
uma sentença, mas poderia ser um documento inteiro)
2 Cria um índice de todos os tokens nos dados
3 Tokeniza as amostras pelo método de divisão. Na vida real, você também
tira pontos e caracteres especiais das amostras.
4 Atribui um índice exclusivo para cada palavra única. Observe que você
não atribui o índice 0 a nada.
5 Vectoriza as amostras. Você só considerará as primeiras palavras
max_length em cada amostra.
6 Aqui é onde você armazena os resultados.
string de importação
samples = ['O gato sentou no tatame.', 'O cachorro comeu meu dever de casa.']
caracteres = string.printable 1
max_length = 50
Observe que o Keras possui utilitários integrados para fazer uma codificação de texto quente no
nível de palavra ou nível de caractere, a partir de dados de texto bruto. Você deve usar esses
utilitários, pois eles cuidam de vários recursos importantes, como descartar caracteres especiais
de strings e levar em conta apenas as N palavras mais comuns em seu conjunto de dados (uma
restrição comum, para evitar lidar com espaços vetoriais de entrada muito grandes ).
samples = ['O gato sentou no tatame.', 'O cachorro comeu meu dever de casa.']
word_index = tokenizer.word_index 5
Uma variante da codificação one-hot é o chamado truque de hash quente , que você pode usar
quando o número de tokens exclusivos no seu vocabulário for muito grande para manipular
explicitamente. Em vez de atribuir explicitamente um índice a cada palavra e manter uma
referência desses índices em um dicionário, você pode dividir as palavras em vetores de
tamanho fixo. Isso geralmente é feito com uma função de hash muito leve. A principal vantagem
deste método é que ele acaba com a manutenção de um índice de palavras explícito, que
economiza memória e permite a codificação online dos dados (você pode gerar vetores de token
imediatamente, antes de ver todos os dados disponíveis). A única desvantagem dessa
abordagem é que ela é suscetível a colisões de hash: duas palavras diferentes podem acabar com
o mesmo hash, e subseqüentemente qualquer modelo de aprendizado de máquina olhando para
esses hashes não será capaz de dizer a diferença entre essas palavras. A probabilidade de
colisões de hash diminui quando a dimensionalidade do espaço de hash é muito maior do que o
número total de tokens exclusivos sendo hash.
Listagem 6.4. Codificação one-hot no nível da palavra com truque de hashing (exemplo de brinquedo)
samples = ['O gato sentou no tatame.', 'O cachorro comeu meu dever de casa.']
dimensionalidade = 1000 1
max_length = 10
Figura 6.2. Enquanto as representações de palavras obtidas a partir de uma codificação ou hash quente são esparsas, de alta
dimensão e codificadas, as incorporações de palavras são densas, relativamente pouco dimensionais e aprendidas com os
dados.
A maneira mais simples de associar um vetor denso a uma palavra é escolher o vetor
aleatoriamente. O problema com essa abordagem é que o espaço de incorporação resultante não
tem estrutura: por exemplo, as palavras precisas e exatas podem ter inclusões completamente
diferentes, embora sejam intercambiáveis na maioria das sentenças. É difícil para uma rede
neural profunda dar sentido a um espaço de incorporação tão ruidoso e não estruturado.
Para obter um pouco mais abstrato, as relações geométricas entre os vetores de palavras devem
refletir as relações semânticas entre essas palavras. Os embeddings do Word destinam-se a
mapear a linguagem humana para um espaço geométrico. Por exemplo, em um espaço de
incorporação razoável, você esperaria que os sinônimos fossem incorporados em vetores de
palavras semelhantes; e, em geral, você esperaria que a distância geométrica (como a distância
L2) entre quaisquer dois vetores de palavras se relacionasse com a distância semântica entre as
palavras associadas (palavras significando que coisas diferentes são embutidas em pontos
distantes umas das outras, enquanto palavras relacionadas são mais perto). Além da distância,
você pode querer direções específicas no espaço de incorporação para ser significativo. Para
tornar isso mais claro, vamos dar uma olhada em um exemplo concreto.
Existe algum espaço de incorporação de palavras ideal que mapeie perfeitamente a linguagem
humana e possa ser usado para qualquer tarefa de processamento de linguagem
natural? Possivelmente, mas ainda temos que computar qualquer coisa do tipo. Além disso, não
existe linguagem humana- Existem muitas línguas diferentes e elas não são isomórficas, porque
uma linguagem é o reflexo de uma cultura específica e de um contexto específico. Mas, de forma
mais pragmática, o que faz um bom espaço para a palavra depende muito de sua tarefa: o espaço
perfeito para incorporar palavras a um modelo de análise de opinião de filme em inglês pode
parecer diferente do espaço ideal para uma linguagem jurídica em inglês. - modelo de
classificação de documentos, porque a importância de certos relacionamentos semânticos varia
de tarefa para tarefa.
Portanto, é razoável aprender um novo espaço de incorporação a cada nova tarefa. Felizmente, a
retropropagação torna isso fácil, e o Keras torna isso ainda mais fácil. É sobre aprender os pesos
de uma camada: a Embeddingcamada.
A Embeddingcamada é melhor entendida como um dicionário que mapeia índices inteiros (que
representam palavras específicas) para vetores densos. Ele recebe inteiros como entrada,
procura esses inteiros em um dicionário interno e retorna os vetores associados. É efetivamente
uma pesquisa de dicionário (veja a figura 6.4 ).
Quando você instancia uma Embeddingcamada, seus pesos (seu dicionário interno de vetores
de token) são inicialmente aleatórios, assim como com qualquer outra camada. Durante o
treinamento, esses vetores de palavras são gradualmente ajustados via retropropagação,
estruturando o espaço em algo que o modelo a jusante pode explorar. Uma vez totalmente
treinado, o espaço de incorporação mostrará muita estrutura - um tipo de estrutura
especializada para o problema específico para o qual você está treinando seu modelo.
Vamos aplicar essa ideia à tarefa de previsão do sentimento de revisão de filme do IMDB com a
qual você já está familiarizado. Primeiro, você preparará rapidamente os dados. Você restringirá
as resenhas de filmes às 10 mil palavras mais comuns (como fez na primeira vez que trabalhou
com esse conjunto de dados) e cortará as resenhas depois de apenas 20 palavras. A rede
iráaprenda embedings de 8 dimensões para cada uma das 10.000 palavras, gire as sequências
inteiras de entrada (tensor inteiro 2D) em sequências embutidas (tensor de flutuação 3D),
achatar o tensor para 2D e treinar uma única Densecamada no topo para classificação.
Listagem 6.6. Carregando os dados do IMDB para uso com uma Embeddingcamada
max_features = 10000 1
maxlen = 20 2
num_words = max_features) 3
model = Sequential ()
model.summary ()
épocas = 10,
batch_size = 32,
validation_split = 0,2)
Você chega a uma precisão de validação de ~ 76%, o que é muito bom, considerando que você
está olhando apenas as primeiras 20 palavras em cada revisão. Mas observe que apenas achatar
as sequências incorporadas e treinar uma única Densecamada no topo leva a um modelo que
trata cada palavra na sequência de entrada separadamente, sem considerar as relações entre
palavras e a estrutura da sentença (por exemplo, este modelo provavelmente trataria ambos
filme é uma bomba ”e“ este filme é a bomba ”como sendo críticas negativas). EstáÉ muito
melhor adicionar camadas recorrentes ou camadas convolucionais 1D sobre as sequências
incorporadas para aprender recursos que levam em conta cada seqüência como um todo. É nisso
que vamos nos concentrar nas próximas seções.
Às vezes, você tem tão poucos dados de treinamento disponíveis que você não pode usar seus
dados sozinho para aprender uma incorporação apropriada de seu vocabulário específica da
tarefa. O que fazes, então?
Em vez de aprender as incorporações de palavras juntamente com o problema que você deseja
resolver, é possível carregar vetores de incorporação a partir de um espaço de incorporação pré-
computado que você sabe que é altamente estruturado e exibe propriedades úteis - que
capturam aspectos genéricos da estrutura da linguagem. O raciocínio por trás do uso de
incorporação de palavras pré-formatadas no processamento de linguagem natural é semelhante
ao uso de convnets pré-formatados na classificação de imagens: você não tem dados suficientes
disponíveis para aprender recursos realmente poderosos, mas espera os recursos de que precisa
ser razoavelmente genérico - ou seja, recursos visuais comuns ou recursos semânticos. Nesse
caso, faz sentido reutilizar os recursos aprendidos em um problema diferente.
Existem vários bancos de dados pré-computados de incorporação de palavras que você pode
baixar e usar em uma Embeddingcamada Keras . Word2vec é um deles. Outro popular é
chamado de Vetores Globais para Representação de Palavras
(GloVe, https://nlp.stanford.edu/projects/glove ), que foi desenvolvido por pesquisadores de
Stanford em 2014. Esta técnica de incorporação é baseada na fatoração de uma matriz de co-
estatísticas de ocorrências. Seus desenvolvedores disponibilizaram integrações pré-computadas
para milhões de tokens ingleses, obtidos a partir de dados da Wikipedia e dados de
rastreamento comuns.
Vejamos como você pode começar a usar os envios do GloVe em um modelo Keras. O mesmo
método é válido para incorporações do Word2vec ou qualquer outro banco de dados de
incorporação de palavras. Você também usará este exemplo para atualizar as técnicas de
tokenização de texto introduzidas há alguns parágrafos: você começará a partir do texto não
processado e trabalhará para cima.
Agora, vamos coletar as avaliações individuais de treinamento em uma lista de strings, uma
string por revisão. Você também coletará os rótulos de revisão (positivo / negativo) em
uma labelslista.
importar os
labels = []
textos = []
f.close ()
if label_type == 'neg':
labels.append (0)
outro:
labels.append (1)
Vamos vetorizar o texto e preparar uma divisão de treinamento e validação, usando os conceitos
introduzidos anteriormente nesta seção. Como a incorporação de palavras pré-concebidas é
particularmente útil em problemas em que há poucos dados de treinamento disponíveis (caso
contrário, é provável que as integrações específicas da tarefa os superem), adicionaremos a
seguinte alteração: restringir os dados de treinamento às primeiras 200 amostras. Então, você
aprenderá a classificar as resenhas de filmes depois de ver apenas 200 exemplos.
maxlen = 100 1
training_samples = 200 2
validation_samples = 10000 3
max_words = 10000 4
tokenizer.fit_on_texts (textos)
word_index = tokenizer.word_index
np.random.shuffle (índices)
dados = dados [índices]
Vamos analisar o arquivo descompactado (um arquivo .txt) para construir um índice que
mapeia palavras (como strings) para sua representação vetorial (como vetores numéricos).
glove_dir = '/Users/fchollet/Downloads/glove.6B'
embeddings_index = {}
para linha em f:
valores = line.split ()
f.close ()
embedding_dim = 100
se eu <max_words:
Definindo um modelo
model = Sequential ()
model.summary ()
A Embeddingcamada tem uma matriz de peso única: uma matriz flutuante 2D em que cada
entrada i é o vetor de palavras que se pretende associar ao índice i . Simples o
suficiente. Carregue a matriz GloVe que você preparou na Embeddingcamada, a primeira
camada no modelo.
Além disso, você congelará a Embeddingcamada (defina seu trainableatributo como False),
seguindo a mesma lógica com a qual já está familiarizado no contexto de recursos convnet pré-
tratados: quando partes de um modelo são pré-tratadas (como sua Embeddingcamada) e partes
são inicializadas aleatoriamente (como seu classificador), as partes pré-tratadas não devem ser
atualizadas durante o treinamento, para evitar esquecer o que elas já sabem. O grandeas
atualizações de gradiente acionadas pelas camadas inicializadas aleatoriamente seriam
prejudiciais aos recursos já aprendidos.
perda = 'binary_crossentropy',
metrics = ['acc'])
épocas = 10,
batch_size = 32,
model.save_weights ('pre_trained_glove_model.h5')
Agora, plote o desempenho do modelo ao longo do tempo (veja figuras 6.5 e 6.6 ).
Figura 6.5. Perda de treinamento e validação ao usar embeddings de palavras pré-rotuladas
Figura 6.6. Precisão de treinamento e validação ao usar embeddings de palavras pré-rotuladas
plt.legend ()
plt.figure ()
plt.legend ()
plt.show ()
Observe que sua milhagem pode variar: como você tem poucas amostras de treinamento, o
desempenho depende muito de exatamente as 200 amostras escolhidas - e você as escolhe
aleatoriamente. Se isto funcionar mal para você, tente escolher um conjunto aleatório diferente
de 200 amostras, para o bem do exercício (na vida real, você não consegue escolher seus dados
de treinamento).
Você também pode treinar o mesmo modelo sem carregar a palavra pré-encadeada e sem
congelar a camada de incorporação. Nesse caso, você aprenderá uma incorporação específica de
tarefas dos tokens de entrada, que geralmente é mais poderosa do que os envoltórios de palavras
pré-roteados quando muitos dados estão disponíveis. Mas neste caso, você tem apenas 200
amostras de treinamento. Vamos tentar (veja as figuras 6.7 e 6.8 ).
Figura 6.7. Perda de treinamento e validação sem usar embeddings de palavras pré-rotuladas
Figura 6.8. Precisão de treinamento e validação sem usar embeddings de palavras pré-rotuladas
model = Sequential ()
model.summary ()
perda = 'binary_crossentropy',
metrics = ['acc'])
épocas = 10,
batch_size = 32,
A precisão de validação fica parada nos 50s baixos. Portanto, neste caso, a incorporação de
palavras pré-formatadas supera os embeddings aprendidos em conjunto. Se você aumentar o
número de amostras de treinamento, isso rapidamente deixará de ser o caso - experimente-o
como um exercício.
Finalmente, vamos avaliar o modelo nos dados de teste. Primeiro, você precisa tokenizar os
dados de teste.
labels = []
textos = []
f.close ()
if label_type == 'neg':
labels.append (0)
outro:
labels.append (1)
model.load_weights ('pre_trained_glove_model.h5')
Você obtém uma precisão de teste chocante de 56%. Trabalhar com apenas algumas amostras de
treinamento é difícil!
6.1.4. Empacotando
Agora você pode fazer o seguinte:
Transforme o texto bruto em algo que uma rede neural pode processar
Use a Embeddingcamada em um modelo Keras para aprender sobre incorporações de
token específicas da tarefa
Use encadeamentos pré-formatados para obter um impulso extra em pequenos
problemas de processamento de linguagem natural
Uma característica importante de todas as redes neurais que você viu até agora, como redes e
convnets densamente conectadas, é que elas não têm memória. Cada entrada mostrada a eles é
processada independentemente, sem estado mantido entre entradas. Com essas redes, para
processar uma seqüência ou uma série temporal de pontos de dados, você precisa mostrar a
sequência inteira para a rede de uma vez: transformá-la em um único ponto de dados. Por
exemplo, isso é o que você fez no exemplo do IMDB: uma revisão completa do filme foi
transformada em um único vetor grande e processada de uma só vez. Essas redes são chamadas
de redes feedforward .
Em contraste, quando você está lendo a sentença atual, você está processando palavra por
palavra - ou melhor, reveste os olhos com sacadas oculares - enquanto mantém memórias do
que veio antes; isso lhe dá uma representação fluida do significado transmitido por essa
sentença. A inteligência biológica processa as informações de forma incremental, mantendo um
modelo interno do que está processando, construído a partir de informações passadas e
constantemente atualizado à medida que novas informações chegam.
Uma rede neural recorrente (RNN) adota o mesmo princípio, ainda que em uma versão
extremamente simplificada: processa seqüências, iterando os elementos de sequência e
mantendo um estadocontendo informações relativas ao que foi visto até o momento. Na
verdade, um RNN é um tipo de rede neural que possui um loop interno (consulte a figura
6.9 ). O estado da RNN é redefinido entre o processamento de duas seqüências independentes
diferentes (como duas revisões diferentes do IMDB), portanto, você considera uma sequência
como um único ponto de dados: uma única entrada na rede. O que muda é que esse ponto de
dados não é mais processado em uma única etapa; em vez disso, a rede faz um loop interno
sobre os elementos da sequência.
Para tornar claras essas noções de loop e state , vamos implementar o forward forward de um
brinquedo RNN em Numpy. Este RNN toma como entrada uma seqüência de vetores, que você
codificará como um tensor 2D de tamanho (timesteps, input_features). Faz um loop
sobre timesteps, e em cada timestep, considera seu estado atual em te a entrada em t(de
shape (input_features,), e combina-os para obter a saída em t. Você então definirá o
estado para a próxima etapa para ser a saída anterior. Para o primeiro timestep, a saída anterior
não está definida, portanto, não há estado atual, portanto, você inicializará o estado como um
vetor all-zero chamado de estado inicial da rede.
state_t = 0 1
state_t = output_t 3
1 O estado em t
2 Itera sobre os elementos da sequência
3 A saída anterior se torna o estado da próxima iteração.
Pode mesmo carne para fora a função f: a transformação da entrada e uma saída em estado vai
ser parametrizado por duas matrizes, We U, e um vector de polarização. É semelhante à
transformação operada por uma camada densamente conectada em uma rede feedforward.
state_t = 0
state_t = output_t
Para tornar essas noções absolutamente inequívocas, vamos escrever uma implementação
ingênua do Numpy do passe para frente do RNN simples.
timesteps = 100 1
input_features = 32 2
output_features = 64 3
b = np.random.random ((output_features,)) 6
successive_outputs = []
successive_outputs.append (output_t) 9
state_t = output_t 10
Como todas as camadas recorrentes em Keras, SimpleRNNpode ser executado em dois modos
diferentes: pode retornar as sequências completas de saídas sucessivas para cada timestep (um
tensor 3D de forma (batch_size, timesteps, output_features)) ou somente a última
saída para cada seqüência de entrada (um tensor 2D de forma (batch_size,
output_features)). Esses dois modos são controlados
pelo return_sequencesargumento do construtor. Vamos ver um exemplo que
usa SimpleRNNe retorna apenas a saída no último momento:
>>> model.summary ()
________________________________________________________________
================================================== ==============
________________________________________________________________
================================================== ==============
>>> model.summary ()
________________________________________________________________
================================================== ==============
________________________________________________________________
================================================== ==============
Às vezes é útil empilhar várias camadas recorrentes, uma após a outra, para aumentar o poder
de representação de uma rede. Em tal configuração, você precisa obter todas as camadas
intermediárias para retornar a sequência completa de saídas:
>>> model.summary ()
________________________________________________________________
================================================== ==============
________________________________________________________________
simplernn_12 (SimpleRNN) (nenhum, nenhum, 32) 2080
________________________________________________________________
________________________________________________________________
________________________________________________________________
================================================== ==============
max_features = 10000 1
maxlen = 500 2
batch_size = 32
num_words = max_features)
model = Sequential ()
épocas = 10,
batch_size = 128,
validation_split = 0,2)
plt.legend ()
plt.figure ()
plt.show ()
Como lembrete, no capítulo 3 , a primeira abordagem ingênua a este conjunto de dados levou
você a uma precisão de teste de 88%. Infelizmente, essa pequena rede recorrente não apresenta
um bom desempenho em comparação com essa linha de base (apenas 85% de precisão de
validação). Parte do problema é que suas entradas consideram apenas as primeiras 500
palavras, em vez de sequências completas - portanto, o RNN tem acesso a menos informações
do que o modelo de linha de base anterior. O restante do problema é que SimpleRNNnão é bom
no processamento de sequências longas, como texto. Outros tipos de camadas recorrentes têm
um desempenho muito melhor. Vamos ver algumas camadas mais avançadas.
Veja, por exemplo, Yoshua Bengio, Patrice Simard e Paolo Frasconi, “Aprender Dependências de Longo Prazo com Descida de Gradiente é
Sepp Hochreiter e Jürgen Schmidhuber, “Long Short-Term Memory”, Neural Computation 9, no. 8 (1997).
Essa camada é uma variante da SimpleRNNcamada que você já conhece; Ele adiciona uma
maneira de transportar informações através de muitos timesteps. Imagine uma correia
transportadora correndo paralela à seqüência que você está processando. As informações da
sequência podem saltar para a correia transportadora em qualquer ponto, ser transportadas
para um intervalo de tempo mais recente e pular, intactas, quando você precisar. Isso é
essencialmente o que o LSTM faz: ele salva informações para mais tarde, evitando que os sinais
antigos desapareçam gradualmente durante o processamento.
Para entender isso em detalhes, vamos começar a partir da SimpleRNNcélula (veja a figura
6.13 ). Como você terá muitas matrizes de ponderação, indexe as matrizes We Una célula com a
letra o( Woe Uo) para saída .
Figura 6.13. O ponto de partida de uma lstmcamada: umsimplernn
Vamos adicionar a esta imagem um fluxo de dados adicional que transporta informações através
de timesteps. Chame seus valores em diferentes timesteps Ct, onde C significa carry . Esta
informação terá o seguinte impacto na célula: será combinada com a conexão de entrada e a
conexão recorrente (através de uma transformação densa: um produto de ponto com uma
matriz de peso seguido por uma adição de polarização e a aplicação de uma função de ativação)
e isso afetará o estado sendo enviado para o próximo timestep (via uma função de ativação e
uma operação de multiplicação). Conceitualmente, o fluxo de dados carry é uma maneira de
modular a próxima saída e o próximo estado (veja a figura 6.14 ). Simples até agora.
Agora, a sutileza: a maneira como o próximo valor do fluxo de dados carry é calculado. Envolve
três transformações distintas. Todos os três têm a forma de uma SimpleRNNcélula:
Mas todas as três transformações têm suas próprias matrizes de peso, que você índice com as
letras i, f, e k. Aqui está o que você tem até agora (pode parecer um pouco arbitrário, mas tenha
paciência comigo).
output_t = ativação (ponto (state_t, Uo) + ponto (input_t, Wo) + ponto (C_t,
Vo) + bo)
i_t = ativação (ponto (estado_t, Ui) + ponto (entrada_t, Wi) + bi)
Pode obter o novo estado carry (o próximo c_t), combinando i_t, f_te k_t.
Adicione isto como mostrado na figura 6.15 . E é isso. Não é tão complicado - apenas um pouco
complexo.
Se você quiser ser filosófico, pode interpretar o que cada uma dessas operações deve fazer. Por
exemplo, você pode dizer que multiplicar c_te f_té uma maneira de esquecer deliberadamente
informações irrelevantes no fluxo de dados de transporte. Enquanto isso, i_te k_tfornecer
informações sobre o presente, atualizando a faixa de transporte com novas informações. Mas no
final do dia, essas interpretações não significam muito, porque o que essas
operações realmentefazer é determinado pelo conteúdo dos pesos parametrizando-os; e os
pesos são aprendidos de uma maneira completa, recomeçando a cada rodada de treinamento,
tornando impossível creditar essa ou aquela operação com uma finalidade específica. A
especificação de uma célula RNN (como acabamos de descrever) determina seu espaço de
hipótese - o espaço no qual você procurará uma boa configuração de modelo durante o
treinamento - mas não determina o que a célula faz; isso é até os pesos das células. A mesma
célula com diferentes pesos pode estar fazendo coisas muito diferentes. Assim, a combinação de
operações que compõem uma célula RNN é melhor interpretada como um conjunto
de restrições em sua pesquisa, não como um design no sentido de engenharia.
Para um pesquisador, parece que a escolha de tais restrições - a questão de como implementar
células RNN - é melhor deixar para algoritmos de otimização (como algoritmos genéticos ou
processos de aprendizado por reforço) do que para engenheiros humanos. E no futuro, é assim
que vamos construir redes. Em resumo: você não precisa entender nada sobre a arquitetura
específica de uma LSTMcélula; como um ser humano, não deveria ser seu trabalho entendê-
lo. Basta ter em mente o que a LSTMcélula deve fazer: permitir que informações passadas sejam
reinjetadas mais tarde, combatendo assim o problema do gradiente de desaparecimento.
model = Sequential ()
perda = 'binary_crossentropy',
metrics = ['acc'])
épocas = 10,
batch_size = 128,
validation_split = 0,2)
Desta vez, você atinge até 89% de precisão de validação. Nada mal: certamente muito melhor
que a SimpleRNNrede - em grande parte porque o LSTM sofre muito menos com o problema do
gradiente de desaparecimento - e um pouco melhor do que a abordagem totalmente conectada
do capítulo 3 , embora você esteja olhando menos dados do que estava no capítulo 3 . Você
está truncando sequências depois de 500 timesteps, enquanto no capítulo 3 , você estava
considerando sequências completas.
Mas esse resultado não é inovador para essa abordagem computacionalmente intensiva. Por que
o desempenho do LSTM não é melhor? Uma razão é que você não fez nenhum esforço para
ajustar hiperparâmetros, como a dimensionalidade de incorporação ou a dimensionalidade de
saída do LSTM. Outro pode ser falta de regularização. Mas honestamente, a principal razão é
que analisar a estrutura global e de longo prazo das revisões (o que a LSTM é boa) não é útil
para um problema de análise de sentimentos. Esse problema básico é bem resolvido
observando-se quais palavras ocorrem em cada revisão e com que frequência. Isso é o que a
primeira abordagem totalmente conectada olhou. Mas existem problemas de processamento de
linguagem natural muito mais difíceis por aí, onde a força do LSTM se tornará aparente: em
particular,
6.2.4. Empacotando
Agora você entende o seguinte:
A seguir, analisaremos vários recursos mais avançados de RNNs, que podem ajudar você a
aproveitar ao máximo seus modelos de sequência de aprendizado profundo.
Nesta seção, revisaremos três técnicas avançadas para melhorar o desempenho e o poder de
generalização de redes neurais recorrentes. No final da seção, você saberá mais sobre o que há
para saber sobre o uso de redes recorrentes com o Keras. Vamos demonstrar todos os três
conceitos sobre um problema de previsão de temperatura, onde você tem acesso a uma série
temporal de pontos de dados provenientes de sensores instalados no telhado de um edifício,
como temperatura, pressão do ar e umidade, que você usa para prever qual a temperatura será
24 horas após o último ponto de dados. Este é um problema bastante desafiador que exemplifica
muitas dificuldades comuns encontradas quando se trabalha com timeseries.
Nós vamos cobrir as seguintes técnicas:
cd ~ / Downloads
mkdir jena_climate
cd jena_climate
wget https://s3.amazonaws.com/keras-datasets/jena_climate_2009_2016.csv.zip
descompacte jena_climate_2009_2016.csv.zip
importar os
f = aberto (fname)
data = f.read ()
f.close ()
imprimir (cabeçalho)
Isso gera uma contagem de 420.551 linhas de dados (cada linha é um timestep: um registro de
uma data e 14 valores relacionados ao clima), bem como o seguinte cabeçalho:
["Data hora",
"p (mbar)",
"T (degC)",
"Tpot (K)",
"Tdew (degC)",
"rh (%)",
"VPmax (mbar)",
"VPact (mbar)",
"VPdef (mbar)",
"sh (g / kg)",
"rho (g / m ** 3)",
"wv (m / s)",
"max. wv (m / s)",
"wd (deg)"]
Por exemplo, aqui está o gráfico da temperatura (em graus Celsius) ao longo do tempo (veja
a figura 6.18 ). Nesta trama, você pode ver claramente a periodicidade anual da temperatura.
Aqui está um gráfico mais estreito dos primeiros 10 dias de dados de temperatura (ver figura
6.19 ). Como os dados são registrados a cada 10 minutos, você obtém 144 pontos de dados por
dia.
Figura 6.19. Temperatura nos primeiros 10 dias do conjunto de dados (° C)
Neste gráfico, você pode ver a periodicidade diária, especialmente evidente nos últimos 4
dias. Observe também que esse período de 10 dias deve ser proveniente de um mês de inverno
bastante frio.
Se você estivesse tentando prever a temperatura média no mês seguinte, dados alguns meses de
dados anteriores, o problema seria fácil, devido à periodicidade confiável dos dados em escala
anual. Mas olhando para os dados em uma escala de dias, a temperatura parece muito mais
caótica. É esta timeseries previsível em uma escala diária? Vamos descobrir.
Pré-processe os dados para um formato que uma rede neural possa ingerir. Isso é fácil:
os dados já são numéricos, então você não precisa fazer nenhuma vetorização. Mas cada
timeseries nos dados está em uma escala diferente (por exemplo, a temperatura é
tipicamente entre -20 e +30, mas a pressão atmosférica, medida em mbar, é em torno
de 1.000). Você irá normalizar cada série de tempo de forma independente, para que
todos eles tomem pequenos valores em uma escala similar.
Escreva um gerador Python que use a matriz atual de dados flutuantes e forneça lotes de
dados do passado recente, junto com uma temperatura-alvo no futuro. Como as
amostras no conjunto de dados são altamente redundantes (a amostra N e a
amostra N + 1 terão a maioria de seus timesteps em comum), seria um desperdício
atribuir explicitamente cada amostra. Em vez disso, você gerará as amostras
rapidamente usando os dados originais.
Você pré-processará os dados subtraindo a média de cada timeseries e dividindo pelo desvio
padrão. Você usará os primeiros 200.000 timesteps como dados de treinamento, portanto
calcule a média e o desvio padrão apenas nessa fração dos dados.
float_data - = mean
float_data / = std
A Listagem 6.33 mostra o gerador de dados que você usará. Ela produz uma tupla (samples,
targets), onde samplesestá um lote de dados de entrada e targetsé a matriz
correspondente de temperaturas alvo. Leva os seguintes argumentos:
data- A matriz original de dados de ponto flutuante, que você normalizou na listagem
6.32 .
lookback—Quantos timesteps retornam os dados de entrada devem ir.
delay- Quantos timesteps no futuro o alvo deveria ser.
min_indexe max_index—Indices na datamatriz que delimitam quais
timesteps serão extraídos. Isso é útil para manter um segmento dos dados para
validação e outro para teste.
shuffle—Para misturar as amostras ou desenhá-las em ordem cronológica.
batch_size—O número de amostras por lote.
step—O período, em tempo, no qual você amostra dados. Você configurará para 6 para
desenhar um ponto de dados a cada hora.
i = min_index + lookback
enquanto 1:
se shuffle:
linhas = np.random.randint (
outro:
if i + batch_size> = max_index:
i = min_index + lookback
lookback // step,
data.shape [-1]))
Agora, vamos usar a generatorfunção abstrata para instanciar três geradores: um para
treinamento, um para validação e um para teste. Cada um examinará diferentes segmentos
temporais dos dados originais: o gerador de treinamento analisa os primeiros 200.000
timesteps, o gerador de validação examina os 100.000 a seguir e o gerador de teste examina o
restante.
lookback = 1440
etapa = 6
atraso = 144
batch_size = 128
lookback = lookback
atraso = atraso
min_index = 0,
max_index = 200000,
shuffle = Verdadeiro
passo = passo
batch_size = batch_size)
lookback = lookback
atraso = atraso
min_index = 200001,
max_index = 300000,
passo = passo
batch_size = batch_size)
lookback = lookback
atraso = atraso
min_index = 300001,
max_index = None,
passo = passo
batch_size = batch_size)
Neste caso, a série de tempos de temperatura pode ser seguramente assumida como contínua
(as temperaturas de amanhã provavelmente estarão próximas das temperaturas de hoje), bem
como periódicas com um período diário. Assim, uma abordagem de senso comum é sempre
prever que a temperatura daqui a 24 horas será igual à temperatura no momento. Vamos avaliar
essa abordagem usando a métrica de erro absoluto médio (MAE):
batch_maes = []
batch_maes.append (mae)
evaluate_naive_method ()
Isso produz um MAE de 0,29. Como os dados de temperatura foram normalizados para serem
centralizados em 0 e têm um desvio padrão de 1, esse número não é imediatamente
interpretável. Isso se traduz em um erro absoluto médio de 0,29 × temperature_stdgraus
Celsius: 2,57 ° C.
Esse é um erro absoluto médio razoavelmente grande. Agora o jogo é usar seu conhecimento de
aprendizagem profunda para fazer melhor.
A listagem a seguir mostra um modelo totalmente conectado que inicia com o achatamento dos
dados e, em seguida, o executa por meio de duas Densecamadas. Observe a falta de função de
ativação na última Densecamada, o que é típico de um problema de regressão. Você usa o MAE
como a perda. Como você avalia exatamente os mesmos dados e com a mesma métrica que fez
com a abordagem de senso comum, os resultados serão diretamente comparáveis.
model = Sequential ()
model.add (layers.Flatten (input_shape = (lookback // passo, float_data.shape
[-1])))
steps_per_epoch = 500,
épocas = 20,
validation_data = val_gen,
validation_steps = val_steps)
Vamos exibir as curvas de perda para validação e treinamento (veja a figura 6.20 ).
Figura 6.20. Perda de treinamento e validação na tarefa de previsão de temperatura do Jena com uma rede simples e
densamente conectada
plt.figure ()
plt.plot (epochs, loss, 'bo', label = 'perda de treino')
plt.legend ()
plt.show ()
Algumas das perdas de validação estão próximas da linha de base de não-aprendizado, mas não
de forma confiável. Isso mostra o mérito de ter essa linha de base em primeiro lugar: não é fácil
superá-la. Seu senso comum contém muitas informações valiosas que um modelo de
aprendizado de máquina não tem acesso.
Você pode se perguntar, se existe um modelo simples e de bom desempenho para ir dos dados
até os alvos (a linha de base do senso comum), por que o modelo que você está treinando não o
encontra e melhora? Porque esta solução simples não é o que sua configuração de treinamento
está procurando. O espaço dos modelos em que você está procurando uma solução - ou seja, o
espaço da sua hipótese - é o espaço de todas as redes possíveis de duas camadas com a
configuração que você definiu. Essas redes já são bastante complicadas. Quando você está
procurando por umCom um espaço de modelos complicados, a linha de base simples e de bom
desempenho pode ser desaprendida, mesmo que seja tecnicamente parte do espaço de
hipóteses. Essa é uma limitação bastante significativa do aprendizado de máquina em geral: a
menos que o algoritmo de aprendizado seja codificado para procurar um tipo específico de
modelo simples, o aprendizado de parâmetro pode, às vezes, falhar em encontrar uma solução
simples para um problema simples.
Junyoung Chung et al., “Avaliação Empírica de Redes Neurais Recorrentes Conectadas na Modelagem de Seqüências”, Conferência sobre
model = Sequential ()
steps_per_epoch = 500,
épocas = 20,
validation_data = val_gen,
validation_steps = val_steps)
A Figura 6.21 mostra os resultados. Muito melhor! Você pode superar significativamente a
linha de base de senso comum, demonstrando o valor do aprendizado de máquina, bem como a
superioridade das redes recorrentes em comparação com redes densas de achatamento de
seqüência nesse tipo de tarefa.
Figura 6.21. Perda de treinamento e validação na tarefa de previsão de temperatura de Jena com uma GRU
2016, http://mlg.eng.cam.ac.uk/yarin/blog_2248.html .
Yarin Gal fez sua pesquisa usando Keras e ajudou a construir esse mecanismo diretamente nas
camadas recorrentes de Keras. Cada camada recorrente em Keras possui dois argumentos
relacionados a dropout:, dropoutum float especificando a taxa de dropout para unidades de
entrada da camada, e recurrent_dropoutespecificando a taxa de dropout das unidades
recorrentes. Vamos adicionar dropout e dropout recorrente à GRUcamada e ver como isso afeta o
overfitting. Como as redes que estão sendo regularizadas com o dropout sempre demoram mais
para convergir totalmente, você treinará a rede para o dobro de épocas.
Listagem 6.40. Treinamento e avaliação de um modelo baseado em GRU regularizado por desistência
model = Sequential ()
abandono = 0,2,
recurrent_dropout = 0,2,
épocas = 40,
validation_data = val_gen,
validation_steps = val_steps)
A Figura 6.22 mostra os resultados. Sucesso! Você não está mais exagerando durante as
primeiras 30 épocas. Mas embora você tenha pontuações de avaliação mais estáveis, suas
melhores pontuações não são muito menores do que eram anteriormente.
Figura 6.22. Perda de treinamento e validação na tarefa de previsão de temperatura de Jena com um GRU regularizado por
abandono
Para empilhar camadas recorrentes umas em cima das outras em Keras, todas as camadas
intermediárias devem retornar sua sequência completa de saídas (um tensor 3D) em vez de sua
saída no último intervalo de tempo. Isso é feito especificando return_sequences=True.
Listagem 6.41. Treinamento e avaliação de um modelo GRU empilhado regularizado por desistência
abandono = 0,1,
recurrent_dropout = 0,5,
return_sequences = Verdadeiro,
abandono = 0,1,
recurrent_dropout = 0,5))
steps_per_epoch = 500,
épocas = 40,
validation_data = val_gen,
validation_steps = val_steps)
A Figura 6.23 mostra os resultados. Você pode ver que a camada adicionada melhora os
resultados um pouco, embora não significativamente. Você pode tirar duas conclusões:
Como você ainda não está super adaptando muito mal, você pode aumentar com
segurança o tamanho de suas camadas em uma busca pela melhoria da perda de
validação. Isto tem um custo computacional não desprezível, no entanto.
Adicionar uma camada não ajudou por um fator significativo, portanto, você pode estar
vendo retornos decrescentes de aumentar a capacidade da rede neste momento.
Figura 6.23. Perda de treinamento e validação na tarefa de previsão de temperatura Jena com uma rede GRU empilhada
Notavelmente, o fato de que as camadas RNN nesta seção processaram sequências em ordem
cronológica (datas mais antigas primeiro) pode ter sido uma decisão arbitrária. Pelo menos, é
uma decisão que não fizemos nenhuma tentativa de questionar até agora. Os RNNs poderiam
ter funcionado bem o suficiente se processassem seqüências de entrada em ordem
anticronológica, por exemplo (newest timepeps first)? Vamos tentar isso na prática e ver o que
acontece. Tudo o que você precisa fazer é gravar uma variante do gerador de dados onde as
seqüências de entrada são revertidas ao longo da dimensão de tempo (substitua a última linha
por yield samples[:, ::-1, :], targets). Treinando a mesma GRUrede de camada
única usada no primeiro experimento desta seção, você obtém os resultados mostrados
na figura 6.24 .
Figura 6.24. Perda de treinamento e validação na tarefa de previsão de temperatura de Jena com uma GRU treinada em
sequências invertidas
A GRU de ordem reversa tem um desempenho muito baixo até mesmo na linha de base de senso
comum, indicando que, nesse caso, o processamento cronológico é importante para o sucesso de
sua abordagem. Isso faz todo o sentido: o subjacenteGRUA camada normalmente será melhor
para lembrar o passado recente do que o passado distante e, naturalmente, os pontos de dados
climáticos mais recentes são mais preditivos do que os pontos de dados mais antigos para o
problema (é isso que torna a linha de base de senso comum bastante forte). Assim, a versão
cronológica da camada é obrigada a superar a versão de ordem inversa. É importante ressaltar
que isso não é verdade para muitos outros problemas, incluindo a linguagem natural:
intuitivamente, a importância de uma palavra para entender uma frase geralmente não depende
de sua posição na sentença. Vamos tentar o mesmo truque no exemplo do LSTM IMDB
da seção 6.2 .
max_features = 10000 1
maxlen = 500 2
num_words = max_features) 3
model = Sequential ()
perda = 'binary_crossentropy',
metrics = ['acc'])
épocas = 10,
batch_size = 128,
validation_split = 0,2)
Você obtém desempenho quase idêntico ao da ordem cronológica LSTM. Notavelmente, em tal
conjunto de dados de texto, inverteu-ordem de processamento funciona tão bem como o
processamento cronológica, confirmando a hipótese de que, embora a ordem das
palavras faz questão em linguagem compreensão, que pedir que você usa não é crucial. É
importante ressaltar que um RNN treinado em sequências invertidas aprenderá diferentes
representações do que um treinado nas sequências originais, da mesma forma que você teria
modelos mentais diferentes se o tempo fluísse para trás no mundo real - se você vivesse uma
vida onde morresse no primeiro dia e nasceram no seu último dia. Em aprendizado de máquina,
representações diferentes, mas úteisSempre vale a pena explorar, e quanto mais eles diferem,
melhor: eles oferecem um novo ângulo a partir do qual analisar seus dados, capturando aspectos
dos dados que foram perdidos por outras abordagens e, portanto, podem ajudar a melhorar o
desempenho em uma tarefa. Essa é a intuição por trás do conjunto , um conceito que
exploraremos no capítulo 7 .
Um RNN bidirecional explora essa ideia para melhorar o desempenho de RNNs de ordem
cronológica. Ele examina sua sequência de entrada nos dois sentidos (consulte a figura 6.25 ),
obtendo representações potencialmente mais ricas e capturando padrões que podem ter sido
perdidos somente pela versão de ordem cronológica.
Figura 6.25. Como funciona uma camada RNN bidirecional
Para instanciar um RNN bidirecional em Keras, você usa a Bidirectionalcamada, que toma
como primeiro argumento uma instância de camada recorrente. Bidirectionalcria uma
segunda instância separada dessa camada recorrente e usa uma instância para processar as
seqüências de entrada em ordem cronológica e a outra instância para processar as seqüências de
entrada em ordem inversa. Vamos tentar na tarefa de análise de sentimentos do IMDB.
model = Sequential ()
épocas = 10,
batch_size = 128,
validation_split = 0,2)
Ele executa um pouco melhor que o normal que LSTMvocê tentou na seção anterior, alcançando
mais de 89% de precisão na validação. Também parece sobrecarregar mais rapidamente, o que
não é surpreendente porque uma camada bidirecional tem duas vezes mais parâmetros que um
cronológico LSTM. Com alguma regularização, a abordagem bidirecional provavelmente seria
um bom desempenho nessa tarefa.
model = Sequential ()
model.add (layers.Bidirectional (
steps_per_epoch = 500,
épocas = 40,
validation_data = val_gen,
validation_steps = val_steps)
Isso funciona tão bem quanto a GRUcamada normal . É fácil entender por quê: toda a capacidade
preditiva deve vir da metade cronológica da rede, porque a metade anticronológica é conhecida
por estar gravemente abaixo do desempenho nessa tarefa (novamente, porque o passado recente
é muito mais importante do que o passado distante neste caso). ).
Como sempre, o aprendizado profundo é mais uma arte do que uma ciência. Podemos fornecer
diretrizes que sugiram o que provavelmente funcionará ou não em determinado problema, mas,
em última análise, todo problema é único; você terá que avaliar empiricamente estratégias
diferentes. Atualmente, não há uma teoria que lhe diga com antecedência o que você deve fazer
para solucionar um problema de maneira ideal. Você deve iterar.
6.3.10. Empacotando
Veja o que você deve tirar desta seção:
Como você aprendeu no capítulo 4 , ao abordar um novo problema, é bom primeiro
estabelecer linhas de base de bom senso para sua métrica escolhida. Se você não tem
uma base para vencer, você não pode dizer se você está fazendo um progresso real.
Tente modelos simples antes dos caros, para justificar a despesa adicional. Às vezes, um
modelo simples se tornará sua melhor opção.
Quando você tem dados em que a ordenação temporal é importante, as redes
recorrentes são um ótimo ajuste e superam facilmente os modelos que primeiro nivelam
os dados temporais.
Para usar o dropout com redes recorrentes, você deve usar uma máscara de dropout de
constante de tempo e máscara de dropout recorrente. Estes são construídos em
camadas recorrentes Keras, então tudo que você tem a fazer é usar
os argumentos dropoute recurrent_dropoutde camadas recorrentes.
Os RNNs empilhados fornecem mais poder representacional do que uma única camada
RNN. Eles também são muito mais caros e, portanto, nem sempre valem a
pena. Embora eles ofereçam ganhos claros em problemas complexos (como a tradução
automática), eles nem sempre são relevantes para problemas menores e mais simples.
Os RNNs bidirecionais, que analisam uma sequência nos dois sentidos, são úteis em
problemas de processamento de linguagem natural. Mas eles não são fortes em dados
de sequência, onde o passado recente é muito mais informativo do que o início da
sequência.
Nota
Há dois conceitos importantes que não abordaremos em detalhes aqui: atenção recorrente e
mascaramento de sequência. Ambos tendem a ser especialmente relevantes para o
processamento de linguagem natural e não são particularmente aplicáveis ao problema de
previsão de temperatura. Vamos deixá-los para estudo futuro fora deste livro.
Alguns leitores são obrigados a tomar as técnicas que introduzimos aqui e testá-las no problema
da previsão do preço futuro dos títulos no mercado de ações (ou taxas de câmbio, e assim por
diante). Os mercados têm características estatísticas muito diferentes dos fenômenos naturais,
como padrões climáticos. Tentar usar o aprendizado de máquina para vencer mercados, quando
você só tem acesso a dados disponíveis publicamente, é um esforço difícil, e é provável que você
perca tempo e recursos sem nada para mostrar.
Lembre-se sempre de que, quando se trata de mercados, o desempenho passado não é um bom
indicador de retornos futuros - olhar no espelho retrovisor é uma maneira ruim de dirigir. O
aprendizado de máquina, por outro lado, é aplicável a conjuntos de dados em que o
passado é um bom preditor do futuro.
No capítulo 5 , você aprendeu sobre redes neurais convolucionais (convnets) e como elas
funcionam particularmente bem em problemas de visão computacional, devido à sua capacidade
de operar de forma convolucional , extraindo recursos de correções de entrada locais e
permitindo modularidade de representação e eficiência de dados. As mesmas propriedades que
fazem as redes se destacarem na visão computacional também as tornam altamente relevantes
para o processamento de seqüências. O tempo pode ser tratado como uma dimensão espacial,
como a altura ou a largura de uma imagem 2D.
Tais capas 1D podem ser competitivas com RNNs em certos problemas de processamento de
sequência, geralmente a um custo computacional consideravelmente mais
barato. Recentemente, os modelos 1D, normalmente usados com núcleos dilatados, foram
usados com grande sucesso para geração de áudio e tradução automática. Além desses sucessos
específicos, há muito tempo se sabe que pequenas convnets 1D podem oferecer uma alternativa
rápida aos RNNs para tarefas simples, como classificação de texto e previsão de timeseries.
Figura 6.26. Como funciona a convolução 1D: cada timestep de saída é obtido de um patch temporal na sequência de entrada.
Essas camadas de convolução 1D podem reconhecer padrões locais em uma sequência. Como a
mesma transformação de entrada é executada em cada patch, um padrão aprendido em uma
determinada posição em uma sentença pode ser reconhecido posteriormente em uma posição
diferente, tornando invariante de tradução de convés 1D (para traduções temporais). Por
exemplo, uma seqüência de processamento de caracteres de convecção 1D usando janelas de
convolução de tamanho 5 deve ser capaz de aprender palavras ou fragmentos de tamanho 5 ou
menos, e deve ser capaz de reconhecer essas palavras em qualquer contexto em uma sequência
de entrada. Uma convã 1D de nível de personagem é, portanto, capaz de aprender sobre a
morfologia da palavra.
Vamos construir uma simples convecção 1D de duas camadas e aplicá-la à tarefa de classificação
de sentimentos do IMDB com a qual você já está familiarizado. Como lembrete, este é o código
para obter e pré-processar os dados.
max_features = 10000
max_len = 500
As capas 1D são estruturadas da mesma forma que suas contrapartes 2D, que você usou
no capítulo 5 : elas consistem em uma pilha Conv1De MaxPooling1Dcamadas, terminando
em uma camada de pool global ou uma Flattencamada, que transformam as saídas 3D em
saídas 2D, permitindo você adicionar uma ou mais Densecamadas ao modelo para classificação
ou regressão.
Uma diferença, no entanto, é o fato de que você pode se dar ao luxo de usar janelas de
convolução maiores com os modelos 1D. Com uma camada de convolução 2D, uma janela de
convolução 3 × 3 contém 3 × 3 = 9 vetores de recursos; mas com uma camada de convolução 1D,
uma janela de convolução de tamanho 3 contém apenas 3 vetores de recursos. Você pode assim
facilmente ter janelas de convolução 1D de tamanho 7 ou 9.
Listagem 6.46. Treinamento e avaliação de uma simples convenção 1D nos dados do IMDB
model = Sequential ()
model.summary ()
perda = 'binary_crossentropy',
metrics = ['acc'])
épocas = 10,
batch_size = 128,
validation_split = 0,2)
Figura 6.28. Treinamento e validação de validação no IMDB com uma simples convulsão 1D
Listagem 6.47. Treinar e avaliar uma simples convecção 1D nos dados de Jena
model = Sequential ()
steps_per_epoch = 500,
épocas = 20,
validation_data = val_gen,
validation_steps = val_steps)
A validação do MAE permanece nos 0,40s: você não pode nem superar a linha de base do senso
comum usando a pequena convnet. Novamente, isso acontece porque o convnet procura
padrões em qualquer ponto da entrada das séries de tempo e não tem conhecimento da posição
temporal de um padrão que vê (no início, no final e assim por diante). Como os pontos de dados
mais recentes devem ser interpretados de maneira diferente dos pontos de dados mais antigos,
no caso desse problema de previsão específico, a convnet falha ao produzir resultados
significativos. Essa limitação de convnets não é um problema com os dados do IMDB, porque os
padrões de palavras-chave associados a um sentimento positivo ou negativo são informativos
independentemente de onde eles são encontrados nas sentenças de entrada.
Uma estratégia para combinar a velocidade e a leveza das conversas com a sensibilidade à
ordem dos RNNs é usar uma convnet 1D como uma etapa de pré-processamento antes de uma
RNN (consulte a figura 6.30 ). Isso é especialmente benéfico quando você está lidando com
sequências que são tão longas que elas não podem ser processadas de maneira realista com
RNNs, como sequências com milhares de etapas. A convnet transformará a longa seqüência de
entrada em seqüências muito mais curtas (com resolução reduzida) de recursos de nível
superior. Essa seqüência de recursos extraídos se torna a entrada para a parte RNN da rede.
Figura 6.30. Combinando uma convnet 1D e um RNN para processar seqüências longas
Essa técnica não é vista com freqüência em trabalhos de pesquisa e aplicações práticas,
possivelmente porque não é bem conhecida. É eficaz e deveria ser mais comum. Vamos tentar
no conjunto de dados de previsão de temperatura. Como essa estratégia permite manipular
seqüências muito mais longas, é possível observar dados de mais tempo (aumentando
o lookbackparâmetro do gerador de dados) ou ver as séries de tempo de alta resolução
(diminuindo o stepparâmetro do gerador). Aqui, um pouco arbitrariamente, você usará
um stepque é metade do tamanho, resultando em uma série de tempo duas vezes mais longa,
onde oos dados de temperatura são amostrados a uma taxa de 1 ponto por 30 minutos. O
exemplo reutiliza a generatorfunção definida anteriormente.
Listagem 6.48. Preparando geradores de dados de alta resolução para o conjunto de dados do Jena
etapa = 3 1
lookback = 720 2
atraso = 144 2
lookback = lookback
atraso = atraso
min_index = 0,
max_index = 200000,
shuffle = Verdadeiro
passo = passo)
lookback = lookback
atraso = atraso
min_index = 200001,
max_index = 300000,
passo = passo)
lookback = lookback
atraso = atraso
min_index = 300001,
max_index = None,
passo = passo)
Este é o modelo, começando com duas Conv1Dcamadas e seguindo com uma GRUcamada. A
Figura 6.31mostra os resultados.
Figura 6.31. Perda de treinamento e validação na tarefa de previsão de temperatura de Jena com uma convecção 1D seguida
por uma gru
model = Sequential ()
model.summary ()
steps_per_epoch = 500,
épocas = 20,
validation_data = val_gen,
validation_steps = val_steps)
A julgar pela perda de validação, essa configuração não é tão boa quanto a
regularização GRUsozinha, mas é significativamente mais rápida. Ele analisa o dobro de dados, o
que, nesse caso, não parece ser muito útil, mas pode ser importante para outros conjuntos de
dados.
6.4.5. Empacotando
Veja o que você deve tirar desta seção:
Da mesma forma que as réplicas 2D funcionam bem para processar padrões visuais no
espaço 2D, as réplicas 1D têm um bom desempenho no processamento de padrões
temporais. Eles oferecem uma alternativa mais rápida para os RNNs em alguns
problemas, em particular tarefas de processamento de linguagem natural.
Tipicamente, as conversas 1D são estruturadas de maneira muito semelhante a seus
equivalentes 2D do mundo da visão computacional: elas consistem em pilhas
de Conv1Dcamadas e Max-Pooling1Dcamadas, terminando em uma operação de
agrupamento global ou operação de achatamento.
Como os RNNs são extremamente caros para processar sequências muito longas, mas as
convntas 1D são baratas, pode ser uma boa ideia usar uma convnet 1D como uma etapa
de pré-processamento antes de uma RNN, encurtando a sequência e extraindo
representações úteis para o RNN processar.
Resumo do capítulo
Neste capítulo, você aprendeu as seguintes técnicas, que são amplamente aplicáveis a
qualquer conjunto de dados de dados de sequência, do texto às séries de tempo:
Como tokenizar texto
Que palavra é a incorporação e como usá-las
Quais são as redes recorrentes e como usá-las
Como empilhar camadas RNN e usar RNNs bidirecionais para construir
modelos de processamento de seqüência mais poderosos
Como usar convnets 1D para processamento sequencial
Como combinar convnets 1D e RNNs para processar seqüências longas
Você pode usar RNNs para regressão de timeseries (“predizendo o futuro”), classificação de
timeseries, detecção de anomalias em timeseries e rotulagem de seqüências (como identificação
de nomes ou datas em sentenças).
Da mesma forma, você pode usar as convés 1D para tradução automática (modelos
convolucionais seqüência a seqüência, como SliceNet [ a ] ), classificação de documentos e
correção ortográfica.
uma
Veja https://arxiv.org/abs/1706.03059 .
Se a ordem global é importante em seus dados de sequência, é preferível usar uma rede
recorrente para processá-la. Este é tipicamente o caso de timeseries, onde o passado recente é
provavelmente mais informativo do que o passado distante.
Se a ordenação global não for fundamentalmente significativa , então as torneiras 1D
funcionarão pelo menos tão bem e serão mais baratas. Este é frequentemente o caso dos dados
de texto, em que uma palavra-chave encontrada no início de uma frase é tão significativa quanto
uma palavra-chave encontrada no final.
Capítulo 7. Práticas recomendadas avançadas de
aprendizagem profunda
Este capítulo cobre
Este capítulo explora uma série de ferramentas poderosas que o aproximarão da capacidade de
desenvolver modelos de última geração em problemas difíceis. Usando a API funcional Keras,
você pode construir modelos semelhantes a gráficos, compartilhar uma camada em diferentes
entradas e usar modelos Keras como as funções do Python. Os callbacks da Keras e a ferramenta
de visualização baseada no navegador TensorBoard permitem monitorar modelos durante o
treinamento. Também discutiremos várias outras práticas recomendadas, incluindo a
normalização de lotes, conexões residuais, otimização de hiperparâmetros e modelagem de
conjuntos.
Até agora, todas as redes neurais introduzidas neste livro foram implementadas usando
o Sequentialmodelo. O Sequentialmodelo faz a suposição de que a rede tem exatamente
uma entrada e exatamente uma saída, e que consiste em uma pilha linear de camadas (consulte
a figura 7.1 ).
Essa é uma suposição comumente verificada; a configuração é tão comum que conseguimos
cobrir muitos tópicos e aplicativos práticos nestas páginas usando apenas a Sequentialclasse
de modelo. Mas esse conjunto de suposições é muito inflexível em vários casos. Algumas redes
exigem várias entradas independentes, outras exigem várias saídas e algumas redes têm
ramificações internas entre camadas, o que as torna semelhantes a gráficos de camadas, em vez
de pilhas lineares de camadas.
Algumas tarefas, por exemplo, exigem recursos multimodaisEntradas: eles mesclam dados
provenientes de diferentes fontes de entrada, processando cada tipo de dado usando diferentes
tipos de camadas neurais. Imagine um modelo de aprendizagem profunda tentando prever o
preço de mercado mais provável de uma peça de roupa usada, usando as seguintes entradas:
metadados fornecidos pelo usuário (como a marca, a idade e assim por diante), fornecidos pelo
usuário descrição de texto e uma imagem do item. Se você tivesse apenas os metadados
disponíveis, você poderia codificá-los e usar uma rede densamente conectada para prever o
preço. Se você tivesse apenas a descrição de texto disponível, você poderia usar uma RNN ou
uma convecção 1D. Se você tivesse apenas a foto, você poderia usar uma convnet 2D. Mas como
você pode usar os três ao mesmo tempo? Uma abordagem ingênua seria treinar três modelos
separados e, em seguida, fazer uma média ponderada de suas previsões. Mas isso pode ser sub-
ótimo, porque as informações extraídas pelos modelos podem ser redundantes. A melhor
maneira éem conjunto, aprendemos um modelo mais preciso dos dados usando um modelo que
pode ver todas as modalidades de entrada disponíveis simultaneamente: um modelo com três
ramificações de entrada (ver figura 7.2 ).
Da mesma forma, algumas tarefas precisam prever vários atributos de destino dos dados de
entrada. Dado o texto de um romance ou conto, você pode querer classificá-lo automaticamente
por gênero (como romance ou suspense), mas também prever a data aproximada em que foi
escrito. Claro, você poderia treinar dois modelos separados: um para o gênero e outro para a
data. Mas, como esses atributos não são estatisticamente independentes, você pode criar um
modelo melhor aprendendo a prever o gênero e a data juntos ao mesmo tempo. Tal modelo
conjunto teria então duas saídas, ou cabeças (ver figura 7.3).). Devido às correlações entre
gênero e data, saber a data de um romance ajudaria o modelo a aprender representações ricas e
precisas do espaço de novos gêneros, e vice-versa.
Além disso, muitas arquiteturas neurais recentemente desenvolvidas exigem topologia de rede
não-linear: redes estruturadas como gráficos acíclicos direcionados. A família de Iniciação de
redes (desenvolvido por Szegedy et ai. Em Google), [ 1 ] , por exemplo, depende de módulos de
Iniciação , onde a entrada é processado por vários ramos convolucionais paralelos cujas saídas
são então integrado de volta para um único tensor (ver figura 7,4 ). Há também a tendência
recente de adicionar conexões residuais a um modelo, que começou com a família de redes
ResNet (desenvolvida por He et al. Na Microsoft). [ 2 ]Uma conexão residual consiste em reinjetar
representações anteriores no fluxo de dados a jusante, adicionando um tensor de saída passado
a um tensor de saída posterior (consulte a figura 7.5 ), que ajuda a evitar a perda de informações
ao longo do fluxo de processamento de dados. Existem muitos outros exemplos de tais redes
semelhantes a gráficos.
Christian Szegedy et al., “Enfrentando as Convoluções”, Conferência sobre Visão Computacional e Reconhecimento de Padrões
(2014), https://arxiv.org/abs/1409.4842 .
Kaiming He et al., “Deep Residual Learning for Image Recognition”, Conferência sobre Visão Computacional e Reconhecimento de Padrões
(2015), https://arxiv.org/abs/1512.03385 .
Figura 7.4. Um módulo de iniciação: um subgrafo de camadas com vários ramos convolucionais paralelos
Figura 7.5 Uma conexão residual: reinjeção de informação prévia a jusante via adição de mapa de característica
Esses três casos de uso importantes - modelos com várias entradas, modelos com várias saídas e
modelos semelhantes a gráficos - não são possíveis quando se usa apenas
a Sequentialclasse do modelo em Keras. Mas há outra maneira muito mais geral e flexível de
usar Keras: a API funcional . Esta seção explica em detalhes o que é, o que pode fazer e como
usá-lo.
1 tensor
2 Uma camada é uma função.
3 Uma camada pode ser chamada em um tensor e retorna um tensor.
de importação de keras
seq_model = Sequencial () 1
_________________________________________________________________
================================================== ===============
_________________________________________________________________
_________________________________________________________________
_________________________________________________________________
================================================== ===============
A única parte que pode parecer um pouco mágica neste ponto é instanciar um Modelobjeto
usando apenas um tensor de entrada e um tensor de saída. Nos bastidores, Keras recupera todas
as camadas envolvidas em ir de input_tensora output_tensor, trazendo-osjuntos em uma
estrutura de dados semelhante a um gráfico - a Model. Naturalmente, a razão pela qual
funciona é que output_tensorfoi obtida pela transformação repetida input_tensor. Se
você tentou criar um modelo a partir de entradas e saídas que não estavam relacionadas, você
obteria um RuntimeError:
Este erro diz-lhe, em essência, que Keras não conseguiu alcançar input_1o tensor de saída
fornecido.
Quando se trata de compilar, treinar ou avaliar tal instância Model, a API é a mesma que a
de Sequential:
1 Compila o modelo
2 Gera dados fictícios de Numpy para treinar
3 Treina o modelo por 10 épocas
4 Avalia o modelo
Um modelo típico de resposta a perguntas tem duas entradas: uma pergunta de linguagem
natural e um trecho de texto (como um artigo de notícias) que fornece informações a serem
usadas para responder à pergunta. O modelo deve então produzir uma resposta: na
configuração mais simples possível, esta é uma resposta de uma palavra obtida através de um
softmax sobre algum vocabulário pré-definido (ver figura 7.6 ).
Figura 7.6 Um modelo de perguntas e respostas
A seguir, um exemplo de como você pode construir um modelo desse tipo com a API
funcional. Você configura dois ramos independentes, codificando a entrada de texto e a entrada
de pergunta como vetores de representação; então, concatenar esses vetores; e, finalmente,
adicione um classificador softmax no topo das representações concatenadas.
Listagem 7.1. Implementação de API funcional de um modelo de resposta a perguntas de duas entradas
da importação de keras.models
de importação de keras
text_vocabulary_size = 10000
question_vocabulary_size = 10000
answer_vocabulary_size = 500
embedded_text = layers.Embedding (
dtype = 'int32'
name = 'question') 4
embedded_question = layers.Embedding (
eixo = -1) 5
perda = 'categorical_crossentropy',
metrics = ['acc'])
Agora, como você treina esse modelo de duas entradas? Existem duas APIs possíveis: você pode
fornecer ao modelo uma lista de matrizes Numpy como entradas ou pode alimentá-lo com um
dicionário que mapeia nomes de entrada para matrizes Numpy. Naturalmente, a última opção
só está disponível se você der nomes às suas entradas.
num_samples = 1000
max_length = 100
de importação de keras
da importação de keras.models
vocabulary_size = 50000
num_income_groups = 10
x = layers.GlobalMaxPooling1D () (x)
ativação = 'softmax',
'receita': 'categorical_crossentropy', 1
'sexo': 'binary_crossentropy'}) 1
Note que as contribuições de perda muito desequilibradas farão com que as representações do
modelo sejam otimizadas preferencialmente para a tarefa com a maior perda individual, às
custas das outras tarefas. Para remediar isso, você pode atribuir diferentes níveis de importância
aos valores de perda em sua contribuição para a perda final. Isto é útil, em particular, se os
valores das perdas usarem diferentes escalas. Por exemplo, a perda de erro quadrático médio
(MSE) usada para a tarefa de regressão de idade normalmente leva um valor em torno de 3–5,
enquanto a perda de entropia cruzada usada para a tarefa de classificação de gênero pode ser tão
baixa quanto 0,1. Nessa situação, para equilibrar a contribuição das diferentes perdas, você pode
atribuir um peso de 10 à perda de crossentropy e um peso de 0,25 à perda de MSE.
'income': 'categorical_crossentropy', 1
'gender': 'binary_crossentropy'}, 1
'rendimento': 1., 1
'sexo': 10.}) 1
Assim como no caso de modelos com várias entradas, você pode passar dados do Numpy para o
modelo para treinamento, seja por meio de uma lista de matrizes ou por meio de um dicionário
de matrizes.
'income': income_targets, 2
'gender': gender_targets}, 2
Vários componentes comuns da rede neural são implementados como gráficos. Dois notáveis
são módulos de Iniciação e conexões residuais. Para entender melhor como a API funcional
pode ser usada para construir gráficos de camadas, vamos dar uma olhada em como você pode
implementar ambas em Keras.
Módulos de criação
Inception [ 3 ] é um tipo popular de arquitetura de rede para redes neurais convolucionais; foi
desenvolvido por Christian Szegedy e seus colegas do Google em 2013-2014,
inspirado na arquitetura anterior de rede em rede . [ 4 ]Consiste em uma pilha de módulos que se
parecem com pequenas redes independentes, divididas em vários ramos paralelos. A forma mais
básica de um módulo de Iniciação tem de três a quatro ramificações começando com uma
convolução de 1 × 1, seguida por uma convolução 3 × 3 e terminando com a concatenação dos
recursos resultantes. Essa configuração ajuda a rede a aprender separadamente os recursos
espaciais e os recursos do canal, o que é mais eficiente do que aprendê-los em conjunto. Versões
mais complexas de um módulo de criação também são possíveis, geralmente envolvendo
operações de pooling, diferentes tamanhos de convolução espacial (por exemplo, 5 × 5 em vez de
3 × 3 em algumas ramificações) e ramificações sem uma convolução espacial (somente 1 × 1
convolução). Um exemplo de tal módulo, tirado do Inception V3, é mostrado emfigura 7.8 .
https://arxiv.org/abs/1409.4842 .
Min Lin, Qiang Chen e Shuicheng Yan, “Rede em Rede”, Conferência Internacional sobre Representações de Aprendizagem
(2013),https://arxiv.org/abs/1312.4400 .
Figura 7.8. Um módulo de iniciação
O propósito de 1 × 1 convoluções
Você já sabe que convoluções extraem patches espaciais ao redor de cada bloco em um tensor de
entrada e aplicam a mesma transformação a cada patch. Um caso extremo é quando os patches
extraídos consistem em um único bloco. A operação de convolução se torna equivalente a
executar cada vetor de bloco por meio de uma Densecamada: ele computará recursos que
combinam informações dos canais do tensor de entrada, mas não misturará informações no
espaço (porque ele está observando um bloco de cada vez) ). Tais convoluções 1 × 1 (também
chamadas de convoluções pontuais) são apresentados nos módulos de Iniciação, onde eles
contribuem para fatorar o aprendizado de recurso em termos de canal e aprendizado de recurso
espacial - uma coisa razoável a ser feita se você assumir que cada canal é altamente
autocorrelacionado no espaço, mas canais diferentes podem não ser altamente correlacionados
um com o outro.
Veja como você implementaria o módulo apresentado na figura 7.8 usando a API funcional. Este
exemplo assume a existência de um tensor de entrada 4D x:
output = layers.concatenate (
François Chollet, “Xception: Aprendizado Profundo com Convoluções Separáveis Profundas”, Conferência sobre Visão Computacional e
Conexões residuais
Veja como implementar uma conexão residual em Keras quando os tamanhos do mapa de
recursos forem os mesmos, usando conexões residuais de identidade. Este exemplo assume a
existência de um tensor de entrada 4D x:
x = ...
x = ...
Você pode entender esse conceito com uma analogia de processamento de sinais: se você tem
um pipeline de processamento de áudio que consiste em uma série de operações, cada uma das
quais toma como entrada a saída da operação anterior, então se uma operação corta seu sinal
para um faixa de baixa freqüência (por exemplo, 0-15 kHz), as operações a jusante nunca serão
capazes de recuperar as freqüências perdidas. Qualquer perda de informação é permanente. As
conexões residuais, ao reinjetar informações anteriores a jusante, resolvem parcialmente esse
problema para modelos de aprendizagem profunda.
A retropropagação, o algoritmo mestre usado para treinar redes neurais profundas, funciona
propagando um sinal de feedback da perda de saída para camadas anteriores. Se esse sinal de
realimentação tiver que ser propagado por meio de uma pilha profunda de camadas, o sinal
pode se tornar tênue ou até mesmo ser totalmente perdido, tornando a rede inatingível. Esse
problema é conhecido como gradientes de desaparecimento .
Esse problema ocorre tanto com redes profundas quanto com redes recorrentes em seqüências
muito longas - em ambos os casos, um sinal de feedback deve ser propagado por meio de uma
longa série de operações. Você já está familiarizado com a solução que a LSTMcamada usa para
resolver esse problema em redes recorrentes: ela introduz uma trilha de transporte que propaga
informações paralelas à trilha de processamento principal. Conexões residuais funcionam de
maneira semelhante em redes deep feedforward, mas são ainda mais simples: elas introduzem
uma informação puramente linear que acompanha a pilha da camada principal, ajudando a
propagar gradientes através de pilhas de camadas arbitrariamente profundas.
Por exemplo, considere um modelo que tenta avaliar a semelhança semântica entre duas
sentenças. O modelo tem duas entradas (as duas sentenças para comparar) e gera uma
pontuação entre 0 e 1, em que 0 significa sentenças não relacionadas e 1 significa sentenças que
são idênticas ou reformulações uma da outra. Esse modelo pode ser útil em muitos aplicativos,
incluindo deduplicação de consultas em linguagem natural em um sistema de diálogo.
de importação de keras
da importação de keras.models
Naturalmente, uma instância de camada pode ser usada mais de uma vez - ela pode ser
chamada arbitrariamente várias vezes, reutilizando o mesmo conjunto de pesos todas as vezes.
Se o modelo possui múltiplos tensores de entrada e múltiplos tensores de saída, ele deve ser
chamado com uma lista de tensores:
Quando você chama uma instância de modelo, você reutiliza os pesos do modelo, exatamente
como acontece quando você chama uma instância de camada. Chamar uma instância, seja uma
instância de camada ou uma instância de modelo, sempre reutilizará as representações
aprendidas existentes da instância - o que é intuitivo.
Um exemplo prático simples do que você pode construir reutilizando uma instância de modelo é
um modelo de visão que usa uma câmera dupla como entrada: duas câmeras paralelas,
separadas por alguns centímetros (uma polegada). Tal modelo pode perceber profundidade, o
que pode ser útil em muitas aplicações. Você não deve precisar de dois modelos independentes
para extrair recursos visuais da câmera esquerda e da câmera direita antes de mesclar os dois
feeds. Esse processamento de baixo nível pode ser compartilhado entre as duas entradas: isto é,
feito por camadas que usam os mesmos pesos e, portanto, compartilham as mesmas
representações. Veja como você implementaria um modelo de visão siamês (base convolucional
compartilhada) em Keras:
de importação de keras
include_top = Falso) 1
merged_features = layers.concatenate (
Para sair da SequentialAPI sempre que precisar de algo mais do que uma pilha linear
de camadas
Como construir modelos Keras com várias entradas, várias saídas e topologia de rede
interna complexa, usando a API funcional Keras
Como reutilizar os pesos de uma camada ou modelo em diferentes ramificações de
processamento, chamando a mesma camada ou instância de modelo várias vezes
Nesta seção, analisaremos maneiras de obter maior acesso e controle sobre o que acontece
dentro de seu modelo durante o treinamento. Lançar uma corrida de treinamento em um
grande conjunto de dados para dezenas de épocas
usando model.fit()ou model.fit_generator()pode ser um pouco como lançar um avião
de papel: além do impulso inicial, você não tem nenhum controle sobre sua trajetória ou seu
ponto de pouso. Se você quiser evitar resultados ruins (e, portanto, desperdiçar aviões de papel),
é mais inteligente usar não um avião de papel, mas um drone capaz de detectar seu ambiente,
enviar dados para seu operador e tomar decisões de direção automaticamente com base em seu
estado atual . As técnicas que apresentamos aqui transformarão a chamada model.fit()de
um avião de papel em um drone inteligente e autônomo que pode se auto-introspectar e agir
dinamicamente.
Uma maneira muito melhor de lidar com isso é parar o treinamento quando você mede que a
perda de validação não está mais melhorando. Isso pode ser conseguido usando um retorno de
chamada Keras. Um retorno de chamada é um objeto (uma instância de classe que implementa
métodos específicos) que é passado para o modelo na chamada fite que é chamado pelo
modelo em vários pontos durante o treinamento. Ele tem acesso a todos os dados disponíveis
sobre o estado do modelo e seu desempenho, e pode agir: interromper o treinamento, salvar um
modelo, carregar um conjunto de pesos diferente ou alterar o estado do modelo.
O keras.callbacksmódulo inclui vários retornos de chamada internos (isso não é uma lista
exaustiva):
keras.callbacks.ModelCheckpoint
keras.callbacks.EarlyStopping
keras.callbacks.LearningRateScheduler
keras.callbacks.ReduceLROnPlateau
keras.callbacks.CSVLogger
Vamos rever alguns deles para lhe dar uma idéia de como usá-
los: ModelCheckpoint, EarlyStopping, e ReduceLROnPlateau.
importar keras
callbacks_list = [ 1
keras.callbacks.EarlyStopping ( 2
monitor = 'acc', 3
paciência = 1, 4
keras.callbacks.ModelCheckpoint ( 5 filepath
= 'my_model.h5', 6
monitor = 'val_loss', 7
save_best_only = True, 7
perda = 'binary_crossentropy',
métricas = ['acc']) 8
model.fit (x, y, 9
epochs = 10, 9
batch_size = 32, 9
callbacks = callbacks_list, 9
Você pode usar esse retorno de chamada para reduzir a taxa de aprendizado quando a perda de
validação parar de melhorar. Reduzir ou aumentar a taxa de aprendizado em caso de perda de
platô é uma estratégia eficaz para sair dos mínimos locais durante o treinamento. O exemplo a
seguir usa o ReduceLROnPlateauretorno de chamada:
callbacks_list = [
keras.callbacks.ReduceLROnPlateau (
monitor = 'val_loss' 1
fator = 0.1, 2
paciência = 10, 3
model.fit (x, y, 4
epochs = 10, 4
batch_size = 32, 4
callbacks = callbacks_list, 4
Se você precisar executar uma ação específica durante o treinamento que não esteja coberta por
um dos retornos de chamada internos, poderá escrever seu próprio retorno de
chamada. Callbacks são implementados por meio da subclasse da
classe keras.callbacks.Callback. Você pode então implementar qualquer número dos
seguintes métodos nomeados de forma transparente, que são chamados em vários pontos
durante o treinamento:
on_epoch_begin 1
on_epoch_end 2
on_batch_begin 3
on_batch_end 4
on_train_begin 5
on_train_end 6
Todos esses métodos são chamados com um logsargumento, que é um dicionário que contém
informações sobre o lote, época ou treinamento anterior: métricas de treinamento e validação e
assim por diante. Além disso, o retorno de chamada tem acesso aos seguintes atributos:
Aqui está um exemplo simples de um retorno de chamada personalizado que salva em disco
(como matrizes Numpy) as ativações de cada camada do modelo no final de cada época,
calculado na primeira amostra do conjunto de validação:
importar keras
self.model = modelo 1
layer_outputs) 2
f.close () 4
Isso é tudo o que você precisa saber sobre os retornos de chamada - o resto são detalhes
técnicos, que você pode facilmente consultar. Agora você está equipado para realizar qualquer
tipo de registro ou intervenção pré-programada em um modelo Keras durante o treinamento.
Vamos demonstrar esses recursos em um exemplo simples. Você treinará uma conv. 1D na
tarefa de análise de sentimentos do IMDB.
O modelo é semelhante ao que você viu na última seção do capítulo 6 . Você considerará apenas
as 2.000 palavras no vocabulário do IMDB, para tornar mais fácil a visualização de encartes de
palavras.
importar keras
max_features = 2000 1
max_len = 500 2
model = keras.models.Sequential ()
input_length = max_len,
nome = 'embed'))
model.summary ()
perda = 'binary_crossentropy',
metrics = ['acc'])
Antes de começar a usar o TensorBoard, você precisa criar um diretório onde armazene os
arquivos de log gerados.
$ mkdir my_log_dir
retornos de chamada = [
keras.callbacks.TensorBoard (
log_dir = 'my_log_dir', 1
histogram_freq = 1, 2
embeddings_freq = 1, 3
]
history = model.fit (x_train, y_train,
épocas = 20,
batch_size = 128,
validation_split = 0,2,
Neste ponto, você pode iniciar o servidor TensorBoard a partir da linha de comando, instruindo-
o a ler os registros que o callback está gravando atualmente. O tensorboardutilitário deve ter
sido instalado automaticamente na sua máquina no momento em que você instalou o
TensorFlow (por exemplo, via pip):
Você pode então navegar para http: // localhost: 6006 e ver o treinamento do seu modelo (veja
a figura 7.10 ). Além de gráficos ao vivo das métricas de treinamento e validação, você obtém
acesso à guia Histogramas, onde você pode encontrar belas visualizações de histogramas de
valores de ativação obtidos por suas camadas (veja a figura 7.11 ).
A guia Gráficos mostra uma visualização interativa do gráfico de operações de baixo nível do
TensorFlow subjacente ao seu modelo Keras (consulte a figura 7.13 ). Como você pode ver, há
muito mais acontecendo do que você esperaria. O modelo que você acabou de construir pode
parecer simples quando definido em Keras - uma pequena pilha de camadas básicas - mas, sob o
capô, você precisa construir uma estrutura gráfica bastante complexa para fazê-lo
funcionar. Muito disso está relacionado ao processo gradiente-descendente. Esse diferencial de
complexidade entre o que você vê e o que você está manipulando é a principal motivação para
usar o Keras como sua maneira de construir modelos, em vez de trabalhar com o TensorFlow
bruto para definir tudo do zero. Keras torna seu fluxo de trabalho muito mais simples.
Figura 7.13 TensorBoard: Visualização do gráfico TensorFlow
Note que o Keras também fornece outra maneira mais limpa de plotar modelos como gráficos de
camadas, em vez de gráficos de operações do TensorFlow: o
utilitário keras.utils.plot_model. Usá-lo requer que você tenha instalado o
Python pydote as pydot-ngbibliotecas, assim como a graphvizbiblioteca. Vamos dar uma
olhada rápida:
Você também tem a opção de exibir informações de forma no gráfico de camadas. Este exemplo
visualiza a topologia de modelo usando plot_modele a show_shapesopção (veja a figura
7.15 ):
7.2.3. Empacotando
Os callbacks da Keras fornecem uma maneira simples de monitorar modelos durante o
treinamento e executar ações automaticamente com base no estado do modelo.
Quando você está usando o TensorFlow, o TensorBoard é uma ótima maneira de
visualizar a atividade do modelo em seu navegador. Você pode usá-lo em modelos Keras
através do TensorBoardretorno de chamada.
Experimentar arquiteturas cegas funciona bem o suficiente se você precisar de algo que funcione
bem. Nesta seção, vamos além de "funciona bem" para "funciona muito bem e vence
competições de aprendizado de máquina", oferecendo-lhe um guia rápido para um conjunto de
técnicas indispensáveis para a construção de aprendizado profundo de ponta. modelos.
A normalização é uma categoria ampla de métodos que buscam fazer com que diferentes
amostras vistas por um modelo de aprendizado de máquina sejam mais semelhantes entre si, o
que ajuda o modelo a aprender e generalizar bem com novos dados. A forma mais comum de
normalização de dados é aquela que você já viu várias vezes neste livro: centralizar os dados em
0 subtraindo a média dos dados e dando aos dados um desvio padrão da unidade dividindo os
dados por seu desvio padrão. Na verdade, isso pressupõe que os dados seguem uma distribuição
normal (ou gaussiana) e garante que essa distribuição seja centralizada e dimensionada para a
variação da unidade:
Exemplos anteriores normalizaram os dados antes de inseri-los nos modelos. Mas normalização
de dados deve ser uma preocupação após cada transformação operada pela rede: mesmo que os
dados entrando em um Denseou Conv2Drede tem uma variação de 0 média e unidade, não há
nenhuma razão para esperar a priori que este será o caso para os dados que saem.
Sergey Ioffe e Christian Szegedy, “Normalização de Lote: Acelerando o Treinamento em Rede Profunda pela Redução da Mudança de
Covariância Interna”, Anais da 32ª Conferência Internacional sobre Aprendizado de Máquina (2015), https://arxiv.org/abs/1502.03167.
uma
Sergey Ioffe, “Renormalização em lote: para reduzir a dependência de minibatch em modelos normalizados em lote”
(2017), https://arxiv.org/abs/1702.03275 .
Günter Klambauer et al., “Redes Neurais Auto-Normalizantes”, Conferência sobre Sistemas de Processamento de Informação Neural
(2017), https://arxiv.org/abs/1706.02515 .
E se eu dissesse que há uma camada que você pode usar como substituto para Conv2Disso, isso
tornará seu modelo mais leve (menos parâmetros de peso treinável) e mais rápido (menos
operações de ponto flutuante) e fará com que ele execute alguns pontos percentuais melhor em
sua tarefa? Isso é precisamente o que a camada de convolução separável em profundidade faz (-
SeparableConv2D). Esta camada executa uma convolução espacial em cada canal de sua
entrada, independentemente, antes de misturar os canais de saída através de uma convolução
pontual (uma convolução de 1 × 1), como mostrado na figura 7.16.. Isso equivale a separar o
aprendizado de recursos espaciais e o aprendizado de recursos de canal, o que faz muito sentido
se você assumir que os locais espaciais na entrada são altamente correlacionados, mas canais
diferentes são bastante independentes. Requer significativamente menos parâmetros eenvolve
menos cálculos, resultando em modelos menores e mais rápidos. E como é uma maneira mais
representacionalmente eficiente de executar a convolução, ela tende a aprender representações
melhores usando menos dados, resultando em modelos com melhor desempenho.
Figura 7.16 Convolução separável em profundidade: uma convolução em profundidade seguida por uma convolução pontual
Essas vantagens se tornam especialmente importantes quando você está treinando modelos
pequenos do zero em dados limitados. Por exemplo, aqui está como você pode criar uma
convnet separável leve e profunda para uma tarefa de classificação de imagem (classificação
categórica softmax) em um pequeno conjunto de dados:
altura = 64
largura = 64
canais = 3
num_classes = 10
model = Sequential ()
ativação = 'relu',
A chave para esse processo é o algoritmo que usa esse histórico de desempenho de validação,
considerando vários conjuntos de hiperparâmetros, para escolher o próximo conjunto de
hiperparâmetros a ser avaliado. Muitas técnicas diferentes são possíveis: otimização bayesiana,
algoritmos genéticos, pesquisa aleatória simples e assim por diante.
Treinar os pesos de um modelo é relativamente fácil: você calcula uma função de perda em um
mini lote de dados e, em seguida, usa o algoritmo Backpropagation para mover os pesos na
direção correta. A atualização de hiperparâmetros, por outro lado, é extremamente
desafiadora. Considere o seguinte:
Como esses desafios são difíceis e o campo ainda é jovem, atualmente só temos acesso a
ferramentas muito limitadas para otimizar modelos. Muitas vezes, verifica-se que a pesquisa
aleatória (escolhendo hiperparâmetros para avaliar aleatoriamente, repetidamente) é a melhor
solução, apesar de ser a mais ingênua. Mas uma ferramenta que eu encontrei melhor do que a
pesquisa aleatória é Hyperopt ( https://github.com/hyperopt/hyperopt ), uma biblioteca Python para
otimização de hyperparameter que usa internamente árvores de estimadores Parzen para prever
conjuntos de hiperparâmetros que provavelmente funcionarão bem. Outra biblioteca chamada
Hyperas ( https://github.com/maxpumperla/hyperas ) integra Hyperopt para uso com modelos
Keras. Faça o check-out.
Nota
Vamos usar a classificação como exemplo. A maneira mais fácil de agrupar as previsões de um
conjunto de classificadores (para agrupar os classificadores ) é calcular a média de suas
previsões no tempo de inferência:
Uma maneira mais inteligente de agrupar classificadores é fazer uma média ponderada, em que
os pesos são aprendidos nos dados de validação - normalmente, os classificadores melhores
recebem um peso maior e os classificadores inferiores recebem um peso menor. Para procurar
por um bom conjunto de pesos, você pode usar busca aleatória ou um algoritmo de otimização
simples, como Nelder-Mead:
1 Supõe-se que estes pesos (0,5, 0,25, 0,1, 0,15) sejam aprendidos
empiricamente.
Existem muitas variantes possíveis: você pode fazer uma média exponencial das previsões, por
exemplo. Em geral, uma média ponderada simples com pesos otimizados nos dados de
validação fornece uma linha de base muito forte.
Por esse motivo, você deve montar modelos que sejam tão bons quanto possível , sendo os mais
diferentes possíveis . Isso normalmente significa usar arquiteturas muito diferentes ou até
mesmo diferentes marcas de abordagens de aprendizado de máquina. Uma coisa que não vale a
pena fazer é agrupar a mesma rede treinada várias vezes de forma independente, a partir de
diferentes inicializações aleatórias. Se a única diferença entre seus modelos é sua inicialização
aleatória e a ordem em que eles foram expostos aos dados de treinamento, seu conjunto terá
baixa diversidade e fornecerá apenas uma pequena melhoria em relação a qualquer modelo
único.
Uma coisa que descobri que funciona bem na prática - mas que não se generaliza para todos os
domínios do problema - é o uso de um conjunto de métodos baseados em árvore (como florestas
aleatórias ou árvores impulsionadas por gradientes) e redes neurais profundas. Em 2014, o
sócio Andrei Kolev e eu ficamos em quarto lugar no desafio de detecção de decaimento do Higgs
Boson em Kaggle ( www.kaggle.com/c/higgs-boson) usando um conjunto de vários modelos de
árvores e redes neurais profundas. Notavelmente, um dos modelos no conjunto originou-se de
um método diferente dos outros (era uma floresta gananciosa regularizada) e teve uma
pontuação significativamente pior do que os outros. Sem surpresa, foi atribuído um pequeno
peso no conjunto. Mas para nossa surpresa, acabou por melhorar o conjunto geral por um fator
grande, porque era tão diferente de todos os outros modelos: fornecia informações que os outros
modelos não tinham acesso. Esse é precisamente o ponto de se agrupar. Não é tanto sobre quão
bom é seu melhor modelo; é sobre a diversidade do seu conjunto de modelos candidatos.
Nos últimos tempos, um estilo de conjunto básico que tem sido muito bem sucedido na prática é
a categoria ampla e profunda de modelos, misturando aprendizagem profunda com
aprendizagem superficial. Tais modelos consistem em treinar conjuntamente uma rede neural
profunda com um grande modelo linear. A formação conjunta de uma família de diversos
modelos é mais uma opção para alcançar o conjunto de modelos.
7.3.4. Empacotando
Ao construir convnets profundos de alto desempenho, você precisará usar conexões
residuais, normalização de lotes e convoluções separáveis em profundidade. No futuro,
é provável que as circunvoluções separáveis em profundidade substituam
completamente as convoluções regulares, seja para aplicações 1D, 2D ou 3D, devido à
sua maior eficiência representacional.
Construir redes profundas requer muitas pequenas opções de hiperparâmetro e
arquitetura, que juntas definem o quão bom seu modelo será. Em vez de basear essas
escolhas em intuição ou acaso, é melhor pesquisar sistematicamente o hiperparâmetros
para encontrar as melhores opções. No momento, o processo é caro e as ferramentas
para isso não são muito boas. Mas as bibliotecas Hyperopt e Hyperas podem ajudá-
lo. Ao fazer a otimização de hyperparameter, esteja atento ao overfitting de conjunto de
validação!
Vencer competições de aprendizado de máquina ou obter os melhores resultados
possíveis em uma tarefa só pode ser feito com grandes conjuntos de modelos. O
conjunto por meio de uma média ponderada bem otimizada costuma ser bom o
suficiente. Lembre-se: diversidade é força. É em grande parte sem sentido agrupar
modelos muito semelhantes; os melhores conjuntos são conjuntos de modelos tão
diferentes quanto possível (embora tenham o máximo de poder de previsão possível,
naturalmente).
Resumo do capítulo
O potencial da inteligência artificial para emular processos de pensamento humano vai além de
tarefas passivas, como o reconhecimento de objetos e principalmente tarefas reativas, como
dirigir um carro. Estende-se bem em atividades criativas. Quando afirmei que, num futuro não
tão distante, a maior parte do conteúdo cultural que consumimos será criado com a ajuda
substancial de IAs, fui recebido com total descrença, mesmo de praticantes de longa data de
aprendizado de máquina. Isso foi em 2014. Avançando três anos e a descrença recuou - a uma
velocidade incrível. No verão de 2015, ficamos entretidos com o algoritmo DeepDream do
Google transformando uma imagem em uma confusão psicodélica de olhos de cachorro e
artefatos pareidólicos; em 2016, usamos o aplicativo Prisma para transformar fotos em pinturas
de vários estilos.Sunspring , foi direcionado usando um script escrito por um algoritmo LSTM
(Long Short-Term Memory) - completo com diálogo. Talvez você tenha ouvido recentemente
músicas que foram geradas por uma rede neural.
Concedido, as produções artísticas que temos visto da IA até agora têm sido de baixa
qualidade. A IA não está nem perto de rivalizar com roteiristas, pintores e compositores
humanos. Mas substituir os seres humanos sempre foi irrelevante: a inteligência artificial não é
sobre substituir nossa própria inteligência por outra, é trazer para nossas vidas e
trabalhar mais inteligência - inteligência de um tipo diferente. Em muitos campos, mas
especialmente nos criativos, a IA será usada pelos humanos como uma ferramenta para
aumentar suas próprias capacidades: mais inteligência aumentada
do que inteligência artificial .
Iannis Xenakis, “Musiques formelles: nouveaux principes formels de composition musicale”, edição especial de La Revue musicale , n. 253-
254 (1963).
Liberto de cálculos tediosos, o compositor é capaz de dedicar-se aos problemas gerais que a
nova forma musical representa e explorar os cantos e recantos desta forma enquanto modifica
os valores dos dados de entrada. Por exemplo, ele pode testar todas as combinações
instrumentais de solistas a orquestras de câmara, a grandes orquestras. Com a ajuda de
computadores eletrônicos, o compositor se torna uma espécie de piloto: ele aperta os botões,
introduz coordenadas e supervisiona os controles de um navio cósmico navegando no espaço
sonoro, através de constelações e galáxias sonoras que ele só podia vislumbrar como um
sonho distante.
Nesta seção, exploraremos como as redes neurais recorrentes podem ser usadas para gerar
dados sequenciais. Usaremos a geração de texto como exemplo, mas as mesmas técnicas podem
ser generalizadas para qualquer tipo de dados de sequência: você poderia aplicá-la a seqüências
de notas musicais para gerar novas músicas, a timeseries de dados de pincelada (por exemplo,
gravada enquanto um artista pinta em um iPad) para gerar quadros a pincelada, e assim por
diante.
A geração de dados de sequência não se limita à geração de conteúdo artístico. Ele foi aplicado
com sucesso na síntese de fala e na geração de diálogos para chatbots. O recurso de resposta
inteligente que o Google lançou em 2016, capaz de gerar automaticamente uma seleção de
respostas rápidas para e-mails ou mensagens de texto, usa técnicas semelhantes.
Sepp Hochreiter e Jürgen Schmidhuber, “Long Short-Term Memory”, Neural Computation 9, no. 8 (1997).
No final dos anos 2000 e início de 2010, Alex Graves fez um importante trabalho pioneiro no
uso de redes recorrentes para geração de dados em sequência. Em particular, seu trabalho de
2013 na aplicação de redes de densidade de mistura recorrentes para gerar caligrafia humana,
usando timeseries de posições de caneta, é visto por alguns como um ponto de virada. [ 3 ] Esta
aplicação específica de redes neurais naquele momento específico captou para mim a noção
de máquinas que sonham.e foi uma inspiração significativa na época em que comecei a
desenvolver o Keras. Graves deixou uma observação comentada semelhante, escondida em um
arquivo LaTeX de 2013, carregado no arXiv do servidor preprint: “gerar dados sequenciais é o
computador mais próximo que sonha”. Vários anos depois, consideramos muitos desses
desenvolvimentos como garantidos; mas na época, era difícil assistir às demonstrações de
Graves e não ir embora inspiradas pelas possibilidades.
Alex Graves, “Gerando Sequências com Redes Neurais Recorrentes”, arXiv (2013), https://arxiv.org/abs/1308.0850 .
Desde então, redes neurais recorrentes têm sido usadas com sucesso para geração de músicas,
geração de diálogos, geração de imagens, síntese de voz e design de moléculas. Eles foram
usados até mesmo para produzir um roteiro de filme que foi então lançado com atores ao vivo.
Uma vez que você tenha um modelo de linguagem treinado, você pode fazer amostras dele
(gerar novas sequências): você alimenta uma sequência inicial de texto (chamada
de condicionamento de dados ), pede para gerar o próximo caractere ou a próxima palavra (você
pode gerar vários tokens de uma vez), adicione a saída gerada de volta aos dados de entrada e
repita o processo várias vezes (veja a figura 8.1 ). Esse loop permite gerar seqüências de
comprimento arbitrário que refletem a estrutura dos dados nos quais o modelo foi treinado:
sequências que se parecem quase com sentenças escritas por humanos. No exemplo que
apresentamos nesta seção, você pegará uma camada LSTM, alimentará as strings de Ncaracteres
extraídos de um corpo de texto e treiná-lo para prever o caractere N + 1. A saída do modelo será
um softmax sobre todos os caracteres possíveis: uma distribuição de probabilidade para o
próximo caractere. Este LSTM é chamado de modelo de linguagem neural em nível de
caractere .
Figura 8.1. O processo de geração de texto caractere por caractere usando um modelo de linguagem
A amostragem probabilística da saída softmax do modelo é clara: permite que até mesmo
personagens improváveis sejam amostrados em parte do tempo, gerando frases de aparência
mais interessante e, às vezes, mostrando criatividade ao criar palavras novas e realistas que não
ocorreram nos dados de treinamento. Mas há um problema com essa estratégia: ela não oferece
uma maneira de controlar a quantidade de aleatoriedade no processo de amostragem.
Por que você quer mais ou menos aleatoriedade? Considere um caso extremo: amostragem
aleatória pura, em que você desenha o próximo caractere de uma distribuição de probabilidade
uniforme e cada caractere é igualmente provável. Este esquema tem aleatoriedade máxima; em
outras palavras, essa distribuição de probabilidade tem entropia máxima. Naturalmente, não vai
produzir nada de interessante. No outro extremo, a amostragem gananciosa também não
produz nada de interessante, e não tem aleatoriedade: a distribuição de probabilidade
correspondente tem entropia mínima. A amostragem a partir da distribuição de probabilidade
“real” - a distribuição que é produzida pela função softmax do modelo - constitui um ponto
intermediário entre esses dois extremos. Mas há muitos outros pontos intermediários de maior
ou menor entropia que você pode querer explorar. Menos entropia dará às sequências geradas
uma estrutura mais previsível (e, portanto, elas terão um visual mais realista), ao passo que
mais entropia resultará em seqüências mais surpreendentes e criativas. Ao fazer amostragens a
partir de modelos generativos, é sempre bom explorar diferentes quantidades de aleatoriedade
no processo de geração. Porque nós, os humanos, somos os juízes finais de quão interessantes
são os dados gerados, o interessante é altamente subjetivo, e não há como dizer
antecipadamente onde está o ponto de entropia ideal. enquanto que mais entropia resultará em
seqüências mais surpreendentes e criativas. Ao fazer amostragens a partir de modelos
generativos, é sempre bom explorar diferentes quantidades de aleatoriedade no processo de
geração. Porque nós, os humanos, somos os juízes finais de quão interessantes são os dados
gerados, o interessante é altamente subjetivo, e não há como dizer antecipadamente onde está o
ponto de entropia ideal. enquanto que mais entropia resultará em seqüências mais
surpreendentes e criativas. Ao fazer amostragens a partir de modelos generativos, é sempre bom
explorar diferentes quantidades de aleatoriedade no processo de geração. Porque nós, os
humanos, somos os juízes finais de quão interessantes são os dados gerados, o interessante é
altamente subjetivo, e não há como dizer antecipadamente onde está o ponto de entropia ideal.
Listagem 8.1. Repensando uma distribuição de probabilidade para uma temperatura diferente
Temperaturas mais altas resultam em distribuições de amostragem de entropia mais alta que
gerarão dados gerados mais surpreendentes e não estruturados, enquanto uma temperatura
mais baixa resultará em menos aleatoriedade e dados gerados muito mais previsíveis (consulte
a figura 8.2 ).
Figura 8.2 Diferentes reponders de uma distribuição de probabilidade. Baixa temperatura = mais determinista, alta
temperatura = mais aleatória.
Preparando os dados
importar keras
path = keras.utils.get_file (
'nietzsche.txt',
maxlen = 60 1
passo = 3 2
frases = [] 3
next_chars = [] 4
Construindo a rede
Esta rede é uma LSTMcamada única seguida de um Denseclassificador e softmax sobre todos os
caracteres possíveis. Mas observe que as redes neurais recorrentes não são a única maneira de
fazer a geração de dados em sequência; As capotas 1D também se mostraram extremamente
bem sucedidas nessa tarefa nos últimos tempos.
Listagem 8.4. Modelo LSTM de camada única para predição de próximo caractere
model = keras.models.Sequential ()
Dado um modelo treinado e um snippet de texto de origem, você pode gerar um novo texto
fazendo o seguinte repetidamente:
Este é o código que você usa para reponderar a distribuição de probabilidade original que sai do
modelo e desenhar um índice de caractere a partir dele (a função de amostragem ).
Listagem 8.6. Função para provar o próximo caractere, dadas as previsões do modelo
Finalmente, o loop seguinte treina e gera repetidamente texto. Você começa a gerar texto
usando uma gama de diferentes temperaturas após cada época. Isso permite que você veja como
o texto gerado evolui à medida que o modelo começa a convergir, bem como o impacto da
temperatura na estratégia de amostragem.
importação aleatória
import sys
sys.stdout.write (generated_text)
generated_text + = next_char
sys.stdout.write (next_char)
1 Treina o modelo por 60 épocas
2 Ajusta o modelo para uma iteração nos dados
3 Seleciona uma semente de texto aleatoriamente
4 Tenta uma gama de diferentes temperaturas de amostragem
5 Gera 400 caracteres, começando do texto inicial
6 Um-quente codifica os caracteres gerados até agora
7 Mostra o próximo caractere
Aqui, usamos o texto-semente aleatório “nova faculdade, e o júbilo alcançou seu clímax quando
kant”. Aqui está o que você obtém na época 20, muito antes de o modelo convergir totalmente,
com temperature=0.2:
nova faculdade, e o júbilo atingiu seu clímax quando kant e tal homem
divulgar a humanidade, com um assunto e fato tudo que você tem que ser o
suporte
também, como se nunca o proping to faz como ciência. para ser julgado,
e uma minoria
Na época 60, o modelo convergiu e o texto começa a parecer significativamente mais
coerente. Aqui está o resultado com temperature=0.2:
Não vou dizer, qual é a mais alta e com a religião dos frences.
ciuture para o
nome, por exemplo, mas voludd atu-especity "- ou rank onee, ou mesmo todos
Como você pode ver, um valor de baixa temperatura resulta em texto extremamente repetitivo e
previsível, mas a estrutura local é altamente realista: em particular, todas as palavras
(uma palavrasendo um padrão local de caracteres) são palavras inglesas reais. Com
temperaturas mais altas, o texto gerado se torna mais interessante, surpreendente e até
criativo; às vezes inventa palavras completamente novas que soam algo plausíveis
(como eterned e troveration). Com uma temperatura alta, a estrutura local começa a se
desintegrar e a maioria das palavras se parece com cadeias de caracteres semi-aleatórias de
caracteres. Sem dúvida, 0.5 é a temperatura mais interessante para geração de texto nesta
configuração específica. Sempre experimente com várias estratégias de amostragem! Um
equilíbrio inteligente entre estrutura aprendida e aleatoriedade é o que torna a geração
interessante.
Note que, ao treinar um modelo maior, mais tempo, com mais dados, você pode obter amostras
geradas que parecem muito mais coerentes e realistas do que essa. Mas, é claro, não espere
gerar nenhum texto significativo, a não ser por acaso: tudo o que você está fazendo é amostrar
dados de um modelo estatístico de quais caracteres vêm depois de quais caracteres. A linguagem
é um canal de comunicação, e há uma distinção entre o que é comunicação e a estrutura
estatística das mensagens nas quais as comunicações são codificadas. Para evidenciar essa
distinção, eis uma experiência de pensamento: e se a linguagem humana fizesse um trabalho
melhor de compactar as comunicações, da mesma forma que os computadores fazem com a
maioria das comunicações digitais? A linguagem não seria menos significativa,
8.1.5. Empacotando
Você pode gerar dados de seqüência discreta treinando um modelo para prever os
próximos tokens, dados os tokens anteriores.
No caso do texto, esse modelo é chamado de modelo de linguagem . Pode ser baseado
em palavras ou caracteres.
Amostrar a próxima ficha requer equilíbrio entre aderir ao que o modelo julga provável
e introduzir aleatoriedade.
Uma maneira de lidar com isso é a noção de temperatura softmax. Sempre experimente
com diferentes temperaturas para encontrar o caminho certo.
8.2. DEEPDREAM
Alexander Mordvintsev, Christopher Olah e Mike Tyka, “DeepDream: um exemplo de código para visualização de redes neurais” , Blog de
include_top = False) 2
Em seguida, você calculará a perda : a quantidade que você procura maximizar durante o
processo de subida do gradiente. No capítulo 5Para a visualização de filtros, você tentou
maximizar o valor de um filtro específico em uma camada específica. Aqui, você maximizará
simultaneamente a ativação de todos os filtros em várias camadas. Especificamente, você
maximizará uma soma ponderada da norma L2 das ativações de um conjunto de camadas de
alto nível. O conjunto exato de camadas que você escolhe (assim como sua contribuição para a
perda final) tem uma grande influência nos recursos visuais que você poderá produzir, portanto,
você deseja tornar esses parâmetros facilmente configuráveis. Camadas inferiores resultam em
padrões geométricos, enquanto camadas superiores resultam em visuais nos quais você pode
reconhecer algumas classes do ImageNet (por exemplo, pássaros ou cães).
layer_contributions = { 1
'mixed2': 0,2,
'mixed3': 3.,
'mixed4': 2.,
'mixed5': 1,5,
Agora, vamos definir um tensor que contém a perda: a soma ponderada da norma L2 das
ativações das camadas na listagem 8.9 .
sonho = model.input 1
x + = step * grad_values 5
retorno x 5
Finalmente: o algoritmo DeepDream atual. Primeiro, você define uma lista de escalas (também
chamadas de oitavas ) nas quais processar as imagens. Cada escala sucessiva é maior do que a
anterior por um fator de 1,4 (é 40% maior): você começa processando uma imagem pequena e
então aumenta a escala (veja a figura 8.4 ).
Figura 8.4. O processo DeepDream: escalas sucessivas de processamento espacial (oitavas) e reinjeção de detalhes após
upscaling
Para cada escala sucessiva, do menor para o maior, você executa a subida do gradiente para
maximizar a perda anteriormente definida nessa escala. Após cada subida do gradiente, você
aumenta a imagem resultante em 40%.
Para evitar a perda de muitos detalhes da imagem após cada escalada sucessiva (resultando em
imagens cada vez mais borradas ou pixeladas), você pode usar um truque simples: após cada
escalonamento, você reinjetará os detalhes perdidos de volta na imagem, o que é possível
porque você sabe como a imagem original deve se parecer na escala maior. Dado um pequeno
tamanho da imagem S e um maior tamanho da imagem L , você pode calcular a diferença entre
a imagem original redimensionada para o tamanho L eo original redimensionada para o
tamanho S -este diferença quantifica os detalhes perdidos quando se passa de S a L .
passo = 0,01 1 2
num_octave = 3 1 3
octave_scale = 1,4 1 4
iterações = 20 5
max_loss = 10. 6
base_image_path = '...' 7
successive_shapes = [original_shape] 9
successive_shapes.append (forma) 9
iterações = iterações, 13
passo = passo,
13
max_loss = max_loss 13
img + = missing_detail
17
Observe que esse código usa as seguintes funções Numpy auxiliares simples, que todas fazem
como seus nomes sugerem. Eles exigem que você tenha o SciPy instalado.
import scipy
fatores = (1,
retorno img
se K.image_data_format () == 'channels_first':
outro:
x / = 2.
x + = 0,5
x * = 255.
return x
Nota
Como a rede Inception V3 original foi treinada para reconhecer conceitos em imagens de
tamanho 299 × 299, e dado que o processo envolve reduzir as imagens por um fator razoável, a
implementação do DeepDream produz resultados muito melhores em imagens que estão em
algum lugar entre 300 x 300 e 400 × 400. Independentemente disso, você pode executar o
mesmo código em imagens de qualquer tamanho e qualquer proporção.
A partir de uma fotografia tirada nas pequenas colinas entre a Baía de São Francisco e o campus
do Google, obtivemos o DeepDream mostrado na figura 8.5 .
Sugerimos enfaticamente que você explore o que pode fazer ajustando as camadas usadas na sua
perda. Camadas que são mais baixas na rede contêm representações mais locais, menos
abstratas e levam a padrões de sonhos que parecem mais geométricos. As camadas mais altas
levam a padrões visuais mais reconhecíveis com base nos objetos mais comuns encontrados no
ImageNet, como olhos de cachorro, penas de pássaros e assim por diante. Você pode usar a
geração aleatória dos parâmetros no layer_contributionsdicionário para explorar
rapidamente diversas combinações de camadas. A Figura 8.6 mostra uma série de resultados
obtidos usando diferentes configurações de camadas, a partir de uma imagem de uma deliciosa
massa caseira.
Figura 8.6. Tentando uma variedade de configurações do DeepDream em uma imagem de exemplo
8.2.2. Empacotando
O DeepDream consiste em executar um convnet ao contrário para gerar entradas com
base nas representações aprendidas pela rede.
Os resultados produzidos são divertidos e similares aos artefatos visuais induzidos em
humanos pela ruptura do córtex visual via psicodélicos.
Observe que o processo não é específico para modelos de imagem ou mesmo para
convnets. Isso pode ser feito para fala, música e muito mais.
5
Leon A. Gatys, Alexander S. Ecker e Matthias Bethge, “Algoritmo Neural do Estilo Artístico”, arXiv
(2015), https://arxiv.org/abs/1508.06576 .
Transferência de estilo neural consiste em aplicar o estilo de uma imagem de referência a uma
imagem de destino enquanto conserva o conteúdo da imagem de destino. A Figura 8.7 mostra
um exemplo.
Nesse contexto, estilo significa essencialmente texturas, cores e padrões visuais na imagem, em
várias escalas espaciais; e o conteúdo é a macroestrutura de alto nível da imagem. Por exemplo,
pinceladas circulares azuis e amarelas são consideradas o estilo da figura 8.7 (usando Noite
Estrelada por Vincent Van Gogh), e os edifícios da fotografia de Tübingen são considerados o
conteúdo.
A ideia de transferência de estilo, que é estreitamente relacionada à geração de textura, teve uma
longa história na comunidade de processamento de imagens antes do desenvolvimento da
transferência de estilo neural em 2015. Mas, como se vê, a base de aprendizagem profunda As
implementações de transferência de estilo oferecem resultados sem paralelo com o que havia
sido alcançado anteriormente com técnicas clássicas de visão computacional, e desencadearam
um incrível renascimento em aplicações criativas da visão computacional.
A noção básica por trás da implementação da transferência de estilo é a mesma que é central em
todos os algoritmos de aprendizado profundo: você define uma função de perda para especificar
o que deseja alcançar e minimiza essa perda. Você sabe o que quer alcançar: conservar o
conteúdo da imagem original enquanto adota o estilo da imagem de referência. Se pudéssemos
definir matematicamente oconteúdo e o estilo , uma função de perda apropriada para minimizar
seria a seguinte:
Aqui, distanceé uma função normativa como a norma L2, contenté uma função que pega
uma imagem e calcula uma representação de seu conteúdo, e styleé uma função que pega uma
imagem e calcula uma representação de seu estilo. Minimizar essa perda
faz style(generated_image)com que esteja
próximo style(reference_image)e content(-generated_image)esteja próximo
de content(generated_image)alcançar a transferência de estilo como a definimos.
Uma observação fundamental feita por Gatys et al. foi que as redes neurais convolucionais
profundas oferecem uma maneira de definir matematicamente
as funções stylee content. Vamos ver como.
Um bom candidato para perda de conteúdo é, portanto, a norma L2 entre as ativações de uma
camada superior em uma convã pré-treinada, calculada sobre a imagem alvo, e as ativações da
mesma camada calculadas sobre a imagem gerada. Isso garante que, como visto na camada
superior, a imagem gerada será semelhante à imagem de destino original. Assumindo que o que
as camadas superiores de uma convnet veem é realmente o conteúdo de suas imagens de
entrada, isso funciona como uma forma de preservar o conteúdo da imagem.
Assim, a perda de estilo visa preservar correlações internas semelhantes dentro das ativações de
diferentes camadas, através da imagem de referência de estilo e da imagem gerada. Por sua vez,
isso garante que as texturas encontradas em diferentes escalas espaciais sejam semelhantes na
imagem de referência de estilo e na imagem gerada.
Em resumo, você pode usar uma convnet pré-treinada para definir uma perda que fará o
seguinte:
1. Configure uma rede que calcule as ativações de camada VGG19 para a imagem de
referência de estilo, a imagem de destino e a imagem gerada ao mesmo tempo.
2. Use as ativações de camada calculadas sobre essas três imagens para definir a função de
perda descrita anteriormente, que você minimizará para obter transferência de estilo.
3. Configure um processo de gradiente descendente para minimizar essa função de perda.
Vamos começar definindo os caminhos para a imagem de referência de estilo e a imagem de
destino. Para garantir que as imagens processadas tenham tamanho semelhante (tamanhos
muito diferentes dificultam a transferência de estilos), você redimensionará todas elas para uma
altura compartilhada de 400 px.
img_height = 400 3
retorno img
x [:,:, 0] + = 103.939 1
x [:,:, 1] + = 116,779 1
x [:,:, 2] + = 123,68 1
x = x [:,:, :: - 1] 2
x = np.clip (x, 0, 255) .astype ('uint8')
return x
Vamos configurar a rede VGG19. Ele recebe como entrada um lote de três imagens: a imagem de
referência de estilo, a imagem de destino e um espaço reservado que conterá a imagem
gerada. Um espaço reservado é um tensor simbólico, cujos valores são fornecidos externamente
por meio de matrizes Numpy. A referência de estilo e a imagem de destino são estáticas e,
portanto, definidas usando K.constant, enquanto os valores contidos no espaço reservado da
imagem gerada serão alterados ao longo do tempo.
style_reference_image, 2
combination_image], eixo = 0) 2
pesos = 'imagenet', 3
include_top = False) 3
Vamos definir a perda de conteúdo, que fará com que a camada superior da convnet VGG19
tenha uma visão semelhante da imagem de destino e da imagem gerada.
retorno grama
S = gram_matrix (estilo)
C = gram_matrix (combinação)
canais = 3
Para esses dois componentes de perda, você adiciona um terceiro: a perda de variação total ,
que opera nos pixels da imagem combinada gerada. Incentiva a continuidade espacial na
imagem gerada, evitando resultados excessivamente pixelados. Você pode interpretá-lo como
uma perda de regularização.
a = K.square (
b = K.square (
A perda que você minimiza é uma média ponderada dessas três perdas. Para calcular a perda de
conteúdo, você usa apenas uma camada superior - a block5_conv2camada - enquanto que,
para a perda de estilo, você usa uma lista de camadas que abrange camadas de baixo e alto
nível. Você adiciona a perda total de variação no final.
Dependendo da imagem de referência de estilo e da imagem de conteúdo que você está usando,
é provável que você queira ajustar o content_weightcoeficiente (a contribuição da perda de
conteúdo para a perda total). Uma maior content_weightsignifica que o conteúdo alvo será
mais reconhecível na imagem gerada.
Listagem 8.20. Definindo a perda final que você minimizará
content_layer = 'block5_conv2' 2
style_layers = ['block1_conv1', 3
'block2_conv1', 3
'block3_conv1', 3
'block4_conv1' , 3
'block5_conv1'] 3
total_variation_weight = 1e-4 4
style_weight = 1. 4
content_weight = 0.025 4
combination_features) 6
Isso requer que você passe o valor da função loss e o valor dos gradientes como duas
funções separadas.
Ele só pode ser aplicado a vetores simples, enquanto você tem um array de imagens 3D.
Seria ineficiente calcular o valor da função de perda e o valor dos gradientes de forma
independente, porque isso levaria a muitos cálculos redundantes entre os dois; o processo seria
quase duas vezes mais lento do que computá-los em conjunto. Para contornar isso, você
configurará uma classe Python chamada Evaluatorque calcula o valor de perda e o valor de
gradientes de uma vez, retorna o valor de perda quando chamado pela primeira vez e armazena
em cache os gradientes da próxima chamada.
self.loss_value = Nenhum
self.grads_values = Nenhum
self.loss_value = loss_value
self.grad_values = grad_values
return self.loss_value
self.loss_value = Nenhum
self.grad_values = Nenhum
return grad_values
avaliador = avaliador ()
Finalmente, você pode executar o processo de subida de gradiente usando o algoritmo L-BFGS
do SciPy, salvando a imagem gerada atual em cada iteração do algoritmo (aqui, uma única
iteração representa 20 etapas de subida do gradiente).
tempo de importação
result_prefix = 'my_result'
iterações = 20
x = preprocess_image (target_image_path) 1
x = x.flatten () 2
start_time = time.time ()
x, 3
fprime = avaliador.grads, 3
maxfun = 20) 3
end_time = time.time () 4
A Figura 8.8 mostra o que você obtém. Tenha em mente que o que essa técnica alcança é
meramente uma forma de retexturização de imagem ou transferência de textura. Funciona
melhor com imagens de referência de estilo que são fortemente texturizadas e altamente
semelhantes, e com destinos de conteúdo que não exigem altos níveis de detalhes para serem
reconhecíveis. Normalmente, não consegue realizar proezas bastante abstratas, como transferir
o estilo de um retrato para outro. O algoritmo está mais próximo do processamento de sinal
clássico do que da AI, então não espere que ele funcione como mágica!
Figura 8.8. Alguns exemplos de resultados
Além disso, observe que executar esse algoritmo de transferência de estilo é lento. Mas a
transformação operada pela configuração é simples o suficiente para que ela também possa ser
aprendida por uma pequena e rápida convenção de feedforward - contanto que você tenha
dados de treinamento apropriados disponíveis. A transferência rápida de estilo pode ser obtida
primeiro gastando muitos ciclos de computação para gerar exemplos de treinamento de entrada
/ saída para uma imagem de referência de estilo fixa, usando o método descrito aqui e treinando
uma convnet simples para aprender essa transformação específica de estilo. Uma vez feito isso,
a estilização de uma determinada imagem é instantânea: é apenas um passo para a frente dessa
pequena convnet.
8.3.4. Empacotando
A transferência de estilo consiste em criar uma nova imagem que preserve o conteúdo
de uma imagem de destino e, ao mesmo tempo, capturar o estilo de uma imagem de
referência.
O conteúdo pode ser capturado pelas ativações de alto nível de uma convnet.
O estilo pode ser capturado pelas correlações internas das ativações de diferentes
camadas de uma convnet.
Assim, o aprendizado profundo permite que a transferência de estilo seja formulada
como um processo de otimização usando uma perda definida com uma convnet pré-
treinada.
Partindo desta ideia básica, muitas variantes e refinamentos são possíveis.
Amostrar a partir de um espaço latente de imagens para criar imagens inteiramente novas ou
editar imagens existentes é atualmente a aplicação mais popular e bem-sucedida da IA
criativa. Nesta seção e na próxima, revisaremos alguns conceitos de alto nível pertencentes à
geração de imagens, juntamente com detalhes de implementações relativos às duas principais
técnicas neste domínio: autoencodificadores variacionais (VAEs) e redes geradoras de
adversários (GANs). As técnicas que apresentamos aqui não são específicas para imagens - você
pode desenvolver espaços latentes de som, música ou até mesmo texto, usando GANs e VAEs -
mas, na prática, os resultados mais interessantes foram obtidos com imagens, e é nisso que nos
concentramos. aqui.
GANs e VAEs são duas estratégias diferentes para aprender tais espaços latentes de
representações de imagens, cada um com suas próprias características. As VAEs são ótimas para
aprender espaços latentes que são bem estruturados, onde instruções específicas codificam um
eixo significativo de variação nos dados. As GANs geram imagens que podem ser altamente
realistas, mas o espaço latente de onde elas vêm pode não ter tanta estrutura e continuidade.
Figura 8.10. Um espaço contínuo de rostos gerados por Tom White usando VAEs
Diederik P. Kingma e Max Welling, “Bayes Variacionais de Codificação Automática, arXiv (2013), https://arxiv.org/abs/1312.6114 .
Danilo Jimenez Rezende, Shakir Mohamed e Daan Wierstra, “Retropropagação Estocástica e Inferência Aproximada em Modelos
Um autoencoder de imagem clássica pega uma imagem, mapeia-a para um espaço vetorial
latente através de um módulo codificador e a decodifica de volta para uma saída com as mesmas
dimensões da imagem original, através de um módulo decodificador (veja a figura 8.12 ). Em
seguida, ele é treinado usando como dados de destino as mesmas imagens que as imagens de
entrada, o que significa que o autoencoder aprende a reconstruir as entradas originais. Impondo
várias restrições no código (a saída do codificador), você pode obter o autocoder para aprender
representações latentes mais ou menos interessantes dos dados. Mais comumente, você
restringirá o código a ser de baixa dimensão e escasso (principalmente zeros), caso em que o
codificador atua como uma maneira de compactar os dados de entrada em menos bits de
informação.
Figura 8.12. Um autoencoder: mapeando uma entrada xpara uma representação comprimida e, em seguida, decodificando-a
de voltax'
Um VAE, em vez de compactar sua imagem de entrada em um código fixo no espaço latente,
transforma a imagem nos parâmetros de uma distribuição estatística: uma média e uma
variância. Essencialmente, isso significa que você está assumindo que a imagem de entrada foi
gerada por um processo estatístico e que a aleatoriedade desse processo deve ser levada em
consideração durante a codificação e a decodificação. O VAE então usa os parâmetros de média
e variância para amostrar aleatoriamente um elemento da distribuição e decodifica esse
elemento de volta para a entrada original (consulte a figura 8.13 ). A estocasticidade desse
processo melhora a robustez e força o espaço latente a codificar representações significativas em
todos os lugares: cada ponto amostrado no espaço latente é decodificado para uma saída válida.
Figura 8.13. Um VAE mapeia uma imagem para dois vetores z_meane z_log_sigma, que definem uma distribuição
de probabilidade sobre o espaço latente, usado para amostrar um ponto latente para decodificar.
Como epsiloné aleatório, o processo garante que cada ponto que esteja próximo ao local
latente onde você codificou input_img( z-mean) possa ser decodificado para algo semelhante
a input_img, forçando assim o espaço latente a ser continuamente significativo. Quaisquer
dois pontos próximos no espaço latente decodificarão para imagens altamente semelhantes. A
continuidade, combinada com a baixa dimensionalidade do espaço latente, força todas as
direções no espaço latente a codificar um eixo significativo de variação dos dados, tornando o
espaço latente muito estruturado e, portanto, altamente adequado à manipulação por meio de
vetores conceituais.
Os parâmetros de um VAE são treinados por meio de duas funções de perda: uma perda de
reconstrução que força as amostras decodificadas a corresponderem às entradas iniciais e
uma perda de regularização que ajuda a aprender espaços latentes bem formados e reduz o
overfitting aos dados de treinamento. Vamos rapidamente passar por cima de uma
implementação Keras de um VAE. Esquematicamente, parece com isso:
Você pode então treinar o modelo usando a perda de reconstrução e a perda de regularização.
A listagem a seguir mostra a rede do codificador que você usará, mapeando as imagens para os
parâmetros de uma distribuição de probabilidade sobre o espaço latente. É uma simples convnet
que mapeia a imagem de entrada xpara dois vetores z_meane z_log_var.
importar keras
da importação de keras.models
batch_size = 16
latent_dim = 2 1
input_img = keras.Input (shape = img_shape)
x = camadas.Conv2D (32, 3,
x = camadas.Conv2D (64, 3,
x = camadas.Conv2D (64, 3,
x = camadas.Conv2D (64, 3,
x = layers.Flatten () (x)
Listagem 8.25. Rede decodificadora VAE, mapeando pontos de espaço latente para imagens
x = layers.ConvDDTranspose (32, 3, 4
padding = 'same', 4
activation = 'relu', 4
x = camadas.Conv2D (1, 3, 4
preenchimento = 'mesmo', 4
A perda dupla de um VAE não se ajusta à expectativa tradicional de uma função de amostra do
formulário loss(input, target). Assim, você configurará a perda escrevendo uma camada
personalizada que usa internamente o add_lossmétodo da camada interna para criar uma
perda arbitrária.
x = K.flatten (x)
x = entradas [0]
retorno x 2
Finalmente, você está pronto para instanciar e treinar o modelo. Como a perda é cuidada na
camada personalizada, você não especifica uma perda externa em tempo de compilação
( loss=None),o que significa que você não passará dados de destino durante o treinamento
(como você pode ver, você só passa x_trainpara o modelo fit).
vae.summary ()
shuffle = Verdadeiro
épocas = 10,
batch_size = batch_size,
Uma vez que tal modelo seja treinado - no MNIST, neste caso - você pode usar a decoderrede
para transformar vetores espaciais latentes arbitrários em imagens.
Listagem 8.28. Amostrar uma grade de pontos do espaço latente 2D e decodificá-los para imagens
n = 15 1
digit_size = 28
plt.show ()
A grade de dígitos amostrados (veja a figura 8.14 ) mostra uma distribuição completamente
contínua das diferentes classes de dígitos, com um dígito se transformando em outro à medida
que você segue um caminho através do espaço latente. As direções específicas neste espaço têm
um significado: por exemplo, há uma direção para “quatro-ness”, “one-ness” e assim por diante.
8.4.4. Empacotando
A geração de imagens com aprendizado profundo é feita pelo aprendizado de espaços
latentes que capturam informações estatísticas sobre um conjunto de dados de
imagens. Por amostragem e decodificação de pontos do espaço latente, você pode gerar
imagens nunca antes vistas. Existem duas ferramentas principais para fazer isso: VAEs
e GANs.
As VAEs resultam em representações latentes contínuas e altamente estruturadas. Por
essa razão, eles funcionam bem para fazer todo tipo de edição de imagem no espaço
latente: trocar de rosto, transformar um rosto carrancudo em um rosto sorridente e
assim por diante. Eles também funcionam muito bem para fazer animações baseadas
em espaço latente, como animar uma caminhada ao longo de uma seção transversal do
espaço latente, mostrando uma imagem inicial lentamente se transformando em
imagens diferentes de maneira contínua.
As GANs permitem a geração de imagens realistas de um único quadro, mas podem não
induzir espaços latentes com estrutura sólida e alta continuidade.
As aplicações práticas mais bem sucedidas que tenho visto com imagens dependem de VAEs,
mas as GANs são extremamente populares no mundo da pesquisa acadêmica - pelo menos, por
volta de 2016–2017. Você descobrirá como eles funcionam e como implementar um na próxima
seção.
Gorjeta
Para continuar a trabalhar com a geração de imagens, sugiro trabalhar com o conjunto de dados
CelebA Attributes em grande escala. É um conjunto de dados de imagens gratuito para
download que contém mais de 200.000 retratos de celebridades. É ótimo para experimentar
com vetores conceituais em particular - definitivamente supera o MNIST.
As redes geradoras de conflitos (GANs), introduzidas em 2014 por Goodfellow et al., [ 8 ] são uma
alternativa aos VAEs para o aprendizado de espaços latentes de imagens. Eles permitem a
geração de imagens sintéticas razoavelmente realistas, forçando as imagens geradas a serem
estatisticamente quase indistinguíveis das imagens reais.
Uma maneira intuitiva de entender as GANs é imaginar um falsificador tentando criar uma
pintura falsa de Picasso. No início, o falsificador é muito ruim na tarefa. Ele mistura algumas de
suas falsificações com o autêntico Picassos e as mostra a um negociante de arte. O negociante de
arte faz uma avaliação de autenticidade para cada pintura e dá ao falsificador um feedback sobre
o que faz um Picasso parecer um Picasso. O falsificador volta ao seu estúdio para preparar
algumas novas falsificações. Com o passar do tempo, o falsificador se torna cada vez mais
competente em imitar o estilo de Picasso, e o negociante de arte se torna cada vez mais
especialista em identificar falsificações. No final, eles têm em suas mãos alguns excelentes
Picassos falsos.
Isso é o que uma GAN é: uma rede de falsificação e uma rede de especialistas, cada uma sendo
treinada para melhorar a outra. Como tal, uma GAN é composta de duas partes:
Rede do gerador - Toma como entrada um vetor aleatório (um ponto aleatório no
espaço latente) e o decodifica em uma imagem sintética
Rede discriminadora (ou adversária) - Toma como entrada uma imagem (real ou
sintética) e prevê se a imagem veio do conjunto de treinamento ou foi criada pela rede
do gerador.
A rede geradora é treinada para enganar a rede discriminadora, e assim evolui para gerar
imagens cada vez mais realistas à medida que o treinamento avança: imagens artificiais que
parecem indistinguíveis das imagens reais, na medida em que é impossível para a rede
discriminadora dizer a dois separados (ver figura 8.15 ). Enquanto isso, o discriminador está
constantemente se adaptando às capacidades de melhoria gradual do gerador, estabelecendo um
alto nível de realismo para as imagens geradas. Uma vez terminado o treinamento, o gerador é
capaz de transformar qualquer ponto em seu espaço de entrada em uma imagem verossímil. Ao
contrário dos VAEs, esse espaço latente tem menos garantias explícitas de estrutura
significativa; em particular, não é contínuo.
Figura 8.15. Um gerador transforma vetores latentes aleatórios em imagens, e um discriminador procura dizer imagens reais
das geradas. O gerador é treinado para enganar o discriminador.
Notavelmente, uma GAN é um sistema em que o mínimo de otimização não é fixo, ao contrário
de qualquer outra configuração de treinamento que você encontrou neste livro. Normalmente, a
descida em gradiente consiste em descer colinas em um cenário de perda estática. Mas com um
GAN, cada passo descendo a colina muda um pouco a paisagem inteira. É um sistema dinâmico
em que o processo de otimização não busca um mínimo, mas um equilíbrio entre duas
forças. Por essa razão, as GANs são notoriamente difíceis de treinar - fazer com que uma GAN
funcione requer um ajuste cuidadoso da arquitetura do modelo e dos parâmetros de
treinamento.
Figura 8.16. Moradores do espaço latente. Imagens geradas por Mike Tyka usando uma GAN multiestágio treinada em um
conjunto de dados de faces ( www.miketyka.com ).
Aqui estão alguns dos truques usados na implementação do gerador e discriminador de GAN
nesta seção. Não é uma lista exaustiva de dicas relacionadas à GAN; você encontrará muito mais
na literatura do GAN:
8.5.3. O gerador
Primeiro, vamos desenvolver um generatormodelo que transforme um vetor (do espaço
latente - durante o treinamento, ele será amostrado aleatoriamente) em uma imagem
candidata. Um dos muitos problemas que geralmente surgem com as GANs é que o gerador fica
preso a imagens geradas que parecem ruído. Uma solução possível é usar o dropout no
discriminador e no gerador.
importar keras
latent_dim = 32
altura = 32
largura = 32
canais = 3
x = camadas.LeakyReLU () (x) 1
x = camadas.LeakyReLU () (x)
x = camadas.LeakyReLU () (x)
x = camadas.LeakyReLU () (x)
generator.summary () 3
8.5.4. O discriminador
Em seguida, você desenvolverá um discriminatormodelo que toma como entrada uma
imagem candidata (real ou sintética) e a classifica em uma das duas classes: “imagem gerada” ou
“imagem real que vem do conjunto de treinamento”.
x = camadas.LeakyReLU () (x)
x = camadas.LeakyReLU () (x)
x = camadas.LeakyReLU () (x)
x = camadas.LeakyReLU () (x)
x = layers.Flatten () (x)
discriminator.summary ()
discriminator_optimizer = keras.optimizers.RMSprop (
lr = 0,0008,
decaimento = 1e-8) 5
perda = 'binary_crossentropy')
discriminator.trainable = Falso 1
Vamos implementá-lo.
importar os
x_train = x_train.reshape (
(x_train.shape [0],) + 3
iterações = 10000
batch_size = 20
save_dir = 'your_dir' 4
start = 0
latent_dim)) 5
latent_dim)) 11
misleading_targets) 13
start + = batch_size
start = 0
if step% 100 == 0: 14
gan.save_weights ('gan.h5')
15
Ao treinar, você pode ver que a perda adversária começa a aumentar consideravelmente,
enquanto a perda discriminativa tende a zero - o discriminador pode acabar dominando o
gerador. Se esse for o caso, tente reduzir a taxa de aprendizado do discriminador e aumente a
taxa de desistência do discriminador.
Figura 8.18. Jogue o discriminador: em cada linha, duas imagens foram sonhadas pelo GAN, e uma imagem vem do conjunto
de treinamento. Você pode diferenciá-los? (Respostas: as imagens reais em cada coluna são média, superior, inferior,
central).
8.5.7. Empacotando
Uma GAN consiste em uma rede geradora acoplada a uma rede discriminadora. O
discriminador é treinado para diferenciar entre a saída do gerador e as imagens reais de
um conjunto de dados de treinamento, e o gerador é treinado para enganar o
discriminador. Notavelmente, o gerador nunca vê imagens do conjunto de treinamento
diretamente; a informação que tem sobre os dados vem do discriminador.
As GANs são difíceis de treinar, porque o treinamento de uma GAN é um processo
dinâmico, em vez de um simples processo de descida gradiente com um cenário de
perda fixa. Conseguir que uma GAN treine corretamente requer o uso de vários truques
heurísticos, bem como um ajuste extenso.
As GANs podem produzir imagens altamente realistas. Mas, ao contrário dos VAEs, o
espaço latente que eles aprendem não tem uma estrutura contínua pura e, portanto,
pode não ser adequado para certas aplicações práticas, como a edição de imagens por
meio de vetores conceituais de espaço latente.
Resumo do capítulo
Capítulo 9 Conclusões
Este capítulo cobre
Você quase chegou ao final deste livro. Este último capítulo resumirá e revisará os principais
conceitos, além de expandir seus horizontes para além das noções relativamente básicas que
você aprendeu até agora. Compreender a aprendizagem profunda e a IA é uma jornada, e
terminar este livro é apenas o primeiro passo. Quero ter certeza de que você percebeu isso e está
devidamente preparado para dar os próximos passos dessa jornada sozinho.
Começaremos com uma visão panorâmica do que você deve tirar deste livro. Isso deve refrescar
sua memória em relação a alguns dos conceitos que você aprendeu. A seguir, apresentaremos
uma visão geral de algumas das principais limitações da aprendizagem profunda. Para usar uma
ferramenta adequadamente, você deve não apenas entender o que ela pode fazer, mas também
estar ciente do que ela não pode fazer. Finalmente, apresentarei alguns pensamentos
especulativos sobre a evolução futura dos campos de aprendizado profundo, aprendizado de
máquina e inteligência artificial. Isso deve ser especialmente interessante para você se quiser
entrar em pesquisa fundamental. O capítulo termina com uma pequena lista de recursos e
estratégias para aprender mais sobre IA e manter-se atualizado com novos avanços.
Esta seção sintetiza brevemente as principais conclusões deste livro. Se você precisar de uma
atualização rápida para ajudá-lo a lembrar o que aprendeu, leia estas poucas páginas.
Mesmo que o aprendizado profundo seja apenas uma entre muitas abordagens para
aprendizado de máquina, ele não está em pé de igualdade com os outros. Aprendizagem
profunda é um sucesso de fuga. Aqui está o porquê.
Devido aos seus sucessos técnicos sem precedentes, o aprendizado profundo trouxe sozinho o
terceiro e de longe o maior verão de IA: um período de intenso interesse, investimento e
propaganda no campo da IA. Enquanto este livro está sendo escrito, estamos no meio disso. Se
esse período terminará em um futuro próximo, e o que acontece depois que ele termina, são
tópicos de debate. Uma coisa é certa: em contraste com os anteriores veranistas de inteligência
artificial, o aprendizado profundo forneceu um enorme valor comercial a várias empresas de
tecnologia de grande porte, permitindo reconhecimento de voz em nível humano, assistentes
inteligentes, classificação de imagens em nível humano, melhorou a tradução automática e
Mais. O hype pode (e provavelmente irá) retroceder, mas o impacto econômico e tecnológico
sustentado da aprendizagem profunda permanecerá. Nesse sentido, o aprendizado profundo
poderia ser análogo à internet: pode ser excessivamente sensacionalista por alguns anos,
Estou particularmente otimista em relação ao aprendizado profundo, porque, mesmo que não
fizéssemos mais avanços tecnológicos na próxima década, a implantação de algoritmos
existentes em todos os problemas aplicáveis seria um divisor de águas para a maioria das
indústrias. O aprendizado profundo não é nada menos que uma revolução, e o progresso está
acontecendo atualmente a uma taxa incrivelmente rápida, devido a um investimento
exponencial em recursos e número de funcionários. De onde estou, o futuro parece brilhante,
embora as expectativas de curto prazo sejam um tanto excessivamente otimistas; A implantação
de uma aprendizagem profunda em toda a extensão do seu potencial levará mais de uma
década.
Richard Feynman, entrevista, O mundo de outro ponto de vista , Yorkshire Television, 1972.
Na aprendizagem profunda, tudo é um vetor: tudo é um ponto em um espaço
geométrico . Entradas de modelo (texto, imagens e assim por diante) e alvos são
primeiramente vetorizados: transformado em um espaço vetorial de entrada inicial e espaço
vetorial de destino. Cada camada em um modelo de aprendizagem profunda opera uma
transformação geométrica simples nos dados que passam por ela. Juntos, a cadeia de camadas
no modelo forma uma transformação geométrica complexa, dividida em uma série de
transformações simples. Essa transformação complexa tenta mapear o espaço de entrada para o
espaço de destino, um ponto por vez. Essa transformação é parametrizada pelos pesos das
camadas, que são atualizadas iterativamente com base no desempenho do modelo no
momento. Uma característica fundamental dessa transformação geométrica é que ela deve
ser diferenciável, o que é necessário para que possamos aprender seus parâmetros via gradiente
descendente. Intuitivamente, isso significa que a metamorfose geométrica das entradas para as
saídas deve ser suave e contínua - uma restrição significativa.
Todo o processo de aplicar essa transformação geométrica complexa aos dados de entrada pode
ser visualizado em 3D imaginando uma pessoa tentando descompactar uma bola de papel: a
bola de papel amassada é a variedade dos dados de entrada que o modelo inicia. Cada
movimento operado pela pessoa na bola de papel é semelhante a uma transformação geométrica
simples operada por uma camada. A sequência do gesto completo de desalinhamento é a
transformação complexa do modelo inteiro. Os modelos de aprendizagem profunda são
máquinas matemáticas para desalinhar variedades complexas de dados de alta dimensão.
A coisa toda depende de uma única idéia central: esse significado é derivado da relação de
pares entre coisas (entre palavras em uma linguagem, entre pixels em uma imagem e assim por
diante) e que essas relações podem ser capturadas por uma função de distância . Mas observe
que se o cérebro implementa o significado através de espaços geométricos é uma questão
totalmente separada. Espaços vetoriais são eficientes para trabalhar do ponto de vista
computacional, mas diferentes estruturas de dados para inteligência podem ser facilmente
visualizadas - em particular, gráficos. As redes neurais surgiram inicialmente da ideia de usar
gráficos como uma maneira de codificar o significado, e é por isso que eles são
denominados redes neurais ; o campo circundante de pesquisa costumava ser
chamadoconexionismo . Hoje em dia, o nome rede neural existe apenas por razões históricas - é
um nome extremamente enganador porque não é nem neural nem redes. Em particular, as
redes neurais dificilmente têm algo a ver com o cérebro. Um nome mais apropriado teria sido
o aprendizado de representações em camadas ou o aprendizado de
representações hierárquicas , ou talvez até mesmo modelos profundamente
diferenciáveis ou transformações geométricas encadeadas , para enfatizar o fato de que a
manipulação contínua do espaço geométrico é o núcleo.
No futuro, a aprendizagem profunda não será usada apenas por especialistas - pesquisadores,
estudantes de pós-graduação e engenheiros com um perfil acadêmico -, mas também será uma
ferramenta na caixa de ferramentas de todos os desenvolvedores, muito parecida com a
tecnologia da web de hoje. Todo mundo precisa criar aplicativos inteligentes: assim como toda
empresa hoje precisa de um website, todos os produtos precisarão entender inteligentemente os
dados gerados pelo usuário. Trazer esse futuro nos exigirá construir ferramentas que tornem a
aprendizagem profunda radicalmente fácil de usar e acessível a qualquer pessoa com
habilidades básicas de codificação. Keras é o primeiro grande passo nessa direção.
1. Defina o problema: quais dados estão disponíveis e o que você está tentando
prever? Você precisará coletar mais dados ou contratar pessoas para rotular
manualmente um conjunto de dados?
2. Identifique uma maneira de medir com segurança o sucesso em seu objetivo. Para
tarefas simples, isso pode ser uma precisão de previsão, mas em muitos casos exigirá
métricas sofisticadas específicas do domínio.
3. Prepare o processo de validação que você usará para avaliar seus modelos. Em
particular, você deve definir um conjunto de treinamento, um conjunto de validação e
um conjunto de testes. Os rótulos de validação e conjunto de testes não devem vazar
para os dados de treinamento: por exemplo, com previsão temporal, os dados de
validação e teste devem ser posteriores aos dados de treinamento.
4. Vetorize os dados transformando-os em vetores e pré-processando-os de uma maneira
que os torne mais facilmente acessíveis por uma rede neural (normalização e assim por
diante).
5. Desenvolva um primeiro modelo que supere uma linha de base trivial de senso comum,
demonstrando assim que o aprendizado de máquina pode funcionar em seu
problema. Isso nem sempre pode ser o caso!
6. Refine gradualmente sua arquitetura de modelo ajustando hiperparâmetros e
adicionando regularização. Faça alterações com base no desempenho apenas nos dados
de validação, não nos dados de teste ou nos dados de treinamento. Lembre-se de que
você deve fazer com que seu modelo seja super adaptável (identificando, assim, um
nível de capacidade de modelo maior do que o necessário) e só então comece a adicionar
regularização ou reduzir o tamanho do seu modelo.
7. Esteja ciente de overfitting de conjunto de validação ao transformar hyperparameters: o
fato de que seus hiperparâmetros podem acabar sendo superespecializados para o
conjunto de validação. Evitar isso é o propósito de ter um conjunto de testes separado!
9.1.6. Principais arquiteturas de rede
As três famílias de arquiteturas de rede com as quais você deve estar familiarizado são redes
densamente conectadas , redes convolucionais e redes recorrentes . Cada tipo de rede destina-
se a uma modalidade de entrada específica: uma arquitetura de rede (densa, convolucional,
recorrente) codifica suposições sobre a estrutura dos dados: um espaço de hipóteses dentro do
qual a busca por um bom modelo prosseguirá. Se uma determinada arquitetura funcionará em
um determinado problema, depende inteiramente da correspondência entre a estrutura dos
dados e as suposições da arquitetura de rede.
Esses diferentes tipos de rede podem ser facilmente combinados para alcançar redes
multimodais maiores, da mesma forma que você combina blocos LEGO. De certa forma, as
camadas de aprendizagem profunda são peças LEGO para processamento de informações. Aqui
está uma visão geral rápida do mapeamento entre as modalidades de entrada e as arquiteturas
de rede apropriadas:
Uma rede densamente conectada é uma pilha de Densecamadas, destinada a processar dados
vetoriais (lotes de vetores). Essas redes não assumem nenhuma estrutura específica nos
recursos de entrada: elas são chamadas densamente conectadas porque as unidades de
uma Densecamada são conectadas a todas as outras unidades. A camada tenta mapear
relacionamentos entre quaisquer dois recursos de entrada; Isso é diferente de uma camada de
convolução 2D, por exemplo, que apenas analisa asrelações locais .
Redes densamente conectadas são mais comumente usadas para dados categóricos (por
exemplo, onde os recursos de entrada são listas de atributos), como o conjunto de dados Price
Housing da Boston usado no capítulo 3 . Eles também são usados como classificação final ou
estágio de regressão da maioria das redes. Por exemplo, as capas cobertas no capítulo
5 geralmente terminam com uma ou duas Densecamadas, assim como as redes recorrentes
no capítulo 6 .
Lembre-se: para realizar a classificação binária , termine sua pilha de camadas com
uma Densecamada com uma única unidade e uma sigmoidativação e
use binary_crossentropycomo a perda. Seus alvos devem ser 0 ou 1:
model = models.Sequential ()
model.add (layers.Dense (32, activation = 'relu', input_shape =
(num_input_features,)))
Para realizar uma classificação categórica de rótulo único (em que cada amostra tem
exatamente uma classe, não mais), termine sua pilha de camadas com uma Densecamada com
um número de unidades igual ao número de classes e uma softmaxativação. Se seus alvos
forem codificados com um clique, use categorical_crossentropycomo perda; se forem
inteiros, use sparse_categorical_crossentropy:
model = models.Sequential ()
Para executar uma classificação categórica de várias linhas (em que cada amostra pode ter
várias classes), termine sua pilha de camadas com uma Densecamada com um número de
unidades igual ao número de classes e uma sigmoidativação e
use binary_crossentropycomo a perda. Seus alvos devem ser codificados com k-hot:
model = models.Sequential ()
Para realizar a regressão em direção a um vetor de valores contínuos, termine sua pilha de
camadas com uma Densecamada com um número de unidades igual ao número de valores que
você está tentando prever (geralmente um único, como o preço de uma casa). e sem
ativação. Várias perdas podem ser usadas para regressão, mais
comumente mean_squared_error(MSE) e mean_absolute_error(MAE):
model = models.Sequential ()
Convnets
Note que é altamente provável que as circunvoluções regulares sejam em grande parte em breve
(ou completamente) substituídas por uma alternativa equivalente, porém mais rápida e
representativa: a convolução ( SeparableConv2Dcamada) separável em profundidade . Isso
vale para entradas 3D, 2D e 1D. Quando você está construindo uma nova rede a partir do zero, o
uso de convoluções separáveis em profundidade é definitivamente o caminho a
percorrer. A SeparableConv2Dcamada pode ser usada como um substituto
substituto Conv2D, resultando em uma rede menor e mais rápida que também executa melhor
em sua tarefa.
Aqui está uma típica rede de classificação de imagens (classificação categórica, neste caso):
model = models.Sequential ()
RNNs
Três camadas RNN estão disponíveis em Keras: SimpleRNN, GRU, e LSTM. Para fins mais
práticos, você deve usar GRUou LSTM. LSTMé o mais poderoso dos dois, mas também é mais
caro; você pode pensar GRUem uma alternativa mais simples e barata.
Para empilhar várias camadas RNN uma sobre a outra, cada camada anterior à última camada
na pilha deve retornar a sequência completa de suas saídas (cada timestep de entrada
corresponderá a um timestep de saída); Se você não estiver empilhando nenhuma camada RNN,
é comum retornar apenas a última saída, que contém informações sobre toda a sequência.
A seguir está uma única camada RNN para classificação binária de seqüências de vetores:
model = models.Sequential ()
E esta é uma camada RNN empilhada para classificação binária de seqüências de vetores:
model = models.Sequential ()
Eu classifiquei meus aplicativos sugeridos por modalidades de entrada e saída. Observe que
muitos deles ampliam os limites do que é possível - embora um modelo possa ser treinado em
todas essas tarefas, em alguns casos esse modelo provavelmente não seria generalizado longe de
seus dados de treinamento. As seções 9.2 e 9.3 abordarão como essas limitações poderiam ser
levantadas no futuro.
Quase tudo é possível - mas não é nada . Vamos ver na próxima seção o que não podemos fazer
com o aprendizado profundo.
O espaço de aplicativos que podem ser implementados com aprendizado profundo é quase
infinito. E, no entanto, muitas aplicações estão completamente fora do alcance das atuais
técnicas de aprendizado profundo - mesmo com grandes quantidades de dados anotados por
humanos. Digamos, por exemplo, que você pudesse montar um conjunto de dados de centenas
de milhares - até milhões - de descrições em inglês dos recursos de um produto de software,
escrito por um gerente de produto, bem como o código-fonte correspondente desenvolvido por
uma equipe de engenheiros para atender a esses requisitos. Mesmo com esses dados,
você não podiatreinar um modelo de aprendizagem profunda para ler uma descrição do produto
e gerar a base de código apropriada. Esse é apenas um exemplo entre muitos. Em geral,
qualquer coisa que exija raciocínio - como programar ou aplicar o método científico -
planejamento a longo prazo e manipulação algorítmica de dados está fora do alcance de modelos
de aprendizagem profunda, não importa quantos dados você jogue neles. Até mesmo aprender
um algoritmo de classificação com uma rede neural profunda é tremendamente difícil.
Ampliar as atuais técnicas de aprendizagem profunda, empilhando mais camadas e usando mais
dados de treinamento, pode apenas superficialmente eliminar alguns desses problemas. Não
resolverá os problemas mais fundamentais que os modelos de aprendizagem profunda são
limitados no que eles podem representar e que a maioria dos programas que você pode desejar
aprender não pode ser expressa como um morphing geométrico contínuo de uma variedade de
dados.
Em particular, isso é destacado por exemplos contraditórios , que são amostras fornecidas a
uma rede de aprendizagem profunda que são projetadas para enganar o modelo e classificá-los
erroneamente. Você já sabe que, por exemplo, é possível fazer subida de gradiente no espaço de
entrada para gerar entradas que maximizam a ativação de algum filtro de convnet - essa é a base
da técnica de visualização de filtro apresentada no capítulo 5 , bem como a Algoritmo
DeepDream no capítulo 8 . Da mesma forma, através da subida do gradiente, você pode
modificar ligeiramente uma imagem para maximizar a previsão de classe para uma determinada
classe. Tirando uma foto de um panda e acrescentando um gradiente de gibão, podemos obter
uma rede neural para classificar o panda como um gibão (verfigura 9.2 ). Isso evidencia a
fragilidade desses modelos e a profunda diferença entre o mapeamento de entrada-saída e a
percepção humana.
Figura 9.2. Um exemplo contraditório: mudanças imperceptíveis em uma imagem podem derrubar a classificação de um
modelo da imagem.
Figura 9.3. Modelos atuais de aprendizado de máquina: como uma imagem escura em um espelho
Como um praticante de aprendizado de máquina, esteja sempre atento a isso, e nunca caia na
armadilha de acreditar que as redes neurais entendem a tarefa que realizam - elas não o fazem,
pelo menos não de uma maneira que faça sentido para nós. Eles foram treinados em uma tarefa
diferente, muito mais estreita do que aquela que queríamos ensiná-los: a de mapear as entradas
de treinamento para os alvos de treinamento, ponto a ponto. Mostre-lhes qualquer coisa que se
desvie de seus dados de treinamento, e eles vão quebrar de maneiras absurdas.
Os seres humanos são capazes de muito mais do que mapear estímulos imediatos a respostas
imediatas, como faria uma rede profunda, ou talvez um inseto. Mantemos modelos complexos
e abstratos de nossa situação atual, de nós mesmos e de outras pessoas, e podemos usar esses
modelos para antecipar diferentes futuros possíveis e realizar um planejamento de longo
prazo. Podemos unir conceitos conhecidos para representar algo que nunca
experimentamosantes - como imaginar um cavalo vestindo jeans, por exemplo, ou imaginando o
que faríamos se ganhássemos na loteria. Essa capacidade de lidar com hipóteses, de expandir
nosso espaço de modelos mentais muito além do que podemos experimentar diretamente - para
executar abstração e raciocínio - é sem dúvida a característica definidora da cognição
humana. Chamo isso de generalização extrema : uma capacidade de se adaptar a situações
novas, nunca antes experimentadas, usando poucos dados ou mesmo nenhum dado novo.
Isso contrasta fortemente com o que as redes profundas fazem, que eu chamo de generalização
local(veja a figura 9.4 ). O mapeamento de entradas para saídas realizadas por uma rede
profunda rapidamente deixa de fazer sentido se novos insumos diferirem, mesmo que
ligeiramente, do que a rede viu no tempo de treinamento. Considere, por exemplo, o problema
de aprender os parâmetros de lançamento apropriados para fazer um foguete pousar na lua. Se
você usasse uma rede profunda para essa tarefa e treinasse-a usando aprendizado
supervisionado ou aprendizado de reforço, teria de fornecer a ela milhares ou até milhões de
testes de lançamento: você precisaria expô-la a uma densa amostragemdo espaço de entrada,
para que ele aprenda um mapeamento confiável do espaço de entrada para o espaço de
saída. Em contraste, como seres humanos, podemos usar nosso poder de abstração para chegar
a física modelos-rocket science-e derivar uma exatasolução que vai pousar o foguete na lua em
uma ou algumas tentativas. Da mesma forma, se você desenvolvesse uma rede profunda
controlando um corpo humano, e quisesse aprender a navegar com segurança em uma cidade
sem ser atropelado por carros, a rede teria que morrer milhares de vezes em várias situações até
poder inferir que os carros são perigosas e desenvolver comportamentos de evitação
adequados. Caiu em uma nova cidade, a rede teria que reaprender a maior parte do que
sabe. Por outro lado, os humanos são capazes de aprender comportamentos seguros sem ter que
morrer nem uma vez - mais uma vez, graças ao nosso poder de modelagem abstrata de situações
hipotéticas.
Figura 9.4. Generalização local vs. generalização extrema
Em suma, apesar do nosso progresso na percepção das máquinas, ainda estamos longe da IA do
nível humano. Nossos modelos só podem realizar generalizações locais, adaptando-se a novas
situações que devem ser similares a dados passados, enquanto a cognição humana é capaz de
generalização extrema, adaptando-se rapidamente a situações radicalmente novas e planejando
situações futuras de longo prazo.
9.2.3. Empacotando
Aqui está o que você deve lembrar: o único sucesso real da aprendizagem profunda até agora
tem sido a capacidade de mapear o espaço X para o espaço Y usando uma transformação
geométrica contínua, dados grandes quantidades de dados anotados por humanos. Fazer isso
bem é um divisor de águas para praticamente todos os setores, mas ainda está muito longe da
inteligência artificial em nível humano.
Para levantar algumas das limitações que discutimos e criamos IA que podem competir com os
cérebros humanos, precisamos nos afastar dos mapeamentos diretos de entrada para saída
e raciocinar e abstrair . Um provável substrato apropriado para modelagem abstrata de várias
situações e conceitos é o dos programas de computador. Dissemos anteriormente que os
modelos de aprendizado de máquina podem ser definidos como programas
aprendíveis ; Atualmente, só podemos aprender programas que pertencem a um subconjunto
restrito e específico de todos os programas possíveis. Mas e se pudéssemos
aprender algum programa, de forma modular e reutilizável? Vamos ver na próxima seção como
a estrada à frente pode parecer.
Esta é uma seção mais especulativa destinada a abrir horizontes para pessoas que querem
participar de um programa de pesquisa ou começar a fazer pesquisas independentes. Dado o
que sabemos de como redes profundas funcionam, suas limitações e o estado atual do cenário da
pesquisa, podemos prever onde as coisas estão caminhando no médio prazo? A seguir estão
alguns pensamentos puramente pessoais. Note que eu não tenho uma bola de cristal, então
muito do que eu antecipo pode não se tornar realidade. Eu estou compartilhando essas
previsões não porque eu espero que elas sejam completamente provadas no futuro, mas porque
elas são interessantes e acionáveis no presente.
Além disso, observe que essas considerações não são específicas para o tipo de aprendizado
supervisionado que tem sido o pão com manteiga de aprendizado profundo até o momento - ao
contrário, elas são aplicáveis a qualquer forma de aprendizado de máquina, inclusive sem
supervisão, auto-supervisão e aprendizagem de reforço. Não é de fundamental importância a
origem das suas etiquetas ou o aspecto do seu ciclo de treino; Esses diferentes ramos da
aprendizagem de máquina são diferentes facetas da mesma construção. Vamos mergulhar.
Qual caminho poderia fazer isso acontecer? Considere um tipo bem conhecido de rede: RNNs. É
importante observar que os RNNs têm limitações um pouco menores do que as redes
feedforward. Isso porque os RNNs são um pouco mais que meras transformações
geométricas:são transformações geométricas repetidamente aplicadas dentro de
um for loop . O forloop temporal é codificado por desenvolvedores humanos: é uma suposição
interna da rede. Naturalmente, os RNNs ainda são extremamente limitados no que eles podem
representar, principalmente porque cada passo que eles executam é uma transformação
geométrica diferenciável, e eles carregam informações de passo a passo através de pontos em
um espaço geométrico contínuo (vetores de estado). Agora imagine uma rede neural que é
aumentada de forma semelhante às primitivas de programação - mas em vez de um
único forloop codificado com memória geométrica codificada, a rede inclui um grande
conjunto de primitivas de programação que o modelo é livre para manipular para expandir sua
função de processamento. Comoiframificações, whileinstruções, criação de variáveis,
armazenamento em disco para memória de longo prazo, operadores de classificação, estruturas
de dados avançadas (como listas, gráficos e tabelas de hash) e muito mais. O espaço de
programas que tal rede poderia representar seria muito mais amplo do que o que pode ser
representado pelos atuais modelos de aprendizagem profunda, e alguns desses programas
poderiam alcançar um poder de generalização superior.
Vamos nos afastar de ter, por um lado, inteligência algorítmica codificada (software artesanal) e,
por outro lado, aprendemos a inteligência geométrica (aprendizado profundo). Em vez disso,
teremos uma combinação de módulos algorítmicos formais que fornecem recursos de raciocínio
e abstração e módulos geométricos que fornecem recursos informais de intuição e
reconhecimento de padrões. Todo o sistema será aprendido com pouco ou nenhum
envolvimento humano.
Um subcampo relacionado da IA que eu acho que pode estar prestes a decolar em grande escala
é a síntese do programa., em particular síntese do programa neural. A síntese de programas
consiste em gerar automaticamente programas simples usando um algoritmo de busca
(possivelmente busca genética, como na programação genética) para explorar um grande espaço
de programas possíveis. A pesquisa é interrompida quando é encontrado um programa que
corresponda às especificações necessárias, geralmente fornecido como um conjunto de pares de
entrada-saída. Isso lembra muito o aprendizado de máquina: dados de treinamento fornecidos
como pares de entrada-saída, encontramos um programa que combina entradas em saídas e
pode generalizar para novos insumos. A diferença é que, em vez de valores de parâmetros de
aprendizagem em um programa codificado (uma rede neural), geramos código-fonte por meio
de um processo de pesquisa discreto.
Eu definitivamente espero que este subcampo tenha uma onda de interesse renovado nos
próximos anos. Em particular, espero o surgimento de um subcampo cruzado entre
aprendizagem profunda e síntese de programas, onde, em vez de gerar programas em uma
linguagem de propósito geral, geraremos redes neurais (fluxos de processamento de dados
geométricos) aumentadas com um rico conjunto de algoritmos. primitivos, como forloops e
muitos outros (veja a figura 9.5). Isso deve ser muito mais tratável e útil do que gerar
diretamente o código-fonte, e expandirá dramaticamente o escopo de problemas que podem ser
resolvidos com o aprendizado de máquina - o espaço de programas que podemos gerar
automaticamente, dados apropriados de treinamento. RNNs contemporâneos podem ser vistos
como um ancestral pré-histórico de tais modelos híbridos algorítmicos-geométricos.
Figura 9.5. Um programa aprendido baseado em primitivas geométricas (reconhecimento de padrões, intuição) e primitivas
algorítmicas (raciocínio, busca, memória)
Além disso, a retropropagação é de ponta a ponta, o que é ótimo para o aprendizado de boas
transformações encadeadas, mas é computacionalmente ineficiente porque não aproveita
totalmente a modularidade das redes profundas. Para tornar algo mais eficiente, há uma receita
universal: introduzir modularidade e hierarquia. Assim, podemos tornar a retropropagação
mais eficiente, introduzindo módulos de treinamento desacoplados com um mecanismo de
sincronização entre eles, organizados de forma hierárquica. Essa estratégia é de certa forma
refletida no trabalho recente da DeepMind sobre gradientes sintéticos. Espero mais nesse
sentido no futuro próximo.
No nível mais básico, tal sistema ajustaria o número de camadas em uma pilha, sua ordem e o
número de unidades ou filtros em cada camada. Isso é comumente feito com bibliotecas como o
Hyperopt, que discutimos no capítulo 7 . Mas também podemos ser muito mais ambiciosos e
tentar aprender uma arquitetura apropriada do zero, com o mínimo de restrições possíveis: por
exemplo, através de aprendizado por reforço ou algoritmos genéticos.
Quando isso começar a acontecer, os trabalhos dos engenheiros de aprendizado de máquina não
desaparecerão - em vez disso, os engenheiros irão subir na cadeia de criação de valor. Eles
começarão a se esforçar muito mais para criar funções de perda complexas que realmente
reflitam os objetivos de negócios e entender como seus modelos afetam os ecossistemas digitais
nos quais estão implantados (por exemplo, usuários que consomem as previsões do modelo e
geram os dados de treinamento do modelo) ) - problemas que apenas as maiores empresas
podem considerar atualmente.
Uma observação notável foi feita repetidamente nos últimos anos: treinar o mesmo modelo para
fazer várias tarefas fracamente conectadas ao mesmo tempo resulta em um modelo que
é melhor em cada tarefa . Por exemplo, treinar o mesmo modelo de tradução de máquina
neural para realizar tradução de inglês para alemão e tradução de francês para italiano resultará
em um modelo melhor em cada par de idiomas. Da mesma forma, treinar um modelo de
classificação de imagem em conjunto com um modelo de segmentação de imagem,
compartilhando a mesma base convolucional, resulta em um modelo que é melhor em ambas as
tarefas. Isso é bastante intuitivo: sempre há a informação se sobrepõe entre tarefas
aparentemente desconectadas, e um modelo conjunto tem acesso a uma quantidade maior de
informações sobre cada tarefa individual do que um modelo treinado apenas nessa tarefa
específica.
Atualmente, quando se trata de reutilização de modelos entre tarefas, usamos pesos pré-
treinados para modelos que executam funções comuns, como a extração de recursos
visuais. Você viu isso em ação no capítulo 5 . No futuro, espero que uma versão generalizada
disso seja comum: usaremos não apenas recursos aprendidos anteriormente (pesos de
submodelos), mas também arquiteturas de modelo e procedimentos de treinamento. À medida
que os modelos se tornam mais parecidos com programas, começaremos a reutilizar sub-rotinas
de programas como as funções e classes encontradas em linguagens de programação humanas.
Pense no processo de desenvolvimento de software hoje: uma vez que um engenheiro resolve
um problema específico (consultas HTTP em Python, por exemplo), ele o empacota como uma
biblioteca abstrata e reutilizável. Os engenheiros que enfrentarem um problema semelhante no
futuro poderão procurar por bibliotecas existentes, baixar uma delas e usá-la em seu próprio
projeto. De maneira semelhante, no futuro, os sistemas de metal-learning serão capazes de
montar novos programas examinando uma biblioteca global de blocos reutilizáveis de alto
nível. Quando o sistema se encontra desenvolvendo sub-rotinas de programa similares para
várias tarefas diferentes, ele pode criar uma versão abstrata e reutilizável da sub-rotina e
armazená-la na biblioteca global (veja a figura 9.6 ). Tal processo implementará a abstração: um
componente necessário para alcançar uma generalização extrema. Uma sub-rotina que é útil em
diferentes tarefas e domínios pode ser usada para abstrairalguns aspectos da resolução de
problemas. Essa definição de abstração é semelhante à noção de abstração em engenharia de
software. Essas sub-rotinas podem ser geométricas (módulos de aprendizagem profunda com
representações pré-tratadas) ou algorítmicas (mais próximas das bibliotecas que os engenheiros
de software contemporâneos manipulam).
Figura 9.6. Um meta-aprendiz capaz de desenvolver rapidamente modelos específicos de tarefas usando primitivas
reutilizáveis (tanto algorítmicas quanto geométricas), obtendo assim generalização extrema
Os modelos serão mais parecidos com programas e terão capacidades que vão muito
além das contínuas transformações geométricas dos dados de entrada com os quais
trabalhamos atualmente. Esses programas estarão, sem dúvida, muito mais próximos
dos modelos mentais abstratos que os humanos mantêm em torno de si mesmos e de si
mesmos, e serão capazes de uma generalização mais forte, devido à sua rica natureza
algorítmica.
Em particular, os modelos combinarão módulos algorítmicos fornecendo recursos de
raciocínio, pesquisa e abstração formais com módulos geométricos que fornecem
recursos informais de intuição e reconhecimento de padrões. O AlphaGo (um sistema
que exigia muitas decisões manuais de engenharia de software e de design feito pelo
homem) fornece um exemplo inicial de como essa mistura de IA simbólica e geométrica
poderia se parecer.
Esses modelos serão cultivados automaticamente, em vez de codificados por
engenheiros humanos, usando peças modulares armazenadas em uma biblioteca global
de sub-rotinas reutilizáveis - uma biblioteca que foi desenvolvida aprendendo modelos
de alto desempenho em milhares de tarefas e conjuntos de dados anteriores. Como
padrões freqüentes de solução de problemas são identificados pelo sistema de meta-
aprendizagem, eles serão transformados em sub-rotinas reutilizáveis - muito parecido
com funções e classes na engenharia de software - e adicionados à biblioteca global. Isso
vai conseguir abstração .
Esta biblioteca global e sistema de crescimento de modelo associado será capaz de
alcançar alguma forma de generalização extrema semelhante à humana: dada uma nova
tarefa ou situação, o sistema será capaz de montar um novo modelo de trabalho
apropriado para a tarefa usando muito poucos dados, graças a primitivas ricas em
programas que generalizam bem e extensa experiência com tarefas semelhantes. Da
mesma forma, os humanos podem aprender rapidamente a jogar um novo videogame
complexo se tiverem experiência com muitos jogos anteriores, porque os modelos
derivados dessa experiência anterior são abstratos e semelhantes a programas, em vez
de um mapeamento básico entre estímulos e ação.
Como tal, este sistema de crescimento de modelo de aprendizagem perpétua pode ser
interpretado como uma inteligência geral artificial (AGI). Mas não espere qualquer
apocalipse de robô singularitário: é pura fantasia, vindo de uma longa série de mal-
entendidos profundos de inteligência e tecnologia. Tal crítica, no entanto, não pertence
a este livro.
Como palavras finais de despedida, quero lhe dar algumas dicas sobre como continuar
aprendendo e atualizando seus conhecimentos e habilidades depois de virar a última página
deste livro. O campo da aprendizagem profunda moderna, como a conhecemos hoje, tem apenas
alguns anos, apesar de uma longa e lenta pré-história que remonta a décadas. Com um aumento
exponencial dos recursos financeiros e do número de pesquisadores desde 2013, o campo como
um todo está agora se movendo em um ritmo frenético. O que você aprendeu neste livro não
será relevante para sempre, e não é tudo que você precisará para o resto de sua carreira.
Felizmente, há muitos recursos on-line gratuitos que você pode usar para se manter atualizado e
expandir seus horizontes. Aqui estão alguns.
Uma desvantagem importante é que a grande quantidade de novos trabalhos publicados todos
os dias no arXiv torna impossível até mesmo ignorá-los; e o fato de não serem revisados por
pares dificulta a identificação daqueles que são importantes e de alta qualidade.É difícil, e cada
vez mais, encontrar o sinal no ruído. Atualmente, não há uma boa solução para esse
problema. Mas algumas ferramentas podem ajudar: um site auxiliar chamado arXiv Sanity
Preserver ( http://arxiv-sanity.com ) serve como um mecanismo de recomendação para novos
documentos e pode ajudá-lo a acompanhar os novos desenvolvimentos dentro de um estreito
vertical específico de aprendizado profundo. Além disso, você pode usar o Google Acadêmico
( https://scholar.google.com ) para acompanhar as publicações de seus autores favoritos.
Este é o final do Deep Learning com Python ! Espero que você tenha aprendido uma coisa ou
duas sobre aprendizado de máquina, aprendizado profundo, Keras e talvez até cognição em
geral. Aprender é uma jornada para toda a vida, especialmente no campo da IA, onde temos
muito mais incógnitas em nossas mãos do que certezas. Então, por favor, continue aprendendo,
questionando e pesquisando. Nunca pare. Porque, mesmo considerando o progresso feito até
agora, a maioria das questões fundamentais da IA permanece sem resposta. Muitos ainda não
foram propriamente perguntados.
Pode parecer um processo assustador. Na verdade, a única parte difícil é configurar o suporte à
GPU - caso contrário, todo o processo pode ser feito com alguns comandos e leva apenas alguns
minutos.
Vamos supor que você tenha uma nova instalação do Ubuntu, com uma GPU NVIDIA
disponível. Antes de começar, verifique se você pipinstalou e se o seu gerenciador de pacotes
está atualizado:
Por padrão, o Ubuntu usa o Python 2 quando instala pacotes do Python como python-pip. Se
você deseja usar o Python 3, use o python3prefixo em vez de python. Por exemplo:
Quando você estiver instalando pacotes usando pip, tenha em mente que, por padrão, ele tem
como alvo o Python 2. Para direcionar o Python 3, você deve usar pip3:
Se você usa um Mac, recomendamos que você instale o pacote científico Python via Anaconda,
que você pode obter em www.continuum.io/downloads . Observe que isso não incluirá o HDF5 e o
Graphviz, que você precisa instalar manualmente. A seguir estão as etapas para
uma instalação manual do conjunto científico Python no Ubuntu:
1. Instale uma biblioteca BLAS (OpenBLAS, neste caso), para garantir que você possa
executar operações de tensor rápidas em sua CPU:
3. Instale o pacote científico Python: Numpy, SciPy e Matplotlib. Isso é necessário para
realizar qualquer tipo de aprendizado de máquina ou computação científica no Python,
independentemente de você estar aprendendo profundamente:
python-yaml
8. Instale pacotes adicionais que são usados em alguns dos nossos exemplos de código:
Usar uma GPU não é estritamente necessário, mas é altamente recomendável. Todos os
exemplos de código encontrados neste livro podem ser executados em uma CPU de laptop, mas
às vezes você pode ter que esperar por várias horas para um modelo treinar, em vez de meros
minutos em uma boa GPU. Se você não tiver uma GPU NVIDIA moderna, você pode pular esta
etapa e ir diretamente para a seção A.3 .
Para usar sua GPU NVIDIA para aprendizagem profunda, você precisa instalar duas coisas:
CUDA— Um conjunto de drivers para sua GPU que permite executar uma linguagem
de programação de baixo nível para computação paralela.
cuDNN— Uma biblioteca de primitivos altamente otimizados para aprendizado
profundo. Ao usar cuDNN e rodar em uma GPU, você pode aumentar a velocidade de
treinamento de seus modelos em 50% a 100%.
1. Baixe CUDA. Para o Ubuntu (e outros tipos de Linux), a NVIDIA fornece um pacote
pronto para uso que você pode baixar em https://developer.nvidia.com/cuda-downloads :
2. $ wget http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/
x86_64 / cuda-repo-ubuntu1604_9.0.176-1_amd64.deb
3. Instale CUDA. A maneira mais fácil de fazer isso é usar o Ubuntu aptneste pacote. Isso
permitirá que você instale atualizações facilmente aptassim que estiverem disponíveis:
6. http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/
7. x86_64 / 7fa2af80.pub
9. Instale o cuDNN:
1. Registre-se para obter uma conta de desenvolvedor NVIDIA gratuita
(infelizmente, isso é necessário para obter acesso ao download da cuDNN) e
baixe cuDNN em https://developer.NVIDIA.com/cudnn (selecione a versão do
cuDNN compatível com o TensorFlow). Como o CUDA, a NVIDIA fornece
pacotes para diferentes versões do Linux - usaremos a versão para o Ubuntu
16.04. Observe que, se você estiver trabalhando com uma instalação do EC2,
não poderá fazer o download do arquivo cuDNN diretamente para sua
instância; em vez disso, faça o download para sua máquina local e, em seguida,
faça o upload para a instância do EC2 (via scp).
2. Instale o cuDNN:
Como você já instalou o TensorFlow, não é necessário instalar o Theano para executar o código
do Keras. Mas às vezes pode ser útil alternar entre Tensor-Flow e Theano ao criar modelos
Keras.
Se você estiver usando uma GPU, você deve configurar o Theano para usar sua GPU. Você pode
criar um arquivo de configuração Theano com este comando:
nano ~ / .theanorc
[global]
floatX = float32
device = gpu0
[nvcc]
fastmath = Verdadeiro
Alternativamente, você pode instalar o Keras do GitHub. Ao fazer isso, você poderá acessar a
pasta keras / examples, que contém vários scripts de exemplo para você aprender:
$ cd keras
Agora você pode tentar executar um script Keras, como este exemplo MNIST:
Note que a execução deste exemplo para conclusão pode demorar alguns minutos, então sinta-
se à vontade para forçá-lo a sair (Ctrl-C) assim que verificar que está funcionando normalmente.
Depois de executar o Keras pelo menos uma vez, o arquivo de configuração do Keras pode ser
encontrado em ~ / .keras / keras.json. Você pode editá-lo para selecionar o backend que Keras é
executado em: tensorflow, theanoou cntk. Seu arquivo de configuração deve gostar disso:
"image_data_format": "channels_last",
"epsilon": 1e-07,
"floatx": "float32",
"backend": "tensorflow"
Enquanto o script Keras / mnist_cnn.py está sendo executado, você pode monitorar a utilização
da GPU em uma janela de shell diferente:
Você está pronto! Parabéns - agora você pode começar a criar aplicativos de aprendizado
profundo.
Apêndice B. Executando notebooks Jupyter em uma
instância de GPU do EC2
Este apêndice fornece um guia passo a passo para executar os notebooks Jupyter de
aprendizagem profunda em uma instância da GPU da AWS e editar os blocos de anotações de
qualquer lugar em seu navegador. Esta é a configuração perfeita para pesquisas de aprendizado
profundo se você não tiver uma GPU em sua máquina local. A versão original (e atualizada)
deste guia pode ser encontrada em https://blog.keras.io .
Um notebook Jupyter é um aplicativo da web que permite escrever e anotar o código Python de
forma interativa. É uma ótima maneira de experimentar, pesquisar e compartilhar o que você
está trabalhando.
Muitas aplicações de aprendizado profundo são muito intensivas em computação e podem levar
horas ou até dias ao serem executadas nos núcleos de CPU de um laptop. A execução em uma
GPU pode acelerar o treinamento e a inferência por um fator considerável (geralmente de 5 a 10
vezes, ao passar de uma CPU moderna para uma única GPU moderna). Mas você pode não ter
acesso a uma GPU em sua máquina local. A execução de notebooks Jupyter na AWS oferece a
mesma experiência que a execução em sua máquina local, permitindo que você use uma ou
várias GPUs na AWS. E você paga apenas pelo que usa, que pode se comparar favoravelmente ao
investimento em sua própria GPU (s) se você usar o aprendizado profundo apenas
ocasionalmente.
B.2. POR QUE VOCÊ NÃO GOSTARIA DE USAR O JUPYTER NA AWS PARA
APRENDIZADO PROFUNDO?
As instâncias de GPU da AWS podem se tornar caras rapidamente. O que sugerimos usar custa
US $ 0,90 por hora. Isso é bom para uso ocasional; mas se você for realizar experimentos por
várias horas por dia todos os dias, é melhor construir sua própria máquina de aprendizado
profundo com um TITAN X ou GTX 1080 Ti.
Em resumo, use a configuração Jupyter-on-EC2 se você não tiver acesso a uma GPU local ou se
não quiser lidar com a instalação das dependências do Keras, em particular dos drivers da
GPU. Se você tiver acesso a uma GPU local, recomendamos executar seus modelos
localmente. Nesse caso, use o guia de instalação no apêndice A .
Nota
Você precisará de uma conta ativa da AWS. Alguma familiaridade com o AWS EC2 ajudará, mas
não é obrigatória.
2. Selecione o AWS Marketplace (consulte a figura B.2 ) e pesquise por “deep learning” na
caixa de pesquisa. Role para baixo até encontrar o AMI chamado Deep Learning AMI
Ubuntu Version (veja a figura B.3 ); selecione-o.
Figura B.2. O mercado de AMI do EC2
3. Selecione a instância p2.xlarge (veja a figura B.4 ). Esse tipo de instância fornece acesso
a uma única GPU e custa US $ 0,90 por hora de uso (em março de 2017).
Figura B.4. A instância p2.xlarge
4. Você pode manter a configuração padrão das etapas Configurar Instância, Adicionar
Armazenamento e Adicionar Tags, mas você personalizará a etapa Configurar Grupo de
Segurança. Crie uma regra TCP customizada para permitir a porta 8888 (veja a figura
B.5): esta regra pode ser permitida para o seu IP público atual (como o do seu laptop) ou
para qualquer IP (como 0.0.0.0/0) se o primeiro não for possível. Observe que, se você
permitir a porta 8888 para qualquer IP, então, literalmente, qualquer pessoa poderá
ouvir essa porta em sua instância (que é onde você executará os notebooks
IPython). Você adicionará proteção por senha aos blocos de anotações para reduzir o
risco de estranhos aleatórios modificá-los, mas isso pode ser uma proteção muito
fraca. Se possível, você deve considerar restringir o acesso a um IP específico. Mas se o
seu endereço IP mudar constantemente, então essa não é uma opção prática. Se você for
deixar o acesso aberto a qualquer IP, lembre-se de não deixar dados confidenciais na
instância.
Figura B.5. Configure um novo grupo de segurança.
Nota
No final do processo de lançamento, você será perguntado se deseja criar novas chaves
de conexão ou se deseja reutilizar as chaves existentes. Se você nunca usou o EC2 antes,
crie novas chaves e faça o download delas.
6. Uma vez que você esteja logado na instância via SSH, crie um diretório ssl na raiz da
instância, e cdpara ele (não obrigatório, mas mais limpo):
7. $ mkdir ssl
$ cd ssl
8. Crie um novo certificado SSL usando o OpenSSL e crie os arquivos cert.key e cert.pem
no diretório ssl atual:
9. $ openssl req -x509 -nodes -days 365 -newkey rsa: 1024 -keyout "cert.key" -out
"cert.pem" -batch
2. Opcionalmente, você pode gerar uma senha Jupyter para seus blocos de
anotações. Como sua instância pode ser configurada para ser acessível de qualquer IP
(dependendo da escolha feita ao configurar o grupo de segurança), é melhor restringir o
acesso ao Jupyter por meio de uma senha. Para gerar uma senha, abra um shell IPython
( ipythoncomando) e execute o seguinte:
4. passwd ()
Saída
5. O passwd()comando solicitará que você digite e verifique uma senha. Depois disso, ele
exibirá um hash da sua senha. Copie esse hash - você precisará dele em breve. Parece
algo como isto:
Note que este é um hash da palavra password , que não é uma senha que você deve
usar.
6. Use o vi (ou seu editor de texto disponível) para editar o arquivo de configuração do
Jupyter:
$ vi ~ / .jupyter / jupyter_notebook_config.py
8. c = get_config () 1
13.
15. c.NotebookApp.password = 7
Nota
Caso você não esteja acostumado a usar o vi, lembre-se de que precisa pressionar I para começar
a inserir o conteúdo. Quando terminar, pressione Esc, insira :wqe pressione Enter para sair do
vi e salvar suas alterações ( :wqsignifica write-quit ).
Você está quase pronto para começar a usar o Jupyter. Mas primeiro, você precisa atualizar o
Keras. Uma versão do Keras está pré-instalada na AMI, mas pode não estar necessariamente
atualizada. Na instância remota, execute este comando:
Se houver um arquivo de configuração Keras existente na instância (não deve haver, mas a AMI
pode ter mudado desde que eu escrevi isso), você deve excluí-lo, apenas no caso. Keras recriará
um arquivo de configuração padrão quando for lançado pela primeira vez.
Se o seguinte trecho de código retornar um erro dizendo que o arquivo não existe, você pode
ignorá-lo:
$ rm -f ~ / .keras / keras.json
Na instância remota, clone o repositório do GitHub que contém os cadernos de notas Jupyter
associados a este livro:
cd deep-learning-com-python-notebooks
$ jupyter notebook
Em seguida, no navegador local, navegue até o endereço local que você está encaminhando para
o processo de notebook remoto (https://127.0.0.1). Certifique-se de usar HTTPS no endereço ou
você receberá um erro de SSL.
Você deve ver o aviso de segurança mostrado na figura B.7 . Esse aviso é devido ao fato de que o
certificado SSL gerado por você não é verificado por uma autoridade confiável (obviamente,
você gerou o seu próprio). Clique em Avançado e prossiga para navegar.
Figura B.7. Um aviso de segurança que você pode ignorar
Você deve ser solicitado a digitar sua senha Jupyter. Você então chegará ao painel Jupyter (veja
a figura B.8 ).
Escolha Novo> Bloco de notas para começar (veja a figura B.9 ). Você pode usar a versão do
Python de sua escolha. Tudo pronto!
SÍMBOLO
* operator
+ operator
0D tensors.
See scalars.
1D convolutions
1D pooling, for sequence data
1D tensors.
See vectors.
2D tensors.
See matrices.
3D embeddings
UMA
activation
activation functions
ad targeting
add_loss method
ADMM (alternating direction method of multipliers)
adversarial networks.
See also generative deep learning; ; generative adversarial networks.
adversary network
affine transformations
Amazon Web Services.
See AWS.
AMD
Analytical Engine
annotations, 2 nd
expectations for
history of
arXiv preprint server, 2 nd
assembling datasets
augmented intelligence
augmenting data
feature extraction with
feature extraction without
autoencoders.
See VAEs (variational autoencoders).
AutoML systems
autonomous driving
AWS (Amazon Web Services)
GPU instances
running Jupyter on
setting up
using Jupyter on
B
Babbage, Charles
backend engine, Keras
backpropagation algorithm, 2 , 3 nd rd
backward pass
bag-of-2-grams
bag-of-3-grams
bag-of-words
Baidu
batch axis
batch normalization
BatchNormalization layer
batch_size
Bengio, Yoshua, 2 , 3 nd rd
bidirectional layers
binary classifications, 2 nd
binary crossentropy, 2 nd
black boxes
BLAS (Basic Linear Algebra Subprograms), 2 nd
border effects
broadcasting operations
browsers, local, using Jupyter from
C
callbacks, writing
CAM (class activation map)
categorical encoding
categorical_crossentropy function, 2 , 3 nd rd
CERN
channels axis
channels-first convention
channels-last convention
character-level neural language model
Ciresan, Dan, 2nd
classification
cloud, running jobs in
clustering
CNNs.
See convnets (convolutional neural networks).
CNTK (Microsoft Cognitive Toolkit)
compilation step
concept vectors, for editing images
conditioning data
Connect button, EC2 control panel
connections, residual
content loss
Conv1D layer
Conv2D layer, 2 , 3 nd rd
cuDNN library, 2 , 3 nd rd
curvature
data
augmenting
feature extraction with
feature extraction without
batches of
generating sequence data
heterogeneous
homogenous
learning representations from
missing
preparing
for character-level LSTM text generation
for recurrent neural networks
preprocessing, 2nd
redundancy
representations for neural networks
3D tensors
data batches
examples of data tensors
higher-dimensional tensors
image data
key attributes of tensors
manipulating tensors in Numpy
matrices (2D tensors)
scalars (0D tensors)
sequence data
timeseries data
vector data
vectors (1D tensors)
video data
shuffling, 2nd
splitting
tokenizing
transformations
transforming
vectorization
data augmentation
data distillation
data points, 2nd
data representativeness
data tensors, examples of
datasets, assembling
accomplishments of
achievements of
democratization of
enabling technologies
future of, 2nd
automated machine learning
lifelong learning
long-term vision
models as programs
modular subroutine reuse
geometric interpretation of
hardware and
investment in
limitations of
local generalization vs. extreme generalization
risk of anthropomorphizing machine-learning models
overview, 2nd
possible uses of
reasons for interest in.
See also generative deep learning.
Deep Learning AMI, EC2
DeepDream technique
overview
implementing in Keras
DeepMind, Google, 2 nd
Dense layers, 2 , 3 , 4 , 5 , 6 , 7 , 8
nd rd th th th th th
dense sampling
densely connected networks
depthwise separable convolution
derivatives, defined
developer account, NVIDIA
digital assistants
dimension
dimensionality, 2 nd
discriminator networks
overview
implementing
distance function
dot operations
dot product
downloading
GloVe word embeddings
raw text
Dropout layer
dtype attribute
E
EarlyStopping callbacks
Eck, Douglas
editing images, concept vectors for
Eigen
element-wise operations
embedding layers, learning word embeddings with
engineering features
ensembling models
epochs, 2 , 3 , 4
nd rd th
epsilon
evaluating models
evaluation protocols, choosing, 2 nd
expert systems
extreme generalization, local generalization vs.
extreme inception
F
feature engineering, 2 nd
feature learning
feature maps, 2 nd
features
engineering
extracting
with data augmentation
without data augmentation
features axis
feedforward networks, 2 nd
Feynman, Richard
fill_mode
filter visualizations
filters
overview
convnets, visualizing
fine-tuning
fit method
fit_generator method
Flatten layer
Flickr
float32, 2 , 3
nd rd
for loop, 2 , 3
nd rd
forward pass
freezing layers
fully connected layers
functional APIs, Keras
directed acyclic graphs of layers
layer weight sharing
models as layers
multi-input models
multi-output models
G
Gal, Yarin
GANs (generative adversarial networks), 2 nd
geometric interpretation
of deep learning
of tensor operations
geometric space
Graves, Alex
greedy sampling
ground-truth
GRU (gated recurrent unit) layers, 2 nd
H
handwriting transcription
hardware
hash collisions
HDF5
heatmaps
of class activation, visualizing
overview
height_shift range
heterogeneous data
hidden layers
hidden unit
hierarchical representation learning
Hinton, Geoffrey, 2 nd
Hochreiter, Sepp
hold-out validation
homogenous data
horizontal_flip
HSV (hue-saturation-value) format
Hyperas library
Hyperopt
hyperparameters
optimizing
overview
tuning
hyperplanes
hypothesis space, 2 , 3 nd rd
Eu
IDSIA
ILSVRC (ImageNet Large Scale Visual Recognition Challenge)
image classification
image data, 2 nd
image segmentation
image-classification task
ImageDataGenerator class, 2 , 3 nd rd
ImageNet class, 2 , 3 nd rd
images
editing concept vectors for
flipping
generating with variational autoencoders
concept vectors for image editing
overview
sampling from latent spaces of
inception blocks
Inception modules, 2 , 3 nd rd
include_top argument
information bottlenecks, 2 nd
input_shape argument
input_tensor
installing
CUDA
cuDNN
Keras, 2nd
OpenBLAS
OpenCV
Python scientific suite on Ubuntu
TensorFLow
Theano on Ubuntu
Intel
intermediate activations, visualizing
investments in deep learning
ipython command
J
joint feature learning
Jupyter notebooks
configuring
overview
running on AWS GPU instances
installing Keras
setting up AWS GPU instances
setting up local port forwarding
using from local browsers
using on AWS
K
K80, NVIDIA
Kaggle platform
overview, 2nd, 3rd
practice on real-world problems using
Keras API
directed acyclic graphs of layers
exploring
functional APIs
implementing DeepDream in
installing, 2nd
layer weight sharing
models as layers
multi-input models
multi-output models
neural style transfer in
recurrent layers in
using callbacks
Keras framework
CNTK
developing with
running
TensorFlow
Theano
Keras library
keras.applications module
keras.callbacks module, 2 nd
keras.preprocessing.image
kernel methods
kernel trick
K-fold validation, iterated with shuffling
Kingma, Diederik P.
Krizhevsky, Alex
eu
L1 regularization
L2 regularization
label, 2 nd
Lambda layer
language models
sampling from
training
last-layer activation
latent spaces
of images, sampling from
overview
layer compatibility
layered representations learning
layers
differentiable
directed acyclic graphs of
inception modules
residual connections
freezing
models as
overview, 2nd
recurrent
in Keras
stacking
unfreezing
weight sharing
layer-wise pretraining
LeakyReLU layer
LeCun, Yann, 2 nd
LeNet network
LHC (Large Hadron Collider)
lifelong learning
linear transformations
local generalization, extreme generalization vs.
local port forwarding, setting up
logistic regression algorithm
logreg (logistic regression)
logs argument
lookback parameter
lookback timesteps
loss function, 2 , 3 , 4 , 5
nd rd th th
loss plateau
loss value
Lovelace, Ada
LSTM (long short-term memory), 2 nd
machine learning
automated
basic approaches
branches of
reinforcement learning
self-supervised learning
supervised learning
unsupervised learning
data preprocessing
deep learning vs.
evaluating models of
choosing evaluation protocols
test sets
training sets
validation sets
feature engineering
feature learning
history of
decision trees
gradient boosting machines
kernel methods
neural networks, 2nd
probabilistic modeling
random forests
learning representations from data
models, risk of anthropo-morphizing
overfitting and underfitting
adding dropout
adding weight regularization
reducing network size
workflow of, 2nd
assembling datasets
choosing evaluation protocol
choosing measure of success
defining problems
developing models
preparing data
regularizing models
tuning hyperparameters.
See non-machine learning.
MAE (mean absolute error), 2 , 3 , 4 nd rd th
Matplotlib library, 2 , 3
nd rd
MaxPooling2D layer, 2 , 3 nd rd
mean_squared_error
memorization capacity
metrics
metrics, logging
Microsoft Cognitive Toolkit.
See CNTK.
Mikolov, Tomas
mini-batch
mini-batch SGD (mini-batch stochastic gradient descent)
Minsky, Marvin
MNIST dataset, 2 nd
model checkpointing
Model class
model depth
model plot
model.fit() function
model.fit_generator() function
ModelCheckpoint callbacks
models
architecture patterns
batch normalization
depthwise separable convolution
residual connections, 2nd
as layers
as programs
defining
developing
achieving statistical power
determining capacity
ensembling
evaluating
hyperparameter optimization
language
sampling from
training
loading GloVe embeddings in
machine learning, risk of anthropomorphizing
multi-input
multi-output
regularizing
training
using Keras callbacks
using TensorBoard
modular subroutines, reusing
modules, inception
momentum
Moore’s law, 2 nd
multiclass classifications
multihead networks
multi-input models
multilabel classification, 2 , 3
nd rd
multimodal inputs
multi-output models
N
N classes
Naive Bayes algorithm
naive_add
National Institute of Standards and Technology.
See NIST.
ndim attribute
Nervana Systems
neural layers
neural networks
anatomy of
layers
loss functions
models
optimizers
binary classifications
breakthroughs in
data preprocessing for
handling missing values
value normalization
vectorization
data representations for
3D tensors
data batches
examples of data tensors
higher-dimensional tensors
image data
key attributes of tensors
manipulating tensors in Numpy
matrices (2D tensors)
scalars (0D tensors)
sequence data
timeseries data
vector data
vectors (1D tensors)
video data
gradient-based optimization
backpropagation algorithm
derivatives
gradients
stochastic gradient descent
Keras
CNTK
developing with
TensorFlow
Theano
multiclass classifications
regression
setting up workstations
GPUs for deep learning
Jupyter notebooks
running jobs in cloud
running Keras
tensor operations
broadcasting
dot
element-wise
geometric interpretation of
geometric interpretation of deep learning
reshaping
neural style transfer
content loss
in Keras
style loss
N-grams
NIST (National Institute of Standards and Technology)
non-linearity function
non-machine learning, baselines
nonstationary problems
normalizing batches
normalizing values
Numpy arrays, 2 nd
O
object detection
objective function, 2 nd
one-hot encoding
of characters
of words
overview, 2nd, 3rd
online documentation, Keras
optimization, 2 , 3 , 4 , 5
nd rd th th
optimizer argument, 2 , 3 , 4 nd rd th
optimizers
output
classes
overview
tensor
overfitting
adding dropout
adding weight regularization
reducing network size
using recurrent dropout to fight
P
padding
parameterized layers
parameters
adjusting
overview
partitions
passwd() command
PCA (principal component analysis)
Pichai, Sundar
pip
plot_model
plotting code
pointwise convolutions
pooling 1D, for sequence data
predict method, 2 , 3 nd rd
prediction error
predictions
preparing data
preprocessing
data, 2nd
for neural networks
overview
embeddings
pretrained convnets
feature extraction
with data augmentation
without data augmentation
fine-tuning
with small datasets
pretrained networks, 2 nd
Python
installing scientific suite on Ubuntu
overview
python-pip package
Q
question-answering model
R
random forests
randomly shuffle data
randomness
rank
recurrent dropout, 2 nd
representations
extracting
overview
reshaping tensors
residual connections
response map
return_sequences argument
reusability
reverse-mode differentiation
RGB (red-green-blue) format
RMSProp optimizer, 2 , 3 , 4 , 5 , 6
nd rd th th th
sampling
from language models
from latent spaces of images
strategies
Sanity Preserver, arXiv
scalar regression, 2 nd
scalar tensor
scalars (0D tensors)
schematic implementation, of GAN
Schmidhuber, Jürgen
Scikit-Learn
SciPy, 2 nd
self-supervised learning
selu function
SeparableConv2D layer, 2 nd
separation hyperplane
sequence data
generating
overview
sequence generation
sequence prediction
sequences, processing with convnets
1D convolution for sequence data
1D pooling for sequence data
combining with recurrent neural networks
implementing 1D convnets
Sequential class, 2 nd
Sequential model, 2 nd
shallow learning
shared LSTM
shear_range
show_shapes option
shuffling, iterated K-fold validation with
Siamese LSTM model
sigmoid function, 2 , 3 nd rd
Simonyan, Karen
SimpleRNN layer, 2 nd
single-label
categorical classification
multiclass classification
sliding windows
Smart Reply feature, Google
smile vector
softmax, 2 , 3 , 4 , 5
nd rd th th
sound data
sparse_categorical_crossentropy
spatially hierarchical patterns
speech recognition
ssl directory
stacking recurrent layers
statistical power, developing models with
steps_per_epoch
stochastic gradient descent.
See SGD.
stochastic sampling
stochasticity, 2 nd
strided convolutions
strides
style function
style loss
subroutines, reusing modular
supervised learning
SVM (support vector machine)
symbolic AI, 2 nd
symbolic differentiation
syntax tree prediction
Szegedy, Christian
T
tanh activation
target
temporal leak
temporally supervised learning
TensorBoard applications, 2 nd
tensors
higher-dimensional
key attributes of
manipulating in Numpy
operations of
broadcasting
dot
element-wise
geometric interpretation of
geometric interpretation of deep learning
reshaping
reshaping
slicing.
See also data tensors.
test sets
hold-out validation
iterated K-fold validation with shuffling
K-fold validation
text data, 2 nd
Theano
installing on Ubuntu
overview, 2nd
timeseries data, 2 nd
timesteps
TITAN X, NVIDIA
token embedding
tokenizing data, word embeddings
total variation loss
TPU (tensor processing unit)
trainable attribute
training
convnets on small datasets
building networks
data preprocessing
downloading data
relevance for small-data problems
using data augmentation
interrupting
language models
models
training loop, 2 nd
training sets
hold-out validation
iterated K-fold validation with shuffling
K-fold validation
train_labels variable, 2 nd
translation-invariant patterns, 2 nd
transposition
Turing test
Turing, Alan
two-branch networks
Tyka, Mike, 2 nd
você
Ubuntu
installing Keras on
installing Python scientific suite on
installing Theano on
setting up GPU support
underfitting
adding dropout
adding weight regularization
reducing network size
unfreezing layers
Unix workstation
unsupervised learning
V
VAEs (variational autoencoders), generating images with
concept vectors for image editing
sampling from latent spaces of images
validation scores
validation sets
hold-out validation
iterated K-fold validation with shuffling
K-fold validation
overfitting
overview
validation_data argument, 2 nd
validation_steps argument
values
handling missing
normalizing
vanishing gradient problem
Vapnik, Vladimir
vector data, 2 nd
vector regression
vectorization
vectorized data
vectorized implementations
vectorizing text
vectors (1D tensors)
versatility
vi
video data, 2 nd
visual concepts
visualizing
convnet filters
convnet learning
heatmaps of class activation
intermediate activations
volumetric data
W
weight decay
weight regularization, adding
weight sharing of layers
weight-initialization schemes
weights argument, VGG16, 2 nd
weights, layers
Welling, Max
width_shift range
word embeddings
defining models
downloading GloVe word embeddings
evaluating models
learning embedding layers
loading GloVe embeddings in models
preprocessing embeddings
tokenizing data
training models
using pretrained word embeddings
word vectors
Word2vec algorithm
word-embedding space
word_index
workflow of machine learning, 2
nd
assembling datasets
choosing evaluation protocol
choosing measure of success
defining problems
developing models
preparing data
regularizing models
tuning hyperparameters
workflows
workstations, setting up
Jupyter notebooks
running jobs in cloud
running Keras
selecting GPUs
writing callbacks
X
Xception, 2 nd
XGBoost library, 2 nd
Y
yield operator
Z
Zisserman, Andrew
zoom_range