Commit 2846327c authored by Petr Kubeš's avatar Petr Kubeš

ui polishing

parent e2a2c585
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -18,42 +18,62 @@
<body>
<div class="container mt-3">
<h1>Neural network visualized</h1>
<p class="lead">Visualization of the simplest possible neural network. :)</p>
<p class="lead">Visualization of a simple neural network for strictly educational purposes.</p>
<hr>
<div class="container-fluid">
<div class="container-fluid" id="controls">
<div class="row">
<div class="col-md-4">
<div class="col-md-3">
<h5>Input values</h5>
<div id="input-controls"></div>
</div>
<div class="col-md-4">
<div class="col-md-3">
<h5>Network size</h5>
<div id="network-size-control">
Number of layers: <b id="layer-cnt">3</b>
<div class="btn-group" role="group">
<button type="button" class="btn btn-secondary btn-sm" onclick="addOrRemoveLayer(false)">-</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="addOrRemoveLayer(true)">+</button>
</div>
<div id="layer-controls" class="mt-2"></div>
<table id="layer-controls">
</table>
</div>
<div class="col-md-3">
<h5>Learning settings</h5>
<div class="row">
<label for="rate-input" class="control-label col-8">Learning rate:</label>
<input class="form-control form-control-sm col-4" id="rate-input" type="number" name="rate" min="0" max="10" step="0.1" value="1">
</div>
<div class="row">
<label for="regularization-input" class="col-8">Regularization:</label>
<select class="form-control form-control-sm col-4" id="regularization-input">
<option value="none">None</option>
<option value="L2">L2</option>
</select>
</div>
<div class="row">
<label for="regularization-input" class="col-8">Regularization rate:</label>
<input class="form-control form-control-sm col-4" id="regularization-rate-input" type="number" name="rate" min="0" max="10"
value="1">
</div>
</div>
<div class="col-md-4">
<div class="col-md-3">
<h5>Train</h5>
Training rate: <input id="rate-input" type="number" name="rate" min="0" max="10" value="1"><br>
Number of iterations: <input id="iters-input" type="number" name="rate" min="0" max="1000000" value="5000"><br>
Current iteration: TODO<br>
<div class="row">
<label for="iters-input" class="col-8">Num. of iterations:</label>
<input class="form-control form-control-sm col-4" id="iters-input" type="number" name="rate" min="0" max="20000" value="5000"
step="1000">
</div>
<div class="row">
<label for="iter-output" class="col-8">Current iteration:</label>
<b id="iter-output" class="col-4">0</b>
</div>
</div>
</div>
<hr>
<div class="row mb-3">
<div class="col-md-auto">
<button type="button" class="btn btn-primary mr-2" onclick="train(true)">Train</button>
<button type="button" class="btn btn-secondary" onclick="train(false)">Step</button>
<button type="button" class="btn btn-danger" onclick="reset()">Reset</button>
</div>
<div class="col-md">
Cost: <b id="cost"></b>
</div>
<div class="col-md-auto">
<button type="button" class="btn btn-primary mr-2" onclick="train(true)">Train</button>
<button type="button" class="btn btn-secondary" onclick="train(false)">Step</button>
<button type="button" class="btn btn-danger" onclick="reset()">Reset</button>
</div>
<div class="col-md">
Cost:
<b id="cost"></b>
</div>
</div>
</div>
......
b {
padding-right: 10px;
padding-left: 5px;
#controls label {
font-size: 0.9em;
margin: auto 0px;
}
#controls input {
margin: 5px 0px;
}
#controls b {
margin: auto 0px;
}
\ No newline at end of file
......@@ -18,7 +18,10 @@ import { NeuralCore } from './neuralNetwork/NeuralCore';
(window as any).addOrRemoveNeuron = (add: boolean, layerIdx: number) => {
neuralCore.addOrRemoveNeuron(add, layerIdx);
if (layerIdx == 0) {
input.push(1);
if (add)
input.push(1);
else
input.pop();
}
neuralCore.evaluate(input);
......@@ -30,7 +33,7 @@ import { NeuralCore } from './neuralNetwork/NeuralCore';
let iters = multipleIters ? Number.parseInt(itersInput.value) : 1;
neuralCore.setRate(Number.parseFloat(rateInput.value));
for (let i=0;i<iters;i++) {
for (let i = 0; i < iters; i++) {
neuralCore.train();
}
......@@ -61,8 +64,8 @@ let outputSize = 4;
let layerControls: HTMLElement;
let inputControls: HTMLElement;
let layerCnt: HTMLElement;
let cost: HTMLElement;
let iter: HTMLElement;
let rateInput: HTMLInputElement;
let itersInput: HTMLInputElement;
......@@ -71,7 +74,7 @@ const main = () => {
const content: HTMLCanvasElement = document.getElementById('content') as HTMLCanvasElement;
inputControls = document.getElementById('input-controls');
layerControls = document.getElementById('layer-controls');
layerCnt = document.getElementById('layer-cnt');
iter = document.getElementById('iter-output');
cost = document.getElementById('cost');
rateInput = document.getElementById('rate-input') as HTMLInputElement;
itersInput = document.getElementById('iters-input') as HTMLInputElement;
......@@ -84,9 +87,9 @@ const main = () => {
const initCore = () => {
neuralCore = new NeuralCore(inputSize, hiddenSizes, outputSize);
neuralCore.addTrainingSet([1,0,0,0], [0,1,0,0]);
neuralCore.addTrainingSet([0,1,0,0], [0,0,1,0]);
neuralCore.addTrainingSet([0,0,1,0], [0,0,0,1]);
neuralCore.addTrainingSet([1, 0, 0, 0], [0, 1, 0, 0]);
neuralCore.addTrainingSet([0, 1, 0, 0], [0, 0, 1, 0]);
neuralCore.addTrainingSet([0, 0, 1, 0], [0, 0, 0, 1]);
// Set default values
input = new Array(neuralCore.getInputSize());
......@@ -98,34 +101,52 @@ const initCore = () => {
}
const updateUI = () => {
let content = '<table>';
content += `<tr><td align='right'>Input size: <b>${inputSize}</b></td><td>
<div class="btn-group" role="group">
<button type="button" class="btn btn-secondary btn-sm" onclick="addOrRemoveNeuron(false, 0)">-</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="addOrRemoveNeuron(true, 0)">+</button>
</div></td></tr>`;
let content = addLayerControlRow(
'Layers',
neuralCore.getLayerCnt().toString(),
'addOrRemoveLayer(true)',
'addOrRemoveLayer(false)'
);
content += addLayerControlRow(
'Input size',
neuralCore.getInputSize().toString(),
'addOrRemoveNeuron(true, 0)',
'addOrRemoveNeuron(false, 0)'
);
for (let i = 0; i < neuralCore.getLayerCnt() - 2; i++) {
content += `<tr><td align='right'>Hidden layer size: <b>${neuralCore.getHiddenLayerSizes()[i]}</b></td><td>
<div class="btn-group" role="group">
<button type="button" class="btn btn-secondary btn-sm" onclick="addOrRemoveNeuron(false, ${i+1})">-</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="addOrRemoveNeuron(true, ${i+1})">+</button>
</div></td></tr>`;
content += addLayerControlRow(
'Hidden layer size',
neuralCore.getHiddenLayerSizes()[i].toString(),
`addOrRemoveNeuron(true, ${i + 1})`,
`addOrRemoveNeuron(false, ${i + 1})`
);
}
content += `<tr><td align='right'>Output size: <b>${outputSize}</b></td><td>
<div class="btn-group" role="group">
<button type="button" class="btn btn-secondary btn-sm" onclick="addOrRemoveNeuron(false, ${neuralCore.getLayerCnt()-1})">-</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="addOrRemoveNeuron(true, ${neuralCore.getLayerCnt()-1})">+</button>
</div></td></tr>`;
content += '</table>';
content += addLayerControlRow(
'Output size',
neuralCore.getOutputSize().toString(),
`addOrRemoveNeuron(true, ${neuralCore.getLayerCnt() - 1})`,
`addOrRemoveNeuron(false, ${neuralCore.getLayerCnt() - 1})`
);
layerControls.innerHTML = content;
inputControls.innerHTML = '';
for (let i = 0; i < neuralCore.getInputSize(); i++) {
inputControls.innerHTML += `Neuron ${i}: <input style="position: relative; top: 5px;" type="range" min="0" max="1" value="1" step="0.05" id="slider${i}" oninput="slide(${i}, this.value);"><br>`;
inputControls.innerHTML += `<label>Neuron ${i}:</label> <input style="position: relative; top: 5px;" type="range" min="0" max="1" value="1" step="0.05" id="slider${i}" oninput="slide(${i}, this.value);"><br>`;
}
layerCnt.innerText = neuralCore.getLayerCnt().toString();
iter.innerHTML = neuralCore.getIteration().toString();
cost.innerHTML = neuralCore.getCost().toString();
}
const addLayerControlRow = (label: string, size: string, onclickPos: string, onclickNeg: string): string => {
return `<tr><td align='right'><label>${label}:</label><b style="margin: auto 6px">${size}</b></td><td>
<div class="btn-group" role="group">
<button type="button" class="btn btn-secondary btn-sm" onclick="${onclickNeg}">-</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="${onclickPos}">+</button>
</div></td></tr>`;
}
\ No newline at end of file
......@@ -9,8 +9,10 @@ export class NeuralCore {
private layerCnt: number;
private iterCnt = 0;
private rate = 1;
private lambda = 0.003;
private lambda = 0.001;
private biasNeuron = new Neuron('bias', true);
private neurons: Neuron[][] = [];
......@@ -95,7 +97,7 @@ export class NeuralCore {
}, 0);
}, 0);
return 1 / 2 * costSum * (1 / this.trainSamples.length) +
return 1 / 2 * costSum * (1 / this.trainSamples.length) +
1 / 2 * this.lambda * this.connections.reduce( // Regularization
(prev, connLayer: Connection[]) => {
return prev + connLayer.reduce((acc, conn) => acc + conn.getWeight(), 0) ** 2
......@@ -131,7 +133,7 @@ export class NeuralCore {
const weightChange =
connection.getOutputNeuron().getSigma() *
connection.getInputNeuron().getActivation() *
this.rate -
this.rate -
this.lambda * connection.getWeight() * (1 / this.getNumberOfConnections()); // Regularization
connection.addSampleWeightChange(weightChange);
......@@ -145,6 +147,8 @@ export class NeuralCore {
connection.applyAverageWeightChange();
});
});
this.iterCnt++;
}
public addOrRemoveLayer(add: boolean) {
......@@ -184,7 +188,7 @@ export class NeuralCore {
const isInput = layerIdx == 0;
const isOutput = layerIdx == this.layerCnt - 1;
const isHidden = !isInput && !isOutput;
const sizeChange = (add) ? 1 : -1
if (isHidden) {
......@@ -208,7 +212,7 @@ export class NeuralCore {
newNeuronIdx = this.inputSize - 1;
} else {
newNeuronIdx = this.outputSize - 1;
}
}
const newNeuron = new Neuron(`Neuron${layerIdx}${newNeuronIdx}`);
this.neurons[layerIdx][newNeuronIdx] = newNeuron;
......@@ -260,7 +264,7 @@ export class NeuralCore {
// Remove the unused connections
if (!isInput) {
this.connections[layerIdx-1] = this.connections[layerIdx-1].filter((connection: Connection) => {
this.connections[layerIdx - 1] = this.connections[layerIdx - 1].filter((connection: Connection) => {
return connection.getOutputNeuron().getName() != removedNeuron.getName();
});
}
......@@ -310,7 +314,7 @@ export class NeuralCore {
}
}
private getNumberOfConnections():number {
private getNumberOfConnections(): number {
return this.connections.reduce((acc, conn) => acc + conn.length, 0);
}
......@@ -326,6 +330,10 @@ export class NeuralCore {
return this.inputSize;
}
public getOutputSize() {
return this.outputSize;
}
public getLayerCnt() {
return this.layerCnt;
}
......@@ -337,4 +345,8 @@ export class NeuralCore {
public setRate(newRate: number) {
this.rate = newRate;
}
public getIteration() {
return this.iterCnt;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment