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
292 lines
12 KiB
JavaScript
292 lines
12 KiB
JavaScript
|
|
// Experimental
|
|
|
|
const sklearn = {};
|
|
|
|
sklearn.ModelFactory = class {
|
|
|
|
async match(context) {
|
|
const obj = await context.peek('pkl');
|
|
const validate = (obj, name) => {
|
|
if (obj && obj.__class__ && obj.__class__.__module__ && obj.__class__.__name__) {
|
|
const key = `${obj.__class__.__module__}.${obj.__class__.__name__}`;
|
|
return key.startsWith(name);
|
|
}
|
|
return false;
|
|
};
|
|
const formats = [
|
|
{ name: 'sklearn.', format: 'sklearn' },
|
|
{ name: 'xgboost.sklearn.', format: 'sklearn' },
|
|
{ name: 'lightgbm.sklearn.', format: 'sklearn' },
|
|
{ name: 'scipy.', format: 'scipy' },
|
|
{ name: 'hmmlearn.', format: 'hmmlearn' }
|
|
];
|
|
for (const format of formats) {
|
|
if (validate(obj, format.name)) {
|
|
return context.set(format.format, obj);
|
|
}
|
|
if (Array.isArray(obj) && obj.length > 0 && obj.every((item) => validate(item, format.name))) {
|
|
return context.set(`${format.format}.list`, obj);
|
|
}
|
|
if (Object(obj) === obj || obj instanceof Map) {
|
|
const entries = obj instanceof Map ? Array.from(obj) : Object.entries(obj);
|
|
if (entries.length > 0 && entries.every(([, value]) => validate(value, format.name))) {
|
|
return context.set(`${format.format}.map`, obj);
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async open(context) {
|
|
const metadata = await context.metadata('sklearn-metadata.json');
|
|
return new sklearn.Model(metadata, context.type, context.value);
|
|
}
|
|
};
|
|
|
|
sklearn.Model = class {
|
|
|
|
constructor(metadata, type, obj) {
|
|
const formats = new Map([
|
|
['sklearn', 'scikit-learn'],
|
|
['scipy', 'SciPy'],
|
|
['hmmlearn', 'hmmlearn']
|
|
]);
|
|
this.format = formats.get(type.split('.').shift());
|
|
this.modules = [];
|
|
const version = [];
|
|
switch (type) {
|
|
case 'sklearn':
|
|
case 'scipy':
|
|
case 'hmmlearn': {
|
|
if (obj._sklearn_version) {
|
|
version.push(` v${obj._sklearn_version}`);
|
|
}
|
|
this.modules.push(new sklearn.Module(metadata, '', obj));
|
|
break;
|
|
}
|
|
case 'sklearn.list':
|
|
case 'scipy.list': {
|
|
const list = obj;
|
|
for (let i = 0; i < list.length; i++) {
|
|
const obj = list[i];
|
|
this.modules.push(new sklearn.Module(metadata, i.toString(), obj));
|
|
if (obj._sklearn_version) {
|
|
version.push(` v${obj._sklearn_version}`);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'sklearn.map':
|
|
case 'scipy.map': {
|
|
const entries = obj instanceof Map ? Array.from(obj) : Object.entries(obj);
|
|
for (const [name, value] of entries) {
|
|
this.modules.push(new sklearn.Module(metadata, name, value));
|
|
if (value._sklearn_version) {
|
|
version.push(` v${value._sklearn_version}`);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
throw new sklearn.Error(`Unsupported scikit-learn format '${type}'.`);
|
|
}
|
|
}
|
|
if (version.length > 0 && version.every((value) => value === version[0])) {
|
|
this.format += version[0];
|
|
}
|
|
}
|
|
};
|
|
|
|
sklearn.Module = class {
|
|
|
|
constructor(metadata, name = '', obj = null) {
|
|
this.name = name;
|
|
this.nodes = [];
|
|
this.inputs = [];
|
|
this.outputs = [];
|
|
const node = new sklearn.Node(metadata, '', obj);
|
|
this.nodes.push(node);
|
|
}
|
|
};
|
|
|
|
sklearn.Argument = class {
|
|
|
|
constructor(name, value, type = null, visible = true) {
|
|
this.name = name;
|
|
this.value = value;
|
|
this.type = type;
|
|
this.visible = visible;
|
|
}
|
|
};
|
|
|
|
sklearn.Value = class {
|
|
|
|
constructor(name, type, initializer = null) {
|
|
if (typeof name !== 'string') {
|
|
throw new sklearn.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
|
|
}
|
|
this.name = name;
|
|
this.type = initializer ? initializer.type : type;
|
|
this.initializer = initializer;
|
|
}
|
|
};
|
|
|
|
sklearn.Node = class {
|
|
|
|
constructor(metadata, name, obj, stack) {
|
|
this.name = name || '';
|
|
const type = obj.__class__ ? `${obj.__class__.__module__}.${obj.__class__.__name__}` : 'builtins.dict';
|
|
this.type = metadata.type(type) || { name: type };
|
|
this.inputs = [];
|
|
this.outputs = [];
|
|
const isObject = (obj) => {
|
|
if (obj && typeof obj === 'object') {
|
|
const proto = Object.getPrototypeOf(obj);
|
|
return proto === Object.prototype || proto === null;
|
|
}
|
|
return false;
|
|
};
|
|
if (type === 'builtins.bytearray') {
|
|
const attribute = new sklearn.Argument('value', Array.from(obj), 'byte[]');
|
|
this.inputs.push(attribute);
|
|
} else {
|
|
const entries = Object.entries(obj);
|
|
for (const [name, value] of entries) {
|
|
if (name === '__class__') {
|
|
continue;
|
|
} else if (value && sklearn.Utility.isTensor(value)) {
|
|
const tensor = new sklearn.Tensor(value);
|
|
const argument = new sklearn.Argument(name, tensor, 'tensor');
|
|
this.inputs.push(argument);
|
|
} else if (Array.isArray(value) && value.length > 0 && value.every((obj) => sklearn.Utility.isTensor(obj))) {
|
|
const tensors = value.map((obj) => new sklearn.Tensor(obj));
|
|
const argument = new sklearn.Argument(name, tensors, 'tensor[]');
|
|
this.inputs.push(argument);
|
|
} else if (sklearn.Utility.isType(value, 'builtins.bytearray')) {
|
|
const argument = new sklearn.Argument(name, Array.from(value), 'byte[]');
|
|
this.inputs.push(argument);
|
|
} else {
|
|
stack = stack || new Set();
|
|
if (value && Array.isArray(value) && value.every((obj) => typeof obj === 'string')) {
|
|
const argument = new sklearn.Argument(name, value, 'string[]');
|
|
this.inputs.push(argument);
|
|
} else if (value && Array.isArray(value) && value.every((obj) => typeof obj === 'number')) {
|
|
const argument = new sklearn.Argument(name, value, 'attribute');
|
|
this.inputs.push(argument);
|
|
} else if (sklearn.Utility.isType(value, 'builtins.function') || sklearn.Utility.isType(value, 'builtins.type')) {
|
|
const node = new sklearn.Node(metadata, '', { __class__: value }, stack);
|
|
const argument = new sklearn.Argument(name, node, 'object');
|
|
this.inputs.push(argument);
|
|
} else if (sklearn.Utility.isType(value, 'builtins.list') && value.every((value) => Array.isArray(value) && value.length === 2 && typeof value[0] === 'string')) {
|
|
const chain = stack;
|
|
const nodes = value.map(([name, value]) => {
|
|
chain.add(value);
|
|
const node = new sklearn.Node(metadata, name, value, chain);
|
|
chain.delete(value);
|
|
return node;
|
|
});
|
|
const argument = new sklearn.Argument(name, nodes, 'object[]');
|
|
this.inputs.push(argument);
|
|
} else if (value && Array.isArray(value) && value.length > 0 && value.every((obj) => obj && (obj.__class__ || obj === Object(obj)))) {
|
|
const chain = stack;
|
|
const values = value.filter((value) => !chain.has(value));
|
|
const nodes = values.map((value) => {
|
|
chain.add(value);
|
|
const node = new sklearn.Node(metadata, '', value, null, chain);
|
|
chain.delete(value);
|
|
return node;
|
|
});
|
|
const argument = new sklearn.Argument(name, nodes, 'object[]');
|
|
this.inputs.push(argument);
|
|
} else if (value && (value.__class__ || isObject(value)) && !stack.has(value)) {
|
|
stack.add(value);
|
|
const node = new sklearn.Node(metadata, '', value, null, stack);
|
|
const argument = new sklearn.Argument(name, node, 'object');
|
|
this.inputs.push(argument);
|
|
stack.delete(value);
|
|
} else {
|
|
let type = 'attribute';
|
|
let visible = true;
|
|
const schema = metadata.attribute(type, name);
|
|
if (schema) {
|
|
if (schema.type) {
|
|
type = schema.type;
|
|
}
|
|
if (schema.visible === false || (schema.optional && value === null)) {
|
|
visible = false;
|
|
} else if (schema.default !== undefined) {
|
|
if (Array.isArray(value)) {
|
|
if (Array.isArray(schema.default)) {
|
|
visible = value.length !== schema.default || !value.every((item, index) => item === metadata.default[index]);
|
|
} else {
|
|
visible = !value.every((item) => item === schema.default);
|
|
}
|
|
} else {
|
|
visible = value !== schema.default;
|
|
}
|
|
}
|
|
}
|
|
const argument = new sklearn.Argument(name, value, type, visible);
|
|
this.inputs.push(argument);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
sklearn.Tensor = class {
|
|
|
|
constructor(array) {
|
|
this.type = new sklearn.TensorType(array.dtype.__name__, new sklearn.TensorShape(array.shape));
|
|
this.stride = Array.isArray(array.strides) ? array.strides.map((stride) => stride / array.itemsize) : null;
|
|
this.encoding = this.type.dataType === 'string' || this.type.dataType === 'object' ? '|' : array.dtype.byteorder;
|
|
this.values = this.type.dataType === 'string' || this.type.dataType === 'object' || this.type.dataType === 'void' ? array.flatten().tolist() : array.tobytes();
|
|
}
|
|
};
|
|
|
|
sklearn.TensorType = class {
|
|
|
|
constructor(dataType, shape) {
|
|
this.dataType = dataType;
|
|
this.shape = shape;
|
|
}
|
|
|
|
toString() {
|
|
return this.dataType + this.shape.toString();
|
|
}
|
|
};
|
|
|
|
sklearn.TensorShape = class {
|
|
|
|
constructor(dimensions) {
|
|
this.dimensions = dimensions;
|
|
}
|
|
|
|
toString() {
|
|
return this.dimensions ? (`[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`) : '';
|
|
}
|
|
};
|
|
|
|
sklearn.Utility = class {
|
|
|
|
static isType(obj, name) {
|
|
return obj && obj.__class__ && obj.__class__.__module__ && obj.__class__.__name__ && `${obj.__class__.__module__}.${obj.__class__.__name__}` === name;
|
|
}
|
|
|
|
static isTensor = (obj) => {
|
|
return sklearn.Utility.isType(obj, 'numpy.ndarray') || sklearn.Utility.isType(obj, 'numpy.matrix');
|
|
};
|
|
};
|
|
|
|
sklearn.Error = class extends Error {
|
|
|
|
constructor(message) {
|
|
super(message);
|
|
this.name = 'Error loading scikit-learn model.';
|
|
}
|
|
};
|
|
|
|
export const ModelFactory = sklearn.ModelFactory;
|