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

在C+中使用TensorFlow训练深度神经网络

AiTechYun

编辑:yuxiangyu

你可能知道TensorFlow的核心是用C++构建的,然而只有python的API才能获得多种便利。

当我写上一篇文章时,目标是仅使用TensorFlow的C ++ API实现相同的DNN(深度神经网络),然后仅使用CuDNN。从我入手TensorFlow的C ++版本开始,我意识到即使对于简单DNN来说,也有很多东西被忽略了。

文章地址:https://matrices.io/deep-neural-network-from-scratch/

请记住,使用外部运算训练网络肯定是不可能的。你最可能面临的错误是缺少梯度运算。我目前正在将梯度运算从Python迁移到C ++。

在这个博客文章中,我们将建立一个深度神经网络,使用宝马车的车龄、公里数和发动机使用的燃料类型预测车的价格。我们将只在C ++中使用TensorFlow。目前在C ++中没有优化器,所以你会看到训练代码不那么好看,但是未来会添加优化器。

阅读本文需对谷歌的指南(https://www.tensorflow.org/api_guides/cc/guide)有所了解。

GitHub:https://github.com/theflofly/dnn_tensorflow_cpp

安装

我们将在TensorFlow C++ code中运行我们的C ++代码,我们可以尝试使用已编译的库,但是相信有些人会由于其环境的特殊性而遇到麻烦。从头开始构建TensorFlow会避免出现这些问题,并确保我们正在使用最新版本的API。

你需要安装bazel构建工具。

安装:https://docs.bazel.build/versions/master/install.html

在OSX上使用brew就可以了:

我们将从TensorFlow源文件开始构建:

然后你必须对安装进行配置,如选择是否启用GPU,你要运行以下配置脚本:

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

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

我们将bazel指令添加到BUILD文件中:

基本上它会使用model.cc建立一个模型二进制文件。我们现在准备编写我们的模型。

读取数据

这些数据是从法国网站leboncoin.fr中截取,然后清理和归一化并保存到CSV文件中。我们的目标是读取这些数据。用于归一化数据的元数据被保存到CSV文件的第一行,我们需要他们重新构建网络输出的价格。我创建了一个data_set.h和data_set.cc文件以保持代码清洁。他们从CSV文件中产生一个浮点型二维数组,馈送给我们的网络。我把代码粘贴在这里,但这无关紧要,你不需要花时间阅读。

data_set.h

data_set.cc

我们还必须在我们的bazel BUILD文件中添加这两个文件。

建立模型

第一步是读取CSV文件加入两个张量:x表示输入,y表示预期的结果。我们使用之前定义的DataSet类。访问下方链接下载CSV数据集。

链接:https://github.com/theflofly/dnn_tensorflow_cpp/blob/master/normalized_car_features.csv

要定义一个张量,我们需要它的类型和形状。在data_set对象中,x数据以平坦(flat)的方式保存,所以我们要将尺寸缩减成3(每辆车有3个特征)。然后,我们正在使用std::copy_n将数据从data_set对象复制到张量(Eigen::TensorMap)的底层数据结构。我们现在将数据作为TensorFlow数据结构,开始构建模型。

你可以使用以下方法调试张量:

C ++ API的独特之处在于,你将需要一个Scope对象来保存图形构造的状态,并将该对象传递给每个操作。

我们将有两个占位符,x包含汽车的特征和y表示每辆车相应的价格。

我们的网络有两个隐藏层,因此我们将有三个权重矩阵和三个偏置矩阵。而在Python中,它是在底层完成的,在C++中你必须定义一个变量,然后定义一个Assign节点,以便为该变量分配一个默认值。我们使用RandomNormal来初始化我们的变量,这将给我们一个正态分布的随机值。

然后我们使用Tanh作为激活函数来构建我们的三个层。

添加L2正则化。

最后,我们计算损失,我们的预测和实际价格之间y的差异,并且将正则化加入损失。

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

所有操作必须计算关于每个变量被添加到图中的损失的梯度,关于,我们初始化一个空的grad_outputs向量,它会TensorFlow会话使用时填充了为变量提供梯度的节点,grad_outputs[0]会给我们关于w1, grad_outputs[1]损失的梯度和关于w2的损失梯度,它顺序为,变量的顺序传递给AddSymbolicGradients。

现在我们在grad_outputs中有一个节点列表。当在TensorFlow会话中使用时,每个节点计算一个变量的损失梯度。我们用它来更新变量。我们将为每个变量设置一行,在这里我们使用最简单的梯度下降进行更新。

Cast操作实际上是学习速率参数,在我们的例子中为0.01。

我们的网络已准备好在会话中启动,Python中的Optimizers API的最小化函数基本上封装了在函数调用中计算和应用梯度。这就是我在PR#11377中所做的。

PR#11377:https://github.com/tensorflow/tensorflow/pull/11377

我们初始化一个ClientSession和一个名为outputs的张量向量,它将接收我们网络的输出。

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

在这一点上,我们可以按训练步骤的数量循环。在本例中,我们做5000步。首先使用loss节点运行前向传播部分,输出网络的损失。每隔100步记录一次损失值,减少损失是活动网络的强制性属性。然后我们必须计算我们的梯度节点并更新变量。我们的梯度节点被用作ApplyGradientDescent节点的输入,所以运行我们的apply_节点将首先计算梯度,然后将其应用于正确的变量。

到这里,我们的网络训练完成,可以试着预测(或者说推理)一辆车的价格。我们尝试预测一辆使用7年的宝马1系车的价格,这辆车是柴油发动机里程为11万公里。我们运行我们的layer_3节点吧汽车数据输入x,它本质上是一个前向传播步骤。因为我们已经训练过网络5000步,所以权重有一个学习值,所产生的结果不会是随机的。

我们不能直接使用汽车属性,因为我们的网络从归一化的属性中学习的,它们必须经过相同的归一化化过程。DataSet类有一个input方法,使用CSV读取期间加载的数据集的元数据来处理该步骤。

我们的网络产生一个介于0和1之间的值,data_set的output方法还会使用数据集元数据将该值转换为可读的价格。该模型可以使用命令bazel run -c opt //tensorflow/cc/models:model运行,如果最近编译了TensorFlow,你会很快看到如下输出:

它展示了汽车预计价格13377.7欧元。每次运行模型都会得到不同的结果,有时差异很大(8000—17000)。这是由于我们只用三个属性来描述汽车,而我们的网络架构也相对简单。

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

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券