Capa: Diagrama comparando a porta lógica
CNOT de um computador quântico (à esquerda)
e uma w:XOR de um computador clássico (à direita).
Por George.ad.Stamatiou e Alksentrs.
April 1, 2009. Fonte: Wikimedia Commons.
Maurício Pinheiro
1. Introdução às Redes Neurais e à Retropropagação
As redes neurais são um tipo de algoritmo de aprendizado de máquina [1] inspirado na estrutura e função do cérebro humano. Elas são capazes de aprender padrões e relações complexas em dados, tornando-as úteis para tarefas como classificação de imagem, processamento de linguagem natural e modelagem preditiva.
Em um nível mais alto, uma rede neural consiste em nós interconectados, ou neurônios, organizados em camadas. Cada neurônio recebe entrada de outros neurônios ou dados externos, processa essa entrada usando uma função matemática e passa a saída para outros neurônios na próxima camada. A saída da camada final representa a previsão ou saída da rede.
Treinar uma rede neural envolve ajustar os pesos e os bias dos neurônios para que a rede possa fazer previsões precisas em novos dados. A retropropagação [2] é um algoritmo amplamente utilizado para otimizar os pesos e bias de uma rede neural. Ela funciona propagando o erro, ou a diferença entre a saída prevista e a saída real, de volta pela rede e usando essas informações para atualizar os pesos e os bias.
Durante a retropropagação, a saída da camada final é comparada à saída verdadeira, e o erro é calculado. O erro é então propagado de volta pela rede, com cada neurônio em cada camada contribuindo para o erro com base em suas conexões ponderadas com os neurônios na próxima camada. Isso permite que o algoritmo determine quanto cada peso e bias contribuiu para o erro, para que possam ser ajustados para melhorar a precisão das previsões da rede.
A retropropagação é um algoritmo iterativo, o que significa que é executado várias vezes em um conjunto de dados para melhorar continuamente os pesos e os bias da rede. Ao minimizar o erro entre a saída prevista e a saída verdadeira, a rede se torna melhor em fazer previsões em novos dados.
2. Definindo o Problema: Implementando uma porta lógica XOR com uma Rede Neural
A porta lógica XOR é uma operação lógica que recebe dois valores binários como entrada e retorna uma saída binária. A saída é verdadeira (1) se as entradas são diferentes e falsa (0) se elas são iguais.

A tabela verdade para a porta lógica XOR é a seguinte:
| Input 1 | Input 2 | Output |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
A operação XOR é um exemplo de problema que não pode ser resolvido utilizando um modelo linear, como um algoritmo simples de regressão ou classificação. Em vez disso, um modelo não-linear é necessário para capturar com precisão as relações entre as entradas e as saídas. Uma rede neural é um tipo de modelo não-linear que pode ser usado para resolver o problema XOR.
Para aprender a realizar a operação XOR, uma rede neural deve ser treinada em um conjunto de dados de pares de entrada-saída. Para a porta XOR, a entrada é um par de valores binários, e a saída é um único valor binário que representa o resultado da operação XOR.
A chave para a capacidade da rede neural em aprender a operação XOR é sua capacidade de modelar relações não lineares complexas entre as entradas e saídas. Ao usar múltiplas camadas de neurônios, cada uma com suas próprias funções de ativação não lineares, a rede pode aprender a representar funções e fronteiras de decisão complexas.
O algoritmo de retropropagação, em seguida, permite que a rede ajuste seus pesos e bias para minimizar o erro entre a saída prevista e a saída real, aprendendo, em última instância, a realizar com precisão a operação XOR.
3. Implementando a Rede Neural em Python
Agora que definimos o problema de como implementar uma porta XOR com uma rede neural, podemos passar para a implementação em si. Nesta seção, explicaremos o código fornecido e como ele treina a rede neural para realizar a operação XOR. Também discutiremos os parâmetros usados no código e como eles afetam o desempenho da rede neural.
Para isso, implementaremos uma rede neural composta por três camadas: uma camada de entrada, uma camada oculta e uma camada de saída. A camada de entrada tem dois neurônios, que correspondem aos dois valores de entrada da porta XOR. A camada oculta tem três neurônios, que foram escolhidos arbitrariamente. A camada de saída tem um neurônio, que corresponde ao valor de saída da porta XOR. Portanto, a rede possui um total de seis neurônios, incluindo as camadas de entrada e saída. Os pesos e bias para cada camada são aprendidos durante o processo de treinamento, que usa o backpropagation para atualizar os pesos e bias.

O código usa a linguagem de programação Python e a biblioteca NumPy para cálculos numéricos. A rede neural é implementada usando uma arquitetura feedforward, com uma camada oculta e uma camada de saída. A função de ativação usada para todos os neurônios é a função sigmóide, que comprime a saída do neurônio entre 0 e 1.
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def train(X, y, n_hidden, n_epochs, lr):
n_input = X.shape[1]
n_output = y.shape[1]
# initialize weights and biases
w1 = np.random.randn(n_input, n_hidden)
b1 = np.zeros((1, n_hidden))
w2 = np.random.randn(n_hidden, n_output)
b2 = np.zeros((1, n_output))
# train the model for n_epochs
cost_values = []
for i in range(n_epochs):
# forward pass
z1 = np.dot(X, w1) + b1
a1 = sigmoid(z1)
z2 = np.dot(a1, w2) + b2
y_pred = sigmoid(z2)
# compute cost
cost = np.mean(-y * np.log(y_pred) - (1 - y) * np.log(1 - y_pred))
cost_values.append(cost)
# backward pass
dz2 = y_pred - y
dw2 = np.dot(a1.T, dz2)
db2 = np.sum(dz2, axis=0, keepdims=True)
dz1 = np.dot(dz2, w2.T) * a1 * (1 - a1)
dw1 = np.dot(X.T, dz1)
db1 = np.sum(dz1, axis=0)
# update weights and biases
w1 -= lr * dw1
b1 -= lr * db1
w2 -= lr * dw2
b2 -= lr * db2
return w1, w2, b1, b2, cost_values
# train the model
X = np.array([[0,0], [0,1], [1,0], [1,1]])
y = np.array([[0], [1], [1], [0]])
w1, w2, b1, b2, cost_values = train(X, y, n_hidden=3, n_epochs=1000, lr=0.1)
# print final weights and biases
# print final weights and biases
print("Final weights and biases:")
print(f"w1 = {w1}")
print(f"b1 = {b1}")
print(f"w2 = {w2}")
print(f"b2 = {b2}")
O código consiste em uma única função, train(), que recebe cinco argumentos: X, y, n_hidden, n_epochs e lr. X é a matriz de entrada, y é a matriz de saída, n_hidden é o número de neurônios na camada oculta, n_epochs é o número de épocas de treinamento e lr é a taxa de aprendizado, que controla o tamanho do passo das atualizações de peso durante o treinamento.
O primeiro passo da função train() é inicializar os pesos e os bias da rede neural. Isso é feito usando a função np.random.randn() para gerar valores aleatórios para os pesos e a função np.zeros() para inicializar os bias com zero.
Em seguida, a função entra em um loop que é executado por n_epochs iterações. Em cada iteração, a rede neural realiza um passe para frente, seguido por um passe para trás, e atualiza seus pesos e biases usando o algoritmo de retropropagação.
Durante o passe para frente, a entrada X é multiplicada pela matriz de peso w1 e o vetor de bias b1 é adicionado.

Os valores resultantes são então passados pela função de ativação sigmoidal para produzir a saída da camada oculta, a1.

A saída da camada oculta é então multiplicada pela matriz de peso w2, e o vetor de bias b2 é adicionado. Os valores resultantes são passados pela função de ativação sigmoid para produzir a saída final da rede neural, y_pred.
O custo das previsões da rede neural é então calculado usando a função de perda de entropia cruzada binária.

Esse custo é uma medida de quão bem a rede está se saindo nos dados de treinamento e é usado para atualizar os pesos e biases durante o passe para trás.
Durante o passe para trás, os gradientes do custo em relação aos pesos e biases são calculados usando a regra da cadeia do cálculo. Esses gradientes são então usados para atualizar os pesos e biases usando a regra de atualização: w -= lr * dw, onde w é um peso ou bias, lr é a taxa de aprendizado e dw é o gradiente do custo em relação a w.
Finalmente, a função retorna os pesos e biases aprendidos, bem como um array dos valores de custo ao longo das épocas de treinamento.
Na seção principal do código, a função train() é chamada com as matrizes de entrada e saída do XOR, X e y, bem como os valores de n_hidden=3, n_epochs=1000 e lr=0.1. Esses parâmetros foram escolhidos empiricamente e podem ser ajustados para melhorar o desempenho da rede neural.
O parâmetro n_hidden controla o número de neurônios na camada oculta e, portanto, a complexidade da rede neural. Um valor maior de n_hidden pode permitir que a rede modele melhor a operação XOR, mas também pode levar a um ajuste excessivo e tempos de treinamento mais lentos.
Um exemplo de saída (executado com Jupyterlite) é:
Final weights and biases:
w1 = [[-3.09192944 3.84673602 0.86697588]
[ 5.43416261 5.64932615 -2.87473456]]
b1 = [[ 2.09802913 -0.99750375 -1.18703525]]
w2 = [[-4.42330887][ 5.18063322][ 0.44384871]]
b2 = [[-0.76509979]]
A saída mostra os valores finais dos pesos e dos vieses aprendidos pela rede neural durante o treinamento. Os pesos e vieses são os parâmetros que são otimizados pelo algoritmo de retropropagação para minimizar a função de custo e fazer previsões precisas nos dados de treinamento.
Neste caso específico, a rede neural tem uma camada oculta com três neurônios e uma camada de saída com um neurônio. Os pesos são representados como matrizes, onde cada elemento representa o peso da conexão entre dois neurônios. Por exemplo, o elemento w1 [0,1] representa o peso da conexão entre o primeiro neurônio na camada de entrada e o segundo neurônio na camada oculta.
Os vieses são representados como vetores, onde cada elemento representa o viés de um neurônio. Por exemplo, o elemento b1 [0,1] representa o viés do segundo neurônio na camada oculta.
Os valores dos pesos e vieses determinam como a rede neural processa as entradas e faz previsões. Neste caso, a rede neural aprendeu a executar a operação XOR com precisão, como demonstrado pelas previsões na entrada de teste [1, 0].
4. Testando a Rede Neural:
Agora que treinamos a rede neural para realizar a operação XOR, podemos usá-la para fazer previsões em novas entradas. No nosso caso, a rede neural deve ser capaz de prever a saída do portão XOR para qualquer combinação de entrada de 0s e 1s.
Para usar a rede neural treinada para fazer previsões, basta passar a entrada pela passagem direta da rede, que aplicará os pesos e vieses aprendidos durante o treinamento para calcular a saída.
Podemos testar nossa rede neural fornecendo-lhe a entrada [1, 0] e verificando se ela produz a saída esperada de 1. Podemos usar o seguinte código para fazer essa previsão:
x_test = np.array([[1, 0]])
z1_test = np.dot(x_test, w1) + b1
a1_test = sigmoid(z1_test)
z2_test = np.dot(a1_test, w2) + b2
y_pred_test = sigmoid(z2_test)
print(f"Prediction for input {x_test}: {y_pred_test}")
Este código irá aplicar os pesos e viés aprendidos na entrada [1, 0] e calcular a saída prevista da rede neural. A saída deve ser próxima de 1, indicando que a rede neural aprendeu com sucesso a operação XOR. Com uma execução, obtemos:
Prediction for input [[1 0]]: [[0.9578651]]
Podemos testar a rede neural em todas as possíveis combinações de entradas de 0s e 1s e comparar suas previsões com as saídas esperadas da porta XOR. Podemos usar o seguinte código para fazer isso:
inputs = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
expected_outputs = np.array([[0], [1], [1], [0]])
for i in range(len(inputs)):
x = inputs[i]
y_pred = predict(x, w1, w2, b1, b2)
print(f"Input: {x}, Expected Output: {expected_outputs[i]}, Predicted Output: {y_pred}")
Este código irá iterar por todas as possíveis combinações de entrada e imprimir a saída prevista da rede neural juntamente com a saída esperada da porta XOR. Se a rede neural aprendeu a executar a operação XOR corretamente, suas previsões deverão corresponder às saídas esperadas para cada combinação de entrada.
Input: [0 0], Expected Output: [0], Predicted Output: [[0.01065188]]
Input: [0 1], Expected Output: [1], Predicted Output: [[0.98866357]]
Input: [1 0], Expected Output: [1], Predicted Output: [[0.99169754]]
Input: [1 1], Expected Output: [0], Predicted Output: [[0.01046418]]
Em geral, treinando a rede neural com retropropagação (backpropagation), conseguimos implementar com sucesso uma solução para o problema XOR usando uma rede neural em Python. Com essa base, podemos começar a explorar arquiteturas e aplicações de rede neural mais complexas.
5. Conclusão e Aplicações Futuras
Neste artigo, exploramos a implementação de uma rede neural em Python para realizar a operação XOR usando o backpropagation. Começamos com uma breve introdução às redes neurais e ao backpropagation, seguido por uma discussão sobre como uma rede neural pode ser treinada para realizar a operação XOR. Em seguida, apresentamos o código Python para implementar a rede neural e discutimos o papel de vários parâmetros usados no código.
Para testar o desempenho da nossa rede neural, usamos os inputs de teste do portão XOR e apresentamos os resultados das previsões da rede neural.
Em conclusão, demonstramos que uma rede neural pode ser treinada para executar uma operação lógica complexa como o XOR usando o backpropagation. As técnicas usadas neste artigo podem ser aplicadas a outros problemas em que a relação entre inputs e outputs não é imediatamente aparente. As redes neurais se tornaram uma ferramenta importante para resolver uma ampla variedade de problemas em áreas como reconhecimento de imagem, processamento de linguagem natural e finanças.
Para aqueles interessados em explorar ainda mais e aprender, sugerimos experimentar diferentes parâmetros no código para ver como eles afetam o desempenho da rede neural. Além disso, pode-se tentar usar diferentes funções de ativação, diferentes arquiteturas ou mesmo construir uma rede neural profunda para melhorar o desempenho do modelo. Por fim, pode-se explorar técnicas mais avançadas como redes neurais convolucionais, redes neurais recorrentes ou redes adversárias generativas para problemas mais complexos.
6. Referências
[1] Goodfellow, Ian, Yoshua Bengio, and Aaron Courville. Deep learning. MIT press, 2016.
[2] Rumelhart, David E., Geoffrey E. Hinton, and Ronald J. Williams. “Learning representations by back-propagating errors.” nature 323.6088 (1986): 533-536.
#IA #RedesNeurais #Retropropagação #ProgramaçãoPython #PortaXOR #AprendizadoDeMáquina #AprendizadoProfundo #InteligênciaArtificial #ComputaçãoNeural #TutorialDeProgramação #DicasDeCodificação #CódigoAberto

Copyright 2026 AI-Talks.org