@mineproness/tensorloop 1.5.9 → 2.1.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/package.json +1 -1
- package/readme.md +4 -3
- package/src/Embedding/index.d.ts +1 -1
- package/src/Embedding/index.js +13 -19
- package/src/FFN/index.js +14 -18
- package/src/GELU/index.d.ts +2 -2
- package/src/GELU/index.js +19 -29
- package/src/Linear/index.d.ts +2 -2
- package/src/Linear/index.js +12 -14
- package/src/PositionEmbedding/index.d.ts +1 -1
- package/src/PositionEmbedding/index.js +5 -3
- package/src/ReLU/index.d.ts +1 -0
- package/src/ReLU/index.js +12 -9
- package/src/SelfAttention/index.js +15 -11
- package/src/Softmax/index.d.ts +1 -2
- package/src/Softmax/index.js +9 -11
- package/src/TransformerBlock/index.d.ts +2 -1
- package/src/TransformerBlock/index.js +3 -1
- package/src/gpiInit.js +604 -0
- package/src/GPU.js +0 -279
package/src/gpiInit.js
ADDED
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
import { GPU } from 'gpu.js'
|
|
2
|
+
|
|
3
|
+
const gpu = new GPU({ mode: "gpu" })
|
|
4
|
+
if (gpu.mode == "cpu") {
|
|
5
|
+
throw new Error("The tensorloop didn't have working GPU access.")
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// ==========================
|
|
9
|
+
// Init all Things
|
|
10
|
+
// ==========================
|
|
11
|
+
|
|
12
|
+
const maxTexture = GetMaxTexture()
|
|
13
|
+
|
|
14
|
+
// ===========================
|
|
15
|
+
// UTILS FUNCITON
|
|
16
|
+
// ===========================
|
|
17
|
+
function GetMaxTexture() {
|
|
18
|
+
const dummyKernel = gpu.createKernel(function () {
|
|
19
|
+
return 1 + 1
|
|
20
|
+
}).setOutput([1])
|
|
21
|
+
dummyKernel()
|
|
22
|
+
const gl = dummyKernel.context;
|
|
23
|
+
return gl.getParameter(gl.MAX_TEXTURE_SIZE)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function spilt2DArray(arr) {
|
|
27
|
+
const result = []
|
|
28
|
+
for (let a = 0; a < arr.length; a += maxTexture) {
|
|
29
|
+
result.push(arr.slice(a, a + maxTexture))
|
|
30
|
+
}
|
|
31
|
+
return result
|
|
32
|
+
}
|
|
33
|
+
function split2DByColumns(matrix, chunkSize = maxTexture) {
|
|
34
|
+
const chunks = [];
|
|
35
|
+
for (let start = 0; start < matrix[0].length; start += chunkSize) {
|
|
36
|
+
const end = Math.min(start + chunkSize, matrix[0].length);
|
|
37
|
+
const chunk = matrix.map(row =>
|
|
38
|
+
row.slice(start, end)
|
|
39
|
+
);
|
|
40
|
+
chunks.push(chunk);
|
|
41
|
+
}
|
|
42
|
+
return chunks;
|
|
43
|
+
}
|
|
44
|
+
// =======================
|
|
45
|
+
// Init all Classes
|
|
46
|
+
// =======================
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
export class GPULinear {
|
|
50
|
+
cacheLF = [];
|
|
51
|
+
cacheLInputGra = []
|
|
52
|
+
cacheLWeigths = []
|
|
53
|
+
cacheLBias = []
|
|
54
|
+
Weights = [];
|
|
55
|
+
Bias = [];
|
|
56
|
+
contextSize;
|
|
57
|
+
embeddingSize;
|
|
58
|
+
outputSize;
|
|
59
|
+
constructor(weights, bias, contextSize) {
|
|
60
|
+
|
|
61
|
+
this.contextSize = contextSize;
|
|
62
|
+
this.embeddingSize = weights[0].length;
|
|
63
|
+
this.outputSize = weights.length
|
|
64
|
+
const splitW = spilt2DArray(weights)
|
|
65
|
+
const splitB = spilt2DArray(bias)
|
|
66
|
+
process.stdout.write(`\r[DEBUG] Saving Weights in VRAM${".".repeat(Math.floor(Math.random() * 4))}`)
|
|
67
|
+
for (let i = 0; i < splitW.length; i++) {
|
|
68
|
+
const biasVram = gpu.createKernel(function (x) {
|
|
69
|
+
return x[this.thread.x]
|
|
70
|
+
}, {
|
|
71
|
+
output: [splitW[i].length],
|
|
72
|
+
immutable: true,
|
|
73
|
+
pipeline: true
|
|
74
|
+
})
|
|
75
|
+
const weightVram = gpu.createKernel(function (x) {
|
|
76
|
+
return x[this.thread.y][this.thread.x]
|
|
77
|
+
}, {
|
|
78
|
+
output: [this.embeddingSize, splitW[i].length],
|
|
79
|
+
immutable: true,
|
|
80
|
+
pipeline: true
|
|
81
|
+
})
|
|
82
|
+
this.Weights[i] = weightVram(splitW[i]);
|
|
83
|
+
this.Bias[i] = biasVram(splitB[i]);
|
|
84
|
+
this.cacheLF[i] = gpu.createKernel(function (weights, bias, x) {
|
|
85
|
+
let sum = bias[this.thread.x]
|
|
86
|
+
for (let i = 0; i < this.constants.inputSize; i++) {
|
|
87
|
+
sum += weights[this.thread.x][i] * x[this.thread.y][i]
|
|
88
|
+
}
|
|
89
|
+
return sum;
|
|
90
|
+
}).setOutput([splitW[i].length, contextSize]).setConstants({ inputSize: this.embeddingSize })
|
|
91
|
+
this.cacheLWeigths[i] = gpu.createKernel(function (weights, learningRate, out, input) {
|
|
92
|
+
let sum = 0;
|
|
93
|
+
for (let a = 0; a < this.constants.contextSize; a++) {
|
|
94
|
+
sum += out[a][this.thread.y] * input[a][this.thread.x]
|
|
95
|
+
}
|
|
96
|
+
return weights[this.thread.y][this.thread.x] - learningRate * sum
|
|
97
|
+
|
|
98
|
+
}, {
|
|
99
|
+
constants: {
|
|
100
|
+
contextSize
|
|
101
|
+
},
|
|
102
|
+
output: [this.embeddingSize, splitW[i].length],
|
|
103
|
+
pipeline: true,
|
|
104
|
+
immutable: true
|
|
105
|
+
})
|
|
106
|
+
this.cacheLBias[i] = gpu.createKernel(function (bias, learningRate, out) {
|
|
107
|
+
let sum = 0;
|
|
108
|
+
for (let a = 0; a < this.constants.contextSize; a++) {
|
|
109
|
+
sum += out[a][this.thread.x]
|
|
110
|
+
}
|
|
111
|
+
return bias[this.thread.x] - learningRate * sum
|
|
112
|
+
|
|
113
|
+
}, {
|
|
114
|
+
constants: {
|
|
115
|
+
contextSize
|
|
116
|
+
},
|
|
117
|
+
output: [splitW[i].length],
|
|
118
|
+
pipeline: true,
|
|
119
|
+
immutable: true
|
|
120
|
+
})
|
|
121
|
+
this.cacheLInputGra[i] = gpu.createKernel(function (weights, out) {
|
|
122
|
+
let sum = 0;
|
|
123
|
+
for (let a = 0; a < this.constants.outputSizes; a++) {
|
|
124
|
+
sum += out[this.thread.y][a] * weights[a][this.thread.x]
|
|
125
|
+
}
|
|
126
|
+
return sum
|
|
127
|
+
|
|
128
|
+
}, {
|
|
129
|
+
output: [this.embeddingSize, contextSize],
|
|
130
|
+
constants: {
|
|
131
|
+
outputSizes: splitW[i].length
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
process.stdout.write(
|
|
135
|
+
`\r[DEBUG] Weigths are Saved in VRAM!`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
forward(input) {
|
|
140
|
+
const result = Array.from({ length: this.contextSize }, () => new Float32Array(this.outputSize))
|
|
141
|
+
let offset = 0;
|
|
142
|
+
for (let i = 0; i < this.cacheLF.length; i++) {
|
|
143
|
+
const out = this.cacheLF[i](this.Weights[i], this.Bias[i], input)
|
|
144
|
+
for (let a = 0; a < this.contextSize; a++) {
|
|
145
|
+
result[a].set(out[a], offset)
|
|
146
|
+
}
|
|
147
|
+
offset += out[0].length
|
|
148
|
+
}
|
|
149
|
+
return result
|
|
150
|
+
}
|
|
151
|
+
Save() {
|
|
152
|
+
const weights = []
|
|
153
|
+
const bias = []
|
|
154
|
+
for (let a = 0; a < this.Weights.length; a++) {
|
|
155
|
+
const chunkB = this.Bias[a]
|
|
156
|
+
const chunkW = this.Weights[a]
|
|
157
|
+
if (chunkB.toArray) {
|
|
158
|
+
bias.push(...chunkB.toArray())
|
|
159
|
+
weights.push(...chunkW.toArray())
|
|
160
|
+
} else {
|
|
161
|
+
bias.push(...chunkB)
|
|
162
|
+
weights.push(...chunkW)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return { weights, bias: new Float32Array(bias) }
|
|
166
|
+
}
|
|
167
|
+
backward(outGradient, input, learningRate) {
|
|
168
|
+
const splitOut = split2DByColumns(outGradient)
|
|
169
|
+
const result = Array.from({ length: this.contextSize }, () => new Float32Array(this.embeddingSize))
|
|
170
|
+
for (let i = 0; i < splitOut.length; i++) {
|
|
171
|
+
const oldW = this.Weights[i]
|
|
172
|
+
const oldB = this.Bias[i]
|
|
173
|
+
const out = this.cacheLInputGra[i](this.Weights[i], splitOut[i])
|
|
174
|
+
this.Weights[i] = this.cacheLWeigths[i](this.Weights[i], learningRate, splitOut[i], input)
|
|
175
|
+
this.Bias[i] = this.cacheLBias[i](this.Bias[i], learningRate, splitOut[i])
|
|
176
|
+
for (let a = 0; a < this.contextSize; a++) {
|
|
177
|
+
for (let g = 0; g < this.embeddingSize; g++) {
|
|
178
|
+
result[a][g] += out[a][g]
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (oldW.delete) {
|
|
182
|
+
oldW.delete()
|
|
183
|
+
oldB.delete()
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return result
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
export class GPUSoftmax {
|
|
192
|
+
cacheSumCalculate;
|
|
193
|
+
cacheMaxCache;
|
|
194
|
+
cacheSF = [];
|
|
195
|
+
cacheSB = []
|
|
196
|
+
contextSize;
|
|
197
|
+
calculateLoss;
|
|
198
|
+
avgLoss;
|
|
199
|
+
constructor(logits, contextSize) {
|
|
200
|
+
this.contextSize = contextSize;
|
|
201
|
+
this.avgLoss = gpu.createKernel(function (losses) {
|
|
202
|
+
let sum = 0;
|
|
203
|
+
for (let i = 0; i < this.constants.contextSize; i++) {
|
|
204
|
+
sum += losses[i]
|
|
205
|
+
}
|
|
206
|
+
return sum / this.constants.contextSize
|
|
207
|
+
}, {
|
|
208
|
+
output: [1],
|
|
209
|
+
constants: { contextSize }
|
|
210
|
+
})
|
|
211
|
+
this.calculateLoss = gpu.createKernel(function (probs, target) {
|
|
212
|
+
return -Math.log(probs[this.thread.x][target[this.thread.x]] + 1e-9)
|
|
213
|
+
}, { output: [contextSize], immutable: true, pipeline: true })
|
|
214
|
+
this.cacheSumCalculate = gpu.createKernel(function (maxLogits, logits) {
|
|
215
|
+
const maxLogit = maxLogits[this.thread.x]
|
|
216
|
+
let sum = 0;
|
|
217
|
+
for (let i = 0; i < this.constants.logitSize; i++) {
|
|
218
|
+
sum += Math.exp(logits[this.thread.x][i] - maxLogit)
|
|
219
|
+
}
|
|
220
|
+
return sum
|
|
221
|
+
}, {
|
|
222
|
+
output: [contextSize],
|
|
223
|
+
constants: { logitSize: logits[0].length },
|
|
224
|
+
immutable: true,
|
|
225
|
+
pipeline: true
|
|
226
|
+
})
|
|
227
|
+
this.cacheMaxCache = gpu.createKernel(function (logits) {
|
|
228
|
+
let maxLogit = logits[this.thread.x][0]
|
|
229
|
+
for (let i = 0; i < this.constants.logitSize; i++) {
|
|
230
|
+
if (logits[this.thread.x][i] > maxLogit) {
|
|
231
|
+
maxLogit = logits[this.thread.x][i]
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return maxLogit
|
|
235
|
+
}, {
|
|
236
|
+
output: [contextSize],
|
|
237
|
+
constants: { logitSize: logits[0].length },
|
|
238
|
+
immutable: true,
|
|
239
|
+
pipeline: true
|
|
240
|
+
})
|
|
241
|
+
const splited2d = split2DByColumns(logits)
|
|
242
|
+
let offset = 0;
|
|
243
|
+
for (let a = 0; a < splited2d.length; a++) {
|
|
244
|
+
this.cacheSF[a] = gpu.createKernel(function (logits, sum, maxLogit) {
|
|
245
|
+
return Math.exp(logits[this.thread.y][this.thread.x] - maxLogit[this.thread.y]) / sum[this.thread.y]
|
|
246
|
+
}, {
|
|
247
|
+
output: [splited2d[a][0].length, contextSize]
|
|
248
|
+
})
|
|
249
|
+
this.cacheSB[a] = gpu.createKernel(function (logits, targets) {
|
|
250
|
+
let logit = logits[this.thread.y][this.thread.x]
|
|
251
|
+
let globalIndex = this.constants.offset + this.thread.x
|
|
252
|
+
return globalIndex == targets[this.thread.y] ? logit - 1.0 : logit
|
|
253
|
+
}, {
|
|
254
|
+
output: [splited2d[a][0].length, contextSize],
|
|
255
|
+
constants: { offset: offset }
|
|
256
|
+
})
|
|
257
|
+
offset += splited2d[a][0].length
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
forward(logits) {
|
|
261
|
+
const maxLogits = this.cacheMaxCache(logits)
|
|
262
|
+
const sumes = this.cacheSumCalculate(maxLogits, logits)
|
|
263
|
+
const splitedLogits = split2DByColumns(logits)
|
|
264
|
+
const result = Array.from({ length: this.contextSize }, () => [])
|
|
265
|
+
for (let i = 0; i < splitedLogits.length; i++) {
|
|
266
|
+
const out = this.cacheSF[i](splitedLogits[i], sumes, maxLogits)
|
|
267
|
+
for (let a = 0; a < this.contextSize; a++) {
|
|
268
|
+
result[a].push(...out[a])
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
maxLogits.delete()
|
|
272
|
+
sumes.delete()
|
|
273
|
+
return result
|
|
274
|
+
}
|
|
275
|
+
backward(probs, target) {
|
|
276
|
+
const splitedProbs = split2DByColumns(probs)
|
|
277
|
+
const result = Array.from({ length: this.contextSize }, () => [])
|
|
278
|
+
for (let i = 0; i < this.cacheSB.length; i++) {
|
|
279
|
+
const out = this.cacheSB[i](splitedProbs[i], target)
|
|
280
|
+
for (let a = 0; a < this.contextSize; a++) {
|
|
281
|
+
result[a].push(...out[a])
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return result
|
|
285
|
+
}
|
|
286
|
+
crossEntropy(probs, target) {
|
|
287
|
+
const losses = this.calculateLoss(probs, target)
|
|
288
|
+
const loss = this.avgLoss(losses)
|
|
289
|
+
losses.delete()
|
|
290
|
+
return loss
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
export class GPUHiddenLayer {
|
|
297
|
+
cacheHF = [];
|
|
298
|
+
cacheHB = []
|
|
299
|
+
contextSize = 0;
|
|
300
|
+
input = [];
|
|
301
|
+
initKernel(probs, contextSize) {
|
|
302
|
+
const splitL = split2DByColumns(probs)
|
|
303
|
+
this.contextSize = contextSize;
|
|
304
|
+
for (let a = 0; a < splitL.length; a++) {
|
|
305
|
+
this.cacheHF[a] = gpu.createKernel(function (xs) {
|
|
306
|
+
const x = xs[this.thread.y][this.thread.x]
|
|
307
|
+
return x > 0 ? x : x * 0.02
|
|
308
|
+
}).setOutput([splitL[a].length, contextSize])
|
|
309
|
+
this.cacheHB[a] = gpu.createKernel(function (outputGradient, input) {
|
|
310
|
+
const grad = outputGradient[this.thread.y][this.thread.x]
|
|
311
|
+
const x = input[this.thread.y][this.thread.x]
|
|
312
|
+
return x > 0 ? grad : grad * 0.02
|
|
313
|
+
}, {
|
|
314
|
+
output: [splitL[a].length, contextSize]
|
|
315
|
+
})
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
forward(logits) {
|
|
319
|
+
if (this.cacheHF.length == 0) {
|
|
320
|
+
this.initKernel(logits, logits.length)
|
|
321
|
+
}
|
|
322
|
+
this.input = logits
|
|
323
|
+
const split = split2DByColumns(logits)
|
|
324
|
+
const result = Array.from({ length: this.contextSize }, () => new Float32Array(logits[0].length))
|
|
325
|
+
let offset = 0;
|
|
326
|
+
for (let a = 0; a < split.length; a++) {
|
|
327
|
+
const out = this.cacheHF[a](split[a])
|
|
328
|
+
for (let i = 0; i < this.contextSize; i++) {
|
|
329
|
+
result[i].set(out[i], offset)
|
|
330
|
+
}
|
|
331
|
+
offset += out[0].length
|
|
332
|
+
}
|
|
333
|
+
return result
|
|
334
|
+
}
|
|
335
|
+
backward(outputGradient) {
|
|
336
|
+
const splitOut = split2DByColumns(outputGradient)
|
|
337
|
+
const inputSp = split2DByColumns(this.input)
|
|
338
|
+
const result = Array.from({ length: this.contextSize }, () => new Float32Array(outputGradient[0].length))
|
|
339
|
+
let offset = 0;
|
|
340
|
+
for (let a = 0; a < splitOut.length; a++) {
|
|
341
|
+
const out = this.cacheHB[a](splitOut[a], inputSp[a])
|
|
342
|
+
for (let i = 0; i < this.contextSize; i++) {
|
|
343
|
+
result[i].set(out[i], offset)
|
|
344
|
+
}
|
|
345
|
+
offset += out[0].length
|
|
346
|
+
}
|
|
347
|
+
return result
|
|
348
|
+
}
|
|
349
|
+
ClearInputCache() {
|
|
350
|
+
this.input = []
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
export class GPUEmbedding {
|
|
354
|
+
vocabSize = 0;
|
|
355
|
+
embeddingSize = 0;
|
|
356
|
+
contextSize = 0;
|
|
357
|
+
chunkStarts = [];
|
|
358
|
+
chunkEnds = [];
|
|
359
|
+
vectors = [];
|
|
360
|
+
|
|
361
|
+
lookupKernels = [];
|
|
362
|
+
cacheEB = [];
|
|
363
|
+
|
|
364
|
+
indexes = [];
|
|
365
|
+
positions = [];
|
|
366
|
+
|
|
367
|
+
constructor(vectors, vocabSize, embeddingSize, contextSize) {
|
|
368
|
+
this.embeddingSize = embeddingSize;
|
|
369
|
+
this.contextSize = contextSize;
|
|
370
|
+
this.vocabSize = vocabSize;
|
|
371
|
+
|
|
372
|
+
const splitedVectors = spilt2DArray(vectors);
|
|
373
|
+
let start = 0;
|
|
374
|
+
process.stdout.write(
|
|
375
|
+
`\r[DEBUG] Saving Vectors in VRAM${".".repeat(Math.floor(Math.random() * 4))}`
|
|
376
|
+
);
|
|
377
|
+
for (let a = 0; a < splitedVectors.length; a++) {
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
const length = splitedVectors[a].length;
|
|
381
|
+
|
|
382
|
+
this.chunkStarts[a] = start;
|
|
383
|
+
this.chunkEnds[a] = start + length;
|
|
384
|
+
|
|
385
|
+
const vectorsK = gpu.createKernel(function (x) {
|
|
386
|
+
return x[this.thread.x][this.thread.y];
|
|
387
|
+
}, {
|
|
388
|
+
output: [length, embeddingSize],
|
|
389
|
+
pipeline: true,
|
|
390
|
+
immutable: true
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
this.vectors[a] = vectorsK(splitedVectors[a]);
|
|
394
|
+
vectorsK.destroy();
|
|
395
|
+
|
|
396
|
+
this.cacheEB[a] = gpu.createKernel(function (vectors, indexes, positions, outGradient, learningRate) {
|
|
397
|
+
const tokenIndex = this.thread.x;
|
|
398
|
+
const dim = this.thread.y;
|
|
399
|
+
let gradient = 0;
|
|
400
|
+
|
|
401
|
+
for (let p = 0; p < this.constants.contextSize; p++) {
|
|
402
|
+
const index = indexes[p];
|
|
403
|
+
const pos = positions[p];
|
|
404
|
+
|
|
405
|
+
if (index >= 0 && index == tokenIndex && pos >= 0) {
|
|
406
|
+
gradient += outGradient[pos][dim];
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return vectors[tokenIndex][dim] - learningRate * gradient;
|
|
411
|
+
}, {
|
|
412
|
+
output: [length, embeddingSize],
|
|
413
|
+
immutable: true,
|
|
414
|
+
pipeline: true,
|
|
415
|
+
constants: {
|
|
416
|
+
contextSize: this.contextSize
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
start += length;
|
|
421
|
+
|
|
422
|
+
}
|
|
423
|
+
process.stdout.write(
|
|
424
|
+
`\n[DEBUG] Vectors are Saved in VRAM!`
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
process.stdout.write("\n");
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
getLookupKernel(count) {
|
|
431
|
+
if (!this.lookupKernels[count]) {
|
|
432
|
+
this.lookupKernels[count] = gpu.createKernel(function (vectors, indexes) {
|
|
433
|
+
const dim = this.thread.x;
|
|
434
|
+
const pos = this.thread.y;
|
|
435
|
+
const index = Math.floor(indexes[pos]);
|
|
436
|
+
|
|
437
|
+
return vectors[index][dim];
|
|
438
|
+
}, {
|
|
439
|
+
output: [
|
|
440
|
+
this.embeddingSize,
|
|
441
|
+
count
|
|
442
|
+
]
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return this.lookupKernels[count];
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
forward(tokens) {
|
|
450
|
+
const result = Array.from(
|
|
451
|
+
{ length: this.contextSize },
|
|
452
|
+
() => new Float32Array(this.embeddingSize)
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
for (let i = 0; i < this.vectors.length; i++) {
|
|
456
|
+
|
|
457
|
+
const start = this.chunkStarts[i];
|
|
458
|
+
const end = this.chunkEnds[i];
|
|
459
|
+
|
|
460
|
+
const indexArray = new Float32Array(this.contextSize);
|
|
461
|
+
const positionArray = new Int32Array(this.contextSize);
|
|
462
|
+
|
|
463
|
+
for (let p = 0; p < this.contextSize; p++) {
|
|
464
|
+
indexArray[p] = -1;
|
|
465
|
+
positionArray[p] = -1;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
let count = 0;
|
|
469
|
+
|
|
470
|
+
for (let pos = 0; pos < tokens.length && count < this.contextSize; pos++) {
|
|
471
|
+
const token = tokens[pos];
|
|
472
|
+
if (token >= start && token < end) {
|
|
473
|
+
indexArray[count] = token - start;
|
|
474
|
+
positionArray[count] = pos;
|
|
475
|
+
count++;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
this.indexes[i] = indexArray;
|
|
479
|
+
this.positions[i] = positionArray;
|
|
480
|
+
if (count === 0) {
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
const compactIndexes = indexArray.subarray(0, count);
|
|
484
|
+
const lookup = this.getLookupKernel(count);
|
|
485
|
+
const out = lookup(
|
|
486
|
+
this.vectors[i],
|
|
487
|
+
compactIndexes
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
for (let j = 0; j < count; j++) {
|
|
491
|
+
const originalPosition = positionArray[j];
|
|
492
|
+
for (let dim = 0; dim < this.embeddingSize; dim++) {
|
|
493
|
+
result[originalPosition][dim] = out[j][dim];
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return result;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
backward(outGradient, learningRate) {
|
|
501
|
+
for (let a = 0; a < this.vectors.length; a++) {
|
|
502
|
+
if (!this.indexes[a]) {
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
let hasToken = false;
|
|
507
|
+
|
|
508
|
+
for (let p = 0; p < this.contextSize; p++) {
|
|
509
|
+
if (this.indexes[a][p] >= 0) {
|
|
510
|
+
hasToken = true;
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (!hasToken) {
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const oldVec = this.vectors[a];
|
|
520
|
+
|
|
521
|
+
this.vectors[a] = this.cacheEB[a](
|
|
522
|
+
oldVec,
|
|
523
|
+
this.indexes[a],
|
|
524
|
+
this.positions[a],
|
|
525
|
+
outGradient,
|
|
526
|
+
learningRate
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
if (oldVec && oldVec.delete) {
|
|
530
|
+
oldVec.delete();
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
destroy() {
|
|
536
|
+
for (const vector of this.vectors) {
|
|
537
|
+
if (vector && vector.delete) {
|
|
538
|
+
vector.delete();
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
for (const kernel of this.lookupKernels) {
|
|
543
|
+
if (kernel) {
|
|
544
|
+
kernel.destroy();
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
for (const kernel of this.cacheEB) {
|
|
549
|
+
if (kernel) {
|
|
550
|
+
kernel.destroy();
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
this.vectors = [];
|
|
555
|
+
this.lookupKernels = [];
|
|
556
|
+
this.cacheEB = [];
|
|
557
|
+
this.indexes = [];
|
|
558
|
+
this.positions = [];
|
|
559
|
+
this.chunkStarts = [];
|
|
560
|
+
this.chunkEnds = [];
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
export class GELUGPU {
|
|
565
|
+
cacheGF;
|
|
566
|
+
cacheGB;
|
|
567
|
+
constructor(embeddingSize, contextSize) {
|
|
568
|
+
this.cacheGF = gpu.createKernel(function (input) {
|
|
569
|
+
const k = this.constants.kVal;
|
|
570
|
+
const x = input[this.thread.y][this.thread.x]
|
|
571
|
+
const u = k * (x + 0.044715 * x * x * x)
|
|
572
|
+
return 0.5 * x * (1 + Math.tanh(u))
|
|
573
|
+
}, {
|
|
574
|
+
output: [embeddingSize, contextSize],
|
|
575
|
+
constants: {
|
|
576
|
+
kVal: Math.sqrt(2 / Math.PI)
|
|
577
|
+
}
|
|
578
|
+
})
|
|
579
|
+
this.cacheGB = gpu.createKernel(function (input, gradient) {
|
|
580
|
+
const x = input[this.thread.y][this.thread.x]
|
|
581
|
+
const k = this.constants.kVal;
|
|
582
|
+
const x3 = x * x * x;
|
|
583
|
+
const u = k * (x + 0.044715 * x3)
|
|
584
|
+
const t = Math.tanh(u)
|
|
585
|
+
const sech2 = 1 - t * t
|
|
586
|
+
const duDx = k * (1 + 3 * 0.044715 * x * x)
|
|
587
|
+
const geluGrad =
|
|
588
|
+
0.5 * (1 + t) +
|
|
589
|
+
0.5 * x * sech2 * duDx;
|
|
590
|
+
return gradient[this.thread.y][this.thread.x] * geluGrad
|
|
591
|
+
}, {
|
|
592
|
+
output: [embeddingSize, contextSize],
|
|
593
|
+
constants: {
|
|
594
|
+
kVal: Math.sqrt(2 / Math.PI)
|
|
595
|
+
}
|
|
596
|
+
})
|
|
597
|
+
}
|
|
598
|
+
forward(input) {
|
|
599
|
+
return this.cacheGF(input)
|
|
600
|
+
}
|
|
601
|
+
backward(dOutput, input) {
|
|
602
|
+
return this.cacheGB(input, dOutput)
|
|
603
|
+
}
|
|
604
|
+
}
|