TensorFlow.js Machine Learning Tutorial - Tensor manipulation of JS taste

TensorFlow.js Machine Learning Tutorial (2) - Tensor manipulation of JS taste

Now that we use TensorFlow.js to write machine learning code instead of Python versions of TensorFlow and PyTorch, we want to make the code taste JS itself.

Review: js arrays are dynamic

To avoid being skewed by the TensorFlow.js API, which is later full of static language features, let's first review the array operations for js.

First of all, let's not forget that js is a dynamic language, js arrays are dynamic arrays, there is no such statement that fixed-length arrays are out of bounds.

For example, if we want to assign a value to the second element of an empty array, there is no problem:

let a1 = [];
a1[2] = 3;
console.log(a1);

The output is:

[ <2 empty items>, 3 ]

We can use such arrays to generate tensors without any pressure:

let a1_t = tf.tensor1d(a1);
a1_t.print();

tf.js will shake out two NaN s for us:

Tensor
    [NaN, NaN, 3]

Not only is an empty array free to add elements, but once we have generated an array of lengths with new Array, we can still say nothing and assign values at will.For example, let's say we have Array of five new elements, which assigns a value to the ninth:

let a2 = new Array(5);
a2[9] = 10;
console.log(a2);


let a2_t = tf.tensor1d(a2);
a2_t.print();

tf.js gives us 9 NaN s as usual:

[ <9 empty items>, 10 ]
Tensor
    [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 10]

If you are too lazy to count a total of several elements, and want to add a new element at the end of the array, you can use the push method with an unlimited number of parameters. Push can have several elements:

let a3 = new Array();
a3.push(1,2,3);
a3.push(4,5);

let a3_t = tf.tensor1d(a3);
a3_t.print();

The output is:

Tensor
    [1, 2, 3, 4, 5]

If you want to add new elements from scratch, you can use the unshift method:

let a3 = new Array();
a3.push(1,2,3);
a3.push(4,5);
a3.unshift(6);

let a3_t = tf.tensor1d(a3);
a3_t.print();

The output is:

Tensor
    [6, 1, 2, 3, 4, 5]

In the meantime, let's review that, as opposed to push, the pop method deletes the last element.The shift method is the opposite of unshift.

For example, let's pop the a3 above:

let a4 = a3;
let a00 = a3.pop();
console.log(a00);
console.log(a4);

The result is:

5
[ 6, 1, 2, 3, 4 ]

Finally, we have a powerful splice method that you can add and delete anywhere.

The first parameter of the splice method is the starting position, and the second parameter is the number to delete.
Let's take an example. Our husband makes an array of 10 elements and deletes the first five empty elements:

let a5 = []
a5.length = 10;
a5[5] = 100;
console.log(a5);
a5.splice(0,5);
console.log(a5);

The output is:

[ <5 empty items>, 100, <4 empty items> ]
[ 100, <4 empty items> ]

If you don't delete and want to add an element, we can set the second parameter to 0, followed by the element to be added.For example, let's add three new elements 1.5, 2.5, 3.5 to a5 above after 100:

a5.splice(1,0,1.5,2.5,3.5);
console.log(a5);

The output is as follows:

[ 100, 1.5, 2.5, 3.5, <4 empty items> ]

Remember to give element values, not arrays, or two-dimensional arrays:

a5.splice(1,0,[1.5,2.5,3.5]);
console.log(a5);

The results are:

[ 100, [ 1.5, 2.5, 3.5 ], 1.5, 2.5, 3.5, <4 empty items> ]

Okay, let's go over this and look at the tensor in tf.js

Tensors in tf.js

One-dimensional tensor

tfjs supports a total of 6-dimensional tensor constructors from 1d to 6d. Of course, reshape is possible without a special function for more than 7d.

The simplest tensor is one dimensional, so we can use tf.tensor1d:

let t1d = tf.tensor1d([1, 2, 3]);
t1d.print();

The output is:

Tensor
    [1, 2, 3]

Of course, you can also specify a data type:

const t1d_f = tf.tensor1d([1.0,2.0,3.0],'float32')
t1d_f.print();

The output is:

Tensor
    [1, 2, 3]

Available values for data types are:

  • 'float32'
  • 'int32'
  • 'bool'
  • 'complex64'
  • 'string'

One-dimensional sequences can be generated using the linspace function, which is prototyped as:

tf.linspace (start, stop, num)

among

  • start is the starting value
  • End is the end value
  • num is the number of elements in the generated sequence

Example:

tf.linspace(1, 10, 10).print();

The output is:

Tensor
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

If you want to generate it with a specified step size, you can use the range function:

tf.range(start, stop, step?, dtype?)

Let's take an example:

tf.range(0, 9, 2).print();

The output is:

Tensor
    [0, 2, 4, 6, 8]

Two-dimensional Tensor

Two-dimensional tensors can be defined with a two-dimensional array:

let t2d = tf.tensor2d([[0,0],[0,1]]);
t2d.print();

It is important to note, however, that the two-dimensional tensor of tf.js must be a matrix, and that the two-dimensional arrays of JS can be unequally long.

Since two-dimensional tensors are mainly used to store matrices, there are ways to generate matrices that can be invoked.

For example, we can use tf.eye to generate the unit matrix:

const t_eye = tf.eye(4);
t_eye.print();

The output is:

Tensor
    [[1, 0, 0, 0],
     [0, 1, 0, 0],
     [0, 0, 1, 0],
     [0, 0, 0, 1]]

We can also convert a one-dimensional vector into a two-dimensional vector with its diagonal vector:

const x1 = tf.tensor1d([1, 2, 3, 4, 5, 6, 7, 8]);
tf.diag(x1).print();

The output is:

Tensor
    [[1, 0, 0, 0, 0, 0, 0, 0],
     [0, 2, 0, 0, 0, 0, 0, 0],
     [0, 0, 3, 0, 0, 0, 0, 0],
     [0, 0, 0, 4, 0, 0, 0, 0],
     [0, 0, 0, 0, 5, 0, 0, 0],
     [0, 0, 0, 0, 0, 6, 0, 0],
     [0, 0, 0, 0, 0, 0, 7, 0],
     [0, 0, 0, 0, 0, 0, 0, 8]]

Starting with a two-dimensional tensor, we can specify the shape of the tensor.

For example, we give a value with a one-dimensional array and specify the shape of [2,2]:

let t2d2 = tf.tensor2d([1,2,3,4],[2,2],'float32');
t2d2.print();

The output is as follows:

Tensor
    [[1, 2],
     [3, 4]]

High Dimension Vector

Starting in three dimensions, using high-dimensional arrays to represent tensor values becomes less readable.For example:

tf.tensor3d([[[1], [2]], [[3], [4]]]).print();

The output is:

Tensor
    [[[1],
      [2]],

     [[3],
      [4]]]

Can we specify a one-dimensional array before specifying a shape?

tf.tensor3d([1,2,3,4,5,6,7,8],[2,2,2],'int32').print();

The output is as follows:

Tensor
    [[[1, 2],
      [3, 4]],

     [[5, 6],
      [7, 8]]]

We're heading into the 4, 5, 6 dimensions:

tf.tensor4d([[[[1], [2]], [[3], [4]]]]).print();
tf.tensor5d([[[[[1],[2]],[[3],[4]]],[[[5],[6]],[[7],[8]]]]]).print();
tf.tensor6d([[[[[[1],[2]],[[3],[4]]],[[[5],[6]],[[7],[8]]]]]]).print();

The output is as follows:

Tensor
    [[[[1],
       [2]],

      [[3],
       [4]]]]
Tensor
    [[[[[1],
        [2]],

       [[3],
        [4]]],


      [[[5],
        [6]],

       [[7],
        [8]]]]]
Tensor
    [[[[[[1],
         [2]],

        [[3],
         [4]]],


       [[[5],
         [6]],

        [[7],
         [8]]]]]]

At this point, the advantage of specifying a shape is more obvious.

We can use the tf.zeros function to generate a tensor of any dimension that is all 0:

tf.zeros([2,2,2,2,2,2]).print();

You can also set all values to 1 through tf.ones:

tf.ones([3,3,3]).print();

The tf.fill function can also be used to generate a tensor of a specified value:

tf.fill([4,4,4],255).print();

Generating random values that conform to the normal distribution may be a more common scenario than sequence values and fixed values.Its prototype is:

tf.truncatedNormal(shape, mean?, stdDev?, dtype?, seed?)

Where:

  • Shape is a tensor shape
  • mean is average
  • stdDev is the standard deviation
  • dtype is a data type, where integers and floating-point shapes may differ greatly
  • Seed is a random number seed

Let's take an example:

tf.truncatedNormal([3,3,3],1,1,"float32",123).print();
tf.truncatedNormal([2,2,2],1,1,"int32",99).print();

The output is as follows:

Tensor
    [[[0.9669023 , 0.2715541 , 0.6810297 ],
      [-0.8329115, -0.7022814, 1.4331075 ],
      [1.8136243 , 1.8001028 , -0.3285823]],

     [[1.381816  , 1.1050107 , 0.7487067 ],
      [1.9785664 , 0.9248876 , -0.9470147],
      [0.0489896 , 0.3297685 , 0.8626058 ]],

     [[0.3341007 , 1.1067212 , 0.4879217 ],
      [2.1620302 , 1.3034405 , 0.2832415 ],
      [1.3012471 , 1.0853187 , 1.9235317 ]]]
Tensor
    [[[0, 1],
      [1, 0]],

     [[0, 0],
      [1, 2]]]

Converting a tensor to a js array

Previously, we learned a lot about how to generate tensors.However, I don't know if you realize that many times it's easier to go back to the js array and do some higher-order operations.

There are two ways to convert a tensor to an array, one is to convert it to an array according to its original shape.Asynchronous can use the Tensor.array() method, and synchronous can use the Tensor.arraySync() method.

Let's turn the vector of the random number generated in the previous section back into a js array:

let t7 = tf.truncatedNormal([2,2,2],1,1,"int32",99);
let a7 = t7.arraySync();
console.log(a7);

The output is:

[ [ [ 0, 1 ], [ 1, 0 ] ], [ [ 0, 0 ], [ 1, 2 ] ] ]

Remember that this is a high-dimensional array, and every element is an array.
For example:

a7.forEach(
    (x) => { console.log(x);}
);

The output will be two array elements:

[ [ 0, 1 ], [ 1, 0 ] ]
[ [ 0, 0 ], [ 1, 2 ] ]

If you don't want shapes, you can convert the tensor to TypedArray using either data() or dataSync().

let t5 = tf.truncatedNormal([2,2,2],1,1,"int32",99);
let a5 = t5.dataSync();
console.log(a5);

The output is as follows:

Int32Array(8) [
  0, 1, 1, 0,
  0, 0, 1, 2
]

If you forEach TypedArray:

a5.forEach(
    (x) => { console.log(x);}
);

The result is linear:

0
1
1
0
0
0
1
2

Once flattened into one dimension, we can use every and some s to make element judgments.
For example, let's see if a5 is zero for all elements and zero for some elements:

console.log(a5.every((x) => { return(x===0)}));
console.log(a5.some((x) => { return(x===0)}));

Because not all are zero, each has a false value and some is true.

Keywords: Python Javascript TensorFlow

Added by trmbne2000 on Tue, 07 Sep 2021 01:56:49 +0300