LABORATÓRIO · SVM LINEAR
Treine a margem, não apenas a classificação.
Duas características descrevem cada amostra. Observe a reta u=0, os limites u=−1 e u=+1 e os pontos que ainda geram perda hinge.
u = w₁x₁ + w₂x₂ + bm = y × uL = máx(0, 1 − m)m ≤ 1 → ajustarPlano de decisão
Os círculos e quadrados verdes estão sobre a margem ou dentro dela.
classe −1classe +1vetor de suporteu=0u=±1
Matemática da última amostra
Os valores anteriores são usados para calcular a correção mostrada.
Clique em “Treinar próxima amostra” para acompanhar uma atualização completa.
Perda × resposta
A perda hinge deve cair enquanto a acurácia se aproxima de 100%.
perda hingeacurácia
Estado de todas as amostras
A linha verde indica margem funcional m≤1,05. A pequena tolerância visual mantém destacados os pontos praticamente sobre u=±1.
CÓDIGO COMPLETO E EXECUTÁVEL
Treine, avalie e use uma SVM linear
O programa contém dados de treino e teste, otimização da perda hinge, parada antecipada, avaliação e previsão de uma nova entrada.
potencial()treinarAmostra()treinar()avaliar()prever()
// Cada amostra contém [x1, x2, y].
// As classes da SVM binária são -1 e +1.
var dados = [
[-3.0, -1.0, -1],
[-2.0, 1.0, -1],
[ 1.0, -0.4, +1],
[ 2.5, 0.8, +1]
];
var teste = [
[-2.5, 0.2, -1],
[-1.5, -0.8, -1],
[ 1.5, 0.1, +1],
[ 3.0, 1.2, +1]
];
var pesos = [0, 0];
var bias = 0;
// Calcula u = w1*x1 + w2*x2 + b.
function potencial(amostra) {
return pesos[0] * amostra[0]
+ pesos[1] * amostra[1]
+ bias;
}
// O sinal de u determina a classe prevista.
function prever(amostra) {
if (potencial(amostra) >= 0) {
return +1;
}
return -1;
}
// Calcula L = max(0, 1 - y*u).
function perdaHinge(amostra) {
var margem = amostra[2] * potencial(amostra);
return Math.max(0, 1 - margem);
}
// Faz uma atualização pela margem suave.
function treinarAmostra(amostra, taxa, C, lambda) {
var x1 = amostra[0];
var x2 = amostra[1];
var y = amostra[2];
var margem = y * potencial(amostra);
// A regularização reduz um pouco os pesos.
pesos[0] = (1 - taxa * lambda) * pesos[0];
pesos[1] = (1 - taxa * lambda) * pesos[1];
// Se m < 1, a amostra viola a margem e corrige o modelo.
if (margem < 1) {
pesos[0] = pesos[0] + taxa * C * y * x1;
pesos[1] = pesos[1] + taxa * C * y * x2;
bias = bias + taxa * C * y;
}
}
// Uma época apresenta todas as amostras uma vez.
function treinarEpoca(taxa, C, lambda) {
for (var i = 0; i < dados.length; i++) {
treinarAmostra(dados[i], taxa, C, lambda);
}
}
// Repete épocas e interrompe quando a perda praticamente não muda.
function treinar(maximoEpocas, taxa, C, lambda) {
var perdaAnterior = Infinity;
for (var epoca = 1; epoca <= maximoEpocas; epoca++) {
treinarEpoca(taxa, C, lambda);
var perdaAtual = 0;
for (var i = 0; i < dados.length; i++) {
perdaAtual = perdaAtual + perdaHinge(dados[i]);
}
perdaAtual = perdaAtual / dados.length;
if (Math.abs(perdaAnterior - perdaAtual) < 0.0001) return epoca;
perdaAnterior = perdaAtual;
}
return maximoEpocas;
}
function avaliar(amostras) {
var acertos = 0;
for (var i = 0; i < amostras.length; i++) {
if (prever(amostras[i]) === amostras[i][2]) acertos++;
}
return acertos / amostras.length;
}
// ----------------------- USO DO MODELO -----------------------
var epocasUsadas = treinar(500, 0.02, 1, 0.01);
console.log('Épocas:', epocasUsadas);
console.log('Pesos e bias:', pesos, bias);
console.log('Acurácia de teste:', avaliar(teste));
console.log('Previsão para [2, 0.5]:', prever([2, 0.5]));