美文网首页
tensorflow 之 张量与变量

tensorflow 之 张量与变量

作者: 水之心 | 来源:发表于2020-10-09 20:16 被阅读0次

    TensorFlow 与 MXNet、PyTorch 很相似,上手速度很快(相比于 TensorFlow1.x)。

    TensorFlow 拆开来看,就是 TensorFlow,即数据流程图,也是有向无环图(DAG)。在 TensorFlow 中以 Tensor(张量)表示数据,即 DAG 的(实线)边,而虚线的边用来表示流程的控制关系。DAG 的节点表示数学操作符(即数学运算)。

    1 张量 tf.Tensor

    张量是由 tf.Tensor 对象表示的具有统一类型(称为 dtype)的多维数组。您可以在 tf.dtypes.DType 中查看所有支持的 dtypes。就像 Python 数值和字符串一样,所有张量都是不可变的:永远无法更新张量的内容,只能创建新的张量。

    编写 TensorFlow 程序时,主要操纵和传递的对象是 tf.Tensortf.Tensor 具有以下属性:

    • 单一数据类型 (比如,float32,int32或字符串)
    • a shape

    TensorFlow 支持 eager 执行和 graph 执行。eager 执行时,将立即执行运算。在 graph 中执行时,构造一个计算图为以后运算。TensorFlow 默认 eager 执行。在以下示例中,立即计算矩阵乘法结果。其中 tf.constant 是张量的一种。

    # 使用 Tensor 计算一些值
    c = tf.constant([[1.0, 2.0], [3.0, 4.0]]) # 常量
    d = tf.constant([[1.0, 1.0], [0.0, 1.0]])
    e = tf.matmul(c, d) # 矩阵乘法
    print(e)
    

    输出:

    tf.Tensor(
    [[1. 3.]
     [3. 7.]], shape=(2, 2), dtype=float32)
    

    请注意,在 eager 执行过程中,您可能会发现您的 Tensors 实际上是类型为“EagerTensor”。这是内部细节,但确实可以给您访问有用的函数 numpy()

    在 TensorFlow 中,tf.function 是定义 graph 执行的常用方法。张量的 shape(即张量的 rank 和每个维度的大小)可能并不总是完全已知的。在 tf.function 定义中,shape 可能仅是部分已知的。

    如果也可以完全知道其输入的形状,则大多数操作都会生成形状已知的张量,但是在某些情况下,只有在执行时才能找到张量的形状。

    有许多专用的张量:参见 tf.constant, tf.sparse.SparseTensor, 和 tf.RaggedTensor

    1.1 常量 tf.constant

    def constant(value, dtype=None, shape=None, name="Const"):
    

    从 tensor-like 的对象创建恒定张量,即常量。

    注意:所有 eager 的 tf.Tensor 值都是不可变的(与 tf.Variable 相反)。从 tf.constant 返回的值没有特别常数。此功能与 tf.convert_to_tensor 基本上没有区别。名称 tf.constant 来自符号API(例如 tf.datatf.keras functional models),其中 value 嵌入在 tf.GraphConst 节点中。tf.constant 可用于断言该值可以通过这种方式嵌入。

    我们来创建一些基本张量。

    下面是一个“标量”(或称“0 秩”张量)。标量包含单个值,但没有“轴”。

    rank_0_tensor = tf.constant(4)
    print(rank_0_tensor)
    

    输出:

    tf.Tensor(4, shape=(), dtype=int32)
    

    “向量”(或称“1 秩”张量)就像一个值的列表。向量有 1 个轴:

    rank_1_tensor = tf.constant([2.0, 3.0, 4.0])
    print(rank_1_tensor)
    

    输出:

    tf.Tensor([2. 3. 4.], shape=(3,), dtype=float32)
    

    “矩阵”(或称“2 秩”张量)有 2 个轴:

    rank_2_tensor = tf.constant([[1, 2],
                                 [3, 4],
                                 [5, 6]], dtype=tf.float16)
    print(rank_2_tensor)
    

    输出:

    tf.Tensor(
    [[1. 2.]
     [3. 4.]
     [5. 6.]], shape=(3, 2), dtype=float16)
    

    张量的轴可能更多,下面是一个包含 3 个轴的张量:

    rank_3_tensor = tf.constant([
      [[0, 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]],])
                        
    print(rank_3_tensor)
    

    输出:

    tf.Tensor(
    [[[ 0  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]]], shape=(3, 2, 5), dtype=int32)
    

    对于包含 2 个以上的轴的张量,您可以通过多种方式将其可视化。

    通过使用 np.arraytensor.numpy 方法,您可以将张量转换为 NumPy 数组:

    张量通常包含浮点型和整型数据,但是还有许多其他数据类型,包括:

    • 复杂的数值
    • 字符串

    tf.Tensor 基类要求张量是“矩形”——也就是说,每个轴上的每一个元素大小相同。但是,张量有可以处理不同形状的特殊类型。

    上面并没有指定参数 dtype,而从 value 的类型中自动推断出类型。

    >>> # Constant 1-D Tensor from a python list.
    >>> tf.constant([1, 2, 3, 4, 5, 6])
      <tf.Tensor: shape=(6,), dtype=int32,
          numpy=array([1, 2, 3, 4, 5, 6], dtype=int32)>
    >>> # Or a numpy array
    >>> a = np.array([[1, 2, 3], [4, 5, 6]])
    >>> tf.constant(a)
      <tf.Tensor: shape=(2, 3), dtype=int64, numpy=
        array([[1, 2, 3],
               [4, 5, 6]])>
    

    如果指定了 dtype,则将结果张量值强制转换为请求的 dtype

    >>> tf.constant([1, 2, 3, 4, 5, 6], dtype=tf.float64)
      <tf.Tensor: shape=(6,), dtype=float64,
          numpy=array([1., 2., 3., 4., 5., 6.])>
    

    如果设置了 shape,则将 value 调整为匹配的形状。标量被扩展以填充 shape

    >>> tf.constant(0, shape=(2, 3))
        <tf.Tensor: shape=(2, 3), dtype=int32, numpy=
        array([[0, 0, 0],
               [0, 0, 0]], dtype=int32)>
    >>> tf.constant([1, 2, 3, 4, 5, 6], shape=[2, 3])
      <tf.Tensor: shape=(2, 3), dtype=int32, numpy=
        array([[1, 2, 3],
               [4, 5, 6]], dtype=int32)>
    

    如果将 eager 张量作为 value 传递,tf.constant 无效,它甚至会传输梯度:

    v = tf.Variable([0.0])
    with tf.GradientTape() as g:
        loss = tf.constant(v + v)
    g.gradient(loss, v).numpy()
    

    输出:

    array([2.], dtype=float32)
    

    但是,由于 tf.constant 将值嵌入到 tf.Graph 中,因此对于符号张量(symbolic tensors)而言失败:

    Related Ops:

    • tf.convert_to_tensortf.constant 类似,而不同于:
      • shape 参数
      • Symbolic tensors 被允许传递
    >>> i = tf.keras.layers.Input(shape=[None, None])
    >>> t = tf.convert_to_tensor(i)
    

    1.2 张量的简单运算

    我们可以对张量执行基本数学运算,包括加法、逐元素乘法和矩阵乘法运算。

    a = tf.constant([[1, 2],
                     [3, 4]])
    b = tf.constant([[1, 1],
                     [1, 1]]) # Could have also said `tf.ones([2,2])`
    
    tf.print(tf.add(a, b), "\n") # 矩阵加法
    tf.print(tf.multiply(a, b), "\n") # 逐元素乘法
    tf.print(tf.matmul(a, b), "\n") # 矩阵乘法
    

    输出:

    [[2 3]
     [4 5]] 
    
    [[1 2]
     [3 4]] 
    
    [[3 3]
     [7 7]] 
    

    其中,tf.print 用于打印信息。

    TensorFlow 也重载了运算符:

    tf.print(a + b, "\n") # element-wise addition
    tf.print(a * b, "\n") # element-wise multiplication
    tf.print(a @ b, "\n") # matrix multiplication```
    

    输出:

    [[2 3]
     [4 5]] 
    
    [[1 2]
     [3 4]] 
    
    [[3 3]
     [7 7]] 
    

    张量支持其他各种运算 (op) 。

    c = tf.constant([[4.0, 5.0], [10.0, 1.0]])
    
    # Find the largest value
    tf.print(tf.reduce_max(c), '\n')
    # Find the index of the largest value
    tf.print(tf.argmax(c), '\n')
    # Compute the softmax
    tf.print(tf.nn.softmax(c), '\n')
    

    输出:

    10 
    
    [1 0] 
    
    [[0.268941432 0.731058598]
     [0.999876618 0.00012339458]] 
    

    1.3 形状简介

    张量有形状。下面是几个相关术语:

    • 形状:张量的每个维度的长度(元素数量)。
    • 秩:张量的维度数量。标量的秩为 0,向量的秩为 1,矩阵的秩为 2。
      轴或维度:张量的一个特殊维度。
    • 大小:张量的总项数,即乘积形状向量

    注:虽然您可能会看到“二维张量”之类的表述,但 2 秩张量通常并不是用来描述二维空间。

    张量和 tf.TensorShape 对象提供了方便的属性来访问:

    虽然通常用索引来指代轴,但是您始终要记住每个轴的含义。轴一般按照从全局到局部的顺序进行排序:首先是批次轴,随后是空间维度,最后是每个位置的特征。这样,在内存中,特征向量就会位于连续的区域。

    tf.size(x).numpy()len(x) 等效。

    1.4 操作形状

    通过重构可以改变张量的形状。重构的速度很快,资源消耗很低,因为不需要复制底层数据。

    x = tf.constant([[1], [2], [3]])
    reshaped = tf.reshape(x, [1, 3])
    
    print(x.shape)
    print(reshaped.shape)
    

    输出:

    (3, 1)
    (1, 3)
    

    数据在内存中的布局保持不变,同时使用请求的形状创建一个指向同一数据的新张量。TensorFlow 采用 C 样式的“行优先”内存访问顺序,即最右侧的索引值递增对应于内存中的单步位移。

    如果您展平张量,则可以看到它在内存中的排列顺序。

    一般来说,tf.reshape 唯一合理的用途是用于合并或拆分相邻轴(或添加/移除 1)。

    对于 3x2x5 张量,重构为 (3x2)x5 或 3x(2x5) 都合理,因为切片不会混淆:

    重构可以处理总元素个数相同的任何新形状,但是如果不遵从轴的顺序,则不会发挥任何作用。

    利用 tf.reshape 无法实现轴的交换,要交换轴,您需要使用 tf.transpose

    您可能会遇到非完全指定的形状。要么是形状包含 None 维度(维度的长度未知),要么是形状为 None(张量的秩未知)。

    除了 tf.RaggedTensor 外,这种情况只会在 TensorFlow 的符号化计算图构建 API 环境中出现:

    1.5 DTypes 详解

    使用 Tensor.dtype 属性可以检查 tf.Tensor 的数据类型。

    从 Python 对象创建 tf.Tensor 时,您可以选择指定数据类型。

    如果不指定,TensorFlow 会选择一个可以表示您的数据的数据类型。TensorFlow 将 Python 整数转换为 tf.int32,将 Python 浮点数转换为 tf.float32。另外,当转换为数组时,TensorFlow 会采用与 NumPy 相同的规则。

    数据类型可以相互转换。

    1.6 广播

    广播是从 NumPy 中的等效功能借用的一个概念。简而言之,在一定条件下,对一组张量执行组合运算时,为了适应大张量,会对小张量进行“扩展”。

    最简单和最常见的例子是尝试将张量与标量相乘或相加。在这种情况下会对标量进行广播,使其变成与其他参数相同的形状。

    同样,可以扩展大小为 1 的维度,使其符合其他参数。在同一个计算中可以同时扩展两个参数。

    在本例中,一个 3x1 的矩阵与一个 1x4 进行元素级乘法运算,从而产生一个 3x4 的矩阵。注意前导 1 是可选的:y 的形状是 [4]。

    下面是不使用广播的同一运算:

    在大多数情况下,广播的时间和空间效率更高,因为广播运算不会在内存中具体化扩展的张量。

    使用 tf.broadcast_to 可以了解广播的运算方式。

    与数学运算不同,比方说,broadcast_to 并不会节省内存。在这个例子中,张量已经具体化。

    这可能会变得更复杂。Jake VanderPlas 的《Python 数据科学手册》一书中的这一节介绍了更多广播技巧(同样使用 NumPy)。

    1.7 tf.convert_to_tensor

    大部分运算(如 tf.matmultf.reshape)会使用 tf.Tensor 类的参数。不过,在上面的示例中,您会发现我们经常传递形状类似于张量的 Python 对象。

    大部分(但并非全部)运算会在非张量参数上调用 convert_to_tensor。我们提供了一个转换注册表,大多数对象类(如 NumPy 的 ndarrayTensorShape、Python 列表和 tf.Variable)都可以自动转换。

    有关更多详细信息,请参阅 tf.register_tensor_conversion_function。如果您有自己的类型,则可能希望自动转换为张量。

    1.8 不规则张量

    如果张量的某个轴上的元素个数可变,则称为“不规则”张量。对于不规则数据,请使用 tf.ragged.RaggedTensor

    例如,下面的例子无法用规则张量表示:

    应使用 tf.ragged.constant 来创建 tf.RaggedTensor

    tf.RaggedTensor 的形状包含未知维度:

    1.9 字符串张量

    tf.string 是一种 dtype,也就是说,在张量中,我们可以用字符串(可变长度字节数组)来表示数据。

    字符串是原子类型,无法像 Python 字符串一样编制索引。字符串的长度并不是张量的一个维度。有关操作字符串的函数,请参阅 tf.strings

    下面是一个标量字符串张量:

    下面是一个字符串向量:

    在上面的打印输出中,b 前缀表示 tf.string dtype 不是 Unicode 字符串,而是字节字符串。有关在 TensorFlow 如何使用 Unicode 文本的详细信息,请参阅 Unicode 教程

    如果传递 Unicode 字符,则会使用 utf-8 编码。

    tf.strings 中可以找到用于操作字符串的一些基本函数,包括 tf.strings.split

    以及 tf.string.to_number

    虽然不能使用 tf.cast 将字符串张量转换为数值,但是可以先将其转换为字节,然后转换为数值。

    tf.string dtype 可用于 TensorFlow 中的所有原始字节数据。tf.io 模块包含在数据与字节类型之间进行相互转换的函数,包括解码图像和解析 csv 的函数。

    1.10 稀疏张量

    在某些情况下,数据很稀疏,比如说在一个非常宽的嵌入空间中。为了高效存储稀疏数据,TensorFlow 支持 tf.sparse.SparseTensor 和相关运算。

    2 变量

    TensorFlow 变量是用于表示程序处理的共享持久状态的推荐方法。本指南介绍在 TensorFlow 中如何创建、更新和管理 tf.Variable 的实例。

    变量通过 tf.Variable 类进行创建和跟踪。tf.Variable 表示张量,对它执行运算可以改变其值。利用特定运算可以读取和修改此张量的值。更高级的库(如 tf.keras)使用 tf.Variable 来存储模型参数。

    2.1 设置

    本笔记本讨论变量布局。如果要查看变量位于哪一个设备上,请取消注释这一行代码。

    import tensorflow as tf
    
    # Uncomment to see where your variables get placed (see below)
    # tf.debugging.set_log_device_placement(True)
    

    2.2 创建变量

    要创建变量,请提供一个初始值。tf.Variable 与初始值的 dtype 相同。

    my_tensor = tf.constant([[1.0, 2.0], [3.0, 4.0]])
    my_variable = tf.Variable(my_tensor)
    
    # Variables can be all kinds of types, just like tensors
    bool_variable = tf.Variable([False, False, False, True])
    complex_variable = tf.Variable([5 + 4j, 6 + 1j])
    

    变量与张量的定义方式和操作行为都十分相似,实际上,它们都是 tf.Tensor 支持的一种数据结构。与张量类似,变量也有 dtype 和形状,并且可以导出至 NumPy。

    大部分张量运算在变量上也可以按预期运行,不过变量无法重构形状。

    如上所述,变量由张量提供支持。您可以使用 tf.Variable.assign 重新分配张量。调用 assign(通常)不会分配新张量,而会重用现有张量的内存。

    如果在运算中像使用张量一样使用变量,那么通常会对支持张量执行运算。

    从现有变量创建新变量会复制支持张量。两个变量不能共享同一内存空间。

    2.3 生命周期、命名和监视

    在基于 Python 的 TensorFlow 中,tf.Variable 实例与其他 Python 对象的生命周期相同。如果没有对变量的引用,则会自动将其解除分配。

    为了便于跟踪和调试,您还可以为变量命名。两个变量可以使用相同的名称。

    保存和加载模型时会保留变量名。默认情况下,模型中的变量会自动获得唯一变量名,所以除非您希望自行命名,否则不必多此一举。

    虽然变量对微分很重要,但某些变量不需要进行微分。在创建时,通过将 trainable 设置为 False 可以关闭梯度。例如,训练计步器就是一个不需要梯度的变量。

    step_counter = tf.Variable(1, trainable=False)
    

    2.4 放置变量和张量

    为了提高性能,TensorFlow 会尝试将张量和变量放在与其 dtype 兼容的最快设备上。这意味着如果有 GPU,那么大部分变量都会放置在 GPU 上。

    不过,我们可以重写变量的位置。在以下代码段中,即使存在可用的 GPU,我们也可以将一个浮点张量和一个变量放置在 CPU 上。通过打开设备分配日志记录(参阅设置),可以查看变量的所在位置。

    注:虽然可以手动放置变量,但使用分布策略是一种可优化计算的更便捷且可扩展的方式。

    如果在有 GPU 和没有 GPU 的不同后端上运行此笔记本,则会看到不同的记录。请注意,必须在会话开始时打开设备布局记录。

    您可以将变量或张量的位置设置在一个设备上,然后在另一个设备上执行计算。但这样会产生延迟,因为需要在两个设备之间复制数据。

    不过,如果您有多个 GPU 工作进程,但希望变量只有一个副本,则可以这样做。

    注:由于 tf.config.set_soft_device_placement 默认处于打开状态,所以,即使在没有 GPU 的设备上运行此代码,它也会运行,只不过乘法步骤在 CPU 上执行。

    有关分布式训练的详细信息,请参阅指南。要了解变量的一般使用方法,请参阅关于自动微分的指南。

    相关文章

      网友评论

          本文标题:tensorflow 之 张量与变量

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