catniff 0.1.3 → 0.1.4

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 CHANGED
@@ -69,6 +69,7 @@ All available APIs are in `./src/autograd.ts`.
69
69
 
70
70
  I'm mostly just learning and playing with this currently, so there are no concrete plans yet, but here are what I currently have in mind:
71
71
 
72
+ * Fix whatever is the problem right now (there are a lot of problems right now lol).
72
73
  * GPU acceleration.
73
74
  * Some general neural net APIs.
74
75
 
@@ -33,5 +33,6 @@ export declare class Node {
33
33
  sigmoid(): Node;
34
34
  tanh(): Node;
35
35
  backward(): void;
36
- forceNode(value: Node | number): Node;
36
+ static forceNode(value: Node | number): Node;
37
+ static addGrad(node: Node, accumGrad: Tensor): void;
37
38
  }
package/dist/autograd.js CHANGED
@@ -34,35 +34,35 @@ class Node {
34
34
  this.feedBackward = () => { };
35
35
  }
36
36
  add(other) {
37
- other = this.forceNode(other);
37
+ other = Node.forceNode(other);
38
38
  const out = new Node(add(this.value, other.value), [this, other], OP.ADD);
39
39
  out.feedBackward = () => {
40
40
  // x + y d/dx = 1, note that we apply the chain rule continuously so out.grad is multiplied into our derivative
41
- this.grad = add(this.grad, out.grad);
41
+ Node.addGrad(this, out.grad);
42
42
  // x + y d/dy = 1
43
- other.grad = add(other.grad, out.grad);
43
+ Node.addGrad(other, out.grad);
44
44
  };
45
45
  return out;
46
46
  }
47
47
  sub(other) {
48
- other = this.forceNode(other);
48
+ other = Node.forceNode(other);
49
49
  const out = new Node(sub(this.value, other.value), [this, other], OP.SUB);
50
50
  out.feedBackward = () => {
51
51
  // x - y d/dx = 1
52
- this.grad = add(this.grad, out.grad);
52
+ Node.addGrad(this, out.grad);
53
53
  // x - y d/dy = -1
54
- other.grad = add(other.grad, neg(out.grad));
54
+ Node.addGrad(other, neg(out.grad));
55
55
  };
56
56
  return out;
57
57
  }
58
58
  mul(other) {
59
- other = this.forceNode(other);
59
+ other = Node.forceNode(other);
60
60
  const out = new Node(mul(this.value, other.value), [this, other], OP.MUL);
61
61
  out.feedBackward = () => {
62
62
  // x * y d/dx = y
63
- this.grad = add(this.grad, mul(out.grad, other.value));
63
+ Node.addGrad(this, mul(out.grad, other.value));
64
64
  // x + y d/dy = x
65
- other.grad = add(other.grad, mul(out.grad, this.value));
65
+ Node.addGrad(other, mul(out.grad, this.value));
66
66
  };
67
67
  return out;
68
68
  }
@@ -71,26 +71,26 @@ class Node {
71
71
  const out = new Node(pow(this.value, other.value), [this, other], OP.POW);
72
72
  out.feedBackward = () => {
73
73
  // x^a d/dx = a*x^(a-1)
74
- this.grad = add(this.grad, mul(out.grad, mul(other.value, pow(this.value, sub(other.value, 1)))));
74
+ Node.addGrad(this, mul(out.grad, mul(other.value, pow(this.value, sub(other.value, 1)))));
75
75
  // x^a d/da = x^a*lnx
76
- other.grad = add(other.grad, mul(out.grad, mul(pow(this.value, other.value), log(this.value))));
76
+ Node.addGrad(other, mul(out.grad, mul(pow(this.value, other.value), log(this.value))));
77
77
  };
78
78
  return out;
79
79
  }
80
80
  const out = new Node(pow(this.value, other), [this], OP.POW);
81
81
  out.feedBackward = () => {
82
- this.grad = add(this.grad, mul(out.grad, mul(other, pow(this.value, sub(other, 1)))));
82
+ Node.addGrad(this, mul(out.grad, mul(other, pow(this.value, sub(other, 1)))));
83
83
  };
84
84
  return out;
85
85
  }
86
86
  div(other) {
87
- other = this.forceNode(other);
87
+ other = Node.forceNode(other);
88
88
  const out = new Node(div(this.value, other.value), [this, other], OP.DIV);
89
89
  out.feedBackward = () => {
90
90
  // x/y d/dx = 1/y
91
- this.grad = add(this.grad, div(out.grad, other.value));
91
+ Node.addGrad(this, div(out.grad, other.value));
92
92
  // x/y d/dy = -x/y^2
93
- other.grad = add(other.grad, mul(out.grad, div(neg(this.value), pow(other.value, 2))));
93
+ Node.addGrad(other, mul(out.grad, div(neg(this.value), pow(other.value, 2))));
94
94
  };
95
95
  return out;
96
96
  }
@@ -98,7 +98,7 @@ class Node {
98
98
  const out = new Node(neg(this.value), [this], OP.NEG);
99
99
  out.feedBackward = () => {
100
100
  // -x d/dx = -1
101
- this.grad = add(this.grad, neg(out.grad));
101
+ Node.addGrad(this, neg(out.grad));
102
102
  };
103
103
  return out;
104
104
  }
@@ -107,7 +107,7 @@ class Node {
107
107
  const out = new Node(expResult, [this], OP.EXP);
108
108
  out.feedBackward = () => {
109
109
  // e^x d/dx = e^x
110
- this.grad = add(this.grad, mul(out.grad, expResult));
110
+ Node.addGrad(this, mul(out.grad, expResult));
111
111
  };
112
112
  return out;
113
113
  }
@@ -115,14 +115,14 @@ class Node {
115
115
  const out = new Node(log(this.value), [this], OP.LOG);
116
116
  out.feedBackward = () => {
117
117
  // lnx d/dx = 1/x
118
- this.grad = add(this.grad, div(out.grad, this.value));
118
+ Node.addGrad(this, div(out.grad, this.value));
119
119
  };
120
120
  return out;
121
121
  }
122
122
  relu() {
123
123
  const out = new Node(relu(this.value), [this], OP.RELU);
124
124
  out.feedBackward = () => {
125
- this.grad = add(this.grad, mul(out.grad, ge(this.value, 0)));
125
+ Node.addGrad(this, mul(out.grad, ge(this.value, 0)));
126
126
  };
127
127
  return out;
128
128
  }
@@ -130,7 +130,7 @@ class Node {
130
130
  const sigmoidResult = sigmoid(this.value);
131
131
  const out = new Node(sigmoidResult, [this], OP.SIGMOID);
132
132
  out.feedBackward = () => {
133
- this.grad = add(this.grad, mul(mul(out.grad, sigmoidResult), sub(1, sigmoidResult)));
133
+ Node.addGrad(this, mul(mul(out.grad, sigmoidResult), sub(1, sigmoidResult)));
134
134
  };
135
135
  return out;
136
136
  }
@@ -138,7 +138,7 @@ class Node {
138
138
  const tanhResult = tanh(this.value);
139
139
  const out = new Node(tanhResult, [this], OP.TANH);
140
140
  out.feedBackward = () => {
141
- this.grad = add(this.grad, mul(out.grad, sub(1, mul(tanhResult, tanhResult))));
141
+ Node.addGrad(this, mul(out.grad, sub(1, mul(tanhResult, tanhResult))));
142
142
  };
143
143
  return out;
144
144
  }
@@ -162,10 +162,29 @@ class Node {
162
162
  topo[index].feedBackward();
163
163
  }
164
164
  }
165
- forceNode(value) {
165
+ static forceNode(value) {
166
166
  if (value instanceof Node)
167
167
  return value;
168
168
  return new Node(value);
169
169
  }
170
+ static addGrad(node, accumGrad) {
171
+ const axesToSqueeze = [];
172
+ const axesToReduce = [];
173
+ const shape = node.shape;
174
+ const gradShape = tensor_1.TensorMath.getShape(accumGrad);
175
+ const paddedDims = gradShape.length - shape.length;
176
+ for (let i = 0; i < paddedDims; i++) {
177
+ axesToReduce.push(i);
178
+ axesToSqueeze.push(i);
179
+ }
180
+ for (let i = 0; i < shape.length; i++) {
181
+ if (shape[i] === 1 && gradShape[i + paddedDims] > 1) {
182
+ axesToReduce.push(i + paddedDims);
183
+ }
184
+ }
185
+ const reducedGrad = tensor_1.TensorMath.sum(accumGrad, axesToReduce, true);
186
+ const squeezedGrad = tensor_1.TensorMath.squeeze(reducedGrad, axesToSqueeze);
187
+ node.grad = add(squeezedGrad, node.grad);
188
+ }
170
189
  }
171
190
  exports.Node = Node;
package/dist/tensor.d.ts CHANGED
@@ -19,4 +19,8 @@ export declare class TensorMath {
19
19
  static relu(tA: Tensor): Tensor;
20
20
  static sigmoid(tA: Tensor): Tensor;
21
21
  static tanh(tA: Tensor): Tensor;
22
+ static squeezeAxis(tA: Tensor, axis: number): Tensor;
23
+ static squeeze(tA: Tensor, dims?: number[] | number): Tensor;
24
+ static sumAxis(tA: Tensor, axis: number): Tensor;
25
+ static sum(tA: Tensor, dims?: number[] | number, keepDims?: boolean): Tensor;
22
26
  }
package/dist/tensor.js CHANGED
@@ -253,5 +253,67 @@ class TensorMath {
253
253
  return tA.map(subA => TensorMath.tanh(subA));
254
254
  }
255
255
  }
256
+ static squeezeAxis(tA, axis) {
257
+ if (typeof tA === "number")
258
+ return tA;
259
+ if (axis === 0) {
260
+ return tA[0];
261
+ }
262
+ else {
263
+ return tA.map(slice => TensorMath.squeezeAxis(slice, axis - 1));
264
+ }
265
+ }
266
+ static squeeze(tA, dims) {
267
+ if (typeof tA === "number")
268
+ return tA;
269
+ if (typeof dims === "number") {
270
+ dims = [dims];
271
+ }
272
+ if (typeof dims === "undefined") {
273
+ const shape = TensorMath.getShape(tA);
274
+ dims = [];
275
+ for (let index = 0; index < shape.length; index++) {
276
+ if (shape[index] === 1) {
277
+ dims.push(index);
278
+ }
279
+ }
280
+ }
281
+ dims = [...dims].sort((a, b) => b - a);
282
+ let out = tA;
283
+ for (const axis of dims) {
284
+ out = TensorMath.squeezeAxis(out, axis);
285
+ }
286
+ return out;
287
+ }
288
+ static sumAxis(tA, axis) {
289
+ if (typeof tA === "number")
290
+ return tA;
291
+ if (axis === 0) {
292
+ let result = tA[0];
293
+ for (let i = 1; i < tA.length; i++) {
294
+ result = TensorMath.add(result, tA[i]);
295
+ }
296
+ return [result];
297
+ }
298
+ else {
299
+ return tA.map(slice => TensorMath.sumAxis(slice, axis - 1));
300
+ }
301
+ }
302
+ static sum(tA, dims, keepDims = false) {
303
+ if (typeof tA === "number")
304
+ return tA;
305
+ if (typeof dims === "number") {
306
+ dims = [dims];
307
+ }
308
+ if (typeof dims === "undefined") {
309
+ dims = Array.from({ length: TensorMath.getShape(tA).length }, (_, index) => index);
310
+ }
311
+ dims = [...dims].sort((a, b) => b - a);
312
+ let out = tA;
313
+ for (const axis of dims) {
314
+ out = TensorMath.sumAxis(out, axis);
315
+ }
316
+ return keepDims ? out : TensorMath.squeeze(out, dims);
317
+ }
256
318
  }
257
319
  exports.TensorMath = TensorMath;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "catniff",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "A cute autograd engine for Javascript",
5
5
  "main": "index.js",
6
6
  "scripts": {