Como é que as redes neurais aprendem?

  • 05/02/2019
  • Tempo para ler: 11 minutos

Abril 2019

Volume 34, Edição 4

Por Frank La La | Abril 2019

Frank La VigneFrank La VigneNa minha coluna anterior (“Um Olhar mais atento às Redes Neurais” , msdn.com/magazine/mt833269), explorei a estrutura básica das redes neurais e criei uma a partir do zero com Python. Depois de rever as estruturas básicas comuns a todas as redes neurais, criei uma estrutura modelo para calcular somas ponderadas e valores de saída. Os próprios neurónios são simples e desempenham funções matemáticas básicas para normalizar a sua produção entre 1 e 0, ou -1 e 1. No entanto, aumentam a sua eficiência quando ligados entre si. Os neurónios estão organizados em camadas numa rede neural e cada neurónio passa valores para a camada seguinte. Os valores de entrada em cascata na rede e afectam a saída num processo chamado propagação para a frente.

Mas como é que as redes neurais aprendem, exactamente? O que é o processo e o que acontece dentro de uma rede neural quando esta aprende? Na minha coluna anterior, concentrei-me na propagação de valores para a frente. Para cenários de aprendizagem supervisionados, as redes neurais podem tirar partido de um processo chamado propagação para trás.

Propagação para trás, perda e épocas

Lembrar que cada neurónio numa rede neural toma valores de entrada multiplicados por uma ponderação para representar a força dessa ligação. A propagação traseira detecta os pesos correctos a aplicar aos nós de uma rede neural, comparando as saídas actuais da rede com os resultados correctos ou desejados. A diferença entre a produção desejada e a produção de corrente é calculada pela função perda ou custo. Por outras palavras, a função de perda diz-nos quão precisa é a nossa rede neural ao fazer previsões para uma determinada entrada.

A fórmula para calcular a perda está representada na Figura 1. Não deixe que a matemática o intimide: é apenas uma questão de somar os quadrados de todas as diferenças. Inicialmente, os pesos e enviesamentos são normalmente definidos para valores aleatórios, que muitas vezes produzem um elevado valor de perda quando se começa a treinar uma rede neural.

A função custo ou perdabr>Figure 1 A função custo ou perda

Next, o algoritmo ajusta cada ponderação para minimizar a diferença entre o valor calculado e o valor correcto. O termo “propagação para trás” provém do facto de o algoritmo voltar atrás e ajustar os pesos e enviesamentos após o cálculo de uma resposta. Quanto menor for a perda para uma rede, mais precisa ela é. O processo de aprendizagem pode então ser quantificado como o resultado da redução da função de perda. Cada ciclo de correcção da propagação para a frente e para trás para reduzir a perda é chamado uma época. Em suma, a propagação retrógrada consiste em determinar os melhores pesos de entrada e enviesamentos para obter um resultado mais exacto ou “minimizador de perdas”. Se pensa que isto consome muitos recursos de processamento, está certo. De facto, o poder de processamento era insuficiente até há relativamente pouco tempo para tornar este processo prático para uso geral.

Descida de gradiente, velocidade de aprendizagem e descida de gradiente estocástico

Como é que os pesos são ajustados em cada época? Podem ser ajustados aleatoriamente, ou existe um processo? É aqui que muitos principiantes começam a ficar confusos, pois há muitos termos desconhecidos envolvidos, tais como descida de gradiente e velocidade de aprendizagem. No entanto, quando bem explicado, não é assim tão complicado. A função de perda reduz toda a complexidade de uma rede neural a um único número que indica se a resposta da rede neural está muito longe da resposta desejada. Pensar na saída da rede neural como um número único permite-nos pensar no seu desempenho em termos simples. O objectivo é encontrar a série de pesos que devolve o valor de perda mais baixo ou mínimo.

Quando plotado num gráfico, como na Figura 2, pode-se ver que a função de perda tem a sua própria curva e gradientes que podem ser usados como guia para ajustar os pesos. A inclinação da curva da função de perda serve de guia e aponta para o valor mínimo. O objectivo é encontrar o mínimo da curva completa, que representa os inputs onde a rede neural é mais precisa.

Gráfico de la función de pérdida con una curva sencillaGráfico da função Perda com uma curva simplesbr> Gráfico da função Perda 2 com uma curva simples

Na Figura 2, à medida que mais é adicionado aos pesos, atinge-se um ponto baixo e depois começa a subir novamente. A inclinação da linha mostra a direcção para o ponto inferior da curva, o que representa a perda mais baixa. Quando a inclinação é negativa, uma quantia é adicionada aos pesos. Quando a inclinação é positiva, é subtraída uma quantia dos pesos. A quantidade específica que é adicionada ou subtraída dos pesos é conhecida como a taxa de aprendizagem. A determinação de uma taxa de aprendizagem ideal é simultaneamente uma arte e uma ciência. Se for demasiado grande, o algoritmo pode falhar o mínimo. Se for demasiado baixo, a aprendizagem demorará demasiado tempo. Este processo chama-se descida por gradiente. Os leitores que estão mais familiarizados com as complicações do cálculo verão este processo pelo que é: determinar a derivada da função de perda.

No entanto, raramente o gráfico de uma função de perda é tão simples como o da Figura 2. Na prática, há muitos altos e baixos. Assim, o desafio é encontrar o mais baixo dos pontos baixos (o mínimo global) e não se deixar enganar pelos pontos baixos mais próximos (mínimo local). A melhor abordagem nesta situação é escolher um ponto na curva ao acaso e depois continuar o processo de descida de gradiente descrito acima, daí o termo “Descida de Gradiente Estocástico”. Para uma excelente explicação dos conceitos matemáticos deste processo, ver o vídeo do YouTube “Gradient Descent, How Neural Networks Learn | Deep Learning, Chapter 2” em youtu.be/IHZwWFHWa-w.

Para a maior parte, este nível de arquitectura de redes neurais foi em grande parte abstraído utilizando bibliotecas como Keras e TensorFlow. Como em qualquer esforço de engenharia de software, saber o básico ajuda sempre quando se enfrentam desafios no campo.

Pôr a teoria em prática

Na coluna anterior, criei uma rede neural do zero para processar os dígitos MNIST. A base de código resultante para iniciar o problema era ideal para ilustrar o funcionamento interno das arquitecturas de redes neurais, mas não era prático fazê-lo arrancar. Agora, existem muitas estruturas e bibliotecas que realizam a mesma tarefa com menos código.

Para começar, abra um novo caderno Jupyter, escreva o seguinte numa célula em branco, e execute-o para importar todas as bibliotecas necessárias:

import kerasfrom keras.models import Sequentialfrom keras.layers import Densefrom keras.utils import to_categoricalimport matplotlib.pyplot as plt

Nota que a saída desta célula indica que Keras usa um back-end TensorFlow. Uma vez que o exemplo da rede neural MNIST é tão comum, a Keras inclui-o como parte do seu API e até divide os dados num conjunto de aprendizagem e num conjunto de testes. Digite o seguinte código numa nova célula e execute-o para descarregar os dados e ler nas variáveis apropriadas:

# import the datafrom keras.datasets import mnist# read the data(X_train, y_train), (X_test, y_test) = mnist.load_data()

Após a saída indicar que os ficheiros foram descarregados, utilize o seguinte código para examinar brevemente o conjunto de dados de aprendizagem e teste:

print(X_train.shape)print(X_test.shape)

A saída deve indicar que o conjunto de dados x_train tem 60 000 elementos e o conjunto de dados x_test, 10 000. Para visualizar uma imagem concreta dos dados MNIST, utilizar MatPlotLib para traçar uma imagem com o seguinte código:

plt.imshow(X_train)

O resultado deve parecer um “3” desenhado à mão. Para ver o conteúdo do conjunto de dados do teste, digite o seguinte código:

plt.imshow(X_test)

O resultado mostra um zero. Sinta-se à vontade para experimentar alterando o número de índice e conjunto de dados para explorar conjuntos de dados de imagem.

Forma os dados

Como com qualquer projecto de IA ou de ciência de dados, os dados de entrada devem ser combinados com as necessidades dos algoritmos. Os dados da imagem devem ser achatados num vector unidimensional. Como cada imagem tem 28 x 28 pixels, o vector unidimensional será 1 vezes (28 x 28) ou 1 vezes 784. Digite o seguinte código numa nova célula e execute-o (note que isto não produzirá qualquer texto de saída):

num_pixels = X_train.shape * X_train.shapeX_train = X_train.reshape(X_train.shape, num_pixels).astype('float32')X_test = X_test.reshape(X_test.shape, num_pixels).astype('float32')

Os valores dos pixels podem variar de 0 a 255. Para os utilizar, deve normalizá-los para valores entre zero e um. Para o fazer, utilize o seguinte código:

X_train = X_train / 255X_test = X_test / 255

Em seguida, escreva o seguinte código para dar uma vista de olhos ao aspecto actual dos dados:

X_train

O resultado revela um conjunto de 784 valores entre zero e um.

A tarefa de utilizar múltiplas imagens de dígitos escritos à mão e determinar o número que representam é ordenar. Antes de gerar o modelo, é necessário dividir as variáveis-alvo em categorias. Neste caso, sabe que existem 10, mas pode usar a função to_categorical de Keras para determinar isto automaticamente. Escreva o seguinte código e execute-o (o resultado deve mostrar 10):

y_train = to_categorical(y_train)y_test = to_categorical(y_test)num_classes = y_test.shapeprint(num_classes)

Compilar, treinar, e testar a rede neural

Agora de os dados estarem moldados e prontos, é altura de compilar as redes neurais usando Keras. Escreva o seguinte código para criar uma função que cria uma rede neural sequencial com três níveis com uma camada de entrada de um valor neuronal num_pixels (ou 784):

def classification_model(): model = Sequential() model.add(Dense(num_pixels, activation='relu', input_shape=(num_pixels,))) model.add(Dense(100, activation='relu')) model.add(Dense(num_classes, activation='softmax')) model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=) return model

Compare este código com o código dos métodos do zero na minha última coluna. Poderá notar que novos termos, tais como “relu” ou “softmax” são referenciados nas funções de activação. Até agora, apenas explorei a função de activação Sigmoid, mas existem vários tipos de funções de activação. Por agora, lembre-se que todas as funções de activação comprimem um valor de entrada gerando um valor entre 0 e 1 ou -1 e 1,

Com toda a infra-estrutura existente, é tempo de compilar, treinar e pontuar o modelo. Digite o seguinte código numa nova célula e execute-o:

model = classification_model()model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, verbose=2)scores = model.evaluate(X_test, y_test, verbose=0)

Ao executar a rede neural, note que o valor da perda diminui a cada iteração. Consequentemente, a precisão também melhora. Note-se também o tempo necessário para executar cada época. Uma vez concluído, escreva o seguinte código para visualizar a precisão e as taxas de erro:

print('Model Accuracy: {} \n Error: {}'.format(scores, 1 - scores))

O resultado revela uma precisão superior a 98% e um erro de 1,97%.

Persistência do modelo

Agora que o modelo tenha sido treinado para um elevado grau de precisão, pode guardá-lo para utilização futura para evitar ter de o reeducar. Felizmente, Keras torna esta tarefa fácil. Digite o seguinte código numa nova célula e execute-o:

model.save('MNIST_classification_model.h5')

Isto cria um ficheiro binário de cerca de 8 KB de tamanho contendo os valores óptimos para pesos e enviesamentos. Carregar o modelo também é fácil com Keras, como mostrado abaixo:

from keras.models import load_modelpretrained_model = load_model('MNIST_classification_model.h5')

Este ficheiro h5 contém o modelo e pode ser implementado juntamente com o código para reformular e preparar os dados da imagem de entrada. Por outras palavras, o demorado processo de formação de um modelo só precisa de ser feito uma vez. Para referenciar um modelo predefinido, o processo de aprendizagem que consome tantos recursos de processamento não é necessário, e no sistema de produção final, a rede neural pode ser implementada rapidamente.

Sumário

Rede neural pode resolver os problemas que têm frustrado os algoritmos tradicionais durante décadas. Como já vimos, a sua estrutura simples desmente a sua verdadeira complexidade. As redes neurais funcionam através da propagação de entradas, pesos e enviesamentos para a frente. No entanto, é no processo inverso de propagação para trás que a rede aprende realmente ao determinar as alterações exactas que devem ser aplicadas aos pesos e enviesamentos para produzir um resultado preciso.

Do ponto de vista da máquina, aprender é minimizar a diferença entre o resultado real e o resultado correcto. Este processo é enfadonho e consome muitos recursos de processamento, como é evidenciado pelo tempo que leva a executar uma época. Felizmente, esta aprendizagem só precisa de ser feita uma vez e não sempre que o modelo é necessário. Além disso, explorei, utilizando Keras, como construir esta rede neural. Embora seja possível escrever o código necessário para construir redes neurais a partir do zero, é muito mais fácil utilizar as bibliotecas existentes, tais como Keras, que tratam dos pequenos detalhes para si.

Frank La Vigne trabalha na Microsoft como profissional de soluções tecnológicas de inteligência artificial, onde ajuda as empresas a ir mais longe, aproveitando ao máximo os seus dados com inteligência artificial e análises. Também é co-apresentador do podcast DataDriven. Escreve posts regulares em FranksWorld.com e pode ser visto no seu canal do YouTube, “Frank’s World TV” (FranksWorld.TV).

P>Escreve agradecimentos aos seguintes peritos técnicos pela revisão deste artigo: Andy Leonard

Frank’s World TV

Frank’s World TV.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *