Aprendizado e redes: do gradiente à avaliação
Construa uma rede que você consiga inspecionar completamente: um atributo, uma unidade oculta e quatro parâmetros. A entrega será um arquivo neural.mjs com dados locais, gradientes conferidos, checkpoints selecionados por validação e uma avaliação final do modelo e do baseline. A tarefa é deliberadamente simples para tornar erros de implementação e vazamento de avaliação visíveis.
Antes de executar, calcule h e p para o exemplo manual da leitura e escreva qual conjunto pode influenciar cada decisão. Depois tente implementar example e batch sem consultar a solução. Se o resultado discordar, use os testes como roteiro de diagnóstico, começando na passagem direta e só depois investigando os gradientes.
JavaScriptIA & MLAvaliaçãoMatemáticaRedesAo terminar esta aula
- Passagem direta, perda e gradiente devem representar a mesma função.
- Teste reservado avalia decisões já tomadas.
- Evidência executável deve acompanhar limites e alternativas simples.
Antes de continuar: Leitura: Aprendizado e redes: do gradiente à avaliação
Fixar dados e decisões antes do treinamento
FundamentosCrie três listas imutáveis com identificadores de origem diferentes. O treino contém x igual a −2, −1, 1 e 2; a validação contém −1.5, −0.5, 0.5 e 1.5; o teste contém −1.25, −0.25, 0.25 e 1.25. Os rótulos seguem a regra conhecida x>0. Confira que nenhum identificador se repete. Essa prova local não é uma análise geral de duplicação de dados: em uma operação, dois identificadores diferentes ainda podem representar o mesmo pedido ou cliente. Aqui ela documenta uma separação construída para o exercício.
Determine o baseline usando apenas os rótulos de treino. São duas ocorrências de cada classe, então o desempate previamente definido escolhe zero. Mantenha o limiar da rede em 0.5 e os checkpoints em cem, quinhentos e mil e quinhentos passos. Não escolha esses valores após observar o teste. O relatório deve registrar as decisões antes de exibir os resultados finais, tornando possível verificar se o conjunto reservado participou indevidamente da seleção. O baseline será avaliado junto com a rede no mesmo conjunto final, mantendo a comparação consistente.
Conferir passagem direta e estabilidade da perda
FundamentosImplemente sigmoid com duas expressões equivalentes, escolhidas pelo sinal de z. Para valores negativos, exp(z)/(1+exp(z)) evita calcular um expoente positivo enorme. forward calcula primeiro h, depois z da saída e finalmente p. Não confunda h com o rótulo nem z com probabilidade. No caso w=0,b=0,c=1,d=0,x=2, a ativação oculta deve ser 0.5 e a probabilidade aproximadamente 0.622459. Essa conta isolada localiza erros na passagem direta antes de discutir o treinamento.
A função example usa o logit para calcular uma forma estável da perda binária. Ela também valida que y seja zero ou um. Usar uma categoria dois nesse contrato não é uma classificação multiclasse válida: seria um alvo fora do problema definido. Evite corrigir probabilidades com recortes arbitrários e manter um gradiente de outra função, pois isso pode criar inconsistência entre loss e gradient. Nossa fórmula de perda e δ=p−y representam o mesmo objetivo. Essa consistência será conferida por diferença central, não apenas pela aparência das probabilidades.
Implementar backpropagation e quebrá-lo de propósito
FundamentosUse delta=p−y para a saída. O gradiente de c é delta×h e o de d é delta. Para a camada oculta, multiplique delta por c e por h(1−h). O resultado multiplica x para atualizar w e permanece igual para atualizar b. Compare as quatro componentes com o caso manual: ele mantém h exatamente em 0.5, facilitando a conferência. Os pesos que entram nessa conta são os atuais, antes de qualquer atualização. Backpropagation reutiliza os valores da passagem direta para aplicar a regra da cadeia pelas camadas.
Em batch, acumule a média das perdas e de cada gradiente. Depois altere separadamente w,b,c,d em mais e menos 0.00001 no ponto de teste numérico e compare as diferenças centrais. Retire temporariamente o fator c do gradiente oculto: os testes de w e b devem acusar a omissão. Retire o fator x: o teste de w deve falhar. Esses experimentos distinguem qual caminho da composição foi implementado incorretamente. Apenas constatar que a perda cai não identificaria uma derivada com escala errada.
Treinar, selecionar e consumir o teste uma vez
AvaliaçãoInicialize w=0.2,b=0,c=0.5,d=0. A cada passo, calcule o gradiente médio no treino e crie um novo objeto de parâmetros com taxa 0.5. Guarde cópias dos parâmetros nos três checkpoints; guardar apenas uma referência mutável poderia fazer todas as entradas apontarem para o último estado. Calcule a perda de validação de cada cópia e escolha a menor, preservando uma regra de desempate consistente. A redução da perda de treino confirma que o procedimento ajustou o exemplo; a seleção por validação responde a uma pergunta separada.
A função evaluateTest tem uma guarda que permite uma única avaliação efetiva. Ela calcula acurácia da rede, acurácia do baseline e probabilidades dos quatro pontos. Uma tentativa seguinte lança erro antes de percorrer os dados. A guarda é uma demonstração pedagógica: em um projeto real, disciplina experimental e registro de acesso são necessários para evitar inspeção manual indevida. Nossa solução alcança quatro acertos contra dois do baseline constante, mas a regra conhecida já resolve a tarefa sem treinamento. Escreva essa comparação junto ao resultado para não apresentar complexidade como benefício automático.
Use uma rubrica de cinco evidências: separação de conjuntos, conta manual, gradientes numéricos, seleção documentada e interpretação limitada do teste. Para cada item, mostre um trecho de código ou uma conta e explique o que não foi demonstrado. Retome a inferência: uma acurácia de um em quatro casos não autoriza certeza sobre uma taxa populacional. Retome também a álgebra: a entrada de cada camada começa por uma soma ponderada. Essa conexão fecha a preparação matemática e oferece uma base concreta para estudar arquiteturas maiores no restante do curso.
import assert from 'node:assert/strict';
const make=(prefix,xs)=>Object.freeze(xs.map((x,i)=>Object.freeze({id:prefix+i,x,y:Number(x>0)})));
const train=make('tr',[-2,-1,1,2]);
const validation=make('va',[-1.5,-0.5,0.5,1.5]);
const test=make('te',[-1.25,-0.25,0.25,1.25]);
const allIds=[...train,...validation,...test].map(r=>r.id);
assert.equal(new Set(allIds).size,allIds.length);
const sigmoid=z=>z>=0?1/(1+Math.exp(-z)):Math.exp(z)/(1+Math.exp(z));
const keys=['w','b','c','d']; // Entrada->oculta: w,b; oculta->saída: c,d.
function forward(theta,x){
if(!Number.isFinite(x)||!keys.every(k=>Number.isFinite(theta[k])))throw Error('entrada inválida');
const h=sigmoid(theta.w*x+theta.b),z=theta.c*h+theta.d;
return {h,z,p:sigmoid(z)};
}
function example(theta,{x,y}){
if(y!==0&&y!==1)throw Error('rótulo inválido');
const {h,z,p}=forward(theta,x);
const value=Math.max(z,0)-z*y+Math.log1p(Math.exp(-Math.abs(z)));
const delta=p-y,hiddenDelta=delta*theta.c*h*(1-h);
return {value,grad:{w:hiddenDelta*x,b:hiddenDelta,c:delta*h,d:delta}};
}
function batch(theta,rows){
if(!rows.length)throw Error('lote vazio');
const grad={w:0,b:0,c:0,d:0};let value=0;
for(const row of rows){
const item=example(theta,row);value+=item.value/rows.length;
for(const k of keys)grad[k]+=item.grad[k]/rows.length;
}
return {value,grad};
}
const near=(a,b,tol=1e-7)=>assert.ok(Math.abs(a-b)<tol,String(a)+' != '+b);
const manual=example({w:0,b:0,c:1,d:0},{x:2,y:1});
const delta=sigmoid(0.5)-1;
near(manual.grad.c,delta*0.5);near(manual.grad.d,delta);
near(manual.grad.w,delta*0.25*2);near(manual.grad.b,delta*0.25);
const point={w:0.2,b:0.1,c:0.5,d:-0.2},analytic=batch(point,train);
for(const k of keys){
const h=1e-5,plus={...point,[k]:point[k]+h},minus={...point,[k]:point[k]-h};
const numeric=(batch(plus,train).value-batch(minus,train).value)/(2*h);
near(analytic.grad[k],numeric);
}
// Baseline escolhido só com os rótulos de treino; empate definido previamente como zero.
const majority=Number(train.reduce((sum,r)=>sum+r.y,0)>train.length/2);
const checkpoints=new Set([100,500,1500]);const candidates=[];
let theta={w:0.2,b:0,c:0.5,d:0};const initial=batch(theta,train).value;
for(let epoch=1;epoch<=1500;epoch++){
const {grad}=batch(theta,train);
theta=Object.fromEntries(keys.map(k=>[k,theta[k]-0.5*grad[k]]));
if(checkpoints.has(epoch))candidates.push({epoch,theta:{...theta},validationLoss:batch(theta,validation).value});
}
assert.ok(batch(theta,train).value<initial);
const chosen=candidates.reduce((best,item)=>item.validationLoss<best.validationLoss?item:best);
let testEvaluations=0;
function evaluateTest(){
if(++testEvaluations!==1)throw Error('teste já consumido');
let modelHits=0,baselineHits=0;
const probabilities=[];
for(const row of test){
const p=forward(chosen.theta,row.x).p;probabilities.push(p);
modelHits+=Number(Number(p>=0.5)===row.y);
baselineHits+=Number(majority===row.y);
}
return {accuracy:modelHits/test.length,baseline:baselineHits/test.length,probabilities};
}
const result=evaluateTest();
assert.equal(result.baseline,0.5);assert.equal(result.accuracy,1);
assert.equal(testEvaluations,1);
assert.throws(()=>evaluateTest(),/consumido/); // Guarda demonstrada, não nova avaliação.
assert.throws(()=>example(point,{x:1,y:2}),/rótulo/);
assert.throws(()=>forward(point,NaN),/entrada/);
assert.throws(()=>batch(point,[]),/vazio/);
console.log({manual:manual.grad,candidates:candidates.map(({epoch,validationLoss})=>({epoch,validationLoss})),chosenEpoch:chosen.epoch,result});
// 4/4 neste conjunto sintético não é estimativa confiável de desempenho real.Exercício aplicado
Entregue um experimento pequeno que permita rastrear cada conta e cada decisão de avaliação.
- Separe os três conjuntos e fixe baseline, checkpoints e limiar.
- Implemente passagem direta, perda estável e gradientes dos quatro parâmetros.
- Confira o caso manual e as quatro diferenças centrais.
- Treine apenas com treino e selecione checkpoint pela validação.
- Avalie uma vez no teste, compare baseline e reconheça a regra simples.
- Demonstre rejeição de rótulo inválido, NaN, lote vazio e segunda avaliação efetiva.
Abrir resolução comentada
O arquivo completo contém dados e decisões explícitos. A verificação de identificadores demonstra a separação local; a maioria do treino define o baseline antes do teste. example calcula valor e gradientes a partir da mesma passagem direta, e batch preserva a convenção de média. O caso manual verifica a sequência da regra da cadeia, enquanto diferenças centrais verificam todas as coordenadas em outro ponto.
Os parâmetros são atualizados simultaneamente e copiados nos checkpoints. Somente a validação escolhe o candidato. evaluateTest percorre o conjunto reservado uma vez para avaliar rede e baseline; a segunda chamada falha antes da avaliação. As asserções também cobrem alvos fora de {0,1}, entrada não finita e lote vazio. Cada etapa pedida tem implementação e uma evidência executável, sem depender de biblioteca de aprendizado ou provedor externo.
A saída do programa identifica o checkpoint escolhido e mostra as probabilidades. O assert de quatro acertos confirma o comportamento determinístico nestes pontos, não uma promessa de precisão futura. O relatório deve citar a alternativa x>0, capaz de resolver os rótulos por construção, e reconhecer o tamanho mínimo do teste. A conclusão correta é que você verificou a mecânica do aprendizado e a separação das decisões; avaliar utilidade real exigiria dados representativos, custos de erro e um desenho experimental maior.
import assert from 'node:assert/strict';
const make=(prefix,xs)=>Object.freeze(xs.map((x,i)=>Object.freeze({id:prefix+i,x,y:Number(x>0)})));
const train=make('tr',[-2,-1,1,2]);
const validation=make('va',[-1.5,-0.5,0.5,1.5]);
const test=make('te',[-1.25,-0.25,0.25,1.25]);
const allIds=[...train,...validation,...test].map(r=>r.id);
assert.equal(new Set(allIds).size,allIds.length);
const sigmoid=z=>z>=0?1/(1+Math.exp(-z)):Math.exp(z)/(1+Math.exp(z));
const keys=['w','b','c','d']; // Entrada->oculta: w,b; oculta->saída: c,d.
function forward(theta,x){
if(!Number.isFinite(x)||!keys.every(k=>Number.isFinite(theta[k])))throw Error('entrada inválida');
const h=sigmoid(theta.w*x+theta.b),z=theta.c*h+theta.d;
return {h,z,p:sigmoid(z)};
}
function example(theta,{x,y}){
if(y!==0&&y!==1)throw Error('rótulo inválido');
const {h,z,p}=forward(theta,x);
const value=Math.max(z,0)-z*y+Math.log1p(Math.exp(-Math.abs(z)));
const delta=p-y,hiddenDelta=delta*theta.c*h*(1-h);
return {value,grad:{w:hiddenDelta*x,b:hiddenDelta,c:delta*h,d:delta}};
}
function batch(theta,rows){
if(!rows.length)throw Error('lote vazio');
const grad={w:0,b:0,c:0,d:0};let value=0;
for(const row of rows){
const item=example(theta,row);value+=item.value/rows.length;
for(const k of keys)grad[k]+=item.grad[k]/rows.length;
}
return {value,grad};
}
const near=(a,b,tol=1e-7)=>assert.ok(Math.abs(a-b)<tol,String(a)+' != '+b);
const manual=example({w:0,b:0,c:1,d:0},{x:2,y:1});
const delta=sigmoid(0.5)-1;
near(manual.grad.c,delta*0.5);near(manual.grad.d,delta);
near(manual.grad.w,delta*0.25*2);near(manual.grad.b,delta*0.25);
const point={w:0.2,b:0.1,c:0.5,d:-0.2},analytic=batch(point,train);
for(const k of keys){
const h=1e-5,plus={...point,[k]:point[k]+h},minus={...point,[k]:point[k]-h};
const numeric=(batch(plus,train).value-batch(minus,train).value)/(2*h);
near(analytic.grad[k],numeric);
}
// Baseline escolhido só com os rótulos de treino; empate definido previamente como zero.
const majority=Number(train.reduce((sum,r)=>sum+r.y,0)>train.length/2);
const checkpoints=new Set([100,500,1500]);const candidates=[];
let theta={w:0.2,b:0,c:0.5,d:0};const initial=batch(theta,train).value;
for(let epoch=1;epoch<=1500;epoch++){
const {grad}=batch(theta,train);
theta=Object.fromEntries(keys.map(k=>[k,theta[k]-0.5*grad[k]]));
if(checkpoints.has(epoch))candidates.push({epoch,theta:{...theta},validationLoss:batch(theta,validation).value});
}
assert.ok(batch(theta,train).value<initial);
const chosen=candidates.reduce((best,item)=>item.validationLoss<best.validationLoss?item:best);
let testEvaluations=0;
function evaluateTest(){
if(++testEvaluations!==1)throw Error('teste já consumido');
let modelHits=0,baselineHits=0;
const probabilities=[];
for(const row of test){
const p=forward(chosen.theta,row.x).p;probabilities.push(p);
modelHits+=Number(Number(p>=0.5)===row.y);
baselineHits+=Number(majority===row.y);
}
return {accuracy:modelHits/test.length,baseline:baselineHits/test.length,probabilities};
}
const result=evaluateTest();
assert.equal(result.baseline,0.5);assert.equal(result.accuracy,1);
assert.equal(testEvaluations,1);
assert.throws(()=>evaluateTest(),/consumido/); // Guarda demonstrada, não nova avaliação.
assert.throws(()=>example(point,{x:1,y:2}),/rótulo/);
assert.throws(()=>forward(point,NaN),/entrada/);
assert.throws(()=>batch(point,[]),/vazio/);
console.log({manual:manual.grad,candidates:candidates.map(({epoch,validationLoss})=>({epoch,validationLoss})),chosenEpoch:chosen.epoch,result});
// 4/4 neste conjunto sintético não é estimativa confiável de desempenho real.Como conferir seu resultado
- Identificadores não se repetem entre conjuntos.
- Quatro gradientes passam no caso manual e na diferença central.
- Checkpoint escolhido apenas pela perda de validação.
- Rede e baseline avaliados no mesmo teste, uma única vez.
- Rede acerta quatro e baseline dois neste exemplo local.
- Entradas inválidas e segundo consumo efetivo rejeitados.
- Regra simples e limites de generalização documentados.
Teste sua compreensão
Responda com suas palavras antes de abrir o comentário. Saber explicar uma decisão é parte do domínio.
1. Por que copiar os parâmetros no checkpoint?
Para preservar o estado daquela iteração.
Uma referência mutável compartilhada pode apagar a distinção entre candidatos.
2. Qual fator transmite a influência do peso de saída à camada oculta?
O peso c multiplica delta na regra da cadeia.
Omiti-lo produz derivadas ocultas de outra função.
3. A guarda de teste substitui um protocolo experimental real?
Não.
Ela demonstra um fluxo local; procedência e controle das decisões continuam necessários.
Seu progresso fica salvo neste navegador. Concluir a leitura não substitui demonstrar o domínio nos exercícios.
Referências e aprofundamento
Documentação oficial e trabalhos originais. As referências registram o escopo e as limitações para você conferir o que sustentam.
- 6.036 Introduction to Machine Learning
MIT OpenCourseWare • consulta: 2026-10-06
FundamentosProblemas de aprendizado supervisionado, representação, generalização e sobreajuste.
Limites: Página introdutória do curso de 2020; divisão e conjuntos de dados desta unidade são originais e pequenos demais para estimar desempenho real.
- CS229 Lecture Notes — Deep Learning
Stanford University / Andrew Ng e Kian Katanforoosh • consulta: 2026-10-06
FundamentosNeurônios, ativações, composição de camadas e cálculo de gradientes por backpropagation.
Limites: Notas de 2019; rede minúscula, dados sintéticos e resultados executáveis da unidade são exemplos editoriais, sem benchmark de produção.
- 18.02SC Multivariable Calculus — syllabus
MIT OpenCourseWare • consulta: 2026-10-06
FundamentosDerivadas parciais, gradientes, regra da cadeia e otimização; estudo com problemas antes da solução.
Limites: A unidade oferece preparação inicial com polinômios; não substitui cálculo de uma e várias variáveis.