TensorFlow Low-Level-APIs Introduction学习笔记

Introduction

本文翻译自tensorflow官方网站的教程,只作为个人学习笔记,请勿用作商业用途。

Setup

使用底层的api进行编程,而不是使用keras这样的高级API,一般来说会让速度快3倍,并且更能深入了解工作机制。在walkthrough这个教程之前先执行下列代码

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import numpy as np
import tensorflow as tf

Tensor Values

tf中数据的核心就是tensor。一个tensor是一系列的数字按照一定的形状组合起来。一个tensor的rank是它的维度,它的shape是每个维度的长度,这两个都是tensor的静态成员变量。

3. # 一个rank为0的tensor,一个标量shape是[]
[1., 2., 3.] # 一个rank为1的tensor,一个向量shape是[3]
[[1., 2., 3.], [4., 5., 6.]] # rank=2的tensor,一个shape=[2,3]的matrix
[[[1., 2., 3.]], [7., 8., 9.]]] # rank=3, shape=[2,1,3]

在tf中使用numpy array来表示tensor的值,注意ndarray并不是tensor而只是用来表示tensor的值。

TensorFlow Core Walkthrough

tf的核心编程分为两个部分:
1. 构建计算图 tf.Graph
2. 运行计算图 tf.Session

Graph

计算图是由一系列的TensorFlow Operation按次序安排成的,计算图主要包含的有:

  • Operation(ops): 图的节点,operation描述了计算,这些计算接受tensor并输出tensor
  • Tensors: 图的边缘,流经计算图的数据。大多数tf函数返回tf.Tensors

tf.Tensor并不包含数值,他们只是作为构成计算图的基本元素

下面是一个构建计算图的例子,其中tf.constant方法并不接受tensor而是接受常量,并返回tensor:

a = tf.constant(3.0, dtype=tf.float32)
b = tf.constant(4.0) # 默认是tf.float32
total = a + b
print(a)
print(b)
print(total)

输出的结果是

Tensor("Const:0", shape=(), dtype=float32)
Tensor("Const_1:0", shape=(), dtype=float32)
Tensor("add:0", shape=(), dtype=float32)

这些输出并不包含数值3.0 4.0,他们(tf.Tensor)仅是为了构建计算图,只是代表这些计算将会被运行。

图中的每个计算都被赋予了一个唯一的name,在实例化的时候就被赋予,也可以显示的指定,tensor一般使用产生这个tensor的operation的名字命名如上面的add:0。

TensorBoard

TensorBoard是tf的一个工具,TensorBoard的一个功能就是可视化计算图,使用少量代码即可做到可视化。首先将计算图保存进TensorBoard的summary file中:

writer = tf.summary.FileWriter('.')
writer.add_graph(tf.get_default_graph())

这会产生一个event文件,保存在当前目录下,文件的命名格式如下:

events.out.tfevents.{timestamp}.{hostname}

然后在一个新的终端中,使用如下命令启动TensorBoard:

tensorboard --logdir

Session

为了使计算图得到执行,实例化tf.Session对象作为一个session(会话)。一个session总括TensorFlow运行时状态,并且通过其来执行TensorFlow operations。如果tf.Graph像一个.py文件,那么tf.Session就是python版的.exe。

下列代码产生一个tf.Session对象并且调用它的run方法去evaluate Tensor total:

sess = tf.Session()
print(sess.run(total))

使用Session.run从计算图上进行追溯并执行计算图,最终得到这个参数tensor的值。这里的输出是numpy.ndarray而不是tensor。

也可以向tf.Session.run中传入多个tensor,run方法可以handle任何tensor的组合无论是以tuples或者dict的方式:

print(sess.run({'ab':(a,b), 'total':total}))

输出和输入是同样的格式

{'total': 7.0, 'ab': (3.0, 4.0)}

每一次执行计算图的时候,每个tensor只有一个值,如下代码:

vec = tf.random_uniform(shape=(3,))
out1 = vec + 1
out2 = vec + 2
print(sess.run(vec))
print(sess.run(vec))
print(sess.run(out1 + out2))

结果是中两次运行vec的结果都不同,因为每次都是重新进行采样,但是第三次中可以看到执行的时候,out1 out2所依赖的vec的值是固定的:

[ 0.52917576  0.64076328  0.68353939]
[ 0.66192627  0.89126778  0.06254101]
(
  array([ 1.88408756,  1.87149239,  1.84057522], dtype=float32),
  array([ 2.88408756,  2.87149239,  2.84057522], dtype=float32)
)

一些TensorFlow函数返回的是tf.Operation而不是tf.Tensor。向run方法中传递Operation的结果是None。run一个Operation可能是为了对计算图才产生一些作用而不是为了返回值,如初始化和训练operation。

Feeding

上面提到的计算图并不是那么有趣,输入输出的都是固定的值。一个计算图应该可以接受其他的输入,使用placeholder来占位,然后过一会再输入:

x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32)
z = x + y

上面的三行代码有点像一个函数,接受两个参数然后再作用在参数上面。执行的时候使用feed_dict来向占位符传递具体的值:

print(sess.run(z, feed_dict={x:3, y:4.5}))
print(sess.run(z, feed_dict={x:[1, 3], y:[2, 4]}))

输出的结果是:

7.5
[ 3. 7.]

注意到feed_dict可以被用来对计算图中的tensor进行覆盖。placeholder和tf.Tensor之间的区别就是如果不feed给他们数值那么会报错。

Datasets

placeholder在简单的实验的时候比较方便,大量的数据一般都使用Datasets向模型输入数据流。

可以使用make_one_shot_iterator方法来创建一个对数据集的迭代器。下面代码中每次运行run的时候next_item将返回从my_data得到的一行数据:

my_data = [
    [0, 1],
    [2, 3],
    [4, 5],
    [6, 7],
]
slices = tf.data.Dataset.from_tensor_slices(my_data)
next_item = slices.make_one_shot_iterator().get_next()

当到end of the data stream会让Dataset抛出OutOfRangeError。下面的例子从数据集中取数据直到end:

while True:
    try:
        print(sess.run(next_item))
    except tf.errors.OutOfRangeError:
        break

如果Dataset依赖于稳定的operation而不是直接的数值,那么可能需要在使用前先初始化iterator:

r = tf.random_normal([10, 3]) # shape=[10, 3]
# 构建dataset对数据进行封装
dataset = tf.data.Dataset.from_tensor_slices(r)
# 从dataset构建迭代器
iterator = dataset.make_initializable_iterator()
# 从迭代器得到下一行数据
next_row = iterator.get_next() # tf.Tensor

# 对迭代器进行初始化,应该是让迭代器每次取数据的时候不会重新执行
# tf.random_normal
sess.run(iterator.initializer)
while True:
    try:
        print(sess.run(next_row))
    except tf.errors.OutOfRangeError:
        break

Layer

一个可训练的模型必须能够更新参数来获得不同的输出。Layers就是TensorFlow中作为计算图中可训练的节点。

Layers包集成了变量和其他的对变量的操作,如densely-connected layers就执行卷积的操作然后再激活,其中权值的连接由layer管理。

Creating Layers

下面的代码产生了一个Dense Layer,接受一个batch的输入向量,并且对每个向量产生一个值。为了让layer作用于输入,将layer看成函数一样:

x = tf.placeholder(tf.float32, shape=[None, 3])
linear_model = tf.layers.Dense(units=1)
y = linear_model(x)

layer会审查输入的属性来决定中间变量以及输出的变量的shape。所以这里的placeholder必须声明shape,这样layer才能够工作。

现在已经定义了y这个对x计算的层,在开始feed数据进行计算之前还需要对layer进行输出化。

Initializing Layers

layer中包含的参数必须被输出化,虽然也可以单独对每个变量进行初始化,但是global初始化更容易:

init = tf.global_variables_initializer()
sess.run(init)

调用tf.global_variables_initializer只是返回了一个operation,这个operation还需要通过tf.Session.run()得到执行 global_variables_initializer只初始化这个Operation创建之前就已经声明好的变量。所以初始化应该最后声明。

Excuting Layers

初始化之后就可以执行计算图,让上面声明的linear_model输出tensor:

print(sess.run(y, {x:[[1,2,3], [4,5,6]]}))

将会产生含有两个元素的输出向量:

[[-3.41378999]
 [-9.14999008]]
Layer Function shortcuts

对于每个像tf.layers.Dense这样的layer类,tf也提供了一个快捷的函数形式来代替tf.layers.dense。他们的唯一区别是快捷函数每次被调用都会创建layer,下列代码等价于上面的例子:

x = tf.placeholder(tf.float32, shape=[None, 3])
y = tf.layers.dense(x, units=1)

init = tf.global_variables_initializer()
sess.run(init)

print(sess.run(y, {x:[[1,2,3], [4,5,6]]}))

但是这种方式不利于debug,不利于自省,这样产生的layer无法再次使用。

Feature columns

Feature column表示数据中的一个特征。feature column可以表示像height这样的数量,或者表示像’eye_color’这样的类别,其值是从一组离散的可能性(如’蓝色’, ‘棕色’, ‘绿色’)中得到的。在输入模型之前被转换成一个数字序列,feature column对数据的抽象使得你可以将其看待成语义上的向量,而不用管具体内容。你也可以自己指定由原始数据到特征向量之间的转换,可以自己指定要包含的数据。

详细请看TensorFlow原始教程。

Training

熟悉了TensorFlow的基本内核之后,就可以开始尝试训练一个回归模型。

Define the data

首先定义一些输入数据x,以及标签y_true.

x = tf.constant([[1], [2], [3], [4]], dtype=tf.float32)
y_true = tf.constant([[0], [-1], [-2], [-3]], dtype=tf.float32)
Define the model

然后构建一个简单的线性模型,只有一个输出

linear_model = tf.layers.Dense(units=1)
y_pred = linear_model(x)

然后使用如下代码进行计算

sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)

print(sess.run(y_pred))

模型并没有被训练,所以预测出来的结果并不是很好,你可能得到下面这样的输出:

[[ 0.02631879]
 [ 0.05263758]
 [ 0.07895637]
 [ 0.10527515]]
Loss

为了优化一个模型,必须定义loss函数,这里使用均方误差作为演示。当然可以自己实现均方误差,但是tf.losses模块已经把很多的loss函数都封装好了可以直接使用:

loss = tf.losses.mean_squared_error(labels=y_true, predictions=y_pred)

print(sess.run(loss))

可能会输出

2.23962
Training

TensorFlow封装好了各种标准的优化器,他们都是tf.train.Optimizer的子类。他们迭代式的更新每个参数来让loss下降。最简单的是gradient descent,实现的类是tf.train.GraidentDescentOptimizer。

optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minize(loss)

上面的两行代码定义了所有需要进行优化的操作,并且返回一个train的tf.Operatin。当run这个operation的时候就在执行优化了。

for i in range(100):
    _, loss_value = sess.run((train, loss))# _ 是None
    print(loss_value)

输出可能是这个样子的

1.35659
1.00412
0.759167
0.588829
0.470264
0.387626
0.329918
0.289511
0.261112
0.241046
...
Colplete program

完整的代码如下

x = tf.constant([[1], [2], [3], [4]], dtype=tf.float32)
y_true = tf.constant([[0], [-1], [-2], [-3]], dtype=tf.float32)

linear_model = tf.layers.Dense(units=1)

y_pred = linear_model(x)
loss = tf.losses.mean_squared_error(labels=y_true, predictions=y_pred)

optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)

init = tf.global_variables_initializer()

sess = tf.Session()
sess.run(init)
for i in range(100):
  _, loss_value = sess.run((train, loss))
  print(loss_value)

print(sess.run(y_pred))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值