首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何仅使用TensorFlow C+来训练深度神经网络

如何仅使用TensorFlow C+来训练深度神经网络

作者头像
企鹅号小编
发布2018-01-16 14:26:27
8340
发布2018-01-16 14:26:27
举报
文章被收录于专栏:人工智能人工智能

作者|Florian Courtial

译者|Debra

编辑|Emily

AI 前线导读:训练神经网络是一件十分复杂,难度非常大的工作,有没有可能让训练的过程简单便利一些呢?有人突发奇想,尝试仅仅使用 TensorFlow C ++ 来进行这项工作。这样做的效果如何呢?我们来看看 Florian Courtial 用 TensorFlow C ++ 构建 DNN 框架的示例来了解一下吧。

更多干货内容请关注微信公众号“AI 前线”,(ID:ai-front)

正如你所知,TensorFlow(TF)的核心由 C ++ 构建,但是如今还是 Python API 使用起来比较便利一些。

我写这篇博文的目标,是仅使用 TF C ++ API 来构建基础的深度神经网络(DNN),然后再尝试仅使用 CuDNN 实现这一功能。但从使用 TF C ++ 构建神经网络开始,我就意识到即使是在简单的 DNN 中,也有很多东西会丢失。

请记住这一点,进行外部操作训练网络肯定是不可行的,因为你很可能将丢失梯度运算。我目前正在尝试将梯度运算从 Python 改为 C ++。

在这篇文章中,我们将示例如何建立一个深度神经网络,并通过车龄、里程和燃料类型来预测一辆宝马 Serie 1 的价格。我们将仅使用 TensorFlow C ++,并描述缺失的训练细节。目前 C ++ 中没有优化器,所以现在训练的代码没有那么性感,但是将来我可能会添加。

所有代码可以在 Github 上找到。

重建 TensorFlow

我们将用 TensorFlow C ++ 代码进行编码,虽然可以使用现成编译的库,但是我相信有些人在这个过程中会由于库环境的特殊性而遇到麻烦。从头开始构建 TensorFlow 会避免这些问题,而且需要确保使用的是最新版本的 API。

接下来只需要安装 bazel构建工具就可以了,然后遵照你的操作系统指示进行操作。在 OSX上,使用 brew就足够了:(左右滑动可看到全部代码)

因为是从头构建 TF,我们还需要张量源:

然后进行配置安装,你可以选择 GPU,也可以不选择,要做到这一点需要运行配置脚本:

现在我们来创建将接收模型代码的文件,并开始首次构建 TF。请注意,第一次构建需要相当长的时间(10 - 15分钟)。

非核心的 C ++ TF代码在 / tensorflow / cc中,这是我们创建模型文件的位置,另外还需要一个 BUILD文件,以便 bazel可以建立 model.cc。

我们把 bazel指示添加到 BUILD文件中:

一般它会使用 model.cc建立一个二元模型。现在,我们已经做好为模型编写代码的所有准备。

读取数据

如果你还记得的话,这些数据是法国网站 leboncoin.fr报废的,而不是经过清理和规范化,并保存到 CSV文件中的数据。我们的目标是读取这些数据。用来规范化数据的元数据被保存在 CSV文件的第一行,我需要它们重新构建网络输出的价格。我创建了一个 data_set.h和 data_set.cc文件,防止代码被打乱。它们将从 CSV文件中生成一个二维数组,用来训练神经网络。

我把代码放在这里,但因为它与我们的目标没有多大相关性,所以无需在阅读代码上多花时间。

data_set.h

我们还需要将这两个文件添加到 BUILD 文件中。

建模

第一步是将 CSV 文件读取为两个张量,x 为输入,y 为预期结果。我们使用之前定义的 DataSet 类。您可以在这里下载 CSV 数据集。

我们需要类型和形状来定义一个张量。在 data_set 对象中,x 以扁平的方式保存,这就是为什么我们将尺寸缩减至 3(每辆车有 3个特征)。然后我们使用 std :: copy_n 将数据从 data_set 对象复制到 Tensor(Eigen :: TensorMap)的底层数据结构中。现在可以开始建模了。

使用以下方法,我们可以轻松地调试张量:

C ++ API 的独特之处在于,我们需要一个 Scope 对象来保存图构造的状态,这个对象将在运算中传递。

我们将得到两个占位符,x 包含汽车功能和每辆车的相应价格。

该网络有两个隐藏层,因此我们将得到三个权重矩阵和三个偏差矩阵。而 Python 是在 C ++ 下完成的,我们必须定义一个变量和一个 Assign 节点,以便为该变量分配一个默认值。通过使用 RandomNormal 来初始化变量,我们获得正态分布的随机值。

然后使用 Tanh 作为激活函数建立三个层。

添加一个 L2 正则化。

最后,我们计算一下损失,即预测和实际价格 y 之间的差异,再加上正则化。

至此,我们完成了正向传播,并准备好启动反向传播部分。第一步是使用一个函数调用,将正向操作的梯度添加到图形中。

我们将所有计算每个变量损失的梯度所需的运算都添加到图中,初始化一个空的 grad_outputs 向量,当在 TensorFlow session 中使用时,它将保存为生成变量梯度的节点,grad_outputs [0] 将生成梯度损失 wrt w1,grad_outputs [1]grad 损失 wrt w2,按照 的顺序,传递给 AddSymbolicGradients 。

现在,我们得到一个 grad_outputs 节点列表。在 TensorFlow session 中使用时,每个节点计算一个变量的损失梯度,之后被用来更新变量。每个变量设置为一行,使用最简单的梯度下降来进行更新。

我们的网络已做好在 Session 中启动的准备,Python 优化器 API 的最小化功能基本上包含了在函数调用中的计算和应用梯度。

我们对一个 ClientSession 和一个命名为 output 的 Tensor 进行初始化,使之接收网络的输出。

然后初始化变量,在 Python 中,调用 tf.global_variables_initializer()就足够了,因为在构建图的过程中,我们保留了所有变量的列表。使用 C ++,我们必须保留变量列表。每个 RandomNormal 输出将被分配给 Assign 节点中定义的变量。

现在,我们可以循环训练步骤。在示例中,我们将做 5000 步训练。第一步是使用损失节点进行正向传播,输出为网络损失。每隔 100 步,我们记录下损失值,网络的强制性属性会导致损失值减小。之后计算梯度节点并更新变量。如果你还记得,我们的梯度节点已被用作 ApplyGradientDescent 节点的输入,所以为了运行 apply_ 节点,我们需要首先计算梯度,然后将其应用于正确的变量。

到这一步,该网络经过训练,已经可以尝试预测一辆车的价格,也就是所谓的推理。我们来预测一下一台柴油发动机,车龄为 7 年,里程 11 万公里的宝马 Seria 1 的价格。要做到这一点,我们需要使用 layer_3 节点,以汽车数据作为输入 x(基本上是一个正向传播)。因为我们此前曾经对网络进行过 5000步 的训练,所以权重会有一个学习值,产生的结果是非随机的。

我们不能直接使用汽车的属性,因为我们的网络从规范化的属性中学习,同样还必须经过相同的规范化过程。鉴于此,DataSet 使用 CSV 读取期间加载的数据集元数据来处理该步骤。

该网络生成一个介于 0和 1 之间的值,data_set 输出还负责使用数据集元数据,将该值转换回可读的价格。这个模型可以使用命令 bazel run -c opt // tensorflow / cc / models:model 运行,如果 TensorFlow 是重建的,很快就可以得到以下输出:

该模型预测的汽车价格为 13377.7 欧元。多次运行模型可能会得到不同的结果,有时差距非常大,如 8000€ 与 17000€。这是由于我们只用了三个属性来描述汽车,而且网络架构也非常简单。

正如我之前所说,C ++ API 还在不断改进,我们在将来可以找到更简单的方法。如果你知道能改善此方案的解决方法,欢迎留下评论。

https://matrices.io/training-a-deep-neural-network-using-only-tensorflow-c/

本文来自企鹅号 - AI漫游媒体

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文来自企鹅号 - AI漫游媒体

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档