TF-char4-TF2基本语法

2021-03-02 16:23:08 浏览数 (1)

char4-TensorFlow基础入门

TensorFlow是一个面向深度学习算法的科学计算库,内部数据保存在张量Tensor对象中,所有的运算操作都是基于张量进行的

数据类型

数值类型

数值类型的张量是TF的主要数据载体,包含:

  1. 标量Scalar,单个的实数,维度是0,形状shape[]
  2. 向量Vectorn个实数的有序集合,通过中括号包裹,例如[1,2,4,5,3],维数是1,长度不定,shape为n
  3. 矩阵Matrix,m行n列实数的有序集合,shape为[m,n]
  4. 张量是所有维度数(dim>2)的数组的统称,每个维度也称之为轴Axis。通常将标量、向量、矩阵也统称为张量;张量的维度和形状自行判断
标量

创建标量的关键字是constant,必须通过TF规定的方式去创建张量

代码语言:javascript复制
import tensorflow as tf

a = 2  # python形式
b = tf.constant(2.0)  # 这才是TF形式
c = tf.constant([1,2.0,3.7])

tf.is_true(b)  # True

返回值中几个具体信息:

  • id:内部索引对象的编号
  • shape:张量的形状
  • dtype:张量的数值精度
向量

向量的定义必须通过List类型转递给tf.constant函数

代码语言:javascript复制
a = tf.constant([1.0])  # 即使是一个元素也是如此
b = tf.constant([1.0, 2.4, 4.5])
矩阵
代码语言:javascript复制
a = tf.constant([[1,2],
                 [3,4]])  # 2维

b = tf.constant([[[1,2], [3,4]],
                 [[5,6], [7,8]]])  # 3维
字符串类型

字符串类型Strings类型的数据

代码语言:javascript复制
a = tf.constant("hello tensorflow")

tf.strings模块中提供了常见的工具函数:

  • join
  • length
  • split
布尔类型

TF中支持布尔类型的张量

代码语言:javascript复制
a = tf.constant([True, False])

# tf中布尔类型和Python的中布尔类型是不等同的
b =  tf.constant(True)
b == True     # 结果是False

数值精度

精度设置和获取

TF支持不同类型的精度,Bit位数越长,精度越高,同时占用的内存空间越大。

  • tf.int16/32/64
  • tf.float16/32/64;tf.float64就是tf.double

需要注意的点:

  1. 高精度转低精度可能会报错
  2. 对于浮点数,高精度的张量可以表示更精准的数据
  3. 实际中,一般使用tf.int32tf.float32
代码语言:javascript复制
import numpy as np
import tensorflow as tf

# 创建张量的时候指定精度
tf.constant(12345678, dtype=tf.int32)
tf.constant(np.pi, dtype=tf.float64)

通过张量的dtype属性可以获取张量的精度

类型转换

通过tf.cast函数进行转换,需要注意的地方:

  • 保证转换操作的合法性,比如高精度转低精度,可能发生溢出现象
  • 布尔型和整形之间可以转换
  • False默认是0,True表示1;其他非0数字默认是1
代码语言:javascript复制
a = tf.constant([True, False])
tf.cast(a, tf.bool)  # 1,0
待优化张量

有些张量是需要计算梯度,因此产生了需要计算待优化的张量,专门用来支持梯度信息的记录,使用的函数是tf.Variable

tf.Variable类型在普通的张量类型基础上添加了name 、trainable等属性来支持计算的构建。

梯度的计算会消耗大量的资源,且会自动更新相关参数。

创建张量

从Numpy、List对象创建

numpy中的array数组和Python中的list都可以直接用来创建张量,通过tf.convert_to_tensor

代码语言:javascript复制
import tensorflow as tf
import numpy as np

tf.convert_to_tensor([1,2,3])
tf.convert_to_tensor(np.array([[1,2,3],[4,5,6]])

numpy中默认使用的是64-bit精度,转到TF中使用的是tf.float64

创建全0、全1张量

几个函数记住即可,like只是创建形状相同的张量:

  • tf.ones()/tf.ones_like()
  • tf.zeros()/tf.zeros_like()
代码语言:javascript复制
tf.ones([2,3])

a = tf.zeros([2,4])
b = tf.ones_like(a)  # 形状相同
自定义数值张量

在创建张量的时候,可以指定初始值:tf.fill(shape, vlaue)

代码语言:javascript复制
tf.fill([2,3], -1)  # 形状为2*3,值全部是-1
创建已知分布的张量

正态分布和均匀分布是最常见的。

  • 正态分布:卷积神经网络中卷积核张量W,tf.random.normal(shape, mean=0.0, stddev=1.0)
  • 均匀分布:对抗网络中的隐藏层z一般采样自均匀分布,tf.random.uniform(shape, minval=0,maxval=None,dtype=float32)

注意:如果均匀分布中采样的是整数类型,必须指定maxval和数据类型

创建序列

创建序列类型的张量是通过函数tf.range(),标准的格式为:

代码语言:javascript复制
tf.range(start,end,delta=1)  # 含头不含尾,delta为步长

张量的应用

标量

标量的应用主要是误差值的表示、各种测量指标的表示,入精确度、精度、召回率等

代码语言:javascript复制
out = tf.random.uniform(4,10)  # 随机模拟网络输出
y = tf.constant([2,3,4,5]) # 随机构造样本真实输出标签
y = tf.one_hot(y, depth=10)  # 转成热编码
loss = tf.keras.losses.mse(y, out)  # 计算MSE
loss = tf.reduce_mean(loss)  # 平均MSE
print(mse)
向量

在全连接层和卷积神经网络中,偏置b就是向量b=[b_!,b_2]^T

通过高层结口Dense()方式创建地网络层,张量W和b存储在类的内部,由类自动创建。

  • 通过全连接层的bias成员查看偏置b
  • 类的偏置bias初始值全部是0
代码语言:javascript复制
fc = layers.Dense(3)  # 创建一层Wx b,输出节点为3
fc.build(input_shape=(2,4))
fc.bias  # 查看偏置
矩阵

矩阵也是非常常见的张量类型,比如全连接层的批量输入X=[b,d_{in}],其中b表示的是输入样本的个数,即batch size,d_{in}表示的是输入特征的长度。

代码语言:javascript复制
w = tf.constant([3,4])  # 定义两个张量
b = tf.constant([3])
o = x@w   b  # 执行X@W b
  • X@W b叫做线性层,也称之为全连接层,通过Dense类直接实现。
  • 通过全连接层的kernel属性查看权重矩阵W
代码语言:javascript复制
fc = layers.Dense(3)  # 定义全连接层的输出节点为3
fc.build(input_shape=(2,4))  # 定义全连接层的输入节点为4
fc.kernel # 查看权重矩阵
3维张量

三维的张量一个典型应用是表示序列信号,它的格式是? = [?, ???????? ???, ??????? ???]

  • ?表示序列信号的数量
  • ???????? ???表示时间维度上的采样点数
  • ??????? ???表示每个点的特征长度
4维张量

4维张量在卷积神经网络中应用的非常广泛,它用于保存特征图Feature maps数据, 格式一般定义为[b,h,w,c]

  • b表示输入的数量
  • h/w表示特征图的高宽
  • c表示特征图的通道数量

对于含有 RGB 3 个通道的彩色图片,每张图片包含了 h 行 w 列像素点,每个点需要 3 个数 值表示 RGB 通道的颜色强度,因此一张图片可以表示为[h, w, 3]

代码语言:javascript复制
# 创建32x32的彩色图片输入,个数为4
x = tf.random.normal([4,32,32,3]) # 创建卷积神经网络
layer = layers.Conv2D(16,kernel_size=3) out = layer(x) # 前向计算
out.shape # 输出大小

# 卷积核张量也是4维张量,通过kernel属性来查看
layer.kernel.shape

索引和切片

索引
  1. 从0开始
  2. 两种方式
    1. [i][j][k]...
    2. [i,j,k,…]
代码语言:javascript复制
x = tf.random.normal([4, 32, 32, 3])
x[0]
x[0][1][2]
切片

通过?????: ???: ????切片方式提取数据

  • 含头不含尾
  • step步长,可以为负数

关于冒号和三个点的使用:都是表示某个维度上的所有数据

代码语言:javascript复制
x = tf.random.normal([4, 32, 32, 3])
x[1:3]
x[0,::]
x[0, 0:28, 2:28:2, :]
x[::-2]
x[0:2,...,1:]

维度变换

线性层的批量形式

Y=X@W b

假设:

  • X 包含了 2 个样本,每个样本的特征长度为 4,X 的 shape 为[2,4]
  • 线性层的输出为3个节点,其shape为[4,3]
  • 偏置b的shape为[3]

那么不同shape的张量之间如何进行相加?此时,维度变换可以解决

改变视图reshape
张量存储
  1. 张量的存储体现张量在内存上保存为一块连续的存储区域
  2. 张量的存储需要人为跟踪
  3. shape中相对靠左的维度称之为大维度;相对靠右的维度称之为小维度
张量视图

语法格式为tf.reshape(x, new_shape)

  • 改变张量的视图始终不改变张量的存储顺序
  • 视图变换需要满足新视图的元素总量与内存区域大小相等即可
  • 为了能够正确恢复出数据,必须保证张量的存储顺序与新视图的维度顺序一致
  • 在实现reshape操作的时候,需要记住张量的存储顺序
  • 参数-1表示长度的自定推导
代码语言:javascript复制
x = tf.random.normal([4, 32, 32, 3])
tf.reshape(x, [2,-1])
tf.reshape(x,[2,4,12])
tf.reshape(x,[2,-1,3])
增删维度
增加维度
  • 增加一个长度为1的维度相当于是给原数据的维度增加一个新维度,可以理解成改变视图的一种特殊方式
  • 数据的存储方式不变,通过函数tf.expand_dims(x,axis)来实现
  • axis为正,表示在当前维度之前插入一个新维度;axis为负数,在当前维度之后插入一个新维度
代码语言:javascript复制
x = tf.random.uniform([28,28],maxval=10,dtype=tf.int32)
x = tf.expand_dims(x,axis=2)
删除维度
  • 增加维度的逆操作,只能删除长度为1的维度
  • 不改变张量的存储方式
  • 通过tf.squeeze(x, axis)来实现
  • axis表示删除维度的索引号;如果不指定,默认删除全部长度为1的维度
代码语言:javascript复制
x = tf.random.uniform([1,28,28,1],maxval=10,dtype=tf.int32)
x = tf.squeeze(x, axis=2)
tf.squeeze(x)
维度交换
  • 改变张量的存储,后续的所有操作都是基于新的存储顺序
  • 改变张量的视图
  • 通过tf.transpose(x, perm)来实现;其中perm表示新维度的顺序list
代码语言:javascript复制
x = tf.random.normal([2,32,32,3])
tf.transpose(x,perm=[0,3,1,2])
数据复制

通过函数tf.tile(x, multiples)来实现,关于参数multiples

  1. 1表示不复制
  2. 2表示长度为2倍,即复制1份
  3. 3表示长度为3倍,即复制2份;类推

复制操作会创建一个新的张量来保存复制后的张量,涉及到大量的IO操作,运算代价大

代码语言:javascript复制
b = tf.constant([1,2])
b = tf.expand_dims(b, axis=0)  # 插入新维度
b = tf.tile(b, multiples=[2,1])  # axis=0上复制1份

x = tf.range(4)
x = tf.reshape(x, [2,2])
x = tf.tile(x, multiples=[1,2])  # 列上复制
x = tf.tile(x,multiples=[2,1])  # 行上复制
广播机制Broadcasting

通过函数tf.broadcast_to(x, new_shape)实现

特点
  • 自动扩展,一种轻量级张量复制;在逻辑上扩展张量数据的形状
  • 对于大部 分场景,Broadcasting 机制都能通过优化手段避免实际复制数据而完成逻辑运算
  • 通过优化手段避免实际复制数据而完成逻辑运算,较少计算开销
  • 广播机制不会立即复制数据,逻辑上改变张量的形状
代码语言:javascript复制
x = tf.random.normal([2,4])
w = tf.random.normal([4,3])
b = tf.random.normal([3])
y = x@w b   # 等价于y = x@w   tf.broadcast_to(b,[2,3])  实现自动广播
核心思想

广播机制的核心思想是普适性,同一份数据能够适合于不同的位置

  • 长度为1,默认数据适合当前维度的其他位置
  • 长度不是1,增加维度后才会才适合

有些运算可以在处理不同 shape 的张量时,会隐式地调用广播机制

数学运算

方法

作用

//

整除

%

余除

tf.power(x,a),$x**a$

乘方

tf.square(x)

平方

tf.sqrt(x)

平方根

tf.power(a,x),$a**x$

指数运算

tf.exp(x)

自然指数

tf.math.log(x)

自然对数

$log_ax=frac{log_ex}{log_ea}$

其他底数的对数

矩阵相乘

两种方式实现:

  1. @
  2. tf.matmul(a,b)函数

实战-前向传播

采用的手写数字图片集数据:

  • 输入节点数是784,第一层节点数是256,第二次层是128,第三层是10
  • 3层神经网络的实现
o?? = ????{????{????[?@? ? ]@? ? }@? ? }
代码语言:javascript复制
#  计算每个线性函数的张量参数
w1 = tf.Variable(tf.random.truncated_normal([784, 256], stddev=0.1))
b1 = tf.Variable(tf.zeros([256]))
w2 = tf.Variable(tf.random.truncated_normal([256, 128], stddev=0.1))
b2 = tf.Variable(tf.zeros([128]))
w3 = tf.Variable(tf.random.truncated_normal([128, 10], stddev=0.1))
b3 = tf.Variable(tf.zeros([10]))

# 将输入数据进行维度变化
# [b, 28, 28] => [b, 28*28]
x = tf.reshape(x, [-1, 28*28])

# 非线性函数的计算
h1 = x@w1   tf.broadcast_to(b1, [x.shape[0], 256])
h1 = tf.nn.relu(h1)  # 得到输出函数

# 完成剩下两个非线性函数
# [b, 256] => [b, 128]
h2 = h1@w2   b2
h2 = tf.nn.relu(h2)
# [b, 128] => [b, 10]
out = h2@w3   b3

# 计算均方差MSE
# mse = mean(sum(y-out)^2)
# [b, 10]
loss = tf.square(y_onehot - out)
# mean: scalar
loss = tf.reduce_mean(loss)

# 计算梯度
grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3]

# w1 = w1 - lr * w1_grad
w1.assign_sub(lr * grads[0])
b1.assign_sub(lr * grads[1])
w2.assign_sub(lr * grads[2])
b2.assign_sub(lr * grads[3])
w3.assign_sub(lr * grads[4])
b3.assign_sub(lr * grads[5])
  • tape.gradient()求出网络参数的梯度信息
  • assign_sub():实现参数的自我更新

0 人点赞