Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!doctype html>
<html>
<head>
  <title>Logistic Regression</title>
  <style>
    body {
      background: #eee;
      padding: 0;
      margin: 0;
      font-family: monospace;
    }
    canvas {
      background: #fff;
      width: 100%;
      image-rendering: pixelated;
    }
  </style>
</head>
<body>
<div id="plot-app"></div>
</body>
</html>
 
function sum(x, w) {
    return x.reduce((acc, _x, i) => acc + _x * w[i], 0);
  }
  function sig(z) {
    return 1 / (1 + Math.exp(-z));
  }
  function cost(scores, labels) {
    return -(1 / scores.length) * scores.reduce((acc, score, i) => {
        var y = labels[i][0];
        return  y * Math.log(score) + (1 - y) * Math.log(1 - score);
      }, 0);
  }
  function clear(ctx) {
    ctx.clearRect(0, 0, 400, 200);
  }
  function render(ctx, points) {
    points.forEach(point => {
      if (point[2] > 0) {
        ctx.fillStyle = '#3c5cff';
      } else {
        ctx.fillStyle = '#f956ff';
      }
      ctx.fillRect(Math.max(0, point[0] - 2), Math.max(0, point[1] - 2), 4, 4);
//      ctx.fillRect(point[0], point[1], 1, 1);
    })
  }
  function renderEach(ctx, params) {
    for (let y = 0; y < 200; y++) {
      for (let x = 0; x < 400; x++) {
        if (sig(sum([1, x, y], params)) < 0.5) {
          ctx.fillStyle = '#b22438';
        } else {
          ctx.fillStyle = '#fff9b6';
        }
        ctx.fillRect(x, y, 1, 1);
      }
    }
  }
  function doEpoch(samples, params, learningRate, lastCost, cycle, maxCycles) {
    var scores = samples.map(sample => sig(sum(sample, params)));
    var errors = scores.map((score, i) => score - labels[i][0]);
    var p = document.getElementById('log');
    if (!p) {
      p = document.createElement('p');
      p.setAttribute('id', 'log');
      document.body.appendChild(p);
    }
    params = params.map((param, col) => {
      return param - learningRate * errors.reduce((acc, error, row) => (acc + error * samples[row][col]), 0);
    });
    var J = cost(scores, labels);
    if (lastCost === null) {
      lastCost = J;
    }
    if (cycle % 100 === 0) {
      p.textContent = `Epoch = ${cycle}, Cost = ${J} (${J - lastCost}), Params = ${JSON.stringify(params, null, 2)}`;
      clear(ctx);
      renderEach(ctx, params);
      render(ctx, points);
    }
    if (cycle < maxCycles) {
      setTimeout(function(){
        doEpoch(samples, params, learningRate, J, cycle + 1, maxCycles);
      }, 10);
    }
  }
  var canvas = document.createElement('canvas');
  canvas.width = 400;
  canvas.height = 200;
  document.body.appendChild(canvas);
  var ctx = canvas.getContext('2d');
  var lineY = 150;
  var points = [];
  for (let i = 0; i < 500; i++) {
    var point = [parseInt(Math.random() * canvas.width, 10), parseInt(Math.random() * canvas.height, 10)];
    point.push(Number(point[1] <= lineY));
    points.push(point);
  }
  render(ctx, points);
  var samples = points.map(point => [point[0], point[1]]);
  var labels = points.map(point => [point[2]]);
  console.log('Samples');
  console.log(JSON.stringify(samples.slice(0, 10), null, 2));
  console.log('Labels');
  console.log(JSON.stringify(labels.slice(0, 10), null, 2));
  var params = [1].concat(samples[0].map(() => Math.random()));
  var withBias = samples.map(sample => [1].concat(sample));
  var epochs = 100000;
  var learningRate = 0.000003;
  var lastCost = null;
  doEpoch(withBias, params, learningRate, lastCost, 0, epochs);
Output

You can jump to the latest bin by adding /latest to your URL

Dismiss x
public
Bin info
formigonepro
0viewers