pytorch、tensorflow对比学习—张量

张量

前言

本文是《pytorch-tensorflow-Comparative study》,pytorch和tensorflow对比学习专栏,第一章——张量。

虽然说这两个框架在语法和接口的命名上有很多地方是不同的,但是深度学习的建模过程确实基本上都是一个套路的。

所以该笔记的笔记方式是:在使用相同的处理功能模块上,对比记录pytorch和tensorflow两者的API接口,和语法。
pytorch、tensorflow对比学习—张量_第1张图片

1,有利于深入理解深度学习建模过程流程。

2,有利于理解pytorch,和tensorflow设计上的不同,更加灵活的使用在自己的项目中。

3,有利于深入理解各个功能模块的使用。

本章节主要对比学习pytorch 和tensorflow有关张量操作的的API接口,和语法。

pytorch、tensorflow对比学习—张量_第2张图片

张量的数据结构

​ 程序 = 数据结构+算法。

​ TensorFlow和pytorch程序 = 张量数据结构 + 计算图算法语言,区别在于pythorch都是动态图语言,tensorflow在1.X静态图,tensorflow2.x之后的版本吸收了动态图的思想,既可以使用动态图,也可以使用静态图。

pytorch、tensorflow对比学习—张量_第3张图片

​ 张量和计算图是 TensorFlow和pythorch的核心概念。

​ Tensorflow和pythorch的基本数据结构是张量Tensor。张量即多维数组。和numpy中的array很类似。

​ 本节我们主要介绍二者种张量的数据类型、张量的维度、张量的尺寸、张量和numpy数组等基本概念,已经比较其中的不同,加深记忆。

张量数据类型

pytorch

在使用pytorch过程中会发现他的语法非常拟合python语法形式,张量的数据类型和numpy.array基本一一对应,但是不支持str类型,(在pytorch的张量数据结构中基本不需要用到str类型,这是因为张量基本是用于数值计算的)。

包括:

torch.float64(torch.double),torch.float32(torch.float),torch.float16,torch.int64(torch.long),torch.int32(torch.int),torch.int16,torch.int8,torch.uint8,torch.bool

一般神经网络建模使用的都是torch.float32类型。

import numpy as np
import torch 

# 自动推断数据类型
i = torch.tensor(1);print(i,i.dtype)
x = torch.tensor(2.0);print(x,x.dtype)
b = torch.tensor(True);print(b,b.dtype)

# tensor(1) torch.int64
# tensor(2.) torch.float32
# tensor(True) torch.bool
# 指定数据类型
i = torch.tensor(1,dtype = torch.int32);print(i,i.dtype)
x = torch.tensor(2.0,dtype = torch.double);print(x,x.dtype)

# tensor(1, dtype=torch.int32) torch.int32
# tensor(2., dtype=torch.float64) torch.float64
# 使用特定类型构造函数

i = torch.IntTensor(1);print(i,i.dtype)
x = torch.Tensor(np.array(2.0));print(x,x.dtype) #等价于torch.FloatTensor
b = torch.BoolTensor(np.array([1,0,2,0])); print(b,b.dtype)

#tensor([5], dtype=torch.int32) torch.int32
#tensor(2.) torch.float32
#tensor([ True, False,  True, False]) torch.bool

tensorflow

tensorflow 由于刚开始设计之初在TensorFlow1.0时代,采用的是静态计算图,需要先使用TensorFlow的各种算子创建计算图,然后再开启一个会话Session,显式执行计算图。因此在定义变量(以及算子)不像python以及pythorch语法一样这么随意自由,需要更严格的声明。虽然在TensorFlow2.0时代,加入了动态计算图,使得算子的设计可以更加灵活,便于调试,但是定义变量的方式依旧传承下来。

(本质原因是2.0时代相对于1.0时代,相当于增加了一层由动态算子图编译为静态图的封装。这个问题将在tensorflow机制原理中讲解,这里只做简要说明)。

tensorflow的张量数据结构又分类为:常量constant和变量Variable.

常量的值在计算图中不可以被重新赋值,变量可以在计算图中用assign等算子重新赋值。(pytorch中就没有这样的区分)。

其他方面张量的数据类型和numpy.array基本一一对应。

pytorch、tensorflow对比学习—张量_第4张图片

常量变量

import numpy as np
import tensorflow as tf

i = tf.constant(1) # tf.int32 类型常量
l = tf.constant(1,dtype = tf.int64) # tf.int64 类型常量
f = tf.constant(1.23) #tf.float32 类型常量
d = tf.constant(3.14,dtype = tf.double) # tf.double 类型常量
s = tf.constant("hello world") # tf.string类型常量
b = tf.constant(True) #tf.bool类型常量


print(tf.int64 == np.int64) 
print(tf.bool == np.bool)
print(tf.double == np.float64)
print(tf.string == np.unicode) # tf.string类型和np.unicode类型不等价

#True
#True
#True
#False

因为tensorflow有常量constant和变量Variable.的区分,因此tensorflow中可以自己定义str,这也是和pytorch的一个不同。

这并不是因为pytorch没有常量constant而无法定义str,而是因为pytorch设计之初就有着和python语言很好的融合性。pytorch在使用张量数据时可以自动分析那些元素做变量使用,那些做常量使用。pytorch并不需要自己定义str变量,直接使用python定义的str就可以。而tensorflow在编译静态图的时候会跳脱python编译的过程,使用自己的编译库,因此它也需要留有自己定义变量接口的原因。

因此这也是同样基于python语言来使用的两个库,tensorflow要显得比pytorch臃肿一些的原因,但是这也是tensorflow能够做到更快速度所不得不做出的牺牲。

变量张量

模型中需要被训练的参数一般被设置成变量。

# 常量值不可以改变,常量的重新赋值相当于创造新的内存空间
c = tf.constant([1.0,2.0])
print(c)
print(id(c))
c = c + tf.constant([1.0,1.0])
print(c)
print(id(c))
# tf.Tensor([1. 2.], shape=(2,), dtype=float32)
# 5276289568
# tf.Tensor([2. 3.], shape=(2,), dtype=float32)
# 5276290240
# 变量的值可以改变,可以通过assign, assign_add等方法给变量重新赋值
v = tf.Variable([1.0,2.0],name = "v")
print(v)
print(id(v))
v.assign_add([1.0,1.0])
print(v)
print(id(v))
# 
# 5276259888
# 
# 5276259888

pytorch、tensorflow对比学习—张量_第5张图片

不同数据类型进行转换

pytorch

# 不同类型进行转换
i = torch.tensor(1); print(i,i.dtype)
x = i.float(); print(x,x.dtype) #调用 float方法转换成浮点类型
y = i.type(torch.float); print(y,y.dtype) #使用type函数转换成浮点类型
z = i.type_as(x);print(z,z.dtype) #使用type_as方法转换成某个Tensor相同类型

# tensor(1) torch.int64
# tensor(1.) torch.float32
# tensor(1.) torch.float32
# tensor(1.) torch.float32

tensorflow

可以用tf.cast改变张量的数据类型。

h = tf.constant([123,456],dtype = tf.int32)
f = tf.cast(h,tf.float32)
print(h.dtype, f.dtype)
#  

张量的维度

pytroch中不同类型的数据可以用不同维度(dimension)的张量来表示。

tensorflow中不同类型的数据可以用不同维度(rank)的张量来表示。

标量为0维张量,向量为1维张量,矩阵为2维张量。

彩色图像有rgb三个通道,可以表示为3维张量。

视频还有时间维,可以表示为4维张量。

可以简单地总结为:有几层中括号,就是多少维的张量。

pytorch、tensorflow对比学习—张量_第6张图片

pytorch

scalar = torch.tensor(True)
print(scalar)
print(scalar.dim())  # 标量,0维张量
# tensor(True)
# 0
vector = torch.tensor([1.0,2.0,3.0,4.0]) #向量,1维张量
print(vector)
print(vector.dim())
# tensor([1., 2., 3., 4.])
# 1
matrix = torch.tensor([[1.0,2.0],[3.0,4.0]]) #矩阵, 2维张量
print(matrix)
print(matrix.dim())
tensor3 = torch.tensor([[[1.0,2.0],[3.0,4.0]],[[5.0,6.0],[7.0,8.0]]])  # 3维张量
print(tensor3)
print(tensor3.dim())

# tensor([[[1., 2.],
#          [3., 4.]],
# 
#         [[5., 6.],
#          [7., 8.]]])
# 3

tensorflow

scalar = tf.constant(True)  #标量,0维张量
print(tf.rank(scalar))
print(scalar.numpy().ndim)  # tf.rank的作用和numpy的ndim方法相同
# tf.Tensor(0, shape=(), dtype=int32)
# 0
vector = tf.constant([1.0,2.0,3.0,4.0]) #向量,1维张量
print(tf.rank(vector))
print(np.ndim(vector.numpy()))
# tf.Tensor(1, shape=(), dtype=int32)
# 1
matrix = tf.constant([[1.0,2.0],[3.0,4.0]]) #矩阵, 2维张量
print(tf.rank(matrix).numpy())
print(np.ndim(matrix))
# 2
# 2
tensor3 = tf.constant([[[1.0,2.0],[3.0,4.0]],[[5.0,6.0],[7.0,8.0]]])  # 3维张量
print(tensor3)
print(tf.rank(tensor3))
# tf.Tensor(
# [[[1. 2.]
#   [3. 4.]]
# 
#  [[5. 6.]
#   [7. 8.]]], shape=(2, 2, 2), dtype=float32)
# tf.Tensor(3, shape=(), dtype=int32)

张量的尺寸

pytorch

可以使用 shape属性或者 size()方法查看张量在每一维的长度.

scalar = torch.tensor(True)
print(scalar.size())
print(scalar.shape)
# torch.Size([])
# torch.Size([])
matrix = torch.tensor([[1.0,2.0],[3.0,4.0]])
print(matrix.size())
# torch.Size([2, 2])

tensorflow

可以用numpy方法将tensorflow中的张量转化成numpy中的张量。

可以用shape方法查看张量的尺寸。

y = tf.constant([[1.0,2.0],[3.0,4.0]])
print(y.numpy()) #转换成np.array
print(y.shape)
# [[1. 2.]
#  [3. 4.]]
# (2, 2)

张量的结构操作

张量结构操作诸如:张量创建,索引切片,维度变换,合并分割。

创建张量

张量创建的许多方法和numpy中创建array的方法很像。


简单张量创建

import numpy as np
import torch 

import tensorflow as tf
import numpy as np 
pytorch tensorflow
a = torch.tensor([1,2,3],dtype = torch.float) a = tf.constant([1,2,3],dtype = tf.float32)
#tensor([1., 2., 3.]) #[1 2 3]
b = torch.arange(1,10,step = 2) b = tf.range(1,10,delta = 2)
#tensor([1, 3, 5, 7, 9]) #[1 3 5 7 9]
c = torch.linspace(0.0,2*3.14,10) c = tf.linspace(0.0,2*3.14,100)
#tensor([0.0000, 0.6978, 1.3956, …4.1867, 4.8844, 5.5822, 6.2800]) #[0 0.697777808 1.39555562 … 4.88444471 5.58222246 6.28]
d = torch.zeros((3,3)) d = tf.zeros([3,3]) tf.print(d)
#tensor([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]]) #[[0 0 0] [0 0 0] [0 0 0]]
a = torch.ones((3,3),dtype = torch.int) a = tf.ones([3,3])
#tensor([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype=torch.int32) #[[1 1 1] [1 1 1] [1 1 1]]
b = torch.zeros_like(a,dtype = torch.float) b = tf.zeros_like(a,dtype= tf.float32)
#tensor([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]]) #[[0 0 0] [0 0 0] [0 0 0]]
torch.fill_(b,5) b = tf.fill(b,5)
#tensor([[5., 5., 5.], [5., 5., 5.], [5., 5., 5.]]) #[5., 5., 5.], [5., 5., 5.], [5., 5., 5.]
特殊矩阵
I = torch.eye(3,3) #单位矩阵 I = tf.eye(3,3) #单位矩阵
#tensor([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]) #[[1 0 0] [0 1 0] [0 0 1]]
t = torch.diag(torch.tensor([1,2,3])) #对角矩阵 t = tf.linalg.diag([1,2,3]) #对角阵
#tensor([[1, 0, 0], [0, 2, 0], [0, 0, 3]]) #[[1 0 0] [0 2 0] [0 0 3]]

均匀随机分布

pytorch

#均匀随机分布
torch.manual_seed(0)
minval,maxval = 0,10
a = minval + (maxval-minval)*torch.rand([5])
print(a)
# tensor([4.9626, 7.6822, 0.8848, 1.3203, 3.0742])

tensorflow

#均匀分布随机
tf.random.set_seed(1.0)
a = tf.random.uniform([5],minval=0,maxval=10)
tf.print(a)
# [1.65130854 9.01481247 6.30974197 4.34546089 2.9193902]

正态分布随机

pytorch

#正态分布随机
b = torch.normal(mean = torch.zeros(3,3), std = torch.ones(3,3))
print(b)
# tensor([[-1.3836,  0.2459, -0.1312],
#         [-0.1785, -0.5959,  0.2739],
#         [ 0.5679, -0.6731, -1.2095]])
#正态分布随机
mean,std = 2,5
c = std*torch.randn((3,3))+mean
print(c)
# tensor([[  8.7204,  13.9161,  -0.8323],
#         [ -3.7681, -10.5115,   6.3778],
#         [-11.3628,   1.8433,   4.4939]])

tensorflow

#正态分布随机
b = tf.random.normal([3,3],mean=0.0,stddev=1.0)
tf.print(b)
# [[0.403087884 -1.0880208 -0.0630953535]
#  [1.33655667 0.711760104 -0.489286453]
#  [-0.764221311 -1.03724861 -1.25193381]]
#正态分布随机,剔除2倍方差以外数据重新生成
c = tf.random.truncated_normal((5,5), mean=0.0, stddev=1.0, dtype=tf.float32)
tf.print(c)
# [[-0.457012236 -0.406867266 0.728577733 -0.892977774 -0.369404584]
#  [0.323488563 1.19383323 0.888299048 1.25985599 -1.95951891]
#  [-0.202244401 0.294496894 -0.468728036 1.29494202 1.48142183]
#  [0.0810953453 1.63843894 0.556645 0.977199793 -1.17777884]
#  [1.67368948 0.0647980496 -0.705142677 -0.281972528 0.126546144]]

整数随机排列

pytorch

#整数随机排列
d = torch.randperm(20)
print(d)
# tensor([ 5, 15, 19, 10,  7, 17,  0,  4, 12, 16, 14, 13,  1,  3,  9,  6, 18,  2,
#          8, 11])

pytorch、tensorflow对比学习—张量_第7张图片

索引切片

张量的索引切片方式和numpy几乎是一样的。切片时支持缺省参数和省略号。

pytorch、tensorflow对比学习—张量_第8张图片

**pytorch中:**可以通过索引和切片对部分元素进行修改。

此外,对于不规则的切片提取,可以使用torch.index_select, torch.masked_select, torch.take

如果要通过修改张量的某些元素得到新的张量,可以使用torch.where,torch.masked_fill,torch.index_fill。

**tensorflow中:**对于tf.Variable,可以通过索引和切片对部分元素进行修改。

对于提取张量的连续子区域,也可以使用tf.slice.

此外,对于不规则的切片提取,可以使用tf.gather,tf.gather_nd,tf.boolean_mask。

tf.boolean_mask功能最为强大,它可以实现tf.gather,tf.gather_nd的功能,并且tf.boolean_mask还可以实现布尔索引。

如果要通过修改张量的某些元素得到新的张量,可以使用tf.where,tf.scatter_nd。


切片查询

现有如下张量 t :

t = tensor([[4, 7, 0, 1, 3],
        [6, 4, 8, 4, 6],
        [3, 4, 0, 1, 2],
        [5, 6, 8, 1, 2],
        [6, 9, 3, 8, 4]], dtype=torch.int32)
pytorch tensorflow
第0行
print(t[0]) tf.print(t[0])
倒数第一行
print(t[-1]) tf.print(t[-1])
第1行第3列
print(t[1,3]) tf.print(t[1,3])
print(t[1][3])) tf.print(t[1][3]))
第1行至第3行
print(t[1:4,:]) tf.print(t[1:4,:])
tf.print(tf.slice(t,[1,0],[3,5]))
第1行至最后一行,第0列到最后一列每隔两列取一列
print(t[1:4,:4:2]) tf.print(t[1:4,:4:2])

可以使用索引和切片修改部分元素

pytorch

#可以使用索引和切片修改部分元素
x = torch.tensor([[1,2],[3,4]],dtype = torch.float32,requires_grad=True)
x.data[1,:] = torch.tensor([0.0,0.0])

# tensor([[1., 2.],
#         [0., 0.]], requires_grad=True)

tensorflow

需要张量是变量Variable格式

#对变量来说,还可以使用索引和切片修改部分元素
x = tf.Variable([[1,2],[3,4]],dtype = tf.float32)
x[1,:].assign(tf.constant([0.0,0.0]))
tf.print(x)

# [[1 2]
#  [0 0]]

省略号可以表示多个冒号

t = tensor([[[ 0,  1,  2],
         [ 3,  4,  5],
         [ 6,  7,  8]],

        [[ 9, 10, 11],
         [12, 13, 14],
         [15, 16, 17]],

        [[18, 19, 20],
         [21, 22, 23],
         [24, 25, 26]]])

pytorch

#省略号可以表示多个冒号
print(a[...,1])
# tensor([[ 1,  4,  7],
#         [10, 13, 16],
#         [19, 22, 25]])

tensorflow

#省略号可以表示多个冒号
tf.print(a[...,1])
#        [[ 1,  4,  7],
#         [10, 13, 16],
#         [19, 22, 25]]

以上切片方式相对规则,下面是对于不规则的切片提取

在pytorch中:可以使用torch.index_select, torch.take, torch.gather, torch.masked_select.

在tensorflow中:可以使用tf.gather,tf.gather_nd,tf.boolean_mask。

现在有一个张量 t ,它表示班级成绩册,有4个班级,每个班级10个学生,每个学生7门科目成绩。可以用一个4×10×7的张量来表示。

t = tensor([[[55, 95,  3, 18, 37, 30, 93],
         [17, 26, 15,  3, 20, 92, 72],
         [74, 52, 24, 58,  3, 13, 24],
         [81, 79, 27, 48, 81, 99, 69],
         [56, 83, 20, 59, 11, 15, 24],
         [72, 70, 20, 65, 77, 43, 51],
         [61, 81, 98, 11, 31, 69, 91],
         [93, 94, 59,  6, 54, 18,  3],
         [94, 88,  0, 59, 41, 41, 27],
         [69, 20, 68, 75, 85, 68,  0]],

        [[17, 74, 60, 10, 21, 97, 83],
         [28, 37,  2, 49, 12, 11, 47],
         [57, 29, 79, 19, 95, 84,  7],
         [37, 52, 57, 61, 69, 52, 25],
         [73,  2, 20, 37, 25, 32,  9],
         [39, 60, 17, 47, 85, 44, 51],
         [45, 60, 81, 97, 81, 97, 46],
         [ 5, 26, 84, 49, 25, 11,  3],
         [ 7, 39, 77, 77,  1, 81, 10],
         [39, 29, 40, 40,  5,  6, 42]],

        [[50, 27, 68,  4, 46, 93, 29],
         [95, 68,  4, 81, 44, 27, 89],
         [ 9, 55, 39, 85, 63, 74, 67],
         [37, 39,  8, 77, 89, 84, 14],
         [52, 14, 22, 20, 67, 20, 48],
         [52, 82, 12, 15, 20, 84, 32],
         [92, 68, 56, 49, 40, 56, 38],
         [49, 56, 10, 23, 90,  9, 46],
         [99, 68, 51,  6, 74, 14, 35],
         [33, 42, 50, 91, 56, 94, 80]],

        [[18, 72, 14, 28, 64, 66, 87],
         [33, 50, 75,  1, 86,  8, 50],
         [41, 23, 56, 91, 35, 20, 31],
         [ 0, 72, 25, 16, 21, 78, 76],
         [88, 68, 33, 36, 64, 91, 63],
         [26, 26,  2, 60, 21,  5, 93],
         [17, 44, 64, 51, 16,  9, 89],
         [58, 91, 33, 64, 38, 47, 19],
         [66, 65, 48, 38, 19, 84, 12],
         [70, 33, 25, 58, 24, 61, 59]]], dtype=torch.int32)

抽取每个班级第0个学生,第5个学生,第9个学生的全部成绩

pytorch

#抽取每个班级第0个学生,第5个学生,第9个学生的全部成绩
torch.index_select(scores,dim = 1,index = torch.tensor([0,5,9]))

tensorflow

#抽取每个班级第0个学生,第5个学生,第9个学生的全部成绩
p = tf.gather(scores,[0,5,9],axis=1)

抽取每个班级第0个学生,第5个学生,第9个学生的第1门课程,第3门课程,第6门课程成绩

pytorch

q = torch.index_select(torch.index_select(scores,dim = 1,index = torch.tensor([0,5,9]))
                   ,dim=2,index = torch.tensor([1,3,6]))

tensorflow

#抽取每个班级第0个学生,第5个学生,第9个学生的第1门课程,第3门课程,第6门课程成绩
q = tf.gather(tf.gather(scores,[0,5,9],axis=1),[1,3,6],axis=2)

抽取第0个班级第0个学生,第2个班级的第4个学生,第3个班级的第6个学生的全部成绩

pytorch

#抽取第0个班级第0个学生的第0门课程,第2个班级的第4个学生的第1门课程,第3个班级的第9个学生第6门课程成绩
#take将输入看成一维数组,输出和index同形状
s = torch.take(scores,torch.tensor([0*10*7+0,2*10*7+4*7+1,3*10*7+9*7+6]))

tensorflow

以上tf.gather和tf.gather_nd的功能也可以用tf.boolean_mask来实现。

# 抽取第0个班级第0个学生,第2个班级的第4个学生,第3个班级的第6个学生的全部成绩
#indices的长度为采样样本的个数,每个元素为采样位置的坐标
s = tf.gather_nd(scores,indices = [(0,0),(2,4),(3,6)])
#抽取第0个班级第0个学生,第2个班级的第4个学生,第3个班级的第6个学生的全部成绩
s = tf.boolean_mask(scores,
    [[True,False,False,False,False,False,False,False,False,False],
     [False,False,False,False,False,False,False,False,False,False],
     [False,False,False,False,True,False,False,False,False,False],
     [False,False,False,False,False,False,True,False,False,False]])

其他:

pytorch

以上这些方法仅能提取张量的部分元素值,但不能更改张量的部分元素值得到新的张量。

如果要通过修改张量的部分元素值得到新的张量,可以使用torch.where,torch.index_fill 和 torch.masked_fill

torch.where可以理解为if的张量版本。

torch.index_fill的选取元素逻辑和torch.index_select相同。

torch.masked_fill的选取元素逻辑和torch.masked_select相同。

#抽取分数大于等于80分的分数(布尔索引)
#结果是1维张量
g = torch.masked_select(scores,scores>=80)
#如果分数大于60分,赋值成1,否则赋值成0
ifpass = torch.where(scores>60,torch.tensor(1),torch.tensor(0))
#将每个班级第0个学生,第5个学生,第9个学生的全部成绩赋值成满分
torch.index_fill(scores,dim = 1,index = torch.tensor([0,5,9]),value = 100)
#等价于 scores.index_fill(dim = 1,index = torch.tensor([0,5,9]),value = 100)
#将分数小于60分的分数赋值成60分
b = torch.masked_fill(scores,scores<60,60)
#等价于b = scores.masked_fill(scores<60,60)

tensorflow

以上这些方法仅能提取张量的部分元素值,但不能更改张量的部分元素值得到新的张量。

如果要通过修改张量的部分元素值得到新的张量,可以使用tf.where和tf.scatter_nd。

tf.where可以理解为if的张量版本,此外它还可以用于找到满足条件的所有元素的位置坐标。

tf.scatter_nd的作用和tf.gather_nd有些相反,tf.gather_nd用于收集张量的给定位置的元素,

而tf.scatter_nd可以将某些值插入到一个给定shape的全0的张量的指定位置处。

#找到张量中小于0的元素,将其换成np.nan得到新的张量
#tf.where和np.where作用类似,可以理解为if的张量版本

c = tf.constant([[-1,1,-1],[2,2,-2],[3,-3,3]],dtype=tf.float32)
d = tf.where(c<0,tf.fill(c.shape,np.nan),c) 
#找到张量中小于0的元素,将其换成np.nan得到新的张量
#tf.where和np.where作用类似,可以理解为if的张量版本

c = tf.constant([[-1,1,-1],[2,2,-2],[3,-3,3]],dtype=tf.float32)
d = tf.where(c<0,tf.fill(c.shape,np.nan),c) 
#将张量的第[0,0]和[2,1]两个位置元素替换为0得到新的张量
d = c - tf.scatter_nd([[0,0],[2,1]],[c[0,0],c[2,1]],c.shape)
#scatter_nd的作用和gather_nd有些相反
#可以将某些值插入到一个给定shape的全0的张量的指定位置处。
indices = tf.where(c<0)
tf.scatter_nd(indices,tf.gather_nd(c,indices),c.shape)

通过以上例子可以pytorch和tensorflow作用的接口对比:

相同行表示作用一样

pytorch tensorflow
torch.index_select tf.gather
torch.take
tf.gather_nd
tf.masked_select tf.boolean_mask
torch.where tf.where
torch.index_fill tf.scatter_nd

维度变换

pythorch中:维度变换相关函数主要有 torch.reshape(或者调用张量的view方法), torch.squeeze, torch.unsqueeze, torch.transpose

tensorflow中:维度变换相关函数主要有 tf.reshape, tf.squeeze, tf.expand_dims, tf.transpose.

作用 pythorch tensorflow
可以改变张量的形状 torch.reshape/view tf.reshape
可以减少维度 torch.squeeze tf.squeeze
可以增加维度 torch.unsqueeze tf.expand_dims
可以交换维度 torch.transpose tf.transpose

pytorch、tensorflow对比学习—张量_第9张图片

改变张量的形状

pythorch

# 构建多维张量a:
torch.manual_seed(0)
minval,maxval = 0,255
a = (minval + (maxval-minval)*torch.rand([1,3,3,2])).int()
print(a.shape)
print(a)
# torch.Size([1, 3, 3, 2])
# tensor([[[[126, 195],
#           [ 22,  33],
#           [ 78, 161]],
# 
#          [[124, 228],
#           [116, 161],
#           [ 88, 102]],
# 
#          [[  5,  43],
#           [ 74, 132],
#           [177, 204]]]], dtype=torch.int32)
# 张量的view方法有时候会调用失败,可以使用reshape方法。
# 改成 (3,6)形状的张量
b = a.view([3,6]) #torch.reshape(a,[3,6])
print(b.shape)
print(b)
# torch.Size([3, 6])
# tensor([[126, 195,  22,  33,  78, 161],
#         [124, 228, 116, 161,  88, 102],
#         [  5,  43,  74, 132, 177, 204]], dtype=torch.int32)
# 改回成 [1,3,3,2] 形状的张量
c = torch.reshape(b,[1,3,3,2]) # b.view([1,3,3,2]) 
print(c)
# tensor([[[[126, 195],
#           [ 22,  33],
#           [ 78, 161]],
# 
#          [[124, 228],
#           [116, 161],
#           [ 88, 102]],
# 
#          [[  5,  43],
#           [ 74, 132],
#           [177, 204]]]], dtype=torch.int32)

tensorflow

# 改成 (3,6)形状的张量
b = tf.reshape(a,[3,6])
tf.print(b.shape)

# TensorShape([3, 6])

# 改回成 [1,3,3,2] 形状的张量
c = tf.reshape(b,[1,3,3,2])

减少维度、增加维度

pytorch

a = torch.tensor([[1.0,2.0]])
s = torch.squeeze(a)
print(a)
print(s)
print(a.shape)
print(s.shape)
# tensor([[1., 2.]])
# tensor([1., 2.])
# torch.Size([1, 2])
# torch.Size([2])
#在第0维插入长度为1的一个维度

d = torch.unsqueeze(s,axis=0)  
print(s)
print(d)
print(s.shape)
print(d.shape)
# tensor([1., 2.])
# tensor([[1., 2.]])
# torch.Size([2])
# torch.Size([1, 2])

tensorflow

s = tf.squeeze(a)
tf.print(s.shape)
# TensorShape([3, 3, 2])
d = tf.expand_dims(s,axis=0) #在第0维插入长度为1的一个维度
# 

交换维度

pytroch

torch.transpose可以交换张量的维度,torch.transpose常用于图片存储格式的变换上。

如果是二维的矩阵,通常会调用矩阵的转置方法 matrix.t(),等价于 torch.transpose(matrix,0,1)。

minval=0
maxval=255
# Batch,Height,Width,Channel
data = torch.floor(minval + (maxval-minval)*torch.rand([100,256,256,4])).int()
print(data.shape)

# 转换成 Pytorch默认的图片格式 Batch,Channel,Height,Width 
# 需要交换两次
# data_t = torch.transpose(torch.transpose(data,1,2),1,3)
print(data_t.shape)
# torch.Size([100, 256, 256, 4])
# torch.Size([100, 4, 256, 256])
matrix = torch.tensor([[1,2,3],[4,5,6]])
print(matrix)
print(matrix.t()) #等价于torch.transpose(matrix,0,1)
# tensor([[1, 2, 3],
#         [4, 5, 6]])
# tensor([[1, 4],
#         [2, 5],
#         [3, 6]])

tensorflow

tf.transpose可以交换张量的维度,与tf.reshape不同,它会改变张量元素的存储顺序。

tf.transpose常用于图片存储格式的变换上。

# Batch,Height,Width,Channel
a = tf.random.uniform(shape=[100,600,600,4],minval=0,maxval=255,dtype=tf.int32)
tf.print(a.shape)

# 转换成 Channel,Height,Width,Batch
s= tf.transpose(a,perm=[3,1,2,0])
tf.print(s.shape)
# TensorShape([100, 600, 600, 4])
# TensorShape([4, 600, 600, 100])

合并分割

和numpy类似,可以用concat和stack方法对多个张量进行合并,可以用tf.split方法把一个张量分割成多个张量。

作用 pytorch tensorflow
合并 torch.cat tf.concat
堆叠 torch.stack tf.stack
分割 torch.split tf.split

pytorch、tensorflow对比学习—张量_第10张图片

合并和堆叠

pytorch

a = torch.tensor([[1.0,2.0],[3.0,4.0]])
b = torch.tensor([[5.0,6.0],[7.0,8.0]])
c = torch.tensor([[9.0,10.0],[11.0,12.0]])

abc_cat = torch.cat([a,b,c],dim = 0)
print(abc_cat.shape)
print(abc_cat)
# torch.Size([6, 2])
# tensor([[ 1.,  2.],
#         [ 3.,  4.],
#         [ 5.,  6.],
#         [ 7.,  8.],
#         [ 9., 10.],
#         [11., 12.]])

torch.cat([a,b,c],axis = 1)
# tensor([[ 1.,  2.,  5.,  6.,  9., 10.],
#         [ 3.,  4.,  7.,  8., 11., 12.]])
abc_stack = torch.stack([a,b,c],axis = 0) #torch中dim和axis参数名可以混用
print(abc_stack.shape)
print(abc_stack)
# torch.Size([3, 2, 2])
# tensor([[[ 1.,  2.],
#          [ 3.,  4.]],
# 
#         [[ 5.,  6.],
#          [ 7.,  8.]],
# 
#         [[ 9., 10.],
#          [11., 12.]]])

torch.stack([a,b,c],axis = 1)
# tensor([[[ 1.,  2.],
#          [ 5.,  6.],
#          [ 9., 10.]],
# 
#         [[ 3.,  4.],
#          [ 7.,  8.],
#          [11., 12.]]])

tensorflow

a = tf.constant([[1.0,2.0],[3.0,4.0]])
b = tf.constant([[5.0,6.0],[7.0,8.0]])
c = tf.constant([[9.0,10.0],[11.0,12.0]])

tf.concat([a,b,c],axis = 0)
# 
# array([[ 1.,  2.],
#        [ 3.,  4.],
#        [ 5.,  6.],
#        [ 7.,  8.],
#        [ 9., 10.],
#        [11., 12.]], dtype=float32)>

tf.concat([a,b,c],axis = 1)
# 
# array([[ 1.,  2.,  5.,  6.,  9., 10.],
#        [ 3.,  4.,  7.,  8., 11., 12.]], dtype=float32)>
tf.stack([a,b,c]) # 默认axis=0
# 
# array([[[ 1.,  2.],
#         [ 3.,  4.]],
# 
#        [[ 5.,  6.],
#         [ 7.,  8.]],
# 
#        [[ 9., 10.],
#         [11., 12.]]], dtype=float32)>

tf.stack([a,b,c],axis=1)
# 
# array([[[ 1.,  2.],
#         [ 5.,  6.],
#         [ 9., 10.]],
# 
#        [[ 3.,  4.],
#         [ 7.,  8.],
#         [11., 12.]]], dtype=float32)>

分割split

split是torch.cat的逆运算,可以指定分割份数平均分割,也可以通过指定每份的记录数量进行分割。

pytroch

print(abc_cat)
a,b,c = torch.split(abc_cat,split_size_or_sections = 2,dim = 0) #每份2个进行分割
print(a)
print(b)
print(c)
print(abc_cat)
p,q,r = torch.split(abc_cat,split_size_or_sections =[4,1,1],dim = 0) #每份分别为[4,1,1]
print(p)
print(q)
print(r)
# tensor([[ 1.,  2.],
#         [ 3.,  4.],
#         [ 5.,  6.],
#         [ 7.,  8.],
#         [ 9., 10.],
#         [11., 12.]])
# tensor([[1., 2.],
#         [3., 4.],
#         [5., 6.],
#         [7., 8.]])
# tensor([[ 9., 10.]])
# tensor([[11., 12.]])

tensorflow

#tf.split(value,num_or_size_splits,axis)
tf.split(c,3,axis = 0)  #指定分割份数,平均分割
# [
#  array([[1., 2.],
#         [3., 4.]], dtype=float32)>,
#  
#  array([[5., 6.],
#         [7., 8.]], dtype=float32)>,
#  
#  array([[ 9., 10.],
#         [11., 12.]], dtype=float32)>]
tf.split(c,[2,2,2],axis = 0) #指定每份的记录数量
# [
#  array([[1., 2.],
#         [3., 4.]], dtype=float32)>,
#  
#  array([[5., 6.],
#         [7., 8.]], dtype=float32)>,
#  
#  array([[ 9., 10.],
#         [11., 12.]], dtype=float32)>]

张量的数学运算

张量数学运算主要有:标量运算,向量运算,矩阵运算。另外我们会介绍张量运算的广播机制。

pytorch、tensorflow对比学习—张量_第11张图片

标量运算

张量的数学运算符可以分为标量运算符、向量运算符、以及矩阵运算符。

加减乘除乘方,以及三角函数,指数,对数等常见函数,逻辑比较运算符等都是标量运算符。

标量运算符的特点是对张量实施逐元素运算。

有些标量运算符对常用的数学运算符进行了重载。并且支持类似numpy的广播特性。

对于tensorflow来说,许多标量运算符都在tf.math模块下。

import tensorflow as tf 
import numpy as np 
import torch 
运算 pyhtorch tensorflow
+ +
* *
/ /
幂运算 ** **
开方 **(0.5); torch.sqrt **(0.5) ; tf.sqrt
取模 % ;torch.fmod % ; tf.math.mod
地板除法 // //
保留整数部分,四舍五入 torch.round(x) tf.math.round(x)
保留整数部分,向下归整 torch.floor(x) tf.math.floor(x)
保留整数部分,向上归整 torch.ceil(x) tf.math.ceil(x)
保留整数部分,向0归整 torch.trunc(x)
大于,小于,等于 >;<;==;torch.ge;lo;eq > ; < ; ==
或,且,非 & ;| ; ! & ;| ; !
作除法取剩余的部分,结果恒正 torch.remainder
取大值 torch.max(a,b) tf.maximum(a,b)
取小值 torch.min(a,b) tf.minimum(a,b)
多张量求和 a+b+c tf.add_n([a,b,c]
幅值裁剪 torch.clamp(x,min=-1,max = 1) tf.clip_by_value(x,clip_value_min=-1,clip_value_max=1)
z = torch.clamp(x,max = 1) z = tf.clip_by_norm(x,clip_norm = 3)

部分代码示例:

pytorch

# 多涨量求和
a = torch.tensor([1.0,8.0])
b = torch.tensor([5.0,6.0])
c = torch.tensor([6.0,7.0])

d = a+b+c
print(d)
# tensor([12., 21.])
# 幅值裁剪
x = torch.tensor([0.9,-0.8,100.0,-20.0,0.7])
y = torch.clamp(x,min=-1,max = 1)
z = torch.clamp(x,max = 1)
print(y)
print(z)
tensor([ 0.9000, -0.8000,  1.0000, -1.0000,  0.7000])
tensor([  0.9000,  -0.8000,   1.0000, -20.0000,   0.7000])

tensorflow

a = tf.constant([1.0,8.0])
b = tf.constant([5.0,6.0])
c = tf.constant([6.0,7.0])
tf.add_n([a,b,c])
# 
# 幅值裁剪
x = tf.constant([0.9,-0.8,100.0,-20.0,0.7])
y = tf.clip_by_value(x,clip_value_min=-1,clip_value_max=1)
z = tf.clip_by_norm(x,clip_norm = 3)
tf.print(y)
tf.print(z)
[0.9 -0.8 1 -1 0.7]
[0.0264732055 -0.0235317405 2.94146752 -0.588293493 0.0205902718]

pytorch、tensorflow对比学习—张量_第12张图片

向量运算

向量运算符只在一个特定轴上运算,将一个向量映射到一个标量或者另外一个向量。

tensorflow中: 许多向量运算符都以reduce开头。

运算 pytorch tensorflow
向量求和 torch.sum(a) tf.reduce_sum(a)
平均 torch.mean(a) tf.reduce_mean(a)
最大值 torch.max(a) tf.reduce_max(a)
最小值 torch.min(a) tf.reduce_min(a)
累乘 torch.prod(a) tf.reduce_prod(a)
标准差 torch.std(a)
方差 torch.var(a)
中位数 torch.median(a)
cum扫描累加 torch.cumsum(a,0) tf.math.cumsum(a)
cum扫描累乘 torch.cumprod(a,0) tf.math.cumprod(a)
cum扫描最大值 torch.cummax(a,0).values/index
cum扫描最小值 torch.cummin(a,0).values/index
取最大值索引 tf.argmax(a)
取最小值索引 tf.argmin(a)
向量排序 torch.topk(a,2,dim = 0) tf.math.top_k(a,3,sorted=True)
torch.sort(a,dim = 1)

部分代码示例:

pytroch

a = torch.arange(1,10).float()
#指定维度计算统计值
b = a.view(3,3)
print(b)
print(torch.max(b,dim = 0))
print(torch.max(b,dim = 1))

# tensor([[1., 2., 3.],
#         [4., 5., 6.],
#         [7., 8., 9.]])
# torch.return_types.max(
# values=tensor([7., 8., 9.]),
# indices=tensor([2, 2, 2]))
# torch.return_types.max(
# values=tensor([3., 6., 9.]),
# indices=tensor([2, 2, 2]))
#cum扫描
a = torch.arange(1,10)

print(torch.cumsum(a,0))
print(torch.cumprod(a,0))
print(torch.cummax(a,0).values)
print(torch.cummax(a,0).indices)
print(torch.cummin(a,0))
# tensor([ 1,  3,  6, 10, 15, 21, 28, 36, 45])
# tensor([     1,      2,      6,     24,    120,    720,   5040,  40320, 362880])
# tensor([1, 2, 3, 4, 5, 6, 7, 8, 9])
# tensor([0, 1, 2, 3, 4, 5, 6, 7, 8])
# torch.return_types.cummin(
# values=tensor([1, 1, 1, 1, 1, 1, 1, 1, 1]),
# indices=tensor([0, 0, 0, 0, 0, 0, 0, 0, 0]))
#torch.sort和torch.topk可以对张量排序
a = torch.tensor([[9,7,8],[1,3,2],[5,6,4]]).float()
print(torch.topk(a,2,dim = 0),"\n")
print(torch.topk(a,2,dim = 1),"\n")
print(torch.sort(a,dim = 1),"\n")

# torch.return_types.topk(
# values=tensor([[9., 7., 8.],
#         [5., 6., 4.]]),
# indices=tensor([[0, 0, 0],
#         [2, 2, 2]]))
# 
# torch.return_types.topk(
# values=tensor([[9., 8.],
#         [3., 2.],
#         [6., 5.]]),
# indices=tensor([[0, 2],
#         [1, 2],
#         [1, 0]]))
# 
# torch.return_types.sort(
# values=tensor([[7., 8., 9.],
#         [1., 2., 3.],
#         [4., 5., 6.]]),
# indices=tensor([[1, 2, 0],
#         [0, 2, 1],
#         [2, 0, 1]]))

tensorflow

a = tf.range(1,10)
#张量指定维度计算统计值
b = tf.reshape(a,(3,3))
tf.print(tf.reduce_sum(b, axis=1, keepdims=True))
tf.print(tf.reduce_sum(b, axis=0, keepdims=True))
# [[6]
#  [15]
#  [24]]
# [[12 15 18]]

#利用tf.foldr实现tf.reduce_sum
s = tf.foldr(lambda a,b:a+b,tf.range(10)) 
tf.print(s)
# 45
#bool类型的reduce
p = tf.constant([True,False,False])
q = tf.constant([False,False,True])
tf.print(tf.reduce_all(p))
tf.print(tf.reduce_any(q))
# 0
# 1
#cum扫描累积
a = tf.range(1,10)
tf.print(tf.math.cumsum(a))
tf.print(tf.math.cumprod(a))
# [1 3 6 ... 28 36 45]
# [1 2 6 ... 5040 40320 362880]
#arg最大最小值索引
a = tf.range(1,10)
tf.print(tf.argmax(a))
tf.print(tf.argmin(a))
# 8
# 0
#tf.math.top_k可以用于对张量排序
a = tf.constant([1,3,7,5,4,8])

values,indices = tf.math.top_k(a,3,sorted=True)
tf.print(values)
tf.print(indices)

# [8 7 5]
# [5 2 3]

矩阵运算

pytorch、tensorflow对比学习—张量_第13张图片

矩阵必须是二维的。类似[1,2,3]这样的不是矩阵。

矩阵运算包括:矩阵乘法,矩阵转置,矩阵逆,矩阵求迹,矩阵范数,矩阵行列式,矩阵求特征值,矩阵分解等运算。

在tensorflow中:除了一些常用的运算外,大部分和矩阵有关的运算都在tf.linalg(线性代数)子包中。

运算 pytorch tensorflow
矩阵乘法 a@b; torch.matmul(a,b);torch.mm(a,b) a@b;tf.matmul(a,b)
矩阵转置 a.t() tf.transpose(a)
矩阵逆 torch.inverse(a) tf.linalg.inv(a)
矩阵求迹(trace) torch.trace(a) tf.linalg.trace(a)
矩阵求范数 torch.norm(a) tf.linalg.norm(a)
矩阵行列式 torch.det(a) tf.linalg.det(a)
矩阵特征值 torch.eig(a,eigenvectors=True) tf.linalg.eigvals(a)
矩阵QR分解(正交三角分解) torch.qr(a) tf.linalg.qr(a)
矩阵svd分解(奇异值分解) u,s,v = torch.svd(a) s,u,v = tf.linalg.svd(a)

广播机制

pytorch、tensorflow对比学习—张量_第14张图片

pytorch 和 TensorFlow的广播规则和numpy是一样的:

  • 1、如果张量的维度不同,将维度较小的张量进行扩展,直到两个张量的维度都一样。
  • 2、如果两个张量在某个维度上的长度是相同的,或者其中一个张量在该维度上的长度为1,那么我们就说这两个张量在该维度上是相容的。
  • 3、如果两个张量在所有维度上都是相容的,它们就能使用广播。
  • 4、广播之后,每个维度的长度将取两个张量在该维度长度的较大值。
  • 5、在任何一个维度上,如果一个张量的长度为1,另一个张量长度大于1,那么在该维度上,就好像是对第一个张量进行了复制。

pytorch中:torch.broadcast_tensors可以将多个张量根据广播规则转换成相同的维度。

tensorflow中:tf.broadcast_to 以显式的方式按照广播机制扩展张量的维度。

代码示例:

pytorch

a = torch.tensor([1,2,3])
b = torch.tensor([[0,0,0],[1,1,1],[2,2,2]])
print(b + a) 
# tensor([[1, 2, 3],
#         [2, 3, 4],
#         [3, 4, 5]])
a_broad,b_broad = torch.broadcast_tensors(a,b)
print(a_broad,"\n")
print(b_broad,"\n")
print(a_broad + b_broad) 
# tensor([[1, 2, 3],
#         [1, 2, 3],
#         [1, 2, 3]]) 
# 
# tensor([[0, 0, 0],
#         [1, 1, 1],
#         [2, 2, 2]]) 
# 
# tensor([[1, 2, 3],
#         [2, 3, 4],
#         [3, 4, 5]])

tensorflow

a = tf.constant([1,2,3])
b = tf.constant([[0,0,0],[1,1,1],[2,2,2]])
b + a  #等价于 b + tf.broadcast_to(a,b.shape)
<tf.Tensor: shape=(3, 3), dtype=int32, numpy=
# array([[1, 2, 3],
#        [2, 3, 4],
#        [3, 4, 5]], dtype=int32)>
tf.broadcast_to(a,b.shape)
# 
# array([[1, 2, 3],
#        [1, 2, 3],
#        [1, 2, 3]], dtype=int32)>
#计算广播后计算结果的形状,静态形状,TensorShape类型参数
tf.broadcast_static_shape(a.shape,b.shape)
# TensorShape([3, 3])

#计算广播后计算结果的形状,动态形状,Tensor类型参数
c = tf.constant([1,2,3])
d = tf.constant([[1],[2],[3]])
tf.broadcast_dynamic_shape(tf.shape(c),tf.shape(d))
# 

说明

笔记中很多代码案例来自于:

《20天吃掉那只Pytorch》

  • github项目地址: https://github.com/lyhue1991/eat_pytorch_in_20_days

《30天吃掉那只TensorFlow2》

  • github项目地址: https://github.com/lyhue1991/eat_tensorflow2_in_30_days

感兴趣的同学可以进入学习。

===========================================================================

我的笔记一部分是将这两项目中内容整理归纳,一部分是相应功能的内容自己找资料整理归纳。

笔记以MD格式存入我的git仓库,另外代码案例所需要数据集文件也在其中:可以clone下来学习使用。

《pytorch-tensorflow对比学习笔记》

github项目地址: https://github.com/Boris-2021/pytorch-tensorflow-Comparative-study

===========================================================================

笔记中增加了很多趣味性的图片,增加阅读乐趣。

===========================================================================

感觉对你的学习有帮助,就点个星,点个赞,点个关注再走把,整理不易,拒绝白嫖从我做起!
pytorch、tensorflow对比学习—张量_第15张图片

你可能感兴趣的:(pytroch,tensorflow对比学习,tensorflow,pytorch,学习,人工智能)