typescript - Varför returnerar model.predict() från tensorflowjs alltid samma prediktion?


Jag skapar en app i Ionic 3-ramverket för att känna igen handskrivna tecken och för att göra detta använder jag tensorflowjs. Problemet är att när allt är klart returnerar förutsägelsen alltid samma värde.

Modellen (konverterad från keras med tensorflowjs-converter) lagras lokalt i assets/models/model.json.

Bilderna som jag försöker förutsäga är hämtade från ett HTMLCanvasElement där du kan måla karaktären och sedan hämtar funktionen getCanvas() ImageData från den:

getCanvas() {
    let photo = document.getElementById('MyCanvas') as HTMLCanvasElement;
    let ctx = photo.getContext('2d');
    const data: ImageData = ctx.createImageData(300,300);
    return data;
}

Sedan beräknas modellens inmatningstensor [1, 32, 32, 3] från data och därefter matar jag den till modellen med evaluateModel(). I den funktionen laddar jag model.json med tf.loadModel() och jag försöker förutsäga bilden klass:

import * as tf from '@tensorflow/tfjs';

async evaluateModel() {
    imageData: ImageData = this.getCanvas();
    const modelURL: string = './../../assets/models/model.json';

    let img: tf.Tensor<tf.Rank.R3> = tf.fromPixels(imageData);
    img = tf.image.resizeBilinear(img, [32, 32]);
    img = img.expandDims(0);

    await tf.loadModel(modelURL).then(model => {
        const output: any = model.predict(img);
        const results: number = output.argMax(1).dataSync()[0];
        console.log(results);
    }

Allt fungerar bra, inga fel alls, men när man loggar utdataförutsägelsen på konsolen returnerar den alltid samma värde. Dessutom är arrayen med förutsägelser väldigt flack, vilket tyder på att modellen i Python når 99,01% noggrannhet på testet:

console.log(Array.from(output.dataSync())); 

17  /* Result of the argMax function on output */
[0.011652837507426739, 0.03457817807793617, 0.029257778078317642, 0.008851004764437675, 0.01691449247300625, 0.026485547423362732, 0.04974032938480377, 0.016473202034831047, 0.021701140329241753, 0.020724112167954445, 0.03173287212848663, 0.024661116302013397, 0.007072054781019688, 0.022814681753516197, 0.011404283344745636, 0.015105938538908958, 0.024694452062249184, 0.07453715801239014, 0.011547397822141647, 0.03946337103843689, 0.018134022131562233, 0.027423541992902756, 0.014102200977504253, 0.016702469438314438, 0.05513478443026543, 0.030478181317448616, 0.012863627634942532, 0.011269242502748966, 0.022525735199451447, 0.022545555606484413, 0.02840271405875683, 0.011758353561162949, 0.006561313755810261, 0.020660076290369034, 0.009705542586743832, 0.024312887340784073, 0.011940978467464447, 0.020643217489123344, 0.009319263510406017, 0.00957920216023922, 0.01844164915382862, 0.015434195287525654, 0.02170679345726967, 0.017867043614387512, 0.013763428665697575, 0.029312126338481903]

De enda två sakerna som kommer till mitt sinne när jag försöker lösa detta problem är:

  1. ImageData extraherar inte korrekt teckningen från canvas och på grund av det returnerar prediktionen alltid samma värde.
  2. Vikterna på model.json åtkomst eller laddas inte, därför returnerar modellen samma värde.

Några idéer?

All hjälp uppskattas.

Svar #1

createImageData skapar en ny ImageData med transparent svart pixel. Därför kommer samma ImageData att returneras varje gång metoden anropas.

Du vill hellre rita om den ursprungliga bilden på en annan duk vars dimensioner (höjd och bredd) matchar formen på tensorn som ges som parameter till modellen.

// create a new canvas on which the initial canvas will be redrawn 
const canvas = document.createElement('canvas');
// draws on the first 32 * 32 pixels
canvas.drawImage(photos, 0, 0, 32, 32);
// returns the imageData corresponding to the part of the canvas we drew
return canvas.getImageData(0, 0, 32, 32);

Med dessa nya imageData är det inte längre nödvändigt att använda tf.bilinear, tf.reshape räcker för att ge tensorn den önskade formen.

img = tf.image.reshape(img, [1, 32, 32, 3]);

Svar #2

Bara en liten observation, hoppas det hjälper någon. Jag höll på med något liknande där jag detekterade handritade siffror. I mitt fall var bakgrundsfärgen på min duk Vit och min StreckFärg var Svart. Som ett resultat fick jag samma förutsägelse hela tiden. Sen bytte jag bara färgerna, nu var bakgrundsfärgen på min duk Svart och StreckFärgen var Vit. Då fungerade det.