catniff 0.2.16 → 0.3.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 CHANGED
@@ -76,18 +76,18 @@ console.log(X.grad.val(), Y.grad.val());
76
76
 
77
77
  Full documentation is available in [`./docs/documentation.md`](./docs/documentation.md).
78
78
 
79
- All available APIs are in [`./src/core.ts`](./src/core.ts) if you want to dig deeper.
79
+ All available APIs are in [`./src/`](./src/) if you want to dig deeper.
80
80
 
81
81
  ## Todos
82
82
 
83
83
  * Bug fixes.
84
84
  * More tensor ops.
85
- * More detailed documentation.
86
85
  * GPU acceleration.
86
+ * Option to load more backends.
87
87
  * Some general neural net APIs.
88
+ * More detailed documentation.
88
89
  * Code refactoring.
89
90
  * Proper tests.
90
- * Option to load more backends.
91
91
 
92
92
  ## Copyrights and License
93
93
 
package/dist/core.d.ts CHANGED
@@ -158,4 +158,7 @@ export declare class Tensor {
158
158
  backward(): void;
159
159
  val(): TensorValue;
160
160
  withGrad(requiresGrad: boolean): Tensor;
161
+ detach(): Tensor;
162
+ clone(): Tensor;
163
+ replace(other: Tensor, allowShapeMismatch?: boolean): Tensor;
161
164
  }
package/dist/core.js CHANGED
@@ -1456,7 +1456,7 @@ class Tensor {
1456
1456
  }
1457
1457
  return buildNested(this.value, this.shape, this.strides);
1458
1458
  }
1459
- // Returns a copy of the tensor with gradient turned on/off and detaches from autograd
1459
+ // Returns a view of the tensor with gradient turned on/off and detaches from autograd
1460
1460
  withGrad(requiresGrad) {
1461
1461
  return new Tensor(this.value, {
1462
1462
  shape: this.shape,
@@ -1464,5 +1464,34 @@ class Tensor {
1464
1464
  requiresGrad
1465
1465
  });
1466
1466
  }
1467
+ // Returns a view of the tensor with gradient turned off and detaches from autograd
1468
+ detach() {
1469
+ return new Tensor(this.value, {
1470
+ shape: this.shape,
1471
+ strides: this.strides,
1472
+ requiresGrad: false
1473
+ });
1474
+ }
1475
+ // Returns a copy of the tensor (with new data allocation) and detaches from autograd
1476
+ clone() {
1477
+ return new Tensor(typeof this.value === "number" ? this.value : [...this.value], {
1478
+ shape: this.shape,
1479
+ strides: this.strides,
1480
+ requiresGrad: this.requiresGrad
1481
+ });
1482
+ }
1483
+ // Returns this tensor with value replaced with the value of another tensor
1484
+ replace(other, allowShapeMismatch = false) {
1485
+ // Verify shape
1486
+ if (!allowShapeMismatch) {
1487
+ for (let index = 0; index < this.shape.length; index++) {
1488
+ if (this.shape[index] !== other.shape[index]) {
1489
+ throw new Error("Shape mismatch when trying to do tensor value replacement");
1490
+ }
1491
+ }
1492
+ }
1493
+ this.value = other.value;
1494
+ return this;
1495
+ }
1467
1496
  }
1468
1497
  exports.Tensor = Tensor;
@@ -0,0 +1,42 @@
1
+ import { Tensor } from "./core";
2
+ export interface SGDOptions {
3
+ lr?: number;
4
+ momentum?: number;
5
+ dampening?: number;
6
+ weightDecay?: number;
7
+ nesterov?: boolean;
8
+ }
9
+ declare class SGD {
10
+ params: Tensor[];
11
+ momentumBuffers: Map<Tensor, Tensor>;
12
+ lr: number;
13
+ momentum: number;
14
+ dampening: number;
15
+ weightDecay: number;
16
+ nesterov: boolean;
17
+ constructor(params: Tensor[], options?: SGDOptions);
18
+ step(): void;
19
+ }
20
+ export interface AdamOptions {
21
+ lr?: number;
22
+ betas?: [number, number];
23
+ eps?: number;
24
+ weightDecay?: number;
25
+ }
26
+ declare class Adam {
27
+ params: Tensor[];
28
+ momentumBuffers: Map<Tensor, Tensor>;
29
+ velocityBuffers: Map<Tensor, Tensor>;
30
+ stepCount: number;
31
+ lr: number;
32
+ betas: [number, number];
33
+ eps: number;
34
+ weightDecay: number;
35
+ constructor(params: Tensor[], options?: AdamOptions);
36
+ step(): void;
37
+ }
38
+ export declare class Optim {
39
+ static SGD: typeof SGD;
40
+ static Adam: typeof Adam;
41
+ }
42
+ export {};
package/dist/optim.js ADDED
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Optim = void 0;
4
+ const core_1 = require("./core");
5
+ class SGD {
6
+ params;
7
+ momentumBuffers = new Map();
8
+ lr;
9
+ momentum;
10
+ dampening;
11
+ weightDecay;
12
+ nesterov;
13
+ constructor(params, options) {
14
+ this.params = params;
15
+ this.lr = options?.lr || 0.001;
16
+ this.momentum = options?.momentum || 0;
17
+ this.dampening = options?.dampening || 0;
18
+ this.weightDecay = options?.weightDecay || 0;
19
+ this.nesterov = options?.nesterov || false;
20
+ }
21
+ step() {
22
+ for (const param of this.params) {
23
+ if (!param.grad) {
24
+ throw new Error("Can not apply SGD on empty grad");
25
+ }
26
+ let grad = param.grad.detach(), detachedParam = param.detach();
27
+ // Apply weight decay (L2 regularization)
28
+ if (this.weightDecay !== 0) {
29
+ grad = grad.add(detachedParam.mul(this.weightDecay));
30
+ }
31
+ // Apply momentum
32
+ if (this.momentum !== 0) {
33
+ let buf = this.momentumBuffers.get(param);
34
+ if (!buf) {
35
+ // First time: initialize momentum buffer with current gradient
36
+ buf = grad.clone();
37
+ this.momentumBuffers.set(param, buf);
38
+ }
39
+ else {
40
+ // Update momentum buffer: buf = momentum * buf + (1 - dampening) * grad
41
+ buf = buf.mul(this.momentum).add(grad.mul(1 - this.dampening));
42
+ this.momentumBuffers.set(param, buf);
43
+ }
44
+ if (this.nesterov) {
45
+ // Nesterov momentum: grad = grad + momentum * buf
46
+ grad = grad.add(buf.mul(this.momentum));
47
+ }
48
+ else {
49
+ // Standard momentum: use momentum buffer as gradient
50
+ grad = buf;
51
+ }
52
+ }
53
+ // Update parameter: param = param - lr * grad
54
+ const newParam = detachedParam.sub(grad.mul(this.lr));
55
+ param.replace(newParam);
56
+ }
57
+ }
58
+ }
59
+ class Adam {
60
+ params;
61
+ momentumBuffers = new Map(); // First moment (m_t)
62
+ velocityBuffers = new Map(); // Second moment (v_t)
63
+ stepCount = 0;
64
+ lr;
65
+ betas;
66
+ eps;
67
+ weightDecay;
68
+ constructor(params, options) {
69
+ this.params = params;
70
+ this.lr = options?.lr || 0.001;
71
+ this.betas = options?.betas || [0.9, 0.999];
72
+ this.eps = options?.eps || 1e-8;
73
+ this.weightDecay = options?.weightDecay || 0;
74
+ }
75
+ step() {
76
+ this.stepCount++;
77
+ const beta1 = this.betas[0];
78
+ const beta2 = this.betas[1];
79
+ // Bias correction factors
80
+ const biasCorrection1 = 1 - Math.pow(beta1, this.stepCount);
81
+ const biasCorrection2 = 1 - Math.pow(beta2, this.stepCount);
82
+ for (const param of this.params) {
83
+ if (!param.grad) {
84
+ throw new Error("Can not apply Adam on empty grad");
85
+ }
86
+ let grad = param.grad.detach(), detachedParam = param.detach();
87
+ // Apply weight decay (L2 regularization)
88
+ if (this.weightDecay !== 0) {
89
+ grad = grad.add(detachedParam.mul(this.weightDecay));
90
+ }
91
+ // Get or initialize first moment buffer (momentum)
92
+ let momentumBuffer = this.momentumBuffers.get(param);
93
+ if (!momentumBuffer) {
94
+ momentumBuffer = core_1.Tensor.zerosLike(grad); // Initialize with zeros (same shape as grad)
95
+ this.momentumBuffers.set(param, momentumBuffer);
96
+ }
97
+ // Get or initialize second moment buffer (velocity)
98
+ let velocityBuffer = this.velocityBuffers.get(param);
99
+ if (!velocityBuffer) {
100
+ velocityBuffer = core_1.Tensor.zerosLike(grad); // Initialize with zeros (same shape as grad)
101
+ this.velocityBuffers.set(param, velocityBuffer);
102
+ }
103
+ // Update biased first moment estimate: m_t = β1 * m_{t-1} + (1 - β1) * g_t
104
+ momentumBuffer = momentumBuffer.mul(beta1).add(grad.mul(1 - beta1));
105
+ this.momentumBuffers.set(param, momentumBuffer);
106
+ // Update biased second moment estimate: v_t = β2 * v_{t-1} + (1 - β2) * g_t^2
107
+ velocityBuffer = velocityBuffer.mul(beta2).add(grad.pow(2).mul(1 - beta2));
108
+ this.velocityBuffers.set(param, velocityBuffer);
109
+ // Compute bias-corrected first moment: m̂_t = m_t / (1 - β1^t)
110
+ const correctedMomentum = momentumBuffer.div(biasCorrection1);
111
+ // Compute bias-corrected second moment: v̂_t = v_t / (1 - β2^t)
112
+ const correctedVelocity = velocityBuffer.div(biasCorrection2);
113
+ // Update parameters: θ_t = θ_{t-1} - α * m̂_t / (√v̂_t + ε)
114
+ const denom = correctedVelocity.sqrt().add(this.eps);
115
+ const stepSize = correctedMomentum.div(denom).mul(this.lr);
116
+ const newParam = detachedParam.sub(stepSize);
117
+ param.replace(newParam);
118
+ }
119
+ }
120
+ }
121
+ class Optim {
122
+ static SGD = SGD;
123
+ static Adam = Adam;
124
+ }
125
+ exports.Optim = Optim;
package/index.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export * from "./dist/core";
2
+ export * from "./dist/optim";
package/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  module.exports = {
2
- ...require("./dist/core")
2
+ ...require("./dist/core"),
3
+ ...require("./dist/optim")
3
4
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "catniff",
3
- "version": "0.2.16",
3
+ "version": "0.3.1",
4
4
  "description": "A small Torch-like deep learning framework for Javascript with tensor and autograd support",
5
5
  "main": "index.js",
6
6
  "scripts": {