parakeet.ts 1.0.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/LICENSE +21 -0
- package/README.md +286 -0
- package/dist/alignment.d.ts +36 -0
- package/dist/alignment.d.ts.map +1 -0
- package/dist/alignment.js +202 -0
- package/dist/alignment.js.map +1 -0
- package/dist/audio.d.ts +59 -0
- package/dist/audio.d.ts.map +1 -0
- package/dist/audio.js +324 -0
- package/dist/audio.js.map +1 -0
- package/dist/backend.d.ts +61 -0
- package/dist/backend.d.ts.map +1 -0
- package/dist/backend.js +14 -0
- package/dist/backend.js.map +1 -0
- package/dist/decode.d.ts +38 -0
- package/dist/decode.d.ts.map +1 -0
- package/dist/decode.js +124 -0
- package/dist/decode.js.map +1 -0
- package/dist/hub.d.ts +18 -0
- package/dist/hub.d.ts.map +1 -0
- package/dist/hub.js +86 -0
- package/dist/hub.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/load.d.ts +51 -0
- package/dist/load.d.ts.map +1 -0
- package/dist/load.js +34 -0
- package/dist/load.js.map +1 -0
- package/dist/mlx/attention.d.ts +61 -0
- package/dist/mlx/attention.d.ts.map +1 -0
- package/dist/mlx/attention.js +330 -0
- package/dist/mlx/attention.js.map +1 -0
- package/dist/mlx/audio.d.ts +46 -0
- package/dist/mlx/audio.d.ts.map +1 -0
- package/dist/mlx/audio.js +309 -0
- package/dist/mlx/audio.js.map +1 -0
- package/dist/mlx/backend.d.ts +22 -0
- package/dist/mlx/backend.d.ts.map +1 -0
- package/dist/mlx/backend.js +67 -0
- package/dist/mlx/backend.js.map +1 -0
- package/dist/mlx/cache.d.ts +23 -0
- package/dist/mlx/cache.d.ts.map +1 -0
- package/dist/mlx/cache.js +98 -0
- package/dist/mlx/cache.js.map +1 -0
- package/dist/mlx/cli.d.ts +12 -0
- package/dist/mlx/cli.d.ts.map +1 -0
- package/dist/mlx/cli.js +145 -0
- package/dist/mlx/cli.js.map +1 -0
- package/dist/mlx/conformer.d.ts +81 -0
- package/dist/mlx/conformer.d.ts.map +1 -0
- package/dist/mlx/conformer.js +315 -0
- package/dist/mlx/conformer.js.map +1 -0
- package/dist/mlx/index.d.ts +6 -0
- package/dist/mlx/index.d.ts.map +1 -0
- package/dist/mlx/index.js +9 -0
- package/dist/mlx/index.js.map +1 -0
- package/dist/mlx/load.d.ts +27 -0
- package/dist/mlx/load.d.ts.map +1 -0
- package/dist/mlx/load.js +161 -0
- package/dist/mlx/load.js.map +1 -0
- package/dist/mlx/nn.d.ts +121 -0
- package/dist/mlx/nn.d.ts.map +1 -0
- package/dist/mlx/nn.js +511 -0
- package/dist/mlx/nn.js.map +1 -0
- package/dist/mlx/rnnt.d.ts +59 -0
- package/dist/mlx/rnnt.d.ts.map +1 -0
- package/dist/mlx/rnnt.js +233 -0
- package/dist/mlx/rnnt.js.map +1 -0
- package/dist/mlx/server.d.ts +34 -0
- package/dist/mlx/server.d.ts.map +1 -0
- package/dist/mlx/server.js +115 -0
- package/dist/mlx/server.js.map +1 -0
- package/dist/mlx/utils.d.ts +19 -0
- package/dist/mlx/utils.d.ts.map +1 -0
- package/dist/mlx/utils.js +103 -0
- package/dist/mlx/utils.js.map +1 -0
- package/dist/model.d.ts +112 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +196 -0
- package/dist/model.js.map +1 -0
- package/dist/onnx/backend.d.ts +55 -0
- package/dist/onnx/backend.d.ts.map +1 -0
- package/dist/onnx/backend.js +111 -0
- package/dist/onnx/backend.js.map +1 -0
- package/dist/onnx/index.d.ts +7 -0
- package/dist/onnx/index.d.ts.map +1 -0
- package/dist/onnx/index.js +6 -0
- package/dist/onnx/index.js.map +1 -0
- package/dist/onnx/parakeet.d.ts +44 -0
- package/dist/onnx/parakeet.d.ts.map +1 -0
- package/dist/onnx/parakeet.js +135 -0
- package/dist/onnx/parakeet.js.map +1 -0
- package/dist/tokenizer.d.ts +6 -0
- package/dist/tokenizer.d.ts.map +1 -0
- package/dist/tokenizer.js +8 -0
- package/dist/tokenizer.js.map +1 -0
- package/package.json +85 -0
package/dist/mlx/nn.d.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Neural network primitives implemented on top of @mlx-node/core MxArray.
|
|
3
|
+
*
|
|
4
|
+
* Design notes:
|
|
5
|
+
* - Each layer stores its weights as MxArray fields.
|
|
6
|
+
* - Conv1d and Conv2d are implemented via im2col (take + reshape + matmul),
|
|
7
|
+
* keeping all computation on-device.
|
|
8
|
+
* - LSTM is implemented with manual gate arithmetic so no native LSTM kernel
|
|
9
|
+
* is required.
|
|
10
|
+
* - Weight loading uses a flat Map<string, {data, shape}> that mirrors the
|
|
11
|
+
* safetensors key structure produced by mlx-python's tree_flatten.
|
|
12
|
+
*/
|
|
13
|
+
import { MxArray } from '@mlx-node/core';
|
|
14
|
+
export interface TensorEntry {
|
|
15
|
+
data: Float32Array;
|
|
16
|
+
shape: number[];
|
|
17
|
+
}
|
|
18
|
+
export type WeightMap = Map<string, TensorEntry>;
|
|
19
|
+
export declare abstract class Module {
|
|
20
|
+
abstract loadWeights(weights: WeightMap, prefix: string): void;
|
|
21
|
+
}
|
|
22
|
+
export declare class Linear extends Module {
|
|
23
|
+
inFeatures: number;
|
|
24
|
+
outFeatures: number;
|
|
25
|
+
useBias: boolean;
|
|
26
|
+
weight: MxArray;
|
|
27
|
+
bias: MxArray | null;
|
|
28
|
+
constructor(inFeatures: number, outFeatures: number, useBias?: boolean);
|
|
29
|
+
forward(x: MxArray): MxArray;
|
|
30
|
+
loadWeights(weights: WeightMap, prefix: string): void;
|
|
31
|
+
}
|
|
32
|
+
export declare class Embedding extends Module {
|
|
33
|
+
numEmbeddings: number;
|
|
34
|
+
embeddingDim: number;
|
|
35
|
+
weight: MxArray;
|
|
36
|
+
constructor(numEmbeddings: number, embeddingDim: number);
|
|
37
|
+
forward(indices: MxArray): MxArray;
|
|
38
|
+
loadWeights(weights: WeightMap, prefix: string): void;
|
|
39
|
+
}
|
|
40
|
+
export declare class LayerNorm extends Module {
|
|
41
|
+
features: number;
|
|
42
|
+
eps: number;
|
|
43
|
+
weight: MxArray;
|
|
44
|
+
bias: MxArray;
|
|
45
|
+
constructor(features: number, eps?: number);
|
|
46
|
+
forward(x: MxArray): MxArray;
|
|
47
|
+
loadWeights(weights: WeightMap, prefix: string): void;
|
|
48
|
+
}
|
|
49
|
+
export declare class BatchNorm extends Module {
|
|
50
|
+
features: number;
|
|
51
|
+
eps: number;
|
|
52
|
+
weight: MxArray;
|
|
53
|
+
bias: MxArray;
|
|
54
|
+
runningMean: MxArray;
|
|
55
|
+
runningVar: MxArray;
|
|
56
|
+
constructor(features: number, eps?: number);
|
|
57
|
+
forward(x: MxArray): MxArray;
|
|
58
|
+
loadWeights(weights: WeightMap, prefix: string): void;
|
|
59
|
+
}
|
|
60
|
+
export declare class Conv1d extends Module {
|
|
61
|
+
inChannels: number;
|
|
62
|
+
outChannels: number;
|
|
63
|
+
kernelSize: number;
|
|
64
|
+
stride: number;
|
|
65
|
+
padding: number;
|
|
66
|
+
groups: number;
|
|
67
|
+
useBias: boolean;
|
|
68
|
+
weight: MxArray;
|
|
69
|
+
bias: MxArray | null;
|
|
70
|
+
constructor(inChannels: number, outChannels: number, kernelSize: number, stride?: number, padding?: number, groups?: number, useBias?: boolean);
|
|
71
|
+
forward(x: MxArray): MxArray;
|
|
72
|
+
loadWeights(weights: WeightMap, prefix: string): void;
|
|
73
|
+
}
|
|
74
|
+
/** Im2col-based Conv1d. weight: [out_ch, kernel_size, in_ch] */
|
|
75
|
+
export declare function conv1d(x: MxArray, weight: MxArray, bias: MxArray | null, stride: number, padding: number, groups: number): MxArray;
|
|
76
|
+
export declare class Conv2d extends Module {
|
|
77
|
+
inChannels: number;
|
|
78
|
+
outChannels: number;
|
|
79
|
+
kernelSize: number;
|
|
80
|
+
stride: number;
|
|
81
|
+
padding: number;
|
|
82
|
+
groups: number;
|
|
83
|
+
useBias: boolean;
|
|
84
|
+
weight: MxArray;
|
|
85
|
+
bias: MxArray | null;
|
|
86
|
+
constructor(inChannels: number, outChannels: number, kernelSize: number, stride?: number, padding?: number, groups?: number, useBias?: boolean);
|
|
87
|
+
forward(x: MxArray): MxArray;
|
|
88
|
+
loadWeights(weights: WeightMap, prefix: string): void;
|
|
89
|
+
}
|
|
90
|
+
/** Im2col-based Conv2d. weight: [out_ch, kH, kW, in_ch], input: [batch, H, W, in_ch] */
|
|
91
|
+
export declare function conv2d(x: MxArray, weight: MxArray, bias: MxArray | null, stride: number, padding: number, groups: number): MxArray;
|
|
92
|
+
export declare function sigmoid(x: MxArray): MxArray;
|
|
93
|
+
export declare function silu(x: MxArray): MxArray;
|
|
94
|
+
export declare function relu(x: MxArray): MxArray;
|
|
95
|
+
export declare function tanh(x: MxArray): MxArray;
|
|
96
|
+
export declare function glu(x: MxArray, axis: number): MxArray;
|
|
97
|
+
export declare function softmax(x: MxArray, axis: number): MxArray;
|
|
98
|
+
export declare function logSoftmax(x: MxArray, axis: number): MxArray;
|
|
99
|
+
export declare class LSTMLayer extends Module {
|
|
100
|
+
inputSize: number;
|
|
101
|
+
hiddenSize: number;
|
|
102
|
+
useBias: boolean;
|
|
103
|
+
Wx: MxArray;
|
|
104
|
+
Wh: MxArray;
|
|
105
|
+
b: MxArray | null;
|
|
106
|
+
constructor(inputSize: number, hiddenSize: number, useBias?: boolean);
|
|
107
|
+
/** x: [seq, batch, input], h: [batch, hidden] | null, c: [batch, hidden] | null */
|
|
108
|
+
forward(x: MxArray, h: MxArray | null, c: MxArray | null): [MxArray, MxArray];
|
|
109
|
+
loadWeights(weights: WeightMap, prefix: string): void;
|
|
110
|
+
}
|
|
111
|
+
export declare class LSTM extends Module {
|
|
112
|
+
layers: LSTMLayer[];
|
|
113
|
+
constructor(inputSize: number, hiddenSize: number, numLayers: number, useBias?: boolean);
|
|
114
|
+
/**
|
|
115
|
+
* x: [batch, seq, input] (batch_first=true as in Python code)
|
|
116
|
+
* Returns [output: [batch, seq, hidden], [h: [numLayers, batch, H], c: [numLayers, batch, H]]]
|
|
117
|
+
*/
|
|
118
|
+
forward(x: MxArray, hc?: [MxArray, MxArray] | null): [MxArray, [MxArray, MxArray]];
|
|
119
|
+
loadWeights(weights: WeightMap, prefix: string): void;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=nn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nn.d.ts","sourceRoot":"","sources":["../../src/mlx/nn.ts"],"names":[],"mappings":"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"}
|
package/dist/mlx/nn.js
ADDED
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Neural network primitives implemented on top of @mlx-node/core MxArray.
|
|
3
|
+
*
|
|
4
|
+
* Design notes:
|
|
5
|
+
* - Each layer stores its weights as MxArray fields.
|
|
6
|
+
* - Conv1d and Conv2d are implemented via im2col (take + reshape + matmul),
|
|
7
|
+
* keeping all computation on-device.
|
|
8
|
+
* - LSTM is implemented with manual gate arithmetic so no native LSTM kernel
|
|
9
|
+
* is required.
|
|
10
|
+
* - Weight loading uses a flat Map<string, {data, shape}> that mirrors the
|
|
11
|
+
* safetensors key structure produced by mlx-python's tree_flatten.
|
|
12
|
+
*/
|
|
13
|
+
import { MxArray } from '@mlx-node/core';
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Helpers
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
function s(...dims) {
|
|
18
|
+
return BigInt64Array.from(dims.map(BigInt));
|
|
19
|
+
}
|
|
20
|
+
function loadParam(weights, key) {
|
|
21
|
+
const entry = weights.get(key);
|
|
22
|
+
if (!entry)
|
|
23
|
+
throw new Error(`Weight not found: "${key}"`);
|
|
24
|
+
return MxArray.fromFloat32(entry.data, s(...entry.shape));
|
|
25
|
+
}
|
|
26
|
+
function tryLoadParam(weights, key) {
|
|
27
|
+
const entry = weights.get(key);
|
|
28
|
+
if (!entry)
|
|
29
|
+
return null;
|
|
30
|
+
return MxArray.fromFloat32(entry.data, s(...entry.shape));
|
|
31
|
+
}
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Module base
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
export class Module {
|
|
36
|
+
}
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Linear
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
export class Linear extends Module {
|
|
41
|
+
inFeatures;
|
|
42
|
+
outFeatures;
|
|
43
|
+
useBias;
|
|
44
|
+
weight; // [out, in]
|
|
45
|
+
bias = null;
|
|
46
|
+
constructor(inFeatures, outFeatures, useBias = true) {
|
|
47
|
+
super();
|
|
48
|
+
this.inFeatures = inFeatures;
|
|
49
|
+
this.outFeatures = outFeatures;
|
|
50
|
+
this.useBias = useBias;
|
|
51
|
+
}
|
|
52
|
+
forward(x) {
|
|
53
|
+
// x: [..., in] → [..., out]
|
|
54
|
+
const out = x.matmul(this.weight.transpose(new Int32Array([1, 0])));
|
|
55
|
+
return this.bias ? out.add(this.bias) : out;
|
|
56
|
+
}
|
|
57
|
+
loadWeights(weights, prefix) {
|
|
58
|
+
this.weight = loadParam(weights, `${prefix}.weight`);
|
|
59
|
+
if (this.useBias) {
|
|
60
|
+
this.bias = tryLoadParam(weights, `${prefix}.bias`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// Embedding
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
export class Embedding extends Module {
|
|
68
|
+
numEmbeddings;
|
|
69
|
+
embeddingDim;
|
|
70
|
+
weight; // [num_embeddings, embedding_dim]
|
|
71
|
+
constructor(numEmbeddings, embeddingDim) {
|
|
72
|
+
super();
|
|
73
|
+
this.numEmbeddings = numEmbeddings;
|
|
74
|
+
this.embeddingDim = embeddingDim;
|
|
75
|
+
}
|
|
76
|
+
forward(indices) {
|
|
77
|
+
// indices: [...] (int32) → [..., embedding_dim]
|
|
78
|
+
return this.weight.take(indices, 0);
|
|
79
|
+
}
|
|
80
|
+
loadWeights(weights, prefix) {
|
|
81
|
+
this.weight = loadParam(weights, `${prefix}.weight`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// LayerNorm
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
export class LayerNorm extends Module {
|
|
88
|
+
features;
|
|
89
|
+
eps;
|
|
90
|
+
weight; // [features]
|
|
91
|
+
bias; // [features]
|
|
92
|
+
constructor(features, eps = 1e-5) {
|
|
93
|
+
super();
|
|
94
|
+
this.features = features;
|
|
95
|
+
this.eps = eps;
|
|
96
|
+
}
|
|
97
|
+
forward(x) {
|
|
98
|
+
// x: [..., features]
|
|
99
|
+
const ndim = x.ndim();
|
|
100
|
+
const axes = new Int32Array([ndim - 1]);
|
|
101
|
+
const mean = x.mean(axes, true);
|
|
102
|
+
const variance = x.var(axes, true);
|
|
103
|
+
const norm = x.sub(mean).div(variance.addScalar(this.eps).sqrt());
|
|
104
|
+
return norm.mul(this.weight).add(this.bias);
|
|
105
|
+
}
|
|
106
|
+
loadWeights(weights, prefix) {
|
|
107
|
+
this.weight = loadParam(weights, `${prefix}.weight`);
|
|
108
|
+
this.bias = loadParam(weights, `${prefix}.bias`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// BatchNorm (inference mode: uses running stats)
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
export class BatchNorm extends Module {
|
|
115
|
+
features;
|
|
116
|
+
eps;
|
|
117
|
+
weight; // [features]
|
|
118
|
+
bias; // [features]
|
|
119
|
+
runningMean; // [features]
|
|
120
|
+
runningVar; // [features]
|
|
121
|
+
constructor(features, eps = 1e-5) {
|
|
122
|
+
super();
|
|
123
|
+
this.features = features;
|
|
124
|
+
this.eps = eps;
|
|
125
|
+
}
|
|
126
|
+
forward(x) {
|
|
127
|
+
// x: [batch, seq, features] (after Conv1d in Conformer)
|
|
128
|
+
const norm = x.sub(this.runningMean).div(this.runningVar.addScalar(this.eps).sqrt());
|
|
129
|
+
return norm.mul(this.weight).add(this.bias);
|
|
130
|
+
}
|
|
131
|
+
loadWeights(weights, prefix) {
|
|
132
|
+
this.weight = loadParam(weights, `${prefix}.weight`);
|
|
133
|
+
this.bias = loadParam(weights, `${prefix}.bias`);
|
|
134
|
+
this.runningMean = loadParam(weights, `${prefix}.running_mean`);
|
|
135
|
+
this.runningVar = loadParam(weights, `${prefix}.running_var`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
// Conv1d — implemented via im2col + matmul, all on-device
|
|
140
|
+
//
|
|
141
|
+
// MLX weight layout for Conv1d: [out_ch, kernel_size, in_ch]
|
|
142
|
+
// Input layout: [batch, seq, in_ch]
|
|
143
|
+
// Output layout: [batch, out_seq, out_ch]
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
export class Conv1d extends Module {
|
|
146
|
+
inChannels;
|
|
147
|
+
outChannels;
|
|
148
|
+
kernelSize;
|
|
149
|
+
stride;
|
|
150
|
+
padding;
|
|
151
|
+
groups;
|
|
152
|
+
useBias;
|
|
153
|
+
weight; // [out_ch, kernel_size, in_ch]
|
|
154
|
+
bias = null;
|
|
155
|
+
constructor(inChannels, outChannels, kernelSize, stride = 1, padding = 0, groups = 1, useBias = true) {
|
|
156
|
+
super();
|
|
157
|
+
this.inChannels = inChannels;
|
|
158
|
+
this.outChannels = outChannels;
|
|
159
|
+
this.kernelSize = kernelSize;
|
|
160
|
+
this.stride = stride;
|
|
161
|
+
this.padding = padding;
|
|
162
|
+
this.groups = groups;
|
|
163
|
+
this.useBias = useBias;
|
|
164
|
+
}
|
|
165
|
+
forward(x) {
|
|
166
|
+
return conv1d(x, this.weight, this.bias, this.stride, this.padding, this.groups);
|
|
167
|
+
}
|
|
168
|
+
loadWeights(weights, prefix) {
|
|
169
|
+
this.weight = loadParam(weights, `${prefix}.weight`);
|
|
170
|
+
if (this.useBias) {
|
|
171
|
+
this.bias = tryLoadParam(weights, `${prefix}.bias`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** Im2col-based Conv1d. weight: [out_ch, kernel_size, in_ch] */
|
|
176
|
+
export function conv1d(x, weight, bias, stride, padding, groups) {
|
|
177
|
+
const xShape = x.shape();
|
|
178
|
+
const batch = Number(xShape[0]);
|
|
179
|
+
const seq = Number(xShape[1]);
|
|
180
|
+
const inCh = Number(xShape[2]);
|
|
181
|
+
const wShape = weight.shape();
|
|
182
|
+
const outCh = Number(wShape[0]);
|
|
183
|
+
const kernelSize = Number(wShape[1]);
|
|
184
|
+
// Pad the sequence dimension
|
|
185
|
+
let xPad = x;
|
|
186
|
+
if (padding > 0) {
|
|
187
|
+
xPad = x.pad(new Int32Array([0, 0, padding, padding, 0, 0]), 0.0);
|
|
188
|
+
}
|
|
189
|
+
const paddedSeq = Number(xPad.shape()[1]);
|
|
190
|
+
const outLen = Math.floor((paddedSeq - kernelSize) / stride) + 1;
|
|
191
|
+
if (groups === 1) {
|
|
192
|
+
// Standard conv: build gather indices [outLen * kernelSize]
|
|
193
|
+
const idxData = new Int32Array(outLen * kernelSize);
|
|
194
|
+
for (let t = 0; t < outLen; t++) {
|
|
195
|
+
for (let k = 0; k < kernelSize; k++) {
|
|
196
|
+
idxData[t * kernelSize + k] = t * stride + k;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const idx = MxArray.fromInt32(idxData, s(outLen * kernelSize));
|
|
200
|
+
// Gather along seq axis: [batch, outLen*kernelSize, inCh]
|
|
201
|
+
const gathered = xPad.take(idx, 1);
|
|
202
|
+
// Reshape to [batch, outLen, kernelSize, inCh]
|
|
203
|
+
const unfolded4d = gathered.reshape(s(batch, outLen, kernelSize, inCh));
|
|
204
|
+
// Reshape to [batch, outLen, kernelSize * inCh]
|
|
205
|
+
const unfolded = unfolded4d.reshape(s(batch, outLen, kernelSize * inCh));
|
|
206
|
+
// weight: [outCh, kernelSize, inCh] → transpose → [kernelSize, inCh, outCh]
|
|
207
|
+
// → reshape → [kernelSize*inCh, outCh]
|
|
208
|
+
const wT = weight
|
|
209
|
+
.transpose(new Int32Array([1, 2, 0]))
|
|
210
|
+
.reshape(s(kernelSize * inCh, outCh));
|
|
211
|
+
let out = unfolded.matmul(wT); // [batch, outLen, outCh]
|
|
212
|
+
if (bias)
|
|
213
|
+
out = out.add(bias);
|
|
214
|
+
return out;
|
|
215
|
+
}
|
|
216
|
+
if (groups === inCh && outCh === inCh) {
|
|
217
|
+
// Depthwise conv: each output channel depends on exactly one input channel
|
|
218
|
+
const idxData = new Int32Array(outLen * kernelSize);
|
|
219
|
+
for (let t = 0; t < outLen; t++) {
|
|
220
|
+
for (let k = 0; k < kernelSize; k++) {
|
|
221
|
+
idxData[t * kernelSize + k] = t * stride + k;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const idx = MxArray.fromInt32(idxData, s(outLen * kernelSize));
|
|
225
|
+
// Gather: [batch, outLen*kernelSize, inCh]
|
|
226
|
+
const gathered = xPad.take(idx, 1);
|
|
227
|
+
// Reshape: [batch, outLen, kernelSize, inCh]
|
|
228
|
+
const unfolded4d = gathered.reshape(s(batch, outLen, kernelSize, inCh));
|
|
229
|
+
// weight: [outCh, kernelSize, 1] → squeeze last dim → [inCh, kernelSize]
|
|
230
|
+
// then transpose → [kernelSize, inCh]
|
|
231
|
+
const wSquzd = weight.squeeze(new Int32Array([2])); // [inCh, kernelSize]
|
|
232
|
+
const wT = wSquzd.transpose(new Int32Array([1, 0])); // [kernelSize, inCh]
|
|
233
|
+
// Element-wise multiply + sum over kernel axis
|
|
234
|
+
const weighted = unfolded4d.mul(wT); // [batch, outLen, kernelSize, inCh]
|
|
235
|
+
let out = weighted.sum(new Int32Array([2])); // [batch, outLen, inCh]
|
|
236
|
+
if (bias)
|
|
237
|
+
out = out.add(bias);
|
|
238
|
+
return out;
|
|
239
|
+
}
|
|
240
|
+
throw new Error(`conv1d: groups=${groups} not supported (inCh=${inCh}, outCh=${outCh})`);
|
|
241
|
+
}
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
// Conv2d — same im2col strategy, in 2D
|
|
244
|
+
//
|
|
245
|
+
// MLX weight layout: [out_ch, kH, kW, in_ch]
|
|
246
|
+
// Input layout (MLX NHW C): [batch, H, W, in_ch]
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
export class Conv2d extends Module {
|
|
249
|
+
inChannels;
|
|
250
|
+
outChannels;
|
|
251
|
+
kernelSize;
|
|
252
|
+
stride;
|
|
253
|
+
padding;
|
|
254
|
+
groups;
|
|
255
|
+
useBias;
|
|
256
|
+
weight;
|
|
257
|
+
bias = null;
|
|
258
|
+
constructor(inChannels, outChannels, kernelSize, stride = 1, padding = 0, groups = 1, useBias = true) {
|
|
259
|
+
super();
|
|
260
|
+
this.inChannels = inChannels;
|
|
261
|
+
this.outChannels = outChannels;
|
|
262
|
+
this.kernelSize = kernelSize;
|
|
263
|
+
this.stride = stride;
|
|
264
|
+
this.padding = padding;
|
|
265
|
+
this.groups = groups;
|
|
266
|
+
this.useBias = useBias;
|
|
267
|
+
}
|
|
268
|
+
forward(x) {
|
|
269
|
+
return conv2d(x, this.weight, this.bias, this.stride, this.padding, this.groups);
|
|
270
|
+
}
|
|
271
|
+
loadWeights(weights, prefix) {
|
|
272
|
+
this.weight = loadParam(weights, `${prefix}.weight`);
|
|
273
|
+
if (this.useBias) {
|
|
274
|
+
this.bias = tryLoadParam(weights, `${prefix}.bias`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/** Im2col-based Conv2d. weight: [out_ch, kH, kW, in_ch], input: [batch, H, W, in_ch] */
|
|
279
|
+
export function conv2d(x, weight, bias, stride, padding, groups) {
|
|
280
|
+
const xShape = x.shape();
|
|
281
|
+
const batch = Number(xShape[0]);
|
|
282
|
+
const H = Number(xShape[1]);
|
|
283
|
+
const W = Number(xShape[2]);
|
|
284
|
+
const inCh = Number(xShape[3]);
|
|
285
|
+
const wShape = weight.shape();
|
|
286
|
+
const outCh = Number(wShape[0]);
|
|
287
|
+
const kH = Number(wShape[1]);
|
|
288
|
+
const kW = Number(wShape[2]);
|
|
289
|
+
// Pad
|
|
290
|
+
let xPad = x;
|
|
291
|
+
if (padding > 0) {
|
|
292
|
+
xPad = x.pad(new Int32Array([0, 0, padding, padding, padding, padding, 0, 0]), 0.0);
|
|
293
|
+
}
|
|
294
|
+
const pH = Number(xPad.shape()[1]);
|
|
295
|
+
const pW = Number(xPad.shape()[2]);
|
|
296
|
+
const outH = Math.floor((pH - kH) / stride) + 1;
|
|
297
|
+
const outW = Math.floor((pW - kW) / stride) + 1;
|
|
298
|
+
// Build gather indices for height and width dimensions
|
|
299
|
+
const numPositions = outH * outW * kH * kW;
|
|
300
|
+
const hIdx = new Int32Array(numPositions);
|
|
301
|
+
const wIdx = new Int32Array(numPositions);
|
|
302
|
+
let pos = 0;
|
|
303
|
+
for (let oh = 0; oh < outH; oh++) {
|
|
304
|
+
for (let ow = 0; ow < outW; ow++) {
|
|
305
|
+
for (let kh = 0; kh < kH; kh++) {
|
|
306
|
+
for (let kw = 0; kw < kW; kw++) {
|
|
307
|
+
hIdx[pos] = oh * stride + kh;
|
|
308
|
+
wIdx[pos] = ow * stride + kw;
|
|
309
|
+
pos++;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
// We need to gather at (h, w) pairs. Build flat indices into the H*W grid.
|
|
315
|
+
const hwIdx = new Int32Array(numPositions);
|
|
316
|
+
for (let i = 0; i < numPositions; i++) {
|
|
317
|
+
hwIdx[i] = hIdx[i] * pW + wIdx[i];
|
|
318
|
+
}
|
|
319
|
+
// Reshape x to [batch, H*W, inCh] then gather
|
|
320
|
+
const xFlat = xPad.reshape(s(batch, pH * pW, inCh));
|
|
321
|
+
const gathered = xFlat.take(MxArray.fromInt32(hwIdx, s(numPositions)), 1);
|
|
322
|
+
// gathered: [batch, outH*outW*kH*kW, inCh]
|
|
323
|
+
let out;
|
|
324
|
+
if (groups > 1) {
|
|
325
|
+
// Depthwise conv2d: weight shape is [outCh, kH, kW, 1]
|
|
326
|
+
// gathered: [batch, outH*outW*kH*kW, inCh]
|
|
327
|
+
// → [batch, outH*outW, kH*kW, inCh] → transpose → [batch, outH*outW, inCh, kH*kW]
|
|
328
|
+
const unfolded4d = gathered.reshape(s(batch, outH * outW, kH * kW, inCh));
|
|
329
|
+
const unfoldedT = unfolded4d.transpose(new Int32Array([0, 1, 3, 2]));
|
|
330
|
+
// weight: [outCh, kH, kW, 1] → [1, 1, outCh, kH*kW]
|
|
331
|
+
const wBcast = weight.reshape(s(1, 1, outCh, kH * kW));
|
|
332
|
+
// element-wise mul + sum over kernel dim → [batch, outH*outW, inCh]
|
|
333
|
+
out = unfoldedT.mul(wBcast).sum(new Int32Array([3]));
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
// Standard conv2d
|
|
337
|
+
// Reshape to [batch, outH*outW, kH*kW*inCh]
|
|
338
|
+
const unfolded = gathered.reshape(s(batch, outH * outW, kH * kW * inCh));
|
|
339
|
+
// weight: [outCh, kH, kW, inCh] → reshape → [outCh, kH*kW*inCh]
|
|
340
|
+
// → transpose → [kH*kW*inCh, outCh]
|
|
341
|
+
const wFlat = weight.reshape(s(outCh, kH * kW * inCh));
|
|
342
|
+
const wT = wFlat.transpose(new Int32Array([1, 0]));
|
|
343
|
+
out = unfolded.matmul(wT); // [batch, outH*outW, outCh]
|
|
344
|
+
}
|
|
345
|
+
// Reshape to [batch, outH, outW, outCh]
|
|
346
|
+
out = out.reshape(s(batch, outH, outW, outCh));
|
|
347
|
+
if (bias)
|
|
348
|
+
out = out.add(bias);
|
|
349
|
+
return out;
|
|
350
|
+
}
|
|
351
|
+
// ---------------------------------------------------------------------------
|
|
352
|
+
// Activation helpers
|
|
353
|
+
// ---------------------------------------------------------------------------
|
|
354
|
+
export function sigmoid(x) {
|
|
355
|
+
// 1 / (1 + exp(-x))
|
|
356
|
+
return x.negative().exp().addScalar(1.0).reciprocal();
|
|
357
|
+
}
|
|
358
|
+
export function silu(x) {
|
|
359
|
+
return x.mul(sigmoid(x));
|
|
360
|
+
}
|
|
361
|
+
export function relu(x) {
|
|
362
|
+
return x.maximum(MxArray.zeros(x.shape(), null));
|
|
363
|
+
}
|
|
364
|
+
export function tanh(x) {
|
|
365
|
+
return x.tanh();
|
|
366
|
+
}
|
|
367
|
+
export function glu(x, axis) {
|
|
368
|
+
// Split in half along axis, apply sigmoid to the second half
|
|
369
|
+
const shape = x.shape();
|
|
370
|
+
const dim = Number(shape[axis]);
|
|
371
|
+
const half = dim / 2;
|
|
372
|
+
const starts1 = new Array(shape.length).fill(0n);
|
|
373
|
+
const stops1 = shape.map((d, i) => (i === axis ? BigInt(half) : d));
|
|
374
|
+
const starts2 = new Array(shape.length).fill(0n);
|
|
375
|
+
starts2[axis] = BigInt(half);
|
|
376
|
+
const stops2 = shape.map(d => d);
|
|
377
|
+
const a = x.slice(BigInt64Array.from(starts1), BigInt64Array.from(stops1));
|
|
378
|
+
const b = x.slice(BigInt64Array.from(starts2), BigInt64Array.from(stops2));
|
|
379
|
+
return a.mul(sigmoid(b));
|
|
380
|
+
}
|
|
381
|
+
export function softmax(x, axis) {
|
|
382
|
+
const maxVal = x.max(new Int32Array([axis]), true);
|
|
383
|
+
const shifted = x.sub(maxVal);
|
|
384
|
+
const expd = shifted.exp();
|
|
385
|
+
const sumExp = expd.sum(new Int32Array([axis]), true);
|
|
386
|
+
return expd.div(sumExp);
|
|
387
|
+
}
|
|
388
|
+
export function logSoftmax(x, axis) {
|
|
389
|
+
return x.logSoftmax(axis);
|
|
390
|
+
}
|
|
391
|
+
// ---------------------------------------------------------------------------
|
|
392
|
+
// LSTM — manual gate implementation
|
|
393
|
+
//
|
|
394
|
+
// MLX LSTM weight keys (per layer):
|
|
395
|
+
// lstm.{i}.Wx: [4*hidden, input] (gates ordered: i, f, g, o)
|
|
396
|
+
// lstm.{i}.Wh: [4*hidden, hidden]
|
|
397
|
+
// lstm.{i}.b: [4*hidden]
|
|
398
|
+
// ---------------------------------------------------------------------------
|
|
399
|
+
export class LSTMLayer extends Module {
|
|
400
|
+
inputSize;
|
|
401
|
+
hiddenSize;
|
|
402
|
+
useBias;
|
|
403
|
+
Wx; // [4*hidden, input]
|
|
404
|
+
Wh; // [4*hidden, hidden]
|
|
405
|
+
b = null;
|
|
406
|
+
constructor(inputSize, hiddenSize, useBias = true) {
|
|
407
|
+
super();
|
|
408
|
+
this.inputSize = inputSize;
|
|
409
|
+
this.hiddenSize = hiddenSize;
|
|
410
|
+
this.useBias = useBias;
|
|
411
|
+
}
|
|
412
|
+
/** x: [seq, batch, input], h: [batch, hidden] | null, c: [batch, hidden] | null */
|
|
413
|
+
forward(x, h, c) {
|
|
414
|
+
// x: [seq, batch, input]
|
|
415
|
+
const xShape = x.shape();
|
|
416
|
+
const seqLen = Number(xShape[0]);
|
|
417
|
+
const batch = Number(xShape[1]);
|
|
418
|
+
const H = this.hiddenSize;
|
|
419
|
+
// Initialize h, c if null
|
|
420
|
+
if (h === null)
|
|
421
|
+
h = MxArray.zeros(s(batch, H), null);
|
|
422
|
+
if (c === null)
|
|
423
|
+
c = MxArray.zeros(s(batch, H), null);
|
|
424
|
+
// Precompute input projections for all time steps: [seq, batch, 4H]
|
|
425
|
+
// x: [seq, batch, input] → reshape [seq*batch, input]
|
|
426
|
+
const xFlat = x.reshape(s(seqLen * batch, this.inputSize));
|
|
427
|
+
// Wx: [4H, input] → xFlat @ Wx.T → [seq*batch, 4H]
|
|
428
|
+
let xProj = xFlat.matmul(this.Wx.transpose(new Int32Array([1, 0])));
|
|
429
|
+
if (this.b)
|
|
430
|
+
xProj = xProj.add(this.b);
|
|
431
|
+
// Reshape: [seq, batch, 4H]
|
|
432
|
+
const xProjSeq = xProj.reshape(s(seqLen, batch, 4 * H));
|
|
433
|
+
const allH = [];
|
|
434
|
+
const allC = [];
|
|
435
|
+
for (let t = 0; t < seqLen; t++) {
|
|
436
|
+
// Get x projection for this step: [batch, 4H]
|
|
437
|
+
const xt = xProjSeq.slice(s(t, 0, 0), s(t + 1, batch, 4 * H)).squeeze(new Int32Array([0]));
|
|
438
|
+
// Hidden projection: h @ Wh.T → [batch, 4H]
|
|
439
|
+
const hProj = h.matmul(this.Wh.transpose(new Int32Array([1, 0])));
|
|
440
|
+
const gates = xt.add(hProj); // [batch, 4H]
|
|
441
|
+
// Split gates: i, f, g, o each [batch, H]
|
|
442
|
+
const gate = (idx) => gates.slice(s(0, idx * H), s(batch, (idx + 1) * H));
|
|
443
|
+
const gi = sigmoid(gate(0)); // input gate
|
|
444
|
+
const gf = sigmoid(gate(1)); // forget gate
|
|
445
|
+
const gg = gate(2).tanh(); // cell gate
|
|
446
|
+
const go = sigmoid(gate(3)); // output gate
|
|
447
|
+
c = gf.mul(c).add(gi.mul(gg)); // new cell
|
|
448
|
+
h = go.mul(c.tanh()); // new hidden
|
|
449
|
+
// Force evaluation to avoid unbounded graph growth
|
|
450
|
+
h.eval();
|
|
451
|
+
c.eval();
|
|
452
|
+
allH.push(h);
|
|
453
|
+
allC.push(c);
|
|
454
|
+
}
|
|
455
|
+
// Stack: [seq, batch, H]
|
|
456
|
+
const hStack = MxArray.stack(allH, 0);
|
|
457
|
+
// We return all hidden states and the last c
|
|
458
|
+
return [hStack, c];
|
|
459
|
+
}
|
|
460
|
+
loadWeights(weights, prefix) {
|
|
461
|
+
this.Wx = loadParam(weights, `${prefix}.Wx`);
|
|
462
|
+
this.Wh = loadParam(weights, `${prefix}.Wh`);
|
|
463
|
+
if (this.useBias) {
|
|
464
|
+
this.b = tryLoadParam(weights, `${prefix}.bias`) ?? tryLoadParam(weights, `${prefix}.b`);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
export class LSTM extends Module {
|
|
469
|
+
layers;
|
|
470
|
+
constructor(inputSize, hiddenSize, numLayers, useBias = true) {
|
|
471
|
+
super();
|
|
472
|
+
this.layers = [];
|
|
473
|
+
for (let i = 0; i < numLayers; i++) {
|
|
474
|
+
this.layers.push(new LSTMLayer(i === 0 ? inputSize : hiddenSize, hiddenSize, useBias));
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* x: [batch, seq, input] (batch_first=true as in Python code)
|
|
479
|
+
* Returns [output: [batch, seq, hidden], [h: [numLayers, batch, H], c: [numLayers, batch, H]]]
|
|
480
|
+
*/
|
|
481
|
+
forward(x, hc = null) {
|
|
482
|
+
// x: [batch, seq, input] → [seq, batch, input]
|
|
483
|
+
let current = x.transpose(new Int32Array([1, 0, 2]));
|
|
484
|
+
const xShape = x.shape();
|
|
485
|
+
const batch = Number(xShape[0]);
|
|
486
|
+
const H = this.layers[0].hiddenSize;
|
|
487
|
+
const nextHList = [];
|
|
488
|
+
const nextCList = [];
|
|
489
|
+
for (let i = 0; i < this.layers.length; i++) {
|
|
490
|
+
const h = hc ? hc[0].slice(s(i, 0, 0), s(i + 1, batch, H)).squeeze(new Int32Array([0])) : null;
|
|
491
|
+
const c = hc ? hc[1].slice(s(i, 0, 0), s(i + 1, batch, H)).squeeze(new Int32Array([0])) : null;
|
|
492
|
+
const [allH, lastC] = this.layers[i].forward(current, h, c);
|
|
493
|
+
const seqLen = Number(allH.shape()[0]);
|
|
494
|
+
const lastH = allH.slice(s(seqLen - 1, 0, 0), allH.shape()).squeeze(new Int32Array([0]));
|
|
495
|
+
current = allH; // pass all hidden states to next layer
|
|
496
|
+
nextHList.push(lastH);
|
|
497
|
+
nextCList.push(lastC);
|
|
498
|
+
}
|
|
499
|
+
// output: [seq, batch, H] → [batch, seq, H]
|
|
500
|
+
const output = current.transpose(new Int32Array([1, 0, 2]));
|
|
501
|
+
const finalH = MxArray.stack(nextHList, 0); // [numLayers, batch, H]
|
|
502
|
+
const finalC = MxArray.stack(nextCList, 0); // [numLayers, batch, H]
|
|
503
|
+
return [output, [finalH, finalC]];
|
|
504
|
+
}
|
|
505
|
+
loadWeights(weights, prefix) {
|
|
506
|
+
for (let i = 0; i < this.layers.length; i++) {
|
|
507
|
+
this.layers[i].loadWeights(weights, `${prefix}.${i}`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
//# sourceMappingURL=nn.js.map
|