7254f7b4d1
Build / Build (macos-latest) (push) Has been cancelled
Build / Build (ubuntu-latest) (push) Has been cancelled
Build / Build (windows-latest) (push) Has been cancelled
Build / Analyze (javascript) (push) Has been cancelled
Build / Analyze (python) (push) Has been cancelled
269 lines
9.4 KiB
JavaScript
269 lines
9.4 KiB
JavaScript
|
|
// Experimental
|
|
|
|
import * as python from './python.js';
|
|
|
|
const numpy = {};
|
|
|
|
numpy.ModelFactory = class {
|
|
|
|
async match(context) {
|
|
const stream = context.stream;
|
|
const signature = [0x93, 0x4E, 0x55, 0x4D, 0x50, 0x59];
|
|
if (stream && signature.length <= stream.length && stream.peek(signature.length).every((value, index) => value === signature[index])) {
|
|
return context.set('npy');
|
|
}
|
|
const entries = await context.peek('npz');
|
|
if (entries instanceof Map && entries.size > 0) {
|
|
return context.set('npz', entries);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async open(context) {
|
|
let format = '';
|
|
const modules = [];
|
|
switch (context.type) {
|
|
case 'npy': {
|
|
format = 'NumPy Array';
|
|
const unresolved = new Set();
|
|
const execution = new python.Execution();
|
|
execution.on('resolve', (sender, name) => unresolved.add(name));
|
|
const stream = context.stream;
|
|
const io = execution.__import__('io');
|
|
const np = execution.__import__('numpy');
|
|
const bytes = new io.BytesIO(stream);
|
|
const array = np.load(bytes);
|
|
if (unresolved.size > 0) {
|
|
const name = unresolved.values().next().value;
|
|
throw new numpy.Error(`Unknown type name '${name}'.`);
|
|
}
|
|
const layer = { type: 'numpy.ndarray', parameters: [{ name: 'value', tensor: { name: '', array } }] };
|
|
modules.push({ layers: [layer] });
|
|
break;
|
|
}
|
|
case 'npz': {
|
|
format = 'NumPy Archive';
|
|
const layers = new Map();
|
|
const entries = Array.from(context.value);
|
|
const separator = entries.every(([name]) => name.endsWith('.weight.npy')) ? '.' : '/';
|
|
for (const [key, array] of entries) {
|
|
const name = key.replace(/\.npy$/, '');
|
|
const path = name.split(separator);
|
|
const parameterName = path.pop();
|
|
const groupName = path.join(separator);
|
|
if (!layers.has(groupName)) {
|
|
layers.set(groupName, { name: groupName, parameters: [] });
|
|
}
|
|
const layer = layers.get(groupName);
|
|
layer.parameters.push({
|
|
name: parameterName,
|
|
tensor: { name, array }
|
|
});
|
|
}
|
|
modules.push({ layers: Array.from(layers.values()) });
|
|
break;
|
|
}
|
|
default: {
|
|
throw new numpy.Error(`Unsupported NumPy format '${context.type}'.`);
|
|
}
|
|
}
|
|
return new numpy.Model(format, modules);
|
|
}
|
|
};
|
|
|
|
numpy.Model = class {
|
|
|
|
constructor(format, modules) {
|
|
this.format = format;
|
|
this.modules = modules.map((module) => new numpy.Module(module));
|
|
}
|
|
};
|
|
|
|
numpy.Module = class {
|
|
|
|
constructor(graph) {
|
|
this.name = graph.name || '';
|
|
this.nodes = graph.layers.map((layer) => new numpy.Node(layer));
|
|
this.inputs = [];
|
|
this.outputs = [];
|
|
}
|
|
};
|
|
|
|
numpy.Argument = class {
|
|
|
|
constructor(name, value) {
|
|
this.name = name;
|
|
this.value = value;
|
|
}
|
|
};
|
|
|
|
numpy.Value = class {
|
|
|
|
constructor(name, initializer = null) {
|
|
if (typeof name !== 'string') {
|
|
throw new numpy.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
|
|
}
|
|
this.name = name;
|
|
this.type = initializer.type;
|
|
this.initializer = initializer;
|
|
}
|
|
};
|
|
|
|
numpy.Node = class {
|
|
|
|
constructor(layer) {
|
|
this.name = layer.name || '';
|
|
this.type = { name: layer.type || 'Object' };
|
|
this.inputs = [];
|
|
this.outputs = [];
|
|
this.attributes = [];
|
|
for (const parameter of layer.parameters) {
|
|
const initializer = new numpy.Tensor(parameter.tensor.array);
|
|
const value = new numpy.Value(parameter.tensor.name || '', initializer);
|
|
const argument = new numpy.Argument(parameter.name, [value]);
|
|
this.inputs.push(argument);
|
|
}
|
|
}
|
|
};
|
|
|
|
numpy.Tensor = class {
|
|
|
|
constructor(array) {
|
|
this.type = new numpy.TensorType(array.dtype.__name__, new numpy.TensorShape(array.shape));
|
|
this.stride = array.strides.map((stride) => stride / array.itemsize);
|
|
const list = this.type.dataType === 'string' || this.type.dataType === 'object' || this.type.dataType === 'void';
|
|
this.values = list ? array.flatten().tolist() : array.tobytes();
|
|
this.encoding = list ? '|' : array.dtype.byteorder;
|
|
}
|
|
};
|
|
|
|
numpy.TensorType = class {
|
|
|
|
constructor(dataType, shape) {
|
|
this.dataType = dataType || '?';
|
|
this.shape = shape;
|
|
}
|
|
|
|
toString() {
|
|
return this.dataType + this.shape.toString();
|
|
}
|
|
};
|
|
|
|
numpy.TensorShape = class {
|
|
|
|
constructor(dimensions) {
|
|
this.dimensions = dimensions;
|
|
}
|
|
|
|
toString() {
|
|
return this.dimensions && this.dimensions.length > 0 ? `[${this.dimensions.join(',')}]` : '';
|
|
}
|
|
};
|
|
|
|
numpy.Utility = class {
|
|
|
|
static isTensor(obj) {
|
|
return obj && obj.__class__ &&
|
|
((obj.__class__.__module__ === 'numpy' && obj.__class__.__name__ === 'ndarray') ||
|
|
(obj.__class__.__module__ === 'numpy.core.memmap' && obj.__class__.__name__ === 'memmap'));
|
|
}
|
|
|
|
static weights(obj) {
|
|
const dict = (obj, key) => {
|
|
const dict = key === '' ? obj : obj[key];
|
|
if (dict) {
|
|
const weights = new Map();
|
|
if (dict instanceof Map) {
|
|
for (const [key, obj] of dict) {
|
|
if (numpy.Utility.isTensor(obj)) {
|
|
weights.set(key, obj);
|
|
continue;
|
|
} else if (obj instanceof Map && Array.from(obj).every(([, value]) => numpy.Utility.isTensor(value))) {
|
|
for (const [name, value] of obj) {
|
|
weights.set(`${key}.${name}`, value);
|
|
}
|
|
continue;
|
|
} else if (key === '_metadata') {
|
|
continue;
|
|
}
|
|
return null;
|
|
}
|
|
return weights;
|
|
} else if (!Array.isArray(dict)) {
|
|
const set = new Set(['weight_order', 'lr', 'model_iter', '__class__']);
|
|
for (const [name, value] of Object.entries(dict)) {
|
|
if (numpy.Utility.isTensor(value)) {
|
|
weights.set(name, value);
|
|
continue;
|
|
}
|
|
if (set.has(name)) {
|
|
continue;
|
|
}
|
|
if (value && !Array.isArray(value) && Object.entries(value).every(([, value]) => numpy.Utility.isTensor(value))) {
|
|
if (value && value.__class__ && value.__class__.__module__ && value.__class__.__name__) {
|
|
weights.set(`${name}.__class__`, `${value.__class__.__module__}.${value.__class__.__name__}`);
|
|
}
|
|
for (const [key, obj] of Object.entries(value)) {
|
|
weights.set(`${name}.${key}`, obj);
|
|
}
|
|
continue;
|
|
}
|
|
return null;
|
|
}
|
|
return weights;
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
const list = (obj, key) => {
|
|
let list = key === '' ? obj : obj[key];
|
|
if (list && Array.isArray(list) && list.every((obj) => Object.values(obj).every((value) => numpy.Utility.isTensor(value)))) {
|
|
list = list.map((obj) => obj instanceof Map ? obj : new Map(Object.entries(obj)));
|
|
}
|
|
if (list && Array.isArray(list)) {
|
|
const weights = new Map();
|
|
for (let i = 0; i < list.length; i++) {
|
|
const obj = list[i];
|
|
if (numpy.Utility.isTensor(obj)) {
|
|
weights.set(i.toString(), obj);
|
|
continue;
|
|
} else if (obj instanceof Map && Array.from(obj).every(([, value]) => numpy.Utility.isTensor(value))) {
|
|
for (const [name, value] of obj) {
|
|
weights.set(`${i}.${name}`, value);
|
|
}
|
|
continue;
|
|
}
|
|
return null;
|
|
}
|
|
return weights;
|
|
}
|
|
return null;
|
|
};
|
|
const keys = ['', 'blobs', 'model', 'experiment_state'];
|
|
for (const key of keys) {
|
|
const weights = dict(obj, key);
|
|
if (weights && weights.size > 0) {
|
|
return weights;
|
|
}
|
|
}
|
|
for (const key of keys) {
|
|
const weights = list(obj, key);
|
|
if (weights) {
|
|
return weights;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
};
|
|
|
|
numpy.Error = class extends Error {
|
|
|
|
constructor(message) {
|
|
super(message);
|
|
this.name = 'Error loading NumPy model.';
|
|
}
|
|
};
|
|
|
|
export const ModelFactory = numpy.ModelFactory;
|