其是一个面向于深度学习算法的科学计算库,内部数据保存在张量(Tensor)对象上,所有操作也都是基于张量对象进行。
数值类型的张量是TensorFlow的主要数据载体,分为:
标量(Scalar) 单个的实数,维度数(Dimension,也叫秩)为0,shape=[]
向量(Vector) n个实数的有序集合,通过中括号包裹,如[1,2],维度数位1,长度不定,shape=[n]
矩阵(Matrix) n行m列实数的有序集合,如[[1,2],[3,4]],维度数为2,每个维度上的长度不定,shape=[n,m]
**张量(Tensor) ** 所有维度数dim > 2的数组统称为张量,张量的每个维度也做轴(Axis), 比如Shape =[2,32,32,3]的张量共有 4 维
在 TensorFlow 中间,为了表达方便,一般把标量、向量、矩阵也统称为张量,
In :
aa = tf.constant(1.2) # 创建标量
type(aa)
Out:
(float, tensorflow.python.framework.ops.EagerTensor, True)
In :
x = tf.constant([1,2.,3.3]) #创建向量
x,x.shape
Out:
()
#其中 id 是 TensorFlow 中内部索引对象的编号,shape 表示张量的形状,dtype 表示张量的数值精度,张量 numpy()方法可以返回 Numpy.array 类型的数据。
In :
a = tf.constant([[1,2],[3,4]]) #创建矩阵
a, a.shape
Out:
(, TensorShape([2, 2]))
TensorFlow 还支持字符串(String)类型的数据,例如在表示图 片数据时,可以先记录图片的路径,再通过预处理函数根据路径读取图片张量。
In :
a = tf.constant('Hello, Deep Learning.')
Out:
在 tf.strings 模块中,提供了常见的字符串型的工具函数,如拼接 join(),长度 length(),切 分 **split()**等等。
布尔类型的张量只需要传入 Python 语言的布尔类型数据,转换成 TensorFlow 内部布尔型即可。
In :
a = tf.constant(True)
Out:
TensorFlow 的布尔类型和 Python 语言的布尔类型并不对等,不能通用。
Bit 位越长,精度越高,同时占用的内存空间也就越大。常用的精度类型有 tf.int16, tf.int32, tf.int64, tf.float16, tf.float32, tf.float64,其中 tf.float64 即为 tf.double。
In :
tf.constant(123456789, dtype=tf.int16)
tf.constant(123456789, dtype=tf.int32)
Out:
保存精度过低时,数据 123456789 发生了溢出,得到了错误的结果,一般使用 tf.int32, tf.int64 精度。对于浮点数,高精度的张量可以表示更精准的数据。
对于大部分深度学习算法,一般使用 tf.int32, tf.float32 可满足运算精度要求,部分对 精度要求较高的算法,如强化学习,可以选择使用 tf.int64, tf.float64 精度保存张量。
访问张量的 dtype 成员属性
In :
print('before:',a.dtype)
if a.dtype != tf.float32:
a = tf.cast(a,tf.float32) # 转换精度
print('after :',a.dtype)
Out:
before:
after :
通过 tf.cast 函数进行转换
In :
a = tf.constant(np.pi, dtype=tf.float16)
tf.cast(a, tf.double)
Out[16]:
布尔型与整形之间相互转换也是合法的,是比较常见的操作
一般默认 0 表示 False,1 表示 True,在 TensorFlow 中,将非 0 数字都视为 True
为了区分需要计算梯度信息的张量与不需要计算梯度信息的张量,TensorFlow 增加了 一种专门的数据类型来支持梯度信息的记录:tf.Variable。对于不需要的优化的张量,如神经网络的输入 X, 不需要通过 tf.Variable 封装;相反,对于需要计算梯度并优化的张量,如神经网络层的W 和,需要通过 tf.Variable 包裹以便 TensorFlow 跟踪相关梯度信息。
In :
a = tf.constant([-1, 0, 1, 2])
aa = tf.Variable(a)
aa.name, aa.trainable
Out:
('Variable:0', True)
其中张量的 name 和 trainable 属性是 Variable 特有的属性,name 属性用于命名计算图中的变量,这套命名体系是 TensorFlow 内部维护的,一般不需要用户关注 name 属性;trainable 表征当前张量是否需要被优化,创建 Variable 对象是默认启用优化标志,可以设置 trainable=False 来设置张量不需要优化。
待优化张量可看做普通张量的特殊类型,普通张量也可以通过 GradientTape.watch()方法临时加入跟踪梯度信息的列表。
通过 tf.convert_to_tensor 可以创建新 Tensor,并将保存在 Python List 对象或者 Numpy Array 对象中的数据导入到新 Tensor 中:
tf.convert_to_tensor([1,2.])
tf.convert_to_tensor(np.array([[1,2.],[3,4]]))
Numpy 中浮点数数组默认使用 64-Bit 精度保存数据,转换到 Tensor 类型时 精度为 tf.float64,可以在需要的时候转换为 tf.float32 类型。
**tf.constant()**和 **tf.convert_to_tensor()**都能够自动的把 Numpy 数组或者 Python List 数据类型转化为 Tensor 类型,使用其一即可,常使用tf.constant()。
通过 tf.zeros()和 tf.ones()即可创建任意形 状全 0 或全 1 的张量
tf.zeros([]),tf.ones([]) #创建全0或全1的标量
tf.zeros([1]),tf.ones([1]) #创建全0或全1的向量
tf.zeros([2,2]) #创建全0的矩阵
通过 tf.zeros_like, tf.ones_like 可以方便地新建一个与某个张量 shape 一致,内容全 0 或全 1 的张量
In :
a = tf.ones([2,3])
tf.zeros_like(a)
Out:
tf.*_like 是一个便捷函数,可以通过 **tf.zeros(a.shape)**等方式实现。
通过 tf.fill(shape, value)可以创建全为自定义数值 value 的张量。
In :
tf.fill([2,2], 99)
Out:
**正态分布(Normal Distribution,或 Gaussian Distribution)和均匀分布(Uniform Distribution)**是最常见的分布之一,创建采样自这 2 种分布的张量非常有用,比如在卷积神经网络中,卷积核张量 W 初始化为正态分布有利于网络的训练;在对抗生成网络中,隐藏变量z一般采样自均匀分布。
通过 tf.random.normal(shape, mean=0.0, stddev=1.0)可以创建形状为 shape,均值为 mean,标准差为 stddev 的正态分布(,2)。
In :
tf.random.normal([2,2], mean=1,stddev=2)
Out:
通过 **tf.random.uniform(shape, minval=0, maxval=None, dtype=tf.float32)**可以创建采样自 [,]区间的均匀分布的张量。
In :
tf.random.uniform([2,2],maxval=10)
Out:
tf.range(limit, delta=1)可以创建[0,)之间,步长为 delta 的整形序 列,不包含 limit 本身。
通过 tf.range(start, limit, delta=1)可以创建[,),步长为 delta 的序列,不包含 limit 本身。
In :
tf.range(1,10,delta=2)
Out:
标量最容易理解,它就是一个简单的数字,维度数为 0,shape 为 []。标量的典型用途之一是误差值的表示、各种测量指标的表示,比如准确度(Accuracy, acc),精度(Precision)和召回率(Recall)。
经过 tf.keras.losses.mse(或 tf.keras.losses.MSE)返回每个样本 上的误差值,最后取误差的均值作为当前 batch 的误差,它是一个标量
In :
out = tf.random.uniform([4,10]) #随机模拟网络输出
y = tf.constant([2,3,2,0]) # 随机构造样本真实标签
y = tf.one_hot(y, depth=10) # one-hot 编码
loss = tf.keras.losses.mse(y, out) # 计算每个样本的 MSE
loss = tf.reduce_mean(loss) # 平均 MSE
print(loss)
Out:
tf.Tensor(0.19950335, shape=(), dtype=float32)
向量是一种非常常见的数据载体,如在全连接层和卷积神经网络层中,偏置张量就 使用向量来表示。
每个全连接层的输出节点都添加了一个偏置值,把所有输出节点的偏置表示成向量形式: = [1,2]。
通过高层接口类 **Dense()**方式创建的网络层,张量 W 和存储在类的内部,由类自动创 建并管理。可以通过全连接层的 bias 成员变量查看偏置变量。
创建输入节点数为 4, 输出节点数为 3 的线性层网络,那么它的偏置向量 b 的长度应为 3。
In :
fc = layers.Dense(3) # 创建一层 Wx+b,输出节点为 3
# 通过 build 函数创建 W,b 张量,输入节点为 4
fc.build(input_shape=(2,4))
fc.bias # 查看偏置
Out:
#可以看到,类的偏置成员 bias 初始化为全 0,这也是偏置的默认初始化方案。
矩阵也是非常常见的张量类型,比如全连接层的批量输入 = [,],其中表示输入 样本的个数,即 batch size,表示输入特征的长度。比如特征长度为 4,一共包含 2 个样 本的输入可以表示为矩阵:x = tf.random.normal([2,4])
In :
w = tf.ones([4,3]) # 定义 W 张量
b = tf.zeros([3]) # 定义 b 张量
o = x@w+b # X@W+b 运算
Out:
其中 X,W 张量均是矩阵。x@w+b 网络层称为线性层,在 TensorFlow 中可以通过 Dense 类直接实现,**Dense 层也称为全连接层。**我们通过 Dense 类创建输入 4 个节点,输出 3 个 节点的网络层,可以通过全连接层的 kernel 成员名查看其权值矩阵 W:
In :
fc = layers.Dense(3) # 定义全连接层的输出节点为 3 fc.build(input_shape=(2,4)) # 定义全连接层的输入节点为 4
fc.kernel
Out:
三维的张量一个典型应用是表示序列信号,它的格式是 = [, , ] 其中表示序列信号的数量,sequence len 表示序列信号在时间维度上的采样点数,feature len 表示每个点的特征长度。
为了能够方便字符串被神经网络处理,一般将单词通过嵌入层(Embedding Layer)编码为固定长度的向量,比如“a”编码为某个长度 3 的向量,那么 2 个 等长(单词数为 5)的句子序列可以表示为 shape 为[2,5,3]的 3 维张量,其中 2 表示句子个 数,5 表示单词数量,3 表示单词向量的长度:
In : # 自动加载 IMDB 电影评价数据集
(x_train,y_train),(x_test,y_test)=keras.datasets.imdb.load_data(num_words=10 000)
# 将句子填充、截断为等长 80 个单词的句子
x_train = keras.preprocessing.sequence.pad_sequences(x_train,maxlen=80)
x_train.shape
Out [46]:
(25000, 80) # 25000 表示句子个数,80 表示每个句子 共 80 个单词,每个单词使用数字编码方式
通过 layers.Embedding 层将数字编码的单词转换为长度为 100 个词向量
In [47]: # 创建词向量 Embedding 层类
embedding=layers.Embedding(10000, 100)
# 将数字编码的单词转换为词向量
out = embedding(x_train)
out.shape
Out[47]: TensorShape([25000, 80, 100]) # 100表示每个单词编码为长度 100 的向量。
对于特征长度为 1 的序列信号,比如商品价格在 60 天内的变化曲线,只需要一个标量即可表示商品的价格,因此 2 件商品的价格变化趋势可以使用 shape 为[2,60]的张量表示。为了方便统一格式,也将价格变化趋势表达为 shape 为 [2,60,1]的张量,其中的 1 表示特征长度为 1。
4维张量在卷积神经网络中应用的非常广泛,它用于保存**特征图(Feature maps)**数据,格式一般定义为 [b,h,w,c],其中表示输入的数量,h/w分布表示特征图的高宽,表示特征图的通道数。一张图片可以表示为[h,w,3]。
In :
# 创建 32x32 的彩色图片输入,个数为 4
x = tf.random.normal([4,32,32,3])
# 创建卷积神经网络
layer = layers.Conv2D(16,kernel_size=3)
out = layer(x) # 前向计算
out.shape # 输出大小
Out:
TensorShape([4, 30, 30, 16])
其中卷积核张量也是 4 维张量,可以通过 kernel 成员变量访问:
In :
layer.kernel.shape
Out:
TensorShape([3, 3, 3, 16])
在 TensorFlow 中,支持基本的[][]…标准索引方式,也支持通过逗号分隔索引号的索引方式。输入 X 为 4 张 32x32 大小的彩色图片,shape 为[4,32,32,3]。
In :
x = tf.random.normal([4,32,32,3])
x[0] #取第一张图片数据
Out:
x[0][1] #取第 1 张图片的第2行
x[0][1][2] #取第 1 张图片,第 2 行,第 3 列的像素
x[2][1][0][1] #取第 3 张图片,第 2 行,第 1 列的像素,B 通道(第 2 个通道)颜色强度值
当张量的维度数较高时,使用 [i] [j]…[]的方式书写不方便,可以采用[,,…,]的方 式索引,它们是等价的
x[1,9,2] #取第 2 张图片,第 10 行,第 3 列
#以shape为[4,32,32,3]的图片张量为例
x[1:3] #读取第2,3张图片
start:end:step切片方式有很多简写方式,其中 start、end、step 3 个参数可以根据需要 选择性地省略,全部省略时即::
,表示从最开始读取到最末尾,步长为 1,即不跳过任何元 素。如 x[0,::]
表示读取第 1 张图片的所有行,其中::
表示在行维度上读取所有行,它等于 x[0]的写法。
x[0,::]
#为了更加简洁,::可以简写为单个冒号:
x[:,0:28:2,0:28:2,:]
#表示取所有图片,隔行采样,隔列采样,所有通道信息,相当于在图片的高宽各缩放至原 来的 50%。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2dqbrVed-1583337541398)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210175710410.png)]
特别地,step 可以为负数,考虑最特殊的一种例子,step = −1时,start:end:−1表示 从 start 开始,逆序读取至 end 结束(不包含 end),索引号 ≤ 。
In :
x = tf.range(9)
x[8:0:-1]
Out:
为了避免出现像[:,:,:,1]这样出现过多冒号的情况,可以使用⋯符号表示取多个维度 上所有的数据,其中维度的数量需根据规则自动推断:当切片方式出现⋯符号时,⋯符号左边的维度将自动对齐到最左边,⋯符号右边的维度将自动对齐到最右边,此时系统再自 动推断⋯符号代表的维度数量。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dX0VApA3-1583337541399)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210180215157.png)]
x[0:2,...,1:] #读取第1-2张图片的G/B通道数据
x[2:,...] #读取最后 2 张图片
x[...,:2] #读取 R/G 通道数据
算法的每个模块对于数据张量的格式有不同的逻辑要求,当现有的数据格式不满足算 法要求时,需要通过维度变换将数据调整为正确的格式。这就是维度变换的功能。
基本的维度变换包含了改变视图 reshape,插入新维度 expand_dims,删除维度 squeeze,交换维度 transpose,复制数据 tile 等
张量的存储和视图(View)的概念:张量的视图就是我们理解张量的方式,比如 shape 为[2,4,4,3]的张量 A,我们从逻辑上可以理解 为 2 张图片,每张图片 4 行 4 列,每个位置有 RGB 3 个通道的数据;张量的存储体现在张量在内存上保存为一段连续的内存区域,对于同样的存储,我们可以有不同的理解方式, 比如上述 A,我们可以在不改变张量的存储下,将张量 A 理解为 2 个样本,每个样本的特征为长度 48 的向量。这就是存储与视图的关系。
In : x=tf.range(96)
x=tf.reshape(x,[2,4,4,3])
Out:
内存并不支持这个维度层级概念,只能以平铺方式按序写入内存。
张量 shape 中相对靠左侧的维度叫做大维度,shape 中相对靠右侧的维度叫做小维度
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LEBtfg7I-1583337541399)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210185911907.png)]
如果定义新视图为[,w ,ℎ,],[,,ℎ ∗ w] 或者[,,ℎ, w]等时,与张量的存储顺序相悖,如果不同步更新张量的存储顺序,那么恢复 出的数据将与新视图不一致,从而导致数据错乱。
新视图的维度顺 序不能与存储顺序相悖,否则需要通过交换维度操作将存储顺序同步过来。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ySjatZB0-1583337541400)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210190204071.png)]
通过张量的 ndim 和 shape 成员属性获得张量的维度数和形状
In : x.ndim,x.shape
Out:(4, TensorShape([2, 4, 4, 3]))
In :
tf.reshape(x,[2,-1])
Out:
#其中的参数-1 表示当前轴上长度需要根据视图总元素不变的法则自动推导,从而方便用户书写。比如,上面的-1可以推导为48
增加维度 增加一个长度为1的维度相当于给原有的数据增加一个新维度的概念,维度长度为1,故数据并不需要改变,仅仅是改变数据的理解方式
通过 tf.expand_dims(x, axis)可在指定的 axis 轴前可以插入一个新的维度
In :
x = tf.random.uniform([28,28],maxval=10,dtype=tf.int32)
x = tf.expand_dims(x,axis=2)
Out:
tf.expand_dims 的 axis 为正时,表示在当前维度之前插入一个新维度;为负时,表示当前维度之后插入一个新的维度。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W3biXqB1-1583337541400)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210191408876.png)]
删除维度 是增加维度的逆操作,与增加维度一样,删除维度只能删除长度为 1 的维 度,也不会改变张量的存储。
可以通过 **tf.squeeze(x, axis)**函数,axis 参数为待删除的维度的索引号, 图片数量的维度轴 axis=0
In :
x = tf.squeeze(x, axis=2)
Out:
如果不指定维度参数 axis,即 tf.squeeze(x),那么他会默认删除所有长度为 1 的维度
有时需要直接调整的存储顺序,即交换维度(Transpose)。通过交换维度,改变了张量的存储顺序,同时也改变了张量的视图。
使用 tf.transpose(x, perm)函数完成维度交换操作,其中 perm 表示新维度的顺序 List。
图片张量 shape 为[2,32,32,3], 图片数量、行、列、通道数 的维度索引分别为 0,1,2,3,如果需 要交换为[,,ℎ,w ]格式,则新维度的排序为 图片数量、通道数、行、列 ,对应的索引号为 [0,3,1,2]
In : x = tf.random.normal([2,32,32,3])
tf.transpose(x,perm=[0,3,1,2])
Out:
通过 tf.transpose 完成维度交换后,张量的存储顺序已经改变,视图也随之 改变,后续的所有操作必须基于新的存续顺序进行。
以通过 **tf.tile(x, multiples)**函数完成数据在指定维度上的复制操作,multiples 分别指定了每个维度上 面的复制倍数,对应位置为 1 表明不复制,为 2 表明新长度为原来的长度的 2 倍,即数据 复制一份。
In :
b = tf.constant([1,2])
b = tf.expand_dims(b, axis=0)
b = tf.tile(b, multiples=[2,1]) #在 batch 维度上复制数据 1 份
Out:
#输入一个2行2列的矩阵
In :
x = tf.range(4)
x=tf.reshape(x,[2,2])
x = tf.tile(x,multiples=[1,2]) #在列维度复制1份数据
x = tf.tile(x,multiples=[2,1]) #在行维度复制1份数据
Out:
tf.tile 会创建一个新的张量来保存复制后的张量,由于复制操作涉及到 大量数据的读写 IO 运算,计算代价相对较高。则又出现一个Broadcasting操作。
Broadcasting 也叫广播机制(自动扩展也许更合适),它是一种轻量级张量复制的手段, 在逻辑上扩展张量数据的形状,但是只要在需要时才会执行实际存储复制操作。对于大部 分场景,Broadcasting 机制都能通过优化手段避免实际复制数据而完成逻辑运算,从而相对 于 tf.tile 函数,减少了大量计算代价。
Broadcasting 并不会立即复制数据,它会逻辑上改变张量的形状,使得视图上变 成了复制后的形状。Broadcasting 和 tf.tile 复制的最 终效果是一样的,操作对用户透明,但是 Broadcasting 机制节省了大量计算资源,建议在 运算过程中尽可能地利用 Broadcasting 提高计算效率。
x = tf.random.normal([2,4])
w = tf.random.normal([4,3])
b = tf.random.normal([3])
y = x@w+b
#上述加法并没有发生逻辑错误,那么它是怎么实现的呢?这是因为它自动调用Broadcasting 函数 tf.broadcast_to(x, new_shape),将 2 者 shape 扩张为相同的[2,3],即上式可以等效为:
y = x@w + tf.broadcast_to(b,[2,3])
通过自动调用 **tf.broadcast_to(b, [2,3])**的 Broadcasting 机制,既实现了 增加维度、复制数据的目的,又避免实际复制数据的昂贵计算代价,同时书写更加简洁高 效。
**Broadcasting 机制的核心思想是普适性,即同一份数据能普遍适合于其他位置在验证 普适性之前,需要将张量 shape 靠右对齐,然后进行普适性判断:对于长度为 1 的维度, 默认这个数据普遍适合于当前维度的其他位置;对于不存在的维度,则在增加新维度后默 认当前数据也是普适性于新维度的,从而可以扩展为更多维度数、其他长度的张量形状。 **
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ruMZVh4E-1583337541401)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210193200936.png)]
首先将 2 个 shape 靠右对齐,对于通道维度 c,张量的现长度为 1,则默认此数据同样适合 当前维度的其他位置,将数据逻辑上复制 − 1份,长度变为 c;对于不存在的 b 和 h 维 度,则自动插入新维度,新维度长度为 1,同时默认当前的数据普适于新维度的其他位 置,即对于其它的图片、其他的行来说,与当前的这一行的数据完全一致。这样将数据 b,h 维度的长度自动扩展为 b,h。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8pdvnWNJ-1583337541401)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210193310081.png)]
通过 tf.broadcast_to(x, new_shape)可以显式将现有 shape 扩张为 new_shape
In :
A = tf.random.normal([32,1])
tf.broadcast_to(A, [2,32,32,3])
Out:
通过 tf.add, tf.subtract, tf.multiply, tf.divide 函数实 现,TensorFlow 已经重载了+ −∗/运算符,一般推荐直接使用运算符来完成加减乘除运 算。
整除和余除也是常见的运算之一,分别通过//和%运算符实现。
In :
a = tf.range(5)
b = tf.constant(2)
a//b
Out:
In :
a%b
Out:
通过 tf.pow(x, a)可以方便地完成 = 乘方运算,也可以通过运算符 实现 ∗∗ 运 算
tf.pow(x,3)
x**2
x**(0.5) #设置指数为1/形式即可实现根号运算:a√
常见的平方和平方根运算,可以使用 tf.square(x)和 tf.sqrt(x)
x = tf.square(x) #平方
tf.sqrt(x) #平方根
通过 tf.pow(a, x)或者 运算符可以方便实现指数运算的次方。
2**x
特别地,对于自然指数,可以通过 tf.exp(x)实现:
tf.exp(1.)
自然对数l g 可以通过 tf.math.log(x)实现:
tf.math.log(x)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cah2jxgC-1583337541403)(C:\Users\A。\AppData\Roaming\Typora\typora-user-images\image-20200210194510145.png)]
tf.math.log(x)/tf.math.log(10.)
**了通过@运算符可以方 便的实现矩阵相乘,还可以通过 tf.matmul(a, b)实现 **
根据矩阵相乘的定义,a 和 b 能够矩阵相乘的条件是,**a 的倒数第一个维度长度(列)和 b 的倒数第二个维度长度(行)必须相等。**比如张量 a shape:[4,3,28,32]可以与张量 b shape:[4,3,32,2]进行矩阵相乘:
In :
a = tf.random.normal([4,3,28,32])
b = tf.random.normal([4,3,32,2])
a@b #tf.matmul(a,b)
Out:
矩阵相乘函数支持自动 Broadcasting 机制
对数l g 可以通过 tf.math.log(x)实现:**
tf.math.log(x)
[外链图片转存中…(img-Cah2jxgC-1583337541403)]
tf.math.log(x)/tf.math.log(10.)
**了通过@运算符可以方 便的实现矩阵相乘,还可以通过 tf.matmul(a, b)实现 **
根据矩阵相乘的定义,a 和 b 能够矩阵相乘的条件是,**a 的倒数第一个维度长度(列)和 b 的倒数第二个维度长度(行)必须相等。**比如张量 a shape:[4,3,28,32]可以与张量 b shape:[4,3,32,2]进行矩阵相乘:
In :
a = tf.random.normal([4,3,28,32])
b = tf.random.normal([4,3,32,2])
a@b #tf.matmul(a,b)
Out:
矩阵相乘函数支持自动 Broadcasting 机制
参考文献:tensorflow2.0深度学习 龙龙老师著