- 本文基于Tensorflow2.2版本编写
什么是Tensorflow
Tensorflow 是一个面向深度学习算法的科学计算库,内部数据保存在张量(Tensor)对象熵,所有的运算操作(Operation)也都是基于张量对象进行的,我们所接触到的复杂的神经网络算法本质上就是各种张量相乘,想加等一些基本运算操作的组合。
Tensorflow的数据类型
数值类型
Tensorflow的数值类型我们称之为张量(Tensor),根据不同的维度我们可以分成以下几个部分。
- 标量(Scalar):单个实数,如1、2、3、4,我们可以说标量的维度是0。
- 向量(Vector):通过[]来包裹的n个实数的集合,如[1,2,3],向量的维度是1。
- 矩阵(Matrix):n行m列实数的有序集合,如[[1,2],[3,4]],矩阵的维度是2。
- 张量(Tensor):当数据的维度大于2的时候,我们就可以把它称为张量了。
ps:在tensorflow中,为了表达方便,无论怎样的维度我们都称之为张量。
创建张量
在python中我们可以直接使用“=”的方式来创建数据,但是在Tensorflow中,为了能够使用其内部使用的函数,所以我们需要用Tensorflow中内置的函数来进行张量的创建。
ps:布尔类型的数据也可以直接传入。
import numpy as np import tensorflow as tf # 创建标量 a = tf.constant(1.2) # 创建向量 b = tf.constant([1, 2]) # 创建矩阵 c = tf.constant([[1, 2], [3, 4]]) # 创建3维张量 d = tf.constant([[[1, 2, 3], [3, 4, 3]], [[5, 6, 3], [7, 8, 3]]]) d
字符串类型
tensorflow提供了字符串类型数据的创建方法和一些函数操作。
str1 = tf.constant('Hello TF') # 字符串小写 tf.strings.lower(str1) # 字符串的长度 tf.strings.length(str1) # 字符串的拼接 tf.strings.join() # 字符串的切分 tf.strings.split()
Tensorflow的数值精度
对于数值类型的张量,我们可以保存为不同字节长度的精度,如浮点数3.14既可以保存为16位长度,也可以保存为32位、64位等,当然位数越长也就意味着精度越高,统一占用的内容空间也就越大。
Tensorflow中常用的精度类型有:tf.int16、tf.int32、tf.int64、tf.float16、tf.float32、tf.float64。
对于深度学习来说,一般使用tf.int32和tf.float32可满足大部分场合的运算精度要求,部分对精度要求较高的算法,如强化学习某些算法,可以选择使用tf.int64和tf.float64精度保存张量。
# 创建指定精度的张量 tf.constant(123456789, dtype=tf.int16) tf.constant(123456789, dtype=tf.int32) # 读取精度 a.dtype # 类型转换 ''' 进行类型转换时,需要保证转换操作的合法性, 例如将高精度的张量转换为低精度的张量时,可能发生数据溢出隐患. 转换成bool类型的时候,会将非零数字都视为True ''' tf.cast(a, tf.int32)
Tensorflow的待优化张量
为了区分需要计算梯度信息的张量与不需要计算梯度信息的张量,TensorFlow 增加了一种专门的数据类型来支持梯度信息的记录:tf.Variable。tf.Variable 类型在普通的张量类型基础上添加了name,trainable等属性来支持计算图的构建。由于梯度运算会消耗大量的计算资源,而且会自动更新相关参数,对于不需要的优化的张量,如神经网络的输入x, 不需要通过tf.Variable封装;相反,对于需要计算梯度并优化的张量,如神经网络层的w和b,需要通过tf.Variable包裹以便 TensorFlow跟踪相关梯度信息。
# 创建TF张量 a = tf.constant([0, 1, 2, 3]) # 转换为Variable类型 aa = tf.Variable(a) # a = tf.Variable([[1, 2], [3, 4]]) # var中的属性 aa.name, aa.trainable
Tensorflow创建张量
在Tensorflow中我们不仅能够从python列表创建张量,同样也可以从numpy数组中来创建,还可以通过已知的某种分布来进行创建。
# 从列表中创建张量 tf.convert_to_tensor([1, 2]) # 从数组中创建张量 tf.convert_to_tensor(np.array([[1, 2], [3, 4]])) # 创建全0标量 tf.zeros([]) # 创建全1的标量 tf.ones([]) # 创建全0的向量 tf.zeros([5]) # 创建全1的向量 tf.ones([5]) # 创建全0的矩阵 tf.zeros([2, 3]) # 创建全1的矩阵 tf.ones([2, 3]) # 创建全0的3维张量 tf.zeros([2, 2, 3]) # 创建全1的3维张量 tf.ones([2, 2, 3]) # 创建自定义数值的张量 tf.fill([], -1) tf.fill([5], -1) tf.fill([2, 3], -1) tf.fill([2, 2, 3], -1) # 创建已知分布的张量 # 创建标准正态分布的张量 tf.random.normal([2, 2]) # 创建一般正态分布的张量 tf.random.normal([2, 2], mean=1, stddev=2) # 创建采样自[0,1)均匀分布的矩阵 tf.random.uniform([2, 2]) # 创建采样自[0,10]均匀分布的矩阵 tf.random.uniform([2, 2], maxval=10) # 创建序列 tf.range(10) # 设定步长创建序列 tf.range(5, 10, delta=2)
张量的应用
# 计算平均MSE(标量) out = tf.random.uniform([4, 10]) y = tf.constant([2, 3, 2, 0]) y = tf.one_hot(y, depth=10) print(y) loss = tf.keras.losses.mse(y, out) loss = tf.reduce_mean(loss) print(loss) # 向量的加减(向量) ''' 这里的做法可以用于神经网络的全连接层中, 给每一个节点都增加了一个向量形式的偏置。 ''' z = tf.random.normal([4, 2]) b = tf.ones([2]) z = z + b print(z) # 矩阵的运算(矩阵) ''' 在神经网络的全连接层中,我们除了会涉及到 偏置的计算,还会涉及到权重的计算,下面来 演示一下。 ''' x = tf.random.normal([2, 4]) w = tf.ones([4, 3]) b = tf.zeros([3]) o = x @ w + b print(o) # 张量在图像中的应用(张量) ''' 我们日常生活中所见到的图像都是由RGB3个通道的色彩组成的, 再加上图片的尺寸(h行w列的像素点),我们可以把一张图片 表示成[h,w,3]的形式。 ''' # 创建32x32的彩色图片输入,个数为4 x = tf.random.normal([4, 32, 32, 3]) # 使用卷积神经网络 layer = tf.keras.layers.Conv2D(16, kernel_size=3) # 前向计算 out = layer(x) out.shape
索引和切片
张量也提供了通过索引和切片进行部分数据读取的方式,并且这两类方法的使用频率是非常高的。
# 索引 # 创建一个4D的张量 x = tf.random.normal([4, 32, 32, 3]) # 读取第1张图片的数据 x[0] # 读取第1张图片的第2行 x[0][1] # 读取第1张图片的第2行,第3列。 x[0][1][2] # 读取第1张图片的第2行,第3列,第1个通道(RGB中的G通道)的数据。 x[0][1][2][1] # 当维度变的越来越高的时候,[i][j][k]的书写会变的很不方便,我们可以尝试采用[i,j,k]的方法 x[0, 1, 2, 1]
# 切片 ''' 切片在每一个维度上的很多使用方法和我们在列表中使用的切片是一样的。 在张量中我们可以同时对多个维度进行操作。 ''' # 读取第2,3张图片 x[1:3] # 读取某一部分的数据(使用步长) # shape=(4, 14, 14, 3) x[:, 0:28:2, 0:28:2, :] ''' ...切片方式说明 [a,...,b]: a 维度对齐到最左边,b 维度对齐到最右边,中间的维度全部读取, 其他维度按 a/b 的方式读取。 [a,...] : a 维度对齐到最左边,a 维度后的所有维度全部读取,a 维度按 a 方式 读取。这种情况等同于 a 索引/切片方式。 [...,b] : b 维度对齐到最右边,b 之前的所有维度全部读取,b 维度按 b 方式读取。 [...] : 读取张量所有数据。 ''' # 避免过多冒号的写法 x[...,:2]
维度变换
我们可以通过维度变换的形式将数据进行任意形式的切换,满足不同场合的运算需求。
# 改变视图 # 把向量改变成张量 # 生成向量 x = tf.range(96) # 把向量变成4D张量,数据的顺序不变 # 使用tf.reshape(x,[])可以对张量进行任意维度合法的转变 x = tf.reshape(x, [2, 4, 4, 3]) # 获取张量的维度数和形状列表 x.ndim, x.shape
# 增加维度 # 我们可以在不改变原数据的情况下通过增加维度的方式对数据的维度进行改变 # 创建矩阵 x = tf.random.uniform([28, 28], maxval=10, dtype=tf.int32) # 通过tf.expand_dims(x,axis)可以添加一个新的维度 # shape=(28, 28, 1) x = tf.expand_dims(x, axis=2) # axis=2表示在宽度后面增加一个维度 # shape=(1, 28, 28, 1) x = tf.expand_dims(x, axis=0) # axis=0表示在高度前面增加一个纬度 # 删除维度 # 删除维度可以看作是增加维度的你操作,和增加维度一样,删除维度只能删除长度为1的维度 # 通过tf.squeeze(x,axis)可以删除一个维度 # shape=(28, 28, 1) x = tf.squeeze(x, axis=0) # shape=(28, 28) x = tf.squeeze(x, axis=2) # 删除所有长度为1的维度 # x = tf.squeeze(x) # 交换维度 # 通过tf.transpose(x,perm=[])可以进行维度的交换 x = tf.random.normal([2, 32, 32, 3]) tf.transpose(x, perm=[0, 3, 1, 2]) # 复制数据 # 创建向量b b = tf.constant([1, 2]) # 转换成矩阵 # shape=(1, 2) b = tf.expand_dims(b, axis=0) # 行维度上复制一份 # shape=(2, 2) b = tf.tile(b, multiples=[2, 1]) # 列维度:tf.tile(b, multiples=[1,2]) b
数学运算
''' 数学运算中,对于基本的数学运算既可以用函数也可以直接用运算符来完成 加法:tf.add | + 减法:tf.subtract | - 乘法:tf.multiply | * 除法:tf.divide | / ''' a = tf.range(5) b = tf.constant(2) # 整除运算 a//b # 取余数运算 a%b # 乘方运算 tf.pow(a,3) a**2 # 平方根运算 c = tf.constant([1.,4.,9.]) tf.sqrt(c) c**(0.5) # 指数运算 d = tf.constant([1.,2.,3.]) 2**x # 自然指数运算 tf.exp(1.) # 对数运算 e = tf.exp(3.) tf.math.log(e) # 矩阵的相乘 f = tf.random.normal([4,3,28,32]) g = tf.random.normal([4,3,32,2]) # 批量形式的矩阵相乘 f@g # 使用广播机制,先扩展再相乘 h = tf.random.normal([4,28,32]) # 扩展为[4,32,16] i = tf.random.normal([32,16]) tf.matmul(h,i)