079project 3.0.0 → 5.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/GroupStarter.cjs +396 -30
- package/forwarder.js +312 -55
- package/main_Serve.cjs +583 -105
- package/main_Study.cjs +581 -68
- package/notes.txt +241 -0
- package/package.json +7 -1
- package/note.txt +0 -5
- package/notebook.txt +0 -8
package/main_Study.cjs
CHANGED
|
@@ -41,6 +41,71 @@ global.config = {
|
|
|
41
41
|
masterPortOfMain: process.argv[2],
|
|
42
42
|
emitExitport: process.argv[3] || 8641
|
|
43
43
|
};
|
|
44
|
+
// ...existing code...
|
|
45
|
+
const vm = require('vm'); // 新增:沙箱编译
|
|
46
|
+
// ...existing code...
|
|
47
|
+
|
|
48
|
+
// ==== 内置激活/传递函数注册表 + 安全编译工具 ====
|
|
49
|
+
const BuiltinActivations = {
|
|
50
|
+
identity: (x) => x,
|
|
51
|
+
relu: (x) => (x > 0 ? x : 0),
|
|
52
|
+
leaky_relu: (x) => (x > 0 ? x : 0.01 * x),
|
|
53
|
+
tanh: (x) => Math.tanh(x),
|
|
54
|
+
sigmoid: (x) => 1 / (1 + Math.exp(-x)),
|
|
55
|
+
elu: (x) => (x >= 0 ? x : (Math.exp(x) - 1)),
|
|
56
|
+
softplus: (x) => Math.log(1 + Math.exp(x)),
|
|
57
|
+
// 近似 GELU
|
|
58
|
+
gelu: (x) => 0.5 * x * (1 + Math.tanh(Math.sqrt(2 / Math.PI) * (x + 0.044715 * Math.pow(x, 3))))
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const BuiltinTransfers = {
|
|
62
|
+
// 线性衰减:next = value - decayK*weight*(dirMult)
|
|
63
|
+
linear: (value, weight, decayK, ctx) => {
|
|
64
|
+
const dm = ctx?.direction === 0 ? (ctx?.bidirectionalMultiplier ?? 1.2) : (ctx?.directionalMultiplier ?? 0.7);
|
|
65
|
+
return value - (decayK * weight * dm);
|
|
66
|
+
},
|
|
67
|
+
// 指数衰减:next = value * exp(-decayK*weight*(dirMult))
|
|
68
|
+
exp: (value, weight, decayK, ctx) => {
|
|
69
|
+
const dm = ctx?.direction === 0 ? (ctx?.bidirectionalMultiplier ?? 1.2) : (ctx?.directionalMultiplier ?? 0.7);
|
|
70
|
+
return value * Math.exp(-(decayK * weight * dm));
|
|
71
|
+
},
|
|
72
|
+
// 反比例:next = value / (1 + decayK*weight*(dirMult))
|
|
73
|
+
inverse: (value, weight, decayK, ctx) => {
|
|
74
|
+
const dm = ctx?.direction === 0 ? (ctx?.bidirectionalMultiplier ?? 1.2) : (ctx?.directionalMultiplier ?? 0.7);
|
|
75
|
+
return value / (1 + (decayK * weight * dm));
|
|
76
|
+
},
|
|
77
|
+
// 截断线性:线性后下限截断为0,上限截断为value
|
|
78
|
+
capped: (value, weight, decayK, ctx) => {
|
|
79
|
+
const dm = ctx?.direction === 0 ? (ctx?.bidirectionalMultiplier ?? 1.2) : (ctx?.directionalMultiplier ?? 0.7);
|
|
80
|
+
const raw = value - (decayK * weight * dm);
|
|
81
|
+
return Math.max(0, Math.min(value, raw));
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
function compileCustomFunctionSafely(source, argNames, fallback) {
|
|
86
|
+
try {
|
|
87
|
+
const ctx = vm.createContext({ Math });
|
|
88
|
+
// 如果用户提供的是“表达式”,包一层 return
|
|
89
|
+
const body = source.includes('return') || source.includes('=>') || source.includes('function')
|
|
90
|
+
? source
|
|
91
|
+
: `return (${source});`;
|
|
92
|
+
|
|
93
|
+
// 统一包成 function 体
|
|
94
|
+
const wrapper = `(function(${argNames.join(',')}) { "use strict"; ${body} })`;
|
|
95
|
+
const script = new vm.Script(wrapper, { timeout: 50 });
|
|
96
|
+
const fn = script.runInContext(ctx, { timeout: 50 });
|
|
97
|
+
if (typeof fn !== 'function') return fallback;
|
|
98
|
+
// 再包一层,避免传入异常导致抛出
|
|
99
|
+
return (...args) => {
|
|
100
|
+
try { return fn(...args); } catch (_e) { return fallback(...args); }
|
|
101
|
+
};
|
|
102
|
+
} catch (_e) {
|
|
103
|
+
return fallback;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// ...existing code...
|
|
107
|
+
|
|
108
|
+
// 顶部 modelDefaults 增加参数(与本文件后半段的重复默认值保持一致)
|
|
44
109
|
const modelDefaults = {
|
|
45
110
|
decayFactor: 0.5,
|
|
46
111
|
maxMemeWords: 100,
|
|
@@ -52,7 +117,12 @@ const modelDefaults = {
|
|
|
52
117
|
decay: 1,
|
|
53
118
|
decayK: 1,
|
|
54
119
|
maxLen: 16,
|
|
55
|
-
edgeWeight: 1
|
|
120
|
+
edgeWeight: 1,
|
|
121
|
+
// 新增:激活/传递函数选择与自定义
|
|
122
|
+
activationType: 'relu', // identity|relu|leaky_relu|tanh|sigmoid|elu|softplus|gelu|custom
|
|
123
|
+
transferType: 'linear', // linear|exp|inverse|capped|custom
|
|
124
|
+
activationCustom: '', // 自定义激活函数源码/表达式:f(x) 或 return ...
|
|
125
|
+
transferCustom: '' // 自定义传递函数源码/表达式:f(value, weight, decayK, ctx) 或 return ...
|
|
56
126
|
};
|
|
57
127
|
const currentModelParams = { ...modelDefaults };
|
|
58
128
|
// ...existing code...
|
|
@@ -203,7 +273,318 @@ async function deltaCloneRuntime(prevClone, srcRuntime) {
|
|
|
203
273
|
clone.__deltaIndexes.vocabHash = srcVocabHash;
|
|
204
274
|
return clone;
|
|
205
275
|
}
|
|
276
|
+
// ...existing code...
|
|
277
|
+
|
|
278
|
+
// ===== Linear Algebra Backend (CSR + SpMM + Hash Embedding + PCA/UMAP) =====
|
|
279
|
+
class CSR {
|
|
280
|
+
constructor(rowPtr, colIdx, values, nRows, nCols) {
|
|
281
|
+
this.rowPtr = rowPtr; // Uint32Array length nRows+1
|
|
282
|
+
this.colIdx = colIdx; // Uint32Array length nnz
|
|
283
|
+
this.values = values; // Float32Array length nnz
|
|
284
|
+
this.nRows = nRows | 0;
|
|
285
|
+
this.nCols = nCols | 0;
|
|
286
|
+
this.nnz = this.values.length | 0;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
class TensorEngine {
|
|
291
|
+
// y = A x (A in CSR, x dense Float32Array)
|
|
292
|
+
spmm(csr, x, out = null) {
|
|
293
|
+
const { rowPtr, colIdx, values, nRows } = csr;
|
|
294
|
+
const y = out instanceof Float32Array && out.length === nRows ? out : new Float32Array(nRows);
|
|
295
|
+
for (let i = 0; i < nRows; i++) {
|
|
296
|
+
let s = 0.0;
|
|
297
|
+
const start = rowPtr[i], end = rowPtr[i + 1];
|
|
298
|
+
for (let p = start; p < end; p++) {
|
|
299
|
+
s += values[p] * x[colIdx[p]];
|
|
300
|
+
}
|
|
301
|
+
y[i] = s;
|
|
302
|
+
}
|
|
303
|
+
return y;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// x = a*x + b*y
|
|
307
|
+
axpby(a, x, b, y, out = null) {
|
|
308
|
+
const n = x.length | 0;
|
|
309
|
+
const z = out instanceof Float32Array && out.length === n ? out : new Float32Array(n);
|
|
310
|
+
for (let i = 0; i < n; i++) z[i] = a * x[i] + b * y[i];
|
|
311
|
+
return z;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
l2NormalizeRows(mat, nRows, nCols) {
|
|
315
|
+
for (let i = 0; i < nRows; i++) {
|
|
316
|
+
let s = 0.0, base = i * nCols;
|
|
317
|
+
for (let j = 0; j < nCols; j++) { const v = mat[base + j]; s += v * v; }
|
|
318
|
+
s = Math.sqrt(s) || 1.0;
|
|
319
|
+
for (let j = 0; j < nCols; j++) mat[base + j] /= s;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
dot(a, b) {
|
|
324
|
+
let s = 0.0;
|
|
325
|
+
for (let i = 0; i < a.length; i++) s += a[i] * b[i];
|
|
326
|
+
return s;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// 迭代式传播(不跟踪路径,面向速度)
|
|
330
|
+
// return Float32Array activation over rows
|
|
331
|
+
iteratePropagation(csr, seeds, steps, actFn, decayK, damp = 0.02) {
|
|
332
|
+
const n = csr.nRows | 0;
|
|
333
|
+
let x = new Float32Array(n);
|
|
334
|
+
for (const [row, v] of seeds) { if (row >= 0 && row < n) x[row] += v; }
|
|
335
|
+
|
|
336
|
+
let y = new Float32Array(n);
|
|
337
|
+
for (let t = 0; t < steps; t++) {
|
|
338
|
+
// y = A x
|
|
339
|
+
this.spmm(csr, x, y);
|
|
340
|
+
// x = act(x + y - decayK*damp*x)
|
|
341
|
+
for (let i = 0; i < n; i++) {
|
|
342
|
+
const raw = x[i] + y[i] - (decayK * damp * x[i]);
|
|
343
|
+
x[i] = actFn(raw);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return x; // final activation
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// 词-模因hash嵌入:fixed-D feature hashing + L2 normalize
|
|
351
|
+
class GraphTensorBridge {
|
|
352
|
+
constructor(runtime) {
|
|
353
|
+
this.rt = runtime;
|
|
354
|
+
this.rowIndex = new Map(); // memeID -> row
|
|
355
|
+
this.rows = []; // row -> memeID
|
|
356
|
+
this.emb = null; // Float32Array [N*D]
|
|
357
|
+
this.dim = 0;
|
|
358
|
+
this.csrAll = null; // CSR (all directions)
|
|
359
|
+
this._multi = null; // {all,bi,out,in, id2row, row2id}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
static fnv1a32(str) {
|
|
363
|
+
let h = 0x811c9dc5;
|
|
364
|
+
for (let i = 0; i < str.length; i++) {
|
|
365
|
+
h ^= str.charCodeAt(i);
|
|
366
|
+
h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0;
|
|
367
|
+
}
|
|
368
|
+
return h >>> 0;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// 构建行索引,只遍历当前窗口
|
|
372
|
+
rebuildRowIndex() {
|
|
373
|
+
this.rowIndex.clear();
|
|
374
|
+
this.rows.length = 0;
|
|
375
|
+
const pts = this.rt.graph.getAllPoints();
|
|
376
|
+
for (let i = 0; i < pts.length; i++) {
|
|
377
|
+
const id = pts[i].pointID;
|
|
378
|
+
this.rowIndex.set(id, i);
|
|
379
|
+
this.rows.push(id);
|
|
380
|
+
}
|
|
381
|
+
return pts.length;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Hash embedding: D默认512
|
|
385
|
+
buildEmbeddings(D = 512) {
|
|
386
|
+
const N = this.rebuildRowIndex();
|
|
387
|
+
this.dim = D | 0;
|
|
388
|
+
this.emb = new Float32Array(N * D);
|
|
389
|
+
for (let r = 0; r < N; r++) {
|
|
390
|
+
const memeId = this.rows[r];
|
|
391
|
+
const words = this.rt.kvm.get(memeId) || [];
|
|
392
|
+
const base = r * D;
|
|
393
|
+
for (let k = 0; k < words.length; k++) {
|
|
394
|
+
const w = String(words[k] || '').toLowerCase();
|
|
395
|
+
const idx = GraphTensorBridge.fnv1a32(w) % D;
|
|
396
|
+
this.emb[base + idx] += 1.0;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
this.rt.tensor.l2NormalizeRows(this.emb, N, D);
|
|
400
|
+
return { N, D };
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// 构建多通道CSR(每行Top-K;all/bi/out,同时构建out的转置作为in)
|
|
404
|
+
buildMultiOrderCSR(topK = 64) {
|
|
405
|
+
const pts = this.rt.graph.getAllPoints();
|
|
406
|
+
const N = pts.length | 0;
|
|
407
|
+
const id2row = this.rowIndex;
|
|
408
|
+
const row2id = this.rows.slice();
|
|
409
|
+
|
|
410
|
+
const buildChannel = (filterFn) => {
|
|
411
|
+
const rows = new Array(N);
|
|
412
|
+
let nnz = 0;
|
|
413
|
+
for (let i = 0; i < N; i++) {
|
|
414
|
+
const conns = (pts[i].connect || []).filter(filterFn);
|
|
415
|
+
// 取Top-K(按权重大->小)
|
|
416
|
+
conns.sort((a, b) => b[0] - a[0]);
|
|
417
|
+
const pruned = conns.slice(0, topK);
|
|
418
|
+
const cols = [];
|
|
419
|
+
const vals = [];
|
|
420
|
+
for (const [w, tgt] of pruned) {
|
|
421
|
+
const c = id2row.get(tgt);
|
|
422
|
+
if (c === undefined) continue;
|
|
423
|
+
cols.push(c);
|
|
424
|
+
vals.push((typeof w === 'number' && isFinite(w)) ? w : 1.0);
|
|
425
|
+
}
|
|
426
|
+
rows[i] = { cols, vals };
|
|
427
|
+
nnz += cols.length;
|
|
428
|
+
}
|
|
429
|
+
const rowPtr = new Uint32Array(N + 1);
|
|
430
|
+
const colIdx = new Uint32Array(nnz);
|
|
431
|
+
const values = new Float32Array(nnz);
|
|
432
|
+
let p = 0;
|
|
433
|
+
for (let i = 0; i < N; i++) {
|
|
434
|
+
rowPtr[i] = p;
|
|
435
|
+
const { cols, vals } = rows[i];
|
|
436
|
+
for (let j = 0; j < cols.length; j++) {
|
|
437
|
+
colIdx[p] = cols[j];
|
|
438
|
+
values[p] = vals[j];
|
|
439
|
+
p++;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
rowPtr[N] = p;
|
|
443
|
+
return new CSR(rowPtr, colIdx, values, N, N);
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const ALL = buildChannel(_ => true);
|
|
447
|
+
const BI = buildChannel(([, , d]) => d === 0);
|
|
448
|
+
const OUT = buildChannel(([, , d]) => d === 2);
|
|
206
449
|
|
|
450
|
+
// IN = transpose(OUT)
|
|
451
|
+
const IN = this.transposeCSR(OUT);
|
|
452
|
+
|
|
453
|
+
this._multi = { all: ALL, bi: BI, out: OUT, in: IN, id2row, row2id };
|
|
454
|
+
this.csrAll = ALL;
|
|
455
|
+
return this._multi;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
transposeCSR(A) {
|
|
459
|
+
const { nRows, nCols, rowPtr, colIdx, values } = A;
|
|
460
|
+
const nnz = values.length;
|
|
461
|
+
const counts = new Uint32Array(nCols);
|
|
462
|
+
for (let p = 0; p < nnz; p++) counts[colIdx[p]]++;
|
|
463
|
+
const rowPtrT = new Uint32Array(nCols + 1);
|
|
464
|
+
for (let i = 0; i < nCols; i++) rowPtrT[i + 1] = rowPtrT[i] + counts[i];
|
|
465
|
+
const colIdxT = new Uint32Array(nnz);
|
|
466
|
+
const valuesT = new Float32Array(nnz);
|
|
467
|
+
const cursor = rowPtrT.slice();
|
|
468
|
+
for (let i = 0; i < nRows; i++) {
|
|
469
|
+
for (let p = rowPtr[i]; p < rowPtr[i + 1]; p++) {
|
|
470
|
+
const j = colIdx[p];
|
|
471
|
+
const q = cursor[j]++;
|
|
472
|
+
colIdxT[q] = i;
|
|
473
|
+
valuesT[q] = values[p];
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return new CSR(rowPtrT, colIdxT, valuesT, nCols, nRows);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
get multi() { return this._multi; }
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
class DimReducer {
|
|
483
|
+
// PCA(2D) 快速近似:power-iteration + 投影
|
|
484
|
+
pca2D(emb, N, D, iters = 6) {
|
|
485
|
+
// 均值中心化
|
|
486
|
+
const mean = new Float32Array(D);
|
|
487
|
+
for (let i = 0; i < N; i++) {
|
|
488
|
+
const base = i * D;
|
|
489
|
+
for (let j = 0; j < D; j++) mean[j] += emb[base + j];
|
|
490
|
+
}
|
|
491
|
+
for (let j = 0; j < D; j++) mean[j] /= Math.max(1, N);
|
|
492
|
+
const X = new Float32Array(N * D);
|
|
493
|
+
for (let i = 0; i < N; i++) {
|
|
494
|
+
const base = i * D;
|
|
495
|
+
for (let j = 0; j < D; j++) X[base + j] = emb[base + j] - mean[j];
|
|
496
|
+
}
|
|
497
|
+
// 随机初始向量
|
|
498
|
+
let v1 = new Float32Array(D); for (let j = 0; j < D; j++) v1[j] = Math.random() - 0.5;
|
|
499
|
+
let v2 = new Float32Array(D); for (let j = 0; j < D; j++) v2[j] = Math.random() - 0.5;
|
|
500
|
+
|
|
501
|
+
const mulCov = (v) => { // X^T X v
|
|
502
|
+
const tmp = new Float32Array(D);
|
|
503
|
+
for (let i = 0; i < N; i++) {
|
|
504
|
+
const base = i * D;
|
|
505
|
+
// s = x_i dot v
|
|
506
|
+
let s = 0.0;
|
|
507
|
+
for (let j = 0; j < D; j++) s += X[base + j] * v[j];
|
|
508
|
+
for (let j = 0; j < D; j++) tmp[j] += X[base + j] * s;
|
|
509
|
+
}
|
|
510
|
+
return tmp;
|
|
511
|
+
};
|
|
512
|
+
const normalize = (v) => {
|
|
513
|
+
let s = 0.0; for (let j = 0; j < D; j++) s += v[j] * v[j];
|
|
514
|
+
s = Math.sqrt(s) || 1.0;
|
|
515
|
+
for (let j = 0; j < D; j++) v[j] /= s;
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
for (let t = 0; t < iters; t++) { v1 = mulCov(v1); normalize(v1); }
|
|
519
|
+
// v2 去除与 v1 的分量
|
|
520
|
+
for (let t = 0; t < iters; t++) {
|
|
521
|
+
v2 = mulCov(v2);
|
|
522
|
+
// Gram-Schmidt
|
|
523
|
+
let dot = 0.0; for (let j = 0; j < D; j++) dot += v2[j] * v1[j];
|
|
524
|
+
for (let j = 0; j < D; j++) v2[j] -= dot * v1[j];
|
|
525
|
+
normalize(v2);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// 投影到2D
|
|
529
|
+
const out = new Float32Array(N * 2);
|
|
530
|
+
for (let i = 0; i < N; i++) {
|
|
531
|
+
const base = i * D;
|
|
532
|
+
let x = 0.0, y = 0.0;
|
|
533
|
+
for (let j = 0; j < D; j++) {
|
|
534
|
+
const xv = X[base + j];
|
|
535
|
+
x += xv * v1[j];
|
|
536
|
+
y += xv * v2[j];
|
|
537
|
+
}
|
|
538
|
+
out[2 * i + 0] = x;
|
|
539
|
+
out[2 * i + 1] = y;
|
|
540
|
+
}
|
|
541
|
+
return out;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// 若安装 umap-js 则使用 UMAP,否则回退 PCA
|
|
545
|
+
project2D(emb, N, D, method = 'auto') {
|
|
546
|
+
if (method === 'pca') return this.pca2D(emb, N, D);
|
|
547
|
+
if (method === 'umap' || method === 'auto') {
|
|
548
|
+
try {
|
|
549
|
+
// 按需加载
|
|
550
|
+
const { UMAP } = require('umap-js');
|
|
551
|
+
const umap = new UMAP({ nComponents: 2, nNeighbors: 15, minDist: 0.1 });
|
|
552
|
+
// umap-js 需要普通数组
|
|
553
|
+
const data = new Array(N);
|
|
554
|
+
for (let i = 0; i < N; i++) {
|
|
555
|
+
const row = new Array(D);
|
|
556
|
+
const base = i * D;
|
|
557
|
+
for (let j = 0; j < D; j++) row[j] = emb[base + j];
|
|
558
|
+
data[i] = row;
|
|
559
|
+
}
|
|
560
|
+
const coords = umap.fit(data);
|
|
561
|
+
const out = new Float32Array(N * 2);
|
|
562
|
+
for (let i = 0; i < N; i++) { out[2 * i] = coords[i][0]; out[2 * i + 1] = coords[i][1]; }
|
|
563
|
+
return out;
|
|
564
|
+
} catch (_) {
|
|
565
|
+
return this.pca2D(emb, N, D);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return this.pca2D(emb, N, D);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// 提供多通道邻接卷(all/bi/out/in),供三维张量视角
|
|
573
|
+
class MultiOrderAdjacency {
|
|
574
|
+
constructor(runtime) {
|
|
575
|
+
this.rt = runtime;
|
|
576
|
+
}
|
|
577
|
+
rebuild(topK = 64, Demb = 512) {
|
|
578
|
+
if (!this.rt.tensorBridge) this.rt.tensorBridge = new GraphTensorBridge(this.rt);
|
|
579
|
+
const gb = this.rt.tensorBridge;
|
|
580
|
+
gb.buildEmbeddings(Demb);
|
|
581
|
+
const multi = gb.buildMultiOrderCSR(topK);
|
|
582
|
+
return multi;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
// ===== End of Linear Algebra Backend =====
|
|
586
|
+
|
|
587
|
+
// ...existing code...
|
|
207
588
|
// 按需检索器:在用户输入(processInput)时触发网络检索与增量学习
|
|
208
589
|
class OnlineResearcher {
|
|
209
590
|
constructor(runtime, options = {}) {
|
|
@@ -2045,6 +2426,85 @@ class Runtime {
|
|
|
2045
2426
|
batchSizeMultiplier: 1
|
|
2046
2427
|
};
|
|
2047
2428
|
this.memeBarrier = new memeBarrier(this);
|
|
2429
|
+
this._act = BuiltinActivations.relu;
|
|
2430
|
+
this._transfer = BuiltinTransfers.linear;
|
|
2431
|
+
this._activationMeta = { activationType: 'relu', transferType: 'linear' };
|
|
2432
|
+
this.tensor = new TensorEngine();
|
|
2433
|
+
this.tensorBridge = new GraphTensorBridge(this);
|
|
2434
|
+
this.dimReducer = new DimReducer();
|
|
2435
|
+
this.multiAdj = new MultiOrderAdjacency(this);
|
|
2436
|
+
this._laReady = false; // 线代缓存是否就绪
|
|
2437
|
+
}
|
|
2438
|
+
// 重建线代缓存:Embedding + 多通道CSR
|
|
2439
|
+
rebuildLinearAlgebraCaches({ topK = 64, embDim = 512 } = {}) {
|
|
2440
|
+
try {
|
|
2441
|
+
this.multiAdj.rebuild(topK, embDim);
|
|
2442
|
+
this._laReady = !!(this.tensorBridge?.multi?.all);
|
|
2443
|
+
return {
|
|
2444
|
+
ok: this._laReady,
|
|
2445
|
+
nodes: this.tensorBridge?.rows?.length || 0,
|
|
2446
|
+
embDim
|
|
2447
|
+
};
|
|
2448
|
+
} catch (e) {
|
|
2449
|
+
this._laReady = false;
|
|
2450
|
+
return { ok: false, error: e.message };
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
// 导出CSR(all通道)
|
|
2455
|
+
exportSparseMatrix() {
|
|
2456
|
+
const m = this.tensorBridge?.multi;
|
|
2457
|
+
if (!m?.all) return null;
|
|
2458
|
+
const A = m.all;
|
|
2459
|
+
return {
|
|
2460
|
+
nRows: A.nRows, nCols: A.nCols, nnz: A.nnz,
|
|
2461
|
+
rowPtr: Array.from(A.rowPtr),
|
|
2462
|
+
colIdx: Array.from(A.colIdx),
|
|
2463
|
+
values: Array.from(A.values),
|
|
2464
|
+
rows: this.tensorBridge.rows.slice()
|
|
2465
|
+
};
|
|
2466
|
+
}
|
|
2467
|
+
// 高维->2D投影(PCA/UMAP)
|
|
2468
|
+
foldHighDimTo2D(method = 'auto') {
|
|
2469
|
+
const emb = this.tensorBridge?.emb;
|
|
2470
|
+
const N = this.tensorBridge?.rows?.length || 0;
|
|
2471
|
+
const D = this.tensorBridge?.dim || 0;
|
|
2472
|
+
if (!emb || !N || !D) return { ok: false, error: 'embedding not ready' };
|
|
2473
|
+
const coords = this.dimReducer.project2D(emb, N, D, method);
|
|
2474
|
+
const out = {};
|
|
2475
|
+
for (let i = 0; i < N; i++) out[this.tensorBridge.rows[i]] = [coords[2 * i], coords[2 * i + 1]];
|
|
2476
|
+
return { ok: true, dim: 2, points: out };
|
|
2477
|
+
}
|
|
2478
|
+
// 获取/设置激活-传递函数配置
|
|
2479
|
+
getActivationConfig() {
|
|
2480
|
+
return {
|
|
2481
|
+
activationType: this._activationMeta.activationType,
|
|
2482
|
+
transferType: this._activationMeta.transferType,
|
|
2483
|
+
activationCustom: this.config?.activationCustom || '',
|
|
2484
|
+
transferCustom: this.config?.transferCustom || ''
|
|
2485
|
+
};
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
setActivationConfig({ activationType, transferType, activationCustom, transferCustom } = {}) {
|
|
2489
|
+
const aType = String(activationType || this._activationMeta.activationType || 'relu');
|
|
2490
|
+
const tType = String(transferType || this._activationMeta.transferType || 'linear');
|
|
2491
|
+
|
|
2492
|
+
let act = BuiltinActivations[aType] || BuiltinActivations.relu;
|
|
2493
|
+
let tr = BuiltinTransfers[tType] || BuiltinTransfers.linear;
|
|
2494
|
+
|
|
2495
|
+
if (aType === 'custom' && activationCustom) {
|
|
2496
|
+
act = compileCustomFunctionSafely(activationCustom, ['x'], BuiltinActivations.relu);
|
|
2497
|
+
}
|
|
2498
|
+
if (tType === 'custom' && transferCustom) {
|
|
2499
|
+
tr = compileCustomFunctionSafely(transferCustom, ['value', 'weight', 'decayK', 'ctx'], BuiltinTransfers.linear);
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
this._act = (typeof act === 'function') ? act : BuiltinActivations.relu;
|
|
2503
|
+
this._transfer = (typeof tr === 'function') ? tr : BuiltinTransfers.linear;
|
|
2504
|
+
this._activationMeta = { activationType: aType, transferType: tType };
|
|
2505
|
+
this.config = this.config || {};
|
|
2506
|
+
this.config.activationCustom = activationCustom || this.config.activationCustom || '';
|
|
2507
|
+
this.config.transferCustom = transferCustom || this.config.transferCustom || '';
|
|
2048
2508
|
}
|
|
2049
2509
|
// 新增:应用可调参数(含 spiderMix / decayK / maxLen 等)
|
|
2050
2510
|
applyTunableParams(partial = {}) {
|
|
@@ -2579,76 +3039,47 @@ class Runtime {
|
|
|
2579
3039
|
* options.trackPath: 是否记录激活路径
|
|
2580
3040
|
* @returns {Object|Map} { signalMap, activationPaths } 或 signalMap
|
|
2581
3041
|
*/
|
|
3042
|
+
// 线代版多源扩散(不跟踪路径)
|
|
2582
3043
|
propagateSignalMultiSource(startIDs, strengths, decayK, maxStep, options = {}) {
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
// 节点信号累加表
|
|
2590
|
-
const signalMap = new Map();
|
|
2591
|
-
// 路径追踪表(可选)
|
|
2592
|
-
const activationPaths = trackPath ? new Map() : null;
|
|
2593
|
-
|
|
2594
|
-
// 初始化活跃队列,每个元素{id, value, from}
|
|
2595
|
-
let active = startIDs.map((id, i) => ({
|
|
2596
|
-
id,
|
|
2597
|
-
value: strengths[i],
|
|
2598
|
-
from: null // 起点无前驱
|
|
2599
|
-
}));
|
|
2600
|
-
|
|
2601
|
-
let step = 0;
|
|
3044
|
+
// 如果调用方需要trackPath,仍走旧逻辑
|
|
3045
|
+
if (options?.trackPath) {
|
|
3046
|
+
return super.propagateSignalMultiSource
|
|
3047
|
+
? super.propagateSignalMultiSource(startIDs, strengths, decayK, maxStep, options)
|
|
3048
|
+
: this._propagateFallback(startIDs, strengths, decayK, maxStep, options);
|
|
3049
|
+
}
|
|
2602
3050
|
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
3051
|
+
// 优先使用线代后端
|
|
3052
|
+
if (this._laReady && this.tensorBridge?.multi?.all) {
|
|
3053
|
+
const A = this.tensorBridge.multi.all;
|
|
3054
|
+
const id2row = this.tensorBridge.rowIndex;
|
|
3055
|
+
const seeds = [];
|
|
3056
|
+
for (let i = 0; i < startIDs.length; i++) {
|
|
3057
|
+
const r = id2row.get(startIDs[i]);
|
|
3058
|
+
if (r !== undefined) seeds.push([r, strengths[i] || 0]);
|
|
2609
3059
|
}
|
|
3060
|
+
if (!seeds.length) return new Map();
|
|
2610
3061
|
|
|
2611
|
-
const
|
|
2612
|
-
|
|
2613
|
-
|
|
3062
|
+
const actFn = this._act || ((x) => (x > 0 ? x : 0));
|
|
3063
|
+
const steps = Math.max(1, maxStep | 0);
|
|
3064
|
+
const x = this.tensor.iteratePropagation(A, seeds, steps, actFn, (decayK ?? 1));
|
|
2614
3065
|
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
if (trackPath) {
|
|
2620
|
-
if (!activationPaths.has(id)) activationPaths.set(id, []);
|
|
2621
|
-
if (from !== null) activationPaths.get(id).push(from);
|
|
2622
|
-
}
|
|
2623
|
-
|
|
2624
|
-
// 传播到邻居(不防环,允许信号多次叠加)
|
|
2625
|
-
const point = this.graph.points.get(id);
|
|
2626
|
-
if (point) {
|
|
2627
|
-
// 限制每个节点最多处理的邻居数量
|
|
2628
|
-
const MAX_NEIGHBORS = 30;
|
|
2629
|
-
const neighbors = point.connect.slice(0, MAX_NEIGHBORS);
|
|
2630
|
-
|
|
2631
|
-
for (const [weight, neighborID] of neighbors) {
|
|
2632
|
-
// 信号可按边权重调整(如weight越小越容易传播)
|
|
2633
|
-
const nextValue = value - decayK * weight;
|
|
2634
|
-
if (nextValue >= minSignal) {
|
|
2635
|
-
next.push({
|
|
2636
|
-
id: neighborID,
|
|
2637
|
-
value: nextValue,
|
|
2638
|
-
from: id
|
|
2639
|
-
});
|
|
2640
|
-
}
|
|
2641
|
-
}
|
|
2642
|
-
}
|
|
3066
|
+
// 返回Map<MemeID,value>
|
|
3067
|
+
const out = new Map();
|
|
3068
|
+
for (let i = 0; i < x.length; i++) {
|
|
3069
|
+
if (x[i] > 0) out.set(this.tensorBridge.rows[i], x[i]);
|
|
2643
3070
|
}
|
|
2644
|
-
|
|
2645
|
-
step++;
|
|
3071
|
+
return out;
|
|
2646
3072
|
}
|
|
2647
3073
|
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
3074
|
+
// 回退旧实现
|
|
3075
|
+
return this._propagateFallback(startIDs, strengths, decayK, maxStep, options);
|
|
3076
|
+
}
|
|
3077
|
+
|
|
3078
|
+
_propagateFallback(startIDs, strengths, decayK, maxStep, options) {
|
|
3079
|
+
// 使用文件里现有版本(保持兼容)
|
|
3080
|
+
// 这里直接调用之前定义的 Runtime.propagateSignalMultiSource 的原实现
|
|
3081
|
+
const fn = Object.getPrototypeOf(this).propagateSignalMultiSource;
|
|
3082
|
+
return fn.call(this, startIDs, strengths, decayK, maxStep, options);
|
|
2652
3083
|
}
|
|
2653
3084
|
// ...existing code...
|
|
2654
3085
|
|
|
@@ -4225,6 +4656,70 @@ async function main() {
|
|
|
4225
4656
|
if (String(process.env.ADV_AUTOSTART || '').toLowerCase() === 'true') {
|
|
4226
4657
|
adv.start();
|
|
4227
4658
|
}
|
|
4659
|
+
// ...existing code...
|
|
4660
|
+
app.post('/api/tensor/refresh', (req, res) => {
|
|
4661
|
+
try {
|
|
4662
|
+
const { topK = 64, embDim = 512 } = req.body || {};
|
|
4663
|
+
const rt = global.ctrlA?.runtime;
|
|
4664
|
+
if (!rt) return res.status(500).json({ ok: false, error: 'runtime missing' });
|
|
4665
|
+
const ret = rt.rebuildLinearAlgebraCaches({ topK, embDim });
|
|
4666
|
+
res.json({ ok: !!ret.ok, ...ret });
|
|
4667
|
+
} catch (e) {
|
|
4668
|
+
res.status(500).json({ ok: false, error: e.message });
|
|
4669
|
+
}
|
|
4670
|
+
});
|
|
4671
|
+
|
|
4672
|
+
app.get('/api/tensor/csr', (req, res) => {
|
|
4673
|
+
try {
|
|
4674
|
+
const rt = global.ctrlA?.runtime;
|
|
4675
|
+
if (!rt) return res.status(500).json({ ok: false, error: 'runtime missing' });
|
|
4676
|
+
const csr = rt.exportSparseMatrix();
|
|
4677
|
+
if (!csr) return res.status(400).json({ ok: false, error: 'csr not ready' });
|
|
4678
|
+
res.json({ ok: true, csr });
|
|
4679
|
+
} catch (e) {
|
|
4680
|
+
res.status(500).json({ ok: false, error: e.message });
|
|
4681
|
+
}
|
|
4682
|
+
});
|
|
4683
|
+
|
|
4684
|
+
app.get('/api/tensor/project2d', (req, res) => {
|
|
4685
|
+
try {
|
|
4686
|
+
const method = String(req.query?.method || 'auto');
|
|
4687
|
+
const rt = global.ctrlA?.runtime;
|
|
4688
|
+
if (!rt) return res.status(500).json({ ok: false, error: 'runtime missing' });
|
|
4689
|
+
const ret = rt.foldHighDimTo2D(method);
|
|
4690
|
+
res.json(ret);
|
|
4691
|
+
} catch (e) {
|
|
4692
|
+
res.status(500).json({ ok: false, error: e.message });
|
|
4693
|
+
}
|
|
4694
|
+
});
|
|
4695
|
+
|
|
4696
|
+
app.get('/api/tensor/topk', (req, res) => {
|
|
4697
|
+
try {
|
|
4698
|
+
const memeId = String(req.query?.memeId || '');
|
|
4699
|
+
const k = Math.max(1, Math.min(50, Number(req.query?.k || 10)));
|
|
4700
|
+
const rt = global.ctrlA?.runtime;
|
|
4701
|
+
if (!rt?.tensorBridge?.emb) return res.status(400).json({ ok: false, error: 'embedding not ready' });
|
|
4702
|
+
const row = rt.tensorBridge.rowIndex.get(memeId);
|
|
4703
|
+
if (row === undefined) return res.status(404).json({ ok: false, error: 'meme not found' });
|
|
4704
|
+
|
|
4705
|
+
const N = rt.tensorBridge.rows.length, D = rt.tensorBridge.dim;
|
|
4706
|
+
const base = row * D;
|
|
4707
|
+
const q = new Float32Array(D);
|
|
4708
|
+
for (let j = 0; j < D; j++) q[j] = rt.tensorBridge.emb[base + j];
|
|
4709
|
+
|
|
4710
|
+
const scores = new Array(N);
|
|
4711
|
+
for (let i = 0; i < N; i++) {
|
|
4712
|
+
const b = i * D;
|
|
4713
|
+
let s = 0.0; for (let j = 0; j < D; j++) s += q[j] * rt.tensorBridge.emb[b + j];
|
|
4714
|
+
scores[i] = [rt.tensorBridge.rows[i], s];
|
|
4715
|
+
}
|
|
4716
|
+
scores.sort((a, b) => b[1] - a[1]);
|
|
4717
|
+
res.json({ ok: true, memeId, neighbors: scores.slice(0, k) });
|
|
4718
|
+
} catch (e) {
|
|
4719
|
+
res.status(500).json({ ok: false, error: e.message });
|
|
4720
|
+
}
|
|
4721
|
+
});
|
|
4722
|
+
// ...existing code...
|
|
4228
4723
|
// 新增:serve 侧参数调优 API(默认不启用自动调参,仅手动设置)
|
|
4229
4724
|
app.get('/api/tune/get', (req, res) => {
|
|
4230
4725
|
try {
|
|
@@ -4471,7 +4966,11 @@ app.post('/api/adversary/start', (req, res) => {
|
|
|
4471
4966
|
decay: 1, // 新增
|
|
4472
4967
|
decayK: 1, // 新增
|
|
4473
4968
|
maxLen: 16, // 新增
|
|
4474
|
-
edgeWeight: 1
|
|
4969
|
+
edgeWeight: 1, // 新增
|
|
4970
|
+
activationType: 'relu',
|
|
4971
|
+
transferType: 'linear',
|
|
4972
|
+
activationCustom: '',
|
|
4973
|
+
transferCustom: ''
|
|
4475
4974
|
};
|
|
4476
4975
|
const currentModelParams = { ...modelDefaults };
|
|
4477
4976
|
|
|
@@ -4571,10 +5070,10 @@ app.post('/api/adversary/start', (req, res) => {
|
|
|
4571
5070
|
console.log('已设置交错自主学习定时任务,每200s执行一次');
|
|
4572
5071
|
}
|
|
4573
5072
|
|
|
4574
|
-
// 将参数应用到运行时
|
|
4575
|
-
// 扩展 applyModelParams
|
|
4576
5073
|
function applyModelParams(runtime) {
|
|
4577
5074
|
if (!runtime) return;
|
|
5075
|
+
|
|
5076
|
+
// 同步通用参数
|
|
4578
5077
|
runtime.MAX_MEME_WORDS = currentModelParams.maxMemeWords;
|
|
4579
5078
|
runtime.MIN_OVERLAP = currentModelParams.minOverlapThreshold;
|
|
4580
5079
|
runtime.config = runtime.config || {};
|
|
@@ -4585,11 +5084,12 @@ function applyModelParams(runtime) {
|
|
|
4585
5084
|
runtime.config.iteration = currentModelParams.iteration;
|
|
4586
5085
|
runtime.config.threshold = currentModelParams.threshold;
|
|
4587
5086
|
runtime.config.decay = currentModelParams.decay;
|
|
5087
|
+
|
|
4588
5088
|
// memeBarrier
|
|
4589
5089
|
if (runtime.memeBarrier) {
|
|
4590
5090
|
runtime.memeBarrier.maliciousThreshold = currentModelParams.maliciousThreshold;
|
|
4591
5091
|
}
|
|
4592
|
-
//
|
|
5092
|
+
// 全局边权
|
|
4593
5093
|
if (runtime.graph && currentModelParams.edgeWeight !== undefined) {
|
|
4594
5094
|
for (const point of runtime.graph.getAllPoints()) {
|
|
4595
5095
|
for (const conn of point.connect) {
|
|
@@ -4597,7 +5097,20 @@ function applyModelParams(runtime) {
|
|
|
4597
5097
|
}
|
|
4598
5098
|
}
|
|
4599
5099
|
}
|
|
4600
|
-
|
|
5100
|
+
|
|
5101
|
+
// 新增:激活/传递函数配置
|
|
5102
|
+
runtime.setActivationConfig({
|
|
5103
|
+
activationType: currentModelParams.activationType,
|
|
5104
|
+
transferType: currentModelParams.transferType,
|
|
5105
|
+
activationCustom: currentModelParams.activationCustom,
|
|
5106
|
+
transferCustom: currentModelParams.transferCustom
|
|
5107
|
+
});
|
|
5108
|
+
|
|
5109
|
+
console.log('[PARAMS] 已更新运行时参数:', {
|
|
5110
|
+
...currentModelParams,
|
|
5111
|
+
activationType: runtime.getActivationConfig().activationType,
|
|
5112
|
+
transferType: runtime.getActivationConfig().transferType
|
|
5113
|
+
});
|
|
4601
5114
|
}
|
|
4602
5115
|
// 如果直接运行此文件,启动主函数
|
|
4603
5116
|
if (require.main === module) {
|