Commit 4ca26f95 authored by Petr Kubeš's avatar Petr Kubeš

this works

parent 5007ed4b
This diff is collapsed.
import { NumericDictionary } from "lodash";
type Matrix = number[][];
type Vector = number[];
export class NeuralCore {
private inputSize: number;
private hiddenLayerSizes: number[];
......@@ -15,6 +10,8 @@ export class NeuralCore {
private neurons: Neuron[][] = [];
private connections: Connection[][] = [];
private trainSamples: TrainSample[] = [];
constructor(inputSize: number, hiddenLayerSizes: number[], outputSize: number) {
this.inputSize = inputSize;
this.hiddenLayerSizes = hiddenLayerSizes;
......@@ -56,7 +53,7 @@ export class NeuralCore {
// For each neuron in the layer add all connections to neurons in the next layer
this.connections[l] = [];
this.neurons[l].forEach(neuron => {
this.neurons[l+1].forEach(nextNeuron => {
this.neurons[l + 1].forEach(nextNeuron => {
const connection = new Connection(neuron, nextNeuron)
neuron.addOutput(connection);
nextNeuron.addInput(connection);
......@@ -71,15 +68,81 @@ export class NeuralCore {
throw 'Input size does not match';
}
// Reset, so each neuron is recalculated
this.neurons.forEach(layer => {layer.forEach(neuron => neuron.reset())})
this.neurons.forEach(layer => { layer.forEach(neuron => neuron.reset()) })
// Set input layer
this.neurons[0].forEach((neuron, idx) => { neuron.setInput(input[idx]) });
this.neurons[this.layerCnt-1].forEach(neuron => {
this.neurons[this.layerCnt - 1].forEach(neuron => {
neuron.calculateActivation();
});
console.log(this.connections);
console.log(this.neurons);
return this.neurons[this.layerCnt-1].map(neuron => neuron.getActivation());
return this.neurons[this.layerCnt - 1].map(neuron => neuron.getActivation());
}
public addTrainingSet(input: number[], output: number[]) {
this.trainSamples.push(new TrainSample(input, output))
}
public getCost(): number {
const costSum = this.trainSamples.reduce((costSum, sample, idx) => { // Add up all samples
this.evaluate(sample.input);
return costSum + this.neurons[this.layerCnt - 1].reduce((acc, neuron, i) => { // Add up all output neurons
return acc + (neuron.getActivation() - sample.output[i]) ** 2;
}, 0);
}, 0);
return 1 / 2 * costSum * (1 / this.trainSamples.length);
}
public train() {
this.trainSamples.forEach((sample) => {
this.evaluate(sample.input)
// Calculate sigmas of the last layer
this.neurons[this.layerCnt - 1].forEach((neuron, idx) => {
const newSigma =
(sample.output[idx] - neuron.getActivation()) * Activations.SIGMOID.der(neuron.getActivation());
neuron.setSigma(newSigma);
});
// Calculate sigmas for each neuron in the lower layers
for (let l = this.layerCnt - 2; l >= 0; l--) {
this.neurons[l].forEach((neuron) => {
const newSigma =
neuron.getOutputs().reduce((acc, connection) => {
return acc + connection.getOutputNeuron().getSigma() * connection.getWeight();
}, 0) * Activations.SIGMOID.der(neuron.getActivation());
neuron.setSigma(newSigma);
});
}
// Accumulate all weight updates
this.connections.forEach((connLayer) => {
connLayer.forEach((connection) => {
const weightChange =
connection.getOutputNeuron().getSigma() *
connection.getInputNeuron().getActivation();
connection.addSampleWeightChange(weightChange);
});
});
});
// Uff, let's hope everything works and apply the magic
this.connections.forEach((connLayer) => {
connLayer.forEach((connection) => {
connection.applyAverageWeight();
});
});
}
public getNeurons() {
return this.neurons;
}
public getConnections() {
return this.connections;
}
}
......@@ -109,6 +172,18 @@ export class Neuron {
this.inputs = null;
}
public setInput(activation: number) {
if (!this.isInput) {
throw 'Cannot set activation of non-input neuron';
}
this.activation = activation;
}
public setSigma(sigma: number) {
this.sigma = sigma;
}
public addInput(input: Connection) {
this.inputs.push(input);
};
......@@ -117,6 +192,10 @@ export class Neuron {
this.outputs.push(output);
}
public getOutputs(): Connection[] {
return this.outputs;
}
public reset() {
this.isCalculated = false;
}
......@@ -125,6 +204,10 @@ export class Neuron {
return this.activation;
}
public getSigma() {
return this.sigma;
}
public calculateActivation(): number {
if (!this.isInput && !this.isCalculated) {
this.activation = Activations.SIGMOID.output(this.inputs.reduce((acc, currConn) => acc + currConn.calculateValue(), 0));
......@@ -138,19 +221,38 @@ export class Connection {
private weight: number = Math.random();
private inputNeuron: Neuron;
private outputNeuron: Neuron;
private sampleWeightChanges: number[] = [];
constructor(input: Neuron, output: Neuron) {
this.inputNeuron = input;
this.outputNeuron = output;
}
public updateWeight(newWeight: number) {
this.weight = newWeight;
public addSampleWeightChange(weightChange: number) {
this.sampleWeightChanges.push(weightChange);
}
public applyAverageWeight() {
const change = (this.sampleWeightChanges.reduce((acc, val) => acc + val, 0) / this.sampleWeightChanges.length);
this.weight += change;
this.sampleWeightChanges = [];
}
public getWeight() {
return this.weight;
}
public calculateValue() {
return this.weight * this.inputNeuron.calculateActivation();
}
public getOutputNeuron() {
return this.outputNeuron;
}
public getInputNeuron() {
return this.inputNeuron;
}
}
export class Activations {
......@@ -162,3 +264,13 @@ export class Activations {
}
};
}
export class TrainSample {
public input: number[];
public output: number[];
constructor(input: number[], output: number[]) {
this.input = input;
this.output = output;
}
}
......@@ -11,7 +11,20 @@ let visualizer: Visualizer;
const main = () => {
const content: HTMLElement = document.getElementById('content');
visualizer = new Visualizer(content);
neuralCore = new NeuralCore(2, [5], 1);
console.log(neuralCore.evaluate([1, 1]));
neuralCore = new NeuralCore(2, [6], 1);
neuralCore.addTrainingSet([1,1], [1])
neuralCore.addTrainingSet([1,0], [1]);
neuralCore.addTrainingSet([0,1], [1]);
neuralCore.addTrainingSet([0,0], [0])
for (let i = 0; i<5000; i++) {
neuralCore.train();
console.log(neuralCore.getCost());
}
console.log(neuralCore.evaluate([1,1]));
console.log(neuralCore.evaluate([1,0]));
console.log(neuralCore.evaluate([0,1]));
console.log(neuralCore.evaluate([0,0]));
}
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