TENSOR和FLOW

0.简介

此札记的目的还是, 通过输出: 1.理解概念, 2.加深印象, 3.融会贯通.

首先, 先看一段Python中Tensorflow的代码:

import tensorflow as tf
import numpy as np

# NumPy生成假数据(phony data), 共100个;
x_data = np.float32(np.random.rand(2, 100))         # 随机生成: shape=(2,100);
y_data = np.dot([0.100, 0.200], x_data) + 0.300

# 构造一个线性模型;
# 只有tensor(变量)才能被tf的图接受(这是语法上的问题);
b = tf.Variable(tf.zeros([1]))
# 均匀分布的介于-1和1之间的1行2列的tensor(array);
W = tf.Variable(tf.random_uniform([1, 2], -1.0, 1.0))
y = tf.matmul(W, x_data) + b

# 最小化方差;
loss = tf.reduce_mean(tf.square(y - y_data))        # 图中的一个operator;
optimizer = tf.train.GradientDescentOptimizer(0.5)  # 图中的一个operator;
train = optimizer.minimize(loss)                    # 图中的一个operator;

# 初始化变量;
init = tf.initialize_all_variables()

# 启动图(graph);
sess = tf.Session()
sess.run(init)

# 拟合平面;
for step in xrange(0, 201):
    # train是一个operator, 图(即sess)的run()会自动根据依赖, 进行相应计算;
    sess.run(train)
    if step % 20 == 0:
        print step, sess.run(W), sess.run(b)

# 得到最佳拟合结果 W: [[0.100  0.200]], b: [0.300]

在上面的注释中, 我反复添加了注释# 图中的一个operator;, 是为了强调这三行代码中的每个Python变量都是tf图中的operator. 这里的疑问是: 明摆着的是Python代码, 可底层到底如何运作的? 难道Python解释器自动感知了那三个变量? 它怎么知道是trainoptimizer变量是要做模型的训练和优化? 它并没有被告知使用什么模型?

上面的例子直接使用了复杂的训练和优化流程来解释tf图的概念, 下面用一个简单点的例子, 同样也是tf图的概念, 只不过数据没那么复杂了:

# 创建一个变量, 初始化为标量 0.
state     = tf.Variable(0, name="counter")
# 创建一个 op, 其作用是使 state 增加 1
one       = tf.constant(1)
new_value = tf.add(state, one)
update    = tf.assign(state, new_value)
# 启动图后, 变量必须先经过`初始化` (init) op 初始化,
# 首先必须增加一个`初始化` op 到图中.
init_op   = tf.initialize_all_variables()

# 启动图, 运行 op
with tf.Session() as sess:
  sess.run(init_op)     # 运行 'init' op
  print sess.run(state) # 打印 'state' 的初始值
  for _ in range(3):    # 运行 op, 更新 'state', 并打印 'state'
    sess.run(update)
    print sess.run(state)
# 输出为:
# 0
# 1
# 2
# 3

1.基本使用


好了, 尽管有上面的例子, 但它们只是代码(没从中看出tf图的样子), 下面, 开始解释代码背后的tf图逻辑(不准确解释为"编译树", 这才是tf图, 眼睛看不见只有脑子能"看见"的那种图).

在Tensorflow中:

  • 使用**图(graph)**来表示计算任务;

    (p.s. 说到图, 就隐含背后计算的依赖)

  • 在被称之为**会话(Session)**的上下文(context)中执行图;

    (p.s. 图中的数学计算当然要消耗计算资源, 而Python中的Session便是管理这种上下文(1.进入Session代码块:使用资源, 2.执行别的代码块:结束Session代码块中对OS资源的占用)的, 故要在Session中执行图的计算)

  • 使用tensor表示数据;

    (p.s. 不必紧张, tensor根本不是能用于真正计算的数据类型(比如int, float等才是), 它只是一种数据类别的强制要求(Google说在我的tf中必须要用tensor来包装一切数据, 就当这样便于管理tf图吧), 这个关系好比Class与Class中基本数据类型的关系一样, 只不过这里是tf, 那里是一般编程语言的Class概念).

  • 通过**变量(Variable)**维护状态;

  • 使用feedfetch可以为任意的操作(arbitrary operation)赋值或者从其中获取数据.


2.综述

Tensorflow是一个编程系统, 使用图来表示计算任务, 图中的节点被称之为op(operation的缩写, 意为具体某种计算操作, 如加法), 如下图中的’绿/红/黄/蓝色’节点. 一个op获得0个或多个Tensor数据, 执行计算, 产生0个或多个Tensor结果. 每个Tensor是一个类型化的多维数组, 例如, 你可以将一小组图像集表示为一个四维浮点数数组([图像个数x图像的三维]), 这四个维度分别是[batch, height, width, channels].

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lNE7vhZu-1569858099969)(https://i.loli.net/2019/06/19/5d09bbd46ddfc97012.png)]
图自SRC.

一个tf图描述了计算的过程(这个过程即计算的依赖关系). 为了进行计算, 图必须在’会话’里被启动(在Session代码块中进行). 会话将图的op分发到诸如CPU或GPU之类的设备上, 同时提供执行op的方法(e.g. 具体操作, 如加法, 还是乘法, 还是卷积, etc.). 这些方法执行后, 将产生的tensor结果返回. 在Python中, tensor结果是numpyndarray对象; 在C和C++语言中, tensor结果是tensorflow::Tensor实例对象.


3.计算图

Tensorflow的程序通常被组织成: 一个1.构建阶段和一个2.执行阶段(我理解成定义使用tf图).

在构建阶段, op的执行步骤被描述成一个tf图. 在执行阶段, 使用会话执行tf图中的op. 例如, 通常在构建阶段创建一个图来表示和训练神经网络, 然后在执行阶段反复执行图中的训练op(即train变量).

(p.s. TensorFlow支持C, C++, Python编程语言. 目前, TensorFlow的Python库更加易用, Python库提供了大量的辅助函数来简化构建图的工作, 但在C和C++中, 它们尚未被支持, 但三种语言的会话库(session libraries)是一致的)


4.构建图(‘变量声明’与’依赖’)

举例来说, 构建图(如构建AlexNet的tf图), 就是在Python的TF代码中定义好变量的调用关系(如池化层的函数将卷积层的变量当做输入, 这就是一个调用关系, 即图的依赖), 这个AlexNet的输入, 5个Conv层和3个FC层的依赖关系, 就体现在Python的TF代码的变量调用关系中(而继续深究下去, 可能要查找’编译树’了).

构建图的第一步, 是创建’源op’(source op). 源op不需要任何输入, 例如常量(Constant), 它的输出被传递给其它op做进一步的运算. 在代码中(如下), op构造器的返回值代表被该op的输出, 这些返回值可以传递给其它op构造器作为输入(返回值=输出, 这不是废话吗…).

TF的Python库有一个默认图(default graph), op构造器可以为其增加节点, 这个默认图对许多程序来说已经足够用了(代码中并没有相应代码语句的表示, 只不过由Python解释器默认使用). (阅读’Graph类’的文档来了解如何管理多个图)

import tensorflow as tf

# 创建一个常量op, 产生一个1x2矩阵, 这个 op 被作为一个节点加到默认图中.
# 构造器(指'tf.constant()')的返回值代表该常量op的返回值.
matrix1 = tf.constant([[3., 3.]])

# 创建另外一个常量op, 生成一个2x1矩阵.
matrix2 = tf.constant([[2.],[2.]])

# 创建一个'矩阵乘法matmul'op, 把'matrix1'和'matrix2'作为输入.
# 返回值'product'代表矩阵乘法的结果.
product = tf.matmul(matrix1, matrix2)

# 至此, 变量的调用关系, 也即tf图(依赖)的构建, 完成了;

默认图现在有三个节点, 两个constant() op, 和一个matmul() op. 为了真正进行矩阵相乘运算, 并得到矩阵乘法的 结果, 你必须在会话里启动这个图.

(p.s. 这好像有点杀鸡用牛刀了: 矩阵相乘, 我不能用普通的Python代码?! Don’t worry, 这只是个举例, 牛刀能杀鸡, 也能宰牛)

5.启动图(在会话中启动)

构造阶段完成后, 才能启动图(p.s. 要不然没意义啊, 没构造完如何进行有意义的计算). 启动图的第一步是创建一个Session对象(这是语法机制), 如果无任何创建参数(传给Session()函数), 会话构造器将启动默认图. (欲了解完整的’会话API’, 请阅读Session类)

# 启动默认图(无参数传入);
sess = tf.Session()

# 1.调用sess的'run()'方法来执行'矩阵乘法op', 传入'product'作为该方法的参数; 
# 2.在'4节'提到, 'product'代表了'矩阵乘法op'的输出, 
#   传入它是向方法表明, 我们希望取回'矩阵乘法op'的输出;
# 3.整个执行过程是自动化的, (!!!)会话负责传递op所需的全部输入(!!!), op通常是并发执行的(Mark: 如何并发?!);
# 4.函数调用'run(product)'触发了图中三个op(两个常量op和一个矩阵乘法op)的执行(!!!);
# 5.返回值'result'是一个numpy的`ndarray`对象; (tf的返回值都是ndarray对象)
result = sess.run(product)  # (!!!)前面构建好了图, 才有这一步的启动和计算;
print result  # ==> [[ 12.]]

# 任务完成, (需)关闭会话;
sess.close()

Session对象在使用完后需要关闭以释放资源, 除了显式调用close()外, 也可以使用"with"代码块, 来自动完成(符合规范的程序的)关闭动作:

with tf.Session() as sess:
  result = sess.run([product])
  print result

关于CPU和GPU(等设备)

在实现上, TF将图定义转换成分布式执行的操作, 以***充分利用***可用的计算资源(如CPU或GPU). TF能自动检测是使用CPU还是GPU, 一般不需要人为显式指定, 如果检测到GPU, TF会尽可能地利用找到的第一个GPU来执行操作.

如果机器上有***超过一个***可用的GPU, 除第一个外的其它GPU默认不参与计算. 如果要让TF使用这些GPU, 必须将op明确指派给它们执行. with...Device语句用来指派特定的CPU或GPU执行操作:

with tf.Session() as sess:
  with tf.device("/gpu:1"):
    matrix1 = tf.constant([[3., 3.]])
    matrix2 = tf.constant([[2.],[2.]])
    product = tf.matmul(matrix1, matrix2)
    ...

CPU和GPU等设备用字符串进行标识, 目前支持的设备包括:

  • /cpu:0”: 机器的CPU;
  • /gpu:0”: 机器的第一个GPU, 如果有的话;
  • /gpu:1”: 机器的第二个GPU, 以此类推.

(阅读使用GPU章节, 了解 TensorFlow GPU 使用的更多信息)


6.交互式使用

文档中的Python示例使用一个’会话Session’来启动图, 并调用Session.run()方法执行操作. 为了便于使用诸如’IPython’之类的 Python交互环境, 可以使用InteractiveSession代替Session类, 使用Tensor.eval()Operation.run()方法代替Session.run(), 这样可以避免使用一个变量来持有会话.

(上面的话的意思是: 我就不sess.run()了, 你在代码中声明一下InteractiveSession, 然后我就告诉Python解释器关于资源的使用方式, 交由你的Tensor.eval()Operation.run()来使用, 但它(Python解释器)还是会管理的)

# 进入一个交互式 TensorFlow 会话.
import tensorflow as tf
sess = tf.InteractiveSession()

x = tf.Variable([1.0, 2.0])
a = tf.constant([3.0, 3.0])
# 使用初始化器 initializer op 的 run() 方法初始化 'x' 
x.initializer.run()
# 增加一个'减法sub op', 从'x'减去'a'. 运行'减法op', 输出结果;
sub = tf.sub(x, a)
print sub.eval()  # 直接eval()一下好了, 就不要sess.run()了;
# ==> [-2. -1.]

7.Tensor

TF程序使用’Tensor数据结构’来代表所有的数据, 计算图中, 操作间传递的数据都是Tensor(你可以把TF的Tensor看作是一个n维的数组或列表). 一个Tensor包含一个静态类型***rank***, 和一个***shape***.

(想了解TF是如何处理这些概念的, 参见Rank, Shape, 和Type)


8.变量(More Details)

变量维护图执行过程中的状态信息, 下面的例子演示了如何使用变量实现一个简单的计数器. (参见’变量’章节了解更多细节)

###### 创建图 ######
# 1.创建一个变量, 初始化为标量0;
state = tf.Variable(0, name="counter")
# 2.创建一个op, 其作用是使state增加1;
one       = tf.constant(1)
new_value = tf.add(state, one)
update    = tf.assign(state, new_value)

###### 启动图 ######
# 启动图后, #变量#必须先经过`初始化op`(init op)的初始化,
# 即, 必须增加一个`初始化op`到(默认)图中;
init_op = tf.initialize_all_variables()

# 启动图, 运行op;
with tf.Session() as sess:
  # 运行'init op';
  sess.run(init_op)
  # 打印'state'的初始值;
  print sess.run(state)
  # 运行'state op', 更新'state', 并打印'state'值;
  for _ in range(3):
    sess.run(update)
    print sess.run(state)
# 输出:
# 0
# 1
# 2
# 3

代码中assign()操作是图所描绘的表达式的一部分, 正如add()操作一样, 所以在调用run()执行表达式之前, 它并不会真正地执行(赋值操作). 通常会将一个统计模型中的参数表示为一组变量, 如, 你可以将一个神经网络的权重作为某个变量存储在一个Tensor中. 在训练过程中, 通过重复运行训练图, 更新这个 Tensor. (***原来如此!!!***)


9.Fetch(‘sess.run()的结果’)

为了取回’op的输出内容’, 可以在使用Session对象的run()调用执行图时, 传入一些Tensor(原文写得这么不明白?!)声明一个Tensor(即Python变量), 这些Tensor会帮助你取回结果保存结果Tensor到等号左边的变量中. 在之前的例子里, 我们只取回了单个节点的’state’值, 但也可以取回多个Tensor(如下例):

input1   = tf.constant(3.0)
input2   = tf.constant(2.0)
input3   = tf.constant(5.0)
intermed = tf.add(input2, input3)
mul      = tf.mul(input1, intermed)

with tf.Session():
  result = sess.run([mul, intermed])
  print result
  # 输出('mul'一个结果, 'intermed'一个结果):
  # [array([ 21.], dtype=float32), array([ 7.], dtype=float32)]

(需要获取的多个Tensor值,在’result op’的一次运行中一起获得(而不是逐个去获取))


10.Feed(即’Placeholder’)

上述示例在计算图中引入了Tensor, 它们以常量或变量的形式存储. TF还提供了’feed机制’, 该机制可以临时替代图中的任意操作中的Tensor, 可以对图中任何op提交补丁(补丁: Excuse Me?!), 在后续中插入具体的Tensor值. Feed使用一个Tensor值临时替换一个操作的输出结果. 你可以提供Feed数据作为run()调用的参数. Feed只在调用它的方法内有效, 方法结束, Feed就会消失. (讲得真绕, Feed之意就是在某处Feed数据给需要被Feed的东西)

最常见的用例是将某些特殊的操作指定为’feed’操作, 标记的方法是使用tf.placeholder()为这些操作创建占位符.

input1 = tf.placeholder(tf.types.float32)  # Feed;
input2 = tf.placeholder(tf.types.float32)  # Feed;
output = tf.mul(input1, input2)

with tf.Session() as sess:
  # Feed: feed_dict={output op所需的数据(值)};
  print sess.run([output], feed_dict={input1:[7.], input2:[2.]})
  # 输出: [array([ 14.], dtype=float32)]

(如果没有正确提供Feed, placeholder() op将会报错误. MNIST全连通Feed教程(source code)给出了一个更大规模的使用Feed的例子)


(Note *Ends*, Keep For Future *Fine-Tuning*, :))
References

1.参考CNBLOGS博文

2.参考TENSORFLY社区

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值