版本对应:

本网页讨论的机器学习内容,为特定的机器学习内容(并未与CFD结合)。在《无痛苦NS方程笔记》中, 特定的将CFD与机器学习互相结合起来,无普适性机器学习内容。

ML: libtorch张量(一)

在机器学习领域,如果使用libtorch来做训练,要定义大量的tensor。这里面的tensor与CFD里面的tensor要做区别。在CFD里面,最复杂的tensor基本就是二阶张量,就比如雷诺应力,具有9个分量。CFD计算领域更高阶的tensor很少见。OpenFOAM甚至没有提供更高阶tensor的接口。可见,对于一个CFD学习者来说,对于tensor,最高理解到雷诺应力就可以满足99%的研究场景。

但是机器学习领域不一样。首先,因为机器学习主要是倾向于做一些图形识别等这种任务。这跟CFD的计算数学区别很大。因此在libtorch里面的tensor,不能跟CFD那面的tensor直接划等号。最好的理解,就是把libtorch的tensor理解为存储数据的一个东西。用来存储数据而已。

再次强调,在下文中提到的张量,均表示libtorch里面的tensor,而不是CFD领域的tensor。

张量(libtorch里面的tensor)存在形状与维度的概念。张量一般包括矢量、矩阵、张量(这个张量表示除了矢量与矩阵外的更高维度的张量)。目前这个阶段一上来就给出形状以及维度的定义,是非常抽象与困难的。本文以独特的风格去对张量进行介绍。

一个数

如果只有一个数,有2种选择。可以创建0维张量,也可以创建只有一个元素的1维张量(也即矢量)。也即只有一个元素的矢量。在libtorch编程里面,更倾向于后者。在这个链接里面表示,在老版本的0维张量甚至不支持带有元素的0维张量。下面的代码可以创建0维张量以及只有一个元素的1维张量(也即矢量):

auto a = torch::tensor(5.0); //0维张量

auto b = torch::tensor({5}); //只有一个元素的1维张量,也即只有一个元素的矢量

其输出为:

a:
5
[ CPUFloatType{} ]

b:
5
[ CPUFloatType{1} ]

CPUFloatType{}括号里面数字,表示元素数量。如果没有数字,表示是一个0维张量,且只能包含一个数。CPUFloatType{1},表示元素存在1个。

一组多个数,矢量

如果要声明多个数,最简单的就是声明一个1维张量。当然也可以声明多维张量,每个张量里面只有1个数。这部分目前我们先不去理解,到后面再说。

如果有下述代码:

auto a = torch::tensor({1.0,2.0,3.0,4.0}); //1维张量,4个元素

其输出为:

a:
1
2
3
4
[ CPUFloatType{4} ]

首先,CPUFloatType{4}里面的4表示有4个元素。并且只有一个数字4,表示是个1维张量,也即矢量。

Warning

是的,矢量可以包含多个元素,不仅仅只有3个元素,300个元素也可以。

下面的代码,表示首先创建一个全部都是0的,具有10个元素的矢量。然后把其中的第0个元素改成3,第2个元素改成4。

auto aa = torch::zeros({10});
aa[0] = 3.0;
aa[2] = 4.0;

其相应的CPUFloatType{}CPUFloatType{10}

多组多个数,矩阵

首先考虑2组多个数,可以通过下面的代码来创建:

auto f = torch::tensor({{1.0, 2.0}, {3.0, 4.0}});

其输出为

 1  2
 3  4
[ CPUFloatType{2,2} ]

这一次会发现CPUFloatType{2,2}里面有2个数。这表示这个f里面存在两个矢量。进一步的,这两个数都是2,也就是表示每个矢量包含2个元素。同时要注意这个排列顺序。类似的,下列代码:

auto f = torch::tensor({{1.0, 2.0, 3.0, 4.0}, {5.0, 6.0, 7.0, 8.0}});

的输出为:

 1  2  3  4
 5  6  7  8
[ CPUFloatType{2,4} ]

在这里先不对其进行解释,或许把f的代码换一种写法更好理解,下面是一个浓浓的OpenFOAM风格的代码:

auto f = 
    torch::tensor
    (
        {
            {1.0, 2.0, 3.0, 4.0}, //a行
            {5.0, 6.0, 7.0, 8.0}  //b行
        }
    );

其中{2,4}中的2对应f声明里面的a行与b行,因为只有2行,因此是第一个数字是2。{2,4}中的4对应每一行的元素数量。同样的要注意这个排列顺序

Warning

2组多个数可以理解为矩阵。{2,4}第一个数字可以理解为行数,第二个数字可以理解为列数。

如果希望输出为

 1 5
 2 6
 3 7
 4 8

应该怎么办?首先,在这里我们应该知道,其 CPUFloatType应该是{4,2}。这对应4行2列。同时,4对应f声明里面的{}括号里{}矢量的行数,因此,应该是类似下面这样的一个东西:

auto f = 
    torch::tensor
    (
        {
            {...}, //1
            {...}, //2
            {...}, //3
            {...}  //4
        }
    );

进一步的,要对每一行进行填充,那就变成了

auto f = 
    torch::tensor
    (
        {
            {1.0,5.0}, //1
            {2.0,6.0}, //2
            {3.0,7.0}, //3
            {4.0,8.0}  //4
        }
    );

这样我们就完成了一个矩阵的声明。到目前为止,给定一个任意的\(n\times m\)矩阵,也即多组多个数,应该可以通过libtorch进行声明了。

接下来的问题是,能否完成下面类似矩阵的定义?

 1 5 9
 2 6
 3 7
 4 8

上面这种是不可实现的。这并不是一个矩阵。

好多个多组多个数,张量

现在回头看CPUFloatType{}进行总结:

  • CPUFloatType{}{}里面没有数,表示0D张量;可以理解为一个数;

  • CPUFloatType{23}{}里面有一个数,表示矢量,具有23个元素;矢量可以理解为一组多个数;

  • CPUFloatType{4, 23}{}里面有两个个数,表示矩阵,具有4行,23列;矩阵可以理解为多组多个数;

如果有好多个多组多个数,CPUFloatType{}里面的数字数量会增加,比如变成CPUFloatType{2, 4, 3}。这表示有2组,\(4\times 3\)的矩阵。可以通过下面的代码进行声明:

auto test = 
    torch::tensor
    (
        {
            {
                {1.0, 2.0, 3.0}, 
                {4.0, 5.0, 6.0}, 
                {7.0, 8.0, 9.0},            
                {10.0, 11.0, 12.0}
            },
            {
                {13.0, 14.0, 15.0}, 
                {16.0, 17.0, 18.0}, 
                {19.0, 20.0, 21.0}, 
                {22.0, 23.0, 24.0}
            }
        }
    );

很明显,第1组\(4\times 3\)的矩阵为

{1.0, 2.0, 3.0}
{4.0, 5.0, 6.0}
{7.0, 8.0, 9.0}           
{10.0, 11.0, 12.0}

第2组\(4\times 3\)的矩阵为

{13.0, 14.0, 15.0}
{16.0, 17.0, 18.0}
{19.0, 20.0, 21.0}
{22.0, 23.0, 24.0}

在这里不去对每个括号的对应关系去解释。相信OpenFOAM风格的那种代码风格看起来会很明白。

在机器学习领域的代码风格可能是这种的(因为那面用途python居多),个人觉得直接晕掉了:

auto tensor = torch::tensor({{{1.0, 2.0, 3.0}, {4.0, 5.0, 6.0}, {7.0, 8.0, 9.0}, {10.0, 11.0, 12.0}}, {{13.0, 14.0, 15.0}, {16.0, 17.0, 18.0}, {19.0, 20.0, 21.0}, {22.0, 23.0, 24.0}}});

以此类推,CPUFloatType{23, 123, 34}就表示了23组\(123 \times 23\)矩阵。回头看矩阵,如果是CPUFloatType{4, 23},也可以理解为4组含有23个元素的矢量。对于更高维的矩阵。这样理解看起来会更简单一些。比如CPUFloatType{20, 23, 123, 34},首先看后面2个数,这应该是一个\(123\times 34\)的矩阵,然后看23,那应该是23组\(123 \times 23\)矩阵。前面还有个20,这可以理解为把这23组\(123 \times 23\)矩阵当成一组。类似的这种组,还有23组。同时,因为CPUFloatType{20, 23, 123, 34}里面有4个数,因此这是一个4D张量。

更高维度的张量还可以进行类似的去理解。但是即使在机器学习领域用的也是比较少了。但是4D张量用的还是非常多的。对于CPUFloatType{a, b, c, d}这种4D张量,我们可以把a理解为批次的数量,把b理解为通道的数量,把c和d理解为图像的长和宽的像素数。类似这种4D张量在做图像识别的时候大量存在。在目前这个阶段,来理解批次、通道可能会比较困难。相信后续在阅读卷积神经网络的内容后,会更好理解。

在于CFD结合的时候,CPUFloatType{a, b, c, d}这种4D张量也可以把a理解为批次,b可以理解为速度的分量个数,如果是三维模拟,那么b就是3,c和d则对应网格上的点。在这里存在一个对应关系。例如OpenFOAM里面的网格点是按照一维处理的。但是在机器学习领域是按照长宽来处理的。这里面牵涉到一个数据的对应。感兴趣可以阅读这个链接,在此不过多介绍。

在这里可以定义张量的形状与维度。CPUFloatType{}里面的数字组合,就是张量的形状CPUFloatType{}里面有几个数,就表示有几个维度

ML: libtorch张量(二)中,我们继续介绍张量的基本操作。