LABORATÓRIO 06 · ARQUITETURA CONFIGURÁVEL

Monte uma rede e acompanhe o backpropagation.

Escolha de 1 a 10 entradas, as camadas ocultas e até 4 saídas. O laboratório cria dados sintéticos, treina a rede e abre cada etapa matemática do aprendizado.

entradascamadas
ocultas
saídas

1 · MONTE A ARQUITETURA

Quantos sinais e neurônios a rede terá?

Alterar a arquitetura cria uma rede nova. Os pesos começam com valores pequenos e aleatórios.

Arquitetura: —

2 · TREINE E COMPARE

Forward → erro → backpropagation → ajuste

ÉPOCAS0
ERRO MÉDIO
ACERTO POR SAÍDA
AMOSTRAS COMPLETAS

A rede que você montou

Cada linha é um peso. Azul representa peso positivo; laranja, negativo.

Rede nova
peso positivopeso negativoativação da amostra

Erro durante o treinamento

O eixo x mostra as épocas; o eixo y mostra a entropia cruzada média.

3 · ABRA UMA AMOSTRA

Veja os números percorrendo a rede

1 · ForwardCalcula u e a em todas as camadas.u = Σ(wx) + b
2 · CustoCompara cada y com sua previsão ŷ.E = −1/m Σ[y ln(ŷ) + (1−y)ln(1−ŷ)]
3 · BackpropagationLeva os deltas da saída para as camadas anteriores.δˡ = (Wˡ⁺¹)ᵀδˡ⁺¹ ⊙ g′(uˡ)
4 · AjusteMove pesos e bias contra o gradiente.w ← w − ηδa

Por que isso é backpropagation? O erro nasce nas saídas. Em seguida, a regra da cadeia calcula quanto cada neurônio anterior contribuiu para esse erro. Os deltas caminham da direita para a esquerda; depois, os pesos são corrigidos.

4 · RESULTADO AMOSTRA POR AMOSTRA

Tabela adaptada à arquitetura

Para cada saída aparecem o alvo y, a previsão contínua ŷ e o erro com sinal e = y − ŷ. Verde indica decisão correta; vermelho, incorreta. O custo E resume todas as saídas da amostra.

COMO O DATASET FAKE É CRIADO?

Há uma regra escondida por trás dos alvos.

O laboratório sorteia 16 exemplos com valores entre 0 e 1. Para cada saída, combina as entradas com coeficientes fixos e uma interação entre duas características. A mediana divide os resultados em 0 e 1, evitando um conjunto formado quase só por uma classe. A rede vê apenas as entradas e os alvos: ela precisa aproximar essa regra ajustando seus próprios pesos.

CÓDIGO COMPLETO E EXECUTÁVEL

Configure, treine, avalie e use a rede

O exemplo usa matrizes, arrays e laços for. Além do forward e do backpropagation, há dataset, épocas, custo, previsão e avaliação.

criarRede()forward()backpropagation()treinar()prever()avaliar()
rede-configuravel-completa.jsexecute do início ao fim
// Quantidade de neurônios em cada camada: 3 entradas, 4 ocultos e 2 saídas.
var tamanhos = [3, 4, 2];
var pesos = [];
var biases = [];

// As saídas usam one-hot: classe A = [1,0] e classe B = [0,1].
var dados = [
  [[0,0,0], [1,0]], [[0,0,1], [1,0]],
  [[0,1,0], [1,0]], [[1,0,0], [1,0]],
  [[0,1,1], [0,1]], [[1,0,1], [0,1]],
  [[1,1,0], [0,1]], [[1,1,1], [0,1]]
];

// A função Sigmoid será usada nas camadas ocultas e na saída.
function sigmoid(u) {
  return 1 / (1 + Math.exp(-u));
}

// Sua derivada pode ser calculada com a própria ativação a.
function derivadaSigmoid(a) {
  return a * (1 - a);
}

// Cria uma matriz de pesos para cada par de camadas.
function criarRede() {
  for (var camada = 1; camada < tamanhos.length; camada++) {
    pesos[camada - 1] = [];
    biases[camada - 1] = [];

    for (var neuronio = 0; neuronio < tamanhos[camada]; neuronio++) {
      pesos[camada - 1][neuronio] = [];
      biases[camada - 1][neuronio] = Math.random() - 0.5;

      for (var entrada = 0; entrada < tamanhos[camada - 1]; entrada++) {
        pesos[camada - 1][neuronio][entrada] = Math.random() - 0.5;
      }
    }
  }
}

// Calcula todas as ativações, da entrada até a saída.
function forward(entrada) {
  var ativacoes = [entrada];

  for (var camada = 0; camada < pesos.length; camada++) {
    var proximaAtivacao = [];

    for (var neuronio = 0; neuronio < pesos[camada].length; neuronio++) {
      var u = biases[camada][neuronio];

      for (var anterior = 0; anterior < ativacoes[camada].length; anterior++) {
        u = u + pesos[camada][neuronio][anterior] * ativacoes[camada][anterior];
      }

      proximaAtivacao[neuronio] = sigmoid(u);
    }

    ativacoes[camada + 1] = proximaAtivacao;
  }

  return ativacoes;
}

// Calcula primeiro o delta da saída e depois volta pelas camadas ocultas.
function calcularDeltas(ativacoes, alvo) {
  var deltas = [];
  var ultima = pesos.length - 1;
  deltas[ultima] = [];

  for (var saida = 0; saida < alvo.length; saida++) {
    // A divisão aparece porque o custo usa a média das saídas.
    deltas[ultima][saida] =
      (ativacoes[ultima + 1][saida] - alvo[saida]) / alvo.length;
  }

  for (var camada = ultima - 1; camada >= 0; camada--) {
    deltas[camada] = [];

    for (var neuronio = 0; neuronio < ativacoes[camada + 1].length; neuronio++) {
      var erroQueVoltou = 0;

      for (var proximo = 0; proximo < deltas[camada + 1].length; proximo++) {
        erroQueVoltou = erroQueVoltou + pesos[camada + 1][proximo][neuronio] * deltas[camada + 1][proximo];
      }

      var a = ativacoes[camada + 1][neuronio];
      deltas[camada][neuronio] = erroQueVoltou * derivadaSigmoid(a);
    }
  }

  return deltas;
}

// Usa cada delta para corrigir pesos e bias.
function ajustarPesos(ativacoes, deltas, taxa) {
  for (var camada = 0; camada < pesos.length; camada++) {
    for (var neuronio = 0; neuronio < pesos[camada].length; neuronio++) {
      for (var anterior = 0; anterior < pesos[camada][neuronio].length; anterior++) {
        var gradiente = deltas[camada][neuronio] * ativacoes[camada][anterior];
        pesos[camada][neuronio][anterior] = pesos[camada][neuronio][anterior] - taxa * gradiente;
      }

      biases[camada][neuronio] = biases[camada][neuronio] - taxa * deltas[camada][neuronio];
    }
  }
}

// Reúne as três fases do treinamento de uma amostra.
function treinarAmostra(entrada, alvo, taxa) {
  var ativacoes = forward(entrada);          // 1. previsão
  var deltas = calcularDeltas(ativacoes, alvo); // 2. backpropagation
  ajustarPesos(ativacoes, deltas, taxa);     // 3. atualização
}

function calcularCusto() {
  var soma = 0;
  for (var i = 0; i < dados.length; i++) {
    var saida = forward(dados[i][0])[pesos.length];
    for (var j = 0; j < saida.length; j++) {
      var erro = dados[i][1][j] - saida[j];
      soma += erro * erro;
    }
  }
  return soma / dados.length;
}

function treinar(epocas, taxa) {
  for (var epoca = 0; epoca < epocas; epoca++) {
    for (var i = 0; i < dados.length; i++) {
      treinarAmostra(dados[i][0], dados[i][1], taxa);
    }
  }
}

function prever(entrada) {
  var saida = forward(entrada)[pesos.length];
  if (saida[0] > saida[1]) return 'A';
  return 'B';
}

function avaliar() {
  var acertos = 0;
  for (var i = 0; i < dados.length; i++) {
    var esperada = dados[i][1][0] === 1 ? 'A' : 'B';
    if (prever(dados[i][0]) === esperada) acertos++;
  }
  return acertos / dados.length;
}

// ----------------------- USO DO MODELO -----------------------
criarRede();
console.log('Custo inicial:', calcularCusto());
treinar(5000, 0.4);
console.log('Custo final:', calcularCusto());
console.log('Acurácia:', avaliar());
console.log('Previsão para [1, 0, 1]:', prever([1, 0, 1]));