J'ai besoin de calculer l'histogramme du grand tenseur 1D avec tensorflow.js. J'ai trouvé un moyen de le faire, mais ce n'est pas optimal en raison de la consommation de mémoire élevée dans l'opération oneHot. Mon exemple de code ci-dessous:

 for (let index = 0; index < 50; index++) { //repeat some times to ensure no memory leaks
   const hist = getnormalHist(index);
   const histArray = (await hist.array()) as number[];
   const values = histArray.map((v, i) => ({ index: i, value: v }));
   await tfvis.render.barchart({ name: "hist" }, values);
 }

 function getnormalHist(seed: number) {
   return tf.tidy(() => {
     const rand = tf
       .randomNormal([100000], 0, 100, "int32", seed) //Generates long array of normal distributed randoms
       .add(500)
       .toInt();
     const oneHot = rand.oneHot(1000, 1, 0); // convert to oneHot makes it x1000 bigger
     const hist = oneHot.transpose().sum(1); // finally get histogram tensor
     return hist;
   });
 }

J'ai besoin de rendre ce code plus rapide et avec moins de consommation de mémoire, mais je ne comprends pas comment.

1
Mikhail Sokolov 29 août 2020 à 18:50

3 réponses

Meilleure réponse

Maintenant, la meilleure solution que j'ai trouvée est:

const counts = 100000;
const channels = 1000;
const rand = tf.randomNormal([counts], 500, 100, "int32");
const hist = tf.sparseToDense(rand, tf.onesLike(rand), [channels], 0);

0
Mikhail Sokolov 5 sept. 2020 à 07:59

Ma solution consiste à créer une opération webGL personnalisée.

class HistProgram {
  variableNames = ["X"];
  outputShape: number[];
  userCode: string;

  constructor(numIndices: number, counts: number) {
    this.outputShape = [numIndices];
    this.userCode = `
      void main() {
        int ch = getOutputCoords();
        int c = 0;
        for (int i = 0; i < ${counts}; i++){
            if(ch == int(getX(i))){
                c++;
            }
        }
        setOutput(float(c));
      }
    `;
  }
}

Maintenant, je peux l'utiliser de cette manière:

const counts = 100000;
const channels = 1000;
const rand = tf.randomNormal([counts], 500, 100, "int32")
const prog = new HistProgram(channels, counts);
const hist = await tf.backend().compileAndRun(prog, [rand]).array();
0
Mikhail Sokolov 1 sept. 2020 à 15:39
tf.randomNormal([100000], 0, 100, "int32", seed) //Generates long array of normal distributed randoms
       .add(500)
       .toInt()

L'opération ci-dessus génère une distribution d'échantillon avec une moyenne d'environ 500. Elle peut être générée en utilisant une moyenne de 500 au lieu de 0. L'opération d'ajout n'est pas nécessaire pour cela. De plus, il est redondant d'utiliser toInt puisque le dtype est déjà int32. Ainsi, il peut être simplifié de cette façon:

tf.randomNormal([100000], 500, 100, "int32", seed)

Il est redondant de spécifier 0 et 1 onvalue et offvalue car ils sont la valeur par défaut.

rand.oneHot(1000)

Il n'est pas nécessaire de transposer avant de calculer la somme de chaque indice. Le calcul de la somme sur l'axe 0 comptera chaque index. Une matrice intermédiaire de taille 100000 * 1000 ne sera plus utilisée.

oneHot.sum(0)

En résumé, getNormalHist ressemblera à ceci:

function getnormalHist(seed: number) {
   return tf.tidy(() => {
     const rand = tf
       .randomNormal([100000], 500, 100, "int32", seed) //Generates long array of normal distributed randoms

     const oneHot = rand.oneHot(1000); // convert to oneHot makes it x1000 bigger
     const hist = oneHot.sum(0); // finally get histogram tensor
     return hist;
   });
 }
0
edkeveked 31 août 2020 à 12:04