catniff 0.3.1 → 0.4.1
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 +22 -1
- package/dist/backend.d.ts +4 -0
- package/dist/backend.js +2 -0
- package/dist/core.d.ts +5 -0
- package/dist/core.js +21 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,6 +72,28 @@ G.backward();
|
|
|
72
72
|
console.log(X.grad.val(), Y.grad.val());
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
## Optimizer
|
|
76
|
+
|
|
77
|
+
Catniff comes bundled with optimizers as well:
|
|
78
|
+
```js
|
|
79
|
+
const { Tensor, Optim } = require("catniff");
|
|
80
|
+
|
|
81
|
+
// Define some parameter
|
|
82
|
+
const w = new Tensor([1.0], { requiresGrad: true });
|
|
83
|
+
// Define a fake loss function: L = (w - 3)^2
|
|
84
|
+
const loss = w.sub(3).pow(2);
|
|
85
|
+
// Calculate gradient
|
|
86
|
+
loss.backward();
|
|
87
|
+
// Use Adam optimizer
|
|
88
|
+
const optim = new Optim.Adam([w]);
|
|
89
|
+
// Optimization step
|
|
90
|
+
optim.step();
|
|
91
|
+
|
|
92
|
+
console.log("Updated weight:", w.data); // Should move toward 3.0
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
And it can still do much more, check out the docs mentioned below for more information.
|
|
96
|
+
|
|
75
97
|
## Documentation
|
|
76
98
|
|
|
77
99
|
Full documentation is available in [`./docs/documentation.md`](./docs/documentation.md).
|
|
@@ -83,7 +105,6 @@ All available APIs are in [`./src/`](./src/) if you want to dig deeper.
|
|
|
83
105
|
* Bug fixes.
|
|
84
106
|
* More tensor ops.
|
|
85
107
|
* GPU acceleration.
|
|
86
|
-
* Option to load more backends.
|
|
87
108
|
* Some general neural net APIs.
|
|
88
109
|
* More detailed documentation.
|
|
89
110
|
* Code refactoring.
|
package/dist/backend.js
ADDED
package/dist/core.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Backend } from "./backend";
|
|
1
2
|
export type TensorValue = number | TensorValue[];
|
|
2
3
|
export interface TensorOptions {
|
|
3
4
|
shape?: readonly number[];
|
|
@@ -6,6 +7,7 @@ export interface TensorOptions {
|
|
|
6
7
|
requiresGrad?: boolean;
|
|
7
8
|
gradFn?: Function;
|
|
8
9
|
children?: Tensor[];
|
|
10
|
+
device?: string;
|
|
9
11
|
}
|
|
10
12
|
export declare class Tensor {
|
|
11
13
|
value: number[] | number;
|
|
@@ -15,6 +17,7 @@ export declare class Tensor {
|
|
|
15
17
|
requiresGrad: boolean;
|
|
16
18
|
gradFn: Function;
|
|
17
19
|
children: Tensor[];
|
|
20
|
+
device: string;
|
|
18
21
|
constructor(value: TensorValue, options?: TensorOptions);
|
|
19
22
|
static flatten(tensor: TensorValue): number[] | number;
|
|
20
23
|
static getShape(tensor: TensorValue): readonly number[];
|
|
@@ -161,4 +164,6 @@ export declare class Tensor {
|
|
|
161
164
|
detach(): Tensor;
|
|
162
165
|
clone(): Tensor;
|
|
163
166
|
replace(other: Tensor, allowShapeMismatch?: boolean): Tensor;
|
|
167
|
+
static backends: Map<string, Backend>;
|
|
168
|
+
to(device: string): Tensor;
|
|
164
169
|
}
|
package/dist/core.js
CHANGED
|
@@ -10,6 +10,7 @@ class Tensor {
|
|
|
10
10
|
requiresGrad;
|
|
11
11
|
gradFn;
|
|
12
12
|
children;
|
|
13
|
+
device;
|
|
13
14
|
constructor(value, options = {}) {
|
|
14
15
|
this.value = Tensor.flatten(value);
|
|
15
16
|
this.shape = options.shape || Tensor.getShape(value);
|
|
@@ -18,6 +19,7 @@ class Tensor {
|
|
|
18
19
|
this.requiresGrad = options.requiresGrad ?? false;
|
|
19
20
|
this.gradFn = options.gradFn || (() => { });
|
|
20
21
|
this.children = options.children || [];
|
|
22
|
+
this.device = options.device || "cpu";
|
|
21
23
|
}
|
|
22
24
|
// Utility to flatten an nD array to be 1D
|
|
23
25
|
static flatten(tensor) {
|
|
@@ -52,6 +54,8 @@ class Tensor {
|
|
|
52
54
|
}
|
|
53
55
|
// Utility to get strides from shape
|
|
54
56
|
static getStrides(shape) {
|
|
57
|
+
if (shape.length === 0)
|
|
58
|
+
return [];
|
|
55
59
|
const strides = new Array(shape.length);
|
|
56
60
|
strides[strides.length - 1] = 1;
|
|
57
61
|
for (let i = strides.length - 2; i >= 0; i--) {
|
|
@@ -282,7 +286,8 @@ class Tensor {
|
|
|
282
286
|
const outValue = outShape.length === 0 ? this.value[0] : this.value;
|
|
283
287
|
const out = new Tensor(outValue, {
|
|
284
288
|
shape: outShape,
|
|
285
|
-
strides: outStrides
|
|
289
|
+
strides: outStrides,
|
|
290
|
+
device: this.device
|
|
286
291
|
});
|
|
287
292
|
// Set up gradient if needed
|
|
288
293
|
if (this.requiresGrad) {
|
|
@@ -319,7 +324,7 @@ class Tensor {
|
|
|
319
324
|
newDimStride = this.strides[dim] * this.shape[dim];
|
|
320
325
|
}
|
|
321
326
|
newStrides.splice(dim, 0, newDimStride);
|
|
322
|
-
const out = new Tensor(thisValue, { shape: newShape, strides: newStrides });
|
|
327
|
+
const out = new Tensor(thisValue, { shape: newShape, strides: newStrides, device: this.device });
|
|
323
328
|
// Set up gradient if needed
|
|
324
329
|
if (this.requiresGrad) {
|
|
325
330
|
out.requiresGrad = true;
|
|
@@ -981,7 +986,7 @@ class Tensor {
|
|
|
981
986
|
[newShape[dim1], newShape[dim2]] = [newShape[dim2], newShape[dim1]];
|
|
982
987
|
[newStrides[dim1], newStrides[dim2]] = [newStrides[dim2], newStrides[dim1]];
|
|
983
988
|
// Create new tensor with same data but swapped shape/strides
|
|
984
|
-
const out = new Tensor(this.value, { shape: newShape, strides: newStrides });
|
|
989
|
+
const out = new Tensor(this.value, { shape: newShape, strides: newStrides, device: this.device });
|
|
985
990
|
out.requiresGrad = this.requiresGrad;
|
|
986
991
|
// Handle gradient if needed
|
|
987
992
|
if (this.requiresGrad) {
|
|
@@ -1160,32 +1165,7 @@ class Tensor {
|
|
|
1160
1165
|
if (this.shape.length !== 2 || other.shape.length !== 1) {
|
|
1161
1166
|
throw new Error("Input is not a 2D and 1D tensor pair");
|
|
1162
1167
|
}
|
|
1163
|
-
|
|
1164
|
-
const thisMat = new Tensor(this.value, { shape: this.shape, strides: this.strides });
|
|
1165
|
-
const otherMat = new Tensor(other.value, { shape: [other.shape[0], 1], strides: [other.strides[0], 1] });
|
|
1166
|
-
const out = thisMat.mm(otherMat).squeeze(1);
|
|
1167
|
-
// Handle grad with original tensors
|
|
1168
|
-
if (this.requiresGrad) {
|
|
1169
|
-
out.requiresGrad = true;
|
|
1170
|
-
out.children.push(this);
|
|
1171
|
-
}
|
|
1172
|
-
if (other.requiresGrad) {
|
|
1173
|
-
out.requiresGrad = true;
|
|
1174
|
-
out.children.push(other);
|
|
1175
|
-
}
|
|
1176
|
-
if (out.requiresGrad) {
|
|
1177
|
-
out.gradFn = () => {
|
|
1178
|
-
// Disable gradient collecting of gradients themselves
|
|
1179
|
-
const outGrad = out.grad.withGrad(false);
|
|
1180
|
-
const selfNoGrad = this.withGrad(false);
|
|
1181
|
-
const otherNoGrad = other.withGrad(false);
|
|
1182
|
-
if (this.requiresGrad)
|
|
1183
|
-
Tensor.addGrad(this, outGrad.unsqueeze(1).mm(otherNoGrad.unsqueeze(0)));
|
|
1184
|
-
if (other.requiresGrad)
|
|
1185
|
-
Tensor.addGrad(other, selfNoGrad.t().mv(outGrad));
|
|
1186
|
-
};
|
|
1187
|
-
}
|
|
1188
|
-
return out;
|
|
1168
|
+
return this.mm(other.unsqueeze(1)).squeeze(1);
|
|
1189
1169
|
}
|
|
1190
1170
|
// General matrix multiplication with different shapes
|
|
1191
1171
|
matmul(other) {
|
|
@@ -1461,6 +1441,7 @@ class Tensor {
|
|
|
1461
1441
|
return new Tensor(this.value, {
|
|
1462
1442
|
shape: this.shape,
|
|
1463
1443
|
strides: this.strides,
|
|
1444
|
+
device: this.device,
|
|
1464
1445
|
requiresGrad
|
|
1465
1446
|
});
|
|
1466
1447
|
}
|
|
@@ -1469,6 +1450,7 @@ class Tensor {
|
|
|
1469
1450
|
return new Tensor(this.value, {
|
|
1470
1451
|
shape: this.shape,
|
|
1471
1452
|
strides: this.strides,
|
|
1453
|
+
device: this.device,
|
|
1472
1454
|
requiresGrad: false
|
|
1473
1455
|
});
|
|
1474
1456
|
}
|
|
@@ -1493,5 +1475,15 @@ class Tensor {
|
|
|
1493
1475
|
this.value = other.value;
|
|
1494
1476
|
return this;
|
|
1495
1477
|
}
|
|
1478
|
+
// Holds all available backends
|
|
1479
|
+
static backends = new Map();
|
|
1480
|
+
// Op to transfer tensor to another device
|
|
1481
|
+
to(device) {
|
|
1482
|
+
const backend = Tensor.backends.get(device);
|
|
1483
|
+
if (backend && backend.transfer) {
|
|
1484
|
+
return backend.transfer(this);
|
|
1485
|
+
}
|
|
1486
|
+
throw new Error(`No device found to transfer tensor to or a handler is not implemented for device.`);
|
|
1487
|
+
}
|
|
1496
1488
|
}
|
|
1497
1489
|
exports.Tensor = Tensor;
|