TensorFlow.js机器学习教程 - js味儿的张量操作

Posted Jtag特工

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TensorFlow.js机器学习教程 - js味儿的张量操作相关的知识,希望对你有一定的参考价值。

TensorFlow.js机器学习教程(2) - js味儿的张量操作

既然我们使用TensorFlow.js来写机器学习代码而不是用Python版的TensorFlow和PyTorch,我们还是希望让代码充满js本身的味道。

复习:js数组是动态的

为了不被后面充满了静态语言特色的TensorFlow.js API带偏了,我们首先复习下js的数组操作。

首先我们不要忘了,js是一门动态语言,js的数组是动态数组,没有定长数组越界这一说法的。

比如说我们要给一个空数组的第2个元素赋值,这是没有任何问题的:

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

输出结果为:

[ <2 empty items>, 3 ]

我们可以毫无压力地用这样的数组去生成张量:

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

tf.js会给我们甩出两个NaN出来:

Tensor
    [NaN, NaN, 3]

不但是空数组随便添加元素,我们用new Array生成一个长度的数组后,仍然可以说话不算话,随意给赋值。比如我们new 5个元素的Array,给第9个赋值:

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


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

tf.js照例给我们补9个NaN出来:

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

如果懒得数一共多少个元素,就想在数组的末尾添加新元素,可以使用push方法,参数个数不限,push几个元素都可以:

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

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

输出为:

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

如果想从头添加新元素,可以使用unshift方法:

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();

输出为:

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

同时我们复习一下,与push相对的,删除最后一个元素的是pop方法;而与unshift相对的是shift方法。

比如我们对上面的a3进行pop:

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

所得结果为:

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

最后,我们还有强大的splice方法,可以在任意位置添加与删除。

splice方法的第一个参数是起始位置,第二个参数是要删除的个数。
我们来看个例子,我们先生成10个元素的数组,然后把前5个空元素都删掉:

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

输出结果为:

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

如果不删除,想要添加元素的话,我们可以给第二个参数置0,然后后面是要添加的元素。比如我们给上面的a5在100后面增加三个新元素1.5, 2.5, 3.5:

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

输出如下:

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

记住是要给元素值,而不是给个数组啊,否则的话就变成二维数组了:

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

结果为:

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

好,复习至此,我们来看tf.js中的张量

tf.js中的张量

一维张量

tfjs支持从1d到6d一共6维张量构造函数,当然7维以上没有专用函数了还是可以reshape出来。

最简单的张量是一维的,我们可以用tf.tensor1d:

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

输出为:

Tensor
    [1, 2, 3]

当然,还可以指定数据类型:

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

输出结果为:

Tensor
    [1, 2, 3]

数据类型可用值为:

  • ‘float32’
  • ‘int32’
  • ‘bool’
  • ‘complex64’
  • ‘string’

可以通过linspace函数来生成一维序列,其原型为:

tf.linspace (start, stop, num)

其中

  • start为起始值
  • end为结束值
  • num为生成的序列的元素个数

例:

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

输出结果为:

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

如果想用指定步长的方式来生成,可以使用range函数:

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

我们来看个例子:

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

输出结果为:

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

二维张量

二维张量可以用二维数组来定义:

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

不过tf.js的二维张量必须是矩阵,而js的二维数组是可以不等长的,这点尤其要注意。

因为二维张量主要用于存放矩阵,有生成矩阵的方法可供调用。

比如我们可以使用tf.eye来生成单位矩阵:

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

输出结果为:

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

我们也可以将一维向量转化为以其为对角向量的二维向量:

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

输出结果为:

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]]

从二维张量开始,我们可以指定张量的形状了。

比如我们用一维数组给定值,然后指定[2,2]的形状:

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

输出结果如下:

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

高维向量

从三维开始,用高维数组来表示张量值的可读性就越来越差了。比如:

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

输出结果为:

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

     [[3],
      [4]]]

我们可以还是先指定一维数组,然后再指定形状:

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

输出如下:

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

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

我们向4,5,6维挺进:

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();

输出如下:

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

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

       [[3],
        [4]]],


      [[[5],
        [6]],

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

        [[3],
         [4]]],


       [[[5],
         [6]],

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

此时,指定形状的优势就更加明显了。

我们可以用tf.zeros函数生成全是0的任意维的张量:

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

也可以通过tf.ones将所有值置为1:

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

还可以通过tf.fill函数生成为指定值的张量:

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

比起序列值和固定值,生成符合正态分布的随机值可能是更常用的场景。其原型为:

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

其中:

  • shape是张量形状
  • mean是平均值
  • stdDev是标准差
  • dtype是数据类型,整形和浮点形在此差别可能很大
  • seed是随机数种子

我们看个例子:

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

输出如下:

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]]]

将张量转换成js数组

前面我们学习了很多种张量的生成方法。但是,不知道你意识到了没有,很多时候还是转回到js数组更容易进行一些高阶的操作。

将张量转换成为数组有两种方式,一种是按照原形状转换成数组。异步的可以使用Tensor.array()方法,同步的可以使用Tensor.arraySync()方法。

我们来将上节生成的随机数的向量转回成js的数组:

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

输出结果为:

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

记得这是一个高维数组啊,每个元素都是数组。
比如:

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

输出将是两个数组元素:

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

如果不想要形状,可以用data()或者dataSync()方法将张量转换成TypedArray.

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

输出结果如下:

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

如果对TypedArray进行forEach操作:

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

获取的结果就是线性的了:

0
1
1
0
0
0
1
2

拍平成一维的之后,我们就可以用every和some等来进行元素的判断了。
比如我们看a5是不是所有元素都是0,是不是有元素为0:

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

因为不全为0,所以every的值为假,而some为真。

以上是关于TensorFlow.js机器学习教程 - js味儿的张量操作的主要内容,如果未能解决你的问题,请参考以下文章

TensorFlow.js机器学习教程 - js味儿的张量操作

TensorFlow.js机器学习教程 - 穿梭于浏览器与node间

TensorFlow.js机器学习教程 - 穿梭于浏览器与node间

TensorFlow js. 官方教程

TensorFlow js. 官方教程

TensorFlow js. 官方教程