• tensorflow基础


    • TensorFlow是一个面向深度学习的计算库,内部数据保存在张量对象上,所有的运算操作也都是基于张量进行的。
    • 复杂的神经网络本质上就是各种张量相乘、相加等基本操作的组合。

    数据类型

    数值型、字符串型、布尔型

    1.数值型

    标量(shape=())、向量(shape=(n))、矩阵(shape=(n,m))、张量

    注意,下面这两不一样:

    print(tf.constant([2])) # 向量
    print(tf.constant(2)) # 标量
    
    • 1
    • 2
    1. 创建张量
      tf.constant([[1, 2], [3, 4]])
    2. 打印张量时,不仅会有数据信息,还有dtype,shape的信息
    3. 转换成numpy,使用numpy()方法
    4. tf.is_tensor(a)
      判断是否是张量。
    # 标量
    a = tf.constant(1.2)
    print(a)
    print(type(a))
    print(tf.is_tensor(a))  # 判断是否是tensor
    
    x = tf.constant([1.2, 3.3])
    print(x)
    
    # 转换成numpy,使用numpy()方法
    num_x = x.numpy()
    print(num_x, type(num_x))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    <class 'tensorflow.python.framework.ops.EagerTensor'>
    True
    tf.Tensor([1.2 3.3], shape=(2,), dtype=float32)
    [1.2 3.3] <class 'numpy.ndarray'>
    
    • 1
    • 2
    • 3
    • 4

    注意:如果要使用tensorflow提供的功能函数。就必须通过tf规定的方式去创建张量,不能用Python语言的标准变量创建方式。

    2.字符串类型

    在 tf.strings 模块中,提供了常见的字符串类型的工具函数,如小写化 lower()、拼接join()、长度 length()、切分 split()等。

    s = tf.constant('Hello, Deep Learning!')
    print(s)
    # tf.strings模块提供了很多处理字符串的函数
    print(tf.strings.lower(s))
    
    • 1
    • 2
    • 3
    • 4
    tf.Tensor(b'Hello, Deep Learning!', shape=(), dtype=string)
    tf.Tensor(b'hello, deep learning!', shape=(), dtype=string)
    
    • 1
    • 2

    3.bool类型

    注意:tf的bool类型和python的bool类型并不等价!!!数值比较可以,但是对象比较不行。

    b = tf.constant(True)
    print(b)
    print(b == True)  # 数值比较,还是能判断的,结果是True
    print(b is True)  # 结果是False
    
    • 1
    • 2
    • 3
    • 4
    tf.Tensor(True, shape=(), dtype=bool)
    tf.Tensor(True, shape=(), dtype=bool)
    False
    
    • 1
    • 2
    • 3

    4.数值精度

    常用的精度类型有 tf.int16、tf.int32、tf.int64、tf.float16、tf.float32、tf.float64 等。

    对于大部分深度学习算法,一般使用 tf.int32 和 tf.float32 可满足大部分场合的运算精度要求,部分对精度要求较高的算法,如强化学习某些算法,可以选择使用 tf.int64 和tf.float64 精度保存张量。

    可以使用dtype属性读取精度。

    5.类型转换

    对于不符合要求的张量的类型及精度,需要类型转换,使用 :
    tf.cast 函数

    # 转化为高精度张量
    xx = tf.constant(np.pi, dtype=tf.float16)
    print(xx)
    yy = tf.cast(xx, tf.float64)
    print(yy)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    tf.Tensor(3.14, shape=(), dtype=float16)
    tf.Tensor(3.140625, shape=(), dtype=float64)
    
    • 1
    • 2

    注意:需要保证转换操作的合法性,例如将高精度的张量转换为低精度的张量时,可能发生数据溢出隐患:

    aa = tf.constant([123456789], dtype=tf.int32)
    bb = tf.cast(aa, tf.int8)
    print(bb)
    
    • 1
    • 2
    • 3
    tf.Tensor([21], shape=(1,), dtype=int8)
    
    • 1

    待优化张量

    为了区分需要优化和不需要优化的变量。
    需要优化的变量要是 tf.Variable 数据类型,支持记录梯度信息。
    在普通张量上加了name, trainable 等属性来支持计算图构建

    • name和trainable是Variable的特有属性,name 属性用于命名计算图中的变量,这套命名体系是 TensorFlow 内部维护的,一般不需要用户关注 name 属性;;trainable属性表征当前张量是否需要被优化,创建 Variable 对象时是默认启用优化标志,可以设置trainable=False 来设置张量不需要优化。
    1. tf.Variable() 函数将普通张量转换为待优化张量
    z = tf.constant([1, 2, 3.3])
    vz = tf.Variable(z, trainable=False)  # 设置不需要被优化,默认是True
    print(vz)
    print(vz.name, vz.trainable)
    
    • 1
    • 2
    • 3
    • 4
    <tf.Variable 'Variable:0' shape=(3,) dtype=float32, numpy=array([1. , 2. , 3.3], dtype=float32)>
    Variable:0 False
    
    • 1
    • 2
    1. tf.Variable()也可以直接创建
    vv = tf.Variable([1, 2])
    
    • 1
    1. 普通张量其实也可以通过 GradientTape.watch()方法临时加入跟踪梯度信息的列表,从而支持自动求导功能

    5.创建张量

    tf.convert_to_tensor()
    tf.constant()
    都能够自动的把 Numpy 数组或者 Python 列表数据类型转化为 Tensor 类型.

    tf.zeros()
    tf.ones()

    tf.zeros_like()
    tf.ones_like()

    tf.fill(shape, value): tf.fill([], -1) # 创建-1 的标量 / tf.fill([2,2], 99) # 创建 2 行 2 列,元素全为 99 的矩阵

    创建已知分布的张量 :
    tf.random.normal(shape, mean=0.0, stddev=1.0)

    tf.random.uniform(shape, minval=0, maxval=None, dtype=tf.float32)可以创建采样自[minval, maxval)区间的均匀分布的张量。
    如果需要均匀采样整形类型的数据,必须指定采样区间的最大值 maxval 参数,同时指 定数据类型为 tf.int*型:
    tf.random.uniform([2,2],maxval=100,dtype=tf.int32)

    创建序列:
    tf.range(limit, delta=1)可以创建[0, limit)之间,步长为 delta 的整型序列,不包含 limit 本身。

    6.张量典型应用

    标量:误差值、准确度、精度、召回率
    tf.keras.losses.mse(或 tf.keras.losses.MSE)返回每个样本上的误差值

    向量:偏置b
    通过高层接口类Dense()的方式创建网络层,张量W和b存储在类的内部,由类自动创建并管理。

    # 创建一个全连接层对象
    fc = layers.Dense(3)
    # 通过build函数创建w,b张量,输入节点为4
    fc.build(input_shape=(2, 4))
    print(fc.bias)  # 查看偏置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    <tf.Variable 'bias:0' shape=(3,) dtype=float32, numpy=array([0., 0., 0.], dtype=float32)>
    
    • 1

    可以发现 ,bias初始化为全0,这也是默认的初始化方式。
    bias是Variable数据类型,因为它是待优化变量。

    矩阵
    权值矩阵W

    x = tf.random.normal([2, 4])
    w = tf.ones([4, 3])
    b = tf.zeros([3])
    o = x @ w + b
    print(o)
    # 上面其实就是一个不带激活函数的全连接层,下面用Dense接口直接创建
    fc = layers.Dense(3)
    fc.build(input_shape=(2, 4))
    print(fc.kernel)  # 查看权值矩阵w
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    tf.Tensor(
    [[3.6994305 3.6994305 3.6994305]
     [0.3701757 0.3701757 0.3701757]], shape=(2, 3), dtype=float32)
    <tf.Variable 'kernel:0' shape=(4, 3) dtype=float32, numpy=
    array([[-0.01163948,  0.28521907, -0.06265694],
           [-0.42561263,  0.9115014 ,  0.67937183],
           [-0.48957258, -0.55697334, -0.39781886],
           [ 0.39810383,  0.85386693, -0.6553062 ]], dtype=float32)>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    三维张量
    三维的张量一个典型应用是表示序列信号
    在这里插入图片描述
    四维张量
    卷积神经网络用的多。
    [b,h,w,c]: b表示batch、h表示图片高、w表示图片宽、c表示图片通道数,一般是1 or 3;1就是灰度图片,3就是RGB三通道图片。

    创建卷积层的简单例子:

    x = tf.random.normal([4, 32, 32, 3])  # 4张32x32的3通道图片
    layer = layers.Conv2D(16, kernel_size=3)  # 第一个参数表示特征映射的数量
    # 前向传播
    out = layer(x)
    print(out)
    print(out.shape)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    索引与切片

    print("---------------------------------")
    x = tf.random.normal([4, 32, 32, 3])
    print(x[0])  # 32, 32, 3
    print(x[0][5])  # 32, 3
    print(x[0, 1:6:2, :, 0])  # 3, 32
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    • 特别地,step 可以为负数,考虑最特殊的一种例子,当step = −1时,start: end: −1表示从 start 开始,逆序读取至 end 结束(不包含 end),索引号𝑒𝑛𝑑 ≤ 𝑠𝑡𝑎𝑟𝑡。
      在这里插入图片描述

    维度变换

    reshape、插入新维度 expand_dims,删除维度 squeeze、交换维度 transpose、复制数据 tile 等函数。

    改变视图:
    张量的存储体现在张量在内存上保存为一段连续的内存区域,对于同样的存储,我们可以有不同的理解方式
    比如 shape 为[2,4,4,3]的张量𝑨,可以理解为 2 张图片,每张图片 4 行 4 列,每个位置有 RGB 3 个通道的数据;还理解为 2个样本,每个样本的特征为长度 48 的向量。
    如果新的逻辑结构不需要改变数据的存储方式,就可以节省大量计算资源,这也是改变视图操作的优势。

    所以tf.reshape只是从观察角度改变张量的形状,不改变内存。
    在这里插入图片描述
    在这里插入图片描述

    x = tf.range(100)
    y = tf.reshape(x, [2, 25, 2])
    
    • 1
    • 2

    注意:由于只是从观察视角改变形状,x和y共享内存,y改变,x也会改变。 ??不太确定

    增、删维度

    通过 tf.expand_dims(x, axis)可在指定的 axis 轴前可以插入一个新的维度:

    x = tf.random.normal([28, 28])
    y = tf.expand_dims(x, axis=2)
    print(y.shape)
    
    • 1
    • 2
    • 3
    (28, 28, 1)
    
    • 1

    是增加维度的逆操作,与增加维度一样,删除维度只能删除长度为 1 的维度,也不会改变张量的存储。

    # 删除维度
    z = tf.squeeze(y, axis=2)
    print(z.shape)
    
    • 1
    • 2
    • 3
    (28, 28)
    
    • 1

    如果不指定维度参数 axis,即 tf.squeeze(x),那么它会默认删除所有长度为 1 的维度。
    建议使用 tf.squeeze()时逐一指定需要删除的维度参数,防止 TensorFlow 意外删除某些长度为 1 的维度,导致计算结果不合法。

    交换维度

    tf.transpose(x, perm)函数完成维度交换操作。,其中参数 perm表示新维度的顺序 List。
    比如在 TensorFlow 中,图片张量的默认存储格式是通道后行格式:[𝑏, ℎ,w , 𝑐],但是部分库的图片格式是通道先行格式:[𝑏, 𝑐, ℎ,w ],因此需要完成[𝑏, ℎ, , 𝑐]到[𝑏, 𝑐, ℎ, w]维度交换运算,考虑图片张量 shape 为[2,32,32,3],“图片数量、行、列、通道数”的维度索引分别为 0、1、2、3,如果需要交换为[𝑏, 𝑐, ℎ, w]格式,则新维度的排序为“图片数量、通道数、行、列”,对应的索引号为[0,3,1,2],因此参数 perm 需设置为[0,3,1,2]。

    需要注意的是,通过 tf.transpose 完成维度交换后,张量的存储顺序已经改变,视图也随之改变,后续的所有操作必须基于新的存续顺序和视图进行。相对于改变视图操作,维度交换操作的计算代价更高。

    x = tf.random.normal([2, 24, 24, 3])  # [b,h,w,c]
    y = tf.transpose(x, [0, 3, 1, 2])  # [b,c,h,w]
    
    • 1
    • 2

    复制数据

    tf.tile(x, multiples)函数完成数据在指定维度上的复制操作,multiples 分别指定了每个维度上面的复制倍数。
    例子:
    线性回归 Y = X @ W + b Y=X@W+b Y=X@W+b中,b的维度和 X @ W X@W X@W不匹配,需要对b进行复制:1.先将向量b转换成矩阵2.再使用tile复制几行b(行数和样本数匹配):

    b = tf.constant([1, 2, 3])
    b = tf.expand_dims(b, axis=0)  # 扩展成矩阵,(1,3)
    b = tf.tile(b, multiples=[3, 1])  # 复制三行b
    print(b)
    
    • 1
    • 2
    • 3
    • 4
    tf.Tensor(
    [[1 2 3]
     [1 2 3]
     [1 2 3]], shape=(3, 3), dtype=int32)
    
    • 1
    • 2
    • 3
    • 4

    但是,实际上,我们不需要做增加维度和复制的工作,tensor的广播机制会帮我们完成。
    需要注意的是,tf.tile 会创建一个新的张量来保存复制后的张量,由于复制操作涉及大量数据的读写 IO 运算计算代价相对较高。

    Broadcasting★

    是一种轻量级的张量复制手段,逻辑上扩展张量数据的形状,但只有在需要的时候才会执行实际的复制手段。
    broadcasting 和 tile 的区别:
    tile: 会创建一个新的张量,执行复制IO操作,并保存复制后的张量数据。
    broadcasting:避免实际复制数据,最终效果同tile,大大节省了计算资源,计算过程尽可能使用。

    broadcasting会在运算时自动实现,也可以手动使用函数 tf.broadcast_to(x, new_shape)实现:

    y = x@w + tf.broadcast_to(b,[2,3]) # 手动扩展,并相加
    
    • 1

    在这里插入图片描述
    Broadcasting的核心思想是:普适性
    有关Broadcasting的具体规则,见书。
    在这里插入图片描述

    数学运算

    加、减、乘、除是最基本的数学运算,分别通过 tf.add, tf.subtract, tf.multiply, tf.divide函数实现,TensorFlow 已经重载了**+、 − 、 ∗ 、/运算符,一般推荐直接使用运算符来完成加、减、乘、除运算。
    整除和余除也是常见的运算之一,分别通过
    //和%运算符实现。
    通过 tf.pow(x, a)可以方便地完成𝑦 =x^𝑎的乘方运算,也可以通过运算符
    实现 x∗∗ 𝑎运 算

    数e^𝑥,可以通过 **tf.exp(x)**实现

    数log_e x可以通过 tf.math.log(x)

    矩阵相乘运算:

    1. 使用@运算符
    2. 通过 tf.matmul(a, b)函数。
      注:
      TensorFlow 中的矩阵相乘可以使用批量方式,也就是张量𝑨和𝑩的维度数可以大于 2。当张量𝑨和𝑩维度数大于 2 时,TensorFlow 会选择𝑨和𝑩的最后两个维度进行矩阵相乘前面所有的维度都视作Batch 维度。

    矩阵相乘同样适用于广播机制:
    在这里插入图片描述
    会自动将b扩展成[4,32,16]

    前向传播实战

    print("前向传播---------------")
    ## 前向传播实战
    (x, y), (x_test, y_test) = mnist.load_data()
    # 转换从成tensor
    x = tf.convert_to_tensor(x, dtype=tf.float32)
    y = tf.convert_to_tensor(y, dtype=tf.int32)
    with tf.GradientTape() as tape:
        # 初始化参数
        w1 = tf.Variable(tf.random.truncated_normal([784, 256], stddev=0.1))
        b1 = tf.Variable(tf.zeros([256]))
        w2 = tf.Variable(tf.random.normal([256, 128], stddev=0.1))
        b2 = tf.Variable(tf.zeros([128]))
        w3 = tf.Variable(tf.random.normal([128, 10], stddev=0.1))
        b3 = tf.Variable(tf.zeros(10))
        # 将x展平,适应于全连接网络
        x = tf.reshape(x, [-1, 28 * 28])
    
        # 前向传播,计算损失函数
        h1 = x @ w1 + b1
        h1 = tf.nn.relu(h1)
        h2 = h1 @ w2 + b2
        h2 = tf.nn.relu(h2)
        out = h2 @ w3 + b3
    
        # 将y独热编码
        y = tf.one_hot(y, depth=10)
        # 损失
        loss = tf.square(y, out)
        loss = tf.reduce_mean(loss)
    # 获取梯度
    grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3])
    lr = 0.01
    w1.assign_sub(lr*grads[0])
    b3.assign_sub(lr*grads[-1])
    
    • 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
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
  • 相关阅读:
    猫眼逆向协议抢票开发
    sqoop的安装和使用
    商业模式,淘宝,拼多多,京东,短视频商业模式
    并行前缀和计算——MPI SCAN算法的C语言实现
    C语言实现给一个不多于5位的正整数,要求:一、求它是几位数,二、逆序打印出各位数字。
    开发笔记 —— Linux 下的基础指令
    Lesson5-2:OpenCV视频操作---视频追踪
    深入理解Linux网络内 幕(七)——组件初始化的内核基础架构
    c++访问修饰符与继承关系
    GBASE 8s 如何并行执行update statistics
  • 原文地址:https://blog.csdn.net/weixin_44360866/article/details/126655943