使用keras为什么accuracy一直为0_关于 TensorFlow 2.0 你需要了解的一切

本文深入探讨了TensorFlow 2.0的主要变化,包括移除图和Session的概念,引入Eager Execution,使代码更加直观。Keras成为首选的高级API,支持Sequential和模型子类化两种模型创建方式。此外,文章还介绍了TensorFlow 2.0中的模型优化工具Keras-Tuner、TensorBoard的使用、模型保存与部署,以及如何进行后训练量化以适应嵌入式设备。同时,强调了自定义层和激活函数的创建,以及如何将旧版代码升级到TensorFlow 2.0。
摘要由CSDN通过智能技术生成

390ede2e5b3c12e3360b7bdb3bb44914.png

作者丨Thalles Silva

译者丨良言

2019 年 6 月 26 日,在 Sao Paulo 举行的 PAPIs.io LATAM 会议上,作为 Daitan 的代表,本文作者 Thalles Silva 举办了一个关于 TensorFlow(TF)2.0 的研讨会,并在会上探讨了一些关于 TF 2.0 的话题。研讨会的初衷是重点展示 2.0 版本同以往 1.x 版本的不同。本文回顾了会上讨论的主要内容:Keras-APIs、SavedModels、TensorBoard、Keras-Tuner 等。同时,你也可以通过 Colab notebook 来查看练习代码。 1 TensorFlow 2.0 简介  

TensorFlow 是谷歌在 2015 年开源的一个通用高性能计算库。从一开始,TensorFlow 的主要目的就是为构建神经网络(NN)提供高性能 API。然而,借助于机器学习(ML)社区对它的兴趣以及时间上的优势,这个类库演变成了一个完整的 ML 生态系统。

5339b204be87fd8b8f663cc6b3704ffe.png

目前,该类库也在经历着从推出以来最大规模的变化。TensorFLow 2.0 目前仍处在 beta 版本,同 TensorFlow 1.x 版本相比它带来了太多的改变。接下来,让我们深入研究其中的一些主要改变。

2 即时执行 Eager Execution 变为默认模式  
首先,即时执行成为 TensorFlow 代码运行的默认模式。

你或许还记得,在 TensorFlow 1.x 中创建一个神经网络的时候,我们需要定义一个叫做图的抽象数据结构。同时,当我们尝试打印其中一个图节点的时候,我们只能看到这个图节点的引用值,而不能看到期待中的实际值。为了能够运行这个图,我们需要使用一个叫做 Session 的封装。我们通过方法 Session.run() 将 Python 数据传递到图,从而开始对模型的训练。

c0c7b654d6b49b532bac2870597b5078.png

TensorFlow 1.x 代码示例

当使用 eager execution 的时候,上述情况就发生了改变。现在,TensorFlow 代码可以像普通 Python 代码那样运行。Eagerly 表示操作的创建和执行同时进行。

f775c3fc4122babb06827066015d8f5f.png

TensorFlow 2.0 代码示例

TensorFlow 2.0 的代码看起来非常像 NumPy 代码。实际上,TensorFlow 和 NumPy 的对象也可以很容易地相互置换。因此,你也不用为 placeholders、Sessions 以及 feed_dictionaties 等伤脑筋了。

3 API 清理  
像 tf.gans、tf.app、tf.contrib 以及 tf.flags 等很多 API,要么被直接移除,要么就是转移到单独的库。

但是,其中最重要的一项清理同我们如何创建模型相关。你或许还记得,在 TensorFlow 1.x 中我们可以用不止一种方法来创建和训练 ML 模型。

在 TF 1.x 中,像 Tf.slim、tf.layers、tf.contrib.layers 以及 tf.keras 等 API 都可以用来创建神经网络,这还不包括 1.x 中 Sequence to Sequence API。而大多数时候,我们自己也不是很清楚应该在何种情况下使用何种 API。

尽管这些 API 大多数都有很不错的功能,但它们还是不能转换成一种通常的开发方式。如果我们使用了其中一种 API 来训练我们的模型,接下来就很难再切换到别的 API 了。

TensorFlow 2.0 中,tf.keras 是推荐使用的高级 API

正如我们所见,Keras API 正在试图解决所有的使用案例。

4 初级 API  

从 TF 1.x 到 2.0,初级 API 并没有发生太大的改变。但现在,Keras 变成了默认的也是推荐使用的高级 API。简单来讲,Keras 是用来描述如何通过更清晰的标准来创建神经网络的一系列层。当我们通过 pip 来安装 TensorFlow 的时候,我们一般会得到完整的 Keras API 以及一些额外的函数工具集。

model = tf.keras.models.Sequential()model.add(Flatten(input_shape=(IMAGE_HEIGHT,IMAGE_WIDTH)))model.add(Dense(units=32, activation='relu'))model.add(Dropout(0.3))model.add(Dense(units=32, activation='relu'))model.add(Dense(units=10, activation='softmax'))# Configures the model for training.# Define the model optimizer, the loss function and the accuracy metricsmodel.compile(optimizer='adam',              loss='sparse_categorical_crossentropy',              metrics=['accuracy'])model.summary()

初级 API 又被叫作 Sequential(序列化)API。它通过层堆栈来定义一个神经网络。除了简洁以外,它还拥很多其它的优点。注意,我们是按照数据结构(堆栈)的方式来定义模型。因此,这也减少了因为模型定义而出错的可能性。

5 Keras-Tuner  

Keras-Tuner 是一个专门为 Keras 模型优化超参数的独立库。在本文写作的时候,该类库仍处于 pre-alpha 阶段,即便如此,它已经可以在 Colab 上很好地同 tf.keras 以及 TensorFlow2.0 beta 一起工作。

这是一个很简单的概念。首先,我们需要定义一个模型构建函数来返回一个编译好的 Keras 模型。这个函数采用了一个叫做 hp 的输入参数。通过 hp,我们能够定义一个可以用作超参数采样的候选值范围。

下面的代码创建了一个简单的模型并且优化 3 个以上的超参数。在隐藏模块中,我们对一个预定义范围里的整数值进行了取样。我们在一些指定值中进行随机选择,用来表示 dropout 率和学习率参数。

def build_model(hp):    # define the hyper parameter ranges for the learning rate, dropout and hidden unit    hp_units = hp.Range('units', min_value=32, max_value=128, step=32)    hp_lr = hp.Choice('learning_rate', values=[1e-2, 1e-3, 1e-4])    hp_dropout = hp.Choice('dropout', values=[0.1,0.2,0.3])      # build a Sequential model    model = keras.Sequential()    model.add(Flatten(input_shape=(IMAGE_HEIGHT,IMAGE_WIDTH)))    model.add(Dense(units=hp_units, activation='relu'))    model.add(Dropout(hp_dropout))    model.add(Dense(units=32, activation='relu'))    model.add(layers.Dense(10, activation='softmax'))      # compile and return the model    model.compile(optimizer=keras.optimizers.Adam(hp_lr),        loss='sparse_categorical_crossentropy',        metrics=['accuracy'])    return model      # create a Random Search tuner  tuner = RandomSearch(      build_model,      objective='val_accuracy', # define the metric to be optimized over      max_trials=3,      executions_per_trial=1,      directory='my_logs') # define the output log/checkpoints folder      # start hyper-parameter optmization search  tuner.search(x_train, y_train,               epochs=2,               validation_data=(x_test, y_test))

然后,我们创建了一个 tuner 对象。本例中,我们实现了一个随机搜索策略。最后,我们通过 search() 方法来优化。这个方法具有和 fit() 一样的签名。

最后,我们可以通过检查调优结果汇总选出最好的模型。训练日志以及模型的 checkpoints 都被保存在 my_logs 目录下。同时,最小化或最大化目标(验证准确度)的选择也被自动推断出来。

更多内容请参阅此处:

https://github.com/keras-team/keras-tuner

6 高级 API  

当第一眼看到这种类型的实现时很容易让人联想到面向对象编程。这里,你的模型是一个基于 tf.keras.model 扩展的 Python 类。受 Chainer 启发,我们实现了模型子类化,这同 Pytorch 定义模型的方式非常相似。

通过模型子类化,我们在类的构建函数里定义了模型的层。call() 方法处理了正向传播的定义和执行。

class Model(tf.keras.Model):  def __init__(self):    # Define the layers here    super(Model, self).__init__()    self.conv1 = Conv2D(filters=8, kernel_size=4, padding="same", strides=1, input_shape=(IMAGE_HEIGHT,IMAGE_WIDTH,IMAGE_DEPTH))    self.conv2 = Conv2D(filters=16, kernel_size=4, padding="same", strides=1)    self.pool = MaxPool2D(pool_size=2, strides=2, padding="same")    self.flat = Flatten()    self.probs = Dense(units=N_CLASSES, activation='softmax', name="output")    def call(self, x):    # Define the forward pass    net = self.conv1(x)    net = self.pool(net)    net = self.conv2(net)    net = self.pool(net)    net = self.flat(net)    net = self.probs(net)    return net    def compute_output_shape(self, input_shape):    # You need to override this function if you want to use the subclassed model    # as part of a functional-style model.    # Otherwise, this method is optional.    shape = tf.TensorShape(input_shape).as_list()    shape[-1] = self.num_classes    return tf.TensorShape(shape)

模型子类化具有很多优势。它可以更容易地实施模型检查。我们可以通过断点调试的方式,在指定代码行停留,并检查模型的激活函数或 logit 函数。

当然,灵活性也意味着更多的问题。

模型子类化需要程序员更小心,也需要程序员掌握更多的知识。

简而言之,你的代码更容易产生问题。

7 定义训练循环  

在 TF 2.0 中,训练模型最简单的方法是使用 fit() 方法。fit() 同时支持序列化和子类化模型。在使用模型子类化的时候,我们唯一需要调整的就是重写类成员函数 compute_output_shape(),或者干脆放弃这个函数。除此以外,我们可以使用 tf.data.Dataset 或 NumPy 的标准 nd-arrays 来作为 fit() 的输入。

不过,如果你想要更清晰地了解梯度和损失函数运行机制的话,你可以使用梯度带。这对研究学者尤其有用。

通过梯度带,我们可以手动定义训练过程的每一步。训练一个神经网络的基本步骤如下:

  • 正向传播

  • 损失函数评估

  • 反向传播

  • 梯度下降

每一步都被单独定义。

当你想要进一步了解神经网络训练的时候,梯度带就变得很有指导意义。如果你需要检查不同模型权重的损失值或者梯度矢量本身的话,你可以直接把它们打印出来。

梯度带提供了很大的灵活性。但是正如子类化对比序列化一样,更好的灵活性也需要额外的代价。同 fit() 方法相比,我们在这里只需要手动定义一个训练循环。这也因此让代码更容易产生 bug 并且更难调试。对于那些追求标准化编程的编码人员来说,我相信这是一个很棒的折中,毕竟研究人员只是对开发新东西更感兴趣。

同时,我们也可以很容易地通过 fit() 来设置 TensorBoard,具体参看下文。

8 定义训练循环  

我们可以通过 fit() 方法很容易地配置一个 TensorBoard 实例。它同样适用于 Jupyter/Colab 笔记本。

在这里,我们将 TensorBoard 添加为 fit 方法的回调。

只要你使用 fit() 方法,这种方法就可以用于序列化以及子类化 API。

 Load the TensorBoard notebook extension  %load_ext tensorboard    # create the tensorboard callback  tensorboard = TensorBoard(log_dir='logs/{}'.format(time.time()), histogram_freq=1)    # train the model  model.fit(x=x_train,             y=y_train,             epochs=2,             validation_data=(x_test, y_test),             callbacks=[tensorboard])    # launch TensorBoard  %tensorboard --logdir logs

如果你选择使用模型子类化以及通过梯度带来编写训练循环的话,你同样需要手动定义 TensorBoard。手动定义包括创建 summary 文件、使用 tf.summary.create_file_writer() 以及指定你想要可视化的变量。

值得一提的是,TF 2.0 中有很多回调函数可供使用,下面是其中一些相对有用的函数:

  • EarlyStopping:如同字面含义,它设置了一条规则,当被检测的量化值停止改善时就结束训练。

  • ReduceLROnPlateau:当评估值停止改善的时候就降低学习率。

  • TerminateOnNaN:当遇到一个 NaN 损失的时候就回调结束训练。

  • LambdaCallback:一个创建样本的回调,自定义回调正在开发过程中。

你可以在这里查看 TensorFlow 2.0 中所有的回调函数:

https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/keras/callbacks

9 提升 EagerCode 的性能  

如果你选择使用梯度带来训练你的模型,你会注意到性能上会有一定程度的下降。

通过即时执行的方式运行 TF 代码的确容易理解,但是这会损失一些性能。为了避免这个问题,TensorFlow 2.0 引入了 tf.function。

基本上,如果你用 tf.function 装饰了你的 python 函数,你其实让 TensorFlow 接管了这个函数,并且转变成了一个 TF 的高性能抽象。
@tf.function  def train_step(images, labels):      with tf.GradientTape() as tape:      # forward pass      predictions = model(images)            # compute the loss      loss = cross_entropy(tf.one_hot(labels, N_CLASSES), predictions)        # get the gradients w.r.t the model's weights    gradients = tape.gradient(loss, model.trainable_variables)        # perform a gradient descent step    optimizer.apply_gradients(zip(gradients, model.trainable_variables))        # accumulates the training loss and accuracy    train_loss(loss)    train_accuracy(labels, predictions)

这意味着该函数会被标记为即时(JIT)编译,从而 TensorFlow 可以把它按照图来运行。因此 TensorFlow 1.x 可以借助像节点剪枝或内核融合等方式来获得更好的性能。

总之,TensorFlow 2.0 可以允许你将代码分解为更小的函数。然后你可以标注那些你想使用 tf.function 的函数,从而获取额外的性能。最后装饰那些具有最大计算瓶颈的函数。这些函数通常是训练循环或模型的正向传播。

注意,在你装饰函数的时候,你同样损失了 eager execution 的一些优势。换句话说,你不再能够设置断点或者使用代码片段中的 print() 函数。

10 模型保存和恢复  

TensorFlow 1.x 中缺少的另外一项标准就是我们如何为产品保存和装载模型。TensorFlow 2.0 试图通过定义一个单一 API 来解决这个问题。

TF 2.0 并没有采用保存模型的很多方法,它反而标准化了一个叫做 SavedModel 的抽象。

这很容易理解。如果你构建了一个序列化模型或者使用 tf.keras.Model 扩展了你的类,那么你的类就继承了 tf.train.Checkpoints。因此,你可以将你的模型序列化为 SavedModel 对象。

# serialize your model to a SavedModel object  # It includes the entire graph, all variables and weights  model.save('/tmp/model', save_format='tf')    # load your saved model  model = tf.keras.models.load_model('/tmp/model')

TensorFlow 生态系统整合了 SavedModels,也就是说,你可以将它部署到很多设备中,诸如手机、边缘设备以及服务器等。

e695185abe724040f336f4247caadee3.png

11 TF-Lite 转化  

如果你想部署 SaveModel 到一个像 Raspberry Pi、Edge TPU 或手机这样的嵌入式设备中的话,你需要一个 TF Lite 转换器。

在 2.0 中,TFLiteConverter 不支持冻结 GraphDefas。如果你想把一个冻结的 GraphDefas 转化到 TF 2.0 中,你可以使用 tf.compat.v1.TFLiteConveter.

在部署到嵌入式设备之前经常会执行一个后训练量化。使用 TFLiteConveter 时,只需要将优化标志位设定为“OPTIMIZE_FOR_SIZE”即可。这会将模型的权重从浮点型量化为 8 位精度,从而降低了模型的大小,也在模型精确度几乎没有损失的情况下改善了延迟。

# create a TensorFlow Lite converter   converter = tf.lite.TensorFlowLiteConverter.from_keras_model(model)    # performs model quantization to reduce the size of the model and improve latency  converter.optimizations = [tf.lite.Optimize.OPTIMIZE_FOR_SIZE]    tflite_model = converter.convert()

这只是一个实验中的标志位,未来仍有可能改变。

12 TensorFLow.js 转化  

我们可以使用同样的 SavedModel 对象,并将其转化为 TensorFlow.js 格式。然后,我们可以使用 JavaScript 来加载,从而在浏览器中运行我们的模型。

!tensorflowjs_converter \      --input_format=tf_saved_model \      --saved_model_tags=serve \      --output_format=tfjs_graph_model \      /tmp/model \      /tmp/web_modelview raw

首先,我们需要通过 pip 安装 TensorFlow.js。然后,使用 tensorflowjs_converter 脚本来获取训练模型,并将其转化为 JavaScript 兼容的代码。最后,我们可以加载这些代码并通过 JavaScript 实现推断。

另外,我们也可以在浏览器中通过 TensorFlow.js 来训练模型。

13 结   语  

最后,作者介绍了 TF 2.0 的其它一些特性。首先可以更直观地为序列化或子类化模型添加更多的层。尽管 TF 包含了绝大多数比较流行的层,如 Conv2D 以及 TransposeConv2D 等,但是很多时候我们仍然会发现这还远远不够,尤其是在重复一些论文实验或者是做研究的时候。

好消息是,我们可以开发我们自己的自定义层。通过一些 Keras API,我们可以创造一个类,然后通过 tf.keras.Layer 扩展。实际上,我们可以使用一个非常类似的模式来创建自定义的激活函数、标准化层或者是评估函数。更多内容详见此处。

我们也可以将 1.x 的代码转化为 2.0。TF 团队为此开发了 tf_upgrade_v2 工具集。

这个脚本并不是从根本上将 TF 1.x 转化为 2.0,它只是很简单地利用了 tf.compat.v1 模型,更改了函数的命名空间。同时,如果你的遗留代码使用了 tf.contrib 的话,该脚本同样不能使用。你因此只能使用一些其它的类库或者是这些函数的 2.0 版本实现。

原文链接:

https://hackernoon.com/everything-you-need-to-know-about-tensorflow-2-0-b0856960c074

点个在看少个 bug ?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值