catniff 0.1.3 → 0.1.5
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 +4 -1
- package/dist/autograd.d.ts +7 -2
- package/dist/autograd.js +61 -24
- package/dist/tensor.d.ts +6 -0
- package/dist/tensor.js +100 -0
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ console.log(x.grad); // 5
|
|
|
26
26
|
|
|
27
27
|
Tensors in Catniff are either numbers (scalars/0-D tensors) or multidimensional number arrays (n-D tensors).
|
|
28
28
|
|
|
29
|
-
There is a built-in `TensorMath` class to help with
|
|
29
|
+
There is a built-in `TensorMath` class to help with tensor arithmetic, for example:
|
|
30
30
|
```js
|
|
31
31
|
const { TensorMath } = require("catniff");
|
|
32
32
|
|
|
@@ -69,6 +69,9 @@ 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).
|
|
73
|
+
* Add more tensor ops.
|
|
74
|
+
* Proper documentation.
|
|
72
75
|
* GPU acceleration.
|
|
73
76
|
* Some general neural net APIs.
|
|
74
77
|
|
package/dist/autograd.d.ts
CHANGED
|
@@ -11,7 +11,9 @@ export declare enum OP {
|
|
|
11
11
|
LOG = 8,
|
|
12
12
|
RELU = 9,
|
|
13
13
|
SIGMOID = 10,
|
|
14
|
-
TANH = 11
|
|
14
|
+
TANH = 11,
|
|
15
|
+
T = 12,
|
|
16
|
+
MM = 13
|
|
15
17
|
}
|
|
16
18
|
export declare class Node {
|
|
17
19
|
value: Tensor;
|
|
@@ -32,6 +34,9 @@ export declare class Node {
|
|
|
32
34
|
relu(): Node;
|
|
33
35
|
sigmoid(): Node;
|
|
34
36
|
tanh(): Node;
|
|
37
|
+
t(): Node;
|
|
38
|
+
mm(other: Node | number): Node;
|
|
35
39
|
backward(): void;
|
|
36
|
-
forceNode(value: Node | number): Node;
|
|
40
|
+
static forceNode(value: Node | number): Node;
|
|
41
|
+
static addGrad(node: Node, accumGrad: Tensor): void;
|
|
37
42
|
}
|
package/dist/autograd.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Node = exports.OP = void 0;
|
|
4
4
|
const tensor_1 = require("./tensor");
|
|
5
|
-
const { add, sub, mul, pow, div, neg, exp, log, relu, sigmoid, tanh, ge } = tensor_1.TensorMath;
|
|
5
|
+
const { add, sub, mul, pow, div, neg, exp, log, relu, sigmoid, tanh, ge, t, mm } = tensor_1.TensorMath;
|
|
6
6
|
var OP;
|
|
7
7
|
(function (OP) {
|
|
8
8
|
OP[OP["NONE"] = 0] = "NONE";
|
|
@@ -17,6 +17,8 @@ var OP;
|
|
|
17
17
|
OP[OP["RELU"] = 9] = "RELU";
|
|
18
18
|
OP[OP["SIGMOID"] = 10] = "SIGMOID";
|
|
19
19
|
OP[OP["TANH"] = 11] = "TANH";
|
|
20
|
+
OP[OP["T"] = 12] = "T";
|
|
21
|
+
OP[OP["MM"] = 13] = "MM";
|
|
20
22
|
})(OP || (exports.OP = OP = {}));
|
|
21
23
|
class Node {
|
|
22
24
|
value;
|
|
@@ -34,35 +36,35 @@ class Node {
|
|
|
34
36
|
this.feedBackward = () => { };
|
|
35
37
|
}
|
|
36
38
|
add(other) {
|
|
37
|
-
other =
|
|
39
|
+
other = Node.forceNode(other);
|
|
38
40
|
const out = new Node(add(this.value, other.value), [this, other], OP.ADD);
|
|
39
41
|
out.feedBackward = () => {
|
|
40
42
|
// x + y d/dx = 1, note that we apply the chain rule continuously so out.grad is multiplied into our derivative
|
|
41
|
-
|
|
43
|
+
Node.addGrad(this, out.grad);
|
|
42
44
|
// x + y d/dy = 1
|
|
43
|
-
|
|
45
|
+
Node.addGrad(other, out.grad);
|
|
44
46
|
};
|
|
45
47
|
return out;
|
|
46
48
|
}
|
|
47
49
|
sub(other) {
|
|
48
|
-
other =
|
|
50
|
+
other = Node.forceNode(other);
|
|
49
51
|
const out = new Node(sub(this.value, other.value), [this, other], OP.SUB);
|
|
50
52
|
out.feedBackward = () => {
|
|
51
53
|
// x - y d/dx = 1
|
|
52
|
-
|
|
54
|
+
Node.addGrad(this, out.grad);
|
|
53
55
|
// x - y d/dy = -1
|
|
54
|
-
|
|
56
|
+
Node.addGrad(other, neg(out.grad));
|
|
55
57
|
};
|
|
56
58
|
return out;
|
|
57
59
|
}
|
|
58
60
|
mul(other) {
|
|
59
|
-
other =
|
|
61
|
+
other = Node.forceNode(other);
|
|
60
62
|
const out = new Node(mul(this.value, other.value), [this, other], OP.MUL);
|
|
61
63
|
out.feedBackward = () => {
|
|
62
64
|
// x * y d/dx = y
|
|
63
|
-
|
|
64
|
-
// x
|
|
65
|
-
|
|
65
|
+
Node.addGrad(this, mul(out.grad, other.value));
|
|
66
|
+
// x * y d/dy = x
|
|
67
|
+
Node.addGrad(other, mul(out.grad, this.value));
|
|
66
68
|
};
|
|
67
69
|
return out;
|
|
68
70
|
}
|
|
@@ -71,26 +73,26 @@ class Node {
|
|
|
71
73
|
const out = new Node(pow(this.value, other.value), [this, other], OP.POW);
|
|
72
74
|
out.feedBackward = () => {
|
|
73
75
|
// x^a d/dx = a*x^(a-1)
|
|
74
|
-
|
|
76
|
+
Node.addGrad(this, mul(out.grad, mul(other.value, pow(this.value, sub(other.value, 1)))));
|
|
75
77
|
// x^a d/da = x^a*lnx
|
|
76
|
-
|
|
78
|
+
Node.addGrad(other, mul(out.grad, mul(pow(this.value, other.value), log(this.value))));
|
|
77
79
|
};
|
|
78
80
|
return out;
|
|
79
81
|
}
|
|
80
82
|
const out = new Node(pow(this.value, other), [this], OP.POW);
|
|
81
83
|
out.feedBackward = () => {
|
|
82
|
-
|
|
84
|
+
Node.addGrad(this, mul(out.grad, mul(other, pow(this.value, sub(other, 1)))));
|
|
83
85
|
};
|
|
84
86
|
return out;
|
|
85
87
|
}
|
|
86
88
|
div(other) {
|
|
87
|
-
other =
|
|
89
|
+
other = Node.forceNode(other);
|
|
88
90
|
const out = new Node(div(this.value, other.value), [this, other], OP.DIV);
|
|
89
91
|
out.feedBackward = () => {
|
|
90
92
|
// x/y d/dx = 1/y
|
|
91
|
-
|
|
93
|
+
Node.addGrad(this, div(out.grad, other.value));
|
|
92
94
|
// x/y d/dy = -x/y^2
|
|
93
|
-
|
|
95
|
+
Node.addGrad(other, mul(out.grad, div(neg(this.value), pow(other.value, 2))));
|
|
94
96
|
};
|
|
95
97
|
return out;
|
|
96
98
|
}
|
|
@@ -98,7 +100,7 @@ class Node {
|
|
|
98
100
|
const out = new Node(neg(this.value), [this], OP.NEG);
|
|
99
101
|
out.feedBackward = () => {
|
|
100
102
|
// -x d/dx = -1
|
|
101
|
-
|
|
103
|
+
Node.addGrad(this, neg(out.grad));
|
|
102
104
|
};
|
|
103
105
|
return out;
|
|
104
106
|
}
|
|
@@ -107,7 +109,7 @@ class Node {
|
|
|
107
109
|
const out = new Node(expResult, [this], OP.EXP);
|
|
108
110
|
out.feedBackward = () => {
|
|
109
111
|
// e^x d/dx = e^x
|
|
110
|
-
|
|
112
|
+
Node.addGrad(this, mul(out.grad, expResult));
|
|
111
113
|
};
|
|
112
114
|
return out;
|
|
113
115
|
}
|
|
@@ -115,14 +117,14 @@ class Node {
|
|
|
115
117
|
const out = new Node(log(this.value), [this], OP.LOG);
|
|
116
118
|
out.feedBackward = () => {
|
|
117
119
|
// lnx d/dx = 1/x
|
|
118
|
-
|
|
120
|
+
Node.addGrad(this, div(out.grad, this.value));
|
|
119
121
|
};
|
|
120
122
|
return out;
|
|
121
123
|
}
|
|
122
124
|
relu() {
|
|
123
125
|
const out = new Node(relu(this.value), [this], OP.RELU);
|
|
124
126
|
out.feedBackward = () => {
|
|
125
|
-
|
|
127
|
+
Node.addGrad(this, mul(out.grad, ge(this.value, 0)));
|
|
126
128
|
};
|
|
127
129
|
return out;
|
|
128
130
|
}
|
|
@@ -130,7 +132,7 @@ class Node {
|
|
|
130
132
|
const sigmoidResult = sigmoid(this.value);
|
|
131
133
|
const out = new Node(sigmoidResult, [this], OP.SIGMOID);
|
|
132
134
|
out.feedBackward = () => {
|
|
133
|
-
|
|
135
|
+
Node.addGrad(this, mul(mul(out.grad, sigmoidResult), sub(1, sigmoidResult)));
|
|
134
136
|
};
|
|
135
137
|
return out;
|
|
136
138
|
}
|
|
@@ -138,7 +140,23 @@ class Node {
|
|
|
138
140
|
const tanhResult = tanh(this.value);
|
|
139
141
|
const out = new Node(tanhResult, [this], OP.TANH);
|
|
140
142
|
out.feedBackward = () => {
|
|
141
|
-
|
|
143
|
+
Node.addGrad(this, mul(out.grad, sub(1, mul(tanhResult, tanhResult))));
|
|
144
|
+
};
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
t() {
|
|
148
|
+
const out = new Node(t(this.value), [this], OP.T);
|
|
149
|
+
out.feedBackward = () => {
|
|
150
|
+
Node.addGrad(this, t(out.grad));
|
|
151
|
+
};
|
|
152
|
+
return out;
|
|
153
|
+
}
|
|
154
|
+
mm(other) {
|
|
155
|
+
other = Node.forceNode(other);
|
|
156
|
+
const out = new Node(mm(this.value, other.value), [this, other], OP.MM);
|
|
157
|
+
out.feedBackward = () => {
|
|
158
|
+
Node.addGrad(this, mm(out.grad, t(other.value)));
|
|
159
|
+
Node.addGrad(other, mm(t(this.value), out.grad));
|
|
142
160
|
};
|
|
143
161
|
return out;
|
|
144
162
|
}
|
|
@@ -162,10 +180,29 @@ class Node {
|
|
|
162
180
|
topo[index].feedBackward();
|
|
163
181
|
}
|
|
164
182
|
}
|
|
165
|
-
forceNode(value) {
|
|
183
|
+
static forceNode(value) {
|
|
166
184
|
if (value instanceof Node)
|
|
167
185
|
return value;
|
|
168
186
|
return new Node(value);
|
|
169
187
|
}
|
|
188
|
+
static addGrad(node, accumGrad) {
|
|
189
|
+
const axesToSqueeze = [];
|
|
190
|
+
const axesToReduce = [];
|
|
191
|
+
const shape = node.shape;
|
|
192
|
+
const gradShape = tensor_1.TensorMath.getShape(accumGrad);
|
|
193
|
+
const paddedDims = gradShape.length - shape.length;
|
|
194
|
+
for (let i = 0; i < paddedDims; i++) {
|
|
195
|
+
axesToReduce.push(i);
|
|
196
|
+
axesToSqueeze.push(i);
|
|
197
|
+
}
|
|
198
|
+
for (let i = 0; i < shape.length; i++) {
|
|
199
|
+
if (shape[i] === 1 && gradShape[i + paddedDims] > 1) {
|
|
200
|
+
axesToReduce.push(i + paddedDims);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const reducedGrad = tensor_1.TensorMath.sum(accumGrad, axesToReduce, true);
|
|
204
|
+
const squeezedGrad = tensor_1.TensorMath.squeeze(reducedGrad, axesToSqueeze);
|
|
205
|
+
node.grad = add(squeezedGrad, node.grad);
|
|
206
|
+
}
|
|
170
207
|
}
|
|
171
208
|
exports.Node = Node;
|
package/dist/tensor.d.ts
CHANGED
|
@@ -19,4 +19,10 @@ 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;
|
|
26
|
+
static t(tA: Tensor): Tensor;
|
|
27
|
+
static mm(tA: Tensor, tB: Tensor): Tensor;
|
|
22
28
|
}
|
package/dist/tensor.js
CHANGED
|
@@ -253,5 +253,105 @@ 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
|
+
}
|
|
318
|
+
static t(tA) {
|
|
319
|
+
const shapeA = TensorMath.getShape(tA);
|
|
320
|
+
if (shapeA.length !== 2)
|
|
321
|
+
throw new Error("Input is not a matrix");
|
|
322
|
+
const matA = tA;
|
|
323
|
+
const matARows = matA.length;
|
|
324
|
+
const matACols = matA[0].length;
|
|
325
|
+
const matATranspose = Array.from({ length: matACols }, () => new Array(matARows).fill(0));
|
|
326
|
+
for (let i = 0; i < matARows; i++) {
|
|
327
|
+
for (let j = 0; j < matACols; j++) {
|
|
328
|
+
matATranspose[j][i] = matA[i][j];
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return matATranspose;
|
|
332
|
+
}
|
|
333
|
+
static mm(tA, tB) {
|
|
334
|
+
const shapeA = TensorMath.getShape(tA);
|
|
335
|
+
const shapeB = TensorMath.getShape(tB);
|
|
336
|
+
if (shapeA.length !== 2 || shapeB.length !== 2)
|
|
337
|
+
throw new Error("Inputs are not matrices");
|
|
338
|
+
const matA = tA;
|
|
339
|
+
const matB = tB;
|
|
340
|
+
const matARows = matA.length;
|
|
341
|
+
const matACols = matA[0].length;
|
|
342
|
+
const matBRows = matB.length;
|
|
343
|
+
const matBCols = matB[0].length;
|
|
344
|
+
if (matACols !== matBRows)
|
|
345
|
+
throw new Error("Invalid matrices shape for multiplication");
|
|
346
|
+
const matC = Array.from({ length: matARows }, () => new Array(matBCols).fill(0));
|
|
347
|
+
for (let i = 0; i < matARows; i++) {
|
|
348
|
+
for (let j = 0; j < matBCols; j++) {
|
|
349
|
+
for (let k = 0; k < matACols; k++) {
|
|
350
|
+
matC[i][j] += matA[i][k] * matB[k][j];
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return matC;
|
|
355
|
+
}
|
|
256
356
|
}
|
|
257
357
|
exports.TensorMath = TensorMath;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "catniff",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "A cute autograd engine for Javascript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -14,12 +14,18 @@
|
|
|
14
14
|
"cats",
|
|
15
15
|
"catniff",
|
|
16
16
|
"autograd",
|
|
17
|
+
"autodiff",
|
|
17
18
|
"ml",
|
|
18
19
|
"dl",
|
|
19
20
|
"ai",
|
|
20
21
|
"maths",
|
|
21
22
|
"gradient",
|
|
22
|
-
"tensors"
|
|
23
|
+
"tensors",
|
|
24
|
+
"library",
|
|
25
|
+
"framework",
|
|
26
|
+
"neural-network",
|
|
27
|
+
"machine-learning",
|
|
28
|
+
"deep-learning"
|
|
23
29
|
],
|
|
24
30
|
"author": "nguyenphuminh",
|
|
25
31
|
"license": "GPL-3.0",
|