Alura > Cursos de Data Science > Cursos de Machine Learning > Conteúdos de Machine Learning > Primeiras aulas do curso Treinando uma Rede Neural: Deep Learning com PyTorch

Treinando uma Rede Neural: Deep Learning com PyTorch

Funções de Perda - Introdução

Olá, pessoas. Meu nome é Camila, eu dou aula de deep learning desde 2018, em geral em cursos de extensão na federal de Minas Gerais. Eu também tenho um canal no YouTube chamado Peixe Babel, onde eu e a Virginia falamos sobre tecnologia.

Neste curso, vamos aprender como treinar uma rede neural. Existe outro curso aqui na Alura introduzindo como construir uma rede neural. Esse curso já vai esperar que você saiba construir uma rede neural no Pytorch. De qualquer forma, vamos retomar essas ideias neste curso.

Nós vamos aprender todos os elementos envolvidos no processo de otimização, como por exemplo funções de perda, que é nossa métrica de como está a qualidade do modelo. Vamos instanciar modelos neurais partindo do princípio que vocês já viram isso antes.

Vamos aprender os otimizadores no Pytorch também, sempre associado a problemas reais, ainda que simples, e também vamos falar de hiper parâmetros de otimizadores. Para que entendamos quais são os otimizadores mais usados, mais eficientes, e quais já estão implementados no framework do Pytorch, que já adianto, são os mais úteis atualmente.

Por fim, vamos falar um pouco sobre como funciona o fluxo de treinamento e teste. O que é um batch, uma época, uma iteração, quando você treina uma rede neural. Vamos ver na prática como carregar dados no Pytorch, para que possamos trabalhar com dados reais. Vamos ver que existem dados pré-carregados, mas também você pode carregar seu próprio dado. Vamos aprender como fazer isso. Iremos fazer todo o fluxo de treinamento e validação de uma rede neural.

A ideia é que ao final deste curso vocês sejam capazes de construir ou carregar modelos neurais e também consigam implementar um fluxo de treinamento e validação, seja com dados existentes no framework ou com dados próprios seus que você pode carregar de forma muito eficiente no Pytorch.

Funções de Perda - Funções de Perda

Olá, pessoal. Agora que já vimos quando precisamos de um único perceptron ou de múltiplos perceptrons na mesma camada, ou até de várias camadas de perceptron, vamos começar a falar como essas redes aprendem, como otimizamos essas coisas.

Antes de entrar nas explicações de fato, gosto muito de uma analogia do Patrick Estrela, do Bob Esponja, tentando abrir um pote. Vamos ver um vídeo no YouTube e vou tentar explicar com a analogia desse vídeo.

Eu amo de paixão esse exemplo, porque já conseguimos fazer a primeira analogia do que vamos aprender. Vocês viram que o Patrick estava recebendo instruções de como abrir a tampa. À medida em que ele chegava mais perto, ele ia mais devagar.

A primeira coisa é que muita gente se refere ao aprendizado como tentativa e erro. Mas isso dá a ideia de que a pessoa está tentando loucamente várias alternativas, até chegar em algo bom. E não é bem assim. É uma tentativa guiada. Vamos ver como fazer isso de forma mais inteligente.

Nesta aula vamos falar de funções de perda, ou funções objetivas, otimização e hiper parâmetros de otimizadores. Vamos começar pelas funções de perda. Você precisa definir uma medida de qualidade para saber se está se aproximando ou se afastando da solução. Você quer minimizar ou maximizar.

Por exemplo, o Patrick quer minimizar a distância entre a mão dele e a tampa. Toda vez que ele tentar uma nova alternativa, ele sabe que está quente ou frio se estiver afastado ou aproximado da tampa. Seria um exemplo prático de função de perda. Minimizar a distância entre um ponto e outro.

Você pode chamar de função objetiva, função de perda, critério, loss, função de custo. Tem vários nomes que querem dizer a mesma coisa. Eu trouxe alguns quotes interessantes para termos noção do que tem que ser a função de perda. Todas essas aspas foram retiradas da referência que está linkada aqui, que é um livro.

A primeira delas é: “a função de custo reduz todos os aspectos bons e runs de um sistema complexo a um único número, um valor escalar, o que permite ranquear e comparar as soluções candidatas”. Quando falarmos de função de perda, estamos falando de um número. Posso querer minimizar a distância, mas também quero minimizar a força com que o Patrick bate no pote, porque ele pode quebrar o pote. Vou ter que somar esses critérios em um único valor. Não importa quantas métricas o problema exige.

“Se fizermos uma escolha ruim de função de custo e os results obtidos não forem satisfatórios é nossa culpa por não especificar bem nosso objetivo”. Pode ser que você construa uma rede e ela não fique boa de jeito nenhum. Você pode pensar que não treinou direito, que a rede não é ideal, mas é importante pensar também que talvez você precise melhorar sua função de custo. Seu objetivo pode não estar bem definido. Não podemos ignorar a importância da função de custo.

A escolha da função de custo está relacionada diretamente com o problema, assim como a camada de saída da sua rede, e vamos olhar para os principais problemas, que são regressão e classificação. Quando você estiver construindo sua arquivo e definindo sua função de perda, o problema que você está lidando vai interferir muito.

Se você pensar no problema de regressão, sabemos que precisamos encontrar a função que vai descrever essa distribuição de dados. Vou ter uma variável independente, que no meu caso é o número de cômodos da casa, e uma variável dependente, que é o valor. Ela é dependente porque o valor da casa depende do número de cômodos que ela tem. É uma relação que precisa existir.

Dado que o valor depende do número de cômodos, qual é a curva que vai se ajustar, permitir que eu faça predições? Eu consigo ver que se eu fizer uma coisa assim, consigo fazer predições. Dado que tenho uma casa com sete cômodos, consigo prever que ela vai custar algo em torno de 20 mil dólares. Mais ou menos isso que é a regressão.

Aqui conseguimos realizar inferências. Dado que o exemplo que eu dei tem uma casa com sete cômodos custando 20 a 25 mil dólares, a métrica para medir a qualidade desse modelo é a distância entre o preço inferido e o preço real. Preço inferido vai ser o valor que vou inferir em cima da minha curva. Só que tenho uma amostra de uma casa com sete cômodos que custa 10 mil dólares. Eu errei por 15 mil dólares.

A distância entre o preço real e o preço inferido pode ser um critério de qualidade. Neste caso, muito válido. Meu custo é 15 mil dólares de diferença entre o que eu predisse e o valor real.

Dado que você tem uma regressão e você quer fazer a regressão de um valor, quero descobrir o preço de uma casa com base no número de cômodos e a minha inferência, que é o valor que ela custa. Eu criei uma rede com quatro neurônios na camada intermediária. Como temos uma rede com uma camada intermediária e uma camada de saída, vou conseguir modelar curvas, não só retas.

Eu sei que minha camada de saída tem que ter um único neurônio, essa é uma limitação do problema, porque quero inferir um valor apenas, e sei que a minha função de custo pode ser a distância entre o valor real e o valor predito. Posso calcular uma distância absoluta, que é minha predição menos o valor real, ou uma distância quadrática, que é minha predição menos valor real elevado ao quadrado.

Aqui são métricas reais, muito utilizadas para problemas de regressão, que no Pytorch e na maioria dos frameworks recebe o nome de perda L1, que é a distância absoluta, e o mean squared error, que é o erro médio quadrático, ou MSE. São métricas de custo que existem no Pytorch. Se o problema é de regressão, você pode ter essas duas funções de perda, que vão resolver bem o problema. São boas candidatas.

A função de perda neste caso é minimizar a distância entre a predição e o rótulo. No caso do Patrick, ele está fazendo de certa forma uma regressão, dado que ele tem a posição da mão dele, a posição da tampa, ele está calculando a distância entre os dois valores.

Em um problema como classificação, vamos supor que temos um problema de classificação de imagens. Queremos resolver com uma camada intermediária e uma camada de saída. Nesse caso, minha entrada x vai ter o número de dimensões igual ao número de pixels da imagem. Se tenho uma imagem 12x12 são 144 pixels, o numero de pontos no x. A camada de saída vai ter a quantidade de classes que eu modelar.

Se eu quero classificar gato, cachorro, papagaio e calopsita, vou ter quatro neurônios na última camada. Um especializado em cada animal. Vimos isso quando falamos de múltiplos neurônios na mesma camada. Vou ter o x como valor de pixels e o y com o número de classes que estou modelando. A camada intermediária vai ser o que vai permitir a modelagem de funções mais complexas não lineares.

Aqui tenho que y linha é a probabilidade de cada classe. Estou dizendo que a ativação é 0.25 para a classe cachorro, 0.9 para outra classe, 0.12 para outra classe, e daí por diante. O meu rótulo aqui vai ser uma codificação chamada de um 1 hot. Uma posição ativada e o resto desativada. Por exemplo, papagaio desativado, calopsita desativada, cachorro ativado e gato desativado, porque a imagem é de um cachorro.

Teríamos a predição e o rótulo. Como calcular a qualidade dessas coisas? A ideia, como estamos trabalhando com distribuições, é mais complexa, mas vou dar uma ideia geral para explicar por que usamos uma métrica de distribuições.

No caso de classificações com múltiplas classes, vamos usar a cross entropy, ou entropia cruzada, que é um conceito da teoria da informação. Queremos minimizar a entropia das distribuições. Para isso, vamos avaliar par a par os pares da nossa predição com o rótulo real. Se estivermos falando da classe correta, ou seja, se y for igual a 1, iremos tirar menos log da predição, e se não iremos fazer o menos log de 1 menos a predição.

Isso na prática vai significar que quanto maior a probabilidade predita da classe correta, menor meu erro. Quanto mais próximo de 1 esse valor, mais correto estou. Menor tem que ser meu erro. Caso contrário, se y for igual a zero, quanto maior a probabilidade prevista, maior meu erro, porque estou dando uma ativação forte para a classe incorreta.

São detalhes que se você quiser entender melhor você vai precisar pesquisar a literatura da função de entropia cruzada, mas é só para explicar que quando seu problema for de classificação, você vai ter que tratar com a função que vai calcular a diferença entre essas distribuições. Uma das funções é a cross entropy, que é a entropia cruzada.

Quando você treina um modelo, o objetivo é fazer ele convergir para uma solução aproximadamente ótima. A função de perda é utilizada para acompanhar essa convergência. O gráfico de convergência vai ser dado pelos valores da sua perda à medida em que você passa por iterações de treinamento.

Iremos falar um pouco mais disso depois, mas basta você saber que você não vai treinar sua rede uma única vez. Você vai treinar com vários conjuntos de dados, várias vezes. Isso iremos conversar melhor depois. Mas imagine que passei um conjunto de dados na minha rede e a perda foi 0.8. Faço mais uma iteração e a perda foi 0.6. Faço mais uma, deu 0.4. Estou convergindo, diminuindo a perda à medida em que treino meu modelo.

Nosso objetivo é reduzir isso ao mínimo que conseguirmos. O que vai acontecer é que vai chegar um momento em que isso vai saturar e virar uma constante. Esse gráfico vai indicar se você está treinando bem ou mal sua rede, porque você pode também ter uma piora. Medir a loss ao longo das iterações é a forma de saber se você está melhorando ou se você voltou a piorar. O valor da loss é extremamente importante nesses casos, para avaliar se seu treinamento está bom ou ruim, convergindo, se a perda está diminuindo ou não.

No próximo vídeo vamos dar uma olhada nas funções de perda do Pytorch.

Funções de Perda - Classificação

Vamos ver como funciona no Pytorch o cálculo da função de perda. Só que para isso vamos simular um problema inteiro, vamos repetir todo o processo que já conhecemos.

Primeiro vamos fazer os imports de sempre, não iremos precisar de nada diferente, as funções de perda também estão no pacote NN. E vamos relembrar que precisamos verificar qual dispositivo está disponível, se temos GPU disponível para definir qual dispositivo vamos usar para colocar os dados.

Tivemos o cuda, porque antes de começar a gravar eu mudei as configurações para usarmos a GPU. Iremos trabalhar com um dataset de classificação de vinhos. Ele tem três classes e treze características para fazer a classificação, além de 178 amostras.

Se pegamos o pacote de datasets do sklearn, conseguimos ter acesso ao dataset de vinhos. Ele vai dar para nós tanto os dados quanto os rótulos. Podemos imprimir tanto a dimensão dessas coisas como os nomes das nossas features, nossos dados, e o nome das classes.

Temos 178 amostras, cada uma com treze características e 178 rótulos. As características são nível alcoólico, acidez, alcalinidade, etc. Também temos os nomes das classes. Os dados em si, se imprimirmos um target, são valores inteiros. E o dado é uma sequência de floats que são dados das características.

Vamos fazer o que já sabemos e instanciar um MLP, uma camada escondida e uma camada de saída para resolver um problema de classificação. Temos que fazer nossa classe, que vai ser o wine classifier, é uma classe módulo, que cria módulos de redes neurais, e temos duas funções obrigatórias que precisamos implementar. A init e a forward, que recebe não só o self, indicando que ela pertence à classe, mas também a entrada.

Lembra que no init é obrigatório inicializar a super classe. Agora podemos fazer o que quisermos, que no caso do init é a definição da nossa arquitetura. Quero uma camada escondida, que vai ser linear, quero uma ativação relu e uma camada de saída, que também é uma linear.

Como estamos tratando de um problema de classificação de três classes, posso também querer uma softmax que vai transformar minhas saídas em distribuições de probabilidade. Aqui defini minha arquitetura.

Para definir esses valores, já sei que a primeira camada, que é a camada escondida, vai receber o dado original. O input size vai ser o tamanho da feature. E ela vai sair com features latentes, que vai ter uma quantidade de dimensões que o próprio programador vai definir. Eu vou definir o tamanho da camada escondida, porque ela não está relacionada ao problema. Ela é minha forma de solucionar o problema.

Na camada de saída, ela vai receber a saída da camada anterior e vai ter um outsize que vai ser o número de classes do meu problema. Vou receber isso como parâmetro da minha classe, e está definida a arquitetura.

Na hora de fazer o folder, vou definir minha feature intermediária, que vai ser a ativação da camada intermediária, e minha saída vai ser a ativação softmax da minha camada output, que vai receber como entrada a feature que acabei de criar ali em cima. Retorno esse output e posso definir minha rede.

Primeiro vou definir os valores. O input size é a quantidade de features que eu tenho. Essa informação vimos que está no shape. Se eu olhar o shape, a segunda dimensão é o número de características. O meu hidden size é uma escolha minha, posso colocar, por exemplo, 32, porque estou definindo que 32 neurônios é o suficiente para resolver o problema. E meu outsize é o número de classes. Posso pegar, por exemplo, target names. Pego o comprimento dele.

Com isso, posso mudar esses dados a qualquer momento, posso mudar o dataset e não vou precisar implementar nada, porque estou pegando informação do próprio dado, não definindo valores na mão.

Preciso criar a rede. Coloco o to device, porque estou fazendo o cast na GPU. Agora, posso imprimir a rede para ver exatamente o que fizemos.

Vamos instanciar uma função de perda, que é o motivo de estarmos aqui. Posso chamar em geral a variável de loss de critério, e posso colocar NN.crossentropyloss, porque sei que é um problema de classificação. Quero calcular a entropia cruzada entre a distribuição que vai ser predita e a distribuição do rótulo. Aqui também tenho que fazer cast na GPU.

Precisamos agora transformar os dados em tensores, porque por enquanto são arrays, e passar na rede. Uma vez feito isso, podemos calcular a loss entre o rótulo e a predição da rede. Vamos transformar os dados em tensores. Vou ter o xtensor, que vai ser torch.from_numpy(data) e o ytensor, que vai ser torch.from_numpy(target).

Por padrão, o que usamos não é o double na rede, a menos que disséssemos explicitamente que nossa rede é double, tenho que converter para float para ficar de acordo com a rede. No caso do rótulo, é long mesmo. Só que também temos que fazer cast na GPU.

Posso passar na rede. Minha predição vai ser a saída na rede, dada a entrada. O shape disso é 138 por 3, porque o x são 178 amostras, com 13 características. E a saída são 138 amostras com 3 probabilidades. Se eu passasse uma única amostra, eu teria que arrumar as dimensões, e o que eu teria é que passei uma única amostra com 13 características e ele predisse uma única amostra com 3 probabilidades.

Como transformamos numa distribuição, ele vai prever valores cuja soma vai dar 1. É uma distribuição de probabilidades. Isso é aleatório, porque não treinamos. E agora que já fizemos a previsão, podemos comparar com a função de perda.

Vou fazer a predição de tudo e vou comparar as dimensionalidades da predição com as do rótulo. Elas não batem. Minha predição tem 3 dimensões de classe e meu rótulo só uma. Não tem problema, porque no caso específico da cross entropy ele aceita dessa forma. Podemos passar os dados assim.

Dado que nossa predição é uma distribuição de probabilidade e o rótulo um único valor, não tem problema, porque é assim que a cross entropy aceita os dados. Posso simplesmente chamar a loss, falando o critério e mandando como parâmetro a predição e o rótulo. Ele vai calcular a perda média entre todos os valores. Se eu pegar só o rótulo de uma classe ele não vai conseguir por causa da dimensionalidade.

Posso por exemplo pegar um range, da amostra 1 até a 30. Ele vai dar a média das perdas para esses valores. Basicamente, é isso. É dessa forma que vamos usar a loss. Depois vamos aprender a utilidade disso no processo de otimização, mas por hora só estamos vendo como calcula.

Relembrando que a loss vai ser sempre um valor escalar, independente do número de amostras. Ela vai indicar o erro naquele conjunto de dados. No próximo vídeo vamos ver um problema de regressão.

Sobre o curso Treinando uma Rede Neural: Deep Learning com PyTorch

O curso Treinando uma Rede Neural: Deep Learning com PyTorch possui 177 minutos de vídeos, em um total de 35 atividades. Gostou? Conheça nossos outros cursos de Machine Learning em Data Science, ou leia nossos artigos de Data Science.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Machine Learning acessando integralmente esse e outros cursos, comece hoje!

Plus

De
R$ 1.800
12X
R$109
à vista R$1.308
  • Acesso a TODOS os cursos da Alura

    Mais de 1500 cursos completamente atualizados, com novos lançamentos todas as semanas, emProgramação, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.

  • Alura Challenges

    Desafios temáticos para você turbinar seu portfólio. Você aprende na prática, com exercícios e projetos que simulam o dia a dia profissional.

  • Alura Cases

    Webséries exclusivas com discussões avançadas sobre arquitetura de sistemas com profissionais de grandes corporações e startups.

  • Certificado

    Emitimos certificados para atestar que você finalizou nossos cursos e formações.

Matricule-se

Pro

De
R$ 2.400
12X
R$149
à vista R$1.788
  • Acesso a TODOS os cursos da Alura

    Mais de 1500 cursos completamente atualizados, com novos lançamentos todas as semanas, emProgramação, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.

  • Alura Challenges

    Desafios temáticos para você turbinar seu portfólio. Você aprende na prática, com exercícios e projetos que simulam o dia a dia profissional.

  • Alura Cases

    Webséries exclusivas com discussões avançadas sobre arquitetura de sistemas com profissionais de grandes corporações e startups.

  • Certificado

    Emitimos certificados para atestar que você finalizou nossos cursos e formações.

  • Luri powered by ChatGPT

    Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com Luri até 100 mensagens por semana.

  • Alura Língua (incluindo curso Inglês para Devs)

    Estude a língua inglesa com um curso 100% focado em tecnologia e expanda seus horizontes profissionais.

Matricule-se
Conheça os Planos para Empresas

Acesso completo
durante 1 ano

Estude 24h/dia
onde e quando quiser

Novos cursos
todas as semanas