精通 TensorFlow 1.x(五)
十五、TensorFlow 集群的分布式模型
之前我们学习了如何使用 Kubernetes,Docker 和 TensorFlow 服务在生产中大规模运行 TensorFlow 模型。 TensorFlow 服务并不是大规模运行 TensorFlow 模型的唯一方法。 TensorFlow 提供了另一种机制,不仅可以运行,还可以在多个节点或同一节点上的不同节点和不同设备上训练模型。 在第 1 章,TensorFlow 101 中,我们还学习了如何在不同设备上放置变量和操作。在本章中,我们将学习如何分发 TensorFlow 模型以在多个节点上的多个设备上运行。
在本章中,我们将介绍以下主题:
- 分布式执行策略
- TensorFlow 集群
- 数据并行模型
- 对分布式模型的异步和同步更新
分布式执行策略
为了在多个设备或节点上分发单个模型的训练,有以下策略:
- 模型并行:将模型划分为多个子图,并将单独的图放在不同的节点或设备上。子图执行计算并根据需要交换变量。
- 数据并行:将数据分组并在多个节点或设备上运行相同的模型,并在主节点上组合参数。因此,工作节点在批量数据上训练模型并将参数更新发送到主节点,也称为参数服务器。
上图显示了数据并行方法,其中模型副本分批读取数据分区并将参数更新发送到参数服务器,参数服务器将更新的参数发送回模型副本以进行下一次批量计算的更新。
在 TensorFlow 中,有两种方法可以在数据并行策略下在多个节点/设备上实现模型的复制:
- 图中复制:在这种方法中,有一个客户端任务拥有模型参数,并将模型计算分配给多个工作任务。
- 图之间复制:在这种方法中,每个客户端任务都连接到自己的工作者以分配模型计算,但所有工作器都更新相同的共享模型。在此模型中,TensorFlow 会自动将一个工作器指定为主要工作器,以便模型参数仅由主要工作器初始化一次。
在这两种方法中,参数服务器上的参数可以通过两种不同的方式更新:
- 同步更新:在同步更新中,参数服务器等待在更新梯度之前从所有工作器接收更新。参数服务器聚合更新,例如通过计算所有聚合的平均值并将其应用于参数。更新后,参数将同时发送给所有工作器。这种方法的缺点是一个慢工作者可能会减慢每个人的更新速度。
- 异步更新:在异步更新中,工作器在准备好时将更新发送到参数服务器,然后参数服务器在接收更新时应用更新并将其发回。这种方法的缺点是,当工作器计算参数并发回更新时,参数可能已被其他工作器多次更新。这个问题可以通过几种方法来减轻,例如降低批量大小或降低学习率。令人惊讶的是,异步方法甚至可以工作,但实际上,它们确实有效!
TensorFlow 集群
TensorFlow(TF)集群是一种实现我们刚刚讨论过的分布式策略的机制。在逻辑层面,TF 集群运行一个或多个作业,并且每个作业由一个或多个任务组成。因此,工作只是任务的逻辑分组。在进程级别,每个任务都作为 TF 服务器运行。在机器级别,每个物理机器或节点可以通过运行多个服务器(每个任务一个服务器)来运行多个任务。客户端在不同的服务器上创建图,并通过调用远程会话在一台服务器上开始执行图。
作为示例,下图描绘了连接到名为m1的两个作业的两个客户端:
这两个节点分别运行三个任务,作业w1分布在两个节点上,而其他作业包含在节点中。
TF 服务器实现为两个进程:主控制器和工作器。 主控制器与其他任务协调计算,工作器是实际运行计算的工作器。 在更高级别,您不必担心 TF 服务器的内部。 出于我们的解释和示例的目的,我们将仅涉及 TF 任务。
要以数据并行方式创建和训练模型,请使用以下步骤:
-
定义集群规范
-
创建服务器以承载任务
-
定义要分配给参数服务器任务的变量节点
-
定义要在所有工作任务上复制的操作节点
-
创建远程会话
-
在远程会话中训练模型
-
使用该模型进行预测
定义集群规范
要创建集群,首先要定义集群规范。集群规范通常包含两个作业:ps用于创建参数服务器任务,worker用于创建工作任务。worker和ps作业包含运行各自任务的物理节点列表。举个例子:
clusterSpec = tf.train.ClusterSpec({
'ps': [
'master0.neurasights.com:2222', # /job:ps/task:0
'master1.neurasights.com:2222' # /job:ps/task:1
]
'worker': [
'worker0.neurasights.com:2222', # /job:worker/task:0
'worker1.neurasights.com:2222', # /job:worker/task:1
'worker0.neurasights.com:2223', # /job:worker/task:2
'worker1.neurasights.com:2223' # /job:worker/task:3
]
})
该规范创建了两个作业,作业ps中的两个任务分布在两个物理节点上,作业worker中的四个任务分布在两个物理节点上。
在我们的示例代码中,我们在不同端口上的 localhost 上创建所有任务:
ps = [
'localhost:9001', # /job:ps/task:0
]
workers = [
'localhost:9002', # /job:worker/task:0
'localhost:9003', # /job:worker/task:1
'localhost:9004', # /job:worker/task:2
]
clusterSpec = tf.train.ClusterSpec({'ps': ps, 'worker': workers})
正如您在代码中的注释中所看到的,任务通过/job:<job name>/task:<task index>标识。
创建服务器实例
由于集群每个任务包含一个服务器实例,因此在每个物理节点上,通过向服务器传递集群规范,它们自己的作业名称和任务索引来启动服务器。服务器使用集群规范来确定计算中涉及的其他节点。
server = tf.train.Server(clusterSpec, job_name="ps", task_index=0)
server = tf.train.Server(clusterSpec, job_name="worker", task_index=0)
server = tf.train.Server(clusterSpec, job_name="worker", task_index=1)
server = tf.train.Server(clusterSpec, job_name="worker", task_index=2)
在我们的示例代码中,我们有一个 Python 文件可以在所有物理机器上运行,包含以下内容:
server = tf.train.Server(clusterSpec,
job_name=FLAGS.job_name,
task_index=FLAGS.task_index,
config=config
)
在此代码中,job_name和task_index取自命令行传递的参数。包tf.flags是一个花哨的解析器,可以访问命令行参数。 Python 文件在每个物理节点上执行如下(如果您仅使用本地主机,则在同一节点上的单独终端中执行):
# the model should be run in each physical node
# using the appropriate arguments
$ python3 model.py --job_name='ps' --task_index=0
$ python3 model.py --job_name='worker' --task_index=0
$ python3 model.py --job_name='worker' --task_index=1
$ python3 model.py --job_name='worker' --task_index=2
为了在任何集群上运行代码具有更大的灵活性,您还可以通过命令行传递运行参数服务器和工作程序的计算机列表:-ps='localhost:9001' --worker='localhost:9002,localhost:9003,``localhost:9004'。您需要解析它们并在集群规范字典中正确设置它们。
为确保我们的参数服务器仅使用 CPU 而我们的工作器任务使用 GPU,我们使用配置对象:
config = tf.ConfigProto()
config.allow_soft_placement = True
if FLAGS.job_name=='ps':
#print(config.device_count['GPU'])
config.device_count['GPU']=0
server = tf.train.Server(clusterSpec,
job_name=FLAGS.job_name,
task_index=FLAGS.task_index,
config=config
)
server.join()
sys.exit('0')
elif FLAGS.job_name=='worker':
config.gpu_options.per_process_gpu_memory_fraction = 0.2
server = tf.train.Server(clusterSpec,
job_name=FLAGS.job_name,
task_index=FLAGS.task_index,
config=config
当工作器执行模型训练并退出时,参数服务器等待server.join()。
这就是我们的 GPU 在所有四台服务器运行时的样子:
定义服务器和设备之间的参数和操作
您可以使用我们在第 1 章中使用的tf.device()函数,将参数放在ps任务和worker任务上图的计算节点上。
请注意,您还可以通过将设备字符串添加到任务字符串来将图节点放置在特定设备上,如下所示:/job:<job name>/task:<task index>/device:<device type>:<device index>.
对于我们的演示示例,我们使用 TensorFlow 函数tf.train.replica_device_setter()来放置变量和操作。
- 首先,我们将工作器设备定义为当前工作器:
worker_device='/job:worker/task:{}'.format(FLAGS.task_index)
- 接下来,使用
replica_device_setter定义设备函数,传递集群规范和当前工作设备。replica_device_setter函数从集群规范中计算出参数服务器,如果有多个参数服务器,则默认情况下以循环方式在它们之间分配参数。参数放置策略可以更改为tf.contrib包中的用户定义函数或预构建策略。
device_func = tf.train.replica_device_setter(
worker_device=worker_device,cluster=clusterSpec)
- 最后,我们在
tf.device(device_func)块内创建图并训练它。对于同步更新和异步更新,图的创建和训练是不同的,因此我们将在两个单独的小节中介绍这些内容。
定义并训练图以进行异步更新
如前所述,并在此处的图中显示,在异步更新中,所有工作任务在准备就绪时发送参数更新,参数服务器更新参数并发回参数。参数更新没有同步或等待或聚合:
The full code for this example is in
ch-15_mnist_dist_async.py. You are encouraged to modify and explore the code with your own datasets.
对于异步更新,将使用以下步骤创建和训练图:
- 图的定义在
with块内完成:
with tf.device(device_func):
- 使用内置的 TensorFlow 函数创建全局步骤变量:
global_step = tf.train.get_or_create_global_step()
- 此变量也可以定义为:
tf.Variable(0,name='global_step',trainable=False)
- 像往常一样定义数据集,参数和超参数:
x_test = mnist.test.images
y_test = mnist.test.labels
n_outputs = 10 # 0-9 digits
n_inputs = 784 # total pixels
learning_rate = 0.01
n_epochs = 50
batch_size = 100
n_batches = int(mnist.train.num_examples/batch_size)
n_epochs_print=10
- 像往常一样定义占位符,权重,偏差,对率,交叉熵,损失操作,训练操作,准确率:
# input images
x_p = tf.placeholder(dtype=tf.float32,
name='x_p',
shape=[None, n_inputs])
# target output
y_p = tf.placeholder(dtype=tf.float32,
name='y_p',
shape=[None, n_outputs])
w = tf.Variable(tf.random_normal([n_inputs, n_outputs],
name='w'
)
)
b = tf.Variable(tf.random_normal([n_outputs],
name='b'
)
)
logits = tf.matmul(x_p,w) + b
entropy_op = tf.nn.softmax_cross_entropy_with_logits(labels=y_p,
logits=logits
)
loss_op = tf.reduce_mean(entropy_op)
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
train_op = optimizer.minimize(loss_op,global_step=global_step)
correct_pred = tf.equal(tf.argmax(logits, 1), tf.argmax(y_p, 1))
accuracy_op = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
当我们学习如何构建同步更新时,这些定义将会改变。
- TensorFlow 提供了一个主管类,可以帮助创建训练会话,在分布式训练设置中非常有用。创建一个
supervisor对象,如下所示:
init_op = tf.global_variables_initializer
sv = tf.train.Supervisor(is_chief=is_chief,
init_op = init_op(),
global_step=global_step)
- 使用
supervisor对象创建会话并像往常一样在此会话块下运行训练:
with sv.prepare_or_wait_for_session(server.target) as mts:
lstep = 0
for epoch in range(n_epochs):
for batch in range(n_batches):
x_batch, y_batch = mnist.train.next_batch(batch_size)
feed_dict={x_p:x_batch,y_p:y_batch}
_,loss,gstep=mts.run([train_op,loss_op,global_step],
feed_dict=feed_dict)
lstep +=1
if (epoch+1)%n_epochs_print==0:
print('worker={},epoch={},global_step={}, \
local_step={},loss={}'.
format(FLAGS.task_index,epoch,gstep,lstep,loss))
feed_dict={x_p:x_test,y_p:y_test}
accuracy = mts.run(accuracy_op, feed_dict=feed_dict)
print('worker={}, final accuracy = {}'
.format(FLAGS.task_index,accuracy))
在启动参数服务器时,我们得到以下输出:
$ python3 ch-15_mnist_dist_async.py --job_name='ps' --task_index=0
I tensorflow/core/common_runtime/gpu/gpu_device.cc:1030] Found device 0 with properties:
name: Quadro P5000 major: 6 minor: 1 memoryClockRate(GHz): 1.506
pciBusID: 0000:01:00.0
totalMemory: 15.89GiB freeMemory: 15.79GiB
I tensorflow/core/common_runtime/gpu/gpu_device.cc:1120] Creating TensorFlow device (/device:GPU:0) -> (device: 0, name: Quadro P5000, pci bus id: 0000:01:00.0, compute capability: 6.1)
E1213 16:50:14.023235178 27224 ev_epoll1_linux.c:1051] grpc epoll fd: 23
I tensorflow/core/distributed_runtime/rpc/grpc_channel.cc:215] Initialize GrpcChannelCache for job ps -> {0 -> localhost:9001}
I tensorflow/core/distributed_runtime/rpc/grpc_channel.cc:215] Initialize GrpcChannelCache for job worker -> {0 -> localhost:9002, 1 -> localhost:9003, 2 -> localhost:9004}
I tensorflow/core/distributed_runtime/rpc/grpc_server_lib.cc:324] Started server with target: grpc://localhost:9001
在启动工作任务时,我们得到以下三个输出:
工作器 1 的输出:
$ python3 ch-15_mnist_dist_async.py --job_name='worker' --task_index=0
I tensorflow/core/common_runtime/gpu/gpu_device.cc:1030] Found device 0 with properties:
name: Quadro P5000 major: 6 minor: 1 memoryClockRate(GHz): 1.506
pciBusID: 0000:01:00.0
totalMemory: 15.89GiB freeMemory: 9.16GiB
I tensorflow/core/common_runtime/gpu/gpu_device.cc:1120] Creating TensorFlow device (/device:GPU:0) -> (device: 0, name: Quadro P5000, pci bus id: 0000:01:00.0, compute capability: 6.1)
E1213 16:50:37.516609689 27507 ev_epoll1_linux.c:1051] grpc epoll fd: 23
I tensorflow/core/distributed_runtime/rpc/grpc_channel.cc:215] Initialize GrpcChannelCache for job ps -> {0 -> localhost:9001}
I tensorflow/core/distributed_runtime/rpc/grpc_channel.cc:215] Initialize GrpcChannelCache for job worker -> {0 -> localhost:9002, 1 -> localhost:9003, 2 -> localhost:9004}
I tensorflow/core/distributed_runtime/rpc/grpc_server_lib.cc:324] Started server with target: grpc://localhost:9002
I tensorflow/core/distributed_runtime/master_session.cc:1004] Start master session 1421824c3df413b5 with config: gpu_options { per_process_gpu_memory_fraction: 0.2 } allow_soft_placement: true
worker=0,epoch=9,global_step=10896, local_step=5500, loss = 1.2575616836547852
worker=0,epoch=19,global_step=22453, local_step=11000, loss = 0.7158586382865906
worker=0,epoch=29,global_step=39019, local_step=16500, loss = 0.43712112307548523
worker=0,epoch=39,global_step=55513, local_step=22000, loss = 0.3935799300670624
worker=0,epoch=49,global_step=72002, local_step=27500, loss = 0.3877961337566376
worker=0, final accuracy = 0.8865000009536743
工作器 2 的输出:
$ python3 ch-15_mnist_dist_async.py --job_name='worker' --task_index=1
I tensorflow/core/common_runtime/gpu/gpu_device.cc:1030] Found device 0 with properties:
name: Quadro P5000 major: 6 minor: 1 memoryClockRate(GHz): 1.506
pciBusID: 0000:01:00.0
totalMemory: 15.89GiB freeMemory: 12.43GiB
I tensorflow/core/common_runtime/gpu/gpu_device.cc:1120] Creating TensorFlow device (/device:GPU:0) -> (device: 0, name: Quadro P5000, pci bus id: 0000:01:00.0, compute capability: 6.1)
E1213 16:50:36.684334877 27461 ev_epoll1_linux.c:1051] grpc epoll fd: 23
I tensorflow/core/distributed_runtime/rpc/grpc_channel.cc:215] Initialize GrpcChannelCache for job ps -> {0 -> localhost:9001}
I tensorflow/core/distributed_runtime/rpc/grpc_channel.cc:215] Initialize GrpcChannelCache for job worker -> {0 -> localhost:9002, 1 -> localhost:9003, 2 -> localhost:9004}
I tensorflow/core/distributed_runtime/rpc/grpc_server_lib.cc:324] Started server with target: grpc://localhost:9003
I tensorflow/core/distributed_runtime/master_session.cc:1004] Start master session 2bd8a136213a1fce with config: gpu_options { per_process_gpu_memory_fraction: 0.2 } allow_soft_placement: true
worker=1,epoch=9,global_step=11085, local_step=5500, loss = 0.6955764889717102
worker=1,epoch=19,global_step=22728, local_step=11000, loss = 0.5891970992088318
worker=1,epoch=29,global_step=39074, local_step=16500, loss = 0.4183048903942108
worker=1,epoch=39,global_step=55599, local_step=22000, loss = 0.32243454456329346
worker=1,epoch=49,global_step=72105, local_step=27500, loss = 0.5384714007377625
worker=1, final accuracy = 0.8866000175476074
工作器 3 的输出:
$ python3 ch-15_mnist_dist_async.py --job_name='worker' --task_index=2
I tensorflow/core/common_runtime/gpu/gpu_device.cc:1030] Found device 0 with properties:
name: Quadro P5000 major: 6 minor: 1 memoryClockRate(GHz): 1.506
pciBusID: 0000:01:00.0
totalMemory: 15.89GiB freeMemory: 15.70GiB
I tensorflow/core/common_runtime/gpu/gpu_device.cc:1120] Creating TensorFlow device (/device:GPU:0) -> (device: 0, name: Quadro P5000, pci bus id: 0000:01:00.0, compute capability: 6.1)
E1213 16:50:35.568349791 27449 ev_epoll1_linux.c:1051] grpc epoll fd: 23
I tensorflow/core/distributed_runtime/rpc/grpc_channel.cc:215] Initialize GrpcChannelCache for job ps -> {0 -> localhost:9001}
I tensorflow/core/distributed_runtime/rpc/grpc_channel.cc:215] Initialize GrpcChannelCache for job worker -> {0 -> localhost:9002, 1 -> localhost:9003, 2 -> localhost:9004}
I tensorflow/core/distributed_runtime/rpc/grpc_server_lib.cc:324] Started server with target: grpc://The full code for this example is in ch-15_mnist_dist_sync.py. You are encouraged to modify and explore the code with your own datasets.localhost:9004
I tensorflow/core/distributed_runtime/master_session.cc:1004] Start master session cb0749c9f5fc163e with config: gpu_options { per_process_gpu_memory_fraction: 0.2 } allow_soft_placement: true
I tensorflow/core/distributed_runtime/master_session.cc:1004] Start master session 55bf9a2b9718a571 with config: gpu_options { per_process_gpu_memory_fraction: 0.2 } allow_soft_placement: true
worker=2,epoch=9,global_step=37367, local_step=5500, loss = 0.8077645301818848
worker=2,epoch=19,global_step=53859, local_step=11000, loss = 0.26333487033843994
worker=2,epoch=29,global_step=70299, local_step=16500, loss = 0.6506651043891907
worker=2,epoch=39,global_step=76999, local_step=22000, loss = 0.20321622490882874
worker=2,epoch=49,global_step=82499, local_step=27500, loss = 0.4170967936515808
worker=2, final accuracy = 0.8894000053405762
我们打印了全局步骤和本地步骤。全局步骤表示所有工作器任务的步数,而本地步骤是该工作器任务中的计数,这就是为什么本地任务计数高达 27,500 并且每个工作器的每个周期都相同,但是因为工作器正在做按照自己的步骤采取全局性措施,全局步骤的数量在周期或工作器之间没有对称性或模式。此外,我们发现每个工作器的最终准确率是不同的,因为每个工作器在不同的时间执行最终的准确率,当时有不同的参数。
定义并训练图以进行同步更新
如前所述,并在此处的图中描述,在同步更新中,任务将其更新发送到参数服务器,ps任务等待接收所有更新,聚合它们,然后更新参数。工作任务在继续下一次计算参数更新迭代之前等待更新:
此示例的完整代码位于ch-15_mnist_dist_sync.py中。建议您使用自己的数据集修改和浏览代码。
对于同步更新,需要对代码进行以下修改:
- 优化器需要包装在
SyncReplicaOptimizer中。因此,在定义优化器后,添加以下代码:
# SYNC: next line added for making it sync update
optimizer = tf.train.SyncReplicasOptimizer(optimizer,
replicas_to_aggregate=len(workers),
total_num_replicas=len(workers),
)
- 之后应该像以前一样添加训练操作:
train_op = optimizer.minimize(loss_op,global_step=global_step)
- 接下来,添加特定于同步更新方法的初始化函数定义:
if is_chief:
local_init_op = optimizer.chief_init_op()
else:
local_init_op = optimizer.local_step_init_op()
chief_queue_runner = optimizer.get_chief_queue_runner()
init_token_op = optimizer.get_init_tokens_op()
- 使用两个额外的初始化函数也可以不同地创建
supervisor对象:
# SYNC: sv is initialized differently for sync update
sv = tf.train.Supervisor(is_chief=is_chief,
init_op = tf.global_variables_initializer(),
local_init_op = local_init_op,
ready_for_local_init_op = optimizer.ready_for_local_init_op,
global_step=global_step)
- 最后,在训练的会话块中,我们初始化同步变量并启动队列运行器(如果它是主要的工作者任务):
# SYNC: if block added to make it sync update
if is_chief:
mts.run(init_token_op)
sv.start_queue_runners(mts, [chief_queue_runner])
其余代码与异步更新保持一致。
用于支持分布式训练的 TensorFlow 库和函数正在不断发展。 因此,请注意添加的新函数或函数签名的更改。 在撰写本书的时候,我们使用了 TensorFlow 1.4。
总结
在本章中,我们学习了如何使用 TensorFlow 集群在多台机器和设备上分发模型的训练。我们还学习了 TensorFlow 代码分布式执行的模型并行和数据并行策略。
参数更新可以与参数服务器的同步或异步更新共享。我们学习了如何为同步和异步参数更新实现代码。借助本章中学到的技能,您将能够构建和训练具有非常大的数据集的非常大的模型。
在下一章中,我们将学习如何在运行 iOS 和 Android 平台的移动和嵌入式设备上部署 TensorFlow 模型。
{% raw %}
十六、移动和嵌入式平台上的 TensorFlow 模型
TensorFlow 模型还可用于在移动和嵌入式平台上运行的应用。 TensorFlow Lite 和 TensorFlow Mobile 是资源受限移动设备的两种 TensorFlow。与 TensorFlow Mobile 相比,TensorFlow Lite 支持功能的子集。由于较小的二进制大小和较少的依赖项,TensorFlow Lite 可以获得更好的表现。
要将 TensorFlow 集成到您的应用中,首先,使用我们在整本书中提到的技术训练模型,然后保存模型。现在可以使用保存的模型在移动应用中进行推理和预测。
要了解如何在移动设备上使用 TensorFlow 模型,在本章中我们将介绍以下主题:
- 移动平台上的 TensorFlow
- Android 应用中的 TFMobile
- Android 上的 TFMobile 演示
- iOS 上的 TFMobile 演示
- TensorFlow Lite
- Android 上的 TFLite 演示
- iOS 上的 TFLite 演示
移动平台上的 TensorFlow
TensorFlow 可以集成到移动应用中,用于涉及以下一项或多项机器学习任务的许多用例:
- 语音识别
- 图像识别
- 手势识别
- 光学字符识别
- 图像或文本分类
- 图像,文本或语音合成
- 对象识别
要在移动应用上运行 TensorFlow,我们需要两个主要成分:
- 经过训练和保存的模型,可用于预测
- TensorFlow 二进制文件,可以接收输入,应用模型,生成预测,并将预测作为输出发送
高级架构如下图所示:
移动应用代码将输入发送到 TensorFlow 二进制文件,该二进制文件使用训练的模型来计算预测并将预测发回。
Android 应用中的 TFMobile
TensorFlow 生态系统使其能够通过接口类TensorFlowInferenceInterface,和 jar 文件libandroid_tensorflow_inference_java.jar中的 TensorFlow Java API 在 Android 应用中使用。您可以使用 JCenter 中的 jar 文件,从ci.tensorflow.org下载预编译的 jar,也可以自己构建。
推理接口已作为 JCenter 包提供,可以通过将以下代码添加到build.gradle文件中包含在 Android 项目中:
allprojects {
repositories {
jcenter()
}
}
dependencies {
compile 'org.tensorflow:tensorflow-android:+'
}
您可以按照此链接中的说明使用 Bazel 或 Cmake 自行构建它们,而不是使用 JCenter 中的预构建二进制文件。
在 Android 项目中配置 TF 库后,您可以通过以下四个步骤调用 TF 模型:
- 加载模型:
TensorFlowInferenceInterface inferenceInterface =
new TensorFlowInferenceInterface(assetManager, modelFilename);
- 将输入数据发送到 TensorFlow 二进制文件:
inferenceInterface.feed(inputName,
floatValues, 1, inputSize, inputSize, 3);
- 运行预测或推理:
inferenceInterface.run(outputNames, logStats);
- 接收 TensorFlow 二进制文件的输出:
inferenceInterface.fetch(outputName, outputs);
Android 上的 TFMobile 演示
在本节中,我们将学习如何重新创建 TensorFlow 团队在其官方仓库中提供的 Android 演示应用。 Android 演示将在您的 Android 设备上安装以下四个应用:
TF Classify:这是一个对象识别应用,用于识别设备摄像头输入中的图像,并在其中一个预定义的类中对其进行分类。它不会学习新类型的图片,但会尝试将它们分类为已经学过的类别之一。该应用使用 Google 预训练的初始模型构建。TF Detect:这是一个物体检测应用,可检测设备相机输入中的多个物体。在连续图像进纸模式下移动相机时,它会继续识别对象。TF Stylize:这是一个样式转移应用,可将选定的预定义样式之一传输到设备相机的输入。TF Speech:这是一个语音识别应用,用于识别您的语音,如果它与应用中的某个预定义命令匹配,则它会在设备屏幕上突出显示该特定命令。
示例演示仅适用于 API 级别大于 21 的 Android 设备,并且该设备必须具有支持FOCUS_MODE_CONTINUOUS_PICTURE的现代相机。如果您的设备相机不支持此功能,则必须添加作者提交给 TensorFlow 的路径。。
在您的设备上构建和部署演示应用的最简单方法是使用 Android Studio。要以这种方式构建它,请按照下列步骤操作:
-
安装 Android Studio。我们通过此链接的说明在 Ubuntu 16.04 上安装了 Android Studio。
-
查看 TensorFlow 仓库,并应用上一篇技巧中提到的补丁。我们假设您检查了主目录中
tensorflow文件夹中的代码。 -
使用 Android Studio,在路径
~/tensorflow/tensorflow/examples/Android中打开 Android 项目。您的屏幕看起来与此类似:
- 从左侧栏中展开 Gradle Scripts 选项,然后打开
build.gradle文件。 - 在
build.gradle文件中,找到def nativeBuildSystem定义并将其设置为'none'。在我们检出的代码版本中,此定义位于第 43 行:
def nativeBuildSystem = 'none'
- 构建演示并在真实或模拟设备上运行它。我们在这些设备上测试了应用:
- 您还可以构建 apk 并在虚拟或实际连接的设备上安装 apk 文件。一旦应用安装在设备上,您将看到我们之前讨论的四个应用:
Android 模拟器中的 TF 示例应用
您还可以按照此链接中的说明使用 Bazel 或 Cmake 从源构建整个演示应用。
iOS 应用中的 TFMobile
TensorFlow 通过以下步骤支持 iOS 应用:
- 通过在项目的根目录中添加名为
Profile的文件,在您的应用中包含 TFMobile。将以下内容添加到Profile:
target 'Name-Of-Your-Project'
pod 'TensorFlow-experimental'
- 运行
pod install命令下载并安装 TensorFlow 实验舱。 - 运行
myproject.xcworkspace命令打开工作区,以便将预测代码添加到应用逻辑中。
要为 iOS 项目创建自己的 TensorFlow 二进制文件,请按照此链接中的说明。
在 iOS 项目中配置 TF 库后,可以通过以下四个步骤调用 TF 模型:
- 加载模型:
PortableReadFileToProto(file_path, &tensorflow_graph);
- 创建会话:
tensorflow::Status s = session->Create(tensorflow_graph);
- 运行预测或推理并获得输出:
std::string input_layer = "input";
std::string output_layer = "output";
std::vector<tensorflow::Tensor> outputs;
tensorflow::Status run_status = session->Run(
{{input_layer, image_tensor}},
{output_layer}, {}, &outputs);
- 获取输出数据:
tensorflow::Tensor* output = &outputs[0];
iOS 上的 TFMobile 演示
要在 iOS 上构建演示,您需要 Xcode 7.3 或更高版本。请按照以下步骤构建 iOS 演示应用:
- 查看主目录中
tensorflow文件夹中的 TensorFlow 代码。 - 打开终端窗口并从主文件夹执行以下命令以下载 InceptionV1 模型,提取标签和图文件,并将这些文件移动到示例应用代码中的数据文件夹中:
$ mkdir -p ~/Downloads
$ curl -o ~/Downloads/inception5h.zip \
https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip \
&& unzip ~/Downloads/inception5h.zip -d ~/Downloads/inception5h
$ cp ~/Downloads/inception5h/* \
~/tensorflow/tensorflow/examples/ios/benchmark/data/
$ cp ~/Downloads/inception5h/* \
~/tensorflow/tensorflow/examples/ios/camera/data/
$ cp ~/Downloads/inception5h/* \
~/tensorflow/tensorflow/examples/ios/simple/data/
- 导航到其中一个示例文件夹并下载实验窗格:
$ cd ~/tensorflow/tensorflow/examples/ios/camera
$ pod install
- 打开 Xcode 工作区:
$ open tf_simple_example.xcworkspace
- 在设备模拟器中运行示例应用。示例应用将显示“运行模型”按钮。相机应用需要连接 Apple 设备,而其他两个也可以在模拟器中运行。
TensorFlow Lite
在编写本书时,TFLite 是该版块中的新手,并且仍处于开发人员视图中。 TFLite 是 TensorFlow Mobile 和 TensorFlow 的一个非常小的子集,因此使用 TFLite 编译的二进制文件非常小,并提供卓越的表现。除了减小二进制文件的大小,TensorFlow 还采用了各种其他技术,例如:
- 内核针对各种设备和移动架构进行了优化
- 计算中使用的值是量化的
- 激活函数是预融合的
- 它利用设备上可用的专用机器学习软件或硬件,例如 Android NN API
在 TFLite 中使用模型的工作流程如下:
- 获取模型:您可以训练自己的模型或选择可从不同来源获得的预训练模型,并按原样使用预训练或使用您自己的数据再训练,或在修改某些部分后再训练该模型。只要您在文件中使用扩展名为
.pb或.pbtxt的训练模型,就可以继续执行下一步。我们在前面的章节中学习了如何保存模型。 - 检查模型:模型文件只包含图的结构,因此需要保存检查点文件。检查点文件包含模型的序列化变量,例如权重和偏差。我们在前面的章节中学习了如何保存检查点。
- 冻结模型:合并检查点和模型文件,也称为冻结图。 TensorFlow 为此步骤提供
freeze_graph工具,可以按如下方式执行:
$ freeze_graph
--input_graph=mymodel.pb
--input_checkpoint=mycheckpoint.ckpt
--input_binary=true
--output_graph=frozen_model.pb
--output_node_name=mymodel_nodes
- 转换模型:需要使用 TensorFlow 提供的
toco工具将步骤 3 中的冻结模型转换为 TFLite 格式:
$ toco
--input_file=frozen_model.pb
--input_format=TENSORFLOW_GRAPHDEF
--output_format=TFLITE
--input_type=FLOAT
--input_arrays=input_nodes
--output_arrays=mymodel_nodes
--input_shapes=n,h,w,c
- 现在,在步骤 4 中保存的
.tflite模型可以在使用 TFLite 二进制文件进行推理的 Android 或 iOS 应用中使用。在您的应用中包含 TFLite 二进制文件的过程不断发展,因此我们建议读者按照此链接中的信息在您的 Android 或 iOS 应用中包含 TFLite 二进制文件。
通常,您可以使用graph_transforms:summarize_graph工具修剪在步骤 1 中获得的模型。 修剪后的模型将仅具有在推理或预测时从输入到输出的路径。仅删除训练或调试所需的任何其他节点和路径(例如保存检查点),从而使最终模型的大小非常小。
官方 TensorFlow 仓库附带 TFLite 演示,该演示使用预训练的mobilenet对来自 1001 类别中的设备相机的输入进行分类。演示应用显示前三个类别的概率。
Android 上的 TFLite 演示
要在 Android 上构建 TFLite 演示,请按照下列步骤操作:
-
安装 Android Studio。我们通过此链接的说明在 Ubuntu 16.04 上安装了 Android Studio。
-
查看 TensorFlow 仓库,并应用上一篇技巧中提到的补丁。我们假设您检查了主目录中
tensorflow文件夹中的代码。 -
使用 Android Studio,从路径
~/tensorflow/tensorflow/contrib/lite/java/demo打开 Android 项目。如果它抱怨缺少 SDK 或 Gradle 组件,请安装这些组件并同步 Gradle。 -
构建项目并使用
API > 21在虚拟设备上运行它。
我们收到了以下警告,但构建成功了。 如果构建失败,您可能希望解决警告:
Warning:The Jack toolchain is deprecated and will not run. To enable support for Java 8 language features built into the plugin, remove 'jackOptions { ... }' from your build.gradle file, and add
android.compileOptions.sourceCompatibility 1.8
android.compileOptions.targetCompatibility 1.8
Future versions of the plugin will not support usage of 'jackOptions' in build.gradle.
To learn more, go to https://d.android.com/r/tools/java-8-support-message.html
Warning:The specified Android SDK Build Tools version (26.0.1) is ignored, as it is below the minimum supported version (26.0.2) for Android Gradle Plugin 3.0.1.
Android SDK Build Tools 26.0.2 will be used.
To suppress this warning, remove "buildToolsVersion '26.0.1'" from your build.gradle file, as each version of the Android Gradle Plugin now has a default version of the build tools.
您还可以使用 Bazel 从源代码构建整个演示应用,其中包含此链接中的说明。
iOS 上的 TFLite 演示
要在 iOS 上构建演示,您需要 Xcode 7.3 或更高版本。请按照以下步骤构建 iOS 演示应用:
- 查看主目录中
tensorflow文件夹中的 TensorFlow 代码。 - 根据此链接中的说明构建适用于 iOS 的 TFLite 二进制文件。
- 导航到示例文件夹并下载 pod:
$ cd ~/tensorflow/tensorflow/contrib/lite/examples/ios/camera
$ pod install
- 打开 Xcode 工作区:
$ open tflite_camera_example.xcworkspace
- 在设备模拟器中运行示例应用。
总结
在本章中,我们学习了在移动应用和设备上使用 TensorFlow 模型。 TensorFlow 提供了两种在移动设备上运行的方式:TFMobile 和 TFLite。我们学习了如何为 iOs 和 Android 构建 TFMobile 和 TFLite 应用。我们在本章中使用了 TensorFlow 演示应用作为示例。鼓励读者探索这些演示应用的源代码,并使用 TFMobile 和 TFLite 通过使用 TensorFlow 构建的机器学习模型为自己的移动应用提供支持。
在下一章中,我们将学习如何在 R 统计软件中使用 TensorFlow 和 RStudio 发布的 R 包。
{% endraw %}
十七、R 中的 TensorFlow 和 Keras
R 是一个开源平台,包括用于统计计算的环境和语言。它还有一个桌面和基于 Web 的 IDE,称为 R Studio。有关 R 的更多信息,请访问此链接。 R 通过提供以下 R 包提供对 TensorFlow 和 Keras 的支持:
tensorflow包提供对 TF 核心 API 的支持tfestimators包提供对 TF 估计器 API 的支持keras包提供对 Keras API 的支持tfruns包用于 TensorBoard 风格的模型和训练类可视化
在本章中,我们将学习如何在 R 中使用 TensorFlow,并将涵盖以下主题:
- 在 R 中安装 TensorFlow 和 Keras 包
- R 中的 TF 核心 API
- R 中的 TF 估计器 API
- R 中的 Keras API
- R 中的 TensorBoard
- R 中的
tfruns包
在 R 中安装 TensorFlow 和 Keras 包
要在 R 中安装支持 TensorFlow 和 Keras 的三个 R 包,请在 R 中执行以下命令。
- 首先,安装
devtools:
install.packages("devtools")
- 安装
tensorflow和tfestimators包:
devtools::install_github("rstudio/tensorflow")
devtools::install_github("rstudio/tfestimators")
- 加载
tensorflow库并安装所需的功能:
library(tensorflow)
install_tensorflow()
- 默认情况下,安装功能会创建虚拟环境并在虚拟环境中安装
tensorflow包。
有四种可用的安装方法,可以使用method参数指定:
auto | 自动选择当前平台的默认值 |
virtualenv | 安装到位于~/.virtualenvs/r-tensorflow的虚拟环境中 |
conda | 安装到名为r-tensorflow的 Anaconda Python 环境中 |
system | 安装到系统 Python 环境中 |
- 默认情况下,安装功能会安装仅限 CPU 的 TensorFlow 版本。要安装 GPU 版本,请使用版本参数:
gpu | 安装tensorflow-gpu |
nightly | 安装每晚 CPU 的版本 |
nightly-gpu | 安装每晚 GPU 构建 |
n.n.n | 安装特定版本,例如 1.3.0 |
n.n.n-gpu | 安装特定版本的 GPU 版本,例如 1.3.0 |
如果您希望 TensorFlow 库使用特定版本的 Python,请使用以下函数或设置TENSORFLOW_PYTHON环境变量:
use_python('/usr/bin/python2')use_virtualenv('~/venv')use_condaenv('conda-env')Sys.setenv(TENSORFLOW_PYTHON='/usr/bin/python2')
We installed TensorFLow in R on Ubuntu 16.04 using the following command:
install_tensorflow(version="gpu") Note that the installation does not support Python 3 at the time of writing this book.
- 安装 Keras 包:
devtools::install_github("rstudio/keras")
- 在虚拟环境中安装 Keras:
library(keras)
install_keras()
- 要安装 GPU 版本,请使用:
install_keras(tensorflow = "gpu")
- 安装
tfruns包:
devtools::install_github("rstudio/tfruns")
R 中的 TF 核心 API
我们在第 1 章中了解了 TensorFlow 核心 API。在 R 中,该 API 使用tensorflow R 包实现。
作为一个例子,我们提供了 MLP 模型的演练,用于在此链接中对来自 MNIST 数据集的手写数字进行分类。
您可以按照 Jupyter R 笔记本中的代码ch-17a_TFCore_in_R。
- 首先,加载库:
library(tensorflow)
- 定义超参数:
batch_size <- 128
num_classes <- 10
steps <- 1000
- 准备数据:
datasets <- tf$contrib$learn$datasets
mnist <- datasets$mnist$read_data_sets("MNIST-data", one_hot = TRUE)
数据从 TensorFlow 数据集库加载,并已标准化为[0, 1]范围。
- 定义模型:
# Create the model
x <- tf$placeholder(tf$float32, shape(NULL, 784L))
W <- tf$Variable(tf$zeros(shape(784L, num_classes)))
b <- tf$Variable(tf$zeros(shape(num_classes)))
y <- tf$nn$softmax(tf$matmul(x, W) + b)
# Define loss and optimizer
y_ <- tf$placeholder(tf$float32, shape(NULL, num_classes))
cross_entropy <- tf$reduce_mean(-tf$reduce_sum(y_ * log(y), reduction_indices=1L))
train_step <- tf$train$GradientDescentOptimizer(0.5)$minimize(cross_entropy)
- 训练模型:
# Create session and initialize variables
sess <- tf$Session()
sess$run(tf$global_variables_initializer())
# Train
for (i in 1:steps) {
batches <- mnist$train$next_batch(batch_size)
batch_xs <- batches[[1]]
batch_ys <- batches[[2]]
sess$run(train_step,
feed_dict = dict(x = batch_xs, y_ = batch_ys))
}
- 评估模型:
correct_prediction <- tf$equal(tf$argmax(y, 1L), tf$argmax(y_, 1L))
accuracy <- tf$reduce_mean(tf$cast(correct_prediction, tf$float32))
score <-sess$run(accuracy,
feed_dict = dict(x = mnist$test$images,
y_ = mnist$test$labels))
cat('Test accuracy:', score, '\n')
输出如下:
Test accuracy: 0.9185
太酷了!
有关tensorflow R 包的更多文档可以在此链接中找到。
R 中的 TF 估计器 API
我们在第 2 章中了解了 TensorFlow 估计器 API。在 R 中,此 API 使用tfestimator R 包实现。
例如,我们提供了 MLP 模型的演练,用于在此链接中对来自 MNIST 数据集的手写数字进行分类。
您可以按照 Jupyter R 笔记本中的代码ch-17b_TFE_Ttimator_in_R。
- 首先,加载库:
library(tensorflow)
library(tfestimators)
- 定义超参数:
batch_size <- 128
n_classes <- 10
n_steps <- 100
- 准备数据:
# initialize data directory
data_dir <- "~/datasets/mnist"
dir.create(data_dir, recursive = TRUE, showWarnings = FALSE)
# download the MNIST data sets, and read them into R
sources <- list(
train = list(
x = "https://storage.googleapis.com/cvdf-datasets/mnist/train-images-idx3-ubyte.gz",
y = "https://storage.googleapis.com/cvdf-datasets/mnist/train-labels-idx1-ubyte.gz"
),
test = list(
x = "https://storage.googleapis.com/cvdf-datasets/mnist/t10k-images-idx3-ubyte.gz",
y = "https://storage.googleapis.com/cvdf-datasets/mnist/t10k-labels-idx1-ubyte.gz"
)
)
# read an MNIST file (encoded in IDX format)
read_idx <- function(file) {
# create binary connection to file
conn <- gzfile(file, open = "rb")
on.exit(close(conn), add = TRUE)
# read the magic number as sequence of 4 bytes
magic <- readBin(conn, what="raw", n=4, endian="big")
ndims <- as.integer(magic[[4]])
# read the dimensions (32-bit integers)
dims <- readBin(conn,what="integer",n=ndims,endian="big")
# read the rest in as a raw vector
data <- readBin(conn,what="raw",n=prod(dims),endian="big")
# convert to an integer vecto
converted <- as.integer(data)
# return plain vector for 1-dim array
if (length(dims) == 1)
return(converted)
# wrap 3D data into matrix
matrix(converted,nrow=dims[1],ncol=prod(dims[-1]),byrow=TRUE)
}
mnist <- rapply(sources,classes="character",how ="list",function(url) {
# download + extract the file at the URL
target <- file.path(data_dir, basename(url))
if (!file.exists(target))
download.file(url, target)
# read the IDX file
read_idx(target)
})
# convert training data intensities to 0-1 range
mnist$train$x <- mnist$train$x / 255
mnist$test$x <- mnist$test$x / 255
从下载的 gzip 文件中读取数据,然后归一化以落入[0, 1]范围。
- 定义模型:
# construct a linear classifier
classifier <- linear_classifier(
feature_columns = feature_columns(
column_numeric("x", shape = shape(784L))
),
n_classes = n_classes # 10 digits
)
# construct an input function generator
mnist_input_fn <- function(data, ...) {
input_fn(
data,
response = "y",
features = "x",
batch_size = batch_size,
...
)
}
- 训练模型:
train(classifier,input_fn=mnist_input_fn(mnist$train),steps=n_steps)
- 评估模型:
evaluate(classifier,input_fn=mnist_input_fn(mnist$test),steps=200)
输出如下:
Evaluation completed after 79 steps but 200 steps was specified
| average_loss | 损失 | global_step | 准确率 |
|---|---|---|---|
| 0.35656 | 45.13418 | 100 | 0.9057 |
太酷!!
有关tensorflow R 包的更多文档可以在此链接中找到
R 中的 Keras API
我们在第 3 章中了解了 Keras API。在 R 中,此 API 使用keras R 包实现。keras R 包实现了 Keras Python 接口的大部分功能,包括顺序 API 和函数式 API。
作为示例,我们提供了 MLP 模型的演练,用于在此链接中对来自 MNIST 数据集的手写数字进行分类。
您可以按照 Jupyter R 笔记本中的代码ch-17c_Keras_in_R。
- 首先,加载库:
library(keras)
- 定义超参数:
batch_size <- 128
num_classes <- 10
epochs <- 30
- 准备数据:
# The data, shuffled and split between train and test sets
c(c(x_train, y_train), c(x_test, y_test)) %<-% dataset_mnist()
x_train <- array_reshape(x_train, c(nrow(x_train), 784))
x_test <- array_reshape(x_test, c(nrow(x_test), 784))
# Transform RGB values into [0,1] range
x_train <- x_train / 255
x_test <- x_test / 255
cat(nrow(x_train), 'train samples\n')
cat(nrow(x_test), 'test samples\n')
# Convert class vectors to binary class matrices
y_train <- to_categorical(y_train, num_classes)
y_test <- to_categorical(y_test, num_classes)
注释是不言自明的:数据从 Keras 数据集库加载,然后转换为 2D 数组并归一化为[0, 1]范围。
- 定义模型:
model <- keras_model_sequential()
model %>%
layer_dense(units=256,activation='relu',input_shape=c(784)) %>%
layer_dropout(rate = 0.4) %>%
layer_dense(units = 128, activation = 'relu') %>%
layer_dropout(rate = 0.3) %>%
layer_dense(units = 10, activation = 'softmax')
summary(model)
model %>% compile(
loss = 'categorical_crossentropy',
optimizer = optimizer_rmsprop(),
metrics = c('accuracy')
)
- 定义和编译顺序模型。我们得到的模型定义如下:
_____________________________________________________
Layer (type) Output Shape Param #
=====================================================
dense_26 (Dense) (None, 256) 200960
_____________________________________________________
dropout_14 (Dropout) (None, 256) 0
_____________________________________________________
dense_27 (Dense) (None, 128) 32896
_____________________________________________________
dropout_15 (Dropout) (None, 128) 0
_____________________________________________________
dense_28 (Dense) (None, 10) 1290
=====================================================
Total params: 235,146
Trainable params: 235,146
Non-trainable params: 0
- 训练模型:
history <- model %>% fit(
x_train, y_train,
batch_size = batch_size,
epochs = epochs,
verbose = 1,
validation_split = 0.2
)
plot(history)
拟合函数的输出存储在历史对象中,其包含来自训练周期的损失和度量值。绘制历史对象中的数据,结果如下:
Training and Validation Accuracy (y-axis) in Epochs (x-axis)
- 评估模型:
score <- model %>% evaluate(
x_test, y_test,
verbose = 0
)
# Output metrics
cat('Test loss:', score[[1]], '\n')
cat('Test accuracy:', score[[2]], '\n')
输出如下:
Test loss: 0.1128517
Test accuracy: 0.9816
太酷!!
R 中的 TensorBoard
您可以按照 Jupyter R 笔记本中的代码ch-17d_TensorBoard_in_R。
您可以使用tensorboard()函数查看 TensorBoard,如下所示:
tensorboard('logs')
这里,'logs'是应该创建 TensorBoard 日志的文件夹。
数据将显示为执行周期并记录数据。在 R 中,收集 TensorBoard 的数据取决于所使用的包:
- 如果您使用的是
tensorflow包,请将tf$summary$scalar操作附加到图中 - 如果您使用的是
tfestimators包,则 TensorBoard 数据会自动写入创建估计器时指定的model_dir参数 - 如果您正在使用
keras包,则必须在使用fit()函数训练模型时包含callback_tensorboard()函数
我们修改了之前提供的 Keras 示例中的训练,如下所示:
# Training the model --------
tensorboard("logs")
history <- model %>% fit(
x_train, y_train,
batch_size = batch_size,
epochs = epochs,
verbose = 1,
validation_split = 0.2,
callbacks = callback_tensorboard("logs")
)
当我们执行笔记本时,我们获得了训练单元的以下输出:
Started TensorBoard at http://127.0.0.1:4233
当我们点击链接时,我们会看到在 TensorBoard 中绘制的标量:
TensorBoad Visualization of Plots
单击 Graphs 选项卡,我们在 TensorBoard 中看到计算图:
TensorBoard 计算图的可视化有关 R 中 TensorBoard 的更多文档,请访问此链接。
R 中的tfruns包
您可以按照 Jupyter R 笔记本中的代码ch-17d_TensorBoard_in_R。
tfruns包是 R 中提供的非常有用的工具,有助于跟踪多次运行以训练模型。对于使用keras tfestimators包在 R 中构建的模型,tfruns包自动捕获运行数据。使用tfruns非常简单易行。只需在 R 文件中创建代码,然后使用training_run()函数执行该文件。例如,如果你有一个mnist_model.R 文件 ,那么在交互式 R 控制台中使用training_run()函数执行它,如下所示:
library(tfruns)
training_run('mnist_model.R')
训练完成后,将自动显示显示运行摘要的窗口。我们从tfruns GitHub 仓库获得的mnist_mlp.R窗口中获得以下输出。
tfruns visualization of the model run
在“查看器”窗口中,输出选项卡包含以下图:
tfruns visualization of the accuracy and loss values
tfruns包将一个插件安装到 RStudio,也可以从Addins菜单选项访问。该包还允许您比较多个运行并将运行报告发布到 RPub 或 RStudio Connect。您还可以选择在本地保存报告。
有关 R 中tfruns包的更多文档,请访问以下链接:
tensorflow.rstudio.com/tools/tfrun…
tensorflow.rstudio.com/tools/tfrun….
总结
在本章中,我们学习了如何在 R 中使用 TensorFlow 核心,TensorFlow 估计器和 Keras 包来构建和训练机器学习模型。我们提供了来自 RStudio 的 MNIST 示例的演练,并提供了有关 TensorFlow 和 Keras R 包的进一步文档的链接。我们还学习了如何使用 R 中的可视化工具 TensorBoard。我们还介绍了一个来自 R Studio 的新工具tfruns,它允许您为多次运行创建报告,分析和比较它们,并在本地保存或发布它们。
直接在 R 中工作的能力很有用,因为大量的生产数据科学和机器学习代码是使用 R 编写的,现在您可以将 TensorFlow 集成到相同的代码库中并在 R 环境中运行它。
在下一章中,我们将学习一些用于调试构建和训练 TensorFlow 模型的代码的技术。
十八、调试 TensorFlow 模型
正如我们在本书中所学到的,TensorFlow 程序用于构建和训练可用于各种任务预测的模型。在训练模型时,您可以构建计算图,运行图以进行训练,并评估图以进行预测。重复这些任务,直到您对模型的质量感到满意为止,然后将图与学习的参数一起保存。在生产中,图是从文件构建或恢复的,并使用参数填充。
构建深度学习模型是一项复杂的技术,TensorFlow API 及其生态系统同样复杂。当我们在 TensorFlow 中构建和训练模型时,有时我们会得到不同类型的错误,或者模型不能按预期工作。例如,您经常看到自己陷入以下一种或多种情况:
- 在损失和指标输出中得到了 NaN
- 即使经过多次迭代,损失或其他指标也没有改善
在这种情况下,我们需要调试使用 TensorFlow API 编写的代码。
要修复代码以使其正常工作,可以使用调试器或平台提供的其他方法和工具,例如 Python 中的 Python 调试器(pdb)和 Linux OS 中的 GNU 调试器(gdb)。当出现问题时,TensorFlow API 还提供一些额外的支持来修复代码。
在本章中,我们将学习 TensorFlow 中可用的其他工具和技术,以帮助调试:
- 使用
tf.Session.run()获取张量值 - 使用
tf.Print()打印张量值 - 用
tf.Assert()断言条件 - 使用 TensorFlow 调试器进行调试(
tfdbg)
使用tf.Session.run()获取张量值
您可以使用tf.Session.run()获取要打印的张量值。这些值作为 NumPy 数组返回,可以使用 Python 语句打印或记录。这是最简单和最简单的方法,最大的缺点是计算图执行所有相关路径,从获取的张量开始,如果这些路径包括训练操作,那么它前进一步或一个周期。
因此,大多数情况下你不会调用tf.Session.run()来获取图中间的张量,但你会执行整个图并获取所有张量,你需要调试的那些张量以及你不需要的张量调试。
函数tf.Session.partial_run()也适用于您可能想要执行图的一部分的情况,但它是一个高度实验性的 API,尚未准备好用于生产。
使用tf.Print()打印张量值
为调试目的打印值的另一个选项是使用tf.Print()。当执行包含tf.Print()节点的路径时,您可以在tf.Print()中包含张量以在标准错误控制台中打印其值。tf.Print()函数具有以下签名:
tf.Print(
input_,
data,
message=None,
first_n=None,
summarize=None,
name=None
)
该函数的参数如下:
input_是一个从函数返回的张量,没有任何操作data是要打印的张量列表message是一个字符串,它作为打印输出的前缀打印出来first_n表示打印输出的步骤数;如果此值为负,则只要执行路径,就始终打印该值summarize表示从张量打印的元素数量;默认情况下,仅打印三个元素
您可以按照 Jupyter 笔记本中的代码ch-18_TensorFlow_Debugging。
让我们修改之前创建的 MNIST MLP 模型来添加print语句:
model = tf.Print(input_=model,
data=[tf.argmax(model,1)],
message='y_hat=',
summarize=10,
first_n=5
)
当我们运行代码时,我们在 Jupyter 的控制台中获得以下内容:
I tensorflow/core/kernels/logging_ops.cc:79] y_hat=[0 0 0 7 0 0 0 0 0 0...]
I tensorflow/core/kernels/logging_ops.cc:79] y_hat=[0 7 7 1 8 7 2 7 7 0...]
I tensorflow/core/kernels/logging_ops.cc:79] y_hat=[4 8 0 6 1 8 1 0 7 0...]
I tensorflow/core/kernels/logging_ops.cc:79] y_hat=[0 0 1 0 0 0 0 5 7 5...]
I tensorflow/core/kernels/logging_ops.cc:79] y_hat=[9 2 2 8 8 6 6 1 7 7...]
使用tf.Print()的唯一缺点是该函数提供了有限的格式化功能。
tf.Assert()
调试 TensorFlow 模型的另一种方法是插入条件断言。tf.Assert()函数需要一个条件,如果条件为假,则打印给定张量的列表并抛出tf.errors.InvalidArgumentError。
tf.Assert()函数具有以下特征:
tf.Assert(
condition,
data,
summarize=None,
name=None
)
- 断言操作不会像
tf.Print()函数那样落入图的路径中。为了确保tf.Assert()操作得到执行,我们需要将它添加到依赖项中。例如,让我们定义一个断言来检查所有输入是否为正:
assert_op = tf.Assert(tf.reduce_all(tf.greater_equal(x,0)),[x])
- 在定义模型时将
assert_op添加到依赖项,如下所示:
with tf.control_dependencies([assert_op]):
# x is input layer
layer = x
# add hidden layers
for i in range(num_layers):
layer = tf.nn.relu(tf.matmul(layer, w[i]) + b[i])
# add output layer
layer = tf.matmul(layer, w[num_layers]) + b[num_layers]
- 为了测试这段代码,我们在第 5 周期之后引入了一个杂质,如下:
if epoch > 5:
X_batch = np.copy(X_batch)
X_batch[0,0]=-2
- 代码运行正常五个周期,然后抛出错误:
epoch: 0000 loss = 6.975991
epoch: 0001 loss = 2.246228
epoch: 0002 loss = 1.924571
epoch: 0003 loss = 1.745509
epoch: 0004 loss = 1.616791
epoch: 0005 loss = 1.520804
-----------------------------------------------------------------
InvalidArgumentError Traceback (most recent call last)
...
InvalidArgumentError: assertion failed: [[-2 0 0]...]
...
除了tf.Assert()函数,它可以采用任何有效的条件表达式,TensorFlow 提供以下断言操作,检查特定条件并具有简单的语法:
assert_equalassert_greaterassert_greater_equalassert_integerassert_lessassert_less_equalassert_negativeassert_none_equalassert_non_negativeassert_non_positiveassert_positiveassert_proper_iterableassert_rankassert_rank_at_leastassert_rank_inassert_same_float_dtypeassert_scalarassert_typeassert_variables_initialized
作为示例,前面提到的示例断言操作也可以写成如下:
assert_op = tf.assert_greater_equal(x,0)
使用 TensorFlow 调试器(tfdbg)的调试
TensorFlow 调试器(tfdbg)与其他常用调试器(如pdb和gdb)的工作方式相同。要使用调试器,该过程通常如下:
- 在代码中的断点处设置要中断的位置并检查变量
- 在调试模式下运行代码
- 当代码在断点处中断时,检查它然后继续下一步
一些调试器还允许您在代码执行时以交互方式观察变量,而不仅仅是在断点处:
- 为了使用
tfdbg,首先导入所需的模块并将会话包装在调试器包装器中:
from tensorflow.python import debug as tfd
with tfd.LocalCLIDebugWrapperSession(tf.Session()) as tfs:
- 接下来,将过滤器附加到会话对象。附加过滤器与在其他调试器中设置断点相同。例如,以下代码附加
tfdbg.has_inf_or_nan过滤器,如果任何中间张量具有nan或inf值,则会中断:
tfs.add_tensor_filter('has_inf_or_nan_filter', tfd.has_inf_or_nan)
- 现在,当代码执行
tfs.run()时,调试器将在控制台中启动调试器接口,您可以在其中运行各种调试器命令来监视张量值。 - 我们提供了在
ch-18_mnist_tfdbg.py文件中试用tfdbg的代码。当我们用python3执行代码文件时,我们看到tfdbg控制台:
python3 ch-18_mnist_tfdbg.py
- 在
tfdbg>提示符下输入命令run -f has_inf_or_nan。代码在第一个周期后中断,因为我们使用np.inf值填充数据:
- 现在您可以使用
tfdbg控制台或可点击界面来检查各种张量的值。例如,我们查看其中一个梯度的值:
您可以在此链接中找到有关使用tfdbg控制台和检查变量的更多信息。
总结
在本章中,我们学习了如何在 TensorFlow 中调试用于构建和训练模型的代码。我们了解到我们可以使用tf.Session.run()将张量作为 NumPy 数组获取。我们还可以通过在计算图中添加tf.Print()操作来打印张量值。我们还学习了,在使用tf.Assert()和其他tf.assert_*操作执行期间,某些条件无法保持时如何引发错误。我们通过对 TensorFlow 调试器(tfdbg)的介绍结束本章,用于设置断点和观察张量值,就像我们在 Python 调试器(pdb)或 GNU 调试器(gdb中调试代码一样) )。
本章将我们的旅程带入一个新的里程碑。我们不希望旅程在此结束,但我们相信旅程刚刚开始,您将进一步扩展和应用本书中获得的知识和技能。
我们非常期待听到您的经验,反馈和建议。
十九、张量处理单元
张量处理单元(TPU)是专用集成电路(ASIC),它实现了针对计算要求而优化的硬件电路深度神经网络。 TPU 基于复杂指令集计算机(CISC)指令集,该指令集实现用于训练深度神经网络的复杂任务的高级指令。 TPU 架构的核心在于优化矩阵运算的脉动数组。
The Architecture of TPUImage from: cloud.google.com/blog/big-da…
TensorFlow 提供了一个编译器和软件栈,可将 API 调用从 TensorFlow 图转换为 TPU 指令。以下框图描述了在 TPU 栈顶部运行的 TensorFlow 模型的架构:
TPU 的 TensorFlow API 位于tf.contrib.tpu模块中。为了在 TPU 上构建模型,使用以下三个 TPU 特定的 TensorFlow 模块:
tpu_config:tpu_config模块允许您创建配置对象,其中包含有关将运行模型的主机的信息。tpu_estimator:tpu_estimator模块将估计器封装在TPUEstimatorSpec类中。要在 TPU 上运行估计器,我们创建此类的对象。tpu_optimizer:tpu_optimizer模块包装优化器。例如,在下面的示例代码中,我们将tpu_optimizer类中的 SGD 优化器包装在tpu_optimizer类中。
例如,以下代码使用 TFEstimator API 为 TPU 上的 MNIST 数据集构建 CNN 模型:
以下代码改编自这个页面。
import tensorflow as tf
from tensorflow.contrib.tpu.python.tpu import tpu_config
from tensorflow.contrib.tpu.python.tpu import tpu_estimator
from tensorflow.contrib.tpu.python.tpu import tpu_optimizer
learning_rate = 0.01
batch_size = 128
def metric_fn(labels, logits):
predictions = tf.argmax(logits, 1)
return {
"accuracy": tf.metrics.precision(
labels=labels, predictions=predictions),
}
def model_fn(features, labels, mode):
if mode == tf.estimator.ModeKeys.PREDICT:
raise RuntimeError("mode {} is not supported yet".format(mode))
input_layer = tf.reshape(features, [-1, 28, 28, 1])
conv1 = tf.layers.conv2d(
inputs=input_layer,
filters=32,
kernel_size=[5, 5],
padding="same",
activation=tf.nn.relu)
pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2],
strides=2)
conv2 = tf.layers.conv2d(
inputs=pool1,
filters=64,
kernel_size=[5, 5],
padding="same",
activation=tf.nn.relu)
pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2],
strides=2)
pool2_flat = tf.reshape(pool2, [-1, 7 * 7 * 64])
dense = tf.layers.dense(inputs=pool2_flat, units=128,
activation=tf.nn.relu)
dropout = tf.layers.dropout(
inputs=dense, rate=0.4,
training=mode == tf.estimator.ModeKeys.TRAIN)
logits = tf.layers.dense(inputs=dropout, units=10)
onehot_labels = tf.one_hot(indices=tf.cast(labels, tf.int32), depth=10)
loss = tf.losses.softmax_cross_entropy(
onehot_labels=onehot_labels, logits=logits)
if mode == tf.estimator.ModeKeys.EVAL:
return tpu_estimator.TPUEstimatorSpec(
mode=mode,
loss=loss,
eval_metrics=(metric_fn, [labels, logits]))
# Train.
decaying_learning_rate = tf.train.exponential_decay(learning_rate,
tf.train.get_global_step(),
100000,0.96)
optimizer = tpu_optimizer.CrossShardOptimizer(
tf.train.GradientDescentOptimizer(
learning_rate=decaying_learning_rate))
train_op = optimizer.minimize(loss,
global_step=tf.train.get_global_step())
return tpu_estimator.TPUEstimatorSpec(mode=mode,
loss=loss, train_op=train_op)
def get_input_fn(filename):
def input_fn(params):
batch_size = params["batch_size"]
def parser(serialized_example):
features = tf.parse_single_example(
serialized_example,
features={
"image_raw": tf.FixedLenFeature([], tf.string),
"label": tf.FixedLenFeature([], tf.int64),
})
image = tf.decode_raw(features["image_raw"], tf.uint8)
image.set_shape([28 * 28])
image = tf.cast(image, tf.float32) * (1\. / 255) - 0.5
label = tf.cast(features["label"], tf.int32)
return image, label
dataset = tf.data.TFRecordDataset(
filename, buffer_size=FLAGS.dataset_reader_buffer_size)
dataset = dataset.map(parser).cache().repeat()
dataset = dataset.apply(
tf.contrib.data.batch_and_drop_remainder(batch_size))
images, labels = dataset.make_one_shot_iterator().get_next()
return images, labels
return input_fn
# TPU config
master = 'local' #URL of the TPU instance
model_dir = '/home/armando/models/mnist'
n_iterations = 50 # number of iterations per TPU training loop
n_shards = 8 # number of TPU chips
run_config = tpu_config.RunConfig(
master=master,
evaluation_master=master,
model_dir=model_dir,
session_config=tf.ConfigProto(
allow_soft_placement=True,
log_device_placement=True
),
tpu_config=tpu_config.TPUConfig(n_iterations,
n_shards
)
)
estimator = tpu_estimator.TPUEstimator(
model_fn=model_fn,
use_tpu=True,
train_batch_size=batch_size,
eval_batch_size=batch_size,
config=run_config)
train_file = '/home/armando/datasets/mnist/train' # input data file
train_steps = 1000 # number of steps to train for
estimator.train(input_fn=get_input_fn(train_file),
max_steps=train_steps
)
eval_file = '/home/armando/datasets/mnist/test' # test data file
eval_steps = 10
estimator.evaluate(input_fn=get_input_fn(eval_file),
steps=eval_steps
)
有关在 TPU 上构建模型的更多示例,请访问此链接。