Commit 700b4220 authored by Petr Kubeš's avatar Petr Kubeš

make better UI

parent 313d8738
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -28,8 +28,12 @@
<div class="col-md-4">
<h5>Network size</h5>
<div id="network-size-control">
Number of layers: <input type="number" name="layers" min="1" max="10" value="3" onchange="numOflayersChange(this.value)">
<div id="layer-controls"></div>
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>
</div>
</div>
<div class="col-md-4">
......@@ -42,11 +46,12 @@
<hr>
<div class="row mb-3">
<div class="col-md-auto">
<button type="button" class="btn btn-primary mr-2" onclick="train()">Train</button>
<button type="button" class="btn btn-secondary">Step</button>
<button type="button" class="btn btn-primary mr-2" onclick="train(1000)">Train</button>
<button type="button" class="btn btn-secondary" onclick="train(1)">Step</button>
<button type="button" class="btn btn-danger" onclick="reset()">Reset</button>
</div>
<div class="col-md">
TODO cost graph
Cost: <b id="cost"></b>
</div>
</div>
</div>
......
......@@ -7,38 +7,36 @@ import { NeuralCore } from './neuralNetwork/NeuralCore';
visualizer.draw(neuralCore.getNeurons(), neuralCore.getConnections());
}
(window as any).numOflayersChange = (val: number) => {
hiddenSizes = new Array(val-2);
hiddenSizes.fill(3);
initCore();
}
(window as any).addOrRemoveLayer = (add: boolean) => {
neuralCore.addLayer(add);
neuralCore.evaluate(input);
(window as any).numOfNeruonsInLayerChange = (idx: number, val) => {
if (idx == 0) {
inputSize = Number.parseInt(val);
} else if (idx == hiddenSizes.length + 1) {
outputSize = Number.parseInt(val);
} else {
hiddenSizes[idx-1] = Number.parseInt(val);
}
initCore();
updateUI();
visualizer.draw(neuralCore.getNeurons(), neuralCore.getConnections());
}
(window as any).train = () => {
neuralCore.addTrainingSet([1,1], [0,0]);
neuralCore.addTrainingSet([1,0], [1,0]);
neuralCore.addTrainingSet([0,1], [1,0]);
neuralCore.addTrainingSet([0,0], [0,0]);
(window as any).addOrRemoveNeuron = (layer: number, add: boolean) => {
for (let i=0;i<10000;i++) {
}
(window as any).train = (iters: number) => {
for (let i=0;i<iters;i++) {
neuralCore.train();
}
console.log(neuralCore.getCost())
neuralCore.evaluate(input);
updateUI();
visualizer.draw(neuralCore.getNeurons(), neuralCore.getConnections());
}
(window as any).reset = () => {
neuralCore.reset();
neuralCore.evaluate(input);
updateUI();
visualizer.draw(neuralCore.getNeurons(), neuralCore.getConnections());
}
window.onload = () => {
main();
};
......@@ -51,13 +49,19 @@ let input: number[];
let inputSize = 2;
let hiddenSizes = [3];
let outputSize = 2;
let layerControls: HTMLCanvasElement;
let inputControls: HTMLCanvasElement;
let layerControls: HTMLElement;
let inputControls: HTMLElement;
let layerCnt: HTMLElement;
let cost: HTMLElement;
const main = () => {
const content: HTMLCanvasElement = document.getElementById('content') as HTMLCanvasElement;
inputControls = document.getElementById('input-controls') as HTMLCanvasElement;
layerControls = document.getElementById('layer-controls') as HTMLCanvasElement;
inputControls = document.getElementById('input-controls');
layerControls = document.getElementById('layer-controls');
layerCnt = document.getElementById('layer-cnt');
cost = document.getElementById('cost');
visualizer = new Visualizer(content);
initCore();
......@@ -66,22 +70,49 @@ const main = () => {
const initCore = () => {
neuralCore = new NeuralCore(inputSize, hiddenSizes, outputSize);
layerControls.innerHTML = '';
layerControls.innerHTML += `Input size: <input type="number" name="layers" min="1" max="10" value="${inputSize}" onchange="numOfNeruonsInLayerChange(0, this.value)"><br>`;
for (let i = 1; i < hiddenSizes.length+1; i++) {
layerControls.innerHTML += `Layer ${i} size: <input type="number" name="layers" min="1" max="10" value="${hiddenSizes[i-1]}" onchange="numOfNeruonsInLayerChange(${i}, this.value)"><br>`;
}
layerControls.innerHTML += `Output size: <input type="number" name="layers" min="1" max="10" value="${outputSize}" onchange="numOfNeruonsInLayerChange(${hiddenSizes.length + 1}, this.value)"><br>`;
neuralCore.addTrainingSet([1,1], [1,1]);
neuralCore.addTrainingSet([1,0], [0,0]);
neuralCore.addTrainingSet([0,1], [0,0]);
neuralCore.addTrainingSet([0,0], [0,0]);
// Set default values
input = new Array(neuralCore.getInputSize());
input.fill(1);
neuralCore.evaluate(input);
updateUI();
visualizer.draw(neuralCore.getNeurons(), neuralCore.getConnections());
}
const updateUI = () => {
let content = '<table>';
content += `<tr><td align='right'>Input size:</td><td>
<div class="btn-group" role="group">
<button type="button" class="btn btn-secondary btn-sm" onclick="addOrRemoveNeuron(-1, false)">-</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="addOrRemoveNeuron(-1, true)">+</button>
</div></td></tr>`;
for (let i = 0; i < neuralCore.getLayerCnt() - 2; i++) {
content += `<tr><td align='right'>Hidden layer size:</td><td>
<div class="btn-group" role="group">
<button type="button" class="btn btn-secondary btn-sm" onclick="addOrRemoveNeuron(${i}, false)">-</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="addOrRemoveNeuron(${i}, true)">+</button>
</div></td></tr>`;
}
content += `<tr><td align='right'>Output size:</td><td>
<div class="btn-group" role="group">
<button type="button" class="btn btn-secondary btn-sm" onclick="addOrRemoveNeuron(-2, false)">-</button>
<button type="button" class="btn btn-secondary btn-sm" onclick="addOrRemoveNeuron(-2, true)">+</button>
</div></td></tr>`;
content += '</table>';
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>`;
}
neuralCore.evaluate(input);
visualizer.draw(neuralCore.getNeurons(), neuralCore.getConnections());
layerCnt.innerText = neuralCore.getLayerCnt().toString();
cost.innerHTML = neuralCore.getCost().toString();
}
\ No newline at end of file
import { Neuron } from "./Neuron";
export class Connection {
private weight: number = Math.random();
private weight: number = Math.random() * 10 - 5;
private inputNeuron: Neuron;
private outputNeuron: Neuron;
private sampleWeightChanges: number[] = [];
......@@ -15,7 +15,7 @@ export class Connection {
this.sampleWeightChanges.push(weightChange);
}
public applyAverageWeight() {
public applyAverageWeightChange() {
const change = (this.sampleWeightChanges.reduce((acc, val) => acc + val, 0) / this.sampleWeightChanges.length);
this.weight += change;
this.sampleWeightChanges = [];
......
......@@ -9,7 +9,7 @@ export class NeuralCore {
private layerCnt: number;
private rate = 5;
private rate = 1;
private biasNeuron = new Neuron('bias', true);
private neurons: Neuron[][] = [];
......@@ -59,24 +59,7 @@ export class NeuralCore {
}
// Create the Connections
for (let l = 0; l < this.layerCnt - 1; l++) {
// For each neuron in the layer add all connections to neurons in the next layer
this.connections[l] = [];
this.neurons[l + 1].forEach(nextNeuron => { // If you wonder why this cycles are switched, it's because of the bias
this.neurons[l].forEach(currNeuron => {
const connection = new Connection(currNeuron, nextNeuron)
currNeuron.addOutput(connection);
nextNeuron.addInput(connection);
this.connections[l].push(connection);
});
// Add bias neuron to each layer
const biasConnection = new Connection(this.biasNeuron, nextNeuron);
nextNeuron.addInput(biasConnection);
this.connections[l].push(biasConnection);
});
}
this.createConnections(0, this.layerCnt - 1);
}
public evaluate(input: number[]): number[] {
......@@ -149,11 +132,82 @@ export class NeuralCore {
// Uff, let's hope everything works and apply the magic
this.connections.forEach((connLayer) => {
connLayer.forEach((connection) => {
connection.applyAverageWeight();
connection.applyAverageWeightChange();
});
});
}
public addLayer(add: boolean) {
if (add) {
const newLayerSize = 3;
this.hiddenLayerSizes.push(newLayerSize);
this.layerCnt++;
// Create the new neurons
this.neurons[this.layerCnt - 2] = [];
for (let i = 0; i < newLayerSize; i++) {
this.neurons[this.layerCnt - 2][i] = new Neuron(`Neuron${this.layerCnt - 2}${i}`);
}
// Recreate the last layer
this.neurons[this.layerCnt - 1] = [];
for (let i = 0; i < this.outputSize; i++) {
this.neurons[this.layerCnt - 1][i] = new Neuron(`Neuron${this.layerCnt - 1}${i}`);
}
// Recreate all necessary connections
this.createConnections(this.layerCnt - 3, this.layerCnt - 1);
} else {
if (this.layerCnt == 2) {
return;
}
this.hiddenLayerSizes.pop();
this.layerCnt--;
this.neurons.pop();
this.connections.pop();
// Recreate the last layer
this.neurons[this.layerCnt - 1] = [];
for (let i = 0; i < this.outputSize; i++) {
this.neurons[this.layerCnt - 1][i] = new Neuron(`Neuron${this.layerCnt - 1}${i}`);
}
// Recreate all necessary connections
this.createConnections(this.layerCnt - 2, this.layerCnt - 1);
}
}
public reset() {
this.createConnections(0, this.layerCnt-1);
}
private createConnections(firstLayer, lastLayer) {
for (let l = firstLayer; l < lastLayer; l++) {
// For each neuron in the layer add all connections to neurons in the next layer
this.connections[l] = [];
// Reset input & outputs
this.neurons[l + 1].forEach(nextNeuron => {nextNeuron.resetInputs()});
this.neurons[l].forEach(nextNeuron => {nextNeuron.resetOutputs()});
this.neurons[l + 1].forEach(nextNeuron => { // If you wonder why this cycles are switched, it's because of the bias
this.neurons[l].forEach(currNeuron => {
const connection = new Connection(currNeuron, nextNeuron)
currNeuron.addOutput(connection);
nextNeuron.addInput(connection);
this.connections[l].push(connection);
});
// Add bias neuron to each layer
const biasConnection = new Connection(this.biasNeuron, nextNeuron);
nextNeuron.addInput(biasConnection);
this.connections[l].push(biasConnection);
});
}
}
public getNeurons() {
return this.neurons;
}
......@@ -165,4 +219,9 @@ export class NeuralCore {
public getInputSize() {
return this.inputSize;
}
public getLayerCnt() {
return this.layerCnt;
}
}
......@@ -5,8 +5,8 @@ export class Neuron {
private name: string;
private activation: number;
private inputs: Connection[] = [];
private outputs: Connection[] = [];
private inputs: Connection[];
private outputs: Connection[];
// The derivation of C with respect to z
private sigma: number;
......@@ -57,6 +57,14 @@ export class Neuron {
return this.outputs;
}
public resetInputs() {
this.inputs = [];
}
public resetOutputs() {
this.outputs = [];
}
public reset() {
this.isCalculated = false;
}
......
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