<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
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |