TensorFlow深度学习-第四章

作者: 皮皮大 | 来源:发表于2019-11-30 16:41 被阅读0次

    本章中的内容包含:

    • 数值类型
    • 数值精度
    • 数值运算
    • 张量
      • 张量创建
      • 待优化张量
    • 索引与切片
    • 维度变换
      • 改变视图
      • 插入维度
      • 删除维度
      • 复制数据
      • 广播机制

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

    image

    数据类型

    数值类型

    数值类型的张量是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规定的方式去创建张量

    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函数

    a = tf.constant([1.0])  # 即使是一个元素也是如此
    b = tf.constant([1.0, 2.4, 4.5]) 
    
    矩阵
    a = tf.constant([[1,2],
                     [3,4]])  # 2维
    
    b = tf.constant([[[1,2], [3,4]],
                     [[5,6], [7,8]]])  # 3维
    

    字符串类型

    字符串类型Strings类型的数据

    a = tf.constant("hello tensorflow")
    

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

    • join
    • length
    • split

    布尔类型

    TF中支持布尔类型的张量

    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
    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
    a = tf.constant([True, False])
    tf.cast(a, tf.bool)  # 1,0
    

    待优化张量

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

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

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

    • 不需要优化的张量,比如神经网络的输入Xtf.Variable不进行封装

    • 需要优化的张量,比如神经网络的权重和偏置等,通过tf.Variable进行把包裹

      import tensorflow as tf
      
      a = tf.constant([1,2,3])
      b = tf.Variable(a)
      b.name, b.trainable  
      
      # 直接创建
      c = tf.Variable([1,2,3])
      
      • name属性表示计算图中的名称;
      • trainable表示张量是否需要优化,默认是True,表示优化

    创建张量

    从Numpy、List对象创建

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

    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()
    tf.ones([2,3])
    
    a = tf.zeros([2,4])
    b = tf.ones_like(a)  # 形状相同
    

    自定义数值张量

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

    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(),标准的格式为:

    tf.range(start,end,delta=1)  # 含头不含尾,delta为步长
    

    张量的应用

    标量

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

    image
    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

    image

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

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

    矩阵

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

    w = tf.constant([3,4])  # 定义两个张量
    b = tf.constant([3])  
    o = x@w + b  # 执行X@W+b
    
    • X@W+b叫做线性层,也称之为全连接层,通过Dense类直接实现。

    • 通过全连接层的kernel属性查看权重矩阵W

    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]

    # 创建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,…]
    x = tf.random.normal([4, 32, 32, 3])
    x[0]
    x[0][1][2]
    

    切片

    通过𝑠𝑡𝑎𝑟𝑡: 𝑒𝑛𝑑: 𝑠𝑡𝑒𝑝切片方式提取数据

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

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

    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的张量之间如何进行相加?此时,维度变换可以解决


    image

    改变视图reshape

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

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

    • 改变张量的视图始终不改变张量的存储顺序
    • 视图变换需要满足新视图的元素总量与内存区域大小相等即可
    • 为了能够正确恢复出数据,必须保证张量的存储顺序与新视图的维度顺序一致
    • 在实现reshape操作的时候,需要记住张量的存储顺序
    • 参数-1表示长度的自定推导
    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为负数,在当前维度之后插入一个新维度
    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的维度
    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
    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操作,运算代价大

    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 机制都能通过优化手段避免实际复制数据而完成逻辑运算
    • 通过优化手段避免实际复制数据而完成逻辑运算,较少计算开销
    • 广播机制不会立即复制数据,逻辑上改变张量的形状
    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,增加维度后才会才适合
    image

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

    image

    数学运算

    方法 作用
    // 整除
    % 余除
    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𝑢𝑡 = 𝑟𝑒𝑙𝑢\{𝑟𝑒𝑙𝑢\{𝑟𝑒𝑙𝑢[𝑋@𝑊 + 𝑏 ]@𝑊 + 𝑏 \}@𝑊 + 𝑏 \}

    #  计算每个线性函数的张量参数
    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():实现参数的自我更新

    相关文章

      网友评论

        本文标题:TensorFlow深度学习-第四章

        本文链接:https://www.haomeiwen.com/subject/zjbowctx.html