首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

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

作者|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/

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20171229G0V4OX00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券