Voltar para os desafios
Difícil 0 resolvidos 0% de aceitação

Treine uma rede neural para resolver o XOR

Junte tudo: treine uma rede sigmoide 2-2-1 com retropropagação até ela aprender o XOR.

O que você vai aprender

  • Treinar uma rede pequena do começo ao fim
  • Ver por que o XOR precisa de uma camada oculta
  • Juntar as passagens para frente e para trás

Problema

Ver original (inglês)

O XOR (ou exclusivo) é o problema clássico que um único neurônio não consegue resolver, mas uma rede com uma camada oculta consegue. Treine uma rede com 2 entradas, 2 neurônios ocultos e 1 saída, todos sigmoides (σ(z) = 1 / (1 + e^(-z))), usando a perda L = (y - target)².

Os dados de treinamento, sempre usados nesta ordem em cada época:

entradatarget
[0, 0]0
[0, 1]1
[1, 0]1
[1, 1]0

Para cada exemplo:

  1. Propagação direta: a_j = σ(x₁ × hidden_weights[j][0] + x₂ × hidden_weights[j][1] + hidden_biases[j]), y = σ(a₁ × output_weights[0] + a₂ × output_weights[1] + output_bias)
  2. delta_out = 2 × (y - target) × y × (1 - y), e delta_j = delta_out × output_weights[j] × a_j × (1 - a_j), usando os pesos antes da atualização deste exemplo
  3. Atualize cada parâmetro com a taxa de aprendizado lr: output_weights[j] -= lr × delta_out × a_j, output_bias -= lr × delta_out, hidden_weights[j][i] -= lr × delta_j × x_i, hidden_biases[j] -= lr × delta_j

Implemente train_xor(hidden_weights, hidden_biases, output_weights, output_bias, learning_rate, epochs).

Saída

Depois do treinamento, retorne as previsões y da rede para as entradas [0, 0], [0, 1], [1, 0] e [1, 1], nessa ordem.

Restrições

0 ≤ epochs ≤ 5.000

0 < learning_rate ≤ 5

-2 ≤ cada peso e viés inicial ≤ 2

Exemplos

Exemplo 1
Entrada:
epochs = 0
output_bias = 0.0
hidden_biases = [0.0, 0.3]
learning_rate = 1.5
hidden_weights = [
    [0.2, 0.8],
    [0.9, -0.7]
]
output_weights = [-0.6, 0.4]

Saída esperada:
expected = [0.482451, 0.436972, 0.494378, 0.452729]
Entrada:
long long epochs = 0;
double output_bias = 0.0;
std::vector<double> hidden_biases = {0.0, 0.3};
double learning_rate = 1.5;
std::vector<std::vector<double>> hidden_weights = {
    {0.2, 0.8},
    {0.9, -0.7}
};
std::vector<double> output_weights = {-0.6, 0.4};

Saída esperada:
std::vector<double> expected = {0.482451, 0.436972, 0.494378, 0.452729};
Entrada:
long epochs = 0;
double output_bias = 0.0;
double[] hidden_biases = new double[] {0.0, 0.3};
double learning_rate = 1.5;
double[][] hidden_weights = new double[][] {
    new double[] {0.2, 0.8},
    new double[] {0.9, -0.7}
};
double[] output_weights = new double[] {-0.6, 0.4};

Saída esperada:
double[] expected = new double[] {0.482451, 0.436972, 0.494378, 0.452729};

Explicação

Com 0 épocas nada é treinado, então o resultado é só a propagação direta com os pesos iniciais: cerca de [0.482, 0.437, 0.494, 0.453]. A rede não treinada dá quase a mesma resposta para toda entrada.

Exemplo 2
Entrada:
epochs = 2000
output_bias = 0.0
hidden_biases = [0.0, 0.3]
learning_rate = 1.5
hidden_weights = [
    [0.2, 0.8],
    [0.9, -0.7]
]
output_weights = [-0.6, 0.4]

Saída esperada:
expected = [0.015727, 0.982119, 0.985515, 0.013682]
Entrada:
long long epochs = 2000;
double output_bias = 0.0;
std::vector<double> hidden_biases = {0.0, 0.3};
double learning_rate = 1.5;
std::vector<std::vector<double>> hidden_weights = {
    {0.2, 0.8},
    {0.9, -0.7}
};
std::vector<double> output_weights = {-0.6, 0.4};

Saída esperada:
std::vector<double> expected = {0.015727, 0.982119, 0.985515, 0.013682};
Entrada:
long epochs = 2000;
double output_bias = 0.0;
double[] hidden_biases = new double[] {0.0, 0.3};
double learning_rate = 1.5;
double[][] hidden_weights = new double[][] {
    new double[] {0.2, 0.8},
    new double[] {0.9, -0.7}
};
double[] output_weights = new double[] {-0.6, 0.4};

Saída esperada:
double[] expected = new double[] {0.015727, 0.982119, 0.985515, 0.013682};

Explicação

Depois de 2.000 épocas, as previsões ficam em torno de [0.016, 0.982, 0.986, 0.014]: baixas para [0, 0] e [1, 1], altas para [0, 1] e [1, 0]. A rede aprendeu o XOR.

Como abordar

Tente sozinho primeiro e depois abra uma dica de cada vez.

Dica 1

Processe as quatro amostras de XOR na mesma ordem fixa em toda época e atualize os parâmetros imediatamente após cada uma.

Dica 2

Use forward propagation, depois faça backpropagation do erro da saída para os dois neurônios ocultos com os pesos atuais antes de aplicar as atualizações.

Dica 3

Fique atento à derivada da sigmoid e ao fato de que as atualizações da camada oculta dependem dos valores de entrada, então entradas zero deixam alguns pesos inalterados.

Complexidade esperada

tempo O(epochs), espaço extra O(1)

Referências

Este desafio foi adaptado para o CodeUtility. Siga o enunciado e as restrições acima.

Aprenda com outros

Soluções da comunidade

Ainda não há soluções publicadas. Resolva este desafio para compartilhar a primeira.