@dniskav/neuron 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # @dniskav/neuron
2
+
3
+ A minimal, dependency-free neural network library built from scratch in TypeScript. Designed for learning and experimentation — every line of math is readable.
4
+
5
+ ## What's inside
6
+
7
+ | Class | Description |
8
+ |-------|-------------|
9
+ | `Neuron` | Single-input neuron. The simplest possible unit: one weight, one bias. |
10
+ | `NeuronN` | N-input neuron with Xavier initialization and sigmoid activation. |
11
+ | `Layer` | A group of `NeuronN` neurons that share the same inputs. |
12
+ | `Network` | Two-layer network (hidden + output) with backpropagation. |
13
+ | `NetworkN` | Deep network of arbitrary depth. Define your architecture as `[inputs, ...hidden, outputs]`. |
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install @dniskav/neuron
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### Single neuron — learn a threshold
24
+
25
+ ```ts
26
+ import { Neuron } from "@dniskav/neuron";
27
+
28
+ const neuron = new Neuron();
29
+
30
+ // Train: output 1 if input >= 18, else 0
31
+ for (let epoch = 0; epoch < 1000; epoch++) {
32
+ neuron.train(20, 1, 0.1); // adult
33
+ neuron.train(15, 0, 0.1); // minor
34
+ }
35
+
36
+ console.log(neuron.predict(17)); // ~0.1 (minor)
37
+ console.log(neuron.predict(25)); // ~0.9 (adult)
38
+ ```
39
+
40
+ ### N-input neuron — multi-feature classification
41
+
42
+ ```ts
43
+ import { NeuronN } from "@dniskav/neuron";
44
+
45
+ const neuron = new NeuronN(3); // 3 inputs: R, G, B
46
+
47
+ // Teach it to detect bright colors (luminance > 0.65)
48
+ neuron.train([1, 1, 1], 1, 0.05); // white → bright
49
+ neuron.train([0, 0, 0], 0, 0.05); // black → dark
50
+
51
+ console.log(neuron.predict([0.9, 0.9, 0.9])); // close to 1
52
+ ```
53
+
54
+ ### Network — non-linear classification
55
+
56
+ ```ts
57
+ import { Network } from "@dniskav/neuron";
58
+
59
+ // 2 inputs → 8 hidden neurons → 1 output
60
+ const net = new Network(2, 8, 1);
61
+
62
+ // Train on XOR (not linearly separable — needs hidden layer)
63
+ const data = [[0,0,0], [0,1,1], [1,0,1], [1,1,0]];
64
+
65
+ for (let epoch = 0; epoch < 5000; epoch++) {
66
+ for (const [x, y, t] of data) {
67
+ net.train([x, y], t, 0.3);
68
+ }
69
+ }
70
+
71
+ console.log(net.predict([0, 1])); // ~0.97
72
+ console.log(net.predict([1, 1])); // ~0.03
73
+ ```
74
+
75
+ ### NetworkN — deep network with custom architecture
76
+
77
+ ```ts
78
+ import { NetworkN } from "@dniskav/neuron";
79
+
80
+ // 3 inputs → 24 hidden → 16 hidden → 2 outputs
81
+ const net = new NetworkN([3, 24, 16, 2]);
82
+
83
+ // Train with multiple targets
84
+ net.train([0.5, 0.3, 0.8], [1, 0], 0.05);
85
+
86
+ // Predict returns an array — one value per output neuron
87
+ const [out1, out2] = net.predict([0.5, 0.3, 0.8]);
88
+ ```
89
+
90
+ ### trainWithDeltas — custom loss / physics-based gradients
91
+
92
+ `NetworkN` also exposes `trainWithDeltas` for when you compute your own output-layer deltas (e.g., from a physics simulation or a custom loss function):
93
+
94
+ ```ts
95
+ net.trainWithDeltas(inputs, [0.4, -0.2], 0.05);
96
+ ```
97
+
98
+ ## How it works
99
+
100
+ Every class uses **sigmoid** as its activation function and **gradient descent** to update weights:
101
+
102
+ ```
103
+ weight += lr × error × input
104
+ bias += lr × error
105
+ ```
106
+
107
+ `NetworkN` implements full **backpropagation** across all layers, propagating deltas from the output back to the first layer using the chain rule.
108
+
109
+ `NeuronN` uses simplified **Xavier initialization** — weights start in `[-√(1/n), +√(1/n)]` — so gradients flow well from the start of training.
110
+
111
+ ## Build
112
+
113
+ ```bash
114
+ npm run build # outputs CJS + ESM + type declarations to dist/
115
+ npm run dev # watch mode
116
+ ```
117
+
118
+ ## License
119
+
120
+ MIT
@@ -0,0 +1,40 @@
1
+ declare class Neuron {
2
+ weight: number;
3
+ bias: number;
4
+ constructor();
5
+ predict(input: number): number;
6
+ train(input: number, target: number, lr: number): void;
7
+ }
8
+
9
+ declare class NeuronN {
10
+ weights: number[];
11
+ bias: number;
12
+ constructor(nInputs: number);
13
+ predict(inputs: number[]): number;
14
+ train(inputs: number[], target: number, lr: number): void;
15
+ }
16
+
17
+ declare class Layer {
18
+ neurons: NeuronN[];
19
+ constructor(nNeurons: number, nInputs: number);
20
+ predict(inputs: number[]): number[];
21
+ }
22
+
23
+ declare class Network {
24
+ hiddenLayer: Layer;
25
+ outputLayer: Layer;
26
+ constructor(nInputs: number, nHidden: number, nOutputs: number);
27
+ predict(inputs: number[]): number;
28
+ train(inputs: number[], target: number, lr: number): number;
29
+ }
30
+
31
+ declare class NetworkN {
32
+ readonly structure: number[];
33
+ layers: Layer[];
34
+ constructor(structure: number[]);
35
+ predict(inputs: number[]): number[];
36
+ train(inputs: number[], targets: number[], lr: number): number;
37
+ trainWithDeltas(inputs: number[], outputDeltas: number[], lr: number): void;
38
+ }
39
+
40
+ export { Layer, Network, NetworkN, Neuron, NeuronN };
@@ -0,0 +1,40 @@
1
+ declare class Neuron {
2
+ weight: number;
3
+ bias: number;
4
+ constructor();
5
+ predict(input: number): number;
6
+ train(input: number, target: number, lr: number): void;
7
+ }
8
+
9
+ declare class NeuronN {
10
+ weights: number[];
11
+ bias: number;
12
+ constructor(nInputs: number);
13
+ predict(inputs: number[]): number;
14
+ train(inputs: number[], target: number, lr: number): void;
15
+ }
16
+
17
+ declare class Layer {
18
+ neurons: NeuronN[];
19
+ constructor(nNeurons: number, nInputs: number);
20
+ predict(inputs: number[]): number[];
21
+ }
22
+
23
+ declare class Network {
24
+ hiddenLayer: Layer;
25
+ outputLayer: Layer;
26
+ constructor(nInputs: number, nHidden: number, nOutputs: number);
27
+ predict(inputs: number[]): number;
28
+ train(inputs: number[], target: number, lr: number): number;
29
+ }
30
+
31
+ declare class NetworkN {
32
+ readonly structure: number[];
33
+ layers: Layer[];
34
+ constructor(structure: number[]);
35
+ predict(inputs: number[]): number[];
36
+ train(inputs: number[], targets: number[], lr: number): number;
37
+ trainWithDeltas(inputs: number[], outputDeltas: number[], lr: number): void;
38
+ }
39
+
40
+ export { Layer, Network, NetworkN, Neuron, NeuronN };
package/dist/index.js ADDED
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Layer: () => Layer,
24
+ Network: () => Network,
25
+ NetworkN: () => NetworkN,
26
+ Neuron: () => Neuron,
27
+ NeuronN: () => NeuronN
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/Neuron.ts
32
+ function sigmoid(x) {
33
+ return 1 / (1 + Math.exp(-x));
34
+ }
35
+ var Neuron = class {
36
+ constructor() {
37
+ this.weight = Math.random() * 0.1;
38
+ this.bias = Math.random() * 0.1;
39
+ }
40
+ predict(input) {
41
+ return sigmoid(input * this.weight + this.bias);
42
+ }
43
+ train(input, target, lr) {
44
+ const prediction = this.predict(input);
45
+ const error = target - prediction;
46
+ this.weight += lr * error * input;
47
+ this.bias += lr * error;
48
+ }
49
+ };
50
+
51
+ // src/NeuronN.ts
52
+ function sigmoid2(x) {
53
+ return 1 / (1 + Math.exp(-x));
54
+ }
55
+ var NeuronN = class {
56
+ constructor(nInputs) {
57
+ const limit = Math.sqrt(1 / nInputs);
58
+ this.weights = Array.from({ length: nInputs }, () => (Math.random() * 2 - 1) * limit);
59
+ this.bias = 0;
60
+ }
61
+ predict(inputs) {
62
+ const sum = inputs.reduce((acc, e, i) => acc + e * this.weights[i], this.bias);
63
+ return sigmoid2(sum);
64
+ }
65
+ train(inputs, target, lr) {
66
+ const prediction = this.predict(inputs);
67
+ const error = target - prediction;
68
+ this.weights = this.weights.map((w, i) => w + lr * error * inputs[i]);
69
+ this.bias += lr * error;
70
+ }
71
+ };
72
+
73
+ // src/Layer.ts
74
+ var Layer = class {
75
+ constructor(nNeurons, nInputs) {
76
+ this.neurons = Array.from({ length: nNeurons }, () => new NeuronN(nInputs));
77
+ }
78
+ predict(inputs) {
79
+ return this.neurons.map((n) => n.predict(inputs));
80
+ }
81
+ };
82
+
83
+ // src/Network.ts
84
+ var Network = class {
85
+ constructor(nInputs, nHidden, nOutputs) {
86
+ this.hiddenLayer = new Layer(nHidden, nInputs);
87
+ this.outputLayer = new Layer(nOutputs, nHidden);
88
+ }
89
+ predict(inputs) {
90
+ const hiddenOut = this.hiddenLayer.predict(inputs);
91
+ return this.outputLayer.predict(hiddenOut)[0];
92
+ }
93
+ // Trains on a single example. Returns the squared error.
94
+ train(inputs, target, lr) {
95
+ const hiddenOut = this.hiddenLayer.predict(inputs);
96
+ const prediction = this.outputLayer.predict(hiddenOut)[0];
97
+ const outputError = target - prediction;
98
+ const outputDelta = outputError * prediction * (1 - prediction);
99
+ const outputNeuron = this.outputLayer.neurons[0];
100
+ outputNeuron.weights = outputNeuron.weights.map(
101
+ (w, i) => w + lr * outputDelta * hiddenOut[i]
102
+ );
103
+ outputNeuron.bias += lr * outputDelta;
104
+ this.hiddenLayer.neurons.forEach((neuron, i) => {
105
+ const hiddenOut_i = hiddenOut[i];
106
+ const hiddenError = outputDelta * outputNeuron.weights[i];
107
+ const hiddenDelta = hiddenError * hiddenOut_i * (1 - hiddenOut_i);
108
+ neuron.weights = neuron.weights.map((w, j) => w + lr * hiddenDelta * inputs[j]);
109
+ neuron.bias += lr * hiddenDelta;
110
+ });
111
+ return outputError * outputError;
112
+ }
113
+ };
114
+
115
+ // src/NetworkN.ts
116
+ var NetworkN = class {
117
+ constructor(structure) {
118
+ this.structure = structure;
119
+ this.layers = [];
120
+ for (let i = 1; i < structure.length; i++) {
121
+ this.layers.push(new Layer(structure[i], structure[i - 1]));
122
+ }
123
+ }
124
+ predict(inputs) {
125
+ return this.layers.reduce((acc, layer) => layer.predict(acc), inputs);
126
+ }
127
+ // Generalized backpropagation across L layers.
128
+ // Returns the mean squared error for the example.
129
+ train(inputs, targets, lr) {
130
+ const act = [inputs];
131
+ for (const layer of this.layers) act.push(layer.predict(act[act.length - 1]));
132
+ const pred = act[act.length - 1];
133
+ let deltas = pred.map((p, i) => (targets[i] - p) * p * (1 - p));
134
+ for (let l = this.layers.length - 1; l >= 0; l--) {
135
+ const layer = this.layers[l];
136
+ const layerIn = act[l];
137
+ const prevDeltas = layerIn.map((out, j) => {
138
+ const errProp = layer.neurons.reduce((s, n, k) => s + deltas[k] * n.weights[j], 0);
139
+ return errProp * out * (1 - out);
140
+ });
141
+ layer.neurons.forEach((n, k) => {
142
+ n.weights = n.weights.map((w, j) => w + lr * deltas[k] * layerIn[j]);
143
+ n.bias += lr * deltas[k];
144
+ });
145
+ deltas = prevDeltas;
146
+ }
147
+ return pred.reduce((s, p, i) => s + (targets[i] - p) ** 2, 0) / pred.length;
148
+ }
149
+ // Backprop with externally provided output-layer deltas.
150
+ // Useful for custom loss functions (e.g. physics-based gradients).
151
+ trainWithDeltas(inputs, outputDeltas, lr) {
152
+ const act = [inputs];
153
+ for (const layer of this.layers) act.push(layer.predict(act[act.length - 1]));
154
+ let deltas = outputDeltas;
155
+ for (let l = this.layers.length - 1; l >= 0; l--) {
156
+ const layer = this.layers[l];
157
+ const layerIn = act[l];
158
+ const prevDeltas = layerIn.map((out, j) => {
159
+ const errProp = layer.neurons.reduce((s, n, k) => s + deltas[k] * n.weights[j], 0);
160
+ return errProp * out * (1 - out);
161
+ });
162
+ layer.neurons.forEach((n, k) => {
163
+ n.weights = n.weights.map((w, j) => w + lr * deltas[k] * layerIn[j]);
164
+ n.bias += lr * deltas[k];
165
+ });
166
+ deltas = prevDeltas;
167
+ }
168
+ }
169
+ };
170
+ // Annotate the CommonJS export names for ESM import in node:
171
+ 0 && (module.exports = {
172
+ Layer,
173
+ Network,
174
+ NetworkN,
175
+ Neuron,
176
+ NeuronN
177
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,146 @@
1
+ // src/Neuron.ts
2
+ function sigmoid(x) {
3
+ return 1 / (1 + Math.exp(-x));
4
+ }
5
+ var Neuron = class {
6
+ constructor() {
7
+ this.weight = Math.random() * 0.1;
8
+ this.bias = Math.random() * 0.1;
9
+ }
10
+ predict(input) {
11
+ return sigmoid(input * this.weight + this.bias);
12
+ }
13
+ train(input, target, lr) {
14
+ const prediction = this.predict(input);
15
+ const error = target - prediction;
16
+ this.weight += lr * error * input;
17
+ this.bias += lr * error;
18
+ }
19
+ };
20
+
21
+ // src/NeuronN.ts
22
+ function sigmoid2(x) {
23
+ return 1 / (1 + Math.exp(-x));
24
+ }
25
+ var NeuronN = class {
26
+ constructor(nInputs) {
27
+ const limit = Math.sqrt(1 / nInputs);
28
+ this.weights = Array.from({ length: nInputs }, () => (Math.random() * 2 - 1) * limit);
29
+ this.bias = 0;
30
+ }
31
+ predict(inputs) {
32
+ const sum = inputs.reduce((acc, e, i) => acc + e * this.weights[i], this.bias);
33
+ return sigmoid2(sum);
34
+ }
35
+ train(inputs, target, lr) {
36
+ const prediction = this.predict(inputs);
37
+ const error = target - prediction;
38
+ this.weights = this.weights.map((w, i) => w + lr * error * inputs[i]);
39
+ this.bias += lr * error;
40
+ }
41
+ };
42
+
43
+ // src/Layer.ts
44
+ var Layer = class {
45
+ constructor(nNeurons, nInputs) {
46
+ this.neurons = Array.from({ length: nNeurons }, () => new NeuronN(nInputs));
47
+ }
48
+ predict(inputs) {
49
+ return this.neurons.map((n) => n.predict(inputs));
50
+ }
51
+ };
52
+
53
+ // src/Network.ts
54
+ var Network = class {
55
+ constructor(nInputs, nHidden, nOutputs) {
56
+ this.hiddenLayer = new Layer(nHidden, nInputs);
57
+ this.outputLayer = new Layer(nOutputs, nHidden);
58
+ }
59
+ predict(inputs) {
60
+ const hiddenOut = this.hiddenLayer.predict(inputs);
61
+ return this.outputLayer.predict(hiddenOut)[0];
62
+ }
63
+ // Trains on a single example. Returns the squared error.
64
+ train(inputs, target, lr) {
65
+ const hiddenOut = this.hiddenLayer.predict(inputs);
66
+ const prediction = this.outputLayer.predict(hiddenOut)[0];
67
+ const outputError = target - prediction;
68
+ const outputDelta = outputError * prediction * (1 - prediction);
69
+ const outputNeuron = this.outputLayer.neurons[0];
70
+ outputNeuron.weights = outputNeuron.weights.map(
71
+ (w, i) => w + lr * outputDelta * hiddenOut[i]
72
+ );
73
+ outputNeuron.bias += lr * outputDelta;
74
+ this.hiddenLayer.neurons.forEach((neuron, i) => {
75
+ const hiddenOut_i = hiddenOut[i];
76
+ const hiddenError = outputDelta * outputNeuron.weights[i];
77
+ const hiddenDelta = hiddenError * hiddenOut_i * (1 - hiddenOut_i);
78
+ neuron.weights = neuron.weights.map((w, j) => w + lr * hiddenDelta * inputs[j]);
79
+ neuron.bias += lr * hiddenDelta;
80
+ });
81
+ return outputError * outputError;
82
+ }
83
+ };
84
+
85
+ // src/NetworkN.ts
86
+ var NetworkN = class {
87
+ constructor(structure) {
88
+ this.structure = structure;
89
+ this.layers = [];
90
+ for (let i = 1; i < structure.length; i++) {
91
+ this.layers.push(new Layer(structure[i], structure[i - 1]));
92
+ }
93
+ }
94
+ predict(inputs) {
95
+ return this.layers.reduce((acc, layer) => layer.predict(acc), inputs);
96
+ }
97
+ // Generalized backpropagation across L layers.
98
+ // Returns the mean squared error for the example.
99
+ train(inputs, targets, lr) {
100
+ const act = [inputs];
101
+ for (const layer of this.layers) act.push(layer.predict(act[act.length - 1]));
102
+ const pred = act[act.length - 1];
103
+ let deltas = pred.map((p, i) => (targets[i] - p) * p * (1 - p));
104
+ for (let l = this.layers.length - 1; l >= 0; l--) {
105
+ const layer = this.layers[l];
106
+ const layerIn = act[l];
107
+ const prevDeltas = layerIn.map((out, j) => {
108
+ const errProp = layer.neurons.reduce((s, n, k) => s + deltas[k] * n.weights[j], 0);
109
+ return errProp * out * (1 - out);
110
+ });
111
+ layer.neurons.forEach((n, k) => {
112
+ n.weights = n.weights.map((w, j) => w + lr * deltas[k] * layerIn[j]);
113
+ n.bias += lr * deltas[k];
114
+ });
115
+ deltas = prevDeltas;
116
+ }
117
+ return pred.reduce((s, p, i) => s + (targets[i] - p) ** 2, 0) / pred.length;
118
+ }
119
+ // Backprop with externally provided output-layer deltas.
120
+ // Useful for custom loss functions (e.g. physics-based gradients).
121
+ trainWithDeltas(inputs, outputDeltas, lr) {
122
+ const act = [inputs];
123
+ for (const layer of this.layers) act.push(layer.predict(act[act.length - 1]));
124
+ let deltas = outputDeltas;
125
+ for (let l = this.layers.length - 1; l >= 0; l--) {
126
+ const layer = this.layers[l];
127
+ const layerIn = act[l];
128
+ const prevDeltas = layerIn.map((out, j) => {
129
+ const errProp = layer.neurons.reduce((s, n, k) => s + deltas[k] * n.weights[j], 0);
130
+ return errProp * out * (1 - out);
131
+ });
132
+ layer.neurons.forEach((n, k) => {
133
+ n.weights = n.weights.map((w, j) => w + lr * deltas[k] * layerIn[j]);
134
+ n.bias += lr * deltas[k];
135
+ });
136
+ deltas = prevDeltas;
137
+ }
138
+ }
139
+ };
140
+ export {
141
+ Layer,
142
+ Network,
143
+ NetworkN,
144
+ Neuron,
145
+ NeuronN
146
+ };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@dniskav/neuron",
3
+ "version": "0.1.0",
4
+ "description": "Minimal neural network from scratch — neuron, layer, network, backpropagation. No dependencies.",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": ["dist"],
16
+ "scripts": {
17
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
18
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch"
19
+ },
20
+ "keywords": ["neural-network", "machine-learning", "backpropagation", "typescript"],
21
+ "author": "dniskav",
22
+ "license": "MIT",
23
+ "devDependencies": {
24
+ "tsup": "^8.0.0",
25
+ "typescript": "^5.0.0"
26
+ }
27
+ }