expected first layer to have x dimensions but got

2020-04-19 06:36发布

(I am just starting tensorflow.js on node)
I have been searching the web up and down for an answer.
The confusion
I have image data from image1 = tf.fromPixels(img) and I tried inputting it along with other image data to xs = tf.tensor([image1, image2]). The confusion is no matter how I input a bunch of images into xs for model.fit, the program outputs errors described below.

What I already tried
When I run the program I get the error Error: Error when checking input: expected conv2d_Conv2D1_input to have 4 dimension(s). but got array with shape 4,1
I know for a fact that I am not inputting the xs correctly. I read some articles online relating to how you need to input the array in a fashion like tf.tensor([[0.2, 0.1], [0.2, 0.4]]); and some batching of images of some sort. I looked at articles showing that for images, you need another set of layers:

model.add(tf.layers.conv2d({
    inputShape: [scaleHeight, scaleWidth, 3],
    kernelSize: 5,
    filters: 8,
    strides: 1,
    activation: 'relu',
    kernelInitializer: 'VarianceScaling'
}));
model.add(tf.layers.maxPooling2d({
    poolSize: [2, 2],
    strides: [2, 2]
}));

model.add(tf.layers.conv2d({
    kernelSize: 5,
    filters: 16,
    strides: 1,
    activation: 'relu',
    kernelInitializer: 'VarianceScaling'
}));

model.add(tf.layers.maxPooling2d({
    poolSize: [2, 2],
    strides: [2, 2]
}));

model.add(tf.layers.dense({ // Output
    units: 2,
    kernelInitializer: 'VarianceScaling',
    activation: 'softmax'
}));
model.compile({loss: 'categoricalCrossentropy', optimizer: tf.train.sgd(0.1), metrics: ['accuracy']});

Well I tried inputting that in, tried converting them into typedarray format, tried a lot of things. I am pretty lost on coming up with a proper xs variable for multiple images turned to tensors by tf.fromPixels(canvas) for model.fit(xs, ys, {epochs: 100, options....});

Code:

var tf = require('@tensorflow/tfjs');
var cv = require('canvas');
var {Image, createCanvas, ImageData} = cv;
tf.disableDeprecationWarnings();

var scaleWidth = 16;
var scaleHeight = 16;

function getImage(path){
    var img = new Image();
    return new Promise(function(resolve, reject){
        img.onload = function(){
            var element = createCanvas(scaleWidth, scaleHeight);
            var ctx = element.getContext('2d');
            ctx.drawImage(img, 0, 0);
            ctx.scale(scaleWidth/img.width, scaleHeight/img.height);
            //resolve(Array.from(tf.fromPixels(element).flatten().dataSync()));

            resolve(tf.fromPixels(element));
        };
        img.src = path;
    });
}

var log = function(input){console.log(input)};

const model = tf.sequential();
model.add(tf.layers.conv2d({
    inputShape: [scaleHeight, scaleWidth, 3],
    kernelSize: 5,
    filters: 8,
    strides: 1,
    activation: 'relu',
    kernelInitializer: 'VarianceScaling'
}));
model.add(tf.layers.maxPooling2d({
    poolSize: [2, 2],
    strides: [2, 2]
}));

model.add(tf.layers.conv2d({
    kernelSize: 5,
    filters: 16,
    strides: 1,
    activation: 'relu',
    kernelInitializer: 'VarianceScaling'
}));

model.add(tf.layers.maxPooling2d({
    poolSize: [2, 2],
    strides: [2, 2]
}));

model.add(tf.layers.dense({ // Output
    units: 2,
    kernelInitializer: 'VarianceScaling',
    activation: 'softmax'
}));
model.compile({loss: 'categoricalCrossentropy', optimizer: tf.train.sgd(0.1), metrics: ['accuracy']});

(async function(){
    var cats = [], bland = [];

    cats[0] = await getImage('cats/0.jpeg');
    cats[1] = await getImage('cats/1.jpeg');
    bland[0] = await getImage('bland/0.png');
    bland[1] = await getImage('bland/1.png');

    var testCats = await getImage('c.jpeg');
    var testBland = await getImage('b.jpeg');

    var xs = tf.tensor([cats[0], cats[1], bland[0], bland[1]]); // confusion occurs here

    for(var c = 0; c < 10; c++){
        var result = await model.fit(xs, tf.tensor([[0, 1], [0, 1], [1, 0], [1, 0]]), {epochs: 100});
        console.log(result.history.loss[0]);
    }
})();

And after I ran it, I expected to at least log the loss of the model but it thrown this error:
Error: Error when checking input: expected conv2d_Conv2D1_input to have 4 dimension(s). but got array with shape 4,1

1条回答
做个烂人
2楼-- · 2020-04-19 06:48

Looking at your code the data passed in to your model doesn't have the same shape as the model first layer inputShape.

How to go about solving the issue ?

  • check the data.shape.
console.log(xs.shape) // it will return (4,1)
  • compare with the inputShape

    The data shape should one dimension higher than the inputShape (one more dimension for batchsize)

    // Does `xs.inputShape.slice(1) ===[Scaleheight, scaleWidth,3]` ? 

    shape1 = xs.inputShape.slice(1)
    shape2 = [Scaleheight, scaleWidth,3]
    const same = (shape1.length == shape2.length) && shape1.every(function(e, i) {
    return e === shape2[i]; 
});

If they are not equal, there are two ways to get the problem resolved

  • Reshaping the data if possible,using tf.reshape, tf.slice, tf.expandDims(), ...

  • Or simply changing the inputShape to be equal to our data shape


In your case here there is a clear mismatch between the inputShape and the data shape.

First thing first, the way you create your xs is wrong. Actually, xs has the shape (4, 1) with NaN values. It is as if you created a tf.tensor with an array of tensors. You can create the xs this way:

xs = tf.concat([...cats, ...blands], 0)

However it is not sure if this will solve completely the issue. You need to iterate over the step outlined above,ie, check the shape of xs, compare with the inputShape and so on ...

查看更多
登录 后发表回答