TensorFlow学习笔记_Lecture 2 Tensorflow Operations

这篇博客详细介绍了TensorFlow中的基本操作,包括TensorBoard的使用、常量、数学运算、数据类型、变量、InteractiveSession、控制依赖以及数据导入。强调了常量与变量的区别,变量的初始化和赋值,以及避免延迟加载陷阱的重要性。此外,还提到了旧的占位符和feed_dict导入数据方式以及新的tf.data方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本博客是根据斯坦福大学的一门 tensorflow 课程的课件整理。这是课程地址 CS 20: Tensorflow for Deep Learning Research

1 TensorBoard 使用简介

  • tensorboard 可以用来使你创建的图可视化,比如你创建了一个复杂的神经网络,复杂到自己都不太理解,这时候就可以用 tensorboard 来看一看你的图,让你更好的去训练、调试或者优化。

  • 我们首先给出使用的方法,

    import tensorflow as tf
    
    a = tf.constant(2)
    b = tf.constant(3)
    x = tf.add(a, b)
    
    writer = tf.summary.FileWriter('./graphs',tf.get_default_graph())
    
    with tf.Session() as sess:
          # writer = tf.summary.FileWriter('./graphs', sess.graph)
          print(sess.run(x))
    writer.close()
  • 如果你还记得上一节课写的程序,那你应该能注意到这里多了两行程序。想要使用 tensorboard ,就得先将你定义的图写到日志文件里,用法是:

    writer = tf.summary.FileWriter([logdir], [graph])

    其中的 [logdir] 是你日志文件的目录。需要注意的一点是,在 windows 系统上,你的目录路径要么是类似./graphs 这样在日志文件的上一层。要么拥有更长的路径,这时候你需要用双斜杠分开路径,比如 E:\\TensorBoard\\logfile.

    [graph]是你想要查看的图,一般是你当前正在使用的。你可以使用 tf.get_default_graph() 或者 sess.graph 作为参数(当然你得先创建一个 sess才能这么写)。

    注意:上述的语句必须得写在你定义好图之后,执行计算之前。

  • 接下来,运行的程序,并启动 tensorboard,在命令行输入:

    python [filename.py]
    tensorboard --logdir="./graphs" --port 6006

    [filename.py] 是你自己程序的名字,logdir= 后面也要改成你自己的目录,port 是可选的参数,tensorboard 默认的端口就是 6006.

  • 然后根据屏幕上的提示,在浏览器输入地址,就可以看到刚才创建的图了。

    1527731727354

    1527731852464

  • 你还可以自定义上面节点的名字,将代码中的几行改成如下:

    a = tf.constant(2, name="a")
    b = tf.constant(3, name="b")
    x = tf.add(a, b, name="add")

    再运行 tensorboard 以后就会变成:

    1527732081043

    注意:如果你重复运行程序,目录下会生成多个日志文件,需要自己手动清理一下。

  • 现在你已经知道了怎么使用tensorboard ,还有很多其他功能需要你自己去探索。

2 Constant 常量

  • 可以直接创建一个常量,我们之前已经试过很多次了:

    tf.constant(value, dtype=None, shape=None, name='Const', verify_shape=False)
    
    # constant of 1d tensor (vector)
    
    a = tf.constant([2, 2], name="vector")
    
    # constant of 2x2 tensor (matrix)
    
    b = tf.constant([[0, 1], [2, 3]], name="matrix")
  • 可以创建一个特定维数的张量(tensor),然后填入特定的值:

    tf.zeros(shape, dtype=tf.float32, name=None)
    
    # create a tensor of shape and all elements are zeros
    
    tf.zeros([2, 3], tf.int32) ==> [[0, 0, 0], [0, 0, 0]]
    tf.zeros_like(input_tensor, dtype=None, name=None, optimize=True)
    
    # create a tensor of shape and type (unless type is specified) as the input_tensor but all elements are zeros.
    
    
    # input_tensor [[0, 1], [2, 3], [4, 5]]
    
    tf.zeros_like(input_tensor) ==> [[0, 0], [0, 0], [0, 0]]
    tf.ones(shape, dtype=tf.float32, name=None)
    
    # create a tensor of shape and all elements are ones
    
    tf.ones([2, 3], tf.int32) ==> [[1, 1, 1], [1, 1, 1]]
    tf.ones_like(input_tensor, dtype=None, name=None, optimize=True)
    
    # create a tensor of shape and type (unless type is specified) as the input_tensor but all elements are ones.
    
    
    # input_tensor is [[0, 1], [2, 3], [4, 5]]
    
    tf.ones_like(input_tensor) ==> [[1, 1], [1, 1], [1, 1]]
    tf.fill(dims, value, name=None)
    
    # create a tensor filled with a scalar value.
    
    tf.fill([2, 3], 8) ==> [[8, 8, 8], [8, 8, 8]]
  • 可以创建序列常量:

    tf.lin_space(start, stop, num, name=None)
    
    # create a sequence of num evenly-spaced values are generated beginning at start. If num > 1, the values in the sequence increase by (stop - start) / (num - 1), so that the last one is exactly stop.
    
    
    # comparable to but slightly different from numpy.linspace
    
    
    tf.lin_space(10.0, 13.0, 4, name="linspace") ==> [10.0 11.0 12.0 13.0]
    tf.range([start], limit=None, delta=1, dtype=None, name='range')
    
    # create a sequence of numbers that begins at start and extends by increments of delta up to but not including limit
    
    
    # slight different from range in Python
    
    
    
    # 'start' is 3, 'limit' is 18, 'delta' is 3
    
    tf.range(start, limit, delta) ==> [3, 6, 9, 12, 15]
    
    # 'start' is 3, 'limit' is 1,  'delta' is -0.5
    
    tf.range(start, limit, delta) ==> [3, 2.5, 2, 1.5]
    
    # 'limit' is 5
    
    tf.range(limit) ==> [0, 1, 2, 3, 4]

    不像 python 中的 rangeTensorFlow 中的 range 并不能用来迭代。

    for _ in np.linspace(0, 10, 4): # OK
    for _ in tf.linspace(0.0, 10.0, 4): # TypeError: 'Tensor' object is not iterable.
    
    for _ in range(4): # OK
    for _ in tf.range(4): # TypeError: 'Tensor' object is not iterable.
  • 也可以创建特定分布的随机常量,具体查阅 官方文档

    tf.random_normal
    tf.truncated_normal
    tf.random_uniform
    tf.random_shuffle
    tf.random_crop
    tf.multinomial
    tf.random_gamma
    tf.set_random_seed

3 数学运算

  • tensorflow 中的运算都很直观,直接查询官方文档就可以。接下去就讲几个比较棘手的点。

    1527733908565

  • 除法:tensorflow 中有 7 种不同的除法运算,所有的运算都差不多但是又有一些小差异。

    a = tf.constant([2, 2], name='a')
    b = tf.constant([[0, 1], [2, 3]], name='b')
    with tf.Session() as sess:
          print(sess.run(tf.div(b, a)))             ⇒ [[0 0] [1 1]]
          print(sess.run(tf.divide(b, a)))          ⇒ [[0. 0.5] [1. 1.5]]
          print(sess.run(tf.truediv(b, a)))         ⇒ [[0. 0.5] [1. 1.5]]
          print(sess.run(tf.floordiv(b, a)))        ⇒ [[0 0] [1 1]]
          print(sess.run(tf.realdiv(b, a)))         ⇒ # Error: only works for real values
          print(sess.run(tf.truncatediv(b, a)))     ⇒ [[0 0] [1 1]]
          print(sess.run(tf.floor_div(b, a)))       ⇒ [[0 0] [1 1]]
  • tf.add_n(),可以对多个张量进行相加:

    tf.add_n([a, b, b])  => equivalent to a + b + b
  • 点乘:使用 tf.tensordot() 进行点乘:

    a = tf.constant([10, 20], name='a')
    b = tf.constant([2, 3], name='b')
    with tf.Session() as sess:
          print(sess.run(tf.multiply(a, b)))           ⇒ [20 60] # element-wise multiplication
          print(sess.run(tf.tensordot(a, b, 1)))       ⇒ 80

4 数据类型

  • TensorFlow 接受 Python 原生类型,例如 Python 布尔值,数值(整数,浮点数)和字符串。 单个值将被转换为 0维张量(或标量),列表将被转换为 1维张量(向量),列表的列表 将被转换为 2 维张量。

    t_0 = 19 # Treated as a 0-d tensor, or "scalar"
    tf.zeros_like(t_0)                   # ==> 0
    tf.ones_like(t_0)                    # ==> 1
    
    t_1 = [b"apple", b"peach", b"grape"] # treated as a 1-d tensor, or "vector"
    tf.zeros_like(t_1)                   # ==> [b'' b'' b'']
    tf.ones_like(t_1)                    # ==> TypeError
    
    t_2 = [[True, False, False],
         [False, False, True],
         [False, True, False]]         # treated as a 2-d tensor, or "matrix"
    
    tf.zeros_like(t_2)                   # ==> 3x3 tensor, all elements are False
    tf.ones_like(t_2)                    # ==> 3x3 tensor, all elements are True
    
  • 下面是 TensorFlow 的完整数据类型列表:

    1527734066666

  • 如果你使用过 NumPy,你可能已经发现 TensorFlowNumPy 有很多相似的。没错,TensorFlow就是设计成可以跟 NumPy无缝衔接的。其实很多 TensorFlow 的数据类型和 NumPy 的是一样的,可以直接传入 NumPy 的数据类型。

    np.int32 == tf.int32        ==> True
    tf.ones([2, 2], np.float32) ==> [[1.0 1.0], [1.0 1.0]]
  • tf.Session.run() 中, 如果请求的对象是一个张量,那么输出将是一个NumPy 数组。

  • 虽然大多数时候,TensorFlowNumPy 的数据类型可以交替使用,但是因为还是会存在一些小问题,所以我们还是应该尽可能的使用 TensorFlow 的类型。

5 Variables 变量

  • 常量很好玩,但是我们现在需要学习真正有用的东西:变量。常量和变量的区别:

    1. 常量是不可变的,但是你在训练模型的过程中需要更新参数。
    2. 常量的值存储在图中,并且会复制到加载图的地方。而变量的值是单独存储的,比如说存在变量服务器上。

    上述第2点意味着,当你的常量占用很大的空间时,加载图的时候就会多花费一些时间。

  • 创建变量: 需要创建一个类 tf.Variable 的实例。老的方法是这样子:

    s = tf.Variable(2, name="scalar")
    m = tf.Variable([[0, 1], [2, 3]], name="matrix")
    W = tf.Variable(tf.zeros([784,10]))

    但是TensorFlow不建议使用这种方法,而是建议我们使用 tf.get_variable.

    tf.get_variable(
      name,
      shape=None,
      dtype=None,
      initializer=None,
      regularizer=None,
      trainable=True,
      collections=None,
      caching_device=None,
      partitioner=None,
      validate_shape=True,
      use_resource=None,
      custom_getter=None,
      constraint=None
    )
    s = tf.get_variable("scalar", initializer=tf.constant(2))
    m = tf.get_variable("matrix", initializer=tf.constant([[0, 1], [2, 3]]))
    W = tf.get_variable("big_matrix", shape=(784, 10), initializer=tf.zeros_initializer())

    如果使用 tf.constant 作为初始值,那就不用提供 shape参数。

  • 初始化参数: 使用参数之前需要进行初始化。不然会得到一个错误FailedPreconditionError: Attempting to use uninitialized value

    使用下面这条语句可以打印出所有没有被初始化的参数:

    print(session.run(tf.report_uninitialized_variables()))

    最简单的初始化方法就是初始化所有参数:

    with tf.Session() as sess:
          sess.run(tf.global_variables_initializer())

    也可以使用列表初始化部分参数:

    with tf.Session() as sess:
          sess.run(tf.variables_initializer([a, b]))

    也可以仅初始化某个参数:

    with tf.Session() as sess:
          sess.run(W.initializer)
  • 计算变量的值: 同样,我们必须在一个 session 里才能计算变量的值:

    
    # V is a 784 x 10 variable of random values
    
    V = tf.get_variable("normal_matrix", shape=(784, 10),
                       initializer=tf.truncated_normal_initializer())
    
    with tf.Session() as sess:
          sess.run(tf.global_variables_initializer())
          print(sess.run(V))

    也可以通过 tf.Variable.eval()得到变量值:

    with tf.Session() as sess:
          sess.run(tf.global_variables_initializer())
          print(V.eval())
  • 给变量赋值: 使用 tf.Variable.assign()

    W = tf.Variable(10)
    W.assign(100)
    with tf.Session() as sess:
          sess.run(W.initializer)
          print(W.eval()) # >> 10

    为什么会输出 10 呢?因为 assign 也是一个运算,需要在 session 中才能计算出来。

    W = tf.Variable(10)
    
    assign_op = W.assign(100)
    with tf.Session() as sess:
          sess.run(assign_op)
          print(W.eval()) # >> 100

    注意这里我们没有对 W 进行初始化,因为 assign 为我们做了初始化。实际上,初始化操作就是一种 assign 操作。

  • 自增和自减:

    W = tf.Variable(10)
    
    with tf.Session() as sess:
          sess.run(W.initializer)
          print(sess.run(W.assign_add(10))) # >> 20
          print(sess.run(W.assign_sub(2)))  # >> 18

    这里需要对变量进行初始化,因为自增和自减依赖于一个当前值。

  • 每个 session 都是单独存储比变量的,所以每个 session 对一张图中的变量可以拥有自己的当前值。

    W = tf.Variable(10)
    sess1 = tf.Session()
    sess2 = tf.Session()
    sess1.run(W.initializer)
    sess2.run(W.initializer)
    print(sess1.run(W.assign_add(10)))                # >> 20
    print(sess2.run(W.assign_sub(2)))                # >> 8
    print(sess1.run(W.assign_add(100)))                # >> 120
    print(sess2.run(W.assign_sub(50)))                # >> -42
    sess1.close()
    sess2.close()

6 InteractiveSession

  • 与普通 session 唯一的不同就是,InteractiveSession 会使自己成为默认会话,因此可以在不显示使用会话的情况下使用 run() 或者eval() 。 这在交互式 shellIPython 笔记本中很方便,因为它避免了必须传递显式会话对象来运行ops。 但是,当您有多个会话运行时,这会很复杂。

    sess = tf.InteractiveSession()
    a = tf.constant(5.0)
    b = tf.constant(6.0)
    c = a * b
    print(c.eval()) # we can use 'c.eval()' without explicitly stating a session
    sess.close()
  • tf.get_default_session()返回当前线程的默认会话。 返回的 Session将是输入 SessionSession.as_default() 上下文的最内层会话。

7 Control Dependencies 控制依赖

  • 有时候,我们有两个或更多的独立操作,我们想指定哪个操作应该先运行。 在这种情况下,我们使用 tf.Graph.control_dependencies([control_inputs])

    
    # your graph g have 5 ops: a, b, c, d, e
    
    with g.control_dependencies([a, b, c]):
    
    # `d` and `e` will only run after `a`, `b`, and `c` have executed.
    
    d = ...
    e = …

8 Importing Data 导入数据

8.1 老方法:placeholders 和 feed_dict

  • 我们写一个 TensorFlow 程序,一般是两个步骤:

    1. 定义一张图;
    2. 使用 Session 去执行计算。

    在定义图的时候,我们可能根本不知道我们要计算的值是多少,拿我们熟悉的函数举个例子:

    f(x,y)=2x+y f ( x , y ) = 2 x + y

    其中的 x,y x , y 是未知数,或者说 占位符(placeholder) 。使用了占位符后,你可以在你想要计算的时候再提供所需的数据。

  • 使用方法如下:

    tf.placeholder(dtype, shape=None, name=None)

    参数的意义都很明确,唯一一点需要注意的是,当使用 shape=None 时表示接受任意形状的参数。对于一些接受特定形状的函数来说,使用 shape=None 会使程序出错,所以为了不会引发一些奇怪的问题,应该尽量详细的定义占位符的形状。

  • 对于如下程序:

    a = tf.placeholder(tf.float32, shape=[3]) # a is placeholder for a vector of 3 elements
    b = tf.constant([5, 5, 5], tf.float32)
    c = a + b # use the placeholder as you would any tensor
    with tf.Session() as sess:
          print(sess.run(c))

    运行的时候会出错,因为我们没有为占位符 a 赋值,这时候需要用到 feed_dict,这是一个字典,键是占位符,值是要赋给占位符的数据。

    with tf.Session() as sess:
          # compute the value of c given the value of a is [1, 2, 3]
          print(sess.run(c, {a: [1, 2, 3]}))                 # [6. 7. 8.]

    我们也可以通过迭代来连续赋值:

    with tf.Session() as sess:
          for a_value in list_of_a_values:
                  print(sess.run(c, {a: a_value}))
  • feed_dict 不仅可以用在占位符上,我们通过:

    tf.Graph.is_feedable(tensor)

    来判断是否可以使用 feed_dict。如果可以,就能:

    a = tf.add(2, 5)
    b = tf.multiply(a, 3)
    
    with tf.Session() as sess:
          print(sess.run(b))                             # >> 21
          # compute the value of b given the value of a is 15
          print(sess.run(b, feed_dict={a: 15}))          # >> 45

    这个功能可以用在测试你的模型上。当你想要测试一张很大的图的其中的一部分,你可以提供“假”数据给模型,这样子 TensorFlow 就不会浪费时间去做一些无用的计算。

8.2 新方法:tf.data

  • 这个方法留到下节课我们通过一个例子讲解。

9 The trap of lazy loading 延迟加载的陷阱

  • 延迟加载指的是推迟声明/初始化一个对象直到加载它时为止的编程模式。在TensorFlow 中,它意味着你推迟创建一个 op,直到你需要计算它。

    这是一个正常加载的例子:

    x = tf.Variable(10, name='x')
    y = tf.Variable(20, name='y')
    z = tf.add(x, y)
    
    with tf.Session() as sess:
          sess.run(tf.global_variables_initializer())
          writer = tf.summary.FileWriter('graphs/normal_loading', sess.graph)
          for _ in range(10):
                  sess.run(z)
          writer.close()

    这是一个为了省一行代码自作聪明的例子:

    x = tf.Variable(10, name='x')
    y = tf.Variable(20, name='y')
    
    with tf.Session() as sess:
          sess.run(tf.global_variables_initializer())
          writer = tf.summary.FileWriter('graphs/lazy_loading', sess.graph)
          for _ in range(10):
                  sess.run(tf.add(x, y))
          writer.close()

    如果在 tensorboard 里看是这样的:

    1527757587670

    上方的是正常加载,下方的是延迟加载。看起来好像没有什么区别。

    我们再通过下面这条语句查看一下图的定义:

    print(tf.get_default_graph().as_graph_def())

    这是正常加载的:

    node {
    name: "Add"
    op: "Add"
    input: "x/read"
    input: "y/read"
    attr {
      key: "T"
      value {
        type: DT_INT32
      }
    }
    }

    这是延迟加载的:

    node {
    name: "Add_1"
    op: "Add"
    input: "x_1/read"
    input: "y_1/read"
    attr {
      key: "T"
      value {
        type: DT_INT32
      }
    }
    }
    …
    …
    …
    node {
    name: "Add_10"
    op: "Add"
    ...
    }

    我们发现程序创建了 10 个 Add 节点!

  • 以上就是我们要避免延迟加载的原因,因为我们可能会在不经意间产生许多无用的节点,这会使你的图占用空间变得巨大,以及加载缓慢等等许多坏处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值