TensorFlow

TensorFlow™是一个基于数据流编程(dataflow programming)的符号数学系统,被广泛应用于各类机器学习(machine learning)算法的编程实现,其前身是谷歌的神经网络算法库DistBelief [1]。

Tensorflow拥有多层级结构,可部署于各类服务器、PC终端和网页并支持GPUTPU高性能数值计算,被广泛应用于谷歌内部的产品开发和各领域的科学研究 [1-2]。

TensorFlow由谷歌人工智能团队谷歌大脑(Google Brain)开发和维护,拥有包括TensorFlow Hub、TensorFlow Lite、TensorFlow Research Cloud在内的多个项目以及各类应用程序接口(Application Programming Interface, API) [2]。自2015年11月9日起,TensorFlow依据阿帕奇授权协议(Apache 2.0 open source license)开放源代码 [2]。

谷歌大脑自2011年成立起开展了面向科学研究和谷歌产品开发的大规模深度学习应用研究,其早期工作即是TensorFlow的前身DistBelief [1]。DistBelief的功能是构建各尺度下的神经网络分布式学习和交互系统,也被称为“第一代机器学习系统” [1]。DistBelief在谷歌和Alphabet旗下其它公司的产品开发中被改进和广泛使用 [3-4]。2015年11月,在DistBelief的基础上,谷歌大脑完成了对“第二代机器学习系统”TensorFlow的开发并对代码开源。相比于前作,TensorFlow在性能上有显著改进、构架灵活性和可移植性也得到增强 [1]。此后TensorFlow快速发展,截至稳定API版本1.12,已拥有包含各类开发和研究项目的完整生态系统。在2018年4月的TensorFlow开发者峰会中,有21个TensorFlow有关主题得到展示 [2]。

安装

播报

编辑

语言与系统支持

TensorFlow支持多种客户端语言下的安装和运行。截至版本1.12.0,绑定完成并支持版本兼容运行的语言为CPython,其它(试验性)绑定完成的语言为JavaScriptC++JavaGoSwift,依然处于开发阶段的包括C#HaskellJuliaRuby、Rust和Scala [5]。

Python

TensorFlow提供Python语言下的四个不同版本:CPU版本(tensorflow)、包含GPU加速的版本(tensorflow-gpu),以及它们的每日编译版本(tf-nightly、tf-nightly-gpu)。TensorFlow的Python版本支持Ubuntu 16.04、Windows 7macOS 10.12.6 Sierra、Raspbian 9.0及对应的更高版本,其中macOS版不包含GPU加速 [6]。安装Python版TensorFlow可以使用模块管理工具pip/pip3 [7]或anaconda并在终端直接运行。

pip install tensorflow
conda install -c conda-forge tensorflow

此外Python版TensorFlow也可以使用Docker安装 [8]:

docker pull tensorflow/tensorflow:latest
# 可用的tag包括latest、nightly、version等
# docker镜像文件:https://hub.docker.com/r/tensorflow/tensorflow/tags/
docker run -it -p 8888:8888 tensorflow/tensorflow:latest
# dock下运行jupyter notebook
docker run -it tensorflow/tensorflow bash
# 启用编译了tensorflow的bash环境

C

TensorFlow提供C语言下的API用于构建其它语言的API,支持x86-64下的Linux类系统和macOS 10.12.6 Sierra或其更高版本,macOS版不包含GPU加速 [9]。安装过程如下 [9]:

下载TensorFlow预编译的C文件到本地系统路径(通常为/usr/local/lib)并解压缩。

sudo tar -xz libtensorflow.tar.gz -C /usr/local

使用ldconfig编译链接

sudo ldconfig
此外用户也可在其它路径解压文件并手动编译链接。
# Linux
export LIBRARY_PATH=$LIBRARY_PATH:~/mydir/lib
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:~/mydir/lib
# MacOS
export LIBRARY_PATH=$LIBRARY_PATH:~/mydir/lib
export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:~/mydir/lib

编译C接口时需确保本地的C编译器(例如gcc)能够访问TensorFlow库 [9]。

配置GPU

TensorFlow支持在Linux和Window系统下使用统一计算架构(Compute Unified Device Architecture, CUDA)高于3.5的NVIDIA GPU [10-11]和ROCm [79-80]。配置GPU时要求系统有NVIDIA GPU驱动384.x及以上版本、CUDA Toolkit和CUPTI(CUDA Profiling Tools Interface)9.0版本、cuDNN SDK7.2以上版本。可选配置包括NCCL 2.2用于多GPU支持、TensorRT 4.0用于TensorFlow模型优化 [10]。

在Linux下配置GPU时,将CUDA Toolkit和CUPTI的路径加入$LD_LIBRARY_PATH环境变量即可。对于CUDA为3.0或其它版本的NVIDIA程序,需要从源文件编译TensorFlow [10]。对Windows下的GPU配置,需要将CUDA、CUPTI和cuDNN的安装路径加入%PATH%环境变量,在DOS终端有如下操作 [10]:

C:\> SET PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\bin;%PATH%
C:\> SET PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\extras\CUPTI\libx64;%PATH%
C:\> SET PATH=C:\tools\cuda\bin;%PATH%
Linux系统下使用docker安装的Python版TensorFlow也可配置GPU加速且无需CUDA Toolkit [8]:
# 确认GPU状态
lspci | grep -i nvidia
# 导入GPU加速的TensorFlow镜像文件
docker pull tensorflow/tensorflow:latest-gpu
# 验证安装
docker run --runtime=nvidia --rm nvidia/cuda nvidia-smi
# 启用bash环境
docker run --runtime=nvidia -it tensorflow/tensorflow:latest-gpu bash

TensorFlow的公共API版本号使用语义化版本2.0标准 [12],包括主版本号.次版本号.修订号,其中主版本号的更改不是向下兼容的,已保存的TensorFlow工作可能需迁移到新的版本; 次版本号的更改包含向下兼容的性能提升;修订号的更改是向下兼容的问题修正 [5]。

TensorFlow支持版本兼容的部分包括协议缓冲区文件、所有的C接口、Python接口中的tensorflow模块以及除tf.contrib和其它私有函数外的所有子模块、Python函数和类 [5]。更新不支持版本兼容的部分为:包含“试验性(experimental)”字段的组件、使用除C和Python外其它语言开发的TensorFlow API、以GraphDef形式保存的工作、浮点数值特定位的计算精度、随机数、错误和错误消息 [5]。其中GraphDef拥有与TensorFlow相独立的版本号,当TensorFlow的更新放弃对某一GraphDef版本的支持后,可能有相关工具帮助用户将GraphDef转化为受支持的版本 [5]。需要指出,尽管 GraphDef的版本机制与TensorFlow相独立,但对GraphDef的更改仍受限于语义版本控制,即只能在TensorFlow主版本号之间移除或更改功能。此外,修订版本之间实施GraphDef的向前兼容 [5]。

组件与工作原理

播报

编辑

核心组件

TensorFlow的代码结构 [13]

分布式TensorFlow的核心组件(core runtime)包括:分发中心(distributed master)、执行器(dataflow executor/worker service)、内核应用(kernel implementation)和最底端的设备层(device layer)/网络层(networking layer) [13]。

分发中心从输入的数据流图中剪取子图(subgraph),将其划分为操作片段并启动执行器。分发中心处理数据流图时会进行预设定的操作优化,包括公共子表达式消去(common subexpression elimination)、常量折叠(constant folding)等 [13]。

执行器负责图操作(graph operation)在进程和设备中的运行、收发其它执行器的结果。分布式TensorFlow拥有参数器(parameter server)以汇总和更新其它执行器返回的模型参数。执行器在调度本地设备时会选择进行并行计算和GPU加速 [13]。

内核应用负责单一的图操作,包括数学计算、数组操作(array manipulation)、控制流(control flow)和状态管理操作(state management operations)。内核应用使用Eigen执行张量的并行计算、cuDNN库等执行GPU加速、gemmlowp执行低数值精度计算,此外用户可以在内核应用中注册注册额外的内核(fused kernels)以提升基础操作,例如激励函数和其梯度计算的运行效率 [13]。

单进程版本的TensorFlow没有分发中心和执行器,而是使用特殊的会话应用(Session implementation)联系本地设备。TensorFlow的C语言API是核心组件和用户代码的分界,其它组件/API均通过C语言API与核心组件进行交互 [13]。

低阶API

张量(tf.Tensor)

张量是TensorFlow的核心数据单位,在本质上是一个任意维的数组。可用的张量类型包括常数、变量、张量占位符和稀疏张量 [14-15]。这里提供一个对各类张量进行定义的例子:

import numpy as np
import tensorflow as tf
# tf.constant(value, dtype=None, name='Const', verify_shape=False)
tf.constant([0, 1, 2], dtype=tf.float32) # 定义常数
# tf.placeholder(dtype, shape=None, name=None)
tf.placeholder(shape=(None, 2), dtype=tf.float32) # 定义张量占位符
#tf.Variable(<initial-value>, name=<optional-name>)
tf.Variable(np.random.rand(1, 3), name='random_var', dtype=tf.float32) # 定义变量
# tf.SparseTensor(indices, values, dense_shape)
tf.SparseTensor(indices=[[0, 0], [1, 2]], values=[1, 2], dense_shape=[3, 4]) # 定义稀疏张量
# tf.sparse_placeholder(dtype, shape=None, name=None)
tf.sparse_placeholder(dtype=tf.float32)
张量的秩是它的维数,而它的形状是一个整数元组,指定了数组中每个维度的长度 [14]。张量按NumPy数组的方式进行切片和重构 [14]。这里提供一个进行张量操作的例子:
# 定义二阶常数张量
a = tf.constant([[0, 1, 2, 3], [4, 5, 6, 7]], dtype=tf.float32)
a_rank = tf.rank(a) # 获取张量的秩
a_shape = tf.shape(a) # 获取张量的形状
b = tf.reshape(a, [4, 2]) # 对张量进行重构
# 运行会话以显示结果
with tf.Session() as sess:
   print('constant tensor: {}'.format(sess.run(a)))
   print('the rank of tensor: {}'.format(sess.run(a_rank)))
   print('the shape of tensor: {}'.format(sess.run(a_shape)))
   print('reshaped tensor: {}'.format(sess.run(b)))
   # 对张量进行切片
   print("tensor's first column: {}".format(sess.run(a[:, 0])))
张量有23种数据类型,包括4类浮点实数、2类浮点复数、13类整数、逻辑、字符串和两个特殊类型,数据类型之间可以互相转换 [16]。TensorFlow中的张量是数据流图中的单位,可以不具有值,但在图构建完毕后可以获取其中任意张量的值,该过程被称为“评估(evaluate) [16]”:
constant = tf.constant([1, 2, 3]) # 定义常数张量
square = constant*constant # 操作(平方)
# 运行会话
with tf.Session() as sess:
   print(square.eval()) # “评估”操作所得常数张量的值
TensorFlow无法直接评估在函数内部或控制流结构内部定义的张量。如果张量取决于队列中的值,那么只有在某个项加入队列后才能评估。
变量(tf.Variable)
变量是可以通过操作改变取值的特殊张量 [17-18]。变量必须先初始化后才可使用,低阶API中定义的变量必须明确初始化,高阶API例如Keras会自动对变量进行初始化。TensorFlow可以在tf.Session开始时一次性初始化所有变量,对自行初始化变量,在tf.Variable上运行的tf.get_variable可以在定义变量的同时指定初始化器 [19]。这里提供两个变量初始化的例子:
# 例1:使用TensorFlow的全局随机初始化器
a = tf.get_variable(name='var5', shape=[1, 2])
init = tf.global_variables_initializer()
with tf.Session() as sess:
   sess.run(init)
   print(a.eval())
# 例2:自行定义初始化器
# tf.get_variable(name, shape=None, dtype=None, initializer=None, trainable=None, ...)
var1 = tf.get_variable(name="zero_var", shape=[1, 2, 3], dtype=tf.float32,
 initializer=tf.zeros_initializer) # 定义全零初始化的三维变量
var2 = tf.get_variable(name="user_var", initializer=tf.constant([1, 2, 3],  dtype=tf.float32)) 
# 使用常数初始化变量,此时不指定形状shape
Tensorflow提供变量集合以储存不同类型的变量,默认的变量集合包括 [20]:
本地变量:tf.GraphKeys.LOCAL_VARIABLES
全局变量:tf.GraphKeys.GLOBAL_VARIABLES
训练梯度变量:tf.GraphKeys.TRAINABLE_VARIABLES
用户也可以自行定义变量集合:
var3 = tf.get_variable(name="local_var", shape=(), collections=[tf.GraphKeys.LOCAL_VARIABLES])
在对变量进行共享时,可以直接引用tf.Variables,也可以使用 tf.variable_scope [21]进行封装:
def toy_model():
   定义包含变量的操作
   var1 = tf.get_variable(name="user_var5", initializer=tf.constant([1, 2, 3], dtype=tf.float32))
   var2 = tf.get_variable(name="user_var6", initializer=tf.constant([1, 1, 1], dtype=tf.float32))
   return var1+var2
with tf.variable_scope("model") as scope:
   output1 = toy_model()
   # reuse语句后二次利用变量
   scope.reuse_variables()
   output2 = toy_model()
# 在variable_scope程序块内启用reuse
with tf.variable_scope(scope, reuse=True):
   output3 = toy_model()

数据流图(tf.Graph)和会话(tf.Session)

一个前馈神经网络的拓扑结构(左)和TensorFlow数据流图(右) [22]

TensorFlow在数据流编程下运行,具体地,使用数据流图(tf.Graph)表示计算指令间的依赖关系,随后依据图创建会话(tf.Session)并运行图的各个部分 [23]。tf.Graph包含了图结构与图集合两类相关信息,其中图结构包含图的节点(tf.Operation)和边缘(张量)对象,表示各个操作组合在一起的方式,但不规定它们的使用方式,类似于汇编代码;图集合是在tf.Graph中存储元数据集合的通用机制,即对象列表与键(tf.GraphKeys)的关联 [24]。例如当用户创建变量时,系统将其加入变量集合,并在后续操作中使用变量集合作为默认参数 [23]。

构建tf.Graph时将节点和边缘对象加入图中不会触发计算,图构建完成后将计算部分分流给tf.Session实现计算。tf.Session拥有物理资源,通常与Python的with代码块中使用,在离开代码块后释放资源 [11] [23]。在不使用with代码块的情况下创建tf.Session,应在完成会话时明确调用tf.Session.close结束进程。调用Session.run创建的中间张量会在调用结束时或结束之前释放。tf.Session.run是运行节点对象和评估张量的主要方式,tf.Session.run需要指定fetch并提供供给数据(feed)字典,用户也可以指定其它选项以监督会话的运行 [23] [25]。这里使用低阶API以批量梯度下降的线性回归为例展示tf.Graph的构建和tf.Session的运行:

Eager Execution使用Python控制流,支持标准的Python调试工具,状态对象的生命周期也由其对应的Python对象的生命周期,而不是tf.Session决定 [35]。Eager Execution支持大多数TensorFlow操作和GPU加速,但可能会使某些操作的开销增加 [35]。

Data

TensorFlow输入管道的设备占用策略 [36]

tf.data是TensorFlow中进行数据管理的高阶API [37]。在图像处理问题中,tf.data可以对输入图像进行组合或叠加随机扰动,增大神经网络的训练收益;在文字处理问题中,tf.data负责字符提取和嵌入(embedding),后者将文字转化为高维向量,是进行机器学习的重要步骤 [38]。tf.data包含两个类:tf.data.Dataset和tf.data.Iterator,Dataset自身是一系列由张量构成的组元,并包含缓存(cache)、交错读取(interleave)、预读取(prefetch)、洗牌(shuffle)、投影(map)、重复(repeat)等数据预处理方法、Iterator类似于Python的循环器,是从Dataset中提取组元的有效方式 [39]。tf.data支持从NumPy数组和TFRecord中导入数据,在字符数据处理时时,tf.data.TextLineDataset可以直接输入ASCII编码文件 [37] [40]。

tf.data可用于构建和优化大规机器学习的输入管道(input pipline),提升TensorFlow性能。一个典型的输入管道包含三个部分 [36]:

  • 提取(Extract):从本地或云端的数据存储点读取原始数据

  • 转化(Transform):使用计算设备(通常为CPU)对数据进行解析和后处理,例如解压缩、洗牌(shuffling)、打包(batching)等

  • 加载(Load):在运行机器学习算法的高性能计算设备(GPU和TPU)加载经过后处理的数据

在本地的同步操作下,当GPU/TPU进行算法迭代时,CPU处于闲置状态,而当CPU分发数据时,GPU/TPU处于闲置状态。tf.data.Dataset.prefetch在转化和加载数据时提供了预读取技术,可以实现输入管道下算法迭代和数据分发同时进行,在当前学习迭代完成时能更快地提供下一个迭代的输入数据。tf.data.Dataset.prefetch的buffer_size参数通常为预读取值的个数 [36]。

tf.data支持输入管道的并行,tf.contrib.data.parallel_interleave可以并行提取数据 [41];映射函数tf.data.Dataset.map能够并行处理用户的指定操作 [42]。对于跨CPU并行,用户可以通过num_parallel_calls接口指定并行操作的等级。一般而言,并行等级与设备的CPU核心数相同,即在四核处理器上可定义num_parallel_calls=4 [36]。在大数据问题中,可使用tf.contrib.data.map_and_batch并行处理用户操作和分批操作 [43]。这里提供一个构建和优化输入管道的例子:

import tensorflow as tf
# 使用FLAG统一管理输入管道参数
FLAGS = tf.app.flags.FLAGS
tf.app.flags.DEFINE_integer('num_parallel_readers', 0, 'doc info')
tf.app.flags.DEFINE_integer('shuffle_buffer_size', 0, 'doc info')
tf.app.flags.DEFINE_integer('batch_size', 0, 'doc info')
tf.app.flags.DEFINE_integer('num_parallel_calls', 0, 'doc info')
tf.app.flags.DEFINE_integer('prefetch_buffer_size', 0, 'doc info')
# 自定义操作(map)
def map_fn(example):
   # 定义数据格式(图像、分类标签)
   example_fmt = {"image": tf.FixedLenFeature((), tf.string, ""),
                  "label": tf.FixedLenFeature((), tf.int64, -1)}
   # 按格式解析数据
   parsed = tf.parse_single_example(example, example_fmt)
   image = tf.image.decode_image(parsed["image"]) # 图像解码操作
   return image, parsed["label"]
# 输入函数
def input_fn(argv):
   # 列出路径的所有TFRData文件(修改路径后)
   files = tf.data.Dataset.list_files("/path/TFRData*")
   # 并行交叉读取数据
   dataset = files.apply(
      tf.contrib.data.parallel_interleave(
         tf.data.TFRecordDataset, cycle_length=FLAGS.num_parallel_readers))
   dataset = dataset.shuffle(buffer_size=FLAGS.shuffle_buffer_size) # 数据洗牌
   # map和batch的并行操作
   dataset = dataset.apply(
       tf.contrib.data.map_and_batch(map_func=map_fn,
                                     batch_size=FLAGS.batch_size,
                                     num_parallel_calls=FLAGS.num_parallel_calls))
   dataset = dataset.prefetch(buffer_size=FLAGS.prefetch_buffer_size) # 数据预读取设置
   return dataset
# argv的第一个字符串为说明
tf.app.run(input_fn, argv=['pipline_params',
                       '--num_parallel_readers', '2',
                       '--shuffle_buffer_size', '50',
                       '--batch_size', '50',
                       '--num_parallel_calls, 4'
                       '--prefetch_buffer_size', '50'])
在输入管道的各项操作中,交叉读取、 预读取和洗牌能降低内存占用,因此具有高优先级。数据的洗牌应在重复操作前完成,为此可使用两者的组合方法tf.contrib.data.shuffle_and_repeat [36] [44]。
加速器
CPU和GPU设备
TensorFlow支持CPU和GPU运行,在程序中设备使用字符串进行表示。CPU表示为"/cpu:0";第一个GPU表示为"/device:GPU:0";第二个GPU表示为"/device:GPU:1",以此类推 [45]。如果TensorFlow指令中兼有CPU和GPU实现,当该指令分配到设备时,GPU设备有优先权。TensorFlow仅使用计算能力高于3.5的GPU设备 [11]。
在启用会话时打开log_device_placement配置选项,可以在终端查看会话中所有操作和张量所分配的设备,这里提供一个例子:
# 构建数据流图.
a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3], name='a')
b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2], name='b')
c = tf.matmul(a, b)
# 启用会话并设定log_device_placement=True.
with tf.Session(config=tf.ConfigProto(log_device_placement=True)) as sess:
   print(sess.run(c))
# 终端中可见信息:MatMul: (MatMul): /job:localhost/replica:0/task:0/device:CPU:0…
默认地,TensorFlow会尽可能地使用GPU内存,最理想的情况是进程只分配可用内存的一个子集,或者仅根据进程需要增加内存使用量,为此,启用会话时可通过两个编译选项来进行GPU进程管理 [45-46]。
内存动态分配选项allow_growth可以根据需要分配GPU内存,该选项在开启时会少量分配内存,并随着会话的运行对占用内存区域进行扩展。TensorFlow会话默认不释放内存,以避免内存碎片问题。
per_process_gpu_memory_fraction 选项决定每个进程所允许的GPU内存最大比例。
这里提供一个在会话中编译GPU进程选项的例子 [45]:
config = tf.ConfigProto()
config.gpu_options.allow_growth = True # 开启GPU内存动态分配
config.gpu_options.per_process_gpu_memory_fraction = 0.4 # 内存最大占用比例为40%
with tf.Session(config=config) as sess:
   # ...(略去)会话内容 ...
TPU设备
张量处理器(Tensor Processing Unit, TPU)是谷歌为TensorFlow定制的专用芯片。TPU部署于谷歌的云计算平台,并作为机器学习产品开放研究和商业使用 [47]。TensorFlow的神经网络API Estimator拥有支持TPU下可运行的版本TPUEstimator [48]。TPUEstimator可以在本地进行学习/调试,并上传谷歌云计算平台进行计算。
使用云计算TPU设备需要快速向TPU供给数据,为此可使用tf.data.Dataset API从谷歌云存储分区中构建输入管道。小数据集可使用tf.data.Dataset.cache完全加载到内存中 [42],大数据可转化为TFRecord格式并使用tf.data.TFRecordDataset进行读取 [36]。
设备管理(tf.device)
TensorFlow使用tf.device对设备进行管理,tf.device的设备规范具有以下形式 [23]:
/job:<JOB_NAME>/task:<TASK_INDEX>/device:<DEVICE_TYPE>:<DEVICE_INDEX>
其中<JOB_NAME> 是一个字母数字字符串,并且不以数字开头。<DEVICE_TYPE> 是一种注册设备类型(例如 GPU 或 CPU)。<TASK_INDEX> 是一个非负整数,表示名为 <JOB_NAME> 的作业中的任务的索引。<DEVICE_INDEX> 是一个非负整数,表示设备索引,例如用于区分同一进程中使用的不同GPU设备 [10] [23]。
定义变量时可以使用tf.device指定设备名称 [49],tf.train.replica_device_setter可以对变量的设备进行自动分配,这里提供一个在不同设备定义变量和操作的例子 [50]:
# 手动分配
with tf.device("/device:GPU:1"):
 var = tf.get_variable("var", [1])
# 自动分配
cluster_spec = {
   "ps": ["ps0:2222", "ps1:2222"],
   "worker": ["worker0:2222", "worker1:2222", "worker2:2222"]}
with tf.device(tf.train.replica_device_setter(cluster=cluster_spec)):
 v = tf.get_variable("var", shape=[20, 20])
根据tf.device对变量的分配,在单一GPU的系统中,与变量有关的操作会被固定到CPU或GPU上;在多GPU的系统中,操作会在偏好设备(或多个设备同时)运行 [45]。多GPU并行处理图的节点能加快会话的运行,这里提供一个例子:
c = [] # 在GPU:1和GPU:2定义张量 (运行该例子要求系统存在对应GPU设备)
for d in ['/device:GPU:1', '/device:GPU:2']:
   with tf.device(d):
       a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3])
       b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2])
       c.append(tf.matmul(a, b))
# 在CPU定义相加运算
with tf.device('/cpu:0'):
   my_sum = tf.add_n(c)
# 启用会话
with tf.Session(config=tf.ConfigProto(log_device_placement=True)) as sess:
   print(sess.run(my_sum))
优化器
模型优化工具
Tensorflow提供了模型优化工具(Model Optimization Toolkit)对模型的尺度、响应时间和计算开销进行优化 [51]。模型优化工具可以减少模型参数的使用量(pruning)、对模型精度进行量化(quantization)和改进模型的拓扑结构,适用于将模型部署到终端设备,或在有硬件局限时运行模型,因此有很多优化方案是TensorFlow Lite项目的一部分。其中量化能够在最小化精度损失的情况下显著减小模型尺度和缩短响应时间,并是优化深度学习模型的重要手段。这里提供一个使用使用模型优化工具的例子 [52]:
import tensorflow as tf
converter = tf.contrib.lite.TocoConverter.from_saved_model(path) # 从路径导入模型
converter.post_training_quantize = True # 开启学习后量化
tflite_quantized_model = converter.convert() # 输出量化后的模型
open("quantized_model.tflite", "wb").write(tflite_quantized_model) # 写入新文件

XLA

XLA工作流程 [53]

线性代数加速器(Accelerated Linear Algebra, XLA)是一个特殊的编译器,用于优化TensorFlow中的线性代数计算,其目标是优化内存使用,提升TensorFlow的运行速度和跨平台,尤其是移动终端的可移植性 [53]。

XLA工作的前端输入为“高层优化器(High Level Optimizer, HLO)”定义的数据流图,随后XLA使用多种独立于计算设备的算法优化方案对图进行分析,并将HLO计算送入后端。后端会进一步进行基于特定设备,例如GPU的优化 [53]。截至TensorFlow的1.12版本,XLA依然处于早期开发状态,暂不能提供显著的性能优化,其硬件支持包括JITAOT编译的x86-64 CPU、NVIDIA GPU [53]。

可视化工具

TensorFlow拥有自带的可视化工具TensorBoard,TensorBoard具有展示数据流图、绘制分析图、显示附加数据等功能 [54]。开源安装的TensorFlow会自行配置TensorBoard。启动TensorBoard前需要建立模型档案,低阶API使用tf.summary构建档案,Keras包含callback方法、Estimator会自行建立档案。这里提供两个例子:

1# 为低层API构建档案 2my_graph = tf.Graph() 3with my_graph.as_default(): 4   # 构建数据流图 5with tf.Session(graph=my_graph) as sess: 6   # 会话操作    7    file_writer = tf.summary.FileWriter('/user_log_path', sess.graph) # 输出文件 8# 为Keras模型构建档案 9import tensorflow.keras as keras 10tensorboard = keras.callbacks.TensorBoard(log_dir='./logs') 11# … (略去)用户自定义模型 ... 12model.fit(callbacks=[tensorboard]) # 调用fit时加载callback

档案建立完毕后在终端可依据档案路径运行TensorBoard主程序:

1tensorboard --logdir=/user_log_path

当终端显示TensorBoard 1.12.0 at http://your_pc_name:6006 (Press CTRL+C to quit)时,跳转至localhost:6006可使用TensorFlow界面。

调试程序

由于通用调试程序,例如Python的pdb很难对TensorFlow代码进行调试,因此TensorFlow团队开发了专用的调试模块TFDBG,该模块可以在学习和预测时查看会话中数据流图的内部结构和状态 [55]。TFDBG在运行时期间会拦截指令生成的错误,并向用户显示错误信息和调试说明。TFDBG使用文本交互系统curses,在不支持curses的Windows操作系统,可以下载非官方的Windows curses软件包或使用readline作为代替。使用TFDBG调试会话时,可以直接将会话进行封装,具体有如下例子 [55]:

1from tensorflow.python import debug as tf_debug 2with tf.Session() as sess: 3   sess = tf_debug.LocalCLIDebugWrapperSession(sess) 4   print(sess.run(c))

封装容器与会话具有相同界面,因此调试时无需修改代码。封装容器在会话开始时调出命令行界面(Command Line Interface, CLI),CLI包含超过60条指令,用户可以在使用指令控制会话、检查数据流图、打印及保存张量 [55]。

TFDBG可以调试神经网络API Estimator和Keras,对Estimator,TFDBG创建调试挂钩(LocalCLIDebugHook)作为Estimator中的fit和evaluate方法下monitor的参数。对Keras,TFDBG提供Keras后端会话的封装对象,这里提供一些调试例子 [55]:

1# 调试Estimator 2Import tensorflow as tf 3from tensorflow.python import debug as tf_debug 4hooks = [tf_debug.LocalCLIDebugHook()] # 创建调试挂钩 5# classifier = tf.estimator. … 调用Estimator模型 6classifier.fit(x, y, steps, monitors=hooks) # 调试fit 7classifier.evaluate(x, y, hooks=hooks) # 调试evaluate 8# 调试Keras 9from keras import backend as keras_backend 10# 在程序开始时打开后端会话封装 11keras_backend.set_session(tf_debug.LocalCLIDebugWrapperSession(tf.Session())) 12# 构建Keras模型 13model.fit(...)  # 使用模型学习时进入调试界面(CLI)

TFDBG支持远程和离线会话调试,可应用于在没有终端访问权限的远程机器(例如云计算)运行Tensorflow的场合。除CLI外,TFDBG在TensorBoard拥有拥有图形界面的调试程序插件,该插件提供了计算图检查、张量实时可视化、张量连续性和条件性断点以及将张量关联到图源代码等功能 [55]。

部署

播报

编辑

TensorFlow支持在一个或多个系统下使用多个设备并部署分布式服务器(distributed server)和服务器集群(cluster)。tf.train.Server.create_local_server可在本地构建简单的分布式服务器 [56]。这里提供一个例子 [57]:

1import tensorflow as tf 2c = tf.constant("Hello, distributed TensorFlow!") 3# 建立服务器 4server = tf.train.Server.create_local_server() 5# 在服务器运行会话 6with tf.Session(server.target) as sess   7   sess.run(c)

TensorFlow服务器集群是分布运行的数据流图中的“任务(task)”集合,每个任务都会被分配至一个TensorFlow服务,其中包含一个“主干(master)”以启动会话和一个“工作点(worker)”执行图的操作。服务器集群可以被分割为“工作(job)”,每个工作包含一或多个任务 [57]。

部署服务器集群时,通常每个任务分配一台机器,但也可在一台机器的不同设备运行多个任务。每个任务都包含tf.train.ClusterSpec方法以描述该服务器集群的全部任务(每个任务的ClusterSpec是相同的)和tf.train.Server方法按工作名提取本地任务。tf.train.ClusterSpec要求输入一个包含所有工作名和地址的字典;而tf.train.Server对象包含一系列本地设备、与tf.train.ClusterSpec中其它任务的链接和一个使用链接进行分布式计算的会话 [57]。每个任务都是一个特定工作名的成员,并有一个任务编号(task index)。任务可以通过编号与其它任务相联系。这里提供一个部署两个任务于两台服务器的例子 [57]:

1# 假设有局域网内服务器localhost:2222和localhost:2223 2# 在第一台机器建立任务 3cluster = tf.train.ClusterSpec({"local": ["localhost:2222", "localhost:2223"]}) 4server = tf.train.Server(cluster, job_name="local", task_index=0) 5# 在第二台机器建立任务 6cluster = tf.train.ClusterSpec({"local": ["localhost:2222", "localhost:2223"]}) 7server = tf.train.Server(cluster, job_name="local", task_index=1)

分布式TensorFlow支持亚马逊简易存储服务(Amazon Simple Storage Service, S3) [58]和开源的Hadoop分布式文件系统(Hadoop Distributed File System, HDFS) [59]。

安全性

播报

编辑

TensorFlow的模型文件是代码,在执行数据流图计算时可能的操作包括读写文件、从网络发送和接收数据、生成子进程,这些过程对系统会造成影响 [60]。在运行由未知第三方提供的TensorFlow模型、 计算流图(GraphDef和SavedModel)和检查点文件时,一个推荐的做法是使用沙盒(sand box)以监测其行为 [60]。安全的TensorFlow模型在引入未知输入数据时,也可能触发TensorFlow内部或系统的错误。

TensorFlow的分布式计算平台和服务器接口(tf.train.Server)不包含授权协议和信息加密选项,任何具有网络权限的访问者都可以运行tf.train.Server上的任何代码,因此TensorFlow不适用于不信任的网络。在局域网或云计算平台部署TensorFlow计算集群时,需要为其配备独立网络(isolated networks) [60]。

TensorFlow作为一个使用大量第三方库(NumPy、libjpeg-turbo等)的复杂系统,容易出现漏洞。用户可以使用电子邮件向TensorFlow团队报告漏洞和可疑行为,对于高度敏感的漏洞,其GitHub页面提供了邮件的SSH密钥 [60]。以下列出截至2018年7月12日的已知漏洞 [61]:

编号

内容

版本

报告方

TFSA-2018-006

恶意构造编译文件引起非法内存访问

1.7及以下

Tencent Blade Team

TFSA-2018-005

(原文)“Old Snappy Library Usage Resulting in Memcpy Parameter Overlap”

1.7及以下

Tencent Blade Team

TFSA-2018-004

检查点源文件越界读取

1.7及以下

Tencent Blade Team

TFSA-2018-003

TensorFlow Lite TOCO FlatBuffer库解析漏洞

1.7及以下

Tencent Blade Team

TFSA-2018-002

(原文)“GIF File Parsing Null Pointer Dereference Error”

1.5及以下

Tencent Blade Team

TFSA-2018-001

BMP文件解析越界读取

1.6及以下

Tencent Blade Team

生态系统

播报

编辑

社区

TensorFlow位于GitHub的三个代码库负责处理事件和提供技术支持,一般性的求助也可发送至StackOverflow的TensorFlow板块 [62]。TensorFlow使用公共邮箱发布主要版本和重要公告 [63],其官方网站的“路线图”页面汇总了其近期的开发计划 [64]。TensorFlow团队拥有推特账户和博客以发布项目的新闻和动态。TensorFlow的YouTube频道介绍了TensorFlow在机器学习和人工智能领域的应用,并定期推送节目,包括“TensorFlow Meets”、“Ask TensorFlow”和“Coding TensorFlow” [65]。

项目

TensorFlow Hub

TensorFlow Hub是一个允许用户发布、共享和使用TensorFlow模块的库开发项目。用户可以将TensorFlow数据流图或其部分使用Hub进行封装并移植到其它问题中再次利用 [66]。TensorFlow Hub页面列出了由谷歌和DeepMind提供的封装模型,其主题包括字符嵌入、视频分类和图像处理 [67]。

TensorFlow Extended (TFX)

TFX是谷歌基于TensorFlow开发的产品级机器学习平台,其目标是是对产品开发中的模型实现、分析验证和业务化操作进行整合,在实时数据下完成机器学习产品的标准化生产 [68]。TFX包含三个算法库:TensorFlow Data Validation对机器学习数据进行统计描述和验证、TensorFlow Transform对模型数据进行预处理、TensorFlow Model Analysis对机器学习模型进行分析,提供表现评分。另有TensorFlow Serving作为模型业务化的高性能系统,提供模型接口和管理 [69]。

TensorFlow Probability (TFP)

TFP是在TensorFlow Python API基础上开发的统计学算法库,其目标是方便用户将概率模型和深度学习模型相结合使用 [70]。TFP包含大量概率分布的生成器、支持构建深度网络的概率层(probabilistic layers)、提供变分贝叶斯推断(Variational inference)和马尔可夫链蒙特卡罗方法(Markov chain Monte Carlo)和一些特殊的优化器,包括Nelder-Mead方案、BFGS算法(Broyden-Fletcher-Goldfarb-Shanno algorithm)和SGLD(Stochastic Gradient Langevin Dynamics) [70]。

应用开发

TensorFlow.js

TensorFlow.js是TensorFlow的JavaScript API,主要用于网页端的机器学习应用开发。TensorFlow.js可以在浏览器和Node.js下转化和运行TensorFlow构建的机器学习模型,并使用网页端数据对模型进行训练 [71]。截至2018年9月18日,TensorFlow.js的版本号为0.13 [72]。

TensorFlow Lite

TensorFlow Lite是为移动和嵌入式设备运行机器学习代码的问题提供解决方案。TensorFlow Lite包含优化算法以提升Android、iOS等系统下机器学习模型的响应时间并降低文件大小。谷歌内部的许多移动端产品,包括谷歌相册、谷歌邮箱客户端、谷歌键盘等都使用TensorFlow Lite部署了人工智能算法 [73]。

Swift for TensorFlow

Swift for TensorFlow是开源版Swift的TensorFlow API开发项目。Swift for TensorFlow类似于Eager Execution可以直接执行数据流图且性能更高 [74]。截至10月13日,Swift for TensorFlow处于早期开发状态。

研究

TensorFlow Research Cloud

谷歌云计算服务中的TPU计算集群 [75]

TensorFlow Research Cloud是面向科学研究的机器学习TPU云计算平台。该项目拥有1000个云TPU和总计180千万亿次计算力,每个TPU拥有64 GB的高带宽内存 [75-76]。TensorFlow Research Cloud项目在2018年2月进入Beta版,可以申请使用,在官方声明中,其发起目的是“为确保全世界优秀的研究人员拥有足够的计算资源以规划、使用和发表下个机器学习浪潮的革命性突破”

(原文) [75]“Our goal is to ensure that the most promising researchers in the world have access to enough compute power to imagine, implement, and publish the next wave of ML breakthroughs.”

除面向研究的TPU服务外,谷歌也提供商用的Cloud TPU项目以支持企业的Tensorflow开发 [47]。

Magenta

Magenta是在艺术领域使用机器学习的研究项目,该项目使用深度学习网络和强化学习算法学习生成音乐、绘画和其它艺术作品,以帮助艺术人员拓展其创作过程 [77]。Magenta项目的研究成果包括音乐创作工具NSynth和混音工具MusicVAE。

Nucleus

Nucleus是将TensorFlow应用于基因组文件,例如SAM和VCF格式文件的读写和分析的库开发项目 [78]。Nucleus使用Python和C++进行开发,截至2018年9月已发布0.2.0版本 [78]。

版本

播报

编辑

TensorFlow 2.8.0

2022年2月,TensorFlow 官方发布了 2.8.0 正式版,提供了更多的 bug 修复和功能改进,还针对漏洞发布了补丁。TensorFlow 2.8.0 主要功能和改进:

在 tf.lite 中,增加了 TFLite 内置 op 支持以下功能:

tf.raw_ops.Bucketize op 可在 CPU 上操作;

tf.where op 可用于数据类型 tf.int32、tf.uint32、tf.int8、tf.uint8、tf.int64;

tf.random.normal op 用于在 CPU 上输出数据类型 tf.float32;

tf.random.uniform op 用于在 CPU 上输出数据类型 tf.float32;

f.random.categorical op 用于在 CPU 上的输出数据类型 tf.int64。

tensorflow.experimental.tensorrt:

Conversion_params 在 TrtGraphConverterV2 中被弃用,现在可以支持参数 max_workspace_size_bytes、precision_mode、minimum_segment_size、maximum_cached_engines、use_calibration 和 allow_build_at_runtime;

在 TrtGraphConverterV2 中的 .save () 函数中添加了一个名为 save_gpu_specific_engines 的新参数。当为 False 时,.save () 函数不会保存任何已构建的 TRT 引擎;如果为 True(默认),则保留原始行为;

TrtGraphConverterV2 提供了一个名为 .summary () 的新 API。它显示了每个 TRTEngineOp 及其输入和输出的形状和 dtype,并提供了详细版本摘要。

tf.tpu.experimental.embedding:

tf.tpu.experimental.embedding.FeatureConfig 增加了一个额外的参数 output_shape,它可以指定特征输出激活的形状;

tf.tpu.experimental.embedding.TPUEmbedding 现在具有与 tf.tpu.experimental.embedding.serving_embedding_lookup 相同的功能,它可以使用任意等级密集和稀疏的张量。对于不规则张量,尽管输入张量仍然是 2 级,但现在可以通过在特征配置中指定输出形状或通过 build 方法来激活 2 级或更高级别。

添加 tf.config.experimental.enable_op_determinism ,这使得 TensorFlow ops 以性能为代价可以确定性地运行。替换 TF_DETERMINISTIC_OPS 环境变量。

(自 TF 2.7 起)向 TensorFlow Profiler 添加 PluggableDevice 支持。

Bug 修复和其他改进

tf.data:

如果用户未禁用,现在优化 parallel_batch 现在成为默认值,这样可以并行复制批处理元素;

添加了 TensorSliceDataset,用于识别和处理文件输入。

tf.lite:

为 Java API 的序列化添加 GPU 委托支持,当 OpenCL 可用时,这将初始化时间提高了 90%;

弃用 Interpreter::SetNumThreads,支持 InterpreterBuilder::SetNumThreads。

tf.keras

tf.random.Generator 用于 keras 初始化和所有的 RNG 代码;

TextVectorization 增加了额外的 standardize 和 split 模式:standardize="lower" 转化为小写字母输入;standardize="string_punctuation" 删除所有标点符号;Split ="character" 将对每个 unicode 字符进行拆分。

增加 GPU 实现:

(自 2.7 版本开始) tf.math.segment_mean

(自 2.7 版本开始) tf.math.segment_prod

(自 2.7 版本开始) tf.math.segment_sum

TensorFlow 已在适用于 GPU 和 CPU 的 Windows Subsystem for Linux 2(又名 WSL 2)上得到验证。

此外,TensorFlow 2.8.0 在安全方面进行了一些修正,包括修正了执行卷积运算时浮点数被 0 除的问题:CVE-2022-21725;修正了 Dequantize 形状推断中的整数溢出问题:CVE-2022-21727;修正了 ConcatV2 形状推断中的类型混淆问题:CVE-2022-21731 等。 [81]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aheyor编程1041

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值