I'm porting to TF.js a model converted from Keras with its pretrained weights. I followed the instructions on the documentation and saved the weights from a local path.
When I'm initializing my model by loading the .json file, with tfjs-node 1.7.1, I get the following error:
(node:39703) UnhandledPromiseRejectionWarning: Error: 152 of 197 weights are not set: conv1_1/3x3_s1/bn/gamma,conv1_1/3x3_s1/bn/beta,conv1_1/3x3_s1/bn/moving_mean,conv1_1/3x3_s1/bn/moving_variance,conv2_a_1x1_reduce/bn/gamma,conv2_a_1x1_reduce/bn/beta,conv2_a_1x1_reduce/bn/moving_mean,conv2_a_1x1_reduce/bn/moving_variance,conv2_a_3x3/bn/gamma,conv2_a_3x3/bn/beta,conv2_a_3x3/bn/moving_mean,conv2_a_3x3/bn/moving_variance,conv2_a_1x1_increase/bn/gamma,conv2_a_1x1_increase/bn/beta,conv2_a_1x1_increase/bn/moving_mean,conv2_a_1x1_increase/bn/moving_variance,conv2_a_1x1_proj/bn/gamma,conv2_a_1x1_proj/bn/beta,conv2_a_1x1_proj/bn/moving_mean,conv2_a_1x1_proj/bn/moving_variance,conv2_b_1x1_reduce/bn/gamma,conv2_b_1x1_reduce/bn/beta,conv2_b_1x1_reduce/bn/moving_mean,conv2_b_1x1_reduce/bn/moving_variance,conv2_b_3x3/bn/gamma,conv2_b_3x3/bn/beta,conv2_b_3x3/bn/moving_mean,conv2_b_3x3/bn/moving_variance,conv2_b_1x1_increase/bn/gamma,conv2_b_1x1_increase/bn/beta,conv2_b_1x1_increase/bn/moving_mean,conv2_b_1x1_increase/bn/moving_variance,conv3_a_1x1_reduce/bn/gamma,conv3_a_1x1_reduce/bn/beta,conv3_a_1x1_reduce/bn/moving_mean,conv3_a_1x1_reduce/bn/moving_variance,conv3_a_3x3/bn/gamma,conv3_a_3x3/bn/beta,conv3_a_3x3/bn/moving_mean,conv3_a_3x3/bn/moving_variance,conv3_a_1x1_increase/bn/gamma,conv3_a_1x1_increase/bn/beta,conv3_a_1x1_increase/bn/moving_mean,conv3_a_1x1_increase/bn/moving_variance,conv3_a_1x1_proj/bn/gamma,conv3_a_1x1_proj/bn/beta,conv3_a_1x1_proj/bn/moving_mean,conv3_a_1x1_proj/bn/moving_variance,conv3_b_1x1_reduce/bn/gamma,conv3_b_1x1_reduce/bn/beta,conv3_b_1x1_reduce/bn/moving_mean,conv3_b_1x1_reduce/bn/moving_variance,conv3_b_3x3/bn/gamma,conv3_b_3x3/bn/beta,conv3_b_3x3/bn/moving_mean,conv3_b_3x3/bn/moving_variance,conv3_b_1x1_increase/bn/gamma,conv3_b_1x1_increase/bn/beta,conv3_b_1x1_increase/bn/moving_mean,conv3_b_1x1_increase/bn/moving_variance,conv3_c_1x1_reduce/bn/gamma,conv3_c_1x1_reduce/bn/beta,conv3_c_1x1_reduce/bn/moving_mean,conv3_c_1x1_reduce/bn/moving_variance,conv3_c_3x3/bn/gamma,conv3_c_3x3/bn/beta,conv3_c_3x3/bn/moving_mean,conv3_c_3x3/bn/moving_variance,conv3_c_1x1_increase/bn/gamma,conv3_c_1x1_increase/bn/beta,conv3_c_1x1_increase/bn/moving_mean,conv3_c_1x1_increase/bn/moving_variance,conv4_a_1x1_reduce/bn/gamma,conv4_a_1x1_reduce/bn/beta,conv4_a_1x1_reduce/bn/moving_mean,conv4_a_1x1_reduce/bn/moving_variance,conv4_a_3x3/bn/gamma,conv4_a_3x3/bn/beta,conv4_a_3x3/bn/moving_mean,conv4_a_3x3/bn/moving_variance,conv4_a_1x1_increase/bn/gamma,conv4_a_1x1_increase/bn/beta,conv4_a_1x1_increase/bn/moving_mean,conv4_a_1x1_increase/bn/moving_variance,conv4_a_1x1_proj/bn/gamma,conv4_a_1x1_proj/bn/beta,conv4_a_1x1_proj/bn/moving_mean,conv4_a_1x1_proj/bn/moving_variance,conv4_b_1x1_reduce/bn/gamma,conv4_b_1x1_reduce/bn/beta,conv4_b_1x1_reduce/bn/moving_mean,conv4_b_1x1_reduce/bn/moving_variance,conv4_b_3x3/bn/gamma,conv4_b_3x3/bn/beta,conv4_b_3x3/bn/moving_mean,conv4_b_3x3/bn/moving_variance,conv4_b_1x1_increase/bn/gamma,conv4_b_1x1_increase/bn/beta,conv4_b_1x1_increase/bn/moving_mean,conv4_b_1x1_increase/bn/moving_variance,conv4_c_1x1_reduce/bn/gamma,conv4_c_1x1_reduce/bn/beta,conv4_c_1x1_reduce/bn/moving_mean,conv4_c_1x1_reduce/bn/moving_variance,conv4_c_3x3/bn/gamma,conv4_c_3x3/bn/beta,conv4_c_3x3/bn/moving_mean,conv4_c_3x3/bn/moving_variance,conv4_c_1x1_increase/bn/gamma,conv4_c_1x1_increase/bn/beta,conv4_c_1x1_increase/bn/moving_mean,conv4_c_1x1_increase/bn/moving_variance,conv5_a_1x1_reduce/bn/gamma,conv5_a_1x1_reduce/bn/beta,conv5_a_1x1_reduce/bn/moving_mean,conv5_a_1x1_reduce/bn/moving_variance,conv5_a_3x3/bn/gamma,conv5_a_3x3/bn/beta,conv5_a_3x3/bn/moving_mean,conv5_a_3x3/bn/moving_variance,conv5_a_1x1_increase/bn/gamma,conv5_a_1x1_increase/bn/beta,conv5_a_1x1_increase/bn/moving_mean,conv5_a_1x1_increase/bn/moving_variance,conv5_a_1x1_proj/bn/gamma,conv5_a_1x1_proj/bn/beta,conv5_a_1x1_proj/bn/moving_mean,conv5_a_1x1_proj/bn/moving_variance,conv5_b_1x1_reduce/bn/gamma,conv5_b_1x1_reduce/bn/beta,conv5_b_1x1_reduce/bn/moving_mean,conv5_b_1x1_reduce/bn/moving_variance,conv5_b_3x3/bn/gamma,conv5_b_3x3/bn/beta,conv5_b_3x3/bn/moving_mean,conv5_b_3x3/bn/moving_variance,conv5_b_1x1_increase/bn/gamma,conv5_b_1x1_increase/bn/beta,conv5_b_1x1_increase/bn/moving_mean,conv5_b_1x1_increase/bn/moving_variance,conv5_c_1x1_reduce/bn/gamma,conv5_c_1x1_reduce/bn/beta,conv5_c_1x1_reduce/bn/moving_mean,conv5_c_1x1_reduce/bn/moving_variance,conv5_c_3x3/bn/gamma,conv5_c_3x3/bn/beta,conv5_c_3x3/bn/moving_mean,conv5_c_3x3/bn/moving_variance,conv5_c_1x1_increase/bn/gamma,conv5_c_1x1_increase/bn/beta,conv5_c_1x1_increase/bn/moving_mean,conv5_c_1x1_increase/bn/moving_variance
at new ValueError (./AudioReco/node_modules/@tensorflow/tfjs-layers/dist/errors.js:68:28)
at LayersModel.Container.loadWeights (./AudioReco/node_modules/@tensorflow/tfjs-layers/dist/engine/container.js:569:23)
at ./AudioReco/node_modules/@tensorflow/tfjs-layers/dist/models.js:303:27
at step (./AudioReco/node_modules/@tensorflow/tfjs-layers/dist/models.js:54:23)
at Object.next (./AudioReco/node_modules/@tensorflow/tfjs-layers/dist/models.js:35:53)
at fulfilled (./AudioReco/node_modules/@tensorflow/tfjs-layers/dist/models.js:26:58)
I understand this has something to do with all the Batch Normalization layers the model uses but how do I properly restore the Gammas, Betas, Moving Mean & Moving Average to each of them?
Model taken from the work of Wei Xie from VoxCeleb. The original weights of the model can be found on his Google Drive and the converted ones in the .json format can be found on this link.
Code ran:
model.js
const tf = require('@tensorflow/tfjs-node')
class VGGVox_Model {
constructor() {
this.model;
}
async init() {
// Loading the custom layers
require('./layers/VladPooling');
require('./layers/Lambda');
this.model = await tf.loadLayersModel('file://resources/model/model.json', false);
this.model.summary();
}
}
(async function main() {
const myModel = new VGGVox_Model();
myModel.init();
})();
VladPooling.js
const tf = require('../node_modules/@tensorflow/tfjs-node')
class VladPooling extends tf.layers.Layer {
constructor(config) {
super(config);
this.kCenters = config.kCenters;
this.gCenters = config.gCenters;
this.mode = config.mode;
}
compute_output_shape(input_shape) {
return (input_shape[0][0], this.kCenters * input_shape[0][input_shape[0].length - 1])
}
build(input_shape) {
this.cluster = this.addWeight(
'centers',
[ this.kCenters + this.gCenters, input_shape[0][input_shape[0].length - 1] ],
'float32',
'orthogonal');
this.built = true;
}
call(inputs, kwargs) {
return tf.tidy(() => {
console.log('call')
});
}
getConfig() {
const baseConfig = super.getConfig();
const config = {
kCenters: this.kCenters,
gCenters: this.gCenters,
mode: this.mode
};
Object.assign(config, baseConfig);
return config;
}
static get className() {
return 'VladPooling';
}
}
tf.serialization.registerClass(VladPooling);
exports.vlad_pooling = VladPooling;
Lambda.js
const tf = require('../node_modules/@tensorflow/tfjs-node')
class Lambda extends tf.layers.Layer {
constructor(config) {
super(config);
if (config.name === undefined) {
config.name = ((+new Date) * Math.random()).toString(36); //random name from timestamp in case name hasn't been set
}
this.name = config.name;
this.lambdaFunction = config.function;
}
call(input) {
return tf.tidy(() => {
let result = null;
eval(this.lambdaFunction);
return result;
});
}
computeOutputShape(inputShape) {
return inputShape;
}
getConfig() {
const config = super.getConfig();
Object.assign(config, {
lambdaFunction: this.lambdaFunction
});
return config;
}
static get className() {
return 'Lambda';
}
}
tf.serialization.registerClass(Lambda);
As for the two custom layers (Vlad Pooling and Lambda), I added them as well in the Google Drive folder. Lambda layer's code was taken here. I didn't include the call() of the Vlad Pooling yet since I couldn't check the outputs (model doesn't load). Anyways, they are required because the model loading won't work without them.