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
300 lines
12 KiB
JavaScript
300 lines
12 KiB
JavaScript
|
|
const nnabla = {};
|
|
|
|
nnabla.ModelFactory = class {
|
|
|
|
async match(context) {
|
|
const identifier = context.identifier;
|
|
if (identifier.endsWith('.nntxt')) {
|
|
const tags = await context.tags('pbtxt');
|
|
if (tags.has('network')) {
|
|
return context.set('nnabla.pbtxt');
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async open(context) {
|
|
nnabla.proto = await context.require('./nnabla-proto');
|
|
nnabla.proto = nnabla.proto.nnabla;
|
|
switch (context.type) {
|
|
case 'nnabla.pbtxt': {
|
|
const reader = await context.read('protobuf.text');
|
|
const model = nnabla.proto.NNablaProtoBuf.decodeText(reader);
|
|
const files = ['nnp_version.txt', 'parameter.protobuf', 'parameter.h5'];
|
|
let contexts = await Promise.all(files.map((file) => context.fetch(file).catch(() => null)));
|
|
contexts = contexts.filter((context) => context !== null);
|
|
contexts = new Map(contexts.map((context) => [context.identifier, context]));
|
|
let version = '';
|
|
if (contexts.has('nnp_version.txt')) {
|
|
const context = contexts.get('nnp_version.txt');
|
|
const reader = await context.read('text');
|
|
const line = reader.read('\n');
|
|
version = line.split('\r').shift();
|
|
}
|
|
if (contexts.has('parameter.protobuf')) {
|
|
const context = contexts.get('parameter.protobuf');
|
|
const reader = await context.read('protobuf.binary');
|
|
const params = nnabla.proto.NNablaProtoBuf.decode(reader);
|
|
model.parameter = params.parameter;
|
|
} else if (contexts.has('parameter.h5')) {
|
|
const context = contexts.get('parameter.h5');
|
|
const file = await context.read('hdf5');
|
|
const queue = [['',file]];
|
|
while (queue.length > 0) {
|
|
const [name, group] = queue.shift();
|
|
if (group.value) {
|
|
const variable = group.value;
|
|
const data = variable.data.peek();
|
|
const buffer = new Uint8Array(data.length);
|
|
buffer.set(data, 0);
|
|
const parameter = new nnabla.proto.Parameter();
|
|
parameter.variable_name = name;
|
|
parameter.shape = new nnabla.proto.Shape();
|
|
parameter.shape.dim = variable.shape.map((dim) => BigInt(dim));
|
|
parameter.data = new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength >> 2);
|
|
model.parameter.push(parameter);
|
|
} else {
|
|
for (const [key, value] of group.groups) {
|
|
queue.push([name ? `${name}/${key}` : key, value]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const metadata = await context.metadata('nnabla-metadata.json');
|
|
return new nnabla.Model(metadata, model, version);
|
|
}
|
|
default: {
|
|
throw new nnabla.Error(`Unsupported nnabla format '${context.type}'.`);
|
|
}
|
|
}
|
|
}
|
|
|
|
filter(context, match) {
|
|
return context.type !== 'nnabla.pbtxt' || (match.type !== 'hdf5.parameter.h5' && match.type !== 'keras.h5');
|
|
}
|
|
};
|
|
|
|
nnabla.Model = class {
|
|
|
|
constructor(metadata, model, version) {
|
|
this.format = `NNabla${version ? ` v${version}` : ''}`;
|
|
this.modules = [];
|
|
const tensors = new Map(model.parameter.map((parameter) => {
|
|
const name = parameter.variable_name;
|
|
const shape = new nnabla.TensorShape(parameter.shape.dim);
|
|
const type = new nnabla.TensorType(shape);
|
|
return [name, new nnabla.Tensor(name, type, parameter.data)];
|
|
}));
|
|
const networks = new Map(model.network.map((network) => [network.name, network]));
|
|
for (const executor of model.executor) {
|
|
const network = networks.get(executor.network_name);
|
|
const graph = new nnabla.Graph(metadata, network, executor.data_variable, executor.output_variable, tensors);
|
|
this.modules.push(graph);
|
|
}
|
|
for (const optimizer of model.optimizer) {
|
|
const network = networks.get(optimizer.network_name);
|
|
const graph = new nnabla.Graph(metadata, network, optimizer.data_variable, optimizer.loss_variable, tensors);
|
|
this.modules.push(graph);
|
|
}
|
|
for (const monitor of model.monitor) {
|
|
const network = networks.get(monitor.network_name);
|
|
const graph = new nnabla.Graph(metadata, network, monitor.data_variable, monitor.monitor_variable, tensors);
|
|
this.modules.push(graph);
|
|
}
|
|
}
|
|
};
|
|
|
|
nnabla.Graph = class {
|
|
|
|
constructor (metadata, network, inputs, outputs, tensors) {
|
|
this.name = network.name;
|
|
const values = new Map(network.variable.map((variable) => {
|
|
const name = variable.name;
|
|
const shape = new nnabla.TensorShape(variable.shape.dim);
|
|
const type = new nnabla.TensorType(shape);
|
|
return [name, new nnabla.Value(name, type, tensors.get(name))];
|
|
}));
|
|
values.map = (name) => {
|
|
if (!values.has(name)) {
|
|
values.set(name, new nnabla.Value(name, null, tensors.get(name)));
|
|
}
|
|
return values.get(name);
|
|
};
|
|
this.inputs = inputs.map((item) => {
|
|
const name = item.variable_name;
|
|
return new nnabla.Argument(name, [values.map(name)]);
|
|
});
|
|
this.outputs = outputs.map((output) => {
|
|
const name = output.variable_name;
|
|
return new nnabla.Argument(name, [values.map(name)]);
|
|
});
|
|
const get_parameters = (func) => {
|
|
for (const [key, value] of Object.entries(func)) {
|
|
if (key.endsWith("_param")) {
|
|
return value;
|
|
}
|
|
}
|
|
return undefined;
|
|
};
|
|
this.nodes = network.function.map((func) => {
|
|
const parameters = get_parameters(func) || [];
|
|
const attributes = Object.entries(parameters).map(([name, value]) => {
|
|
const attribute = metadata.attribute(func.type, name);
|
|
let type = attribute.type;
|
|
switch (type) {
|
|
case 'shape':
|
|
type = "int64[]";
|
|
value = value.dim;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
const visible = attribute.default !== undefined && value === attribute.default ? false : true;
|
|
return new nnabla.Argument(name, value, type, visible);
|
|
});
|
|
const func_type = metadata.type(func.type);
|
|
const inputs = [];
|
|
for (let index = 0; index < func.input.length;) {
|
|
const input = func_type.inputs && index < func_type.inputs.length ? func_type.inputs[index] : { name: index.toString() };
|
|
const count = input.list ? func.input.length - index : 1;
|
|
const args = func.input.slice(index, index + count).map((input) => values.map(input));
|
|
const argument = new nnabla.Argument(input.name, args);
|
|
inputs.push(argument);
|
|
index += count;
|
|
}
|
|
const outputs = [];
|
|
for (let index = 0; index < func.output.length;) {
|
|
const output = func_type.outputs && index < func_type.outputs.length ? func_type.outputs[index] : { name: index.toString() };
|
|
const count = output.list ? func.output.length - index : 1;
|
|
const args = func.output.slice(index, index + count).map((output) => values.map(output));
|
|
const argument = new nnabla.Argument(output.name, args);
|
|
outputs.push(argument);
|
|
index += count;
|
|
}
|
|
return new nnabla.Node(metadata, func, attributes, inputs, outputs);
|
|
});
|
|
}
|
|
};
|
|
|
|
nnabla.Argument = class {
|
|
|
|
constructor(name, value, type = null, visible = true) {
|
|
this.name = name;
|
|
this.value = value;
|
|
this.type = type;
|
|
this.visible = visible;
|
|
}
|
|
};
|
|
|
|
nnabla.Value = class {
|
|
|
|
constructor(name, type, initializer = null) {
|
|
this.name = name;
|
|
this.type = !type && initializer && initializer.type ? initializer.type : type;
|
|
this.initializer = initializer;
|
|
}
|
|
};
|
|
|
|
nnabla.Node = class {
|
|
|
|
constructor(metadata, func, attributes = [], inputs = [], outputs = []) {
|
|
this.name = func.name;
|
|
this.type = metadata.type(func.type) || { name: func.type, type: func.type };
|
|
this.attributes = attributes;
|
|
this.outputs = outputs;
|
|
this.chain = [];
|
|
// "nonlinearity" does not match metadata type
|
|
const get_nonlinearity = (name) => {
|
|
switch (name) {
|
|
case "identity": return "Identity";
|
|
case "relu": return "ReLU";
|
|
case "sigmoid": return "Sigmoid";
|
|
case "tanh": return "Tanh";
|
|
case "leaky_relu": return "LeakyReLU";
|
|
case "elu": return "ELU";
|
|
case "relu6": return "ReLU6";
|
|
default: return name;
|
|
}
|
|
};
|
|
switch (func.type) {
|
|
case "FusedConvolution": {
|
|
this.inputs = inputs.slice(0, 3) || [];
|
|
if (inputs.length > 3) {
|
|
this.chain.push(new nnabla.Node(metadata, { name: `${func.name}/bn`, type: "BatchNormalization" }, [], inputs.slice(3, 7)));
|
|
}
|
|
if (inputs.length > 7) {
|
|
this.chain.push(new nnabla.Node(metadata, { name: `${func.name}/add`, type: "Add2" }, [], inputs.slice(7)));
|
|
}
|
|
const type_a = attributes.find((item) => item.name === "nonlinearity").value;
|
|
this.chain.push(new nnabla.Node(metadata, { name: `${func.name}/act`, type: get_nonlinearity(type_a) }));
|
|
break;
|
|
}
|
|
case "FusedBatchNormalization": {
|
|
this.inputs = inputs.slice(0, 5) || [];
|
|
if (inputs.length > 4) {
|
|
this.chain.push(new nnabla.Node(metadata, { name: `${func.name}/add`, type: "Add2" }, [], inputs.slice(5)));
|
|
}
|
|
const type_b = attributes.find((item) => item.name === "nonlinearity").value;
|
|
this.chain.push(new nnabla.Node(metadata, { name: `${func.name}/act`, type: get_nonlinearity(type_b) }));
|
|
break;
|
|
}
|
|
default: {
|
|
this.inputs = inputs || [];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
nnabla.Tensor = class {
|
|
|
|
constructor(name, type, values) {
|
|
this.name = name;
|
|
this.type = type;
|
|
this.encoding = '|';
|
|
this.values = values;
|
|
const dataType = this.type.dataType;
|
|
switch (dataType) {
|
|
case 'float32': this.values = new Float32Array(this.values); break;
|
|
default: throw new nnabla.Error(`Unsupported data type '${dataType}'.`);
|
|
}
|
|
}
|
|
};
|
|
|
|
nnabla.TensorType = class {
|
|
|
|
constructor(shape) {
|
|
this.dataType = "float32";
|
|
this.shape = shape;
|
|
}
|
|
|
|
toString() {
|
|
return this.dataType + this.shape.toString();
|
|
}
|
|
};
|
|
|
|
nnabla.TensorShape = class {
|
|
|
|
constructor(dimensions) {
|
|
this.dimensions = dimensions.map((dim) => typeof dim === 'bigint' ? dim.toNumber() : dim);
|
|
}
|
|
|
|
toString() {
|
|
if (Array.isArray(this.dimensions) && this.dimensions.length > 0) {
|
|
return `[${this.dimensions.join(',')}]`;
|
|
}
|
|
return '';
|
|
}
|
|
};
|
|
|
|
nnabla.Error = class extends Error {
|
|
|
|
constructor(message) {
|
|
super(message);
|
|
this.name = 'Error loading Neural Network Library model.';
|
|
}
|
|
};
|
|
|
|
export const ModelFactory = nnabla.ModelFactory;
|